/// <summary> /// Generate a debug mesh containing the centroids of the primary mesh's triangles as points. /// </summary> /// <returns>A new mesh for debug</returns> public Mesh <Vertex3D> GenerateCentroidPointMesh() { // Doesn't rely on topology int numTriangles = indices.Length / 3; Vertex3D[] centroidVerts = new Vertex3D[numTriangles]; Vertex3D centroidVertex = new Vertex3D(); Mesh <Vertex3D> centroidMesh = new Mesh <Vertex3D>(centroidVerts); int triangleIndex = 0; for (int i = 0; i < numTriangles; ++i) { Vector3 centroid = Topology.CalculateCentroid(i); IPositionVertex ipv = centroidVertex as IPositionVertex; if (ipv != null) { ipv.SetPosition(centroid); } centroidMesh.vertices[triangleIndex++] = (Vertex3D)ipv; } return(centroidMesh); }
public float RelaxTriangles(float multiplier) { NeedsUpdate = true; double totalSurfaceArea = 4.0 * Math.PI; double idealFaceArea = totalSurfaceArea / (indices.Length / 3); double idealEdgeLength = Math.Sqrt(idealFaceArea * 4.0 / Math.Sqrt(3.0)); double idealDistanceToCentroid = idealEdgeLength * Math.Sqrt(3) / 3.0 * 0.9; Vector3[] shiftPositions = new Vector3[mesh.vertices.Length]; for (int i = 0; i < mesh.vertices.Length; ++i) { shiftPositions[i] = new Vector3(Vector3.Zero); } int numIndices = indices.Length; TVertex[] centroidVerts = new TVertex[numIndices / 3]; TVertex centroidVertex = new TVertex(); for (int i = 0; i < numIndices; i += 3) { Vector3 centroid = Topology.CalculateCentroid(i / 3); centroid.Normalize(); MeshAttr.SetPosition(ref centroidVertex, ref centroid); Vector3[] oldPositions = new Vector3[3] { new Vector3(MeshAttr.GetPosition(ref mesh.vertices[indices[i]])), new Vector3(MeshAttr.GetPosition(ref mesh.vertices[indices[i + 1]])), new Vector3(MeshAttr.GetPosition(ref mesh.vertices[indices[i + 2]])), }; for (int j = 0; j < 3; ++j) { Vector3 midLine = centroid - oldPositions[j]; float midLength = midLine.Length; midLine *= (float)(multiplier * (midLength - idealDistanceToCentroid) / midLength); shiftPositions[indices[i + j]] += midLine; } } var origin = Vector3.Zero; Plane p = new Plane(Vector3.UnitY, Vector3.Zero); for (int i = 0; i < mesh.vertices.Length; ++i) { Vector3 vertexPosition = MeshAttr.GetPosition(ref mesh.vertices[i]); p.Redefine(vertexPosition, origin); shiftPositions[i] = vertexPosition + p.ProjectPoint(shiftPositions[i]); shiftPositions[i].Normalize(); } // Stop poylgons rotating about their centroid. // Doesn't stop triangles flipping. float[] rotationSuppressions = new float[mesh.vertices.Length]; rotationSuppressions.Initialize(); Topology.GenerateEdges(); float minEdgeLength = (float)idealEdgeLength * 0.8f; float maxEdgeLength = (float)idealEdgeLength * 1.2f; foreach (var iter in Topology.Edges) { Int64 key = iter.Key; int index1 = (int)(key & 0xffffffff); int index2 = (int)((key >> 32) & 0xffffffff); Vector3 oldPos1 = MeshAttr.GetPosition(ref mesh.vertices[index1]); Vector3 oldPos2 = MeshAttr.GetPosition(ref mesh.vertices[index2]); Vector3 newPos1 = shiftPositions[index1]; Vector3 newPos2 = shiftPositions[index2]; Vector3 oldEdge = oldPos2 - oldPos1; Vector3 newEdge = newPos2 - newPos1; if (newEdge.Length < minEdgeLength) { // Move shift positions back out to ensure that the edge is never too small. Vector3 midPt = newPos1 + 0.5f * newEdge; newEdge.Normalize(); newEdge *= minEdgeLength * 0.5f; shiftPositions[index1] = midPt - newEdge; shiftPositions[index2] = midPt + newEdge; newEdge = shiftPositions[index2] - shiftPositions[index1]; } if (newEdge.Length > maxEdgeLength) { // Move shift positions back in to ensure that the edge is never too large. Vector3 midPt = newPos1 + 0.5f * newEdge; newEdge.Normalize(); newEdge *= (maxEdgeLength * 0.5f); shiftPositions[index1] = midPt - newEdge; shiftPositions[index2] = midPt + newEdge; newEdge = shiftPositions[index2] - shiftPositions[index1]; } oldEdge.Normalize(); newEdge.Normalize(); float suppression = (1.0f - Vector3.Dot(oldEdge, newEdge)) * 0.5f; rotationSuppressions[index1] = Math.Max(suppression, rotationSuppressions[index1]); rotationSuppressions[index2] = Math.Max(suppression, rotationSuppressions[index2]); } for (int i = 0; i < mesh.vertices.Length; ++i) { Vector3 pos = MeshAttr.GetPosition(ref mesh.vertices[i]); Vector3 delta = pos; pos = Math2.Lerp(pos, shiftPositions[i], (float)(1.0f - Math.Sqrt(rotationSuppressions[i]))); pos.Normalize(); shiftPositions[i] = pos; } float totalShift = 0; for (int i = 0; i < mesh.vertices.Length; ++i) { Vector3 delta = MeshAttr.GetPosition(ref mesh.vertices[i]); MeshAttr.SetPosition(ref mesh.vertices[i], ref shiftPositions[i]); delta -= shiftPositions[i]; totalShift += delta.Length; } Topology.Regenerate(); return(totalShift); }
// Alternative algorithm // for each triangle // find normal // find centroid radius // Tilt triangle towards centroid radius // moving edges towards symmetry. // generate new point for each tri vert // for each vert, calc average of all new verts, normalise and apply // Algorithm is nearly there, but on some triangles, there is a potential that converging on // centroid tilts through random moves away from centroidal radius, and they become thin / start overlapping. public float RelaxTriangles1(float multiplier) { NeedsUpdate = true; double totalSurfaceArea = 4.0 * Math.PI; double idealFaceArea = totalSurfaceArea / (indices.Length / 3); double idealEdgeLength = Math.Sqrt(idealFaceArea * 4.0 / Math.Sqrt(3.0)); double idealDistanceToCentroid = idealEdgeLength * Math.Sqrt(3) / 3.0 * 0.9; int numIndices = indices.Length; Vector3[] shiftPositions = new Vector3[mesh.vertices.Length]; for (int i = 0; i < mesh.vertices.Length; ++i) { shiftPositions[i] = new Vector3(Vector3.Zero); } for (int i = 0; i < numIndices; i += 3) { if (1 == 1)//TooThin(i)) { Vector3 centroid = Topology.CalculateCentroid(i / 3); centroid.Normalize(); // Compare each corner to the centroid // if too far off some ideal length ( the centroid of an equilateral triangle ), // pull vertex closer to centroid, (without buggering up the other triangles using // this point.) Vector3[] oldPositions = new Vector3[3] { new Vector3(MeshAttr.GetPosition(ref mesh.vertices[indices[i]])), new Vector3(MeshAttr.GetPosition(ref mesh.vertices[indices[i + 1]])), new Vector3(MeshAttr.GetPosition(ref mesh.vertices[indices[i + 2]])), }; Vector3[] newPositions = new Vector3[3]; for (int j = 0; j < 3; ++j) { TVertex v1 = mesh.vertices[indices[i + j]]; Vector3 v1Pos = MeshAttr.GetPosition(ref v1); Vector3 e1 = centroid - v1Pos; if (e1.Length > idealDistanceToCentroid * 1.1) { // Move vertex closer to centroid float factor = 1.0f - (float)idealDistanceToCentroid / e1.Length; float fraction = (multiplier * (1.0f - (float)idealDistanceToCentroid / e1.Length)); factor = factor * fraction; v1Pos = Vector3.Normalize(v1Pos + e1 * factor); } else if (e1.Length < idealDistanceToCentroid * 0.9) { // Move vertex away from the centroid float factor = 1 - e1.Length / (float)idealDistanceToCentroid; float fraction = (multiplier * (e1.Length / (float)idealDistanceToCentroid)); factor = factor * fraction; v1Pos = Vector3.Normalize(v1Pos - e1 * factor); } newPositions[j] = v1Pos; } // If this makes the triangle less parallel to the sphere, don't do it. Vector3 a = newPositions[1] - newPositions[0]; Vector3 b = newPositions[2] - newPositions[1]; Vector3 normal = Vector3.Cross(a, b); normal.Normalize(); float dot = Vector3.Dot(centroid, normal); double phi = Math.Acos(dot); a = oldPositions[1] - oldPositions[0]; b = oldPositions[2] - oldPositions[1]; normal = Vector3.Cross(a, b); normal.Normalize(); dot = Vector3.Dot(centroid, normal); double theta = Math.Acos(dot); if (phi < dot) { for (int j = 0; j < 3; ++j) { //SetPosition(ref mesh.vertices[indices[i + j]], ref newPositions[j]); shiftPositions[indices[i + j]] = newPositions[j]; } } // If any surrounding triangles would be less parallel to the sphere than // this is more parallel, don't do it. } } TVertex[] vertices = new TVertex[mesh.vertices.Length]; float totalShift = 0; for (int i = 0; i < mesh.vertices.Length; ++i) { Vector3 delta = shiftPositions[i] - MeshAttr.GetPosition(ref mesh.vertices[i]); MeshAttr.SetPosition(ref mesh.vertices[i], ref shiftPositions[i]); totalShift += delta.Length; } Topology.Regenerate(); return(totalShift); }