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
}
}