diff --git a/CHANGELOG.md b/CHANGELOG.md
index e3f862292..a0165bc78 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,8 @@
### Added
- `AdaptiveGraphRouting.ErrorMessages`
+- `Elements.Animation`
+- `Element.Animation`
### Changed
diff --git a/Elements/src/Animation.cs b/Elements/src/Animation.cs
new file mode 100644
index 000000000..cf410ce50
--- /dev/null
+++ b/Elements/src/Animation.cs
@@ -0,0 +1,248 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+
+namespace Elements
+{
+ ///
+ /// The animation of a transform expressed using glTF convention.
+ ///
+ public class Animation
+ {
+ 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;
+ readonly float[] _translationMin;
+ readonly float[] _translationMax;
+ float _translationTimeMin;
+ float _translationTimeMax;
+ 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;
+
+ ///
+ /// 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.
+ ///
+ /// The scale value.
+ /// 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)timeSeconds));
+
+ _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)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 angleDegrees, double timeSeconds)
+ {
+ 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));
+ _rotations.AddRange(BitConverter.GetBytes(rotation.Y));
+ _rotations.AddRange(BitConverter.GetBytes(rotation.Z));
+ _rotations.AddRange(BitConverter.GetBytes(rotation.W));
+
+ _rotationTimes.AddRange(BitConverter.GetBytes((float)timeSeconds));
+
+ _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)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 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)timeSeconds));
+
+ _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)timeSeconds);
+ _translationTimeMax = Math.Max(_translationTimeMax, (float)timeSeconds);
+ }
+
+ ///
+ /// 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/GeometricElement.cs b/Elements/src/GeometricElement.cs
index 06c799ce5..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;
@@ -25,10 +24,16 @@ public class GeometricElement : Element
internal BBox3 _bounds;
internal Csg.Solid _csg;
+ ///
+ /// The element's animation.
+ ///
+ [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;
diff --git a/Elements/src/Serialization/glTF/GltfExtensions.cs b/Elements/src/Serialization/glTF/GltfExtensions.cs
index dea2ff7d7..a93bfd6ff 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;
using System.Reflection;
[assembly: InternalsVisibleTo("Hypar.Elements.Tests")]
@@ -498,7 +500,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
{
@@ -991,6 +1000,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>();
@@ -1028,6 +1038,7 @@ internal static Gltf InitializeGlTF(Model model,
meshTransformMap,
currLines,
drawEdges,
+ animations,
mergeVertices);
}
catch (Exception ex)
@@ -1090,6 +1101,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)
{
@@ -1128,10 +1143,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)
{
@@ -1189,7 +1206,9 @@ private static void GetRenderDataForElement(Element e,
nodes,
materialId,
ref meshId,
- content);
+ content,
+ out nodeId,
+ mergeVertices);
if (!meshElementMap.ContainsKey(e.Id))
{
meshElementMap.Add(e.Id, new List { meshId });
@@ -1210,7 +1229,9 @@ private static void GetRenderDataForElement(Element e,
nodes,
materialId,
ref meshId,
- geometricElement);
+ geometricElement,
+ out nodeId,
+ mergeVertices);
if (meshId > -1 && !meshElementMap.ContainsKey(e.Id))
{
meshElementMap.Add(e.Id, new List { meshId });
@@ -1284,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)
@@ -1316,7 +1492,7 @@ 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);
}
}
}
@@ -1428,11 +1604,15 @@ private static int ProcessGeometricRepresentation(Element e,
List nodes,
string materialId,
ref int meshId,
- GeometricElement geometricElement)
+ GeometricElement geometricElement,
+ out int nodeId,
+ bool mergeVertices = false)
{
geometricElement.UpdateRepresentations();
geometricElement.UpdateBoundsAndComputeSolid();
+ 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
@@ -1462,7 +1642,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 09e5894dd..13e875652 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
{
@@ -52,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);
@@ -67,6 +70,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,
@@ -83,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 };
@@ -95,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 };
@@ -107,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;
@@ -159,18 +205,21 @@ 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();
- node.Mesh = meshId;
+ var node = new Node
+ {
+ Mesh = meshId,
+ };
+
var nodeId = AddNode(nodes, node, parentId);
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
diff --git a/Elements/test/TransformTests.cs b/Elements/test/TransformTests.cs
index 23d9f5d62..caa2ef72e 100644
--- a/Elements/test/TransformTests.cs
+++ b/Elements/test/TransformTests.cs
@@ -18,9 +18,9 @@ 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)), 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)));
// Scale the mass.
m2.Transform.Scale(new Vector3(j, j, j));
@@ -30,12 +30,49 @@ public void Example()
// 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.AddTranslationKeyframe(Vector3.Origin, 0.0);
+ m2.Animation.AddScaleKeyframe(Vector3.Origin, 0.0);
+
+ 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]
public void Transform_OfPoint()
{