From 92bccafc1f9e40684f0208457ab49af340ceb03e Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Thu, 4 Aug 2022 14:30:56 -0400 Subject: [PATCH 1/9] Use TRS instead of matrix in glTF. --- .../src/Serialization/glTF/GltfExtensions.cs | 2 + .../src/Serialization/glTF/NodeUtilities.cs | 45 +++++++++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/Elements/src/Serialization/glTF/GltfExtensions.cs b/Elements/src/Serialization/glTF/GltfExtensions.cs index e52cd9c30..ff185f515 100644 --- a/Elements/src/Serialization/glTF/GltfExtensions.cs +++ b/Elements/src/Serialization/glTF/GltfExtensions.cs @@ -16,6 +16,8 @@ using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp; using Image = glTFLoader.Schema.Image; +using Vector3 = Elements.Geometry.Vector3; +using Quaternion = Elements.Geometry.Quaternion; [assembly: InternalsVisibleTo("Hypar.Elements.Tests")] [assembly: InternalsVisibleTo("Elements.Benchmarks")] diff --git a/Elements/src/Serialization/glTF/NodeUtilities.cs b/Elements/src/Serialization/glTF/NodeUtilities.cs index 09e5894dd..922ff2231 100644 --- a/Elements/src/Serialization/glTF/NodeUtilities.cs +++ b/Elements/src/Serialization/glTF/NodeUtilities.cs @@ -4,6 +4,8 @@ using Elements.Collections.Generics; using Elements.Geometry; using glTFLoader.Schema; +using System.Numerics; +using Vector3 = Elements.Geometry.Vector3; namespace Elements.Serialization.glTF { @@ -67,6 +69,43 @@ internal static int CreateAndAddTransformNode(List nodes, Transform transf return parentId; } + internal static int CreateAndAddRTSNode(List nodes, Transform transform, int parentId) + { + if (transform != null) + { + var transNode = new Node(); + + // HACK: Using the matrix class from System.Numerics + // because it has support for matrix decomposition + // out of the box. + var m = new Matrix4x4((float)transform.Matrix.m11, + (float)transform.Matrix.m12, + (float)transform.Matrix.m13, + 0.0f, + (float)transform.Matrix.m21, + (float)transform.Matrix.m22, + (float)transform.Matrix.m23, + 0.0f, + (float)transform.Matrix.m31, + (float)transform.Matrix.m32, + (float)transform.Matrix.m33, + 0.0f, + (float)transform.Origin.X, + (float)transform.Origin.Y, + (float)transform.Origin.Z, + 1.0f); + + Matrix4x4.Decompose(m, out var scale, out System.Numerics.Quaternion rotation, out var translation); + transNode.Scale = new float[] { scale.X, scale.Y, scale.Z }; + transNode.Translation = new float[] { translation.X, translation.Y, translation.Z }; + transNode.Rotation = new float[] { rotation.X, rotation.Y, rotation.Z, rotation.W }; + + parentId = AddNode(nodes, transNode, 0); + } + + return parentId; + } + internal static void AddInstanceAsCopyOfNode( List nodes, ProtoNode nodeToCopy, @@ -159,7 +198,7 @@ internal static int CreateNodeForMesh(int meshId, List n { var parentId = 0; - parentId = NodeUtilities.CreateAndAddTransformNode(nodes, transform, parentId); + parentId = CreateAndAddRTSNode(nodes, transform, parentId); // Add mesh node to gltf nodes var node = new Node(); @@ -168,9 +207,9 @@ internal static int CreateNodeForMesh(int meshId, List n return nodeId; } - internal static void CreateNodeFromNode(List nodes, Node parentNode, Transform transform) + internal static void CreateNodeFromNode(List nodes, Transform transform) { - var parentId = NodeUtilities.CreateAndAddTransformNode(nodes, transform, 0); + CreateAndAddTransformNode(nodes, transform, 0); } } } \ No newline at end of file From 92608a0404b0d9cd4fe601b777f6736f6eee248e Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Thu, 4 Aug 2022 23:38:10 -0400 Subject: [PATCH 2/9] Basic animation working. --- Elements/src/Animation.cs | 184 +++++++++++++++++ Elements/src/Element.cs | 6 + .../src/Serialization/glTF/GltfExtensions.cs | 192 +++++++++++++++++- .../src/Serialization/glTF/NodeUtilities.cs | 34 ++-- Elements/test/TransformTests.cs | 19 +- 5 files changed, 415 insertions(+), 20 deletions(-) create mode 100644 Elements/src/Animation.cs diff --git a/Elements/src/Animation.cs b/Elements/src/Animation.cs new file mode 100644 index 000000000..5a33dd94f --- /dev/null +++ b/Elements/src/Animation.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Numerics; + +namespace Elements +{ + /// + /// An element's animation. + /// + public class Animation + { + List _scaleTimes = new List(); + List _scales = new List(); + List _translationTimes = new List(); + List _translations = new List(); + List _rotationTimes = new List(); + List _rotations = new List(); + + float[] _scaleMin; + float[] _scaleMax; + float _scaleTimeMin; + float _scaleTimeMax; + + float[] _translationMin; + float[] _translationMax; + float _translationTimeMin; + float _translationTimeMax; + + float[] _rotationMin; + float[] _rotationMax; + float _rotationTimeMin; + float _rotationTimeMax; + + public byte[] Scales => _scales.ToArray(); + public byte[] ScaleTimes => _scaleTimes.ToArray(); + public float ScaleTimeMin => _scaleTimeMin; + public float ScaleTimeMax => _scaleTimeMax; + public float[] ScaleMin => _scaleMin; + public float[] ScaleMax => _scaleMax; + + public byte[] Translations => _translations.ToArray(); + public byte[] TranslationTimes => _translationTimes.ToArray(); + public float TranslationTimeMin => _translationTimeMin; + public float TranslationTimeMax => _translationTimeMax; + public float[] TranslationMin => _translationMin; + public float[] TranslationMax => _translationMax; + + public byte[] Rotations => _rotations.ToArray(); + public byte[] RotationTimes => _rotationTimes.ToArray(); + public float RotationTimeMin => _rotationTimeMin; + public float RotationTimeMax => _rotationTimeMax; + public float[] RotationMin => _rotationMin; + public float[] RotationMax => _rotationMax; + + /// + /// Create an animation for an element. + /// + public Animation() + { + _scaleMin = new float[3] { float.MaxValue, float.MaxValue, float.MaxValue }; + _scaleMax = new float[3] { float.MinValue, float.MinValue, float.MinValue }; + _scaleTimeMin = float.MaxValue; + _scaleTimeMax = float.MinValue; + + _translationMin = new float[3] { float.MaxValue, float.MaxValue, float.MaxValue }; + _translationMax = new float[3] { float.MinValue, float.MinValue, float.MinValue }; + _translationTimeMin = float.MaxValue; + _translationTimeMax = float.MinValue; + + _rotationMin = new float[4] { float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue }; + _rotationMax = new float[4] { float.MinValue, float.MinValue, float.MinValue, float.MinValue }; + _rotationTimeMin = float.MaxValue; + _rotationTimeMax = float.MinValue; + } + /// + /// Add a scale keyframe. + /// + /// + /// + public void AddScaleKeyframe(Geometry.Vector3 scale, double time) + { + _scales.AddRange(BitConverter.GetBytes((float)scale.X)); + _scales.AddRange(BitConverter.GetBytes((float)scale.Y)); + _scales.AddRange(BitConverter.GetBytes((float)scale.Z)); + + _scaleTimes.AddRange(BitConverter.GetBytes((float)time)); + + _scaleMin[0] = Math.Min(_scaleMin[0], (float)scale.X); + _scaleMin[1] = Math.Min(_scaleMin[1], (float)scale.Y); + _scaleMin[2] = Math.Min(_scaleMin[2], (float)scale.Z); + + _scaleMax[0] = Math.Max(_scaleMax[0], (float)scale.X); + _scaleMax[1] = Math.Max(_scaleMax[1], (float)scale.Y); + _scaleMax[2] = Math.Max(_scaleMax[2], (float)scale.Z); + + _scaleTimeMin = Math.Min(_scaleTimeMin, (float)time); + _scaleTimeMax = Math.Max(_scaleTimeMax, (float)time); + } + + /// + /// Add a rotation keyframe. + /// + /// The axis of rotation. + /// The angle of rotation in radians. + /// The keyframe time. + public void AddRotationKeyframe(Elements.Geometry.Vector3 axis, double angle, double time) + { + var rotation = new Quaternion(new Vector3((float)axis.X, (float)axis.Y, (float)axis.Z), (float)Units.DegreesToRadians(angle)); + rotation = Quaternion.Normalize(rotation); + + _rotations.AddRange(BitConverter.GetBytes(rotation.X)); + _rotations.AddRange(BitConverter.GetBytes(rotation.Y)); + _rotations.AddRange(BitConverter.GetBytes(rotation.Z)); + _rotations.AddRange(BitConverter.GetBytes(rotation.W)); + + _rotationTimes.AddRange(BitConverter.GetBytes((float)time)); + + _rotationMin[0] = Math.Min(_rotationMin[0], rotation.X); + _rotationMin[1] = Math.Min(_rotationMin[1], rotation.Y); + _rotationMin[2] = Math.Min(_rotationMin[2], rotation.Z); + _rotationMin[3] = Math.Min(_rotationMin[3], rotation.W); + + _rotationMax[0] = Math.Max(_rotationMax[0], rotation.X); + _rotationMax[1] = Math.Max(_rotationMax[1], rotation.Y); + _rotationMax[2] = Math.Max(_rotationMax[2], rotation.Z); + _rotationMax[3] = Math.Max(_rotationMax[3], rotation.W); + + _rotationTimeMin = Math.Min(_rotationTimeMin, (float)time); + _rotationTimeMax = Math.Max(_rotationTimeMax, (float)time); + } + + /// + /// Add a translation keyframe. + /// + /// + /// + public void AddTranslationKeyframe(Geometry.Vector3 translation, double time) + { + _translations.AddRange(BitConverter.GetBytes((float)translation.X)); + _translations.AddRange(BitConverter.GetBytes((float)translation.Y)); + _translations.AddRange(BitConverter.GetBytes((float)translation.Z)); + + _translationTimes.AddRange(BitConverter.GetBytes((float)time)); + + _translationMin[0] = Math.Min(_translationMin[0], (float)translation.X); + _translationMin[1] = Math.Min(_translationMin[1], (float)translation.Y); + _translationMin[2] = Math.Min(_translationMin[2], (float)translation.Z); + + _translationMax[0] = Math.Max(_translationMax[0], (float)translation.X); + _translationMax[1] = Math.Max(_translationMax[1], (float)translation.Y); + _translationMax[2] = Math.Max(_translationMax[2], (float)translation.Z); + + _translationTimeMin = Math.Min(_translationTimeMin, (float)time); + _translationTimeMax = Math.Max(_translationTimeMax, (float)time); + } + + /// + /// Is the scale of the element animated? + /// + /// + public bool HasAnimatedScale() + { + return _scales.Count > 0; + } + + /// + /// Is the translation of the element animated? + /// + /// + public bool HasAnimatedTranslation() + { + return _translations.Count > 0; + } + + /// + /// Is the rotation of the element animated? + /// + /// + public bool HasAnimatedRotation() + { + return _rotations.Count > 0; + } + } +} \ No newline at end of file diff --git a/Elements/src/Element.cs b/Elements/src/Element.cs index 775336bf2..55d7a338c 100644 --- a/Elements/src/Element.cs +++ b/Elements/src/Element.cs @@ -12,6 +12,12 @@ public abstract class Element : System.ComponentModel.INotifyPropertyChanged private System.Guid _id; private string _name; + /// + /// The element's animation. + /// + [JsonIgnore] + public Animation Animation { get; set; } + /// /// Construct an element. /// diff --git a/Elements/src/Serialization/glTF/GltfExtensions.cs b/Elements/src/Serialization/glTF/GltfExtensions.cs index ff185f515..d7fc00abc 100644 --- a/Elements/src/Serialization/glTF/GltfExtensions.cs +++ b/Elements/src/Serialization/glTF/GltfExtensions.cs @@ -497,7 +497,14 @@ internal static void AddLights(this Gltf gltf, List lights, List no } } - private static int AddAccessor(List accessors, int bufferView, int byteOffset, Accessor.ComponentTypeEnum componentType, int count, float[] min, float[] max, Accessor.TypeEnum accessorType) + private static int AddAccessor(List accessors, + int bufferView, + int byteOffset, + Accessor.ComponentTypeEnum componentType, + int count, + float[] min, + float[] max, + Accessor.TypeEnum accessorType) { var a = new Accessor { @@ -963,6 +970,7 @@ internal static Gltf InitializeGlTF(Model model, var textures = new List(); var images = new List(); var samplers = new List(); + var animations = new List(); var materials = gltf.Materials != null ? gltf.Materials.ToList() : new List(); var meshElementMap = new Dictionary>(); @@ -1000,6 +1008,7 @@ internal static Gltf InitializeGlTF(Model model, meshTransformMap, currLines, drawEdges, + animations, mergeVertices); } catch (Exception ex) @@ -1053,6 +1062,10 @@ internal static Gltf InitializeGlTF(Model model, { gltf.Samplers = samplers.ToArray(samplers.Count); } + if (animations.Count > 0) + { + gltf.Animations = animations.ToArray(animations.Count); + } gltf.Nodes = nodes.ToArray(nodes.Count); if (meshes.Count > 0) { @@ -1083,10 +1096,12 @@ private static void GetRenderDataForElement(Element e, Dictionary meshTransformMap, List lines, bool drawEdges, + List animations, bool mergeVertices = false) { var materialId = BuiltInMaterials.Default.Id.ToString(); int meshId = -1; + int nodeId = -1; if (e is GeometricElement element) { @@ -1146,6 +1161,7 @@ private static void GetRenderDataForElement(Element e, materialId, ref meshId, content, + out nodeId, mergeVertices); if (!meshElementMap.ContainsKey(e.Id)) { @@ -1169,6 +1185,7 @@ private static void GetRenderDataForElement(Element e, materialId, ref meshId, geometricElement, + out nodeId, mergeVertices); if (meshId > -1 && !meshElementMap.ContainsKey(e.Id)) { @@ -1232,7 +1249,16 @@ private static void GetRenderDataForElement(Element e, { if (ge.TryToGraphicsBuffers(out List gb, out string id, out MeshPrimitive.ModeEnum? mode)) { - gltf.AddPointsOrLines(id, buffer, bufferViews, accessors, materialIndexMap[ge.Material.Id.ToString()], gb, (MeshPrimitive.ModeEnum)mode, meshes, nodes, ge.Transform); + gltf.AddPointsOrLines(id, + buffer, + bufferViews, + accessors, + materialIndexMap[ge.Material.Id.ToString()], + gb, + (MeshPrimitive.ModeEnum)mode, + meshes, + nodes, + ge.Transform); } } @@ -1266,9 +1292,164 @@ private static void GetRenderDataForElement(Element e, var geom = (GeometricElement)e; if (!geom.IsElementDefinition) { - NodeUtilities.CreateNodeForMesh(meshId, nodes, geom.Transform); + nodeId = NodeUtilities.CreateNodeForMesh(meshId, nodes, geom.Transform); } } + + if (e.Animation != null && nodeId != -1) + { + // https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_007_Animations.md + + var channels = new List(); + var animationSamplers = new List(); + + if (e.Animation.HasAnimatedScale()) + { + var scaleTimeBufferView = AddBufferView(bufferViews, 0, buffer.Count, e.Animation.ScaleTimes.Length, null, null); + buffer.AddRange(e.Animation.ScaleTimes); + var scaleTimeAccessor = AddAccessor(accessors, + scaleTimeBufferView, + 0, + Accessor.ComponentTypeEnum.FLOAT, + e.Animation.ScaleTimes.Length / sizeof(float), + new float[] { e.Animation.ScaleTimeMin }, + new float[] { e.Animation.ScaleTimeMax }, + Accessor.TypeEnum.SCALAR); + + var scaleBufferView = AddBufferView(bufferViews, 0, buffer.Count, e.Animation.Scales.Length, null, null); + buffer.AddRange(e.Animation.Scales); + var scaleAccessor = AddAccessor(accessors, + scaleBufferView, + 0, + Accessor.ComponentTypeEnum.FLOAT, + e.Animation.Scales.Length / sizeof(float) / 3, + e.Animation.ScaleMin, + e.Animation.ScaleMax, + Accessor.TypeEnum.VEC3); + + var scaleSampler = new AnimationSampler + { + Input = scaleTimeAccessor, // time + Output = scaleAccessor, // scale + Interpolation = AnimationSampler.InterpolationEnum.LINEAR + }; + + var scaleTarget = new AnimationChannelTarget + { + Node = nodeId, + Path = AnimationChannelTarget.PathEnum.scale + }; + + var scaleChannel = new AnimationChannel() + { + Target = scaleTarget, + Sampler = animationSamplers.Count + }; + + animationSamplers.Add(scaleSampler); + channels.Add(scaleChannel); + } + if (e.Animation.HasAnimatedTranslation()) + { + var translationTimeBufferView = AddBufferView(bufferViews, 0, buffer.Count, e.Animation.TranslationTimes.Length, null, null); + buffer.AddRange(e.Animation.TranslationTimes); + var translationTimeAccessor = AddAccessor(accessors, + translationTimeBufferView, + 0, + Accessor.ComponentTypeEnum.FLOAT, + e.Animation.TranslationTimes.Length / sizeof(float), + new float[] { e.Animation.TranslationTimeMin }, + new float[] { e.Animation.TranslationTimeMax }, + Accessor.TypeEnum.SCALAR); + + var translationBufferView = AddBufferView(bufferViews, 0, buffer.Count, e.Animation.Translations.Length, null, null); + buffer.AddRange(e.Animation.Translations); + var translationAccessor = AddAccessor(accessors, + translationBufferView, + 0, + Accessor.ComponentTypeEnum.FLOAT, + e.Animation.Translations.Length / sizeof(float) / 3, + e.Animation.TranslationMin, + e.Animation.TranslationMax, + Accessor.TypeEnum.VEC3); + + var translationSampler = new AnimationSampler + { + Input = translationTimeAccessor, // time + Output = translationAccessor, // scale + Interpolation = AnimationSampler.InterpolationEnum.LINEAR + }; + + var translationTarget = new AnimationChannelTarget + { + Node = nodeId, + Path = AnimationChannelTarget.PathEnum.translation + }; + + var translationChannel = new AnimationChannel() + { + Target = translationTarget, + Sampler = animationSamplers.Count + }; + + animationSamplers.Add(translationSampler); + channels.Add(translationChannel); + } + if (e.Animation.HasAnimatedRotation()) + { + var rotationTimeBufferView = AddBufferView(bufferViews, 0, buffer.Count, e.Animation.RotationTimes.Length, null, null); + buffer.AddRange(e.Animation.RotationTimes); + var rotationTimeAccessor = AddAccessor(accessors, + rotationTimeBufferView, + 0, + Accessor.ComponentTypeEnum.FLOAT, + e.Animation.RotationTimes.Length / sizeof(float), + new float[] { e.Animation.RotationTimeMin }, + new float[] { e.Animation.RotationTimeMax }, + Accessor.TypeEnum.SCALAR); + + var rotationBufferView = AddBufferView(bufferViews, 0, buffer.Count, e.Animation.Rotations.Length, null, null); + buffer.AddRange(e.Animation.Rotations); + var rotationAccessor = AddAccessor(accessors, + rotationBufferView, + 0, + Accessor.ComponentTypeEnum.FLOAT, + e.Animation.Rotations.Length / sizeof(float) / 4, + e.Animation.RotationMin, + e.Animation.RotationMax, + Accessor.TypeEnum.VEC4); + + var rotationSampler = new AnimationSampler + { + Input = rotationTimeAccessor, // time + Output = rotationAccessor, // scale + Interpolation = AnimationSampler.InterpolationEnum.LINEAR + }; + + var rotationTarget = new AnimationChannelTarget + { + Node = nodeId, + Path = AnimationChannelTarget.PathEnum.rotation + }; + + var rotationChannel = new AnimationChannel() + { + Target = rotationTarget, + Sampler = animationSamplers.Count + }; + + animationSamplers.Add(rotationSampler); + channels.Add(rotationChannel); + } + + var anim = new glTFLoader.Schema.Animation() + { + Channels = channels.ToArray(), + Samplers = animationSamplers.ToArray() + }; + + animations.Add(anim); + } } private static readonly Dictionary gltfCache = new Dictionary(); @@ -1380,10 +1561,13 @@ private static int ProcessGeometricRepresentation(Element e, string materialId, ref int meshId, GeometricElement geometricElement, + out int nodeId, bool mergeVertices = false) { geometricElement.UpdateRepresentations(); + nodeId = -1; + // TODO: Remove this when we get rid of UpdateRepresentation. // The only reason we don't fully exclude openings from processing // is to ensure that openings have some geometry that will be used @@ -1415,7 +1599,7 @@ private static int ProcessGeometricRepresentation(Element e, if (!geometricElement.IsElementDefinition) { - NodeUtilities.CreateNodeForMesh(meshId, nodes, geometricElement.Transform); + nodeId = NodeUtilities.CreateNodeForMesh(meshId, nodes, geometricElement.Transform); } return meshId; } diff --git a/Elements/src/Serialization/glTF/NodeUtilities.cs b/Elements/src/Serialization/glTF/NodeUtilities.cs index 922ff2231..13e875652 100644 --- a/Elements/src/Serialization/glTF/NodeUtilities.cs +++ b/Elements/src/Serialization/glTF/NodeUtilities.cs @@ -54,13 +54,14 @@ internal static int CreateAndAddTransformNode(List nodes, Transform transf var b = transform.YAxis; var c = transform.ZAxis; - var transNode = new Node(); - - transNode.Matrix = new[]{ + var transNode = new Node + { + Matrix = new[]{ (float)a.X, (float)a.Y, (float)a.Z, 0.0f, (float)b.X, (float)b.Y, (float)b.Z, 0.0f, (float)c.X, (float)c.Y, (float)c.Z, 0.0f, (float)transform.Origin.X,(float)transform.Origin.Y,(float)transform.Origin.Z, 1.0f + } }; parentId = AddNode(nodes, transNode, 0); @@ -122,9 +123,11 @@ internal static void AddInstanceAsCopyOfNode( // transform, so that the transform can be modified in explore at // runtime (e.g. by a transform override) and have the expected effect. float[] elementTransform = TransformToMatrix(transform); - var newNode = new glTFLoader.Schema.Node(); - newNode.Name = $"{instanceElementId}"; - newNode.Matrix = elementTransform; + var newNode = new glTFLoader.Schema.Node + { + Name = $"{instanceElementId}", + Matrix = elementTransform + }; nodes.Add(newNode); newNode.Children = new[] { nodes.Count }; @@ -134,8 +137,10 @@ internal static void AddInstanceAsCopyOfNode( // back to Y up further up in the node hierarchy. rootTransform.Rotate(new Vector3(1, 0, 0), 90.0); float[] glbOrientationTransform = TransformToMatrix(rootTransform); - var elementOrientationNode = new glTFLoader.Schema.Node(); - elementOrientationNode.Matrix = glbOrientationTransform; + var elementOrientationNode = new glTFLoader.Schema.Node + { + Matrix = glbOrientationTransform + }; nodes.Add(elementOrientationNode); elementOrientationNode.Children = new[] { nodes.Count }; @@ -146,8 +151,10 @@ internal static void AddInstanceAsCopyOfNode( private static int RecursivelyCopyNode(List nodes, ProtoNode nodeToCopy) { - var newNode = new Node(); - newNode.Matrix = nodeToCopy.Matrix; + var newNode = new Node + { + Matrix = nodeToCopy.Matrix + }; if (nodeToCopy.Mesh != null) { newNode.Mesh = nodeToCopy.Mesh; @@ -201,8 +208,11 @@ internal static int CreateNodeForMesh(int meshId, List n parentId = CreateAndAddRTSNode(nodes, transform, parentId); // Add mesh node to gltf nodes - var node = new Node(); - node.Mesh = meshId; + var node = new Node + { + Mesh = meshId, + }; + var nodeId = AddNode(nodes, node, parentId); return nodeId; } diff --git a/Elements/test/TransformTests.cs b/Elements/test/TransformTests.cs index 23d9f5d62..358a8c102 100644 --- a/Elements/test/TransformTests.cs +++ b/Elements/test/TransformTests.cs @@ -20,17 +20,28 @@ public void Example() var count = 10; for (var i = 0.0; i < 360.0; i += 360.0 / (double)count) { - var m2 = new Mass(prof, 1.0, new Material($"color_{j}", new Color((float)j - 1.0f, 0.0f, 0.0f, 1.0f)), new Transform()); + var m2 = new Mass(prof, 1.0, new Material($"color_{j}", new Color((float)j - 1.0f, 0.0f, 0.0f, 1.0f))); + var t = new Transform(); // Scale the mass. - m2.Transform.Scale(new Vector3(j, j, j)); + // m2.Transform.Scale(new Vector3(j, j, j)); // Move the mass. - m2.Transform.Move(new Vector3(3, 0, 0)); + t.Move(new Vector3(3, 0, 0)); // Rotate the mass. - m2.Transform.Rotate(Vector3.ZAxis, i); + t.Rotate(Vector3.ZAxis, i); + this.Model.AddElement(m2); + + m2.Animation = new Animation(); + m2.Animation.AddRotationKeyframe(Vector3.ZAxis, 0.0, 0.0); + m2.Animation.AddRotationKeyframe(Vector3.ZAxis, 360.0, 5.0); + m2.Animation.AddTranslationKeyframe(Vector3.Origin, 0.0); + m2.Animation.AddTranslationKeyframe(t.Origin, 2.0); + m2.Animation.AddScaleKeyframe(Vector3.Origin, 0.0); + m2.Animation.AddScaleKeyframe(new Vector3(j, j, j), 1.0); + j += 1.0 / (double)count; } // From 7f44e0c871b139c2fa762df4945f49a7515d32d5 Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Sun, 21 Aug 2022 21:18:33 -0700 Subject: [PATCH 3/9] More notes. --- Elements/src/Animation.cs | 110 ++++++++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 23 deletions(-) diff --git a/Elements/src/Animation.cs b/Elements/src/Animation.cs index 5a33dd94f..da06f5f2d 100644 --- a/Elements/src/Animation.cs +++ b/Elements/src/Animation.cs @@ -9,47 +9,113 @@ namespace Elements /// public class Animation { - List _scaleTimes = new List(); - List _scales = new List(); - List _translationTimes = new List(); - List _translations = new List(); - List _rotationTimes = new List(); - List _rotations = new List(); - - float[] _scaleMin; - float[] _scaleMax; + readonly List _scaleTimes = new List(); + readonly List _scales = new List(); + readonly List _translationTimes = new List(); + readonly List _translations = new List(); + readonly List _rotationTimes = new List(); + readonly List _rotations = new List(); + readonly float[] _scaleMin; + readonly float[] _scaleMax; float _scaleTimeMin; float _scaleTimeMax; - - float[] _translationMin; - float[] _translationMax; + readonly float[] _translationMin; + readonly float[] _translationMax; float _translationTimeMin; float _translationTimeMax; - - float[] _rotationMin; - float[] _rotationMax; + readonly float[] _rotationMin; + readonly float[] _rotationMax; float _rotationTimeMin; float _rotationTimeMax; + /// + /// An array of bytes representing the scale keys. + /// public byte[] Scales => _scales.ToArray(); + + /// + /// An array of bytes representing the scale time keys. + /// public byte[] ScaleTimes => _scaleTimes.ToArray(); + + /// + /// The minimum scale time. + /// public float ScaleTimeMin => _scaleTimeMin; + + /// + /// The maximum scale time. + /// public float ScaleTimeMax => _scaleTimeMax; + + /// + /// The minimum scale as [x,y,z]. + /// public float[] ScaleMin => _scaleMin; + + /// + /// The maximum scale as [x,y,z]; + /// public float[] ScaleMax => _scaleMax; + /// + /// An array of bytes representing translation keys. + /// public byte[] Translations => _translations.ToArray(); + + /// + /// An array of bytes representing translation time keys. + /// public byte[] TranslationTimes => _translationTimes.ToArray(); + + /// + /// The minimum translation time. + /// public float TranslationTimeMin => _translationTimeMin; + + /// + /// The maximum translation time. + /// public float TranslationTimeMax => _translationTimeMax; + + /// + /// The minimum translation as [x,y,z]. + /// public float[] TranslationMin => _translationMin; + + /// + /// The maximum translation as [x,y,z]. + /// public float[] TranslationMax => _translationMax; + /// + /// An array of bytes representing rotation keys. + /// public byte[] Rotations => _rotations.ToArray(); + + /// + /// An array of bytes representing rotation time keys. + /// public byte[] RotationTimes => _rotationTimes.ToArray(); + + /// + /// The minimum rotation time. + /// public float RotationTimeMin => _rotationTimeMin; + + /// + /// The maximum rotation time. + /// public float RotationTimeMax => _rotationTimeMax; + + /// + /// The minimum rotation as [x,y,z,w]. + /// public float[] RotationMin => _rotationMin; + + /// + /// The maximum rotation as [x,y,z,w]. + /// public float[] RotationMax => _rotationMax; /// @@ -72,11 +138,12 @@ public Animation() _rotationTimeMin = float.MaxValue; _rotationTimeMax = float.MinValue; } + /// /// Add a scale keyframe. /// - /// - /// + /// The scale value. + /// The time at which to apply the scale value. public void AddScaleKeyframe(Geometry.Vector3 scale, double time) { _scales.AddRange(BitConverter.GetBytes((float)scale.X)); @@ -103,7 +170,7 @@ public void AddScaleKeyframe(Geometry.Vector3 scale, double time) /// The axis of rotation. /// The angle of rotation in radians. /// The keyframe time. - public void AddRotationKeyframe(Elements.Geometry.Vector3 axis, double angle, double time) + public void AddRotationKeyframe(Geometry.Vector3 axis, double angle, double time) { var rotation = new Quaternion(new Vector3((float)axis.X, (float)axis.Y, (float)axis.Z), (float)Units.DegreesToRadians(angle)); rotation = Quaternion.Normalize(rotation); @@ -132,8 +199,8 @@ public void AddRotationKeyframe(Elements.Geometry.Vector3 axis, double angle, do /// /// Add a translation keyframe. /// - /// - /// + /// The translation value. + /// The time at which to apply the translation. public void AddTranslationKeyframe(Geometry.Vector3 translation, double time) { _translations.AddRange(BitConverter.GetBytes((float)translation.X)); @@ -157,7 +224,6 @@ public void AddTranslationKeyframe(Geometry.Vector3 translation, double time) /// /// Is the scale of the element animated? /// - /// public bool HasAnimatedScale() { return _scales.Count > 0; @@ -166,7 +232,6 @@ public bool HasAnimatedScale() /// /// Is the translation of the element animated? /// - /// public bool HasAnimatedTranslation() { return _translations.Count > 0; @@ -175,7 +240,6 @@ public bool HasAnimatedTranslation() /// /// Is the rotation of the element animated? /// - /// public bool HasAnimatedRotation() { return _rotations.Count > 0; From c15564065b9c92c0f829d7a0d24b4b79f4a6c92c Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Sun, 21 Aug 2022 21:50:42 -0700 Subject: [PATCH 4/9] Separate animation test. --- Elements/test/TransformTests.cs | 44 ++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/Elements/test/TransformTests.cs b/Elements/test/TransformTests.cs index 358a8c102..caa2ef72e 100644 --- a/Elements/test/TransformTests.cs +++ b/Elements/test/TransformTests.cs @@ -18,33 +18,59 @@ public void Example() var j = 1.0; var count = 10; - for (var i = 0.0; i < 360.0; i += 360.0 / (double)count) + for (var i = 0.0; i < 360.0; i += 360.0 / count) { var m2 = new Mass(prof, 1.0, new Material($"color_{j}", new Color((float)j - 1.0f, 0.0f, 0.0f, 1.0f))); - var t = new Transform(); // Scale the mass. - // m2.Transform.Scale(new Vector3(j, j, j)); + m2.Transform.Scale(new Vector3(j, j, j)); // Move the mass. - t.Move(new Vector3(3, 0, 0)); + m2.Transform.Move(new Vector3(3, 0, 0)); // Rotate the mass. + m2.Transform.Rotate(Vector3.ZAxis, i); + + this.Model.AddElement(m2); + + j += 1.0 / (double)count; + } + // + } + + [Fact] + public void Animation() + { + this.Name = "Animation"; + + var m1 = new Mass(Polygon.Rectangle(1.0, 1.0), 1.0, new Material("yellow", Colors.Yellow)); + this.Model.AddElement(m1); + + Profile prof = Polygon.Rectangle(1.0, 1.0); + + var j = 1.0; + var count = 10; + for (var i = 0.0; i < 360.0; i += 360.0 / count) + { + var m2 = new Mass(prof, 1.0, new Material($"color_{j}", new Color((float)j - 1.0f, 0.0f, 0.0f, 1.0f))); + + var t = new Transform(); + t.Move(new Vector3(3, 0, 0)); t.Rotate(Vector3.ZAxis, i); this.Model.AddElement(m2); m2.Animation = new Animation(); m2.Animation.AddRotationKeyframe(Vector3.ZAxis, 0.0, 0.0); - m2.Animation.AddRotationKeyframe(Vector3.ZAxis, 360.0, 5.0); m2.Animation.AddTranslationKeyframe(Vector3.Origin, 0.0); - m2.Animation.AddTranslationKeyframe(t.Origin, 2.0); m2.Animation.AddScaleKeyframe(Vector3.Origin, 0.0); - m2.Animation.AddScaleKeyframe(new Vector3(j, j, j), 1.0); - j += 1.0 / (double)count; + m2.Animation.AddTranslationKeyframe(t.Origin, 3.0); + m2.Animation.AddRotationKeyframe(Vector3.ZAxis, i, 3.1); + m2.Animation.AddScaleKeyframe(new Vector3(j, j, j), 2.9); + + j += 1.0 / count; } - // } [Fact] From 1326013382ec50a1936c4b41dbce41c6a75c8172 Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Wed, 31 Aug 2022 13:24:12 -0700 Subject: [PATCH 5/9] Update the changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 034c462b1..f43848334 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ - `AdaptiveGrid.Obstacle.Intersects(Line line, double tolerance = 1e-05)` method - `AdaptiveGrid.Obstacle.IsInside(Vector3 point, double tolerance = 1e-05)` method - `Elements.SVG.SvgSection.CreatePlanFromFromModels(IList models, double elevation, SvgContext frontContext, SvgContext backContext, string path, bool showGrid = true, double gridHeadExtension = 2.0, double gridHeadRadius = 0.5, PlanRotation planRotation = PlanRotation.Angle, double planRotationDegrees = 0.0)` +- `Elements.Animation` +- `Element.Animation` ### Changed From 617cbc97f4c081df99835e7fef63f1d9a02a7e60 Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Wed, 31 Aug 2022 13:37:48 -0700 Subject: [PATCH 6/9] Move animation to GeometricElement. --- Elements/src/Animation.cs | 4 ++-- Elements/src/Element.cs | 6 ------ Elements/src/GeometricElement.cs | 6 ++++++ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Elements/src/Animation.cs b/Elements/src/Animation.cs index da06f5f2d..d9915b233 100644 --- a/Elements/src/Animation.cs +++ b/Elements/src/Animation.cs @@ -5,7 +5,7 @@ namespace Elements { /// - /// An element's animation. + /// The animation of a transform expressed using glTF convention. /// public class Animation { @@ -168,7 +168,7 @@ public void AddScaleKeyframe(Geometry.Vector3 scale, double time) /// Add a rotation keyframe. /// /// The axis of rotation. - /// The angle of rotation in radians. + /// The angle of rotation in degrees. /// The keyframe time. public void AddRotationKeyframe(Geometry.Vector3 axis, double angle, double time) { diff --git a/Elements/src/Element.cs b/Elements/src/Element.cs index 55d7a338c..775336bf2 100644 --- a/Elements/src/Element.cs +++ b/Elements/src/Element.cs @@ -12,12 +12,6 @@ public abstract class Element : System.ComponentModel.INotifyPropertyChanged private System.Guid _id; private string _name; - /// - /// The element's animation. - /// - [JsonIgnore] - public Animation Animation { get; set; } - /// /// Construct an element. /// diff --git a/Elements/src/GeometricElement.cs b/Elements/src/GeometricElement.cs index 4c821aeee..cf625fb7b 100644 --- a/Elements/src/GeometricElement.cs +++ b/Elements/src/GeometricElement.cs @@ -21,6 +21,12 @@ public class GeometricElement : Element internal BBox3 _bounds; internal Csg.Solid _csg; + /// + /// The element's animation. + /// + [JsonIgnore] + public Animation Animation { get; set; } + /// The element's transform. [JsonProperty("Transform", Required = Required.AllowNull)] public Transform Transform { get; set; } From ee476440caf9dc62652be0b40403962f646ab8b1 Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Wed, 31 Aug 2022 13:41:15 -0700 Subject: [PATCH 7/9] Only export animation if its a geom. --- .../src/Serialization/glTF/GltfExtensions.cs | 310 +++++++++--------- 1 file changed, 155 insertions(+), 155 deletions(-) diff --git a/Elements/src/Serialization/glTF/GltfExtensions.cs b/Elements/src/Serialization/glTF/GltfExtensions.cs index ac7ceb383..d9149f294 100644 --- a/Elements/src/Serialization/glTF/GltfExtensions.cs +++ b/Elements/src/Serialization/glTF/GltfExtensions.cs @@ -1305,6 +1305,161 @@ private static void GetRenderDataForElement(Element e, nodes, ge.Transform); } + + if (ge.Animation != null && nodeId != -1) + { + // https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_007_Animations.md + + var channels = new List(); + var animationSamplers = new List(); + + if (ge.Animation.HasAnimatedScale()) + { + var scaleTimeBufferView = AddBufferView(bufferViews, 0, buffer.Count, ge.Animation.ScaleTimes.Length, null, null); + buffer.AddRange(ge.Animation.ScaleTimes); + var scaleTimeAccessor = AddAccessor(accessors, + scaleTimeBufferView, + 0, + Accessor.ComponentTypeEnum.FLOAT, + ge.Animation.ScaleTimes.Length / sizeof(float), + new float[] { ge.Animation.ScaleTimeMin }, + new float[] { ge.Animation.ScaleTimeMax }, + Accessor.TypeEnum.SCALAR); + + var scaleBufferView = AddBufferView(bufferViews, 0, buffer.Count, ge.Animation.Scales.Length, null, null); + buffer.AddRange(ge.Animation.Scales); + var scaleAccessor = AddAccessor(accessors, + scaleBufferView, + 0, + Accessor.ComponentTypeEnum.FLOAT, + ge.Animation.Scales.Length / sizeof(float) / 3, + ge.Animation.ScaleMin, + ge.Animation.ScaleMax, + Accessor.TypeEnum.VEC3); + + var scaleSampler = new AnimationSampler + { + Input = scaleTimeAccessor, // time + Output = scaleAccessor, // scale + Interpolation = AnimationSampler.InterpolationEnum.LINEAR + }; + + var scaleTarget = new AnimationChannelTarget + { + Node = nodeId, + Path = AnimationChannelTarget.PathEnum.scale + }; + + var scaleChannel = new AnimationChannel() + { + Target = scaleTarget, + Sampler = animationSamplers.Count + }; + + animationSamplers.Add(scaleSampler); + channels.Add(scaleChannel); + } + if (ge.Animation.HasAnimatedTranslation()) + { + var translationTimeBufferView = AddBufferView(bufferViews, 0, buffer.Count, ge.Animation.TranslationTimes.Length, null, null); + buffer.AddRange(ge.Animation.TranslationTimes); + var translationTimeAccessor = AddAccessor(accessors, + translationTimeBufferView, + 0, + Accessor.ComponentTypeEnum.FLOAT, + ge.Animation.TranslationTimes.Length / sizeof(float), + new float[] { ge.Animation.TranslationTimeMin }, + new float[] { ge.Animation.TranslationTimeMax }, + Accessor.TypeEnum.SCALAR); + + var translationBufferView = AddBufferView(bufferViews, 0, buffer.Count, ge.Animation.Translations.Length, null, null); + buffer.AddRange(ge.Animation.Translations); + var translationAccessor = AddAccessor(accessors, + translationBufferView, + 0, + Accessor.ComponentTypeEnum.FLOAT, + ge.Animation.Translations.Length / sizeof(float) / 3, + ge.Animation.TranslationMin, + ge.Animation.TranslationMax, + Accessor.TypeEnum.VEC3); + + var translationSampler = new AnimationSampler + { + Input = translationTimeAccessor, // time + Output = translationAccessor, // scale + Interpolation = AnimationSampler.InterpolationEnum.LINEAR + }; + + var translationTarget = new AnimationChannelTarget + { + Node = nodeId, + Path = AnimationChannelTarget.PathEnum.translation + }; + + var translationChannel = new AnimationChannel() + { + Target = translationTarget, + Sampler = animationSamplers.Count + }; + + animationSamplers.Add(translationSampler); + channels.Add(translationChannel); + } + if (ge.Animation.HasAnimatedRotation()) + { + var rotationTimeBufferView = AddBufferView(bufferViews, 0, buffer.Count, ge.Animation.RotationTimes.Length, null, null); + buffer.AddRange(ge.Animation.RotationTimes); + var rotationTimeAccessor = AddAccessor(accessors, + rotationTimeBufferView, + 0, + Accessor.ComponentTypeEnum.FLOAT, + ge.Animation.RotationTimes.Length / sizeof(float), + new float[] { ge.Animation.RotationTimeMin }, + new float[] { ge.Animation.RotationTimeMax }, + Accessor.TypeEnum.SCALAR); + + var rotationBufferView = AddBufferView(bufferViews, 0, buffer.Count, ge.Animation.Rotations.Length, null, null); + buffer.AddRange(ge.Animation.Rotations); + var rotationAccessor = AddAccessor(accessors, + rotationBufferView, + 0, + Accessor.ComponentTypeEnum.FLOAT, + ge.Animation.Rotations.Length / sizeof(float) / 4, + ge.Animation.RotationMin, + ge.Animation.RotationMax, + Accessor.TypeEnum.VEC4); + + var rotationSampler = new AnimationSampler + { + Input = rotationTimeAccessor, // time + Output = rotationAccessor, // scale + Interpolation = AnimationSampler.InterpolationEnum.LINEAR + }; + + var rotationTarget = new AnimationChannelTarget + { + Node = nodeId, + Path = AnimationChannelTarget.PathEnum.rotation + }; + + var rotationChannel = new AnimationChannel() + { + Target = rotationTarget, + Sampler = animationSamplers.Count + }; + + animationSamplers.Add(rotationSampler); + channels.Add(rotationChannel); + } + + var anim = new glTFLoader.Schema.Animation() + { + Channels = channels.ToArray(), + Samplers = animationSamplers.ToArray() + }; + + animations.Add(anim); + } } if (e is ITessellate geo) @@ -1340,161 +1495,6 @@ private static void GetRenderDataForElement(Element e, nodeId = NodeUtilities.CreateNodeForMesh(meshId, nodes, geom.Transform); } } - - if (e.Animation != null && nodeId != -1) - { - // https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_007_Animations.md - - var channels = new List(); - var animationSamplers = new List(); - - if (e.Animation.HasAnimatedScale()) - { - var scaleTimeBufferView = AddBufferView(bufferViews, 0, buffer.Count, e.Animation.ScaleTimes.Length, null, null); - buffer.AddRange(e.Animation.ScaleTimes); - var scaleTimeAccessor = AddAccessor(accessors, - scaleTimeBufferView, - 0, - Accessor.ComponentTypeEnum.FLOAT, - e.Animation.ScaleTimes.Length / sizeof(float), - new float[] { e.Animation.ScaleTimeMin }, - new float[] { e.Animation.ScaleTimeMax }, - Accessor.TypeEnum.SCALAR); - - var scaleBufferView = AddBufferView(bufferViews, 0, buffer.Count, e.Animation.Scales.Length, null, null); - buffer.AddRange(e.Animation.Scales); - var scaleAccessor = AddAccessor(accessors, - scaleBufferView, - 0, - Accessor.ComponentTypeEnum.FLOAT, - e.Animation.Scales.Length / sizeof(float) / 3, - e.Animation.ScaleMin, - e.Animation.ScaleMax, - Accessor.TypeEnum.VEC3); - - var scaleSampler = new AnimationSampler - { - Input = scaleTimeAccessor, // time - Output = scaleAccessor, // scale - Interpolation = AnimationSampler.InterpolationEnum.LINEAR - }; - - var scaleTarget = new AnimationChannelTarget - { - Node = nodeId, - Path = AnimationChannelTarget.PathEnum.scale - }; - - var scaleChannel = new AnimationChannel() - { - Target = scaleTarget, - Sampler = animationSamplers.Count - }; - - animationSamplers.Add(scaleSampler); - channels.Add(scaleChannel); - } - if (e.Animation.HasAnimatedTranslation()) - { - var translationTimeBufferView = AddBufferView(bufferViews, 0, buffer.Count, e.Animation.TranslationTimes.Length, null, null); - buffer.AddRange(e.Animation.TranslationTimes); - var translationTimeAccessor = AddAccessor(accessors, - translationTimeBufferView, - 0, - Accessor.ComponentTypeEnum.FLOAT, - e.Animation.TranslationTimes.Length / sizeof(float), - new float[] { e.Animation.TranslationTimeMin }, - new float[] { e.Animation.TranslationTimeMax }, - Accessor.TypeEnum.SCALAR); - - var translationBufferView = AddBufferView(bufferViews, 0, buffer.Count, e.Animation.Translations.Length, null, null); - buffer.AddRange(e.Animation.Translations); - var translationAccessor = AddAccessor(accessors, - translationBufferView, - 0, - Accessor.ComponentTypeEnum.FLOAT, - e.Animation.Translations.Length / sizeof(float) / 3, - e.Animation.TranslationMin, - e.Animation.TranslationMax, - Accessor.TypeEnum.VEC3); - - var translationSampler = new AnimationSampler - { - Input = translationTimeAccessor, // time - Output = translationAccessor, // scale - Interpolation = AnimationSampler.InterpolationEnum.LINEAR - }; - - var translationTarget = new AnimationChannelTarget - { - Node = nodeId, - Path = AnimationChannelTarget.PathEnum.translation - }; - - var translationChannel = new AnimationChannel() - { - Target = translationTarget, - Sampler = animationSamplers.Count - }; - - animationSamplers.Add(translationSampler); - channels.Add(translationChannel); - } - if (e.Animation.HasAnimatedRotation()) - { - var rotationTimeBufferView = AddBufferView(bufferViews, 0, buffer.Count, e.Animation.RotationTimes.Length, null, null); - buffer.AddRange(e.Animation.RotationTimes); - var rotationTimeAccessor = AddAccessor(accessors, - rotationTimeBufferView, - 0, - Accessor.ComponentTypeEnum.FLOAT, - e.Animation.RotationTimes.Length / sizeof(float), - new float[] { e.Animation.RotationTimeMin }, - new float[] { e.Animation.RotationTimeMax }, - Accessor.TypeEnum.SCALAR); - - var rotationBufferView = AddBufferView(bufferViews, 0, buffer.Count, e.Animation.Rotations.Length, null, null); - buffer.AddRange(e.Animation.Rotations); - var rotationAccessor = AddAccessor(accessors, - rotationBufferView, - 0, - Accessor.ComponentTypeEnum.FLOAT, - e.Animation.Rotations.Length / sizeof(float) / 4, - e.Animation.RotationMin, - e.Animation.RotationMax, - Accessor.TypeEnum.VEC4); - - var rotationSampler = new AnimationSampler - { - Input = rotationTimeAccessor, // time - Output = rotationAccessor, // scale - Interpolation = AnimationSampler.InterpolationEnum.LINEAR - }; - - var rotationTarget = new AnimationChannelTarget - { - Node = nodeId, - Path = AnimationChannelTarget.PathEnum.rotation - }; - - var rotationChannel = new AnimationChannel() - { - Target = rotationTarget, - Sampler = animationSamplers.Count - }; - - animationSamplers.Add(rotationSampler); - channels.Add(rotationChannel); - } - - var anim = new glTFLoader.Schema.Animation() - { - Channels = channels.ToArray(), - Samplers = animationSamplers.ToArray() - }; - - animations.Add(anim); - } } private static readonly Dictionary gltfCache = new Dictionary(); From e715b9747b0e8e1e0720efec3219f32b52e2b74b Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Wed, 31 Aug 2022 13:44:14 -0700 Subject: [PATCH 8/9] Make the units clearer. --- Elements/src/Animation.cs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Elements/src/Animation.cs b/Elements/src/Animation.cs index d9915b233..cf410ce50 100644 --- a/Elements/src/Animation.cs +++ b/Elements/src/Animation.cs @@ -143,14 +143,14 @@ public Animation() /// Add a scale keyframe. /// /// The scale value. - /// The time at which to apply the scale value. - public void AddScaleKeyframe(Geometry.Vector3 scale, double time) + /// The time at which to apply the scale value. + public void AddScaleKeyframe(Geometry.Vector3 scale, double timeSeconds) { _scales.AddRange(BitConverter.GetBytes((float)scale.X)); _scales.AddRange(BitConverter.GetBytes((float)scale.Y)); _scales.AddRange(BitConverter.GetBytes((float)scale.Z)); - _scaleTimes.AddRange(BitConverter.GetBytes((float)time)); + _scaleTimes.AddRange(BitConverter.GetBytes((float)timeSeconds)); _scaleMin[0] = Math.Min(_scaleMin[0], (float)scale.X); _scaleMin[1] = Math.Min(_scaleMin[1], (float)scale.Y); @@ -160,19 +160,19 @@ public void AddScaleKeyframe(Geometry.Vector3 scale, double time) _scaleMax[1] = Math.Max(_scaleMax[1], (float)scale.Y); _scaleMax[2] = Math.Max(_scaleMax[2], (float)scale.Z); - _scaleTimeMin = Math.Min(_scaleTimeMin, (float)time); - _scaleTimeMax = Math.Max(_scaleTimeMax, (float)time); + _scaleTimeMin = Math.Min(_scaleTimeMin, (float)timeSeconds); + _scaleTimeMax = Math.Max(_scaleTimeMax, (float)timeSeconds); } /// /// Add a rotation keyframe. /// /// The axis of rotation. - /// The angle of rotation in degrees. - /// The keyframe time. - public void AddRotationKeyframe(Geometry.Vector3 axis, double angle, double time) + /// The angle of rotation in degrees. + /// The keyframe time. + public void AddRotationKeyframe(Geometry.Vector3 axis, double angleDegrees, double timeSeconds) { - var rotation = new Quaternion(new Vector3((float)axis.X, (float)axis.Y, (float)axis.Z), (float)Units.DegreesToRadians(angle)); + var rotation = new Quaternion(new Vector3((float)axis.X, (float)axis.Y, (float)axis.Z), (float)Units.DegreesToRadians(angleDegrees)); rotation = Quaternion.Normalize(rotation); _rotations.AddRange(BitConverter.GetBytes(rotation.X)); @@ -180,7 +180,7 @@ public void AddRotationKeyframe(Geometry.Vector3 axis, double angle, double time _rotations.AddRange(BitConverter.GetBytes(rotation.Z)); _rotations.AddRange(BitConverter.GetBytes(rotation.W)); - _rotationTimes.AddRange(BitConverter.GetBytes((float)time)); + _rotationTimes.AddRange(BitConverter.GetBytes((float)timeSeconds)); _rotationMin[0] = Math.Min(_rotationMin[0], rotation.X); _rotationMin[1] = Math.Min(_rotationMin[1], rotation.Y); @@ -192,22 +192,22 @@ public void AddRotationKeyframe(Geometry.Vector3 axis, double angle, double time _rotationMax[2] = Math.Max(_rotationMax[2], rotation.Z); _rotationMax[3] = Math.Max(_rotationMax[3], rotation.W); - _rotationTimeMin = Math.Min(_rotationTimeMin, (float)time); - _rotationTimeMax = Math.Max(_rotationTimeMax, (float)time); + _rotationTimeMin = Math.Min(_rotationTimeMin, (float)timeSeconds); + _rotationTimeMax = Math.Max(_rotationTimeMax, (float)timeSeconds); } /// /// Add a translation keyframe. /// /// The translation value. - /// The time at which to apply the translation. - public void AddTranslationKeyframe(Geometry.Vector3 translation, double time) + /// The time at which to apply the translation. + public void AddTranslationKeyframe(Geometry.Vector3 translation, double timeSeconds) { _translations.AddRange(BitConverter.GetBytes((float)translation.X)); _translations.AddRange(BitConverter.GetBytes((float)translation.Y)); _translations.AddRange(BitConverter.GetBytes((float)translation.Z)); - _translationTimes.AddRange(BitConverter.GetBytes((float)time)); + _translationTimes.AddRange(BitConverter.GetBytes((float)timeSeconds)); _translationMin[0] = Math.Min(_translationMin[0], (float)translation.X); _translationMin[1] = Math.Min(_translationMin[1], (float)translation.Y); @@ -217,8 +217,8 @@ public void AddTranslationKeyframe(Geometry.Vector3 translation, double time) _translationMax[1] = Math.Max(_translationMax[1], (float)translation.Y); _translationMax[2] = Math.Max(_translationMax[2], (float)translation.Z); - _translationTimeMin = Math.Min(_translationTimeMin, (float)time); - _translationTimeMax = Math.Max(_translationTimeMax, (float)time); + _translationTimeMin = Math.Min(_translationTimeMin, (float)timeSeconds); + _translationTimeMax = Math.Max(_translationTimeMax, (float)timeSeconds); } /// From eafa86facf749d01f9706619ba861f1786864f4f Mon Sep 17 00:00:00 2001 From: Ian Keough Date: Thu, 17 Nov 2022 15:02:21 -0800 Subject: [PATCH 9/9] Cleanup bad comment. --- Elements/src/GeometricElement.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Elements/src/GeometricElement.cs b/Elements/src/GeometricElement.cs index ff76698d0..79374ca73 100644 --- a/Elements/src/GeometricElement.cs +++ b/Elements/src/GeometricElement.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Reflection; using System.Runtime.CompilerServices; using Elements.Geometry; using Elements.Geometry.Solids; @@ -31,9 +30,10 @@ public class GeometricElement : Element [JsonIgnore] public Animation Animation { get; set; } + /// /// The element's bounds. /// The bounds are only available when the geometry has been - /// updated using UpdateBoundsAndComputeSolid(), + /// updated using UpdateBoundsAndComputeSolid(). /// [JsonIgnore] public BBox3 Bounds => _bounds;