/* * Copyright 2020 faddenSoft * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Diagnostics; namespace PluginCommon { /// /// Wireframe mesh with optional backface normals, for use with visualization generators. /// Call the various functions to add data, then call Validate() to check for broken /// references. /// [Serializable] public class VisWireframe : IVisualizationWireframe { // // Names and definitions of parameters that are interpreted by the wireframe // renderer, rather than the visualization generator. // public const string P_IS_PERSPECTIVE = "_isPerspective"; public const string P_IS_BFC_ENABLED = "_isBfcEnabled"; public const string P_IS_RECENTERED = "_isRecentered"; public static VisParamDescr Param_IsPerspective(string uiLabel, bool defaultVal) { return new VisParamDescr(uiLabel, P_IS_PERSPECTIVE, typeof(bool), 0, 0, 0, defaultVal); } public static VisParamDescr Param_IsBfcEnabled(string uiLabel, bool defaultVal) { return new VisParamDescr(uiLabel, P_IS_BFC_ENABLED, typeof(bool), 0, 0, 0, defaultVal); } public static VisParamDescr Param_IsRecentered(string uiLabel, bool defaultVal) { return new VisParamDescr(uiLabel, P_IS_RECENTERED, typeof(bool), 0, 0, 0, defaultVal); } private List mVerticesX = new List(); private List mVerticesY = new List(); private List mVerticesZ = new List(); private List mPoints = new List(); private List mEdges = new List(); private List mNormalsX = new List(); private List mNormalsY = new List(); private List mNormalsZ = new List(); private List mVertexFaces = new List(); private List mEdgeFaces = new List(); private List mExcludedVertices = new List(); private List mExcludedEdges = new List(); /// /// Constructor. Nothing much to do. /// public VisWireframe() { } /// /// Adds the vertex to the list. Coordinates may be INVALID_VERTEX to exclude the /// vertex from rendering. /// /// X coordinate. /// Y coordinate. /// Z coordinate. /// Vertex index. Indices start at zero and count up. public int AddVertex(float x, float y, float z) { if (float.IsNaN(x) || float.IsNaN(y) || float.IsNaN(z)) { throw new Exception("Invalid vertex x=" + x + " y=" + y + " z=" + z); } mVerticesX.Add(x); mVerticesY.Add(y); mVerticesZ.Add(z); return mVerticesX.Count - 1; } /// /// Adds a point to the list. /// /// Vertex index. /// Point index. Indices start at zero and count up. public int AddPoint(int index) { mPoints.Add(index); return mPoints.Count - 1; } /// /// Adds an edge to the list. The referenced vertices do not need to be defined /// before calling. /// /// Index of first vertex. /// Index of second vertex. /// Edge index. Indices start at zero and count up. public int AddEdge(int index0, int index1) { Debug.Assert(index0 >= 0); Debug.Assert(index1 >= 0); mEdges.Add(new IntPair(index0, index1)); return mEdges.Count - 1; } /// /// Adds the face normal to the list. /// /// X coordinate. /// Y coordinate. /// Z coordinate. /// Face index. Indices start at zero and count up. public int AddFaceNormal(float x, float y, float z) { if (float.IsNaN(x) || float.IsNaN(y) || float.IsNaN(z)) { throw new Exception("Invalid normal x=" + x + " y=" + y + " z=" + z); } Debug.Assert(x != 0.0f || y != 0.0f || z != 0.0f); // no zero-length normals mNormalsX.Add(x); mNormalsY.Add(y); mNormalsZ.Add(z); return mNormalsX.Count - 1; } /// /// Replaces the specified face normal. /// /// Face index. /// X coordinate. /// Y coordinate. /// Z coordinate. public void ReplaceFaceNormal(int index, float x, float y, float z) { if (float.IsNaN(x) || float.IsNaN(y) || float.IsNaN(z)) { throw new Exception("Invalid normal x=" + x + " y=" + y + " z=" + z); } Debug.Assert(x != 0.0f || y != 0.0f || z != 0.0f); // no zero-length normals mNormalsX[index] = x; mNormalsY[index] = y; mNormalsZ[index] = z; } /// /// Marks a vertex's visibility as being tied to the specified face. The vertices and /// faces being referenced do not need to exist yet. /// /// Index of vertex. /// Index of face. public void AddVertexFace(int vertexIndex, int faceIndex) { Debug.Assert(vertexIndex >= 0); Debug.Assert(faceIndex >= 0); mVertexFaces.Add(new IntPair(vertexIndex, faceIndex)); } /// /// Marks an edge's visibility as being tied to the specified face. The edges and /// faces being referenced do not need to exist yet. /// /// Index of edge. /// Index of face. public void AddEdgeFace(int edgeIndex, int faceIndex) { Debug.Assert(edgeIndex >= 0); Debug.Assert(faceIndex >= 0); mEdgeFaces.Add(new IntPair(edgeIndex, faceIndex)); } /// /// Marks a vertex as excluded. Used for level-of-detail reduction. /// /// Index of vertex. public void AddVertexExclusion(int vertexIndex) { Debug.Assert(vertexIndex >= 0); mExcludedVertices.Add(vertexIndex); } /// /// Marks an edge as excluded. Used for level-of-detail reduction. /// /// Index of edge. public void AddEdgeExclusion(int edgeIndex) { Debug.Assert(edgeIndex >= 0); mExcludedEdges.Add(edgeIndex); } /// /// Verifies that the various references by index are valid. /// /// Failure detail. /// True if everything looks valid. public bool Validate(out string msg) { int vertexCount = mVerticesX.Count; int faceCount = mNormalsX.Count; int edgeCount = mEdges.Count; // complain about empty objects (should we fail if no edges were defined?) if (vertexCount == 0) { msg = "no vertices defined"; return false; } if (mVerticesX.Count != mVerticesY.Count || mVerticesX.Count != mVerticesZ.Count) { msg = "inconsistent vertex arrays"; return false; } // check points foreach (int vi in mPoints) { if (vi < 0 || vi >= vertexCount) { msg = "invalid point (index=" + vi + "; count=" + vertexCount + ")"; return false; } } // check edges foreach (IntPair ip in mEdges) { if (ip.Val0 < 0 || ip.Val0 >= vertexCount || ip.Val1 < 0 || ip.Val1 >= vertexCount) { msg = "invalid edge (vertices " + ip.Val0 + ", " + ip.Val1 + "; count=" + vertexCount + ")"; return false; } } // check vertex-faces foreach (IntPair ip in mVertexFaces) { if (ip.Val0 < 0 || ip.Val0 >= vertexCount || ip.Val1 < 0 || ip.Val1 >= faceCount) { msg = "invalid vertex-face (v=" + ip.Val0 + ", f=" + ip.Val1 + ")"; return false; } } // check edge-faces foreach (IntPair ip in mEdgeFaces) { if (ip.Val0 < 0 || ip.Val0 >= edgeCount || ip.Val1 < 0 || ip.Val1 >= faceCount) { msg = "invalid edge-face (e=" + ip.Val0 + ", f=" + ip.Val1 + ")"; return false; } } // check face normals for (int i = 0; i < mNormalsX.Count; i++) { if (mNormalsX[i] == 0.0f && mNormalsY[i] == 0.0f && mNormalsZ[i] == 0.0f) { msg = "zero-length normal"; return false; } } if (mNormalsX.Count != mNormalsY.Count || mNormalsX.Count != mNormalsZ.Count) { msg = "inconsistent normal arrays"; return false; } // check excluded vertices for (int i = 0; i < mExcludedVertices.Count; i++) { if (mExcludedVertices[i] < 0 || mExcludedVertices[i] >= vertexCount) { msg = "excluded nonexistent vertex " + i; return false; } } // check excluded edges for (int i = 0; i < mExcludedEdges.Count; i++) { if (mExcludedEdges[i] < 0 || mExcludedEdges[i] >= edgeCount) { msg = "excluded nonexistent edge " + i; return false; } } // TODO(maybe): confirm that every face (i.e. normal) has a vertex we can use for // BFC calculation. Not strictly necessary since you can do orthographic-projection // BFC without it... but who does that? msg = string.Empty; return true; } // // IVisualizationWireframe implementation. // public bool Is2d { get; set; } public float[] GetVerticesX() { return mVerticesX.ToArray(); } public float[] GetVerticesY() { return mVerticesY.ToArray(); } public float[] GetVerticesZ() { return mVerticesZ.ToArray(); } public int[] GetPoints() { return mPoints.ToArray(); } public IntPair[] GetEdges() { return mEdges.ToArray(); } public float[] GetNormalsX() { return mNormalsX.ToArray(); } public float[] GetNormalsY() { return mNormalsY.ToArray(); } public float[] GetNormalsZ() { return mNormalsZ.ToArray(); } public IntPair[] GetVertexFaces() { return mVertexFaces.ToArray(); } public IntPair[] GetEdgeFaces() { return mEdgeFaces.ToArray(); } public int[] GetExcludedVertices() { return mExcludedVertices.ToArray(); } public int[] GetExcludedEdges() { return mExcludedEdges.ToArray(); } public override string ToString() { return "[VisWireframe: " + mVerticesX.Count + " vertices, " + mEdges.Count + " edges, " + mNormalsX.Count + " faces, " + mVertexFaces.Count + " vfaces, " + mEdgeFaces.Count + " efaces]"; } } }