Axiom.Core.ProgressiveMesh.ComputeEdgeCollapseCost C# (CSharp) Method

ComputeEdgeCollapseCost() private method

private ComputeEdgeCollapseCost ( PMVertex src, PMVertex dest ) : float
src PMVertex
dest PMVertex
return float
		float ComputeEdgeCollapseCost( PMVertex src, PMVertex dest )
		{
			// if we collapse edge uv by moving src to dest then how 
			// much different will the model change, i.e. how much "error".
			// The method of determining cost was designed in order 
			// to exploit small and coplanar regions for
			// effective polygon reduction.
			Vector3 edgeVector = src.position - dest.position;

			float cost;
			float curvature = 0.001f;

			// find the "sides" triangles that are on the edge uv
			List<PMTriangle> sides = new List<PMTriangle>();
			// Iterate over src's faces and find 'sides' of the shared edge which is being collapsed
			foreach ( PMTriangle srcFace in src.faces )
			{
				// Check if this tri also has dest in it (shared edge)
				if ( srcFace.HasCommonVertex( dest ) )
					sides.Add( srcFace );
			}
			// Special cases
			// If we're looking at a border vertex

			if ( src.IsBorder )
			{
				if ( sides.Count > 1 )
				{
					// src is on a border, but the src-dest edge has more than one tri on it
					// So it must be collapsing inwards
					// Mark as very high-value cost
					// curvature = 1.0f;
					cost = 1.0f;
				}
				else
				{
					// Collapsing ALONG a border
					// We can't use curvature to measure the effect on the model
					// Instead, see what effect it has on 'pulling' the other border edges
					// The more colinear, the less effect it will have
					// So measure the 'kinkiness' (for want of a better term)
					// Normally there can be at most 1 other border edge attached to this
					// However in weird cases there may be more, so find the worst
					Vector3 collapseEdge, otherBorderEdge;
					float kinkiness, maxKinkiness;
					maxKinkiness = 0.0f;
					edgeVector.Normalize();
					collapseEdge = edgeVector;
					foreach ( PMVertex neighbor in src.neighbors )
					{
						if ( neighbor != dest && neighbor.IsManifoldEdgeWith( src ) )
						{
							otherBorderEdge = src.position - neighbor.position;
							otherBorderEdge.Normalize();
							// This time, the nearer the dot is to -1, the better, because that means
							// the edges are opposite each other, therefore less kinkiness
							// Scale into [0..1]
							kinkiness = ( otherBorderEdge.Dot( collapseEdge ) + 1.002f ) * 0.5f;
							maxKinkiness = Utility.Max( kinkiness, maxKinkiness );
						}
					}

					cost = maxKinkiness;

				}
			}
			else // not a border
			{
				// Standard inner vertex
				// Calculate curvature
				// use the triangle facing most away from the sides 
				// to determine our curvature term
				// Iterate over src's faces again
				foreach ( PMTriangle srcFace in src.faces )
				{
					float mincurv = 1.0f; // curve for face i and closer side to it
					// Iterate over the sides
					foreach ( PMTriangle sideFace in sides )
					{
						// Dot product of face normal gives a good delta angle
						float dotprod = srcFace.normal.Dot( sideFace.normal );
						// NB we do (1-..) to invert curvature where 1 is high curvature [0..1]
						// Whilst dot product is high when angle difference is low
						mincurv = Utility.Min( mincurv, ( 1.002f - dotprod ) * 0.5f );
					}
					curvature = Utility.Max( curvature, mincurv );
				}
				cost = curvature;
			}

			// check for texture seam ripping
			if ( src.seam && !dest.seam )
				cost = 1.0f;

			// Check for singular triangle destruction
			// If src and dest both only have 1 triangle (and it must be a shared one)
			// then this would destroy the shape, so don't do this
			if ( src.faces.Count == 1 && dest.faces.Count == 1 )
				cost = float.MaxValue;


			// Degenerate case check
			// Are we going to invert a face normal of one of the neighbouring faces?
			// Can occur when we have a very small remaining edge and collapse crosses it
			// Look for a face normal changing by > 90 degrees
			foreach ( PMTriangle srcFace in src.faces )
			{
				// Ignore the deleted faces (those including src & dest)
				if ( !srcFace.HasCommonVertex( dest ) )
				{
					// Test the new face normal
					PMVertex v0, v1, v2;
					// Replace src with dest wherever it is
					v0 = ( srcFace.vertex[ 0 ].commonVertex == src ) ? dest : srcFace.vertex[ 0 ].commonVertex;
					v1 = ( srcFace.vertex[ 1 ].commonVertex == src ) ? dest : srcFace.vertex[ 1 ].commonVertex;
					v2 = ( srcFace.vertex[ 2 ].commonVertex == src ) ? dest : srcFace.vertex[ 2 ].commonVertex;

					// Cross-product 2 edges
					Vector3 e1 = v1.position - v0.position;
					Vector3 e2 = v2.position - v1.position;

					Vector3 newNormal = e1.Cross( e2 );
					newNormal.Normalize();

					// Dot old and new face normal
					// If < 0 then more than 90 degree difference
					if ( newNormal.Dot( srcFace.normal ) < 0.0f )
					{
						// Don't do it!
						cost = float.MaxValue;
						break; // No point continuing
					}
				}
			}

			Debug.Assert( cost >= 0 );
			return cost;
		}