diff --git a/library/include/PolyVoxCore/Enums.h b/library/include/PolyVoxCore/Enums.h index fd2e8722..6ca71e39 100644 --- a/library/include/PolyVoxCore/Enums.h +++ b/library/include/PolyVoxCore/Enums.h @@ -26,9 +26,11 @@ namespace PolyVox { enum NormalGenerationMethod { - SIMPLE, + SIMPLE, //Fastest CENTRAL_DIFFERENCE, - SOBEL + SOBEL, + CENTRAL_DIFFERENCE_SMOOTHED, + SOBEL_SMOOTHED //Smoothest }; } diff --git a/library/include/PolyVoxCore/GradientEstimators.h b/library/include/PolyVoxCore/GradientEstimators.h index 07cbdd3e..11bb77d0 100644 --- a/library/include/PolyVoxCore/GradientEstimators.h +++ b/library/include/PolyVoxCore/GradientEstimators.h @@ -39,9 +39,11 @@ namespace PolyVox template Vector3DFloat computeSobelGradient(const BlockVolumeIterator& volIter); + template + Vector3DFloat computeSmoothSobelGradient(BlockVolumeIterator& volIter); POLYVOX_API void computeNormalsForVertices(BlockVolume* volumeData, IndexedSurfacePatch& isp, NormalGenerationMethod normalGenerationMethod); - POLYVOX_API Vector3DFloat computeNormal(BlockVolume* volumeData, const Vector3DFloat& position, NormalGenerationMethod normalGenerationMethod); + POLYVOX_API Vector3DFloat computeNormal(BlockVolume* volumeData, const Vector3DFloat& v3dPos, NormalGenerationMethod normalGenerationMethod); } #include "GradientEstimators.inl" diff --git a/library/include/PolyVoxCore/GradientEstimators.inl b/library/include/PolyVoxCore/GradientEstimators.inl index 333f79b0..204d83de 100644 --- a/library/include/PolyVoxCore/GradientEstimators.inl +++ b/library/include/PolyVoxCore/GradientEstimators.inl @@ -182,4 +182,121 @@ namespace PolyVox //For our normals we want the the other way around, so we switch the components as we return them. return Vector3DFloat(static_cast(-xGrad),static_cast(-yGrad),static_cast(-zGrad)); } + + template + Vector3DFloat computeSmoothSobelGradient(BlockVolumeIterator& volIter) + { + static const int weights[3][3][3] = { { {2,3,2}, {3,6,3}, {2,3,2} }, { + {3,6,3}, {6,0,6}, {3,6,3} }, { {2,3,2}, {3,6,3}, {2,3,2} } }; + + uint16 initialX = volIter.getPosX(); + uint16 initialY = volIter.getPosY(); + uint16 initialZ = volIter.getPosZ(); + + volIter.setPosition(initialX-1, initialY-1, initialZ-1); const float pVoxel1nx1ny1nz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX-1, initialY-1, initialZ ); const float pVoxel1nx1ny0pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX-1, initialY-1, initialZ+1); const float pVoxel1nx1ny1pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX-1, initialY , initialZ-1); const float pVoxel1nx0py1nz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX-1, initialY , initialZ ); const float pVoxel1nx0py0pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX-1, initialY , initialZ+1); const float pVoxel1nx0py1pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX-1, initialY+1, initialZ-1); const float pVoxel1nx1py1nz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX-1, initialY+1, initialZ ); const float pVoxel1nx1py0pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX-1, initialY+1, initialZ+1); const float pVoxel1nx1py1pz = computeSmoothedVoxel(volIter); + + volIter.setPosition(initialX , initialY-1, initialZ-1); const float pVoxel0px1ny1nz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX , initialY-1, initialZ ); const float pVoxel0px1ny0pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX , initialY-1, initialZ+1); const float pVoxel0px1ny1pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX , initialY , initialZ-1); const float pVoxel0px0py1nz = computeSmoothedVoxel(volIter); + //volIter.setPosition(initialX , initialY , initialZ ); const float pVoxel0px0py0pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX , initialY , initialZ+1); const float pVoxel0px0py1pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX , initialY+1, initialZ-1); const float pVoxel0px1py1nz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX , initialY+1, initialZ ); const float pVoxel0px1py0pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX , initialY+1, initialZ+1); const float pVoxel0px1py1pz = computeSmoothedVoxel(volIter); + + volIter.setPosition(initialX+1, initialY-1, initialZ-1); const float pVoxel1px1ny1nz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX+1, initialY-1, initialZ ); const float pVoxel1px1ny0pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX+1, initialY-1, initialZ+1); const float pVoxel1px1ny1pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX+1, initialY , initialZ-1); const float pVoxel1px0py1nz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX+1, initialY , initialZ ); const float pVoxel1px0py0pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX+1, initialY , initialZ+1); const float pVoxel1px0py1pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX+1, initialY+1, initialZ-1); const float pVoxel1px1py1nz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX+1, initialY+1, initialZ ); const float pVoxel1px1py0pz = computeSmoothedVoxel(volIter); + volIter.setPosition(initialX+1, initialY+1, initialZ+1); const float pVoxel1px1py1pz = computeSmoothedVoxel(volIter); + + /*const VoxelType pVoxel1nx1ny1nz = volIter.peekVoxel1nx1ny1nz() > 0 ? 1: 0; + const VoxelType pVoxel1nx1ny0pz = volIter.peekVoxel1nx1ny0pz() > 0 ? 1: 0; + const VoxelType pVoxel1nx1ny1pz = volIter.peekVoxel1nx1ny1pz() > 0 ? 1: 0; + const VoxelType pVoxel1nx0py1nz = volIter.peekVoxel1nx0py1nz() > 0 ? 1: 0; + const VoxelType pVoxel1nx0py0pz = volIter.peekVoxel1nx0py0pz() > 0 ? 1: 0; + const VoxelType pVoxel1nx0py1pz = volIter.peekVoxel1nx0py1pz() > 0 ? 1: 0; + const VoxelType pVoxel1nx1py1nz = volIter.peekVoxel1nx1py1nz() > 0 ? 1: 0; + const VoxelType pVoxel1nx1py0pz = volIter.peekVoxel1nx1py0pz() > 0 ? 1: 0; + const VoxelType pVoxel1nx1py1pz = volIter.peekVoxel1nx1py1pz() > 0 ? 1: 0; + + const VoxelType pVoxel0px1ny1nz = volIter.peekVoxel0px1ny1nz() > 0 ? 1: 0; + const VoxelType pVoxel0px1ny0pz = volIter.peekVoxel0px1ny0pz() > 0 ? 1: 0; + const VoxelType pVoxel0px1ny1pz = volIter.peekVoxel0px1ny1pz() > 0 ? 1: 0; + const VoxelType pVoxel0px0py1nz = volIter.peekVoxel0px0py1nz() > 0 ? 1: 0; + //const VoxelType pVoxel0px0py0pz = volIter.peekVoxel0px0py0pz() > 0 ? 1: 0; + const VoxelType pVoxel0px0py1pz = volIter.peekVoxel0px0py1pz() > 0 ? 1: 0; + const VoxelType pVoxel0px1py1nz = volIter.peekVoxel0px1py1nz() > 0 ? 1: 0; + const VoxelType pVoxel0px1py0pz = volIter.peekVoxel0px1py0pz() > 0 ? 1: 0; + const VoxelType pVoxel0px1py1pz = volIter.peekVoxel0px1py1pz() > 0 ? 1: 0; + + const VoxelType pVoxel1px1ny1nz = volIter.peekVoxel1px1ny1nz() > 0 ? 1: 0; + const VoxelType pVoxel1px1ny0pz = volIter.peekVoxel1px1ny0pz() > 0 ? 1: 0; + const VoxelType pVoxel1px1ny1pz = volIter.peekVoxel1px1ny1pz() > 0 ? 1: 0; + const VoxelType pVoxel1px0py1nz = volIter.peekVoxel1px0py1nz() > 0 ? 1: 0; + const VoxelType pVoxel1px0py0pz = volIter.peekVoxel1px0py0pz() > 0 ? 1: 0; + const VoxelType pVoxel1px0py1pz = volIter.peekVoxel1px0py1pz() > 0 ? 1: 0; + const VoxelType pVoxel1px1py1nz = volIter.peekVoxel1px1py1nz() > 0 ? 1: 0; + const VoxelType pVoxel1px1py0pz = volIter.peekVoxel1px1py0pz() > 0 ? 1: 0; + const VoxelType pVoxel1px1py1pz = volIter.peekVoxel1px1py1pz() > 0 ? 1: 0;*/ + + const float xGrad(- weights[0][0][0] * pVoxel1nx1ny1nz - + weights[1][0][0] * pVoxel1nx1ny0pz - weights[2][0][0] * + pVoxel1nx1ny1pz - weights[0][1][0] * pVoxel1nx0py1nz - + weights[1][1][0] * pVoxel1nx0py0pz - weights[2][1][0] * + pVoxel1nx0py1pz - weights[0][2][0] * pVoxel1nx1py1nz - + weights[1][2][0] * pVoxel1nx1py0pz - weights[2][2][0] * + pVoxel1nx1py1pz + weights[0][0][2] * pVoxel1px1ny1nz + + weights[1][0][2] * pVoxel1px1ny0pz + weights[2][0][2] * + pVoxel1px1ny1pz + weights[0][1][2] * pVoxel1px0py1nz + + weights[1][1][2] * pVoxel1px0py0pz + weights[2][1][2] * + pVoxel1px0py1pz + weights[0][2][2] * pVoxel1px1py1nz + + weights[1][2][2] * pVoxel1px1py0pz + weights[2][2][2] * + pVoxel1px1py1pz); + + const float yGrad(- weights[0][0][0] * pVoxel1nx1ny1nz - + weights[1][0][0] * pVoxel1nx1ny0pz - weights[2][0][0] * + pVoxel1nx1ny1pz + weights[0][2][0] * pVoxel1nx1py1nz + + weights[1][2][0] * pVoxel1nx1py0pz + weights[2][2][0] * + pVoxel1nx1py1pz - weights[0][0][1] * pVoxel0px1ny1nz - + weights[1][0][1] * pVoxel0px1ny0pz - weights[2][0][1] * + pVoxel0px1ny1pz + weights[0][2][1] * pVoxel0px1py1nz + + weights[1][2][1] * pVoxel0px1py0pz + weights[2][2][1] * + pVoxel0px1py1pz - weights[0][0][2] * pVoxel1px1ny1nz - + weights[1][0][2] * pVoxel1px1ny0pz - weights[2][0][2] * + pVoxel1px1ny1pz + weights[0][2][2] * pVoxel1px1py1nz + + weights[1][2][2] * pVoxel1px1py0pz + weights[2][2][2] * + pVoxel1px1py1pz); + + const float zGrad(- weights[0][0][0] * pVoxel1nx1ny1nz + + weights[2][0][0] * pVoxel1nx1ny1pz - weights[0][1][0] * + pVoxel1nx0py1nz + weights[2][1][0] * pVoxel1nx0py1pz - + weights[0][2][0] * pVoxel1nx1py1nz + weights[2][2][0] * + pVoxel1nx1py1pz - weights[0][0][1] * pVoxel0px1ny1nz + + weights[2][0][1] * pVoxel0px1ny1pz - weights[0][1][1] * + pVoxel0px0py1nz + weights[2][1][1] * pVoxel0px0py1pz - + weights[0][2][1] * pVoxel0px1py1nz + weights[2][2][1] * + pVoxel0px1py1pz - weights[0][0][2] * pVoxel1px1ny1nz + + weights[2][0][2] * pVoxel1px1ny1pz - weights[0][1][2] * + pVoxel1px0py1nz + weights[2][1][2] * pVoxel1px0py1pz - + weights[0][2][2] * pVoxel1px1py1nz + weights[2][2][2] * + pVoxel1px1py1pz); + + //Note: The above actually give gradients going from low density to high density. + //For our normals we want the the other way around, so we switch the components as we return them. + return Vector3DFloat(-xGrad,-yGrad,-zGrad); + } } diff --git a/library/source/PolyVoxCore/GradientEstimators.cpp b/library/source/PolyVoxCore/GradientEstimators.cpp index d5b1631a..d68becb4 100644 --- a/library/source/PolyVoxCore/GradientEstimators.cpp +++ b/library/source/PolyVoxCore/GradientEstimators.cpp @@ -20,51 +20,13 @@ namespace PolyVox 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); + bool lowerCornerInside = volumeData->containsPoint(v3dFloor,2); + bool upperCornerInside = volumeData->containsPoint(v3dFloor+Vector3DInt32(1,1,1),2); 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); - } + Vector3DFloat v3dGradient = computeNormal(volumeData, v3dPos, normalGenerationMethod); + if(v3dGradient.lengthSquared() > 0.0001) { //If we got a normal of significant length then update it. @@ -77,97 +39,89 @@ namespace PolyVox } } - Vector3DFloat computeNormal(BlockVolume* volumeData, const Vector3DFloat& position, NormalGenerationMethod normalGenerationMethod) + Vector3DFloat computeNormal(BlockVolume* volumeData, const Vector3DFloat& v3dPos, NormalGenerationMethod normalGenerationMethod) { - const float posX = position.getX(); - const float posY = position.getY(); - const float posZ = position.getZ(); + Vector3DFloat v3dGradient; //To store the result - const uint16 floorX = static_cast(posX); - const uint16 floorY = static_cast(posY); - const uint16 floorZ = static_cast(posZ); + BlockVolumeIterator volIter(*volumeData); - //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; - } + const Vector3DInt32 v3dFloor = static_cast(v3dPos); - 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)); + volIter.setPosition(static_cast(v3dFloor)); + Vector3DFloat gradFloor; + switch(normalGenerationMethod) + { + case SOBEL_SMOOTHED: + gradFloor = computeSmoothSobelGradient(volIter); + break; + case CENTRAL_DIFFERENCE_SMOOTHED: + gradFloor = computeSmoothCentralDifferenceGradient(volIter); + break; + case SOBEL: + gradFloor = computeSobelGradient(volIter); + break; + case CENTRAL_DIFFERENCE: + gradFloor = computeCentralDifferenceGradient(volIter); + break; } - if((posY - floorY) > 0.25) //The result should be 0.0 or 0.5 + + if((v3dPos.getX() - v3dFloor.getX()) > 0.25) //The result should be 0.0 or 0.5 { - volIter.setPosition(static_cast(posX),static_cast(posY+1.0),static_cast(posZ)); + volIter.setPosition(static_cast(v3dFloor+Vector3DInt32(1,0,0))); } - if((posZ - floorZ) > 0.25) //The result should be 0.0 or 0.5 + if((v3dPos.getY() - v3dFloor.getY()) > 0.25) //The result should be 0.0 or 0.5 { - volIter.setPosition(static_cast(posX),static_cast(posY),static_cast(posZ+1.0)); + volIter.setPosition(static_cast(v3dFloor+Vector3DInt32(0,1,0))); } - const Vector3DFloat gradCeil = computeSobelGradient(volIter); - result = ((gradFloor + gradCeil) * -1.0f); - if(result.lengthSquared() < 0.0001) + if((v3dPos.getZ() - v3dFloor.getZ()) > 0.25) //The result should be 0.0 or 0.5 + { + volIter.setPosition(static_cast(v3dFloor+Vector3DInt32(0,0,1))); + } + + Vector3DFloat gradCeil; + switch(normalGenerationMethod) + { + case SOBEL_SMOOTHED: + gradCeil = computeSmoothSobelGradient(volIter); + break; + case CENTRAL_DIFFERENCE_SMOOTHED: + gradCeil = computeSmoothCentralDifferenceGradient(volIter); + break; + case SOBEL: + gradCeil = computeSobelGradient(volIter); + break; + case CENTRAL_DIFFERENCE: + gradCeil = computeCentralDifferenceGradient(volIter); + break; + } + + v3dGradient = (gradFloor + gradCeil); + if(v3dGradient.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) + + if(normalGenerationMethod == SIMPLE) { - //Operation failed - fall back on simple gradient estimation - normalGenerationMethod = SIMPLE; + volIter.setPosition(static_cast(v3dFloor)); + const uint8 uFloor = volIter.getVoxel() > 0 ? 1 : 0; + if((v3dPos.getX() - v3dFloor.getX()) > 0.25) //The result should be 0.0 or 0.5 + { + uint8 uCeil = volIter.peekVoxel1px0py0pz() > 0 ? 1 : 0; + v3dGradient = Vector3DFloat(static_cast(uFloor - uCeil),0.0,0.0); + } + else if((v3dPos.getY() - v3dFloor.getY()) > 0.25) //The result should be 0.0 or 0.5 + { + uint8 uCeil = volIter.peekVoxel0px1py0pz() > 0 ? 1 : 0; + v3dGradient = Vector3DFloat(0.0,static_cast(uFloor - uCeil),0.0); + } + else if((v3dPos.getZ() - v3dFloor.getZ()) > 0.25) //The result should be 0.0 or 0.5 + { + uint8 uCeil = volIter.peekVoxel0px0py1pz() > 0 ? 1 : 0; + v3dGradient = Vector3DFloat(0.0, 0.0,static_cast(uFloor - uCeil)); + } } - } - 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; + return v3dGradient; } }