Here is some test code I used when I started learning PolyVox. Sorry it is a bit messy:
Code:
#include "windows.h"
#include <iostream>
#include <sstream>
#include <vector>
// ogre test application. based on old ogre testapplication or whatever it was called
#include "mainOgreApplication.h"
// Polyvox includes
#include <Volume.h>
#include <MaterialDensityPair.h>
#include <SurfaceMesh.h>
#include <CubicSurfaceExtractor.h>
#include <MeshDecimator.h>
typedef PolyVox::MaterialDensityPair<uint8_t, 7, 1> PolyVoxVoxel; // 1 bit for density SOLID / NONSOLID only. Use 7 bits for material to allow 128 material types. Really only useful for cubic extraction i guess...
typedef PolyVox::Volume<PolyVoxVoxel> PolyVoxVolume; // typedef for volume based on 71 pair type
typedef PolyVox::SurfaceMesh<PolyVox::PositionMaterial> PolyVoxMesh; // polyvox mesh without normals
typedef PolyVox::CubicSurfaceExtractor<PolyVoxVoxel> PolyVoxCubicSurfaceExtractor; // cubic surface extractor without normals for 71 pair type
typedef PolyVox::MeshDecimator<PolyVox::PositionMaterial> PolyVoxMeshDecimator; // decimator for 71 pair type
// just some defines for changing demo behaviour
#define VOLUME_SIZE 64 // volume size in voxels
#define SPHERE_SIZE 40 // size of the sphere to cut out of the corner
const bool RENDER_DECIMATED_MESH = false;
class TestApplication : public OgreApplication
{
public:
TestApplication(void):mPolyVoxVolume(0), mLastTime(0), mManualObjectNode(0) {;}
virtual ~TestApplication()
{
delete mPolyVoxCubicSurfaceExtractor;
delete mPolyVoxDecimatedMeshMaterial1;
delete mPolyVoxDecimatedMeshMaterial2;
delete mPolyVoxDecimatedMeshMaterial3;
delete mPolyVoxDecimatedMeshMaterial4;
delete mPolyVoxMeshMaterialALL;
//delete mPolyVoxMeshMaterial1; // cant delete these due to use of shared_ptr. will be deallocated automatically by the auto pointer.
//delete mPolyVoxMeshMaterial2;
//delete mPolyVoxMeshMaterial3;
//delete mPolyVoxMeshMaterial4;
delete mPolyVoxVolume;
}
protected:
virtual void createScene()
{
mSceneMgr->setAmbientLight(Ogre::ColourValue::White); // set full light for testing
// polyvox testscene
Ogre::LogManager::getSingleton().logMessage(""); // create an empty space at the start of log
logTime("createScene START: ");
// allocate data first
mPolyVoxVolume = new PolyVoxVolume(VOLUME_SIZE, VOLUME_SIZE, VOLUME_SIZE); // create cubic volume with default block size 32
mPolyVoxMeshMaterialALL = new PolyVoxMesh(); // regular mesh
mPolyVoxMeshMaterial1 = new PolyVoxMesh(); // regular mesh
mPolyVoxMeshMaterial2 = new PolyVoxMesh(); // regular mesh
mPolyVoxMeshMaterial3 = new PolyVoxMesh(); // regular mesh
mPolyVoxMeshMaterial4 = new PolyVoxMesh(); // regular mesh
mPolyVoxDecimatedMeshMaterial1 = new PolyVoxMesh(); // decimated mesh
mPolyVoxDecimatedMeshMaterial2 = new PolyVoxMesh(); // decimated mesh
mPolyVoxDecimatedMeshMaterial3 = new PolyVoxMesh(); // decimated mesh
mPolyVoxDecimatedMeshMaterial4 = new PolyVoxMesh(); // decimated mesh
// only need single extractor since will extract single mesh first
mPolyVoxCubicSurfaceExtractor = new PolyVoxCubicSurfaceExtractor(mPolyVoxVolume, mPolyVoxVolume->getEnclosingRegion(), mPolyVoxMeshMaterialALL); // just enclose entire volume with single region
logTime("createScene volume data created : ");
// completely fill volume with data. have to skip border to prevent decimation issues.
for(uint16_t px = 0 ; px < mPolyVoxVolume->getWidth() ; px++)
{
uint8_t materialNumber = 4 * px / VOLUME_SIZE; // 4 materials spread evenly across x axis
for(uint16_t py = 0 ; py < mPolyVoxVolume->getHeight() ; py++)
{
for(uint16_t pz = 0 ; pz < mPolyVoxVolume->getDepth() ; pz++)
{
// add a sphere cut into the corner. position is 0, VOLUME_SIZE, 0
uint16_t distX = px;
uint16_t distY = VOLUME_SIZE - py;
uint16_t distZ = pz;
double distance = sqrt( (double)(distX*distX + distY*distY + distZ*distZ) ); // double conversion necessary for compile without error
if(distance < SPHERE_SIZE)
mPolyVoxVolume->setVoxelAt(px, py, pz, PolyVoxVoxel(materialNumber, 0)); // set voxel data in volume. set to non solid if inside sphere radius.
else
mPolyVoxVolume->setVoxelAt(px, py, pz, PolyVoxVoxel(materialNumber, 1)); // set voxel data in volume. set to solid if outside sphere radius.
}
}
}
logTime("createScene volume data filled : ");
// extract cubic surface
mPolyVoxCubicSurfaceExtractor->execute(); // execute to grab single mesh first
logTime("createScene surface extraction executed : ");
// before decimating, need to split into submeshes
// material 0
std::set<uint8_t> setMaterial1;
setMaterial1.insert(0);
polyvox_shared_ptr<PolyVoxMesh> meshPointerMaterial1 = mPolyVoxMeshMaterialALL->extractSubset(setMaterial1); // test: use actual pointer to test return
mPolyVoxMeshMaterial1 = meshPointerMaterial1.get();
// material 1
std::set<uint8_t> setMaterial2;
setMaterial2.insert(1);
polyvox_shared_ptr<PolyVoxMesh> meshPointerMaterial2 = mPolyVoxMeshMaterialALL->extractSubset(setMaterial2);
mPolyVoxMeshMaterial2 = meshPointerMaterial2.get();
// material 2
std::set<uint8_t> setMaterial3;
setMaterial3.insert(2);
polyvox_shared_ptr<PolyVoxMesh> meshPointerMaterial3 = mPolyVoxMeshMaterialALL->extractSubset(setMaterial3);
mPolyVoxMeshMaterial3 = meshPointerMaterial3.get();
// material 3
std::set<uint8_t> setMaterial4;
setMaterial4.insert(3);
polyvox_shared_ptr<PolyVoxMesh> meshPointerMaterial4 = mPolyVoxMeshMaterialALL->extractSubset(setMaterial4);
mPolyVoxMeshMaterial4 = meshPointerMaterial4.get();
// decimated each mesh separately
if(RENDER_DECIMATED_MESH == true)
{
// unlike PolyVoxCubicSurfaceExtractor which can be created in advance, mesh decimator will fail with access violation if allocated in advance. must allocate AFTER mesh extraction. kind of annoying...
// prepare mesh decimators
PolyVoxMeshDecimator decimator1(mPolyVoxMeshMaterial1, mPolyVoxDecimatedMeshMaterial1);
decimator1.execute();
PolyVoxMeshDecimator decimator2(mPolyVoxMeshMaterial2, mPolyVoxDecimatedMeshMaterial2);
decimator2.execute();
PolyVoxMeshDecimator decimator3(mPolyVoxMeshMaterial3, mPolyVoxDecimatedMeshMaterial3);
decimator3.execute();
PolyVoxMeshDecimator decimator4(mPolyVoxMeshMaterial4, mPolyVoxDecimatedMeshMaterial4);
decimator4.execute();
logTime("createScene mesh decimated : ");
// convert the meshes to ogre
mOgreManualObjectMaterial1 = convertPolyVoxMesh(mPolyVoxDecimatedMeshMaterial1, "WireframeNoLightWhite");
mOgreManualObjectMaterial2 = convertPolyVoxMesh(mPolyVoxDecimatedMeshMaterial2, "WireframeNoLightRed");
mOgreManualObjectMaterial3 = convertPolyVoxMesh(mPolyVoxDecimatedMeshMaterial3, "WireframeNoLightGreen");
mOgreManualObjectMaterial4 = convertPolyVoxMesh(mPolyVoxDecimatedMeshMaterial4, "WireframeNoLightBlue");
}
else
{
mOgreManualObjectMaterial1 = convertPolyVoxMesh(mPolyVoxMeshMaterial1, "WireframeNoLightWhite");
mOgreManualObjectMaterial2 = convertPolyVoxMesh(mPolyVoxMeshMaterial2, "WireframeNoLightRed");
mOgreManualObjectMaterial3 = convertPolyVoxMesh(mPolyVoxMeshMaterial3, "WireframeNoLightGreen");
mOgreManualObjectMaterial4 = convertPolyVoxMesh(mPolyVoxMeshMaterial4, "WireframeNoLightBlue");
}
logTime("createScene convert mesh to manualObject : ");
mManualObjectNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("manualObjectSceneNode", Ogre::Vector3(0.0f, 0.0f, 0.0f));
mManualObjectNode->setScale(0.1f, 0.1f, 0.1f);
mManualObjectNode->setVisible(true);
if(mOgreManualObjectMaterial1) mManualObjectNode->attachObject(mOgreManualObjectMaterial1);
if(mOgreManualObjectMaterial2) mManualObjectNode->attachObject(mOgreManualObjectMaterial2);
if(mOgreManualObjectMaterial3) mManualObjectNode->attachObject(mOgreManualObjectMaterial3);
if(mOgreManualObjectMaterial4) mManualObjectNode->attachObject(mOgreManualObjectMaterial4);
// adjust camera
mCameraSceneNode->setPosition(Ogre::Vector3(-5, 11, -4));
mCameraSceneNode->lookAt(Ogre::Vector3(0, 6, 0), Ogre::Node::TransformSpace::TS_WORLD);
}
void logTime(Ogre::String message)
{
// log time in milliseconds to ogre log. just used for testing how long each stage takes in milliseconds
unsigned int ogreTime = mRoot->getTimer()->getMilliseconds();
unsigned int time = ogreTime - mLastTime; // time since last log. want to measure each step individually.
mLastTime = ogreTime;
std::stringstream sstream;
sstream << time;
Ogre::String logMessage = message;
logMessage += sstream.str();
logMessage += "\n";
Ogre::LogManager::getSingleton().logMessage(logMessage);
}
// convert polyvox mesh to ogre mesh
Ogre::ManualObject* convertPolyVoxMesh(PolyVoxMesh* mesh, Ogre::String materialName)
{
Ogre::ManualObject* ogreManualObject = NULL;
if(mesh && mesh->getNoOfVertices() > 0 && mesh->getNoOfIndices() > 0) // make sure mesh is allocated and valid first
{
const vector<uint32_t>& indexVector = mesh->getIndices(); // get a vector of all indices
const std::vector<PolyVox::PositionMaterial>& vertexVector = mesh->getVertices(); // get a vector of all vertices
ogreManualObject = mSceneMgr->createManualObject(); // create manual object
numOgreIndices = mesh->getNoOfIndices();
numOgreVertices = mesh->getNoOfVertices();
ogreManualObject->estimateIndexCount(numOgreIndices); // set index/vertex counts before starting to speed up allocations
ogreManualObject->estimateVertexCount(numOgreVertices);
ogreManualObject->begin(materialName, Ogre::RenderOperation::OT_TRIANGLE_LIST); // start adding triangles
PolyVox::Vector3DFloat positionTempVar;
// iterate through vertices
for(int v = 0 ; v < numOgreVertices ; v++)
{
positionTempVar = vertexVector[v].getPosition(); // get position vector from vertex list
ogreManualObject->position((Ogre::Real)(positionTempVar.getX()), (Ogre::Real)(positionTempVar.getY()), (Ogre::Real)(positionTempVar.getZ())); // add a new vertex to ogre manualobject
}
// iterate through indices
for(int i = 0 ; i < numOgreIndices ; i++)
{
ogreManualObject->index((Ogre::uint32)(indexVector[i])); // set indices in same order as polyvox mesh (if using DirectX render system, may differ for OpenGL)
}
ogreManualObject->end(); // stop adding triangles
}
return ogreManualObject;
}
private:
PolyVoxVolume* mPolyVoxVolume; // single volume encompassing all volumetric data
PolyVoxMesh* mPolyVoxMeshMaterialALL; // single material extracted before extracting per-material submeshes
PolyVoxMesh* mPolyVoxMeshMaterial1; // polyvox mesh used for extraction
PolyVoxMesh* mPolyVoxMeshMaterial2;
PolyVoxMesh* mPolyVoxMeshMaterial3;
PolyVoxMesh* mPolyVoxMeshMaterial4;
PolyVoxMesh* mPolyVoxDecimatedMeshMaterial1; // decimated mesh
PolyVoxMesh* mPolyVoxDecimatedMeshMaterial2;
PolyVoxMesh* mPolyVoxDecimatedMeshMaterial3;
PolyVoxMesh* mPolyVoxDecimatedMeshMaterial4;
PolyVoxCubicSurfaceExtractor* mPolyVoxCubicSurfaceExtractor; // cubic surface extractor (without normals)
Ogre::ManualObject* mOgreManualObjectMaterial1; // ogre manual object for drawing a single mesh
Ogre::ManualObject* mOgreManualObjectMaterial2;
Ogre::ManualObject* mOgreManualObjectMaterial3;
Ogre::ManualObject* mOgreManualObjectMaterial4;
Ogre::SceneNode* mManualObjectNode; // ogre scene node to attach manual object
uint32_t numOgreIndices; // for recording and reporting # of ogre indices/vertices
uint32_t numOgreVertices;
unsigned int mLastTime;
};
Obviously this is only as a demo case and would need to be written totally differently for practical use.
Also, the extractSubset() function is no longer a member of PolyVox::SurfaceMesh, this code was written for the 13/01/2011 PolyVox version. There is another way to handle materials, see addSurfacePatchRenderable and related functions in Thermite (start with ThermiteGameLogic.h)
Because this is cubic extraction without normals, you will need to generate normals yourself or change to a different extraction method. Also, PolyVox does not generate texture coordinates, you will need to do this in your vertex or pixel shaders too.
I can't really be bothered posting the OgreApplication class, it is based on the standard Ogre demo classes from before the new demo framework was created, I am sure you can extract what you need from the code above.
I hope this helps. Maybe someone can post a better example...