From ab906a03f6f9e4faab06e36ac9ce1f9575b2e96b Mon Sep 17 00:00:00 2001 From: David Williams Date: Sat, 4 Jul 2009 21:35:10 +0000 Subject: [PATCH] Improvements to smoothing code. --- .../PolyVoxCore/include/IndexedSurfacePatch.h | 4 +- .../source/IndexedSurfacePatch.cpp | 109 +++++++++++------- 2 files changed, 71 insertions(+), 42 deletions(-) diff --git a/library/PolyVoxCore/include/IndexedSurfacePatch.h b/library/PolyVoxCore/include/IndexedSurfacePatch.h index 9432b6d6..cddffee9 100644 --- a/library/PolyVoxCore/include/IndexedSurfacePatch.h +++ b/library/PolyVoxCore/include/IndexedSurfacePatch.h @@ -53,8 +53,8 @@ namespace PolyVox void clear(void); const bool isEmpty(void) const; - void smooth(float fAmount, bool bIncludeEdgeVertices = false); - void sumNearbyNormals(bool bNormalise = true); + void smoothPositions(float fAmount, uint8_t uNoRequiredUses = 0); + void sumNearbyNormals(bool bNormaliseResult = true); POLYVOX_SHARED_PTR extractSubset(std::set setMaterials); diff --git a/library/PolyVoxCore/source/IndexedSurfacePatch.cpp b/library/PolyVoxCore/source/IndexedSurfacePatch.cpp index 33333bbb..f258f6d0 100644 --- a/library/PolyVoxCore/source/IndexedSurfacePatch.cpp +++ b/library/PolyVoxCore/source/IndexedSurfacePatch.cpp @@ -114,59 +114,89 @@ namespace PolyVox return (getNoOfVertices() == 0) || (getNoOfIndices() == 0); } - void IndexedSurfacePatch::smooth(float fAmount, bool bIncludeEdgeVertices) + //////////////////////////////////////////////////////////////////////////////// + /// The function works on a per triangle basis without any need for connectivity + /// information. It determines whether a triangle is lying on a flat or curved + /// section of the surface patch by examining the normals - therefore these + /// normals must hve been set to something sensible before this functions is called. + /// \param fAmount A factor controlling how much the vertices move by. Find a good + /// value by experimentation, starting with something small such as 0.1f. + /// \param uNoRequiredUses The minumum number of triangles which have to be using + /// a vertex for that vertex to be modified. This is can be used to help prevent + /// the operation being carried out on edge vertices, which could cause discontinuities. + //////////////////////////////////////////////////////////////////////////////// + void IndexedSurfacePatch::smoothPositions(float fAmount, uint8_t uNoRequiredUses) { if(m_vecVertices.size() == 0) //FIXME - I don't think we should need this test, but I have seen crashes otherwise... { return; } + //Used to count how many times a given vertex is used. This helps determine if it's on an edge. + std::vector useCount(m_vecVertices.size()); + //Initialise all counts to zero. Should be ok as the vector should store all elements contiguously. + memset(&useCount[0], 0, useCount.size() * sizeof(uint32_t)); - std::vector vecOriginalVertices = m_vecVertices; - - Vector3DFloat offset = static_cast(m_Region.getLowerCorner()); + //This will hold the new positions, and is initialised with the current positions. + std::vector newPositions(m_vecVertices.size()); + for(uint32_t uIndex = 0; uIndex < newPositions.size(); uIndex++) + { + newPositions[uIndex] = m_vecVertices[uIndex].getPosition(); + } + //Iterate over each triangle for(vector::iterator iterIndex = m_vecTriangleIndices.begin(); iterIndex != m_vecTriangleIndices.end();) { - SurfaceVertex& v0 = vecOriginalVertices[*iterIndex]; - SurfaceVertex& v0New = m_vecVertices[*iterIndex]; + //Get the vertex data for the triangle + SurfaceVertex& v0 = m_vecVertices[*iterIndex]; + Vector3DFloat& v0New = newPositions[*iterIndex]; + useCount[*iterIndex]++; iterIndex++; - SurfaceVertex& v1 = vecOriginalVertices[*iterIndex]; - SurfaceVertex& v1New = m_vecVertices[*iterIndex]; + SurfaceVertex& v1 = m_vecVertices[*iterIndex]; + Vector3DFloat& v1New = newPositions[*iterIndex]; + useCount[*iterIndex]++; iterIndex++; - SurfaceVertex& v2 = vecOriginalVertices[*iterIndex]; - SurfaceVertex& v2New = m_vecVertices[*iterIndex]; + SurfaceVertex& v2 = m_vecVertices[*iterIndex]; + Vector3DFloat& v2New = newPositions[*iterIndex]; + useCount[*iterIndex]++; iterIndex++; - //FIXME - instead of finding these opposite points (Opp) we could just use the midpoint? - Vector3DFloat v0Opp = (v1.position + v2.position) / 2.0f; - Vector3DFloat v1Opp = (v0.position + v2.position) / 2.0f; - Vector3DFloat v2Opp = (v0.position + v1.position) / 2.0f; - - Vector3DFloat v0ToOpp = v0Opp - v0.position; - v0ToOpp.normalise(); - Vector3DFloat v1ToOpp = v1Opp - v1.position; - v1ToOpp.normalise(); - Vector3DFloat v2ToOpp = v2Opp - v2.position; - v2ToOpp.normalise(); + //Find the midpoint + Vector3DFloat v3dMidpoint = (v0.position + v1.position + v2.position) / 3.0f; + //Vectors from vertex to midpoint + Vector3DFloat v0ToMidpoint = v3dMidpoint - v0.position; + Vector3DFloat v1ToMidpoint = v3dMidpoint - v1.position; + Vector3DFloat v2ToMidpoint = v3dMidpoint - v2.position; + + //Get the vertex normals Vector3DFloat n0 = v0.getNormal(); - n0.normalise(); - Vector3DFloat n1 = v1.getNormal(); - n1.normalise(); - Vector3DFloat n2 = v2.getNormal(); - n2.normalise(); + Vector3DFloat n1 = v1.getNormal(); + Vector3DFloat n2 = v2.getNormal(); - if(m_Region.containsPoint(v0.getPosition() + offset, 0.001)) + //I don't think these normalisation are necessary... and could be slow. + //Normals should be normalised anyway, and as long as all triangles are + //about the same size the distances to midpoint should be similar too. + //v0ToMidpoint.normalise(); + //v1ToMidpoint.normalise(); + //v2ToMidpoint.normalise(); + //n0.normalise(); + //n1.normalise(); + //n2.normalise(); + + //If the dot product is zero the the normals are perpendicular + //to the triangle, hence the positions do not move. + v0New += (n0 * (n0.dot(v0ToMidpoint)) * fAmount); + v1New += (n1 * (n1.dot(v1ToMidpoint)) * fAmount); + v2New += (n2 * (n2.dot(v2ToMidpoint)) * fAmount); + } + + //Update with the new positions + for(uint32_t uIndex = 0; uIndex < newPositions.size(); uIndex++) + { + //Check we have enough uses. + if(useCount[uIndex] >= uNoRequiredUses) { - v0New.position += (n0 * (n0.dot(v0ToOpp)) * fAmount); - } - if(m_Region.containsPoint(v1.getPosition() + offset, 0.001)) - { - v1New.position += (n1 * (n1.dot(v1ToOpp)) * fAmount); - } - if(m_Region.containsPoint(v2.getPosition() + offset, 0.001)) - { - v2New.position += (n2 * (n2.dot(v2ToOpp)) * fAmount); + m_vecVertices[uIndex].setPosition(newPositions[uIndex]); } } } @@ -179,7 +209,7 @@ namespace PolyVox /// vertex. Usually, the resulting normals should be renormalised afterwards. /// Note: This function can cause lighting discontinuities accross region boundaries. //////////////////////////////////////////////////////////////////////////////// - void IndexedSurfacePatch::sumNearbyNormals(bool bNormalise) + void IndexedSurfacePatch::sumNearbyNormals(bool bNormaliseResult) { if(m_vecVertices.size() == 0) //FIXME - I don't think we should need this test, but I have seen crashes otherwise... { @@ -188,8 +218,7 @@ namespace PolyVox std::vector summedNormals(m_vecVertices.size()); - //Initialise all normals to zero. Pretty sure this is ok, - //as the vector should stoer all elements contiguously. + //Initialise all normals to zero. Should be ok as the vector should store all elements contiguously. memset(&summedNormals[0], 0, summedNormals.size() * sizeof(Vector3DFloat)); for(vector::iterator iterIndex = m_vecTriangleIndices.begin(); iterIndex != m_vecTriangleIndices.end();) @@ -213,7 +242,7 @@ namespace PolyVox for(uint32_t uIndex = 0; uIndex < summedNormals.size(); uIndex++) { - if(bNormalise) + if(bNormaliseResult) { summedNormals[uIndex].normalise(); }