#include "GradientEstimators.h" #include "IndexedSurfacePatch.h" #include "RegionGeometry.h" #include "SurfaceVertex.h" #include "PolyVoxCStdInt.h" using namespace std; namespace PolyVox { POLYVOX_API void computeNormalsForVertices(BlockVolume* volumeData, RegionGeometry& regGeom, NormalGenerationMethod normalGenerationMethod) { std::vector& vecVertices = regGeom.m_patchSingleMaterial->m_vecVertices; std::vector::iterator iterSurfaceVertex = vecVertices.begin(); while(iterSurfaceVertex != vecVertices.end()) { const Vector3DFloat& v3dPos = iterSurfaceVertex->getPosition() + static_cast(regGeom.m_v3dRegionPosition); const Vector3DInt32 v3dFloor = static_cast(v3dPos); BlockVolumeIterator volIter(*volumeData); //Check all corners are within the volume, allowing a boundary for gradient estimation bool lowerCornerInside = volumeData->containsPoint(v3dFloor,1); bool upperCornerInside = volumeData->containsPoint(v3dFloor+Vector3DInt32(1,1,1),1); if(lowerCornerInside && upperCornerInside) //If this test fails the vertex will be left as it was { Vector3DFloat v3dGradient; //To store the result if(normalGenerationMethod == SOBEL) { volIter.setPosition(static_cast(v3dFloor)); const Vector3DFloat gradFloor = computeSobelGradient(volIter); if((v3dPos.getX() - v3dFloor.getX()) > 0.001) { volIter.setPosition(static_cast(v3dFloor+Vector3DInt32(1,0,0))); } if((v3dPos.getY() - v3dFloor.getY()) > 0.001) { volIter.setPosition(static_cast(v3dFloor+Vector3DInt32(0,1,0))); } if((v3dPos.getZ() - v3dFloor.getZ()) > 0.001) { volIter.setPosition(static_cast(v3dFloor+Vector3DInt32(0,0,1))); } const Vector3DFloat gradCeil = computeSobelGradient(volIter); v3dGradient = (gradFloor + gradCeil); } if(normalGenerationMethod == CENTRAL_DIFFERENCE) { volIter.setPosition(static_cast(v3dFloor)); const Vector3DFloat gradFloor = computeCentralDifferenceGradient(volIter); if((v3dPos.getX() - v3dFloor.getX()) > 0.001) { volIter.setPosition(static_cast(v3dFloor+Vector3DInt32(1,0,0))); } if((v3dPos.getY() - v3dFloor.getY()) > 0.001) { volIter.setPosition(static_cast(v3dFloor+Vector3DInt32(0,1,0))); } if((v3dPos.getZ() - v3dFloor.getZ()) > 0.001) { volIter.setPosition(static_cast(v3dFloor+Vector3DInt32(0,0,1))); } const Vector3DFloat gradCeil = computeCentralDifferenceGradient(volIter); v3dGradient = (gradFloor + gradCeil); } if(v3dGradient.lengthSquared() > 0.0001) { //If we got a normal of significant length then update it. //Otherwise leave it as it was (should be the 'simple' version) v3dGradient.normalise(); iterSurfaceVertex->setNormal(v3dGradient); } } //(lowerCornerInside && upperCornerInside) ++iterSurfaceVertex; } } Vector3DFloat computeNormal(BlockVolume* volumeData, const Vector3DFloat& position, NormalGenerationMethod normalGenerationMethod) { const float posX = position.getX(); const float posY = position.getY(); const float posZ = position.getZ(); const uint16 floorX = static_cast(posX); const uint16 floorY = static_cast(posY); const uint16 floorZ = static_cast(posZ); //Check all corners are within the volume, allowing a boundary for gradient estimation bool lowerCornerInside = volumeData->containsPoint(Vector3DInt32(floorX, floorY, floorZ),1); bool upperCornerInside = volumeData->containsPoint(Vector3DInt32(floorX+1, floorY+1, floorZ+1),1); if((!lowerCornerInside) || (!upperCornerInside)) { normalGenerationMethod = SIMPLE; } Vector3DFloat result; BlockVolumeIterator volIter(*volumeData); //FIXME - save this somewhere - could be expensive to create? if(normalGenerationMethod == SOBEL) { volIter.setPosition(static_cast(posX),static_cast(posY),static_cast(posZ)); const Vector3DFloat gradFloor = computeSobelGradient(volIter); 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 Vector3DFloat gradCeil = computeSobelGradient(volIter); result = ((gradFloor + gradCeil) * -1.0f); if(result.lengthSquared() < 0.0001) { //Operation failed - fall back on simple gradient estimation normalGenerationMethod = SIMPLE; } } if(normalGenerationMethod == CENTRAL_DIFFERENCE) { volIter.setPosition(static_cast(posX),static_cast(posY),static_cast(posZ)); const Vector3DFloat gradFloor = computeCentralDifferenceGradient(volIter); 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 Vector3DFloat gradCeil = computeCentralDifferenceGradient(volIter); result = ((gradFloor + gradCeil) * -1.0f); if(result.lengthSquared() < 0.0001) { //Operation failed - fall back on simple gradient estimation normalGenerationMethod = SIMPLE; } } if(normalGenerationMethod == SIMPLE) { volIter.setPosition(static_cast(posX),static_cast(posY),static_cast(posZ)); const uint8 uFloor = volIter.getVoxel() > 0 ? 1 : 0; if((posX - floorX) > 0.25) //The result should be 0.0 or 0.5 { uint8 uCeil = volIter.peekVoxel1px0py0pz() > 0 ? 1 : 0; result = Vector3DFloat(static_cast(uFloor - uCeil),0.0,0.0); } else if((posY - floorY) > 0.25) //The result should be 0.0 or 0.5 { uint8 uCeil = volIter.peekVoxel0px1py0pz() > 0 ? 1 : 0; result = Vector3DFloat(0.0,static_cast(uFloor - uCeil),0.0); } else if((posZ - floorZ) > 0.25) //The result should be 0.0 or 0.5 { uint8 uCeil = volIter.peekVoxel0px0py1pz() > 0 ? 1 : 0; result = Vector3DFloat(0.0, 0.0,static_cast(uFloor - uCeil)); } } return result; } }