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 >
|
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);
|
auto v111Density = controller.convertToDensity(v111);
|
||||||
const Vector3DFloat n000 = computeCentralDifferenceGradient(sampler, controller);
|
const Vector3DFloat n000 = computeCentralDifferenceGradient(sampler, controller);
|
||||||
|
|
||||||
uint16_t uEdge = edgeTable[iCubeIndex];
|
|
||||||
/* Find the vertices where the surface intersects the cube */
|
/* Find the vertices where the surface intersects the cube */
|
||||||
if ((uEdge & 64) && (uXRegSpace > 0))
|
if ((uEdge & 64) && (uXRegSpace > 0))
|
||||||
{
|
{
|
||||||
@ -232,99 +311,6 @@ namespace PolyVox
|
|||||||
}
|
}
|
||||||
} // For each triangle
|
} // 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
|
} // For each cell
|
||||||
sampler.movePositiveX();
|
sampler.movePositiveX();
|
||||||
} // For X
|
} // For X
|
||||||
|
Loading…
x
Reference in New Issue
Block a user