From 9f7b715279fb7047e33e078a5472894c3ad5f805 Mon Sep 17 00:00:00 2001 From: David Williams Date: Sun, 1 Jul 2007 17:15:21 +0000 Subject: [PATCH] Renamed to PolyVox --- CMakeLists.txt | 61 ++ include/PolyVoxSceneManager.h | 122 +++ source/PolyVoxSceneManager.cpp | 1278 ++++++++++++++++++++++++++++++++ 3 files changed, 1461 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 include/PolyVoxSceneManager.h create mode 100644 source/PolyVoxSceneManager.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..552ed948 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,61 @@ +PROJECT(PolyVoxSceneManager) + +#Projects source files +SET(SRC_FILES + source/Block.cpp + source/MarchingCubesTables.cpp + source/MaterialMap.cpp + source/MaterialMapManager.cpp + source/MaterialMapSerializer.cpp + source/PolyVoxSceneManager.cpp + source/Volume.cpp + source/VolumeIterator.cpp + source/VolumeManager.cpp + source/VolumeSerializer.cpp +) + +#Projects headers files +SET(INC_FILES + include/Block.h + include/Constants.h + include/IntegralVector3.h + include/MarchingCubesTables.h + include/MaterialMap.h + include/MaterialMapManager.h + include/MaterialMapSerializer.h + include/PolyVoxSceneManager.h + include/Triangle.h + include/TypeDef.h + include/Vertex.h + include/Volume.h + include/VolumeIterator.h + include/VolumeManager.h + include/VolumeSerializer.h +) + +ADD_DEFINITIONS(-DVOXEL_SCENE_MANAGER_EXPORT) #Export symbols in the .dll + +#Appends "_d" to the generated library when in debug mode +SET(CMAKE_DEBUG_POSTFIX "_d") + +#"Sources" and "Headers" are the group names in Visual Studio. +#They may have other uses too... +SOURCE_GROUP("Sources" FILES ${SRC_FILES}) +SOURCE_GROUP("Headers" FILES ${INC_FILES}) + +#Tell CMake the paths +LINK_DIRECTORIES(${OGRE_LIB_DIR}) +INCLUDE_DIRECTORIES(${OGRE_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include) + +#Build +ADD_LIBRARY(PolyVoxSceneManager SHARED ${SRC_FILES} ${INC_FILES}) +TARGET_LINK_LIBRARIES(PolyVoxSceneManager ${OGRE_LIBRARIES}) + +#Install +INSTALL(TARGETS PolyVoxSceneManager + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) + +INSTALL(DIRECTORY materials/ DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/materials PATTERN "*.svn*" EXCLUDE) \ No newline at end of file diff --git a/include/PolyVoxSceneManager.h b/include/PolyVoxSceneManager.h new file mode 100644 index 00000000..e4f6839f --- /dev/null +++ b/include/PolyVoxSceneManager.h @@ -0,0 +1,122 @@ +/****************************************************************************** +This file is part of a voxel plugin for OGRE +Copyright (C) 2006 David Williams + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +******************************************************************************/ +#ifndef __PolyVoxSceneManager_H__ +#define __PolyVoxSceneManager_H__ + +#include "OgrePrerequisites.h" +#include "OgreSceneManager.h" + +#include "Constants.h" +#include "MaterialMap.h" +#include "Triangle.h" +#include "TypeDef.h" +#include "Volume.h" +#include "Vertex.h" + +#include + +namespace Ogre +{ + enum NormalGenerationMethod + { + SIMPLE, + CENTRAL_DIFFERENCE, + SOBEL + }; + + /// Factory for default scene manager + class VOXEL_SCENE_MANAGER_API PolyVoxSceneManagerFactory : public SceneManagerFactory + { + public: + /// Factory type name + static const String FACTORY_TYPE_NAME; + SceneManager* createInstance(const String& instanceName); + void destroyInstance(SceneManager* instance); + protected: + void initMetaData(void) const; + }; + + /// Voxel scene manager + class VOXEL_SCENE_MANAGER_API PolyVoxSceneManager : public SceneManager + { + public: + //Constructors, etc + PolyVoxSceneManager(const String& name); + ~PolyVoxSceneManager(); + + //Getters + uchar getMaterialIndexAt(uint uX, uint uY, uint uZ); + const String& getTypeName(void) const; + uint getSideLength(void); + + + //Setters + //void setMaterialNameForIndex(uchar uIndex, std::string sMaterialName); + void setNormalGenerationMethod(NormalGenerationMethod method); + void _findVisibleObjects(Camera* cam, VisibleObjectsBoundsInfo * visibleBounds, bool onlyShadowCasters); + + void setAllUpToDateFalse(void); + void createSphereAt(Vector3 centre, Real radius, uchar value, bool painting); + + bool loadScene(const String& filename); + bool saveScene(const String& filename); + + void generateLevelVolume(void); + + void generateMeshDataForRegion(uint regionX, uint regionY, uint regionZ, std::vector& vertexData, std::vector< std::vector >& indexData) const; + void mergeVertices5(std::vector& vertexData, std::vector< std::vector >& indexData) const; + bool verticesArePlanar3(uint uCurrentVertex, std::set setConnectedVertices, std::vector& vertexData) const; + + void doRegionGrowing(uint xStart, uint yStart, uint zStart, uchar value); + + bool containsPoint(Vector3 pos, float boundary); + bool containsPoint(IntVector3 pos, uint boundary); + + + + SceneNode* sceneNodes[OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS][OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS][OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS]; + std::map m_mapManualObjects[OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS][OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS][OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS]; + bool manualObjectUpToDate[OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS][OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS][OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS]; + bool regionIsHomogenous[OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS][OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS][OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS]; + + void igniteVoxel(UIntVector3 voxelToIgnite); + + private: + void markVoxelChanged(uint x, uint y, uint z); + void markRegionChanged(uint firstX, uint firstY, uint firstZ, uint lastX, uint lastY, uint lastZ); + + + + static uint fileNo; + + bool useNormalSmoothing; + uint normalSmoothingFilterSize; + + NormalGenerationMethod m_normalGenerationMethod; + + VolumePtr volumeData; + MaterialMapPtr materialMap; + + std::queue m_queueVoxelsToBurn; + + //std::string m_aMaterialNames[256]; + }; +} + +#endif diff --git a/source/PolyVoxSceneManager.cpp b/source/PolyVoxSceneManager.cpp new file mode 100644 index 00000000..f53653a3 --- /dev/null +++ b/source/PolyVoxSceneManager.cpp @@ -0,0 +1,1278 @@ +/****************************************************************************** +This file is part of a voxel plugin for OGRE +Copyright (C) 2006 David Williams + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +******************************************************************************/ + +#include "MarchingCubesTables.h" +#include "MaterialMapManager.h" +#include "Vertex.h" +#include "PolyVoxSceneManager.h" +#include "VolumeIterator.h" +#include "VolumeManager.h" + +#include "OgreManualObject.h" +#include "OgreStringConverter.h" +#include "OgreLogManager.h" + +//Tempory... +#include "OgreMeshManager.h" +#include "OgreEntity.h" + +namespace Ogre +{ + + ////////////////////////////////////////////////////////////////////////// + // PolyVoxSceneManagerFactory + ////////////////////////////////////////////////////////////////////////// + const String PolyVoxSceneManagerFactory::FACTORY_TYPE_NAME = "PolyVoxSceneManager"; + + SceneManager* PolyVoxSceneManagerFactory::createInstance( + const String& instanceName) + { + return new PolyVoxSceneManager(instanceName); + } + + void PolyVoxSceneManagerFactory::destroyInstance(SceneManager* instance) + { + delete instance; + } + + void PolyVoxSceneManagerFactory::initMetaData(void) const + { + mMetaData.typeName = FACTORY_TYPE_NAME; + mMetaData.description = "A voxel based scene manager"; + mMetaData.sceneTypeMask = ST_GENERIC; + mMetaData.worldGeometrySupported = false; + } + + ////////////////////////////////////////////////////////////////////////// + // PolyVoxSceneManager + ////////////////////////////////////////////////////////////////////////// + PolyVoxSceneManager::PolyVoxSceneManager(const String& name) + : SceneManager(name) + ,volumeData(0) + ,useNormalSmoothing(false) + ,normalSmoothingFilterSize(1) + ,m_normalGenerationMethod(SOBEL) + { + for(uint regionZ = 0; regionZ < OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS; ++regionZ) + { + for(uint regionY = 0; regionY < OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS; ++regionY) + { + for(uint regionX = 0; regionX < OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS; ++regionX) + { + sceneNodes[regionX][regionY][regionZ] = 0; + } + } + } + } + + PolyVoxSceneManager::~PolyVoxSceneManager() + { + } + + const String& PolyVoxSceneManager::getTypeName(void) const + { + return PolyVoxSceneManagerFactory::FACTORY_TYPE_NAME; + } + + bool PolyVoxSceneManager::loadScene(const String& filename) + { + /*volumeData = new Volume; + + if(filename.empty()) + { + generateLevelVolume(); + } + else + { + volumeData->load(filename); + } */ + + //volumeData = VolumeManager::getSingletonPtr()->getByName(filename + ".volume"); + volumeData = VolumeManager::getSingletonPtr()->load(filename + ".volume", "General"); + if(volumeData.isNull()) + { + LogManager::getSingleton().logMessage("Generating default volume"); + generateLevelVolume(); + LogManager::getSingleton().logMessage("Done generating default volume"); + } + + volumeData->tidy(); + + //Load material map + materialMap = MaterialMapManager::getSingletonPtr()->load(filename + ".materialmap", "General"); + + LogManager::getSingleton().logMessage("HERE"); + VolumeIterator volIter1(*volumeData); + for(uint z = 0; z < OGRE_VOLUME_SIDE_LENGTH; z += 10) + { + for(uint y = 0; y < OGRE_VOLUME_SIDE_LENGTH; y += 10) + { + for(uint x = 0; x < OGRE_VOLUME_SIDE_LENGTH; x += 10) + { + volIter1.setPosition(x,y,z); + uchar value = volIter1.getVoxel(); + //LogManager::getSingleton().logMessage("Value is " + StringConverter::toString(int(value))); + } + } + } + LogManager::getSingleton().logMessage("DONE"); + + for(uint blockZ = 0; blockZ < OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS; ++blockZ) + { + for(uint blockY = 0; blockY < OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS; ++blockY) + { + for(uint blockX = 0; blockX < OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS; ++blockX) + { + m_mapManualObjects[blockX][blockY][blockZ].clear(); + + if(sceneNodes[blockX][blockY][blockZ] != 0) + { + destroySceneNode(sceneNodes[blockX][blockY][blockZ]->getName()); //FIXME - when it's available in CVS, use destroyAllSceneNodes commented out below. + } + sceneNodes[blockX][blockY][blockZ] = 0; + manualObjectUpToDate[blockX][blockY][blockZ] = false; + } + } + } + + destroyAllManualObjects(); + //destroyAllSceneNodes(); + + //Create surrounding box + //FIXME - should do this using indices + /*ManualObject* metalBox = createManualObject("MetalBox"); + + metalBox->begin("Examples/BumpyMetal",RenderOperation::OT_TRIANGLE_STRIP); + metalBox->position(0,0,0); + metalBox->textureCoord(0,0); + metalBox->normal(0,0,1); + metalBox->position(512,0,0); + metalBox->textureCoord(1,0); + metalBox->normal(0,0,1); + metalBox->position(0,512,0); + metalBox->textureCoord(0,1); + metalBox->normal(0,0,1); + metalBox->position(512,512,0); + metalBox->textureCoord(1,1); + metalBox->normal(0,0,1); + metalBox->end();*/ + + /*metalBox->begin("Examples/BumpyMetal",RenderOperation::OT_TRIANGLE_STRIP); + metalBox->position(0,0,0); + metalBox->textureCoord(0,0); + metalBox->normal(0,1,0); + metalBox->position(0,0,512); + metalBox->textureCoord(0,1); + metalBox->normal(0,1,0); + metalBox->position(512,0,0); + metalBox->textureCoord(1,0); + metalBox->normal(0,1,0); + metalBox->position(512,0,512); + metalBox->textureCoord(1,1); + metalBox->normal(0,1,0); + metalBox->end();*/ + + /*metalBox->begin("Examples/BumpyMetal",RenderOperation::OT_TRIANGLE_STRIP); + metalBox->position(0,0,0); + metalBox->textureCoord(0,0); + metalBox->normal(1,0,0); + metalBox->position(0,512,0); + metalBox->textureCoord(1,0); + metalBox->normal(1,0,0); + metalBox->position(0,0,512); + metalBox->textureCoord(0,1); + metalBox->normal(1,0,0); + metalBox->position(0,512,512); + metalBox->textureCoord(1,1); + metalBox->normal(1,0,0); + metalBox->end(); + + metalBox->begin("Examples/BumpyMetal",RenderOperation::OT_TRIANGLE_STRIP); + metalBox->position(0,0,512); + metalBox->textureCoord(0,0); + metalBox->normal(0,0,-1); + metalBox->position(0,512,512); + metalBox->textureCoord(0,1); + metalBox->normal(0,0,-1); + metalBox->position(512,0,512); + metalBox->textureCoord(1,0); + metalBox->normal(0,0,-1); + metalBox->position(512,512,512); + metalBox->textureCoord(1,1); + metalBox->normal(0,0,-1); + metalBox->end(); + + metalBox->begin("Examples/BumpyMetal",RenderOperation::OT_TRIANGLE_STRIP); + metalBox->position(0,512,0); + metalBox->textureCoord(0,0); + metalBox->normal(0,-1,0); + metalBox->position(512,512,0); + metalBox->textureCoord(1,0); + metalBox->normal(0,-1,0); + metalBox->position(0,512,512); + metalBox->textureCoord(0,1); + metalBox->normal(0,-1,0); + metalBox->position(512,512,512); + metalBox->textureCoord(1,1); + metalBox->normal(0,-1,0); + metalBox->end(); + + metalBox->begin("Examples/BumpyMetal",RenderOperation::OT_TRIANGLE_STRIP); + metalBox->position(512,0,0); + metalBox->textureCoord(0,0); + metalBox->normal(-1,0,0); + metalBox->position(512,0,512); + metalBox->textureCoord(0,1); + metalBox->normal(-1,0,0); + metalBox->position(512,512,0); + metalBox->textureCoord(1,0); + metalBox->normal(-1,0,0); + metalBox->position(512,512,512); + metalBox->textureCoord(1,1); + metalBox->normal(-1,0,0); + metalBox->end(); + + SceneNode* boxSceneNode = getRootSceneNode()->createChildSceneNode(); + boxSceneNode->attachObject(metalBox);*/ + + /*ManualObject* water = createManualObject("WaterPlane"); + + water->begin("DavidWater",RenderOperation::OT_TRIANGLE_STRIP); + water->position(-1024,100,-1024); + water->textureCoord(0,0); + water->normal(0,1,0); + water->position(-1024,100,1024); + water->textureCoord(0,1); + water->normal(0,1,0); + water->position(1024,100,-1024); + water->textureCoord(1,0); + water->normal(0,1,0); + water->position(1024,100,1024); + water->textureCoord(1,1); + water->normal(0,1,0); + + water->end(); + + SceneNode* waterSceneNode = getRootSceneNode()->createChildSceneNode(); + waterSceneNode->attachObject(water);*/ + + return true; + } + + void PolyVoxSceneManager::_findVisibleObjects(Camera* cam, VisibleObjectsBoundsInfo * visibleBounds, bool onlyShadowCasters) + { + if(!volumeData.isNull()) + { + //Do burning + //FIXME - can make this more efficient + ulong uNoOfVoxelsToBurn = m_queueVoxelsToBurn.size(); + uNoOfVoxelsToBurn = (std::min)(uNoOfVoxelsToBurn, OGRE_MAX_VOXELS_TO_BURN_PER_FRAME); + VolumeIterator volIter(*volumeData); + for(ulong ct = 0; ct < uNoOfVoxelsToBurn; ++ct) + { + UIntVector3 pos = m_queueVoxelsToBurn.front(); + m_queueVoxelsToBurn.pop(); + if((pos.x >= 0) && (pos.y >= 0) && (pos.z >= 0) && (pos.x < OGRE_VOLUME_SIDE_LENGTH) && (pos.y < OGRE_VOLUME_SIDE_LENGTH) && (pos.z < OGRE_VOLUME_SIDE_LENGTH)) + { + volIter.setPosition(pos.x,pos.y,pos.z); + if(volIter.getVoxel() != 0) + { + volIter.setVoxelAt(pos.x,pos.y,pos.z,0); + markVoxelChanged(pos.x,pos.y,pos.z); + + m_queueVoxelsToBurn.push(UIntVector3(pos.x ,pos.y ,pos.z-1)); + m_queueVoxelsToBurn.push(UIntVector3(pos.x ,pos.y ,pos.z+1)); + m_queueVoxelsToBurn.push(UIntVector3(pos.x ,pos.y-1,pos.z )); + m_queueVoxelsToBurn.push(UIntVector3(pos.x ,pos.y+1,pos.z )); + m_queueVoxelsToBurn.push(UIntVector3(pos.x-1,pos.y ,pos.z )); + m_queueVoxelsToBurn.push(UIntVector3(pos.x+1,pos.y ,pos.z )); + } + } + + //LogManager::getSingleton().logMessage("Burnt voxel at x = " + StringConverter::toString(pos.x) + " y = " + StringConverter::toString(pos.y) + " z = " + StringConverter::toString(pos.z)); + + /*if((pos.x > 0) && (pos.y > 0) && (pos.z > 0) && (pos.x < OGRE_VOLUME_SIDE_LENGTH-1) && (pos.y < OGRE_VOLUME_SIDE_LENGTH-1) && (pos.z < OGRE_VOLUME_SIDE_LENGTH-1)) + {*/ + //In this case we can definatly move in all direction + + /*} + else + { + //In this case we need to check more carefully. + }*/ + } + + //Regenerate meshes. + for(uint regionZ = 0; regionZ < OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS; ++regionZ) + { + LogManager::getSingleton().logMessage("regionZ = " + StringConverter::toString(regionZ)); + for(uint regionY = 0; regionY < OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS; ++regionY) + { + for(uint regionX = 0; regionX < OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS; ++regionX) + { + if(manualObjectUpToDate[regionX][regionY][regionZ] == false) + { + /*for(uint materialCt2 = 0; materialCt2 < MAX_NO_OF_MATERIAL_BOUNDARIES_PER_REGION; ++materialCt2) + { + manualObjects[regionX][regionY][regionZ][materialCt2]->clear(); + }*/ + + std::vector vertexData; + std::vector< std::vector< Triangle> > indexData; + generateMeshDataForRegion(regionX,regionY,regionZ,vertexData,indexData); + + std::map::iterator mapIter = m_mapManualObjects[regionX][regionY][regionZ].begin(); + while(mapIter != m_mapManualObjects[regionX][regionY][regionZ].end()) + { + mapIter->second->beginUpdate(0); + mapIter->second->end(); + ++mapIter; + } + + //uint materialCt = 0; + for(uint meshCt = 1; meshCt < 256; ++meshCt) + { + if(indexData[meshCt].size() == 0) + { + continue; + } + + /*if(materialCt == MAX_NO_OF_MATERIAL_BOUNDARIES_PER_REGION) + { + //We've run out of materials for this region. + LogManager::getSingleton().logMessage("Out of Materials!!"); + break; + }*/ + + if(m_mapManualObjects[regionX][regionY][regionZ].find(meshCt) == m_mapManualObjects[regionX][regionY][regionZ].end()) + { + //We have to create the manual object + ManualObject* manualObject = createManualObject + ( + "ManualObject(" + + StringConverter::toString(regionX) + "," + + StringConverter::toString(regionY) + "," + + StringConverter::toString(regionZ) + ")" + + ", Material = " + StringConverter::toString(meshCt) + ); + manualObject->setDynamic(true); + + sceneNodes[regionX][regionY][regionZ] = getRootSceneNode()->createChildSceneNode(Vector3(regionX*OGRE_REGION_SIDE_LENGTH,regionY*OGRE_REGION_SIDE_LENGTH,regionZ*OGRE_REGION_SIDE_LENGTH)); + sceneNodes[regionX][regionY][regionZ]->attachObject(manualObject); + + m_mapManualObjects[regionX][regionY][regionZ].insert(std::make_pair(meshCt,manualObject)); + + manualObject->begin(materialMap->getMaterialAtIndex(meshCt), RenderOperation::OT_TRIANGLE_LIST); + + //for(ulong i = 0; i < vertexData[meshCt].size(); i += 2) + for(ulong i = 0; i < vertexData/*[meshCt]*/.size(); i++) + { + manualObject->position(vertexData/*[meshCt]*/[i].position); + manualObject->normal(vertexData/*[meshCt]*/[i].normal); + //manualObjects[blockX][blockY][blockZ][materialCt]->textureCoord(vertexData[meshCt][i].position); + } + + for(ulong i = 0; i < indexData[meshCt].size(); ++i) + { + manualObject->index(indexData[meshCt][i].v0); + manualObject->index(indexData[meshCt][i].v1); + manualObject->index(indexData[meshCt][i].v2); + } + manualObject->end(); + } + else + { + //We update the manual object + ManualObject* manualObject = m_mapManualObjects[regionX][regionY][regionZ].find(meshCt)->second; + //manualObject->clear(); + //manualObject->estimateVertexCount(vertexData[meshCt].size()); + //manualObject->estimateIndexCount(indexData[meshCt].size()); + + manualObject->beginUpdate(0); + + //manualObject->begin(m_aMaterialNames[meshCt], RenderOperation::OT_TRIANGLE_LIST); + + for(ulong i = 0; i < vertexData/*[meshCt]*/.size(); i++) + { + manualObject->position(vertexData/*[meshCt]*/[i].position); + manualObject->normal(vertexData/*[meshCt]*/[i].normal); + //manualObjects[blockX][blockY][blockZ][materialCt]->textureCoord(vertexData[meshCt][i].position); + } + + for(ulong i = 0; i < indexData[meshCt].size(); ++i) + { + manualObject->index(indexData[meshCt][i].v0); + manualObject->index(indexData[meshCt][i].v1); + manualObject->index(indexData[meshCt][i].v2); + } + manualObject->end(); + } + + + + //for(uint meshCt = 1; meshCt < 256; ++meshCt) + //{ + /*if(vertexData.size() == 0) + { + continue; + } + manualObjects[regionX][regionY][regionZ][materialCt]->begin("RedMaterial", RenderOperation::OT_LINE_LIST); + for(ulong i = 0; i < vertexData.size(); i++) + { + manualObjects[regionX][regionY][regionZ][materialCt]->position(vertexData[i].position); + manualObjects[regionX][regionY][regionZ][materialCt]->position(vertexData[i].position + vertexData[i].normal*2); + } + manualObjects[regionX][regionY][regionZ][materialCt]->end();*/ + //} + + //++materialCt; + } + } + + manualObjectUpToDate[regionX][regionY][regionZ] = true; + } + } + } + } + //Now call the base class to do the actual visibility determination... + SceneManager::_findVisibleObjects(cam, visibleBounds, onlyShadowCasters); + } + + void PolyVoxSceneManager::setAllUpToDateFalse(void) + { + for(uint blockZ = 0; blockZ < OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS; ++blockZ) + { + for(uint blockY = 0; blockY < OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS; ++blockY) + { + for(uint blockX = 0; blockX < OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS; ++blockX) + { + manualObjectUpToDate[blockX][blockY][blockZ] = false; + } + } + } + } + + void PolyVoxSceneManager::createSphereAt(Vector3 centre, Real radius, uchar value, bool painting) + { + int firstX = static_cast(std::floor(centre.x - radius)); + int firstY = static_cast(std::floor(centre.y - radius)); + int firstZ = static_cast(std::floor(centre.z - radius)); + + int lastX = static_cast(std::ceil(centre.x + radius)); + int lastY = static_cast(std::ceil(centre.y + radius)); + int lastZ = static_cast(std::ceil(centre.z + radius)); + + Real radiusSquared = radius * radius; + + //Check bounds + firstX = std::max(firstX,0); + firstY = std::max(firstY,0); + firstZ = std::max(firstZ,0); + + lastX = std::min(lastX,int(OGRE_VOLUME_SIDE_LENGTH-1)); + lastY = std::min(lastY,int(OGRE_VOLUME_SIDE_LENGTH-1)); + lastZ = std::min(lastZ,int(OGRE_VOLUME_SIDE_LENGTH-1)); + + VolumeIterator volIter(*volumeData); + volIter.setValidRegion(firstX,firstY,firstZ,lastX,lastY,lastZ); + volIter.setPosition(firstX,firstY,firstZ); + while(volIter.isValidForRegion()) + { + //if((volIter.getPosX()*volIter.getPosX()+volIter.getPosY()*volIter.getPosY()+volIter.getPosZ()*volIter.getPosZ()) < radiusSquared) + if((centre - Vector3(volIter.getPosX(),volIter.getPosY(),volIter.getPosZ())).squaredLength() <= radiusSquared) + { + if(painting) + { + if(volIter.getVoxel() != 0) + { + volIter.setVoxel(value); + //volIter.setVoxelAt(volIter.getPosX(),volIter.getPosY(),volIter.getPosZ(),value); + } + } + else + { + volIter.setVoxel(value); + //volIter.setVoxelAt(volIter.getPosX(),volIter.getPosY(),volIter.getPosZ(),value); + } + //markVoxelChanged(volIter.getPosX(),volIter.getPosY(),volIter.getPosZ()); //FIXME - create a version of this function to mark larger regions at a time. + } + volIter.moveForwardInRegion(); + } + markRegionChanged(firstX,firstY,firstZ,lastX,lastY,lastZ); + } + + void PolyVoxSceneManager::generateLevelVolume(void) + { + volumeData = VolumePtr(new Volume); + VolumeIterator volIter(*volumeData); + for(uint z = 0; z < OGRE_VOLUME_SIDE_LENGTH; ++z) + { + for(uint y = 0; y < OGRE_VOLUME_SIDE_LENGTH; ++y) + { + for(uint x = 0; x < OGRE_VOLUME_SIDE_LENGTH; ++x) + { + if((x/16+y/16+z/16)%2 == 0) + volIter.setVoxelAt(x,y,z,4); + else + volIter.setVoxelAt(x,y,z,8); + } + } + } + + for(uint z = 0; z < OGRE_VOLUME_SIDE_LENGTH; ++z) + { + for(uint y = 0; y < OGRE_VOLUME_SIDE_LENGTH; ++y) + { + for(uint x = 0; x < OGRE_VOLUME_SIDE_LENGTH; ++x) + { + if( + (z<62)|| + (z>193)|| + (y<78)|| + (y>177)|| + (x<30)|| + (x>225) + ) + { + volIter.setVoxelAt(x,y,z,2); + } + } + } + } + + //Rooms + Vector3 centre(128,128,128); + Vector3 v3dSize(192,96,128); + + uint uHalfX = static_cast(v3dSize.x / 2); + uint uHalfY = static_cast(v3dSize.y / 2); + uint uHalfZ = static_cast(v3dSize.z / 2); + + for(uint z = static_cast(centre.z) - uHalfZ; z < static_cast(centre.z) + uHalfZ; z++) + { + for(uint y = static_cast(centre.y) - uHalfY; y < static_cast(centre.y) + uHalfY; y++) + { + for(uint x = static_cast(centre.x) - uHalfX; x < static_cast(centre.x) + uHalfX; x++) + { + volIter.setVoxelAt(x,y,z,0); + } + } + } + + for(uint z = 0; z < OGRE_VOLUME_SIDE_LENGTH; ++z) + { + for(uint y = 0; y < OGRE_VOLUME_SIDE_LENGTH; ++y) + { + for(uint x = 0; x < OGRE_VOLUME_SIDE_LENGTH; ++x) + { + if( + (x%64 < 8) && + (y < 128) && + (z>=62)&& + (z<=193)&& + (y>=78)&& + (y<=177)&& + (x>=30)&& + (x<=225) + ) + { + volIter.setVoxelAt(x,y,z,1); + } + } + } + } + } + + void PolyVoxSceneManager::generateMeshDataForRegion(const uint regionX, const uint regionY, const uint regionZ, std::vector& vertexData, std::vector< std::vector >& indexData) const + { + //LogManager::getSingleton().logMessage("Generating Mesh Data"); + /*LogManager::getSingleton().logMessage("HERE"); + VolumeIterator volIter1(volumeData); + for(uint z = 0; z < OGRE_VOLUME_SIDE_LENGTH; z += 10) + { + for(uint y = 0; y < OGRE_VOLUME_SIDE_LENGTH; y += 10) + { + for(uint x = 0; x < OGRE_VOLUME_SIDE_LENGTH; x += 10) + { + volIter1.setPosition(x,y,z); + uchar value = volIter1.getVoxel(); + LogManager::getSingleton().logMessage("Value is " + StringConverter::toString(int(value))); + } + } + } + LogManager::getSingleton().logMessage("DONE");*/ + //The vertex and index data + //vertexData.resize(256); + indexData.resize(256); + + //Used later to check if vertex has already been added + long int vertexIndices[OGRE_REGION_SIDE_LENGTH*2+1][OGRE_REGION_SIDE_LENGTH*2+1][OGRE_REGION_SIDE_LENGTH*2+1]; + //uchar materialAtPosition[OGRE_REGION_SIDE_LENGTH*2+1][OGRE_REGION_SIDE_LENGTH*2+1][OGRE_REGION_SIDE_LENGTH*2+1]; //FIXME - do we really need this? Can't we just get it from the volume... + //bool isVertexSharedBetweenMaterials[OGRE_REGION_SIDE_LENGTH*2+1][OGRE_REGION_SIDE_LENGTH*2+1][OGRE_REGION_SIDE_LENGTH*2+1]; + memset(vertexIndices,0xFF,sizeof(vertexIndices)); //0xFF is -1 as two's complement - this may not be portable... + //memset(materialAtPosition,0x00,sizeof(materialAtPosition)); + //memset(isVertexSharedBetweenMaterials,0x00,sizeof(isVertexSharedBetweenMaterials)); + + //tracks triangles using each vertex + //std::set trianglesUsingVertex[OGRE_REGION_SIDE_LENGTH*2+1][OGRE_REGION_SIDE_LENGTH*2+1][OGRE_REGION_SIDE_LENGTH*2+1]; + //FIXME - is this loop necessary or is a default set empty? + /*for(uint z = 0; z < OGRE_REGION_SIDE_LENGTH*2+1; z += 10) + { + for(uint y = 0; y < OGRE_REGION_SIDE_LENGTH*2+1; y += 10) + { + for(uint x = 0; x < OGRE_REGION_SIDE_LENGTH*2+1; x += 10) + { + trianglesUsingVertex[x][y][z].clear(); + } + } + }*/ + + //First and last voxels in the region + const uint firstX = regionX * OGRE_REGION_SIDE_LENGTH; + const uint firstY = regionY * OGRE_REGION_SIDE_LENGTH; + const uint firstZ = regionZ * OGRE_REGION_SIDE_LENGTH; + const uint lastX = (std::min)(firstX + OGRE_REGION_SIDE_LENGTH-1,static_cast(OGRE_VOLUME_SIDE_LENGTH-2)); + const uint lastY = (std::min)(firstY + OGRE_REGION_SIDE_LENGTH-1,static_cast(OGRE_VOLUME_SIDE_LENGTH-2)); + const uint lastZ = (std::min)(firstZ + OGRE_REGION_SIDE_LENGTH-1,static_cast(OGRE_VOLUME_SIDE_LENGTH-2)); + + //Offset from lower block corner + const Vector3 offset(firstX,firstY,firstZ); + + Vector3 vertlist[12]; + VolumeIterator volIter(*volumeData); + volIter.setValidRegion(firstX,firstY,firstZ,lastX,lastY,lastZ); + + ////////////////////////////////////////////////////////////////////////// + //Get mesh data + ////////////////////////////////////////////////////////////////////////// + + //Iterate over each cell in the region + for(volIter.setPosition(firstX,firstY,firstZ);volIter.isValidForRegion();volIter.moveForwardInRegion()) + { + //Current position + const uint x = volIter.getPosX(); + const uint y = volIter.getPosY(); + const uint z = volIter.getPosZ(); + + //LogManager::getSingleton().logMessage("x = " + StringConverter::toString(int(x)) + " y = " + StringConverter::toString(int(y)) + " z = " + StringConverter::toString(int(z))); + + //Voxels values + const uchar v000 = volIter.getVoxel(); + const uchar v100 = volIter.peekVoxel1px0py0pz(); + const uchar v010 = volIter.peekVoxel0px1py0pz(); + const uchar v110 = volIter.peekVoxel1px1py0pz(); + const uchar v001 = volIter.peekVoxel0px0py1pz(); + const uchar v101 = volIter.peekVoxel1px0py1pz(); + const uchar v011 = volIter.peekVoxel0px1py1pz(); + const uchar v111 = volIter.peekVoxel1px1py1pz(); + + //Determine the index into the edge table which tells us which vertices are inside of the surface + uchar iCubeIndex = 0; + + if (v000 == 0) iCubeIndex |= 1; + if (v100 == 0) iCubeIndex |= 2; + if (v110 == 0) iCubeIndex |= 4; + if (v010 == 0) iCubeIndex |= 8; + if (v001 == 0) iCubeIndex |= 16; + if (v101 == 0) iCubeIndex |= 32; + if (v111 == 0) iCubeIndex |= 64; + if (v011 == 0) iCubeIndex |= 128; + + /* Cube is entirely in/out of the surface */ + if (edgeTable[iCubeIndex] == 0) + { + continue; + } + + /* Find the vertices where the surface intersects the cube */ + if (edgeTable[iCubeIndex] & 1) + { + vertlist[0].x = (static_cast(x) + 0.5); + vertlist[0].y = (static_cast(y)); + vertlist[0].z = (static_cast(z)); + } + if (edgeTable[iCubeIndex] & 2) + { + vertlist[1].x = (static_cast(x + 1)); + vertlist[1].y = (static_cast(y) + 0.5); + vertlist[1].z = (static_cast(z)); + } + if (edgeTable[iCubeIndex] & 4) + { + vertlist[2].x = (static_cast(x) + 0.5); + vertlist[2].y = (static_cast(y + 1)); + vertlist[2].z = (static_cast(z)); + } + if (edgeTable[iCubeIndex] & 8) + { + vertlist[3].x = (static_cast(x)); + vertlist[3].y = (static_cast(y) + 0.5); + vertlist[3].z = (static_cast(z)); + } + if (edgeTable[iCubeIndex] & 16) + { + vertlist[4].x = (static_cast(x) + 0.5); + vertlist[4].y = (static_cast(y)); + vertlist[4].z = (static_cast(z + 1)); + } + if (edgeTable[iCubeIndex] & 32) + { + vertlist[5].x = (static_cast(x + 1)); + vertlist[5].y = (static_cast(y) + 0.5); + vertlist[5].z = (static_cast(z + 1)); + } + if (edgeTable[iCubeIndex] & 64) + { + vertlist[6].x = (static_cast(x) + 0.5); + vertlist[6].y = (static_cast(y + 1)); + vertlist[6].z = (static_cast(z + 1)); + } + if (edgeTable[iCubeIndex] & 128) + { + vertlist[7].x = (static_cast(x)); + vertlist[7].y = (static_cast(y) + 0.5); + vertlist[7].z = (static_cast(z + 1)); + } + if (edgeTable[iCubeIndex] & 256) + { + vertlist[8].x = (static_cast(x)); + vertlist[8].y = (static_cast(y)); + vertlist[8].z = (static_cast(z) + 0.5); + } + if (edgeTable[iCubeIndex] & 512) + { + vertlist[9].x = (static_cast(x + 1)); + vertlist[9].y = (static_cast(y)); + vertlist[9].z = (static_cast(z) + 0.5); + } + if (edgeTable[iCubeIndex] & 1024) + { + vertlist[10].x = (static_cast(x + 1)); + vertlist[10].y = (static_cast(y + 1)); + vertlist[10].z = (static_cast(z) + 0.5); + } + if (edgeTable[iCubeIndex] & 2048) + { + vertlist[11].x = (static_cast(x)); + vertlist[11].y = (static_cast(y + 1)); + vertlist[11].z = (static_cast(z) + 0.5); + } + + for (int i=0;triTable[iCubeIndex][i]!=-1;i+=3) + { + //The three vertices forming a triangle + const Vector3 vertex0 = vertlist[triTable[iCubeIndex][i ]] - offset; + const Vector3 vertex1 = vertlist[triTable[iCubeIndex][i+1]] - offset; + const Vector3 vertex2 = vertlist[triTable[iCubeIndex][i+2]] - offset; + + //FIXME - for the time being the material is the highest vertex + //Need to think about this... + uchar material = std::max(v000,v001); + material = std::max(material,v010); + material = std::max(material,v011); + material = std::max(material,v100); + material = std::max(material,v101); + material = std::max(material,v110); + material = std::max(material,v111); + + //vertexScaled values are always integers and so can be used as indices. + long int index; + unsigned int vertexScaledX; + unsigned int vertexScaledY; + unsigned int vertexScaledZ; + + Triangle triangle; //Triangle to be created... + + //Get scaled values for vertex 0 + vertexScaledX = static_cast((vertex0.x * 2.0) + 0.5); + vertexScaledY = static_cast((vertex0.y * 2.0) + 0.5); + vertexScaledZ = static_cast((vertex0.z * 2.0) + 0.5); + vertexScaledX %= OGRE_REGION_SIDE_LENGTH*2+1; + vertexScaledY %= OGRE_REGION_SIDE_LENGTH*2+1; + vertexScaledZ %= OGRE_REGION_SIDE_LENGTH*2+1; + //If a vertex has not yet been added, it's index is -1 + index = vertexIndices[vertexScaledX][vertexScaledY][vertexScaledZ]; + if((index == -1)) + { + //Add the vertex + vertexData.push_back(Vertex(vertex0)); + triangle.v0 = vertexData.size()-1; + vertexIndices[vertexScaledX][vertexScaledY][vertexScaledZ] = vertexData.size()-1; + } + else + { + //Just reuse the existing vertex + triangle.v0 = index; + } + + //Get scaled values for vertex 1 + vertexScaledX = static_cast((vertex1.x * 2.0) + 0.5); + vertexScaledY = static_cast((vertex1.y * 2.0) + 0.5); + vertexScaledZ = static_cast((vertex1.z * 2.0) + 0.5); + vertexScaledX %= OGRE_REGION_SIDE_LENGTH*2+1; + vertexScaledY %= OGRE_REGION_SIDE_LENGTH*2+1; + vertexScaledZ %= OGRE_REGION_SIDE_LENGTH*2+1; + //If a vertex has not yet been added, it's index is -1 + index = vertexIndices[vertexScaledX][vertexScaledY][vertexScaledZ]; + if((index == -1)) + { + //Add the vertex + vertexData.push_back(Vertex(vertex1)); + triangle.v1 = vertexData.size()-1; + vertexIndices[vertexScaledX][vertexScaledY][vertexScaledZ] = vertexData.size()-1; + } + else + { + //Just reuse the existing vertex + triangle.v1 = index; + } + + //Get scaled values for vertex 2 + vertexScaledX = static_cast((vertex2.x * 2.0) + 0.5); + vertexScaledY = static_cast((vertex2.y * 2.0) + 0.5); + vertexScaledZ = static_cast((vertex2.z * 2.0) + 0.5); + vertexScaledX %= OGRE_REGION_SIDE_LENGTH*2+1; + vertexScaledY %= OGRE_REGION_SIDE_LENGTH*2+1; + vertexScaledZ %= OGRE_REGION_SIDE_LENGTH*2+1; + //If a vertex has not yet been added, it's index is -1 + index = vertexIndices[vertexScaledX][vertexScaledY][vertexScaledZ]; + if((index == -1)) + { + //Add the vertex + vertexData.push_back(Vertex(vertex2)); + triangle.v2 = vertexData.size()-1; + vertexIndices[vertexScaledX][vertexScaledY][vertexScaledZ] = vertexData.size()-1; + } + else + { + //Just reuse the existing vertex + triangle.v2 = index; + } + + //Add the triangle + indexData[material].push_back(triangle); + } + } + + //FIXME - can it happen that we have no vertices or triangles? Should exit early? + + + ////////////////////////////////////////////////////////////////////////// + //Compute normals + ////////////////////////////////////////////////////////////////////////// + + //for(uint materialCt = 0; materialCt < 256; materialCt++) + { + for(uint vertexCt = 0; vertexCt < vertexData/*[materialCt]*/.size(); ++vertexCt) + { + const Vector3 vertexPosition = vertexData/*[materialCt]*/[vertexCt].position; + + const float posX = vertexPosition.x + static_cast(regionX * OGRE_REGION_SIDE_LENGTH); + const float posY = vertexPosition.y + static_cast(regionY * OGRE_REGION_SIDE_LENGTH); + const float posZ = vertexPosition.z + static_cast(regionZ * OGRE_REGION_SIDE_LENGTH); + + const uint floorX = static_cast(vertexPosition.x) + regionX * OGRE_REGION_SIDE_LENGTH; + const uint floorY = static_cast(vertexPosition.y) + regionY * OGRE_REGION_SIDE_LENGTH; + const uint floorZ = static_cast(vertexPosition.z) + regionZ * OGRE_REGION_SIDE_LENGTH; + + switch(m_normalGenerationMethod) + { + case SIMPLE: + { + volIter.setPosition(static_cast(posX),static_cast(posY),static_cast(posZ)); + const uchar uFloor = volIter.getVoxel() > 0 ? 1 : 0; + if((posX - floorX) > 0.25) //The result should be 0.0 or 0.5 + { + uchar uCeil = volIter.peekVoxel1px0py0pz() > 0 ? 1 : 0; + vertexData/*[materialCt]*/[vertexCt].normal = Vector3(uFloor - uCeil,0.0,0.0); + } + else if((posY - floorY) > 0.25) //The result should be 0.0 or 0.5 + { + uchar uCeil = volIter.peekVoxel0px1py0pz() > 0 ? 1 : 0; + vertexData/*[materialCt]*/[vertexCt].normal = Vector3(0.0,uFloor - uCeil,0.0); + } + else if((posZ - floorZ) > 0.25) //The result should be 0.0 or 0.5 + { + uchar uCeil = volIter.peekVoxel0px0py1pz() > 0 ? 1 : 0; + vertexData/*[materialCt]*/[vertexCt].normal = Vector3(0.0, 0.0,uFloor - uCeil); + } + vertexData/*[materialCt]*/[vertexCt].normal.normalise(); + break; + } + case CENTRAL_DIFFERENCE: + { + volIter.setPosition(static_cast(posX),static_cast(posY),static_cast(posZ)); + const Vector3 gradFloor = volIter.getCentralDifferenceGradient(); + if((posX - floorX) > 0.25) //The result should be 0.0 or 0.5 + { + volIter.setPosition(static_cast(posX+1.0),static_cast(posY),static_cast(posZ)); + } + if((posY - floorY) > 0.25) //The result should be 0.0 or 0.5 + { + volIter.setPosition(static_cast(posX),static_cast(posY+1.0),static_cast(posZ)); + } + if((posZ - floorZ) > 0.25) //The result should be 0.0 or 0.5 + { + volIter.setPosition(static_cast(posX),static_cast(posY),static_cast(posZ+1.0)); + } + const Vector3 gradCeil = volIter.getCentralDifferenceGradient(); + vertexData[vertexCt].normal = gradFloor + gradCeil; + vertexData[vertexCt].normal *= -1; + vertexData[vertexCt].normal.normalise(); + break; + } + case SOBEL: + { + volIter.setPosition(static_cast(posX),static_cast(posY),static_cast(posZ)); + const Vector3 gradFloor = volIter.getSobelGradient(); + if((posX - floorX) > 0.25) //The result should be 0.0 or 0.5 + { + volIter.setPosition(static_cast(posX+1.0),static_cast(posY),static_cast(posZ)); + } + if((posY - floorY) > 0.25) //The result should be 0.0 or 0.5 + { + volIter.setPosition(static_cast(posX),static_cast(posY+1.0),static_cast(posZ)); + } + if((posZ - floorZ) > 0.25) //The result should be 0.0 or 0.5 + { + volIter.setPosition(static_cast(posX),static_cast(posY),static_cast(posZ+1.0)); + } + const Vector3 gradCeil = volIter.getSobelGradient(); + vertexData[vertexCt].normal = gradFloor + gradCeil; + vertexData[vertexCt].normal *= -1; + vertexData[vertexCt].normal.normalise(); + break; + } + } + } + } + + ////////////////////////////////////////////////////////////////////////// + //Decimate mesh + ////////////////////////////////////////////////////////////////////////// + + //mergeVertices5(vertexData, indexData); + } + + void PolyVoxSceneManager::mergeVertices5(std::vector& vertexData, std::vector< std::vector >& indexData) const + { + if(vertexData.empty()) + return; + + std::vector< std::set > materialsUsingVertex; + materialsUsingVertex.resize(vertexData.size()); + for(uint material = 1; material < 256; ++material) + { + for(uint triangleCt = 0; triangleCt < indexData[material].size(); ++triangleCt) + { + materialsUsingVertex[indexData[material][triangleCt].v0].insert(material); + materialsUsingVertex[indexData[material][triangleCt].v1].insert(material); + materialsUsingVertex[indexData[material][triangleCt].v2].insert(material); + } + } + + std::vector noOfEdges; //0 means not on ege, 1 means edge, 2 or more means corner + noOfEdges.resize(vertexData.size()); + for(uint vertexCt = 0; vertexCt < vertexData.size(); ++vertexCt) + { + noOfEdges[vertexCt] = 0; + } + for(uint vertexCt = 0; vertexCt < vertexData.size(); ++vertexCt) + { + if(vertexData[vertexCt].position.x < 0.25) + noOfEdges[vertexCt] |= 1; + if(vertexData[vertexCt].position.y < 0.25) + noOfEdges[vertexCt] |= 2; + if(vertexData[vertexCt].position.z < 0.25) + noOfEdges[vertexCt] |= 4; + if(vertexData[vertexCt].position.x > OGRE_REGION_SIDE_LENGTH-0.25) + noOfEdges[vertexCt] |= 8; + if(vertexData[vertexCt].position.y > OGRE_REGION_SIDE_LENGTH-0.25) + noOfEdges[vertexCt] |= 16; + if(vertexData[vertexCt].position.z > OGRE_REGION_SIDE_LENGTH-0.25) + noOfEdges[vertexCt] |= 32; + } + + + for(uint material = 0; material < 256; ++material) + { + if(indexData[material].empty()) + { + continue; + } + + //FIXME - this is innefficient! iterating over ever vertex, even though one material might just use a few of them. + for(uint vertexCt = 0; vertexCt < vertexData.size(); vertexCt++) + { + if(materialsUsingVertex[vertexCt].size() > 1) + { + continue; + } + /*if((vertexData[vertexCt].position.x > 0.25) && + (vertexData[vertexCt].position.y > 0.25) && + (vertexData[vertexCt].position.z > 0.25) && + (vertexData[vertexCt].position.x < OGRE_REGION_SIDE_LENGTH-0.25) && + (vertexData[vertexCt].position.y < OGRE_REGION_SIDE_LENGTH-0.25) && + (vertexData[vertexCt].position.z < OGRE_REGION_SIDE_LENGTH-0.25))*/ + if((noOfEdges[vertexCt] == 0) || + (noOfEdges[vertexCt] == 1) || + (noOfEdges[vertexCt] == 2) || + (noOfEdges[vertexCt] == 4) || + (noOfEdges[vertexCt] == 8) || + (noOfEdges[vertexCt] == 16) || + (noOfEdges[vertexCt] == 32)) + { + + std::set setConnectedVertices; + + for(uint triangleCt = 0; triangleCt < indexData[material].size(); ++triangleCt) + { + if((indexData[material][triangleCt].v0 == vertexCt) || (indexData[material][triangleCt].v1 == vertexCt) || (indexData[material][triangleCt].v2 == vertexCt)) + { + if(noOfEdges[vertexCt] == 0) + { + setConnectedVertices.insert(indexData[material][triangleCt].v0); + setConnectedVertices.insert(indexData[material][triangleCt].v1); + setConnectedVertices.insert(indexData[material][triangleCt].v2); + } + else + { + if(noOfEdges[indexData[material][triangleCt].v0] == noOfEdges[vertexCt]) + setConnectedVertices.insert(indexData[material][triangleCt].v0); + if(noOfEdges[indexData[material][triangleCt].v1] == noOfEdges[vertexCt]) + setConnectedVertices.insert(indexData[material][triangleCt].v1); + if(noOfEdges[indexData[material][triangleCt].v2] == noOfEdges[vertexCt]) + setConnectedVertices.insert(indexData[material][triangleCt].v2); + } + } + } + setConnectedVertices.erase(vertexCt); + + if(setConnectedVertices.empty()) + { + continue; + } + + /*Vertex currentVertex = vertexData[vertexCt]; + uint vertexScaledX = static_cast((currentVertex.position.x * 2.0) + 0.5); + uint vertexScaledY = static_cast((currentVertex.position.y * 2.0) + 0.5); + uint vertexScaledZ = static_cast((currentVertex.position.z * 2.0) + 0.5); + //FIXME - think whether this modding is actually necessary... + vertexScaledX %= OGRE_REGION_SIDE_LENGTH*2+1; + vertexScaledY %= OGRE_REGION_SIDE_LENGTH*2+1; + vertexScaledZ %= OGRE_REGION_SIDE_LENGTH*2+1;*/ + + /*bool shared = isVertexSharedBetweenMaterials[vertexScaledX][vertexScaledY][vertexScaledZ]; + if(shared) + { + continue; + }*/ + + if(verticesArePlanar3(vertexCt, setConnectedVertices, vertexData)) + { + //FIXME - I'm not hapy that we have to use rbegin() here rather than begin(). + //Surely it should be possible to merge with any vertex? Not just the last one? + std::set::reverse_iterator connectedIter = setConnectedVertices.rbegin(); + for(uint triCt = 0; triCt < indexData[material].size(); triCt++) + { + /*if(noOfEdges[vertexCt] == 0) + {*/ + if(indexData[material][triCt].v0 == vertexCt) + indexData[material][triCt].v0 = *connectedIter; + if(indexData[material][triCt].v1 == vertexCt) + indexData[material][triCt].v1 = *connectedIter; + if(indexData[material][triCt].v2 == vertexCt) + indexData[material][triCt].v2 = *connectedIter; + /*} + else + { + if((indexData[material][triCt].v0 == vertexCt) && (noOfEdges[indexData[material][triCt].v0] == noOfEdges[vertexCt])) + indexData[material][triCt].v0 = *connectedIter; + if((indexData[material][triCt].v1 == vertexCt) && (noOfEdges[indexData[material][triCt].v1] == noOfEdges[vertexCt])) + indexData[material][triCt].v1 = *connectedIter; + if((indexData[material][triCt].v2 == vertexCt) && (noOfEdges[indexData[material][triCt].v2] == noOfEdges[vertexCt])) + indexData[material][triCt].v2 = *connectedIter; + }*/ + } + } + } + } + } + + //Hide degenerate triangles + /*for(uint triCt = 0; triCt < indexData.size(); triCt++) + { + if((indexData[material][triCt].v0 == indexData[material][triCt].v1) && (indexData[material][triCt].v1 == indexData[material][triCt].v2)) + { + indexData[material][triCt].v0 = 0; + indexData[material][triCt].v1 = 0; + indexData[material][triCt].v2 = 0; + } + }*/ + } + + bool PolyVoxSceneManager::verticesArePlanar3(uint uCurrentVertex, std::set setConnectedVertices, std::vector& vertexData) const + { + bool allXMatch = true; + bool allYMatch = true; + bool allZMatch = true; + bool allNormalsMatch = true; + + std::set::iterator iterConnectedVertices; + for(iterConnectedVertices = setConnectedVertices.begin(); iterConnectedVertices != setConnectedVertices.end(); ++iterConnectedVertices) + { + if(vertexData[uCurrentVertex].position.x != vertexData[*iterConnectedVertices].position.x) + { + allXMatch = false; + } + if(vertexData[uCurrentVertex].position.y != vertexData[*iterConnectedVertices].position.y) + { + allYMatch = false; + } + if(vertexData[uCurrentVertex].position.z != vertexData[*iterConnectedVertices].position.z) + { + allZMatch = false; + } + //FIXME - are these already normalised? We should make sure they are... + if(vertexData[uCurrentVertex].normal.normalisedCopy().dotProduct(vertexData[*iterConnectedVertices].normal.normalisedCopy()) < 0.99) + { + return false; + } + } + + return allXMatch || allYMatch || allZMatch; + } + + void PolyVoxSceneManager::markVoxelChanged(uint x, uint y, uint z) + { + //If we are not on a boundary, just mark one region. + if((x % OGRE_REGION_SIDE_LENGTH != 0) && + (x % OGRE_REGION_SIDE_LENGTH != OGRE_REGION_SIDE_LENGTH-1) && + (y % OGRE_REGION_SIDE_LENGTH != 0) && + (y % OGRE_REGION_SIDE_LENGTH != OGRE_REGION_SIDE_LENGTH-1) && + (z % OGRE_REGION_SIDE_LENGTH != 0) && + (z % OGRE_REGION_SIDE_LENGTH != OGRE_REGION_SIDE_LENGTH-1)) + { + manualObjectUpToDate[x >> OGRE_REGION_SIDE_LENGTH_POWER][y >> OGRE_REGION_SIDE_LENGTH_POWER][z >> OGRE_REGION_SIDE_LENGTH_POWER] = false; + } + else //Mark surrounding block as well + { + const uint regionX = x >> OGRE_REGION_SIDE_LENGTH_POWER; + const uint regionY = y >> OGRE_REGION_SIDE_LENGTH_POWER; + const uint regionZ = z >> OGRE_REGION_SIDE_LENGTH_POWER; + + const uint minRegionX = (std::max)(uint(0),regionX-1); + const uint minRegionY = (std::max)(uint(0),regionY-1); + const uint minRegionZ = (std::max)(uint(0),regionZ-1); + + const uint maxRegionX = (std::min)(uint(OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS-1),regionX+1); + const uint maxRegionY = (std::min)(uint(OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS-1),regionY+1); + const uint maxRegionZ = (std::min)(uint(OGRE_VOLUME_SIDE_LENGTH_IN_REGIONS-1),regionZ+1); + + for(uint zCt = minRegionZ; zCt <= maxRegionZ; zCt++) + { + for(uint yCt = minRegionY; yCt <= maxRegionY; yCt++) + { + for(uint xCt = minRegionX; xCt <= maxRegionX; xCt++) + { + manualObjectUpToDate[xCt][yCt][zCt] = false; + } + } + } + } + } + + void PolyVoxSceneManager::markRegionChanged(uint firstX, uint firstY, uint firstZ, uint lastX, uint lastY, uint lastZ) + { + const uint firstRegionX = firstX >> OGRE_REGION_SIDE_LENGTH_POWER; + const uint firstRegionY = firstY >> OGRE_REGION_SIDE_LENGTH_POWER; + const uint firstRegionZ = firstZ >> OGRE_REGION_SIDE_LENGTH_POWER; + + const uint lastRegionX = lastX >> OGRE_REGION_SIDE_LENGTH_POWER; + const uint lastRegionY = lastY >> OGRE_REGION_SIDE_LENGTH_POWER; + const uint lastRegionZ = lastZ >> OGRE_REGION_SIDE_LENGTH_POWER; + + for(uint zCt = firstRegionZ; zCt <= lastRegionZ; zCt++) + { + for(uint yCt = firstRegionY; yCt <= lastRegionY; yCt++) + { + for(uint xCt = firstRegionX; xCt <= lastRegionX; xCt++) + { + manualObjectUpToDate[xCt][yCt][zCt] = false; + } + } + } + } + + void PolyVoxSceneManager::doRegionGrowing(uint xStart, uint yStart, uint zStart, uchar value) + { + volumeData->regionGrow(xStart,yStart,zStart,value); + //FIXME - keep track of what has changed... + markRegionChanged(0,0,0,OGRE_VOLUME_SIDE_LENGTH-1,OGRE_VOLUME_SIDE_LENGTH-1,OGRE_VOLUME_SIDE_LENGTH-1); + } + + bool PolyVoxSceneManager::saveScene(const String& filename) + { + volumeData->saveToFile(filename); + return true; //FIXME - check for error... + } + + uint PolyVoxSceneManager::getSideLength(void) + { + return OGRE_VOLUME_SIDE_LENGTH; + } + + uchar PolyVoxSceneManager::getMaterialIndexAt(uint uX, uint uY, uint uZ) + { + if(volumeData->containsPoint(IntVector3(uX,uY,uZ),0)) + { + VolumeIterator volIter(*volumeData); + return volIter.getVoxelAt(uX,uY,uZ); + } + else + { + return 0; + } + } + + void PolyVoxSceneManager::setNormalGenerationMethod(NormalGenerationMethod method) + { + m_normalGenerationMethod = method; + } + + /*void PolyVoxSceneManager::setMaterialNameForIndex(uchar uIndex, std::string sMaterialName) + { + m_aMaterialNames[uIndex] = sMaterialName; + }*/ + + bool PolyVoxSceneManager::containsPoint(Vector3 pos, float boundary) + { + return volumeData->containsPoint(pos, boundary); + } + + bool PolyVoxSceneManager::containsPoint(IntVector3 pos, uint boundary) + { + return volumeData->containsPoint(pos, boundary); + } + + void PolyVoxSceneManager::igniteVoxel(UIntVector3 voxelToIgnite) + { + m_queueVoxelsToBurn.push(voxelToIgnite); + } +}