Another bug
Now that I am finally working on using multiple materials, I have noticed decimating a mesh after using extractSubset would result in big holes in the meshes. I have fixed this myself, but it is just a straight hack, so rather than posting a solution I will just outline what I did to fix it and hopefully that can point you in the right direction to fix the real problem.
Firstly, the main location of the problem seems to be:
Code:
MeshDecimator.cpp
template<>
POLYVOXCORE_API void MeshDecimator<PositionMaterial>::fillInitialVertexMetadata(std::vector<InitialVertexMetadata>& vecVertexMetadata)
{
...
//Find neighbours which are duplicates.
for(int ct = 0; ct < intVertices.size() - 1; ct++)
{
const IntVertex& v0 = intVertices[ct+0];
const IntVertex& v1 = intVertices[ct+1];
if((v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z))
{
vecVertexMetadata[v0.index].isOnMaterialEdge = true;
vecVertexMetadata[v1.index].isOnMaterialEdge = true;
}
}
...
}
The issue being that many duplicates are NOT being detected using the above code, even after sorting.
My work around was to store duplication data directly in the vertex itself:
Code:
VertexTypes.cpp
#ifdef SWIG
class PositionMaterial
#else
class POLYVOXCORE_API PositionMaterial
#endif
{
public:
PositionMaterial();
//PositionMaterial(Vector3DFloat positionToSet, float materialToSet);
PositionMaterial(Vector3DFloat positionToSet, uint8_t materialToSet); //--! DefiniteIntegral : change to uint8_t material
PositionMaterial(Vector3DFloat positionToSet, uint8_t materialToSet, bool isMaterialDuplicate); //--! DefiniteIntegral : add material duplicate flag
//float getMaterial(void) const;
uint8_t getMaterial(void) const; //--! DefiniteIntegral: changed to uint8_t
const Vector3DFloat& getPosition(void) const;
//void setMaterial(float materialToSet);
void setMaterial(uint8_t materialToSet); //--! DefiniteIntegral: changed to uint8_t
void setPosition(const Vector3DFloat& positionToSet);
public:
//Nicely fits into four floats. //--! DefiniteIntegral : not anymore
Vector3DFloat position;
//float material;
uint8_t material; //--! DefiniteIntegral: changed to uint8_t to remove compiler warnings, not sure why float on CPU anyway
bool mIsMaterialDuplicate; //--! DefiniteIntegral: whether this vertex was added as a duplicate position with a different materialID or not
};
Noted above I have also changed material from float to uint8_t because the compiler warnings annoy me, but that is unrelated. Also I am using a bool for mIsMaterialDuplicate but really it should be a uint8_t to save memory space...
This required a change to the CubicSurfaceExtractor (no normals) to make use of the above flags. I have not bothered to alter extractors for cubic with normals, or marching cubes.
Code:
CubicSurfaceExtractor.inl
template <typename VoxelType>
int32_t CubicSurfaceExtractor<VoxelType>::addVertex(float fX, float fY, float fZ, uint8_t uMaterialIn, Array<3, IndexAndMaterial>& existingVertices)
{
uint32_t uX = static_cast<uint32_t>(fX + 0.75f);
uint32_t uY = static_cast<uint32_t>(fY + 0.75f);
for(uint32_t ct = 0; ct < MaxQuadsSharingVertex; ct++)
{
IndexAndMaterial& rEntry = existingVertices[uX][uY][ct];
int32_t iIndex = static_cast<int32_t>(rEntry.iIndex);
uint8_t uMaterial = static_cast<uint8_t>(rEntry.uMaterial);
//If we have an existing vertex and the material matches then we can return it.
if((iIndex != -1) && (uMaterial == uMaterialIn))
{
return iIndex;
}
else
{
uint32_t temp;
//--! DefiniteIntegral: check for existing vertex
if(iIndex == -1)
{
// index is -1, so this is a brand new vertex for this position, no duplicate. so create vertex with duplication flag set FALSE
temp = m_meshCurrent->addVertex(PositionMaterial(Vector3DFloat(fX, fY, fZ), uMaterialIn, false));
}
else
{
// alert previously added vertex that it is now set as a duplicate also
m_meshCurrent->getRawVertexData()[iIndex].mIsMaterialDuplicate = true;
// index is NOT -1, so vertex as this position already exists, but has a different material, so set duplication flag TRUE
temp = m_meshCurrent->addVertex(PositionMaterial(Vector3DFloat(fX, fY, fZ), uMaterialIn, true));
}
//Note - Slightly dodgy casting taking place here. No proper way to convert to 24-bit int though?
//If problematic in future then fix IndexAndMaterial to contain variables rather than bitfield.
rEntry.iIndex = temp;
rEntry.uMaterial = uMaterialIn;
return temp;
}
}
//If we exit the loop here then apparently all the slots were full but none of
//them matched. I don't think this can happen so let's put an assert to make sure.
assert(false);
return 0;
}
Finally, a change to the MeshDecimator duplicate checking code to examine the flag set above
Code:
MeshDecimator.cpp
template<>
POLYVOXCORE_API void MeshDecimator<PositionMaterial>::fillInitialVertexMetadata(std::vector<InitialVertexMetadata>& vecVertexMetadata)
{
...
//Find neighbours which are duplicates.
for(int ct = 0; ct < intVertices.size() - 1; ct++)
{
const IntVertex& v0 = intVertices[ct+0];
const IntVertex& v1 = intVertices[ct+1];
//--! DefiniteIntegral: leaving this here anyway, though probably redundant now
if((v0.x == v1.x) && (v0.y == v1.y) && (v0.z == v1.z))
{
vecVertexMetadata[v0.index].isOnMaterialEdge = true;
vecVertexMetadata[v1.index].isOnMaterialEdge = true;
}
//--! DefiniteIntegral: since neighbor check above seems to be failing, try checking duplicate flag set when vertex created
if( m_pOutputMesh->m_vecVertices[v0.index].mIsMaterialDuplicate == true ||
m_pOutputMesh->m_vecVertices[v1.index].mIsMaterialDuplicate == true)
{
vecVertexMetadata[v0.index].isOnMaterialEdge = true;
vecVertexMetadata[v1.index].isOnMaterialEdge = true;
}
}
...
}
And with that mesh decimation works properly for me across multiple materials. I may have missed some changes I made that but is the main gist of it.
I realize you are going to re-write mesh decimation anyway, but just wanted to alert you to this, something to look out for.