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.
IVertexBuilder 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).
VertexBuilder GetVertexDelta(int vertexIndex);
}
///
/// Represents the vertex deltas of a specific morph target.
///
///
/// The vertex fragment type with Position, Normal and Tangent.
class PrimitiveMorphTargetBuilder : IPrimitiveMorphTargetReader
where TvG : struct, IVertexGeometry
where TvM : struct, IVertexMaterial
{
#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 VertexBuilder GetVertexDelta(int vertexIndex)
{
if (_MorphVertices.TryGetValue(vertexIndex, out VertexBuilder value))
{
var vertex = _BaseVertexFunc(vertexIndex);
return new VertexBuilder(
value.Geometry.Subtract(vertex.Geometry),
value.Material.Subtract(vertex.Material));
}
return default;
}
public void SetVertexDelta(int vertexIndex, VertexGeometryDelta geometryDelta, VertexMaterialDelta materialDelta)
{
if (object.Equals(geometryDelta, default(VertexGeometryDelta)))
{
_RemoveVertex(vertexIndex);
return;
}
var vertex = _BaseVertexFunc(vertexIndex);
vertex.Geometry.Add(geometryDelta);
if (typeof(TvM) != typeof(VertexEmpty))
vertex.Material.Add(materialDelta);
_SetVertex(vertexIndex, vertex);
}
IVertexBuilder IPrimitiveMorphTargetReader.GetVertex(int vertexIndex)
{
return _MorphVertices.TryGetValue(vertexIndex, out VertexBuilder value) ? value : _BaseVertexFunc(vertexIndex);
}
public VertexBuilder GetVertex(int vertexIndex)
{
return _MorphVertices.TryGetValue(vertexIndex, out VertexBuilder value) ? value : _BaseVertexFunc(vertexIndex);
}
public void SetVertex(int vertexIndex, VertexBuilder vertex)
{
if (object.Equals(vertex, _BaseVertexFunc(vertexIndex)))
{
_RemoveVertex(vertexIndex);
return;
}
_SetVertex(vertexIndex, vertex);
}
private void _SetVertex(int vertexIndex, VertexBuilder vertex)
{
_MorphVertices[vertexIndex] = vertex;
}
private void _RemoveVertex(int vertexIndex)
{
_MorphVertices.Remove(vertexIndex);
}
#endregion
#region internals
internal void TransformVertices(Func, VertexBuilder> 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).GetGeometry());
var dstVidx = srcVidx;
if (vertexMap != null)
{
if (!vertexMap.TryGetValue(srcVidx, out dstVidx)) dstVidx = -1;
}
if (dstVidx >= 0) this.SetVertex(dstVidx, g);
}
}
#endregion
}
///
/// Represents the vertex deltas of a specific morph target.
///
///
public interface IMorphTargetBuilder
{
IReadOnlyCollection Positions { get; }
IReadOnlyCollection Vertices { get; }
IReadOnlyList GetVertices(Vector3 position);
///
/// Sets an absolute morph target.
///
/// The base mesh vertex to morph.
/// The morphed vertex.
void SetVertex(IVertexGeometry meshVertex, IVertexGeometry morphVertex);
///
/// Sets an absolute morph target.
///
/// The base mesh vertex to morph.
/// The morphed vertex.
/// The morphed vertex material.
void SetVertex(IVertexGeometry meshVertex, IVertexGeometry morphVertex, IVertexMaterial morphMaterial);
///
/// Sets a relative morph target
///
/// The base mesh vertex to morph.
/// The offset from to morph.
void SetVertexDelta(IVertexGeometry meshVertex, VertexGeometryDelta geometryDelta);
///
/// Sets a relative morph target
///
/// The base mesh vertex to morph.
/// The offset from to morph.
/// The offset from material to morph.
void SetVertexDelta(IVertexGeometry meshVertex, VertexGeometryDelta geometryDelta, VertexMaterialDelta materialDelta);
///
/// Sets a relative morph target to all base mesh vertices matching .
///
/// The base vertex position.
/// The offset to apply to each matching vertex found.
void SetVertexDelta(Vector3 meshPosition, VertexGeometryDelta geometryDelta);
///
/// Sets a relative morph target to all base mesh vertices matching .
///
/// The base vertex position.
/// The offset to apply to each matching vertex found.
/// The offset to apply to each matching vertex material found.
void SetVertexDelta(Vector3 meshPosition, VertexGeometryDelta geometryDelta, VertexMaterialDelta materialDelta);
}
///
/// Represents the vertex deltas of a specific morph target.
///
///
/// The material type used by the base mesh.
/// The vertex geometry type used by the base mesh.
/// The vertex skinning type used by the base mesh.
/// The vertex material type used by the base mesh.
///
/// Morph targets are stored separately on each ,
/// so connecting vertices between two primitives might be duplicated. This means that when we set
/// a displaced vertex, we must be sure we do so for all instances we can find.
///
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;
#endregion
#region API
public IReadOnlyList GetVertices(Vector3 position)
{
return _Positions.TryGetValue(position, out List geos) ? (IReadOnlyList)geos : Array.Empty();
}
public void SetVertexDelta(Vector3 key, VertexGeometryDelta geometryDelta)
{
if (_Positions.TryGetValue(key, out List geos))
{
foreach (var g in geos) SetVertexDelta(g, geometryDelta, VertexMaterialDelta.Zero);
}
}
public void SetVertexDelta(Vector3 key, VertexGeometryDelta geometryDelta, VertexMaterialDelta materialDelta)
{
if (_Positions.TryGetValue(key, out List geos))
{
foreach (var g in geos) SetVertexDelta(g, geometryDelta, materialDelta);
}
}
public void SetVertexDelta(TvG meshVertex, VertexGeometryDelta geometryDelta)
{
if (_Vertices.TryGetValue(meshVertex, out List<(PrimitiveBuilder, int)> val))
{
foreach (var entry in val)
{
entry.Item1
._UseMorphTarget(_MorphTargetIndex)
.SetVertexDelta(entry.Item2, geometryDelta, VertexMaterialDelta.Zero);
}
}
}
public void SetVertexDelta(TvG meshVertex, VertexGeometryDelta geometryDelta, VertexMaterialDelta materialDelta)
{
if (_Vertices.TryGetValue(meshVertex, out List<(PrimitiveBuilder, int)> val))
{
foreach (var entry in val)
{
entry.Item1
._UseMorphTarget(_MorphTargetIndex)
.SetVertexDelta(entry.Item2, geometryDelta, materialDelta);
}
}
}
public void SetVertex(TvG meshVertex, VertexBuilder morphVertex)
{
if (_Vertices.TryGetValue(meshVertex, out List<(PrimitiveBuilder, int)> val))
{
foreach (var entry in val)
{
entry.Item1
._UseMorphTarget(_MorphTargetIndex)
.SetVertex(entry.Item2, morphVertex);
}
}
}
public void SetVertex(TvG meshVertex, TvG morphVertex)
{
if (_Vertices.TryGetValue(meshVertex, out List<(PrimitiveBuilder, int)> val))
{
foreach (var entry in val)
{
var vertexMaterial = entry.Item1.Vertices[entry.Item2].Material;
entry.Item1
._UseMorphTarget(_MorphTargetIndex)
.SetVertex(entry.Item2, new VertexBuilder(morphVertex, vertexMaterial));
}
}
}
#endregion
#region IMorphTargetBuilder
IReadOnlyCollection IMorphTargetBuilder.Vertices => (IReadOnlyList)(IReadOnlyCollection)_Vertices.Keys;
IReadOnlyList IMorphTargetBuilder.GetVertices(Vector3 position)
{
return _Positions.TryGetValue(position, out List geos)
? (IReadOnlyList)geos
: Array.Empty();
}
void IMorphTargetBuilder.SetVertex(IVertexGeometry meshVertex, IVertexGeometry morphVertex)
{
SetVertex(meshVertex.ConvertToGeometry(),
new VertexBuilder(morphVertex.ConvertToGeometry(), default(VertexEmpty).ConvertToMaterial()));
}
void IMorphTargetBuilder.SetVertex(IVertexGeometry meshVertex, IVertexGeometry morphVertex, IVertexMaterial morphMaterial)
{
SetVertex(meshVertex.ConvertToGeometry(),
new VertexBuilder(morphVertex.ConvertToGeometry(), morphMaterial.ConvertToMaterial()));
}
void IMorphTargetBuilder.SetVertexDelta(IVertexGeometry meshVertex, VertexGeometryDelta geometryDelta)
{
SetVertexDelta(meshVertex.ConvertToGeometry(), geometryDelta, VertexMaterialDelta.Zero);
}
void IMorphTargetBuilder.SetVertexDelta(IVertexGeometry meshVertex, VertexGeometryDelta geometryDelta, VertexMaterialDelta materialDelta)
{
SetVertexDelta(meshVertex.ConvertToGeometry(), geometryDelta, materialDelta);
}
#endregion
}
}