1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-11-21 19:30:49 +00:00
6502bench/PluginCommon/VisWireframe.cs
Andy McFadden d48ab7582f Add Atari DVG visualizer
The DVG format, used for vector games like Asteroids, is the
predecessor to the AVG graphics used in games like Battlezone.

Also, added some extended error checks on wireframe vertices.

Also, minor edits to the README and daily tips.
2021-11-02 08:54:07 -07:00

344 lines
13 KiB
C#

/*
* 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.
/// Call the various functions to add data, then call Validate() to check for broken
/// references.
/// </summary>
[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<float> mVerticesX = new List<float>();
private List<float> mVerticesY = new List<float>();
private List<float> mVerticesZ = new List<float>();
private List<int> mPoints = new List<int>();
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>();
private List<int> mExcludedVertices = new List<int>();
private List<int> mExcludedEdges = new List<int>();
/// <summary>
/// Constructor. Nothing much to do.
/// </summary>
public VisWireframe() { }
/// <summary>
/// Adds the vertex to the list. Coordinates may be INVALID_VERTEX to exclude the
/// vertex from rendering.
/// </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) {
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;
}
/// <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;
}
/// <summary>
/// Adds an edge to the list. The referenced vertices do not need to be defined
/// before calling.
/// </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) {
Debug.Assert(index0 >= 0);
Debug.Assert(index1 >= 0);
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) {
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;
}
/// <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) {
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;
}
/// <summary>
/// Marks a vertex's visibility as being tied to the specified face. The vertices and
/// faces being referenced do not need to exist yet.
/// </summary>
/// <param name="vertexIndex">Index of vertex.</param>
/// <param name="faceIndex">Index of face.</param>
public void AddVertexFace(int vertexIndex, int faceIndex) {
Debug.Assert(vertexIndex >= 0);
Debug.Assert(faceIndex >= 0);
mVertexFaces.Add(new IntPair(vertexIndex, faceIndex));
}
/// <summary>
/// Marks an edge's visibility as being tied to the specified face. The edges and
/// faces being referenced do not need to exist yet.
/// </summary>
/// <param name="edgeIndex">Index of edge.</param>
/// <param name="faceIndex">Index of face.</param>
public void AddEdgeFace(int edgeIndex, int faceIndex) {
Debug.Assert(edgeIndex >= 0);
Debug.Assert(faceIndex >= 0);
mEdgeFaces.Add(new IntPair(edgeIndex, faceIndex));
}
/// <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);
}
/// <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;
// 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]";
}
}
}