/// <summary> /// Attempts to fix the surface normals. Call this after the shape has been /// loaded and the data validated. /// </summary> /// <param name="vw">Wireframe data.</param> /// <param name="offset">Initial shape offset.</param> private void FixNormals(VisWireframe vw, int offset) { const int minVisThresh = 0x9; int edgeOffset = offset + (short)(mFileData[offset + 0x03] | (mFileData[offset + 0x10] << 8)); int faceOffset = offset + (short)(mFileData[offset + 0x04] | (mFileData[offset + 0x11] << 8)); float[] verticesX = vw.GetVerticesX(); float[] verticesY = vw.GetVerticesY(); float[] verticesZ = vw.GetVerticesZ(); float[] normalsX = vw.GetNormalsX(); float[] normalsY = vw.GetNormalsY(); float[] normalsZ = vw.GetNormalsZ(); IntPair[] edges = vw.GetEdges(); IntPair[] edgeFaces = vw.GetEdgeFaces(); for (int face = 0; face < normalsX.Length; face++) { // Find the first edge that references this face. We ignore anything whose // visibility threshold is too low. int edge = -1; int ef; for (ef = 0; ef < edgeFaces.Length; ef++) { // pull the flags out of the edge data to get the visibility threshold byte flags = mFileData[edgeOffset + edgeFaces[ef].Val0 * 4]; if (flags > 0x1f) { mAppRef.DebugLog("BAD FLAG " + flags.ToString("x2")); } if (flags < minVisThresh) { continue; } if (edgeFaces[ef].Val1 == face) { edge = edgeFaces[ef].Val0; break; } } if (edge < 0) { mAppRef.DebugLog("Unable to find first edge for face " + face); continue; } // Extract the two vertices. int v0 = edges[edge].Val0; int v1 = edges[edge].Val1; // Find another edge for this face that has a common vertex. edge = -1; for (++ef; ef < edgeFaces.Length; ef++) { byte flags = mFileData[edgeOffset + edgeFaces[ef].Val0 * 4]; if (flags > 0x1f) { mAppRef.DebugLog("BAD FLAG " + flags.ToString("x2")); } if (flags < minVisThresh) { continue; } if (edgeFaces[ef].Val1 == face) { int chkEdge = edgeFaces[ef].Val0; if (edges[chkEdge].Val0 == v0 || edges[chkEdge].Val0 == v1 || edges[chkEdge].Val1 == v0 || edges[chkEdge].Val1 == v1) { edge = chkEdge; break; } } } if (edge < 0) { mAppRef.DebugLog("Unable to find second edge for face " + face); continue; } // Arrange the vertices so the edges are v0-v1 and v1-v2. If the edges have // v0 in common we shuffle things around. int v2; if (edges[edge].Val0 == v0) { v2 = v1; v1 = v0; v0 = edges[edge].Val1; } else if (edges[edge].Val1 == v0) { v2 = v1; v1 = v0; v0 = edges[edge].Val0; } else if (edges[edge].Val0 == v1) { v2 = edges[edge].Val1; } else if (edges[edge].Val1 == v1) { v2 = edges[edge].Val0; } else { mAppRef.DebugLog("BUG!"); continue; } //mAppRef.DebugLog("Face " + face + ": using vertices " + v0 + "," + v1 + "," + v2); // Create vectors for the edges. Vector3 vec0 = new Vector3(verticesX[v0], verticesY[v0], verticesZ[v0]); Vector3 vec1 = new Vector3(verticesX[v1], verticesY[v1], verticesZ[v1]); Vector3 vec2 = new Vector3(verticesX[v2], verticesY[v2], verticesZ[v2]); Vector3 evec0 = Vector3.Subtract(vec0, vec1); Vector3 evec1 = Vector3.Subtract(vec1, vec2); // Compute the cross product. Vector3 cross = Vector3.Cross(evec0, evec1); Vector3 negCross = cross.Multiply(-1); //mAppRef.DebugLog(" evec0=" + evec0 + " evec1=" + evec1 + " cross=" + cross); // Check to see if we got the sign backward by adding the new vector to // one of the vertices. If it moves us farther from the center of the // object, it's facing outward, and we're good. if (Vector3.Add(vec0, cross).Magnitude() < Vector3.Add(vec0, negCross).Magnitude()) { // flip it cross = negCross; } // Replace the entry. Vector3 orig = new Vector3(normalsX[face], normalsY[face], normalsZ[face]).Normalize(); Vector3 subst = cross.Normalize(); vw.ReplaceFaceNormal(face, (float)subst.X, (float)subst.Y, (float)subst.Z); //double ang = Math.Acos(Vector3.Dot(orig, subst)); //mAppRef.DebugLog("Face " + face.ToString("D2") + ": " + subst + // " vs. orig " + orig + // " (off by " + (ang * 180.0 / Math.PI).ToString("N2") + " deg)"); } }