Revert "Split the code which generates vertices and indices for a single cell into a separate function."
This reverts commit 2fa291d16f13adf039dc4fa636bfccde52377989.
This commit is contained in:
parent
942bb37981
commit
96e747d0c3
@ -49,13 +49,92 @@ namespace PolyVox
|
||||
}
|
||||
|
||||
template< typename VolumeType, typename MeshType, typename ControllerType >
|
||||
void generateMeshForCell(Region& region, MeshType* result, ControllerType& controller, typename VolumeType::Sampler& sampler, Array<2, Vector3DInt32>& pIndices, Array<2, Vector3DInt32>& pPreviousIndices, uint8_t iCubeIndex, uint32_t uXRegSpace, uint32_t uYRegSpace, uint32_t uZRegSpace, typename ControllerType::DensityType tThreshold)
|
||||
void extractMarchingCubesMeshCustom(VolumeType* volData, Region region, MeshType* result, ControllerType controller)
|
||||
{
|
||||
POLYVOX_THROW_IF(result == nullptr, std::invalid_argument, "Provided mesh cannot be null");
|
||||
|
||||
Timer timer;
|
||||
result->clear();
|
||||
|
||||
typename ControllerType::DensityType tThreshold = controller.getThreshold();
|
||||
|
||||
const uint32_t uRegionWidthInVoxels = region.getWidthInVoxels();
|
||||
const uint32_t uRegionHeightInVoxels = region.getHeightInVoxels();
|
||||
const uint32_t uRegionDepthInVoxels = region.getDepthInVoxels();
|
||||
|
||||
// No need to clear memory because we only read from elements we have written to.
|
||||
Array<2, Vector3DInt32> pIndices(uRegionWidthInVoxels, uRegionHeightInVoxels);
|
||||
Array<2, Vector3DInt32> pPreviousIndices(uRegionWidthInVoxels, uRegionHeightInVoxels);
|
||||
|
||||
Array2DUint8 pPreviousSliceBitmask(uRegionWidthInVoxels, uRegionHeightInVoxels);
|
||||
Array1DUint8 pPreviousRowBitmask(uRegionWidthInVoxels);
|
||||
|
||||
uint8_t uPreviousCell = 0;
|
||||
|
||||
typename VolumeType::Sampler startOfSlice(volData);
|
||||
startOfSlice.setPosition(region.getLowerX(), region.getLowerY(), region.getLowerZ());
|
||||
|
||||
for (uint32_t uZRegSpace = 0; uZRegSpace < uRegionDepthInVoxels; uZRegSpace++)
|
||||
{
|
||||
typename VolumeType::Sampler startOfRow = startOfSlice;
|
||||
|
||||
for (uint32_t uYRegSpace = 0; uYRegSpace < uRegionHeightInVoxels; uYRegSpace++)
|
||||
{
|
||||
// Copying a sampler which is already pointing at the correct location seems (slightly) faster than
|
||||
// calling setPosition(). Therefore we make use of 'startOfRow' and 'startOfSlice' to reset the sampler.
|
||||
typename VolumeType::Sampler sampler = startOfRow;
|
||||
|
||||
for (uint32_t uXRegSpace = 0; uXRegSpace < uRegionWidthInVoxels; uXRegSpace++)
|
||||
{
|
||||
// Note: In many cases the provided region will be (mostly) empty which means mesh vertices/indices
|
||||
// are not generated and the only thing that is done for each cell is the computation of iCubeIndex.
|
||||
// It appears that retriving the voxel value is not so expensive and that it is the bitwise combining
|
||||
// which actually carries the cost.
|
||||
//
|
||||
// If we really need to speed this up more then it may be possible to pack 4 8-bit cell indices into
|
||||
// a single 32-bit value and then perform the bitwise logic on all four of them at the same time.
|
||||
// However, this complicates the code and there would still be the cost of packing/unpacking so it's
|
||||
// not clear if there is really a benefit. It's something to consider in the future.
|
||||
|
||||
uint8_t iCubeIndex = 0;
|
||||
|
||||
// Four bits of our cube index are obtained by looking at the cube index for
|
||||
// the previous slice and copying four of those bits into their new positions.
|
||||
uint8_t iPreviousCubeIndexZ = pPreviousSliceBitmask(uXRegSpace, uYRegSpace);
|
||||
iPreviousCubeIndexZ >>= 4;
|
||||
iCubeIndex |= iPreviousCubeIndexZ;
|
||||
|
||||
// Two bits of our cube index are obtained by looking at the cube index for
|
||||
// the previous row and copying two of those bits into their new positions.
|
||||
uint8_t iPreviousCubeIndexY = pPreviousRowBitmask(uXRegSpace);
|
||||
iPreviousCubeIndexY &= 204; //204 = 128+64+8+4
|
||||
iPreviousCubeIndexY >>= 2;
|
||||
iCubeIndex |= iPreviousCubeIndexY;
|
||||
|
||||
// One bit of our cube index are obtained by looking at the cube index for
|
||||
// the previous cell and copying one of those bits into it's new position.
|
||||
uint8_t iPreviousCubeIndexX = uPreviousCell;
|
||||
iPreviousCubeIndexX &= 170; //170 = 128+32+8+2
|
||||
iPreviousCubeIndexX >>= 1;
|
||||
iCubeIndex |= iPreviousCubeIndexX;
|
||||
|
||||
// The last bit of our cube index is obtained by looking
|
||||
// at the relevant voxel and comparing it to the threshold
|
||||
typename VolumeType::VoxelType v111 = sampler.getVoxel();
|
||||
if (controller.convertToDensity(v111) < tThreshold) iCubeIndex |= 128;
|
||||
|
||||
// The current value becomes the previous value, ready for the next iteration.
|
||||
uPreviousCell = iCubeIndex;
|
||||
pPreviousRowBitmask(uXRegSpace) = iCubeIndex;
|
||||
pPreviousSliceBitmask(uXRegSpace, uYRegSpace) = iCubeIndex;
|
||||
|
||||
/* Cube is entirely in/out of the surface */
|
||||
uint16_t uEdge = edgeTable[iCubeIndex];
|
||||
if (uEdge != 0)
|
||||
{
|
||||
auto v111 = sampler.getVoxel();
|
||||
auto v111Density = controller.convertToDensity(v111);
|
||||
const Vector3DFloat n000 = computeCentralDifferenceGradient(sampler, controller);
|
||||
|
||||
uint16_t uEdge = edgeTable[iCubeIndex];
|
||||
/* Find the vertices where the surface intersects the cube */
|
||||
if ((uEdge & 64) && (uXRegSpace > 0))
|
||||
{
|
||||
@ -232,99 +311,6 @@ namespace PolyVox
|
||||
}
|
||||
} // For each triangle
|
||||
}
|
||||
}
|
||||
|
||||
template< typename VolumeType, typename MeshType, typename ControllerType >
|
||||
void extractMarchingCubesMeshCustom(VolumeType* volData, Region region, MeshType* result, ControllerType controller)
|
||||
{
|
||||
POLYVOX_THROW_IF(result == nullptr, std::invalid_argument, "Provided mesh cannot be null");
|
||||
|
||||
Timer timer;
|
||||
result->clear();
|
||||
|
||||
typename ControllerType::DensityType tThreshold = controller.getThreshold();
|
||||
|
||||
const uint32_t uRegionWidthInVoxels = region.getWidthInVoxels();
|
||||
const uint32_t uRegionHeightInVoxels = region.getHeightInVoxels();
|
||||
const uint32_t uRegionDepthInVoxels = region.getDepthInVoxels();
|
||||
|
||||
// No need to clear memory because we only read from elements we have written to.
|
||||
Array<2, Vector3DInt32> pIndices(uRegionWidthInVoxels, uRegionHeightInVoxels);
|
||||
Array<2, Vector3DInt32> pPreviousIndices(uRegionWidthInVoxels, uRegionHeightInVoxels);
|
||||
|
||||
Array2DUint8 pPreviousSliceBitmask(uRegionWidthInVoxels, uRegionHeightInVoxels);
|
||||
Array1DUint8 pPreviousRowBitmask(uRegionWidthInVoxels);
|
||||
|
||||
uint8_t uPreviousCell = 0;
|
||||
|
||||
typename VolumeType::Sampler startOfSlice(volData);
|
||||
startOfSlice.setPosition(region.getLowerX(), region.getLowerY(), region.getLowerZ());
|
||||
|
||||
for (uint32_t uZRegSpace = 0; uZRegSpace < uRegionDepthInVoxels; uZRegSpace++)
|
||||
{
|
||||
typename VolumeType::Sampler startOfRow = startOfSlice;
|
||||
|
||||
for (uint32_t uYRegSpace = 0; uYRegSpace < uRegionHeightInVoxels; uYRegSpace++)
|
||||
{
|
||||
// Copying a sampler which is already pointing at the correct location seems (slightly) faster than
|
||||
// calling setPosition(). Therefore we make use of 'startOfRow' and 'startOfSlice' to reset the sampler.
|
||||
typename VolumeType::Sampler sampler = startOfRow;
|
||||
|
||||
for (uint32_t uXRegSpace = 0; uXRegSpace < uRegionWidthInVoxels; uXRegSpace++)
|
||||
{
|
||||
// Note: In many cases the provided region will be (mostly) empty which means mesh vertices/indices
|
||||
// are not generated and the only thing that is done for each cell is the computation of iCubeIndex.
|
||||
// It appears that retriving the voxel value is not so expensive and that it is the bitwise combining
|
||||
// which actually carries the cost.
|
||||
//
|
||||
// If we really need to speed this up more then it may be possible to pack 4 8-bit cell indices into
|
||||
// a single 32-bit value and then perform the bitwise logic on all four of them at the same time.
|
||||
// However, this complicates the code and there would still be the cost of packing/unpacking so it's
|
||||
// not clear if there is really a benefit. It's something to consider in the future.
|
||||
|
||||
uint8_t iCubeIndex = 0;
|
||||
|
||||
// Four bits of our cube index are obtained by looking at the cube index for
|
||||
// the previous slice and copying four of those bits into their new positions.
|
||||
uint8_t iPreviousCubeIndexZ = pPreviousSliceBitmask(uXRegSpace, uYRegSpace);
|
||||
iPreviousCubeIndexZ >>= 4;
|
||||
iCubeIndex |= iPreviousCubeIndexZ;
|
||||
|
||||
// Two bits of our cube index are obtained by looking at the cube index for
|
||||
// the previous row and copying two of those bits into their new positions.
|
||||
uint8_t iPreviousCubeIndexY = pPreviousRowBitmask(uXRegSpace);
|
||||
iPreviousCubeIndexY &= 204; //204 = 128+64+8+4
|
||||
iPreviousCubeIndexY >>= 2;
|
||||
iCubeIndex |= iPreviousCubeIndexY;
|
||||
|
||||
// One bit of our cube index are obtained by looking at the cube index for
|
||||
// the previous cell and copying one of those bits into it's new position.
|
||||
uint8_t iPreviousCubeIndexX = uPreviousCell;
|
||||
iPreviousCubeIndexX &= 170; //170 = 128+32+8+2
|
||||
iPreviousCubeIndexX >>= 1;
|
||||
iCubeIndex |= iPreviousCubeIndexX;
|
||||
|
||||
// The last bit of our cube index is obtained by looking
|
||||
// at the relevant voxel and comparing it to the threshold
|
||||
typename VolumeType::VoxelType v111 = sampler.getVoxel();
|
||||
if (controller.convertToDensity(v111) < tThreshold) iCubeIndex |= 128;
|
||||
|
||||
// The current value becomes the previous value, ready for the next iteration.
|
||||
uPreviousCell = iCubeIndex;
|
||||
pPreviousRowBitmask(uXRegSpace) = iCubeIndex;
|
||||
pPreviousSliceBitmask(uXRegSpace, uYRegSpace) = iCubeIndex;
|
||||
|
||||
/* Cube is entirely in/out of the surface */
|
||||
if (edgeTable[iCubeIndex] != 0)
|
||||
{
|
||||
// This is a rather ugly function call and appears to have some cost compared to inlining the code.
|
||||
// As a result the case when a cell contains vertices/indices is slightly slower, but the (more common)
|
||||
// case where a cell is empty is slightly faster, probably because the main loop is a lot more compact.
|
||||
// Having a seperate function will also make it easier to profile in the future and see whether empty or
|
||||
// occupied cells are really the bottleneck. The large number of parameters is messy though, so it
|
||||
// would be nice to reduce these if we can work out how.
|
||||
generateMeshForCell<VolumeType, MeshType, ControllerType>(region, result, controller,
|
||||
sampler, pIndices, pPreviousIndices, iCubeIndex, uXRegSpace, uYRegSpace, uZRegSpace, tThreshold);
|
||||
} // For each cell
|
||||
sampler.movePositiveX();
|
||||
} // For X
|
||||
|
Loading…
x
Reference in New Issue
Block a user