Editor/HumanoidRetargeter/Embedded/ValveResourceFormat/IO/ContentFormats/DmxModel.cs
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using HumanoidRetargeterVrf.Utils;
using HumanoidRetargeterDmx.Format;
using DMElement = HumanoidRetargeterDmx.Element;
namespace HumanoidRetargeterVrf.IO.ContentFormats.DmxModel;
#nullable disable
#pragma warning disable CA2227 // Collection properties should be read only
[CamelCaseProperties]
internal class DmeModel : DMElement
{
public DmeTransform Transform { get; set; } = [];
public DMElement Shape { get; set; }
public bool Visible { get; set; } = true;
public HumanoidRetargeterDmx.ElementArray Children { get; } = [];
public HumanoidRetargeterDmx.ElementArray JointList { get; set; } = [];
/// <summary>
/// List of <see cref="DmeTransformsList"/> elements.
/// </summary>
public HumanoidRetargeterDmx.ElementArray BaseStates { get; set; } = [];
public DmeAxisSystem AxisSystem { get; set; } = [];
}
/// <summary>
/// Represents an abstract shape
/// </summary>
[CamelCaseProperties]
public class DmeShape : DMElement
{
/// <summary>
/// Gets or sets a value indicating whether this mesh is visible.
/// </summary>
public bool Visible { get; set; } = true;
}
/// <summary>
/// Represents a transformation element with position and orientation.
/// </summary>
[CamelCaseProperties]
public class DmeTransform : DMElement
{
/// <summary>
/// Gets or sets the position in 3D space.
/// </summary>
public global::System.Numerics.Vector3 Position { get; set; } = global::System.Numerics.Vector3.Zero;
/// <summary>
/// Gets or sets the orientation as a quaternion.
/// </summary>
public global::System.Numerics.Quaternion Orientation { get; set; } = global::System.Numerics.Quaternion.Identity;
}
/// <summary>
/// Represents the axis system configuration for the model.
/// </summary>
[CamelCaseProperties]
public class DmeAxisSystem : DMElement
{
/// <summary>
/// Gets or sets the up axis.
/// </summary>
public int UpAxis { get; set; } = 3;
/// <summary>
/// Gets or sets the forward parity.
/// </summary>
public int ForwardParity { get; set; } = 1;
/// <summary>
/// Gets or sets the coordinate system.
/// </summary>
public int CoordSys { get; set; }
}
/// <summary>
/// Represents a list of transform elements.
/// </summary>
[CamelCaseProperties]
public class DmeTransformsList : DMElement
{
/// <summary>
/// List of <see cref="DmeTransform"/> elements.
/// </summary>
public HumanoidRetargeterDmx.ElementArray Transforms { get; } = [];
}
/// <summary>
/// Represents a directed acyclic graph node for model hierarchy.
/// </summary>
[CamelCaseProperties]
public class DmeDag : DMElement
{
/// <summary>
/// Gets the transform of this DAG node.
/// </summary>
public DmeTransform Transform { get; } = [];
/// <summary>
/// Gets the mesh shape of this DAG node.
/// </summary>
public DmeShape Shape { get; set; } = [];
/// <summary>
/// Gets or sets a value indicating whether this node is visible.
/// </summary>
public bool Visible { get; set; } = true;
/// <summary>
/// Gets the child DAG nodes.
/// </summary>
public HumanoidRetargeterDmx.ElementArray Children { get; } = [];
}
/// <summary>
/// Represents a skeletal joint in the model hierarchy.
/// </summary>
[CamelCaseProperties] public class DmeJoint : DmeDag;
/// <summary>
/// Represents a mesh with vertex data and face sets.
/// </summary>
[CamelCaseProperties]
public class DmeMesh : DmeShape
{
/// <summary>
/// Gets or sets the bind state of the mesh.
/// </summary>
public DMElement BindState { get; set; }
/// <summary>
/// Gets or sets the current state of the mesh.
/// </summary>
public DMElement CurrentState { get; set; }
/// <summary>
/// Gets the base states of the mesh.
/// </summary>
public HumanoidRetargeterDmx.ElementArray BaseStates { get; } = [];
/// <summary>
/// Gets the delta states for morph targets.
/// </summary>
public HumanoidRetargeterDmx.ElementArray DeltaStates { get; } = [];
/// <summary>
/// Gets the face sets that define material groups.
/// </summary>
public HumanoidRetargeterDmx.ElementArray FaceSets { get; } = [];
/// <summary>
/// Gets the delta state weights for morph targets.
/// </summary>
public HumanoidRetargeterDmx.Vector2Array DeltaStateWeights { get; } = [];
/// <summary>
/// Gets the lagged delta state weights for morph targets.
/// </summary>
public HumanoidRetargeterDmx.Vector2Array DeltaStateWeightsLagged { get; } = [];
}
/// <summary>
/// Represents a face set with associated material.
/// </summary>
[CamelCaseProperties]
public class DmeFaceSet : DMElement
{
/// <summary>
/// Gets the array of face indices.
/// </summary>
public HumanoidRetargeterDmx.IntArray Faces { get; } = [];
/// <summary>
/// Gets the material definition associated with this face set.
/// </summary>
public DmeMaterial Material { get; } = new() { Name = "material" };
/// <summary>
/// Represents a material reference.
/// </summary>
public class DmeMaterial : DMElement
{
/// <summary>
/// Gets or sets the material name.
/// </summary>
[DMProperty(name: "mtlName")]
public string MaterialName { get; set; } = string.Empty;
}
}
/// <summary>
/// Represents vertex data with multiple streams.
/// </summary>
[CamelCaseProperties]
public class DmeVertexData : DMElement
{
/// <summary>
/// Gets the vertex format specification.
/// </summary>
public HumanoidRetargeterDmx.StringArray VertexFormat { get; } = [];
/// <summary>
/// Gets or sets the number of joints for skinning.
/// </summary>
public int JointCount { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to flip V texture coordinates.
/// </summary>
public bool FlipVCoordinates { get; set; }
/// <summary>
/// Adds a vertex data stream.
/// </summary>
public void AddStream<T>(string name, T[] data)
{
VertexFormat.Add(name);
this[name] = data;
}
/// <summary>
/// Adds an indexed vertex data stream.
/// </summary>
public void AddIndexedStream<T>(string name, T[] data, int[] indices)
{
VertexFormat.Add(name);
this[name] = data;
this[name + "Indices"] = indices;
}
}
/// <summary>
/// Represents a list of animations.
/// </summary>
[CamelCaseProperties]
public class DmeAnimationList : DMElement
{
/// <summary>
/// Gets the array of animations.
/// </summary>
public HumanoidRetargeterDmx.ElementArray Animations { get; } = [];
}
/// <summary>
/// Represents an animation clip with channels.
/// </summary>
[CamelCaseProperties]
public class DmeChannelsClip : DMElement
{
/// <summary>
/// Gets the time frame of the clip.
/// </summary>
public DmeTimeFrame TimeFrame { get; } = [];
/// <summary>
/// Gets or sets the color for display purposes.
/// </summary>
public HumanoidRetargeterDmx.Color Color { get; set; }
/// <summary>
/// Gets or sets the text description.
/// </summary>
public string Text { get; set; } = "";
/// <summary>
/// Gets or sets a value indicating whether the clip is muted.
/// </summary>
public bool Mute { get; set; }
/// <summary>
/// Gets the track groups.
/// </summary>
public HumanoidRetargeterDmx.ElementArray TrackGroups { get; } = [];
/// <summary>
/// Gets or sets the display scale.
/// </summary>
public float DisplayScale { get; set; } = 1f;
/// <summary>
/// Gets the animation channels.
/// </summary>
public HumanoidRetargeterDmx.ElementArray Channels { get; } = [];
/// <summary>
/// Gets or sets the frame rate in frames per second.
/// </summary>
public float FrameRate { get; set; } = 30f;
}
/// <summary>
/// Represents a time frame for animations.
/// </summary>
[CamelCaseProperties]
public class DmeTimeFrame : DMElement
{
/// <summary>
/// Gets or sets the start time.
/// </summary>
public TimeSpan Start { get; set; }
/// <summary>
/// Gets or sets the duration.
/// </summary>
public TimeSpan Duration { get; set; }
/// <summary>
/// Gets or sets the time offset.
/// </summary>
public TimeSpan Offset { get; set; }
/// <summary>
/// Gets or sets the time scale.
/// </summary>
public float Scale { get; set; } = 1f;
}
/// <summary>
/// Represents an animation channel connecting source and target elements.
/// </summary>
[CamelCaseProperties]
public class DmeChannel : DMElement
{
/// <summary>
/// Gets or sets the source element.
/// </summary>
public DMElement FromElement { get; set; }
/// <summary>
/// Gets or sets the source attribute name.
/// </summary>
public string FromAttribute { get; set; } = "";
/// <summary>
/// Gets or sets the source index.
/// </summary>
public int FromIndex { get; set; }
/// <summary>
/// Gets or sets the target element.
/// </summary>
public DMElement ToElement { get; set; }
/// <summary>
/// Gets or sets the target attribute name.
/// </summary>
public string ToAttribute { get; set; } = "";
/// <summary>
/// Gets or sets the target index.
/// </summary>
public int ToIndex { get; set; }
/// <summary>
/// Gets or sets the channel mode.
/// </summary>
public int Mode { get; set; }
private DMElement _log;
/// <summary>
/// Gets or sets the animation log data.
/// </summary>
public DMElement Log
{
get
{
return _log;
}
set
{
var logType = value.GetType();
if (logType.GetGenericTypeDefinition() != typeof(DmeLog<>))
{
throw new ArgumentException($"DmeChannel.Log can only contain DmeLog types");
}
_log = value;
}
}
}
/// <summary>
/// Base class for typed animation logs.
/// </summary>
public abstract class DmeTypedLog<T> : DMElement
{
/// <summary>
/// Initializes a new instance of the <see cref="DmeTypedLog{T}"/> class.
/// </summary>
protected DmeTypedLog(string namePostfix)
{
string typeName;
if (typeof(T) == typeof(float)) //Name would be 'Single' without this
{
typeName = "Float";
}
else
{
typeName = typeof(T).Name;
}
if (char.IsLower(typeName[0]))
{
typeName = char.ToUpperInvariant(typeName[0]) + typeName[1..];
}
ClassName = $"Dme{typeName}{namePostfix}";
Name = $"{typeName.ToLowerInvariant()} log";
}
}
/// <summary>
/// Represents an animation log with keyframe data.
/// </summary>
[CamelCaseProperties]
public class DmeLog<T> : DmeTypedLog<T>
{
/// <summary>
/// Initializes a new instance of the <see cref="DmeLog{T}"/> class.
/// </summary>
public DmeLog() : base("Log") { }
/// <summary>
/// Gets or sets the log layers containing keyframe data.
/// </summary>
[DMProperty("layers")]
public HumanoidRetargeterDmx.ElementArray Layers { get; set; } = [];
/// <summary>
/// Gets or sets the curve interpolation information.
/// </summary>
[DMProperty("curveinfo")]
public DMElement CurveInfo { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to use the default value.
/// </summary>
[DMProperty("usedefaultvalue")]
public bool UseDefaultValue { get; set; }
/// <summary>
/// Gets or sets the default value.
/// </summary>
[DMProperty("defaultvalue")]
public T DefaultValue { get; set; }
/// <summary>
/// Gets the X-axis bookmarks.
/// </summary>
public HumanoidRetargeterDmx.TimeSpanArray BookmarksX { get; } = [];
/// <summary>
/// Gets the Y-axis bookmarks.
/// </summary>
public HumanoidRetargeterDmx.TimeSpanArray BookmarksY { get; } = [];
/// <summary>
/// Gets the Z-axis bookmarks.
/// </summary>
public HumanoidRetargeterDmx.TimeSpanArray BookmarksZ { get; } = [];
/// <summary>
/// Gets the log layer at the specified index.
/// </summary>
public DmeLogLayer<T> GetLayer(int index)
{
return (DmeLogLayer<T>)Layers[index];
}
/// <summary>
/// Adds a log layer.
/// </summary>
public void AddLayer(DmeLogLayer<T> layer)
{
Layers.Add(layer);
}
/// <summary>
/// Gets the number of layers.
/// </summary>
public int LayerCount => Layers.Count;
}
/// <summary>
/// Represents a layer of keyframe data in an animation log.
/// </summary>
[CamelCaseProperties]
public class DmeLogLayer<T> : DmeTypedLog<T>
{
/// <summary>
/// Initializes a new instance of the <see cref="DmeLogLayer{T}"/> class.
/// </summary>
public DmeLogLayer() : base("LogLayer") { }
/// <summary>
/// Gets or sets the keyframe times.
/// </summary>
public HumanoidRetargeterDmx.TimeSpanArray Times { get; set; } = [];
/// <summary>
/// Gets the curve interpolation types for each keyframe.
/// </summary>
[DMProperty("curvetypes")]
public HumanoidRetargeterDmx.IntArray CurveTypes { get; } = [];
/// <summary>
/// Gets or sets the keyframe values.
/// </summary>
[DMProperty("values")]
public T[] LayerValues { get; set; }
/// <summary>
/// Checks if this layer only contains default/zero values.
/// </summary>
public bool IsLayerZero()
{
object defaultValue;
//quaternions initialize to all 0s
if (typeof(T) == typeof(global::System.Numerics.Quaternion))
{
defaultValue = global::System.Numerics.Quaternion.Identity;
}
else
{
defaultValue = default(T);
}
foreach (var item in LayerValues)
{
if (!item.Equals(defaultValue))
{
return false;
}
}
return true;
}
}
#pragma warning restore CA2227 // Collection properties should be read only