using System.Collections.Generic; using System.Linq; using GLTF.Extensions; using GLTF.Math; using Newtonsoft.Json; namespace GLTF.Schema { /// /// A node in the node hierarchy. /// When the node contains `skin`, all `mesh.primitives` must contain `JOINT` /// and `WEIGHT` attributes. A node can have either a `matrix` or any combination /// of `translation`/`rotation`/`scale` (TRS) properties. /// TRS properties are converted to matrices and postmultiplied in /// the `T * R * S` order to compose the transformation matrix; /// first the scale is applied to the vertices, then the rotation, and then /// the translation. If none are provided, the transform is the Identity. /// When a node is targeted for animation /// (referenced by an animation.channel.target), only TRS properties may be present; /// `matrix` will not be present. /// public class Node : GLTFChildOfRootProperty { /// /// If true, extracts transform, rotation, scale values from the Matrix4x4. Otherwise uses the Transform, Rotate, Scale directly as specified by by the node. /// public bool UseTRS; /// /// The index of the camera referenced by this node. /// public CameraId Camera; /// /// The indices of this node's children. /// public List Children; /// /// The index of the skin referenced by this node. /// public SkinId Skin; /// /// A floating-point 4x4 transformation matrix stored in column-major order. /// public Matrix4x4 Matrix = Matrix4x4.Identity; /// /// The index of the mesh in this node. /// public MeshId Mesh; /// /// The node's unit quaternion rotation in the order (x, y, z, w), /// where w is the scalar. /// public Quaternion Rotation = new Quaternion(0, 0, 0, 1); /// /// The node's non-uniform scale. /// public Vector3 Scale = Vector3.One; /// /// The node's translation. /// public Vector3 Translation = Vector3.Zero; /// /// The weights of the instantiated Morph Target. /// Number of elements must match number of Morph Targets of used mesh. /// public List Weights; public LightId Light; public Node() { } public Node(Node node, GLTFRoot gltfRoot) : base(node, gltfRoot) { if (node == null) return; UseTRS = node.UseTRS; if (node.Camera != null) { Camera = new CameraId(node.Camera, gltfRoot); } if (node.Children != null) { Children = new List(node.Children.Count); foreach (NodeId child in node.Children) { Children.Add(new NodeId(child, gltfRoot)); } } if (node.Skin != null) { Skin = new SkinId(node.Skin, gltfRoot); } if (node.Matrix != null) { Matrix = new Matrix4x4(node.Matrix); } if (node.Mesh != null) { Mesh = new MeshId(node.Mesh, gltfRoot); } Rotation = node.Rotation; Scale = node.Scale; Translation = node.Translation; if (node.Weights != null) { Weights = node.Weights.ToList(); } if (node.Light != null) { Light = node.Light; } } public static Node Deserialize(GLTFRoot root, JsonReader reader) { var node = new Node(); while (reader.Read() && reader.TokenType == JsonToken.PropertyName) { var curProp = reader.Value.ToString(); switch (curProp) { case "camera": node.Camera = CameraId.Deserialize(root, reader); break; case "children": node.Children = NodeId.ReadList(root, reader); break; case "skin": node.Skin = SkinId.Deserialize(root, reader); break; case "matrix": var list = reader.ReadDoubleList(); // gltf has column ordered matricies var mat = new Matrix4x4( (float)list[0], (float)list[1], (float)list[2], (float)list[3], (float)list[4], (float)list[5], (float)list[6], (float)list[7], (float)list[8], (float)list[9], (float)list[10], (float)list[11], (float)list[12], (float)list[13], (float)list[14], (float)list[15] ); node.Matrix = mat; break; case "mesh": node.Mesh = MeshId.Deserialize(root, reader); break; case "rotation": node.UseTRS = true; node.Rotation = reader.ReadAsQuaternion(); break; case "scale": node.UseTRS = true; node.Scale = reader.ReadAsVector3(); break; case "translation": node.UseTRS = true; node.Translation = reader.ReadAsVector3(); break; case "weights": node.Weights = reader.ReadDoubleList(); break; default: node.DefaultPropertyDeserializer(root, reader); break; } } return node; } public override void Serialize(JsonWriter writer) { writer.WriteStartObject(); if (Camera != null) { writer.WritePropertyName("camera"); writer.WriteValue(Camera.Id); } if (Children != null && Children.Count > 0) { writer.WritePropertyName("children"); writer.WriteStartArray(); foreach (var child in Children) { writer.WriteValue(child.Id); } writer.WriteEndArray(); } if (Skin != null) { writer.WritePropertyName("skin"); writer.WriteValue(Skin.Id); } if (Matrix != Matrix4x4.Identity) { writer.WritePropertyName("matrix"); writer.WriteStartArray(); writer.WriteValue(Matrix.M11); writer.WriteValue(Matrix.M21); writer.WriteValue(Matrix.M31); writer.WriteValue(Matrix.M41); writer.WriteValue(Matrix.M12); writer.WriteValue(Matrix.M22); writer.WriteValue(Matrix.M32); writer.WriteValue(Matrix.M42); writer.WriteValue(Matrix.M13); writer.WriteValue(Matrix.M23); writer.WriteValue(Matrix.M33); writer.WriteValue(Matrix.M43); writer.WriteValue(Matrix.M14); writer.WriteValue(Matrix.M24); writer.WriteValue(Matrix.M34); writer.WriteValue(Matrix.M44); writer.WriteEndArray(); } if (Mesh != null) { writer.WritePropertyName("mesh"); writer.WriteValue(Mesh.Id); } if (Rotation != Quaternion.Identity) { writer.WritePropertyName("rotation"); writer.WriteStartArray(); writer.WriteValue(Rotation.X); writer.WriteValue(Rotation.Y); writer.WriteValue(Rotation.Z); writer.WriteValue(Rotation.W); writer.WriteEndArray(); } if (Scale != Vector3.One) { writer.WritePropertyName("scale"); writer.WriteStartArray(); writer.WriteValue(Scale.X); writer.WriteValue(Scale.Y); writer.WriteValue(Scale.Z); writer.WriteEndArray(); } if (Translation != Vector3.Zero) { writer.WritePropertyName("translation"); writer.WriteStartArray(); writer.WriteValue(Translation.X); writer.WriteValue(Translation.Y); writer.WriteValue(Translation.Z); writer.WriteEndArray(); } if (Weights != null && Weights.Count > 0) { writer.WritePropertyName("weights"); writer.WriteStartArray(); foreach (var weight in Weights) { writer.WriteValue(weight); } writer.WriteEndArray(); } base.Serialize(writer); writer.WriteEndObject(); } } }