using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; using SharpGLTF.Geometry.VertexTypes; namespace SharpGLTF.Geometry { public interface IPrimitiveMorphTargetReader { /// /// Gets the collection of vertex indices that have morph target deltas. /// /// A collection of vertex indices. IReadOnlyCollection GetTargetIndices(); /// /// Gets the vertex for the given morphed by the current morph target (if any). /// /// The index of the vertex. /// If the given index has a morphed vertex, it will return it, else ir will return the base vertex. IVertexGeometry GetVertex(int vertexIndex); /// /// Gets the of a given vertex for a given morph target. /// /// The index of the vertex. /// A Vertex delta (Morphed vertex minus base vertex). VertexGeometryDelta GetVertexDelta(int vertexIndex); } sealed class PrimitiveMorphTargetBuilder : IPrimitiveMorphTargetReader where TvG : struct, IVertexGeometry { #region lifecycle internal PrimitiveMorphTargetBuilder(Func baseVertexFunc) { this._BaseVertexFunc = baseVertexFunc; this._MorphVertices = new Dictionary(); } internal PrimitiveMorphTargetBuilder(Func baseVertexFunc, PrimitiveMorphTargetBuilder other) { this._BaseVertexFunc = baseVertexFunc; this._MorphVertices = new Dictionary(other._MorphVertices); } #endregion #region data private readonly Func _BaseVertexFunc; private readonly Dictionary _MorphVertices; #endregion #region API public IReadOnlyCollection GetTargetIndices() { return _MorphVertices.Keys; } public VertexGeometryDelta GetVertexDelta(int vertexIndex) { if (_MorphVertices.TryGetValue(vertexIndex, out TvG value)) { return value.Subtract(_BaseVertexFunc(vertexIndex)); } return default; } public void SetVertexDelta(int vertexIndex, VertexGeometryDelta value) { if (object.Equals(value, default(VertexGeometryDelta))) { _RemoveVertex(vertexIndex); return; } var vertex = _BaseVertexFunc(vertexIndex); vertex.Add(value); _SetVertex(vertexIndex, vertex); } IVertexGeometry IPrimitiveMorphTargetReader.GetVertex(int vertexIndex) { return _MorphVertices.TryGetValue(vertexIndex, out TvG value) ? value : _BaseVertexFunc(vertexIndex); } public TvG GetVertex(int vertexIndex) { return _MorphVertices.TryGetValue(vertexIndex, out TvG value) ? value : _BaseVertexFunc(vertexIndex); } public void SetVertex(int vertexIndex, TvG value) { if (object.Equals(value, _BaseVertexFunc(vertexIndex))) { _RemoveVertex(vertexIndex); return; } _SetVertex(vertexIndex, value); } private void _SetVertex(int vertexIndex, TvG value) { _MorphVertices[vertexIndex] = value; } private void _RemoveVertex(int vertexIndex) { _MorphVertices.Remove(vertexIndex); } #endregion #region internals internal void TransformVertices(Func vertexFunc) { foreach (var vidx in _MorphVertices.Keys) { var g = GetVertex(vidx); g = vertexFunc(g); SetVertex(vidx, g); } } internal void SetMorphTargets(IPrimitiveMorphTargetReader other, IReadOnlyDictionary vertexMap, Func vertexFunc) { Guard.NotNull(vertexFunc, nameof(vertexFunc)); var indices = other.GetTargetIndices(); foreach (var srcVidx in indices) { var g = vertexFunc(other.GetVertex(srcVidx)); var dstVidx = srcVidx; if (vertexMap != null) { if (!vertexMap.TryGetValue(srcVidx, out dstVidx)) dstVidx = -1; } if (dstVidx >= 0) this.SetVertex(dstVidx, g); } } #endregion } public interface IMorphTargetBuilder { IReadOnlyCollection Positions { get; } IReadOnlyCollection Vertices { get; } IReadOnlyList GetVertices(Vector3 position); void SetVertex(IVertexGeometry meshVertex, IVertexGeometry morphVertex); void SetVertexDelta(Vector3 key, VertexGeometryDelta delta); void SetVertexDelta(IVertexGeometry meshVertex, VertexGeometryDelta delta); } /// /// Utility class to edit the Morph targets of a mesh. /// public sealed class MorphTargetBuilder : IMorphTargetBuilder where TvG : struct, IVertexGeometry where TvM : struct, IVertexMaterial where TvS : struct, IVertexSkinning { #region lifecycle internal MorphTargetBuilder(MeshBuilder mesh, int morphTargetIndex) { _Mesh = mesh; _MorphTargetIndex = morphTargetIndex; foreach (var prim in _Mesh.Primitives) { for (int vidx = 0; vidx < prim.Vertices.Count; ++vidx) { var key = prim.Vertices[vidx].Geometry; if (!_Vertices.TryGetValue(key, out List<(PrimitiveBuilder, int)> val)) { _Vertices[key] = val = new List<(PrimitiveBuilder, int)>(); } val.Add((prim, vidx)); if (!_Positions.TryGetValue(key.GetPosition(), out List geos)) { _Positions[key.GetPosition()] = geos = new List(); } geos.Add(key); } } } #endregion #region data private readonly MeshBuilder _Mesh; private readonly int _MorphTargetIndex; private readonly Dictionary, int)>> _Vertices = new Dictionary, int)>>(); private readonly Dictionary> _Positions = new Dictionary>(); #endregion #region properties public IReadOnlyCollection Positions => _Positions.Keys; public IReadOnlyCollection Vertices => _Vertices.Keys; IReadOnlyCollection IMorphTargetBuilder.Vertices => (IReadOnlyList)(IReadOnlyCollection)_Vertices.Keys; #endregion #region API public IReadOnlyList GetVertices(Vector3 position) { return _Positions.TryGetValue(position, out List geos) ? (IReadOnlyList)geos : Array.Empty(); } IReadOnlyList IMorphTargetBuilder.GetVertices(Vector3 position) { return _Positions.TryGetValue(position, out List geos) ? (IReadOnlyList)geos : Array.Empty(); } public void SetVertexDelta(Vector3 key, VertexGeometryDelta delta) { if (_Positions.TryGetValue(key, out List geos)) { foreach (var g in geos) SetVertexDelta(g, delta); } } public void SetVertex(TvG meshVertex, TvG morphVertex) { if (_Vertices.TryGetValue(meshVertex, out List<(PrimitiveBuilder, int)> val)) { foreach (var entry in val) { entry.Item1 ._UseMorphTarget(_MorphTargetIndex) .SetVertex(entry.Item2, morphVertex); } } } void IMorphTargetBuilder.SetVertex(IVertexGeometry meshVertex, IVertexGeometry morphVertex) { SetVertex(meshVertex.ConvertToGeometry(), morphVertex.ConvertToGeometry()); } public void SetVertexDelta(TvG meshVertex, VertexGeometryDelta delta) { if (_Vertices.TryGetValue(meshVertex, out List<(PrimitiveBuilder, int)> val)) { foreach (var entry in val) { entry.Item1 ._UseMorphTarget(_MorphTargetIndex) .SetVertexDelta(entry.Item2, delta); } } } void IMorphTargetBuilder.SetVertexDelta(IVertexGeometry meshVertex, VertexGeometryDelta delta) { SetVertexDelta(meshVertex.ConvertToGeometry(), delta); } #endregion } }