using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpGLTF.Transforms;
using XFORM = System.Numerics.Matrix4x4;
namespace SharpGLTF.Runtime
{
///
/// Represents a specific and independent state of a .
///
public sealed class SceneInstance
{
#region lifecycle
internal SceneInstance(NodeTemplate[] nodeTemplates, DrawableTemplate[] drawables, Collections.NamedList tracks)
{
_AnimationTracks = tracks;
_NodeTemplates = nodeTemplates;
_NodeInstances = new NodeInstance[_NodeTemplates.Length];
for (var i = 0; i < _NodeInstances.Length; ++i)
{
var n = _NodeTemplates[i];
var pidx = _NodeTemplates[i].ParentIndex;
if (pidx >= i) throw new ArgumentException("invalid parent index", nameof(nodeTemplates));
var p = pidx < 0 ? null : _NodeInstances[pidx];
_NodeInstances[i] = new NodeInstance(n, p);
}
_DrawableReferences = drawables;
_DrawableTransforms = new IGeometryTransform[_DrawableReferences.Length];
for (int i = 0; i < _DrawableTransforms.Length; ++i)
{
_DrawableTransforms[i] = _DrawableReferences[i].CreateGeometryTransform();
}
}
#endregion
#region data
private readonly NodeTemplate[] _NodeTemplates;
private readonly NodeInstance[] _NodeInstances;
private readonly DrawableTemplate[] _DrawableReferences;
private readonly IGeometryTransform[] _DrawableTransforms;
private readonly Collections.NamedList _AnimationTracks;
#endregion
#region properties
///
/// Gets a list of all the nodes used by this .
///
public IReadOnlyList LogicalNodes => _NodeInstances;
///
/// Gets all the roots used by this .
///
public IEnumerable VisualNodes => _NodeInstances.Where(item => item.VisualParent == null);
///
/// Gets all the names of the animations tracks.
///
public IEnumerable AnimationTracks => _AnimationTracks.Names;
///
/// Gets the number of drawable references.
///
[Obsolete("Use DrawableInstancesCount")]
public int DrawableReferencesCount => _DrawableTransforms.Length;
///
/// Gets the number of drawable instances.
///
public int DrawableInstancesCount => _DrawableTransforms.Length;
///
/// Gets a collection of drawable references, where:
///
/// -
/// MeshIndex
/// The logical Index of a in .
///
/// -
/// Transform
/// An that can be used to transform the into world space.
///
///
///
[Obsolete("Use DrawableInstances.")]
public IEnumerable<(int MeshIndex, IGeometryTransform Transform)> DrawableReferences
{
get
{
for (int i = 0; i < _DrawableTransforms.Length; ++i)
{
yield return GetDrawableReference(i);
}
}
}
public IEnumerable DrawableInstances
{
get
{
for (int i = 0; i < _DrawableTransforms.Length; ++i)
{
yield return GetDrawableInstance(i);
}
}
}
#endregion
#region API
public void SetLocalMatrix(string name, XFORM localMatrix)
{
var n = LogicalNodes.FirstOrDefault(item => item.Name == name);
if (n == null) return;
n.LocalMatrix = localMatrix;
}
public void SetWorldMatrix(string name, XFORM worldMatrix)
{
var n = LogicalNodes.FirstOrDefault(item => item.Name == name);
if (n == null) return;
n.WorldMatrix = worldMatrix;
}
public void SetPoseTransforms()
{
foreach (var n in _NodeInstances) n.SetPoseTransform();
}
public float GetAnimationDuration(int trackLogicalIndex)
{
if (trackLogicalIndex < 0) return 0;
if (trackLogicalIndex >= _AnimationTracks.Count) return 0;
return _AnimationTracks[trackLogicalIndex];
}
public float GetAnimationDuration(string trackName)
{
return GetAnimationDuration(_AnimationTracks.IndexOf(trackName));
}
public void SetAnimationFrame(int trackLogicalIndex, float time, bool looped = true)
{
if (looped)
{
var duration = GetAnimationDuration(trackLogicalIndex);
if (duration > 0) time = time % duration;
}
foreach (var n in _NodeInstances) n.SetAnimationFrame(trackLogicalIndex, time);
}
public void SetAnimationFrame(string trackName, float time, bool looped = true)
{
SetAnimationFrame(_AnimationTracks.IndexOf(trackName), time, looped);
}
public void SetAnimationFrame(params (int TrackIdx, float Time, float Weight)[] blended)
{
SetAnimationFrame(_NodeInstances, blended);
}
public static void SetAnimationFrame(IEnumerable nodes, params (int TrackIdx, float Time, float Weight)[] blended)
{
Guard.NotNull(nodes, nameof(nodes));
Span tracks = stackalloc int[blended.Length];
Span times = stackalloc float[blended.Length];
Span weights = stackalloc float[blended.Length];
float w = blended.Sum(item => item.Weight);
w = w == 0 ? 1 : 1 / w;
for (int i = 0; i < blended.Length; ++i)
{
tracks[i] = blended[i].TrackIdx;
times[i] = blended[i].Time;
weights[i] = blended[i].Weight * w;
}
foreach (var n in nodes) n.SetAnimationFrame(tracks, times, weights);
}
///
/// Gets a drawable reference pair, where:
/// - MeshIndex is the logical Index of a in .
/// - Transform is an that can be used to transform the into world space.
///
/// The index of the drawable reference, from 0 to
/// A drawable reference
[Obsolete("Use GetDrawableInstance")]
public (int MeshIndex, IGeometryTransform Transform) GetDrawableReference(int index)
{
var dref = _DrawableReferences[index];
dref.UpdateGeometryTransform(_DrawableTransforms[index], _NodeInstances);
return (dref.LogicalMeshIndex, _DrawableTransforms[index]);
}
///
/// Gets a object, where:
/// - Name is the name of this drawable instance. Originally, it was the name of .
/// - MeshIndex is the logical Index of a in .
/// - Transform is an that can be used to transform the into world space.
///
/// The index of the drawable reference, from 0 to
/// object.
public DrawableInstance GetDrawableInstance(int index)
{
var dref = _DrawableReferences[index];
dref.UpdateGeometryTransform(_DrawableTransforms[index], _NodeInstances);
return new DrawableInstance(dref, _DrawableTransforms[index]);
}
#endregion
}
}