|
|
@@ -8,7 +8,6 @@ using System.Numerics;
|
|
|
using SharpGLTF.Collections;
|
|
|
using SharpGLTF.Geometry.VertexTypes;
|
|
|
|
|
|
-
|
|
|
namespace SharpGLTF.Geometry
|
|
|
{
|
|
|
public interface IPrimitiveReader<TMaterial>
|
|
|
@@ -28,11 +27,6 @@ namespace SharpGLTF.Geometry
|
|
|
/// </summary>
|
|
|
IReadOnlyList<IVertexBuilder> Vertices { get; }
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Gets the plain list of indices.
|
|
|
- /// </summary>
|
|
|
- IReadOnlyList<int> Indices { get; }
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Gets the indices of all points, given that <see cref="VerticesPerPrimitive"/> is 1.
|
|
|
/// </summary>
|
|
|
@@ -44,9 +38,20 @@ namespace SharpGLTF.Geometry
|
|
|
IReadOnlyList<(int, int)> Lines { get; }
|
|
|
|
|
|
/// <summary>
|
|
|
- /// Gets the indices of all triangles, given that <see cref="VerticesPerPrimitive"/> is 3.
|
|
|
+ /// Gets the indices of all the surfaces as triangles, given that <see cref="VerticesPerPrimitive"/> is 3.
|
|
|
/// </summary>
|
|
|
IReadOnlyList<(int, int, int)> Triangles { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the indices of all the surfaces, given that <see cref="VerticesPerPrimitive"/> is 3.
|
|
|
+ /// </summary>
|
|
|
+ IReadOnlyList<(int, int, int, int?)> Surfaces { get; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Calculates the raw list of indices to use for this primitive.
|
|
|
+ /// </summary>
|
|
|
+ /// <returns>a list of indices.</returns>
|
|
|
+ IReadOnlyList<int> GetIndices();
|
|
|
}
|
|
|
|
|
|
public interface IPrimitiveBuilder
|
|
|
@@ -95,22 +100,17 @@ namespace SharpGLTF.Geometry
|
|
|
/// <see cref="VertexJoints16x4"/>,
|
|
|
/// <see cref="VertexJoints16x8"/>.
|
|
|
/// </typeparam>
|
|
|
- [System.Diagnostics.DebuggerDisplay("Primitive {_Material}")]
|
|
|
- public class PrimitiveBuilder<TMaterial, TvG, TvM, TvS> : IPrimitiveBuilder, IPrimitiveReader<TMaterial>
|
|
|
+ public abstract class PrimitiveBuilder<TMaterial, TvG, TvM, TvS> : IPrimitiveBuilder, IPrimitiveReader<TMaterial>
|
|
|
where TvG : struct, IVertexGeometry
|
|
|
where TvM : struct, IVertexMaterial
|
|
|
where TvS : struct, IVertexSkinning
|
|
|
{
|
|
|
#region lifecycle
|
|
|
|
|
|
- internal PrimitiveBuilder(MeshBuilder<TMaterial, TvG, TvM, TvS> mesh, TMaterial material, int primitiveVertexCount)
|
|
|
+ internal PrimitiveBuilder(MeshBuilder<TMaterial, TvG, TvM, TvS> mesh, TMaterial material)
|
|
|
{
|
|
|
this._Mesh = mesh;
|
|
|
this._Material = material;
|
|
|
- this._PrimitiveVertexCount = primitiveVertexCount;
|
|
|
-
|
|
|
- if (this._PrimitiveVertexCount == 2) _LinesIndices = new LineListWrapper(_Indices);
|
|
|
- if (this._PrimitiveVertexCount == 3) _TrianglesIndices = new TriangleListWrapper(_Indices);
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
@@ -121,14 +121,8 @@ namespace SharpGLTF.Geometry
|
|
|
|
|
|
private readonly TMaterial _Material;
|
|
|
|
|
|
- private readonly int _PrimitiveVertexCount;
|
|
|
-
|
|
|
private readonly VertexListWrapper _Vertices = new VertexListWrapper();
|
|
|
|
|
|
- private readonly List<int> _Indices = new List<int>();
|
|
|
- private readonly LineListWrapper _LinesIndices;
|
|
|
- private readonly TriangleListWrapper _TrianglesIndices;
|
|
|
-
|
|
|
#endregion
|
|
|
|
|
|
#region properties
|
|
|
@@ -143,26 +137,42 @@ namespace SharpGLTF.Geometry
|
|
|
/// 2 - Lines
|
|
|
/// 3 - Triangles
|
|
|
/// </summary>
|
|
|
- public int VerticesPerPrimitive => _PrimitiveVertexCount;
|
|
|
+ public abstract int VerticesPerPrimitive { get; }
|
|
|
+
|
|
|
+ public Type VertexType => typeof(VertexBuilder<TvG, TvM, TvS>);
|
|
|
|
|
|
public IReadOnlyList<VertexBuilder<TvG, TvM, TvS>> Vertices => _Vertices;
|
|
|
|
|
|
IReadOnlyList<IVertexBuilder> IPrimitiveReader<TMaterial>.Vertices => _Vertices;
|
|
|
|
|
|
- public IReadOnlyList<int> Indices => _Indices;
|
|
|
-
|
|
|
- public IReadOnlyList<int> Points => _GetPointIndices();
|
|
|
+ public virtual IReadOnlyList<int> Points => Array.Empty<int>();
|
|
|
|
|
|
- public IReadOnlyList<(int, int)> Lines => _GetLineIndices();
|
|
|
+ public virtual IReadOnlyList<(int, int)> Lines => Array.Empty<(int, int)>();
|
|
|
|
|
|
- public IReadOnlyList<(int, int, int)> Triangles => _GetTriangleIndices();
|
|
|
+ public virtual IReadOnlyList<(int, int, int)> Triangles => Array.Empty<(int, int, int)>();
|
|
|
|
|
|
- public Type VertexType => typeof(VertexBuilder<TvG, TvM, TvS>);
|
|
|
+ public virtual IReadOnlyList<(int, int, int, int?)> Surfaces => Array.Empty<(int, int, int, int?)>();
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region API
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Checks if <paramref name="v"/> is a compatible vertex and casts it, or converts it if it is not.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="v">Any vertex</param>
|
|
|
+ /// <returns>A vertex compatible with this primitive.</returns>
|
|
|
+ private static VertexBuilder<TvG, TvM, TvS> ConvertVertex(IVertexBuilder v)
|
|
|
+ {
|
|
|
+ Guard.NotNull(v, nameof(v));
|
|
|
+
|
|
|
+ var vv = v.ConvertTo<TvG, TvM, TvS>();
|
|
|
+
|
|
|
+ System.Diagnostics.Debug.Assert(vv.Position == v.GetGeometry().GetPosition());
|
|
|
+
|
|
|
+ return vv;
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Adds or reuses a vertex.
|
|
|
/// </summary>
|
|
|
@@ -173,13 +183,8 @@ namespace SharpGLTF.Geometry
|
|
|
/// <typeparamref name="TvS"/> fragments.
|
|
|
/// </param>
|
|
|
/// <returns>The index of the vertex.</returns>
|
|
|
- public int UseVertex(VertexBuilder<TvG, TvM, TvS> vertex)
|
|
|
+ protected int UseVertex(VertexBuilder<TvG, TvM, TvS> vertex)
|
|
|
{
|
|
|
- if (_Mesh.VertexPreprocessor != null)
|
|
|
- {
|
|
|
- if (!_Mesh.VertexPreprocessor.PreprocessVertex(ref vertex)) return -1;
|
|
|
- }
|
|
|
-
|
|
|
return _Vertices.Use(vertex);
|
|
|
}
|
|
|
|
|
|
@@ -190,25 +195,7 @@ namespace SharpGLTF.Geometry
|
|
|
/// <returns>The indices of the vertices.</returns>
|
|
|
public int AddPoint(IVertexBuilder a)
|
|
|
{
|
|
|
- Guard.NotNull(a, nameof(a));
|
|
|
-
|
|
|
- var aa = a.ConvertTo<TvG, TvM, TvS>();
|
|
|
-
|
|
|
- System.Diagnostics.Debug.Assert(aa.Position == a.GetGeometry().GetPosition());
|
|
|
-
|
|
|
- return AddPoint(aa);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Adds a point.
|
|
|
- /// </summary>
|
|
|
- /// <param name="a">vertex for this point.</param>
|
|
|
- /// <returns>The index of the vertex.</returns>
|
|
|
- public int AddPoint(VertexBuilder<TvG, TvM, TvS> a)
|
|
|
- {
|
|
|
- Guard.IsTrue(_PrimitiveVertexCount == 1, nameof(VerticesPerPrimitive), "Points are not supported for this primitive");
|
|
|
-
|
|
|
- return UseVertex(a);
|
|
|
+ return AddPoint(ConvertVertex(a));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -219,46 +206,7 @@ namespace SharpGLTF.Geometry
|
|
|
/// <returns>The indices of the vertices, or, in case the line is degenerated, (-1,-1).</returns>
|
|
|
public (int, int) AddLine(IVertexBuilder a, IVertexBuilder b)
|
|
|
{
|
|
|
- Guard.NotNull(a, nameof(a));
|
|
|
- Guard.NotNull(b, nameof(b));
|
|
|
-
|
|
|
- var aa = a.ConvertTo<TvG, TvM, TvS>();
|
|
|
- var bb = b.ConvertTo<TvG, TvM, TvS>();
|
|
|
-
|
|
|
- System.Diagnostics.Debug.Assert(aa.Position == a.GetGeometry().GetPosition());
|
|
|
- System.Diagnostics.Debug.Assert(bb.Position == b.GetGeometry().GetPosition());
|
|
|
-
|
|
|
- return AddLine(aa, bb);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Adds a line.
|
|
|
- /// </summary>
|
|
|
- /// <param name="a">First corner of the line.</param>
|
|
|
- /// <param name="b">Second corner of the line.</param>
|
|
|
- /// <returns>The indices of the vertices, or, in case the line is degenerated, (-1,-1).</returns>
|
|
|
- public (int, int) AddLine(VertexBuilder<TvG, TvM, TvS> a, VertexBuilder<TvG, TvM, TvS> b)
|
|
|
- {
|
|
|
- Guard.IsTrue(_PrimitiveVertexCount == 2, nameof(VerticesPerPrimitive), "Lines are not supported for this primitive");
|
|
|
-
|
|
|
- if (_Mesh.VertexPreprocessor != null)
|
|
|
- {
|
|
|
- if (!_Mesh.VertexPreprocessor.PreprocessVertex(ref a)) return (-1, -1);
|
|
|
- if (!_Mesh.VertexPreprocessor.PreprocessVertex(ref b)) return (-1, -1);
|
|
|
- }
|
|
|
-
|
|
|
- var aa = _Vertices.Use(a);
|
|
|
- var bb = _Vertices.Use(b);
|
|
|
-
|
|
|
- // check for degenerated line
|
|
|
- if (aa == bb) return (-1, -1);
|
|
|
-
|
|
|
- // TODO: check if a triangle with indices aa-bb-cc already exists.
|
|
|
-
|
|
|
- _Indices.Add(aa);
|
|
|
- _Indices.Add(bb);
|
|
|
-
|
|
|
- return (aa, bb);
|
|
|
+ return AddLine(ConvertVertex(a), ConvertVertex(b));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -270,40 +218,7 @@ namespace SharpGLTF.Geometry
|
|
|
/// <returns>The indices of the vertices, or, in case the triangle is degenerated, (-1,-1,-1).</returns>
|
|
|
public (int, int, int) AddTriangle(IVertexBuilder a, IVertexBuilder b, IVertexBuilder c)
|
|
|
{
|
|
|
- Guard.NotNull(a, nameof(a));
|
|
|
- Guard.NotNull(b, nameof(b));
|
|
|
- Guard.NotNull(c, nameof(c));
|
|
|
-
|
|
|
- var aa = a.ConvertTo<TvG, TvM, TvS>();
|
|
|
- var bb = b.ConvertTo<TvG, TvM, TvS>();
|
|
|
- var cc = c.ConvertTo<TvG, TvM, TvS>();
|
|
|
-
|
|
|
- System.Diagnostics.Debug.Assert(aa.Position == a.GetGeometry().GetPosition());
|
|
|
- System.Diagnostics.Debug.Assert(bb.Position == b.GetGeometry().GetPosition());
|
|
|
- System.Diagnostics.Debug.Assert(cc.Position == c.GetGeometry().GetPosition());
|
|
|
-
|
|
|
- return AddTriangle(aa, bb, cc);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Adds a triangle.
|
|
|
- /// </summary>
|
|
|
- /// <param name="a">First corner of the triangle.</param>
|
|
|
- /// <param name="b">Second corner of the triangle.</param>
|
|
|
- /// <param name="c">Third corner of the triangle.</param>
|
|
|
- /// <returns>The indices of the vertices, or, in case the triangle is degenerated, (-1,-1,-1).</returns>
|
|
|
- public (int, int, int) AddTriangle(VertexBuilder<TvG, TvM, TvS> a, VertexBuilder<TvG, TvM, TvS> b, VertexBuilder<TvG, TvM, TvS> c)
|
|
|
- {
|
|
|
- Guard.IsTrue(_PrimitiveVertexCount == 3, nameof(VerticesPerPrimitive), "Triangles are not supported for this primitive");
|
|
|
-
|
|
|
- if (_Mesh.VertexPreprocessor != null)
|
|
|
- {
|
|
|
- if (!_Mesh.VertexPreprocessor.PreprocessVertex(ref a)) return (-1, -1, -1);
|
|
|
- if (!_Mesh.VertexPreprocessor.PreprocessVertex(ref b)) return (-1, -1, -1);
|
|
|
- if (!_Mesh.VertexPreprocessor.PreprocessVertex(ref c)) return (-1, -1, -1);
|
|
|
- }
|
|
|
-
|
|
|
- return _AddTriangle(a, b, c);
|
|
|
+ return AddTriangle(ConvertVertex(a), ConvertVertex(b), ConvertVertex(c));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -320,100 +235,14 @@ namespace SharpGLTF.Geometry
|
|
|
/// </remarks>
|
|
|
public (int, int, int, int) AddQuadrangle(IVertexBuilder a, IVertexBuilder b, IVertexBuilder c, IVertexBuilder d)
|
|
|
{
|
|
|
- Guard.NotNull(a, nameof(a));
|
|
|
- Guard.NotNull(b, nameof(b));
|
|
|
- Guard.NotNull(c, nameof(c));
|
|
|
- Guard.NotNull(d, nameof(d));
|
|
|
-
|
|
|
- var aa = a.ConvertTo<TvG, TvM, TvS>();
|
|
|
- var bb = b.ConvertTo<TvG, TvM, TvS>();
|
|
|
- var cc = c.ConvertTo<TvG, TvM, TvS>();
|
|
|
- var dd = d.ConvertTo<TvG, TvM, TvS>();
|
|
|
-
|
|
|
- System.Diagnostics.Debug.Assert(aa.Position == a.GetGeometry().GetPosition());
|
|
|
- System.Diagnostics.Debug.Assert(bb.Position == b.GetGeometry().GetPosition());
|
|
|
- System.Diagnostics.Debug.Assert(cc.Position == c.GetGeometry().GetPosition());
|
|
|
- System.Diagnostics.Debug.Assert(dd.Position == d.GetGeometry().GetPosition());
|
|
|
-
|
|
|
- return AddQuadrangle(aa, bb, cc, dd);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Adds a quadrangle.
|
|
|
- /// </summary>
|
|
|
- /// <param name="a">First corner of the quadrangle.</param>
|
|
|
- /// <param name="b">Second corner of the quadrangle.</param>
|
|
|
- /// <param name="c">Third corner of the quadrangle.</param>
|
|
|
- /// <param name="d">Fourth corner of the quadrangle.</param>
|
|
|
- /// <returns>The indices of the vertices, or, in case the quadrangle is degenerated, (-1,-1,-1,-1).</returns>
|
|
|
- public (int, int, int, int) AddQuadrangle(VertexBuilder<TvG, TvM, TvS> a, VertexBuilder<TvG, TvM, TvS> b, VertexBuilder<TvG, TvM, TvS> c, VertexBuilder<TvG, TvM, TvS> d)
|
|
|
- {
|
|
|
- Guard.IsTrue(_PrimitiveVertexCount == 3, nameof(VerticesPerPrimitive), "Quadrangles are not supported for this primitive");
|
|
|
-
|
|
|
- if (_Mesh.VertexPreprocessor != null)
|
|
|
- {
|
|
|
- if (!_Mesh.VertexPreprocessor.PreprocessVertex(ref a)) return (-1, -1, -1, -1);
|
|
|
- if (!_Mesh.VertexPreprocessor.PreprocessVertex(ref b)) return (-1, -1, -1, -1);
|
|
|
- if (!_Mesh.VertexPreprocessor.PreprocessVertex(ref c)) return (-1, -1, -1, -1);
|
|
|
- if (!_Mesh.VertexPreprocessor.PreprocessVertex(ref d)) return (-1, -1, -1, -1);
|
|
|
- }
|
|
|
-
|
|
|
- // at some points it could be interesting to comply with https://github.com/KhronosGroup/glTF/pull/1620
|
|
|
-
|
|
|
- var oddEven = MeshBuilderToolkit.GetQuadrangleDiagonal(a.Position, b.Position, c.Position, d.Position);
|
|
|
-
|
|
|
- if (oddEven)
|
|
|
- {
|
|
|
- var f = _AddTriangle(a, b, c);
|
|
|
- var s = _AddTriangle(a, c, d);
|
|
|
- return
|
|
|
- (
|
|
|
- f.Item1 > s.Item1 ? f.Item1 : s.Item1,
|
|
|
- f.Item2,
|
|
|
- f.Item3 > s.Item2 ? f.Item3 : s.Item2,
|
|
|
- s.Item3
|
|
|
- );
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- var f = _AddTriangle(b, c, d);
|
|
|
- var s = _AddTriangle(b, d, a);
|
|
|
-
|
|
|
- return
|
|
|
- (
|
|
|
- s.Item3,
|
|
|
- f.Item1 > s.Item1 ? f.Item1 : s.Item1,
|
|
|
- f.Item2,
|
|
|
- f.Item3 > s.Item2 ? f.Item3 : s.Item2
|
|
|
- );
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private (int, int, int) _AddTriangle(VertexBuilder<TvG, TvM, TvS> a, VertexBuilder<TvG, TvM, TvS> b, VertexBuilder<TvG, TvM, TvS> c)
|
|
|
- {
|
|
|
- // check for degenerated triangle
|
|
|
- if (a.Equals(b) || a.Equals(c) || b.Equals(c)) return (-1, -1, -1);
|
|
|
-
|
|
|
- var aa = _Vertices.Use(a);
|
|
|
- var bb = _Vertices.Use(b);
|
|
|
- var cc = _Vertices.Use(c);
|
|
|
-
|
|
|
- System.Diagnostics.Debug.Assert(aa != bb && aa != cc && bb != cc, "unexpected degenerated triangle");
|
|
|
-
|
|
|
- // TODO: check if a triangle with indices aa-bb-cc already exists, since there's no point in having the same polygon twice.
|
|
|
-
|
|
|
- _Indices.Add(aa);
|
|
|
- _Indices.Add(bb);
|
|
|
- _Indices.Add(cc);
|
|
|
-
|
|
|
- return (aa, bb, cc);
|
|
|
+ return AddQuadrangle(ConvertVertex(a), ConvertVertex(b), ConvertVertex(c), ConvertVertex(d));
|
|
|
}
|
|
|
|
|
|
internal void AddPrimitive(PrimitiveBuilder<TMaterial, TvG, TvM, TvS> primitive, Func<VertexBuilder<TvG, TvM, TvS>, VertexBuilder<TvG, TvM, TvS>> vertexTransform)
|
|
|
{
|
|
|
if (primitive == null) return;
|
|
|
|
|
|
- if (_PrimitiveVertexCount == 1)
|
|
|
+ if (this.VerticesPerPrimitive == 1)
|
|
|
{
|
|
|
foreach (var p in primitive.Points)
|
|
|
{
|
|
|
@@ -425,7 +254,7 @@ namespace SharpGLTF.Geometry
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (_PrimitiveVertexCount == 2)
|
|
|
+ if (this.VerticesPerPrimitive == 2)
|
|
|
{
|
|
|
foreach (var l in primitive.Lines)
|
|
|
{
|
|
|
@@ -438,15 +267,23 @@ namespace SharpGLTF.Geometry
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (_PrimitiveVertexCount == 3)
|
|
|
+ if (this.VerticesPerPrimitive == 3)
|
|
|
{
|
|
|
- foreach (var t in primitive.Triangles)
|
|
|
+ foreach (var s in primitive.Surfaces)
|
|
|
{
|
|
|
- var a = vertexTransform(primitive.Vertices[t.Item1]);
|
|
|
- var b = vertexTransform(primitive.Vertices[t.Item2]);
|
|
|
- var c = vertexTransform(primitive.Vertices[t.Item3]);
|
|
|
-
|
|
|
- AddTriangle(a, b, c);
|
|
|
+ var a = vertexTransform(primitive.Vertices[s.Item1]);
|
|
|
+ var b = vertexTransform(primitive.Vertices[s.Item2]);
|
|
|
+ var c = vertexTransform(primitive.Vertices[s.Item3]);
|
|
|
+
|
|
|
+ if (s.Item4.HasValue)
|
|
|
+ {
|
|
|
+ var d = vertexTransform(primitive.Vertices[s.Item4.Value]);
|
|
|
+ AddQuadrangle(a, b, c, d);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ AddTriangle(a, b, c);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
@@ -461,33 +298,68 @@ namespace SharpGLTF.Geometry
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private IReadOnlyList<int> _GetPointIndices()
|
|
|
+ public void TransformVertices(Func<VertexBuilder<TvG, TvM, TvS>, VertexBuilder<TvG, TvM, TvS>> transformFunc)
|
|
|
{
|
|
|
- if (_PrimitiveVertexCount != 1) return Array.Empty<int>();
|
|
|
+ _Vertices.TransformVertices(transformFunc);
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region abstract API
|
|
|
|
|
|
- return new PointListWrapper(_Vertices);
|
|
|
+ public abstract IReadOnlyList<int> GetIndices();
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Adds a point.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="a">vertex for this point.</param>
|
|
|
+ /// <returns>The index of the vertex.</returns>
|
|
|
+ public virtual int AddPoint(VertexBuilder<TvG, TvM, TvS> a)
|
|
|
+ {
|
|
|
+ throw new NotSupportedException("Points are not supported for this primitive");
|
|
|
}
|
|
|
|
|
|
- private IReadOnlyList<(int, int)> _GetLineIndices()
|
|
|
+ /// <summary>
|
|
|
+ /// Adds a line.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="a">First corner of the line.</param>
|
|
|
+ /// <param name="b">Second corner of the line.</param>
|
|
|
+ /// <returns>The indices of the vertices, or, in case the line is degenerated, (-1,-1).</returns>
|
|
|
+ public virtual (int, int) AddLine(VertexBuilder<TvG, TvM, TvS> a, VertexBuilder<TvG, TvM, TvS> b)
|
|
|
{
|
|
|
- return _LinesIndices ?? (IReadOnlyList<(int, int)>)Array.Empty<(int, int)>();
|
|
|
+ throw new NotSupportedException("Lines are not supported for this primitive");
|
|
|
}
|
|
|
|
|
|
- private IReadOnlyList<(int, int, int)> _GetTriangleIndices()
|
|
|
+ /// <summary>
|
|
|
+ /// Adds a triangle.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="a">First corner of the triangle.</param>
|
|
|
+ /// <param name="b">Second corner of the triangle.</param>
|
|
|
+ /// <param name="c">Third corner of the triangle.</param>
|
|
|
+ /// <returns>The indices of the vertices, or, in case the triangle is degenerated, (-1,-1,-1).</returns>
|
|
|
+ public virtual (int, int, int) AddTriangle(VertexBuilder<TvG, TvM, TvS> a, VertexBuilder<TvG, TvM, TvS> b, VertexBuilder<TvG, TvM, TvS> c)
|
|
|
{
|
|
|
- return _TrianglesIndices ?? (IReadOnlyList<(int, int, int)>)Array.Empty<(int, int, int)>();
|
|
|
+ throw new NotSupportedException("Triangles are not supported for this primitive");
|
|
|
}
|
|
|
|
|
|
- public void TransformVertices(Func<VertexBuilder<TvG, TvM, TvS>, VertexBuilder<TvG, TvM, TvS>> transformFunc)
|
|
|
+ /// <summary>
|
|
|
+ /// Adds a quadrangle.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="a">First corner of the quadrangle.</param>
|
|
|
+ /// <param name="b">Second corner of the quadrangle.</param>
|
|
|
+ /// <param name="c">Third corner of the quadrangle.</param>
|
|
|
+ /// <param name="d">Fourth corner of the quadrangle.</param>
|
|
|
+ /// <returns>The indices of the vertices, or, in case the quadrangle is degenerated, (-1,-1,-1,-1).</returns>
|
|
|
+ public virtual (int, int, int, int) AddQuadrangle(VertexBuilder<TvG, TvM, TvS> a, VertexBuilder<TvG, TvM, TvS> b, VertexBuilder<TvG, TvM, TvS> c, VertexBuilder<TvG, TvM, TvS> d)
|
|
|
{
|
|
|
- _Vertices.TransformVertices(transformFunc);
|
|
|
+ throw new NotSupportedException("Quadrangles are not supported for this primitive");
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region helper types
|
|
|
|
|
|
- sealed class VertexListWrapper : VertexList<VertexBuilder<TvG, TvM, TvS>>, IReadOnlyList<IVertexBuilder>
|
|
|
+ private sealed class VertexListWrapper : VertexList<VertexBuilder<TvG, TvM, TvS>>, IReadOnlyList<IVertexBuilder>
|
|
|
{
|
|
|
#pragma warning disable SA1100 // Do not prefix calls with base unless local implementation exists
|
|
|
IVertexBuilder IReadOnlyList<IVertexBuilder>.this[int index] => base[index];
|
|
|
@@ -499,14 +371,58 @@ namespace SharpGLTF.Geometry
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- struct PointListWrapper : IReadOnlyList<int>
|
|
|
+ #endregion
|
|
|
+ }
|
|
|
+
|
|
|
+ [System.Diagnostics.DebuggerDisplay("Points[{Points.Count}] {_Material}")]
|
|
|
+ sealed class PointsPrimitiveBuilder<TMaterial, TvG, TvM, TvS> : PrimitiveBuilder<TMaterial, TvG, TvM, TvS>
|
|
|
+ where TvG : struct, IVertexGeometry
|
|
|
+ where TvM : struct, IVertexMaterial
|
|
|
+ where TvS : struct, IVertexSkinning
|
|
|
+ {
|
|
|
+ #region lifecycle
|
|
|
+
|
|
|
+ internal PointsPrimitiveBuilder(MeshBuilder<TMaterial, TvG, TvM, TvS> mesh, TMaterial material)
|
|
|
+ : base(mesh, material)
|
|
|
{
|
|
|
- public PointListWrapper(IReadOnlyList<IVertexBuilder> vertices)
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region properties
|
|
|
+
|
|
|
+ public override int VerticesPerPrimitive => 1;
|
|
|
+
|
|
|
+ public override IReadOnlyList<int> Points => new PointListWrapper<VertexBuilder<TvG, TvM, TvS>>(this.Vertices);
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region API
|
|
|
+
|
|
|
+ public override int AddPoint(VertexBuilder<TvG, TvM, TvS> a)
|
|
|
+ {
|
|
|
+ if (Mesh.VertexPreprocessor != null)
|
|
|
+ {
|
|
|
+ if (!Mesh.VertexPreprocessor.PreprocessVertex(ref a)) return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return UseVertex(a);
|
|
|
+ }
|
|
|
+
|
|
|
+ public override IReadOnlyList<int> GetIndices() { return Array.Empty<int>(); }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region types
|
|
|
+
|
|
|
+ struct PointListWrapper<T> : IReadOnlyList<int>
|
|
|
+ {
|
|
|
+ public PointListWrapper(IReadOnlyList<T> vertices)
|
|
|
{
|
|
|
_Vertices = vertices;
|
|
|
}
|
|
|
|
|
|
- private readonly IReadOnlyList<IVertexBuilder> _Vertices;
|
|
|
+ private readonly IReadOnlyList<T> _Vertices;
|
|
|
|
|
|
public int this[int index] => index;
|
|
|
|
|
|
@@ -517,24 +433,242 @@ namespace SharpGLTF.Geometry
|
|
|
IEnumerator IEnumerable.GetEnumerator() { return Enumerable.Range(0, _Vertices.Count).GetEnumerator(); }
|
|
|
}
|
|
|
|
|
|
- sealed class LineListWrapper : IReadOnlyList<(int, int)>
|
|
|
+ #endregion
|
|
|
+ }
|
|
|
+
|
|
|
+ [System.Diagnostics.DebuggerDisplay("Lines[{Lines.Count}] {_Material}")]
|
|
|
+ sealed class LinesPrimitiveBuilder<TMaterial, TvG, TvM, TvS> : PrimitiveBuilder<TMaterial, TvG, TvM, TvS>
|
|
|
+ where TvG : struct, IVertexGeometry
|
|
|
+ where TvM : struct, IVertexMaterial
|
|
|
+ where TvS : struct, IVertexSkinning
|
|
|
+ {
|
|
|
+ #region lifecycle
|
|
|
+
|
|
|
+ internal LinesPrimitiveBuilder(MeshBuilder<TMaterial, TvG, TvM, TvS> mesh, TMaterial material)
|
|
|
+ : base(mesh, material)
|
|
|
{
|
|
|
- public LineListWrapper(List<int> source) { _Indices = source; }
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region data
|
|
|
+
|
|
|
+ private readonly List<(int, int)> _Indices = new List<(int, int)>();
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region properties
|
|
|
+
|
|
|
+ public override int VerticesPerPrimitive => 2;
|
|
|
|
|
|
- private readonly IList<int> _Indices;
|
|
|
+ public override IReadOnlyList<(int, int)> Lines => _Indices;
|
|
|
|
|
|
- public (int, int) this[int index]
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region API
|
|
|
+
|
|
|
+ public override (int, int) AddLine(VertexBuilder<TvG, TvM, TvS> a, VertexBuilder<TvG, TvM, TvS> b)
|
|
|
+ {
|
|
|
+ if (Mesh.VertexPreprocessor != null)
|
|
|
+ {
|
|
|
+ if (!Mesh.VertexPreprocessor.PreprocessVertex(ref a)) return (-1, -1);
|
|
|
+ if (!Mesh.VertexPreprocessor.PreprocessVertex(ref b)) return (-1, -1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // check for degenerated line
|
|
|
+ if (a.Position == b.Position) return (-1, -1);
|
|
|
+
|
|
|
+ var aa = UseVertex(a);
|
|
|
+ var bb = UseVertex(b);
|
|
|
+
|
|
|
+ System.Diagnostics.Debug.Assert(aa != bb, "unexpected degenerated line");
|
|
|
+
|
|
|
+ _Indices.Add((aa, bb));
|
|
|
+
|
|
|
+ return (aa, bb);
|
|
|
+ }
|
|
|
+
|
|
|
+ public override IReadOnlyList<int> GetIndices()
|
|
|
+ {
|
|
|
+ return _Indices
|
|
|
+ .SelectMany(item => new[] { item.Item1, item.Item2 })
|
|
|
+ .ToList();
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+ }
|
|
|
+
|
|
|
+ [System.Diagnostics.DebuggerDisplay("Triangles[{Triangles.Count}] {_Material}")]
|
|
|
+ sealed class TrianglesPrimitiveBuilder<TMaterial, TvG, TvM, TvS> : PrimitiveBuilder<TMaterial, TvG, TvM, TvS>
|
|
|
+ where TvG : struct, IVertexGeometry
|
|
|
+ where TvM : struct, IVertexMaterial
|
|
|
+ where TvS : struct, IVertexSkinning
|
|
|
+ {
|
|
|
+ #region lifecycle
|
|
|
+
|
|
|
+ internal TrianglesPrimitiveBuilder(MeshBuilder<TMaterial, TvG, TvM, TvS> mesh, TMaterial material)
|
|
|
+ : base(mesh, material)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region data
|
|
|
+
|
|
|
+ private readonly List<(int, int, int)> _TriIndices = new List<(int, int, int)>();
|
|
|
+ private readonly List<(int, int, int, int)> _QuadIndices = new List<(int, int, int, int)>();
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region properties
|
|
|
+
|
|
|
+ public override int VerticesPerPrimitive => 3;
|
|
|
+
|
|
|
+ public override IReadOnlyList<(int, int, int)> Triangles => new TriangleList(_TriIndices, _QuadIndices);
|
|
|
+
|
|
|
+ public override IReadOnlyList<(int, int, int, int?)> Surfaces => new SurfaceList(_TriIndices, _QuadIndices);
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region API
|
|
|
+
|
|
|
+ public override (int, int, int) AddTriangle(VertexBuilder<TvG, TvM, TvS> a, VertexBuilder<TvG, TvM, TvS> b, VertexBuilder<TvG, TvM, TvS> c)
|
|
|
+ {
|
|
|
+ if (Mesh.VertexPreprocessor != null)
|
|
|
+ {
|
|
|
+ if (!Mesh.VertexPreprocessor.PreprocessVertex(ref a)) return (-1, -1, -1);
|
|
|
+ if (!Mesh.VertexPreprocessor.PreprocessVertex(ref b)) return (-1, -1, -1);
|
|
|
+ if (!Mesh.VertexPreprocessor.PreprocessVertex(ref c)) return (-1, -1, -1);
|
|
|
+ }
|
|
|
+
|
|
|
+ return _AddTriangle(a, b, c);
|
|
|
+ }
|
|
|
+
|
|
|
+ public override (int, int, int, int) AddQuadrangle(VertexBuilder<TvG, TvM, TvS> a, VertexBuilder<TvG, TvM, TvS> b, VertexBuilder<TvG, TvM, TvS> c, VertexBuilder<TvG, TvM, TvS> d)
|
|
|
+ {
|
|
|
+ if (Mesh.VertexPreprocessor != null)
|
|
|
+ {
|
|
|
+ if (!Mesh.VertexPreprocessor.PreprocessVertex(ref a)) return (-1, -1, -1, -1);
|
|
|
+ if (!Mesh.VertexPreprocessor.PreprocessVertex(ref b)) return (-1, -1, -1, -1);
|
|
|
+ if (!Mesh.VertexPreprocessor.PreprocessVertex(ref c)) return (-1, -1, -1, -1);
|
|
|
+ if (!Mesh.VertexPreprocessor.PreprocessVertex(ref d)) return (-1, -1, -1, -1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // if diagonals are degenerated, the whole quad is degenerated.
|
|
|
+ if (a.Position == c.Position || b.Position == d.Position) return (-1, -1, -1, -1);
|
|
|
+
|
|
|
+ // A-B degenerated
|
|
|
+ if (a.Position == b.Position)
|
|
|
+ {
|
|
|
+ var tri = _AddTriangle(b, c, d);
|
|
|
+ return (-1, tri.Item1, tri.Item2, tri.Item3);
|
|
|
+ }
|
|
|
+
|
|
|
+ // B-C degenerated
|
|
|
+ if (b.Position == c.Position)
|
|
|
+ {
|
|
|
+ var tri = _AddTriangle(a, c, d);
|
|
|
+ return (tri.Item1, -1, tri.Item2, tri.Item3);
|
|
|
+ }
|
|
|
+
|
|
|
+ // C-D degenerated
|
|
|
+ if (c.Position == d.Position)
|
|
|
+ {
|
|
|
+ var tri = _AddTriangle(a, b, d);
|
|
|
+ return (tri.Item1, tri.Item2, -1, tri.Item3);
|
|
|
+ }
|
|
|
+
|
|
|
+ // D-A degenerated
|
|
|
+ if (d.Position == a.Position)
|
|
|
+ {
|
|
|
+ var tri = _AddTriangle(a, b, c);
|
|
|
+ return (tri.Item1, tri.Item2, tri.Item3, -1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // at some points it could be interesting to comply with https://github.com/KhronosGroup/glTF/pull/1620
|
|
|
+
|
|
|
+ var aa = UseVertex(a);
|
|
|
+ var bb = UseVertex(b);
|
|
|
+ var cc = UseVertex(c);
|
|
|
+ var dd = UseVertex(d);
|
|
|
+
|
|
|
+ System.Diagnostics.Debug.Assert(aa != bb && aa != cc && bb != cc && cc != dd, "unexpected degenerated triangle");
|
|
|
+
|
|
|
+ var oddEven = MeshBuilderToolkit.GetQuadrangleDiagonal(a.Position, b.Position, c.Position, d.Position);
|
|
|
+
|
|
|
+ if (oddEven)
|
|
|
+ {
|
|
|
+ _QuadIndices.Add((aa, bb, cc, dd));
|
|
|
+ return (aa, bb, cc, dd);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _QuadIndices.Add((bb, cc, dd, aa));
|
|
|
+ return (aa, bb, cc, dd); // notice that we return the indices in the same order we got them.
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private (int, int, int) _AddTriangle(VertexBuilder<TvG, TvM, TvS> a, VertexBuilder<TvG, TvM, TvS> b, VertexBuilder<TvG, TvM, TvS> c)
|
|
|
+ {
|
|
|
+ // check for degenerated triangle
|
|
|
+ if (a.Position == b.Position || a.Position == c.Position || b.Position == c.Position) return (-1, -1, -1);
|
|
|
+
|
|
|
+ var aa = UseVertex(a);
|
|
|
+ var bb = UseVertex(b);
|
|
|
+ var cc = UseVertex(c);
|
|
|
+
|
|
|
+ System.Diagnostics.Debug.Assert(aa != bb && aa != cc && bb != cc, "unexpected degenerated triangle");
|
|
|
+
|
|
|
+ // TODO: check if a triangle with indices aa-bb-cc, bb-cc-aa or cc-aa-bb already exists, since there's no point in having the same polygon twice.
|
|
|
+
|
|
|
+ _TriIndices.Add((aa, bb, cc));
|
|
|
+
|
|
|
+ return (aa, bb, cc);
|
|
|
+ }
|
|
|
+
|
|
|
+ public override IReadOnlyList<int> GetIndices()
|
|
|
+ {
|
|
|
+ return Triangles
|
|
|
+ .SelectMany(item => new[] { item.Item1, item.Item2, item.Item3 })
|
|
|
+ .ToList();
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Types
|
|
|
+
|
|
|
+ private struct TriangleList : IReadOnlyList<(int, int, int)>
|
|
|
+ {
|
|
|
+ public TriangleList(IReadOnlyList<(int, int, int)> tris, IReadOnlyList<(int, int, int, int)> quads)
|
|
|
+ {
|
|
|
+ _Tris = tris;
|
|
|
+ _Quads = quads;
|
|
|
+ }
|
|
|
+
|
|
|
+ private readonly IReadOnlyList<(int, int, int)> _Tris;
|
|
|
+ private readonly IReadOnlyList<(int, int, int, int)> _Quads;
|
|
|
+
|
|
|
+ public int Count => _Tris.Count + (_Quads.Count * 2);
|
|
|
+
|
|
|
+ public (int, int, int) this[int index]
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
- index *= 2;
|
|
|
- return (_Indices[index + 0], _Indices[index + 1]);
|
|
|
+ if (index < _Tris.Count)
|
|
|
+ {
|
|
|
+ var tri = _Tris[index];
|
|
|
+ return tri;
|
|
|
+ }
|
|
|
+
|
|
|
+ index -= _Tris.Count;
|
|
|
+
|
|
|
+ var quad = _Quads[index / 2];
|
|
|
+
|
|
|
+ return (index & 1) == 0 ? (quad.Item1, quad.Item2, quad.Item3) : (quad.Item1, quad.Item3, quad.Item4);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public int Count => _Indices.Count / 2;
|
|
|
-
|
|
|
- public IEnumerator<(int, int)> GetEnumerator()
|
|
|
+ public IEnumerator<(int, int, int)> GetEnumerator()
|
|
|
{
|
|
|
var c = this.Count;
|
|
|
for (int i = 0; i < c; ++i) yield return this[i];
|
|
|
@@ -547,24 +681,37 @@ namespace SharpGLTF.Geometry
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- sealed class TriangleListWrapper : IReadOnlyList<(int, int, int)>
|
|
|
+ private struct SurfaceList : IReadOnlyList<(int, int, int, int?)>
|
|
|
{
|
|
|
- public TriangleListWrapper(List<int> source) { _Indices = source; }
|
|
|
+ public SurfaceList(IReadOnlyList<(int, int, int)> tris, IReadOnlyList<(int, int, int, int)> quads)
|
|
|
+ {
|
|
|
+ _Tris = tris;
|
|
|
+ _Quads = quads;
|
|
|
+ }
|
|
|
|
|
|
- private readonly List<int> _Indices;
|
|
|
+ private readonly IReadOnlyList<(int, int, int)> _Tris;
|
|
|
+ private readonly IReadOnlyList<(int, int, int, int)> _Quads;
|
|
|
|
|
|
- public (int, int, int) this[int index]
|
|
|
+ public int Count => _Tris.Count + _Quads.Count;
|
|
|
+
|
|
|
+ public (int, int, int, int?) this[int index]
|
|
|
{
|
|
|
get
|
|
|
{
|
|
|
- index *= 3;
|
|
|
- return (_Indices[index + 0], _Indices[index + 1], _Indices[index + 2]);
|
|
|
+ if (index < _Tris.Count)
|
|
|
+ {
|
|
|
+ var tri = _Tris[index];
|
|
|
+ return (tri.Item1, tri.Item2, tri.Item3, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ index -= _Tris.Count;
|
|
|
+
|
|
|
+ var quad = _Quads[index];
|
|
|
+ return (quad.Item1, quad.Item2, quad.Item3, quad.Item4);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public int Count => _Indices.Count / 3;
|
|
|
-
|
|
|
- public IEnumerator<(int, int, int)> GetEnumerator()
|
|
|
+ public IEnumerator<(int, int, int, int?)> GetEnumerator()
|
|
|
{
|
|
|
var c = this.Count;
|
|
|
for (int i = 0; i < c; ++i) yield return this[i];
|