2020-03-01 02:30:19 +00:00
|
|
|
|
/*
|
|
|
|
|
* 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 {
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Wireframe mesh with optional backface normals, for use with visualization generators.
|
2020-03-02 02:18:31 +00:00
|
|
|
|
/// Call the various functions to add data, then call Validate() to check for broken
|
|
|
|
|
/// references.
|
2020-03-01 02:30:19 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
[Serializable]
|
|
|
|
|
public class VisWireframe : IVisualizationWireframe {
|
2020-04-12 00:24:21 +00:00
|
|
|
|
//
|
|
|
|
|
// Names and definitions of parameters that are interpreted by the wireframe
|
|
|
|
|
// renderer, rather than the visualization generator.
|
|
|
|
|
//
|
2020-03-04 03:29:15 +00:00
|
|
|
|
public const string P_IS_PERSPECTIVE = "_isPerspective";
|
2020-03-07 00:51:47 +00:00
|
|
|
|
public const string P_IS_BFC_ENABLED = "_isBfcEnabled";
|
2020-04-12 00:24:21 +00:00
|
|
|
|
public const string P_IS_RECENTERED = "_isRecentered";
|
2020-03-04 03:29:15 +00:00
|
|
|
|
|
2020-03-02 02:18:31 +00:00
|
|
|
|
public static VisParamDescr Param_IsPerspective(string uiLabel, bool defaultVal) {
|
2020-03-04 03:29:15 +00:00
|
|
|
|
return new VisParamDescr(uiLabel, P_IS_PERSPECTIVE, typeof(bool), 0, 0, 0, defaultVal);
|
2020-03-02 02:18:31 +00:00
|
|
|
|
}
|
2020-03-07 00:51:47 +00:00
|
|
|
|
public static VisParamDescr Param_IsBfcEnabled(string uiLabel, bool defaultVal) {
|
|
|
|
|
return new VisParamDescr(uiLabel, P_IS_BFC_ENABLED, typeof(bool), 0, 0, 0, defaultVal);
|
2020-03-02 02:18:31 +00:00
|
|
|
|
}
|
2020-04-12 00:24:21 +00:00
|
|
|
|
public static VisParamDescr Param_IsRecentered(string uiLabel, bool defaultVal) {
|
|
|
|
|
return new VisParamDescr(uiLabel, P_IS_RECENTERED, typeof(bool), 0, 0, 0, defaultVal);
|
|
|
|
|
}
|
2020-03-02 02:18:31 +00:00
|
|
|
|
|
2020-03-01 02:30:19 +00:00
|
|
|
|
private List<float> mVerticesX = new List<float>();
|
|
|
|
|
private List<float> mVerticesY = new List<float>();
|
|
|
|
|
private List<float> mVerticesZ = new List<float>();
|
|
|
|
|
|
2020-04-11 18:23:16 +00:00
|
|
|
|
private List<int> mPoints = new List<int>();
|
2020-03-01 02:30:19 +00:00
|
|
|
|
private List<IntPair> mEdges = new List<IntPair>();
|
|
|
|
|
|
|
|
|
|
private List<float> mNormalsX = new List<float>();
|
|
|
|
|
private List<float> mNormalsY = new List<float>();
|
|
|
|
|
private List<float> mNormalsZ = new List<float>();
|
|
|
|
|
|
|
|
|
|
private List<IntPair> mVertexFaces = new List<IntPair>();
|
|
|
|
|
private List<IntPair> mEdgeFaces = new List<IntPair>();
|
|
|
|
|
|
2020-03-13 17:53:53 +00:00
|
|
|
|
private List<int> mExcludedVertices = new List<int>();
|
|
|
|
|
private List<int> mExcludedEdges = new List<int>();
|
|
|
|
|
|
|
|
|
|
|
2020-03-01 02:30:19 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructor. Nothing much to do.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public VisWireframe() { }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-13 17:53:53 +00:00
|
|
|
|
/// Adds the vertex to the list. Coordinates may be INVALID_VERTEX to exclude the
|
|
|
|
|
/// vertex from rendering.
|
2020-03-01 02:30:19 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="x">X coordinate.</param>
|
|
|
|
|
/// <param name="y">Y coordinate.</param>
|
|
|
|
|
/// <param name="z">Z coordinate.</param>
|
|
|
|
|
/// <returns>Vertex index. Indices start at zero and count up.</returns>
|
|
|
|
|
public int AddVertex(float x, float y, float z) {
|
2021-11-01 22:07:29 +00:00
|
|
|
|
if (float.IsNaN(x) || float.IsNaN(y) || float.IsNaN(z)) {
|
|
|
|
|
throw new Exception("Invalid vertex x=" + x + " y=" + y + " z=" + z);
|
|
|
|
|
}
|
2020-03-01 02:30:19 +00:00
|
|
|
|
mVerticesX.Add(x);
|
|
|
|
|
mVerticesY.Add(y);
|
|
|
|
|
mVerticesZ.Add(z);
|
|
|
|
|
return mVerticesX.Count - 1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-11 18:23:16 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adds a point to the list.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="index">Vertex index.</param>
|
|
|
|
|
/// <returns>Point index. Indices start at zero and count up.</returns>
|
|
|
|
|
public int AddPoint(int index) {
|
|
|
|
|
mPoints.Add(index);
|
|
|
|
|
return mPoints.Count - 1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-01 02:30:19 +00:00
|
|
|
|
/// <summary>
|
2020-03-02 02:18:31 +00:00
|
|
|
|
/// Adds an edge to the list. The referenced vertices do not need to be defined
|
|
|
|
|
/// before calling.
|
2020-03-01 02:30:19 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="index0">Index of first vertex.</param>
|
|
|
|
|
/// <param name="index1">Index of second vertex.</param>
|
|
|
|
|
/// <returns>Edge index. Indices start at zero and count up.</returns>
|
|
|
|
|
public int AddEdge(int index0, int index1) {
|
2020-03-02 02:18:31 +00:00
|
|
|
|
Debug.Assert(index0 >= 0);
|
|
|
|
|
Debug.Assert(index1 >= 0);
|
2020-03-01 02:30:19 +00:00
|
|
|
|
mEdges.Add(new IntPair(index0, index1));
|
|
|
|
|
return mEdges.Count - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adds the face normal to the list.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="x">X coordinate.</param>
|
|
|
|
|
/// <param name="y">Y coordinate.</param>
|
|
|
|
|
/// <param name="z">Z coordinate.</param>
|
|
|
|
|
/// <returns>Face index. Indices start at zero and count up.</returns>
|
|
|
|
|
public int AddFaceNormal(float x, float y, float z) {
|
2021-11-01 22:07:29 +00:00
|
|
|
|
if (float.IsNaN(x) || float.IsNaN(y) || float.IsNaN(z)) {
|
|
|
|
|
throw new Exception("Invalid normal x=" + x + " y=" + y + " z=" + z);
|
|
|
|
|
}
|
2020-03-01 02:30:19 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-11 03:59:45 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Replaces the specified face normal.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="index">Face index.</param>
|
|
|
|
|
/// <param name="x">X coordinate.</param>
|
|
|
|
|
/// <param name="y">Y coordinate.</param>
|
|
|
|
|
/// <param name="z">Z coordinate.</param>
|
|
|
|
|
public void ReplaceFaceNormal(int index, float x, float y, float z) {
|
2021-11-01 22:07:29 +00:00
|
|
|
|
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
|
2020-03-11 03:59:45 +00:00
|
|
|
|
mNormalsX[index] = x;
|
|
|
|
|
mNormalsY[index] = y;
|
|
|
|
|
mNormalsZ[index] = z;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-01 02:30:19 +00:00
|
|
|
|
/// <summary>
|
2020-03-02 02:18:31 +00:00
|
|
|
|
/// Marks a vertex's visibility as being tied to the specified face. The vertices and
|
|
|
|
|
/// faces being referenced do not need to exist yet.
|
2020-03-01 02:30:19 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="vertexIndex">Index of vertex.</param>
|
|
|
|
|
/// <param name="faceIndex">Index of face.</param>
|
|
|
|
|
public void AddVertexFace(int vertexIndex, int faceIndex) {
|
2020-03-02 02:18:31 +00:00
|
|
|
|
Debug.Assert(vertexIndex >= 0);
|
|
|
|
|
Debug.Assert(faceIndex >= 0);
|
2020-03-01 02:30:19 +00:00
|
|
|
|
mVertexFaces.Add(new IntPair(vertexIndex, faceIndex));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-02 02:18:31 +00:00
|
|
|
|
/// Marks an edge's visibility as being tied to the specified face. The edges and
|
|
|
|
|
/// faces being referenced do not need to exist yet.
|
2020-03-01 02:30:19 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="edgeIndex">Index of edge.</param>
|
|
|
|
|
/// <param name="faceIndex">Index of face.</param>
|
|
|
|
|
public void AddEdgeFace(int edgeIndex, int faceIndex) {
|
2020-03-02 02:18:31 +00:00
|
|
|
|
Debug.Assert(edgeIndex >= 0);
|
|
|
|
|
Debug.Assert(faceIndex >= 0);
|
2020-03-01 02:30:19 +00:00
|
|
|
|
mEdgeFaces.Add(new IntPair(edgeIndex, faceIndex));
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-13 17:53:53 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Marks a vertex as excluded. Used for level-of-detail reduction.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="vertexIndex">Index of vertex.</param>
|
|
|
|
|
public void AddVertexExclusion(int vertexIndex) {
|
|
|
|
|
Debug.Assert(vertexIndex >= 0);
|
|
|
|
|
mExcludedVertices.Add(vertexIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Marks an edge as excluded. Used for level-of-detail reduction.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="edgeIndex">Index of edge.</param>
|
|
|
|
|
public void AddEdgeExclusion(int edgeIndex) {
|
|
|
|
|
Debug.Assert(edgeIndex >= 0);
|
|
|
|
|
mExcludedEdges.Add(edgeIndex);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-01 02:30:19 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Verifies that the various references by index are valid.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="msg">Failure detail.</param>
|
|
|
|
|
/// <returns>True if everything looks valid.</returns>
|
|
|
|
|
public bool Validate(out string msg) {
|
|
|
|
|
int vertexCount = mVerticesX.Count;
|
|
|
|
|
int faceCount = mNormalsX.Count;
|
|
|
|
|
int edgeCount = mEdges.Count;
|
|
|
|
|
|
2020-03-04 03:29:15 +00:00
|
|
|
|
// complain about empty objects (should we fail if no edges were defined?)
|
|
|
|
|
if (vertexCount == 0) {
|
|
|
|
|
msg = "no vertices defined";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-04-23 18:25:45 +00:00
|
|
|
|
if (mVerticesX.Count != mVerticesY.Count || mVerticesX.Count != mVerticesZ.Count) {
|
|
|
|
|
msg = "inconsistent vertex arrays";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-03-04 03:29:15 +00:00
|
|
|
|
|
2020-04-11 18:23:16 +00:00
|
|
|
|
// check points
|
|
|
|
|
foreach (int vi in mPoints) {
|
|
|
|
|
if (vi < 0 || vi >= vertexCount) {
|
|
|
|
|
msg = "invalid point (index=" + vi + "; count=" + vertexCount + ")";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-01 02:30:19 +00:00
|
|
|
|
// check edges
|
|
|
|
|
foreach (IntPair ip in mEdges) {
|
|
|
|
|
if (ip.Val0 < 0 || ip.Val0 >= vertexCount ||
|
|
|
|
|
ip.Val1 < 0 || ip.Val1 >= vertexCount) {
|
2020-03-10 18:23:18 +00:00
|
|
|
|
msg = "invalid edge (vertices " + ip.Val0 + ", " + ip.Val1 +
|
|
|
|
|
"; count=" + vertexCount + ")";
|
2020-03-01 02:30:19 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check vertex-faces
|
|
|
|
|
foreach (IntPair ip in mVertexFaces) {
|
|
|
|
|
if (ip.Val0 < 0 || ip.Val0 >= vertexCount ||
|
|
|
|
|
ip.Val1 < 0 || ip.Val1 >= faceCount) {
|
2020-03-10 18:23:18 +00:00
|
|
|
|
msg = "invalid vertex-face (v=" + ip.Val0 + ", f=" + ip.Val1 + ")";
|
2020-03-01 02:30:19 +00:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check edge-faces
|
2020-03-14 01:39:11 +00:00
|
|
|
|
foreach (IntPair ip in mEdgeFaces) {
|
2020-03-01 02:30:19 +00:00
|
|
|
|
if (ip.Val0 < 0 || ip.Val0 >= edgeCount ||
|
|
|
|
|
ip.Val1 < 0 || ip.Val1 >= faceCount) {
|
2020-03-10 18:23:18 +00:00
|
|
|
|
msg = "invalid edge-face (e=" + ip.Val0 + ", f=" + ip.Val1 + ")";
|
2020-03-01 02:30:19 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-23 18:25:45 +00:00
|
|
|
|
if (mNormalsX.Count != mNormalsY.Count || mNormalsX.Count != mNormalsZ.Count) {
|
|
|
|
|
msg = "inconsistent normal arrays";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-03-01 02:30:19 +00:00
|
|
|
|
|
2020-03-13 17:53:53 +00:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-11 00:18:46 +00:00
|
|
|
|
// 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?
|
2020-03-07 00:51:47 +00:00
|
|
|
|
|
2020-03-01 02:30:19 +00:00
|
|
|
|
msg = string.Empty;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// IVisualizationWireframe implementation.
|
|
|
|
|
//
|
|
|
|
|
|
2020-04-12 00:24:21 +00:00
|
|
|
|
public bool Is2d { get; set; }
|
|
|
|
|
|
2020-03-01 02:30:19 +00:00
|
|
|
|
public float[] GetVerticesX() {
|
|
|
|
|
return mVerticesX.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public float[] GetVerticesY() {
|
|
|
|
|
return mVerticesY.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public float[] GetVerticesZ() {
|
|
|
|
|
return mVerticesZ.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-11 18:23:16 +00:00
|
|
|
|
public int[] GetPoints() {
|
|
|
|
|
return mPoints.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-01 02:30:19 +00:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-13 17:53:53 +00:00
|
|
|
|
public int[] GetExcludedVertices() {
|
|
|
|
|
return mExcludedVertices.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int[] GetExcludedEdges() {
|
|
|
|
|
return mExcludedEdges.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-01 02:30:19 +00:00
|
|
|
|
|
|
|
|
|
public override string ToString() {
|
|
|
|
|
return "[VisWireframe: " + mVerticesX.Count + " vertices, " +
|
|
|
|
|
mEdges.Count + " edges, " +
|
|
|
|
|
mNormalsX.Count + " faces, " +
|
|
|
|
|
mVertexFaces.Count + " vfaces, " +
|
|
|
|
|
mEdgeFaces.Count + " efaces]";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|