polyvox/PolyVoxCore/source/GradientEstimators.cpp

175 lines
7.0 KiB
C++

#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<uint8>* volumeData, RegionGeometry& regGeom, NormalGenerationMethod normalGenerationMethod)
{
std::vector<SurfaceVertex>& vecVertices = regGeom.m_patchSingleMaterial->m_vecVertices;
std::vector<SurfaceVertex>::iterator iterSurfaceVertex = vecVertices.begin();
while(iterSurfaceVertex != vecVertices.end())
{
const Vector3DFloat& v3dPos = iterSurfaceVertex->getPosition() + static_cast<Vector3DFloat>(regGeom.m_v3dRegionPosition);
const Vector3DInt32 v3dFloor = static_cast<Vector3DInt32>(v3dPos);
BlockVolumeIterator<uint8> 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<Vector3DInt16>(v3dFloor));
const Vector3DFloat gradFloor = computeSobelGradient(volIter);
if((v3dPos.getX() - v3dFloor.getX()) > 0.001)
{
volIter.setPosition(static_cast<Vector3DInt16>(v3dFloor+Vector3DInt32(1,0,0)));
}
if((v3dPos.getY() - v3dFloor.getY()) > 0.001)
{
volIter.setPosition(static_cast<Vector3DInt16>(v3dFloor+Vector3DInt32(0,1,0)));
}
if((v3dPos.getZ() - v3dFloor.getZ()) > 0.001)
{
volIter.setPosition(static_cast<Vector3DInt16>(v3dFloor+Vector3DInt32(0,0,1)));
}
const Vector3DFloat gradCeil = computeSobelGradient(volIter);
v3dGradient = (gradFloor + gradCeil);
}
if(normalGenerationMethod == CENTRAL_DIFFERENCE)
{
volIter.setPosition(static_cast<Vector3DInt16>(v3dFloor));
const Vector3DFloat gradFloor = computeCentralDifferenceGradient(volIter);
if((v3dPos.getX() - v3dFloor.getX()) > 0.001)
{
volIter.setPosition(static_cast<Vector3DInt16>(v3dFloor+Vector3DInt32(1,0,0)));
}
if((v3dPos.getY() - v3dFloor.getY()) > 0.001)
{
volIter.setPosition(static_cast<Vector3DInt16>(v3dFloor+Vector3DInt32(0,1,0)));
}
if((v3dPos.getZ() - v3dFloor.getZ()) > 0.001)
{
volIter.setPosition(static_cast<Vector3DInt16>(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<uint8>* 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<uint16>(posX);
const uint16 floorY = static_cast<uint16>(posY);
const uint16 floorZ = static_cast<uint16>(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<uint8> volIter(*volumeData); //FIXME - save this somewhere - could be expensive to create?
if(normalGenerationMethod == SOBEL)
{
volIter.setPosition(static_cast<uint16>(posX),static_cast<uint16>(posY),static_cast<uint16>(posZ));
const Vector3DFloat gradFloor = computeSobelGradient(volIter);
if((posX - floorX) > 0.25) //The result should be 0.0 or 0.5
{
volIter.setPosition(static_cast<uint16>(posX+1.0),static_cast<uint16>(posY),static_cast<uint16>(posZ));
}
if((posY - floorY) > 0.25) //The result should be 0.0 or 0.5
{
volIter.setPosition(static_cast<uint16>(posX),static_cast<uint16>(posY+1.0),static_cast<uint16>(posZ));
}
if((posZ - floorZ) > 0.25) //The result should be 0.0 or 0.5
{
volIter.setPosition(static_cast<uint16>(posX),static_cast<uint16>(posY),static_cast<uint16>(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<uint16>(posX),static_cast<uint16>(posY),static_cast<uint16>(posZ));
const Vector3DFloat gradFloor = computeCentralDifferenceGradient(volIter);
if((posX - floorX) > 0.25) //The result should be 0.0 or 0.5
{
volIter.setPosition(static_cast<uint16>(posX+1.0),static_cast<uint16>(posY),static_cast<uint16>(posZ));
}
if((posY - floorY) > 0.25) //The result should be 0.0 or 0.5
{
volIter.setPosition(static_cast<uint16>(posX),static_cast<uint16>(posY+1.0),static_cast<uint16>(posZ));
}
if((posZ - floorZ) > 0.25) //The result should be 0.0 or 0.5
{
volIter.setPosition(static_cast<uint16>(posX),static_cast<uint16>(posY),static_cast<uint16>(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<uint16>(posX),static_cast<uint16>(posY),static_cast<uint16>(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<float>(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<float>(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<float>(uFloor - uCeil));
}
}
return result;
}
}