Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
d49ffeb42This file is pinned to the selected release. Download the original to open it in your design tool.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using MESHBUILDER = SharpGLTF.Geometry.IMeshBuilder<SharpGLTF.Materials.MaterialBuilder>;
namespace SharpGLTF.Scenes
{
/// <summary>
/// Applies a transform to the underlaying content object (usually a Mesh, a Camera or a light)
/// </summary>
public abstract class ContentTransformer
{
#region debug
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private string _DebugName => string.IsNullOrWhiteSpace(Name) ? "*" : Name;
#endregion
#region lifecycle
protected ContentTransformer(Object content)
{
Guard.NotNull(content, nameof(content));
if (content is MESHBUILDER mesh) content = new MeshContent(mesh);
_Content = content;
}
public abstract ContentTransformer DeepClone(DeepCloneContext args);
protected ContentTransformer(ContentTransformer other)
{
Guard.NotNull(other, nameof(other));
if (other._Content is ICloneable cloneable)
{
this._Content = cloneable.Clone();
}
else
{
this._Content = other._Content;
}
this._Morphings = other._Morphings?.Clone();
}
#endregion
#region data
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private Object _Content;
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private Animations.AnimatableProperty<Transforms.SparseWeight8> _Morphings;
#endregion
#region properties
public abstract String Name { get; }
public Object Content => _Content;
public Animations.AnimatableProperty<Transforms.SparseWeight8> Morphings => _Morphings;
#endregion
#region API
/// <summary>
/// If this <see cref="ContentTransformer"/> contains a <see cref="MESHBUILDER"/>.
/// </summary>
/// <returns>A <see cref="MESHBUILDER"/> instance, or NULL.</returns>
public virtual MESHBUILDER GetGeometryAsset() { return (_Content as IRenderableContent)?.GetGeometryAsset(); }
/// <summary>
/// If this <see cref="ContentTransformer"/> uses a <see cref="NodeBuilder"/> armature, it returns the root of the armature.
/// </summary>
/// <returns>A <see cref="NodeBuilder"/> instance, or NULL.</returns>
public abstract NodeBuilder GetArmatureRoot();
public Animations.AnimatableProperty<Transforms.SparseWeight8> UseMorphing()
{
if (_Morphings == null)
{
_Morphings = new Animations.AnimatableProperty<Transforms.SparseWeight8>();
_Morphings.Value = default;
}
return _Morphings;
}
public Animations.CurveBuilder<Transforms.SparseWeight8> UseMorphing(string animationTrack)
{
return UseMorphing().UseTrackBuilder(animationTrack);
}
public abstract Matrix4x4 GetPoseWorldMatrix();
#endregion
#region nestedTypes
public readonly struct DeepCloneContext
{
public DeepCloneContext(IReadOnlyDictionary<NodeBuilder, NodeBuilder> nmap)
{
_NodeMap = nmap;
}
private readonly IReadOnlyDictionary<NodeBuilder, NodeBuilder> _NodeMap;
public NodeBuilder GetNode(NodeBuilder node)
{
if (_NodeMap == null) return node;
return _NodeMap.TryGetValue(node, out NodeBuilder clone) ? clone : node;
}
}
#endregion
}
/// <summary>
/// Applies a fixed <see cref="Matrix4x4"/> transform to the underlaying content.
/// </summary>
[System.Diagnostics.DebuggerDisplay("Fixed Node[{_DebugName,nq}] = {Content}")]
public partial class FixedTransformer : ContentTransformer
{
#region lifecycle
internal FixedTransformer(Object content, Matrix4x4 xform, string nodeName = null)
: base(content)
{
_WorldTransform = xform;
_NodeName = nodeName;
}
protected FixedTransformer(FixedTransformer other)
: base(other)
{
Guard.NotNull(other, nameof(other));
this._NodeName = other._NodeName;
this._WorldTransform = other._WorldTransform;
}
public override ContentTransformer DeepClone(DeepCloneContext args)
{
return new FixedTransformer(this);
}
#endregion
#region data
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private String _NodeName;
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private Matrix4x4 _WorldTransform;
#endregion
#region properties
public override String Name => _NodeName;
public Matrix4x4 WorldMatrix
{
get => _WorldTransform;
set => _WorldTransform = value;
}
#endregion
#region API
public override NodeBuilder GetArmatureRoot() { return null; }
public override Matrix4x4 GetPoseWorldMatrix() => WorldMatrix;
#endregion
}
/// <summary>
/// Applies the transform of a single <see cref="NodeBuilder"/> to the underlaying content.
/// </summary>
[System.Diagnostics.DebuggerDisplay("Rigid Node[{_DebugName,nq}] = {Content}")]
public partial class RigidTransformer : ContentTransformer
{
#region lifecycle
internal RigidTransformer(Object content, NodeBuilder node)
: base(content)
{
_Node = node;
}
protected RigidTransformer(RigidTransformer other, DeepCloneContext args)
: base(other)
{
Guard.NotNull(other, nameof(other));
this._Node = args.GetNode(other._Node);
}
public override ContentTransformer DeepClone(DeepCloneContext args)
{
return new RigidTransformer(this, args);
}
#endregion
#region data
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private NodeBuilder _Node;
#endregion
#region properties
public override String Name => _Node.Name;
public NodeBuilder Transform
{
get => _Node;
set => _Node = value;
}
#endregion
#region API
public override NodeBuilder GetArmatureRoot() { return _Node.Root; }
public override Matrix4x4 GetPoseWorldMatrix() => Transform.WorldMatrix;
#endregion
}
/// <summary>
/// Applies the transforms of many <see cref="NodeBuilder"/> to the underlaying content.
/// </summary>
[System.Diagnostics.DebuggerDisplay("Skinned Node[{_DebugName,nq}] = {Content}")]
public partial class SkinnedTransformer : ContentTransformer
{
#region lifecycle
internal SkinnedTransformer(MESHBUILDER mesh, Matrix4x4 meshWorldMatrix, NodeBuilder[] joints, string nodeName = null)
: base(mesh)
{
_NodeName = nodeName;
SetJoints(meshWorldMatrix, joints);
}
internal SkinnedTransformer(MESHBUILDER mesh, (NodeBuilder Joint, Matrix4x4 InverseBindMatrix)[] joints, string nodeName = null)
: base(mesh)
{
_NodeName = nodeName;
SetJoints(joints);
}
protected SkinnedTransformer(SkinnedTransformer other, DeepCloneContext args)
: base(other)
{
Guard.NotNull(other, nameof(other));
this._NodeName = other._NodeName;
this._MeshPoseWorldMatrix = other._MeshPoseWorldMatrix;
foreach (var (joint, inverseBindMatrix) in other._Joints)
{
var jj = (args.GetNode(joint), inverseBindMatrix);
this._Joints.Add(jj);
}
}
public override ContentTransformer DeepClone(DeepCloneContext args)
{
return new SkinnedTransformer(this, args);
}
#endregion
#region data
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private String _NodeName;
/// <summary>
/// Defines the world matrix of the mesh at the time of binding.
/// </summary>
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private Matrix4x4? _MeshPoseWorldMatrix;
// condition: all NodeBuilder objects must have the same root.
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private readonly List<(NodeBuilder Joint, Matrix4x4? InverseBindMatrix)> _Joints = new List<(NodeBuilder, Matrix4x4?)>();
#endregion
#region properties
public override String Name => _NodeName;
#endregion
#region API
private void SetJoints(Matrix4x4 meshWorldMatrix, NodeBuilder[] joints)
{
Guard.NotNull(joints, nameof(joints));
Guard.IsTrue(NodeBuilder.IsValidArmature(joints), nameof(joints));
_MeshPoseWorldMatrix = meshWorldMatrix;
_Joints.Clear();
_Joints.AddRange(joints.Select(item => (item, (Matrix4x4?)null)));
}
private void SetJoints((NodeBuilder Joint, Matrix4x4 InverseBindMatrix)[] joints)
{
Guard.NotNull(joints, nameof(joints));
Guard.IsTrue(NodeBuilder.IsValidArmature(joints.Select(item => item.Joint)), nameof(joints));
_MeshPoseWorldMatrix = null;
_Joints.Clear();
_Joints.AddRange(joints.Select(item => (item.Joint, (Matrix4x4?)item.InverseBindMatrix)));
}
public (NodeBuilder Joint, Matrix4x4 InverseBindMatrix)[] GetJointBindings()
{
var jb = new (NodeBuilder Joint, Matrix4x4 InverseBindMatrix)[_Joints.Count];
for (int i = 0; i < jb.Length; ++i)
{
var j = _Joints[i].Joint;
var m = _Joints[i].InverseBindMatrix ?? Transforms.SkinnedTransform.CalculateInverseBinding(_MeshPoseWorldMatrix ?? Matrix4x4.Identity, j.WorldMatrix);
jb[i] = (j, m);
}
return jb;
}
public override NodeBuilder GetArmatureRoot()
{
return _Joints
.Select(item => item.Joint.Root)
.Distinct()
.FirstOrDefault();
}
public Transforms.IGeometryTransform GetWorldTransformer(string animationTrack, float time)
{
var jb = GetJointBindings();
return new Transforms.SkinnedTransform
(
jb.Length,
idx => jb[idx].InverseBindMatrix,
idx => jb[idx].Joint.GetWorldMatrix(animationTrack, time),
default, false
);
}
public override Matrix4x4 GetPoseWorldMatrix() => _MeshPoseWorldMatrix ?? Matrix4x4.Identity;
#endregion
}
}