Browse Source

Removed outdated monogame project.
Refactored some classes in the Runtime namespace. Possible API breaking changes.

Vicente Penades 5 years ago
parent
commit
d0c02f6c39
29 changed files with 298 additions and 555 deletions
  1. 0 7
      SharpGLTF.sln
  2. 1 1
      build/SharpGLTF.CodeGen/SharpGLTF.CodeGen.csproj
  3. 0 6
      examples/MonoGameScene/App.config
  4. 0 163
      examples/MonoGameScene/Game1.cs
  5. 0 69
      examples/MonoGameScene/ModelDrawContext.cs
  6. BIN
      examples/MonoGameScene/Models/Avocado.glb
  7. BIN
      examples/MonoGameScene/Models/BrainStem.glb
  8. BIN
      examples/MonoGameScene/Models/CesiumMan.glb
  9. BIN
      examples/MonoGameScene/Models/haunted_house.glb
  10. 0 38
      examples/MonoGameScene/MonoGameScene.csproj
  11. 0 14
      examples/MonoGameScene/Program.cs
  12. 1 9
      examples/MonoGameScene/readme.md
  13. 2 2
      examples/SharpGLTF.Plotly/PlotlyToolkit.cs
  14. 1 2
      examples/SharpGLTF.Runtime.MonoGame/MonoGameModelTemplate.cs
  15. 0 51
      src/SharpGLTF.Core/Collections/NamedList.cs
  16. 10 11
      src/SharpGLTF.Core/Runtime/AnimatableProperty.cs
  17. 18 0
      src/SharpGLTF.Core/Runtime/AnimationTrackInfo.cs
  18. 132 0
      src/SharpGLTF.Core/Runtime/ArmatureInstance.cs
  19. 88 0
      src/SharpGLTF.Core/Runtime/ArmatureTemplate.cs
  20. 1 0
      src/SharpGLTF.Core/Runtime/MeshDecoder.Schema2.cs
  21. 13 10
      src/SharpGLTF.Core/Runtime/MeshDecoder.cs
  22. 4 5
      src/SharpGLTF.Core/Runtime/NodeTemplate.cs
  23. 7 113
      src/SharpGLTF.Core/Runtime/SceneInstance.cs
  24. 9 45
      src/SharpGLTF.Core/Runtime/SceneTemplate.cs
  25. 1 1
      src/SharpGLTF.Core/Transforms/MeshTransforms.cs
  26. 4 4
      src/SharpGLTF.Toolkit/Schema2/SceneExtensions.cs
  27. 3 3
      tests/SharpGLTF.Tests/Runtime/SceneTemplateTests.cs
  28. 1 1
      tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadSampleTests.cs
  29. 2 0
      tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadSpecialModelsTest.cs

+ 0 - 7
SharpGLTF.sln

@@ -33,8 +33,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InfiniteSkinnedTentacle", "
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpGLTF.Runtime.MonoGame", "examples\SharpGLTF.Runtime.MonoGame\SharpGLTF.Runtime.MonoGame.csproj", "{6C7B3CD8-21D0-447E-9034-8F72057F2ED7}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGameScene", "examples\MonoGameScene\MonoGameScene.csproj", "{894781CA-F508-43AE-8526-6AA6B6EDF613}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpGLTF.DownloadTestFiles", "tests\SharpGLTF.DownloadTestFiles\SharpGLTF.DownloadTestFiles.csproj", "{7CC20DF6-14B5-4C1C-B4FC-151E97AED4F4}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpGLTF.NUnit", "tests\SharpGLTF.NUnit\SharpGLTF.NUnit.csproj", "{7A5EAF7E-D6A6-4861-9488-F98E4AA00A3A}"
@@ -79,10 +77,6 @@ Global
 		{6C7B3CD8-21D0-447E-9034-8F72057F2ED7}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{6C7B3CD8-21D0-447E-9034-8F72057F2ED7}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{6C7B3CD8-21D0-447E-9034-8F72057F2ED7}.Release|Any CPU.Build.0 = Release|Any CPU
-		{894781CA-F508-43AE-8526-6AA6B6EDF613}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{894781CA-F508-43AE-8526-6AA6B6EDF613}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{894781CA-F508-43AE-8526-6AA6B6EDF613}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{894781CA-F508-43AE-8526-6AA6B6EDF613}.Release|Any CPU.Build.0 = Release|Any CPU
 		{7CC20DF6-14B5-4C1C-B4FC-151E97AED4F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{7CC20DF6-14B5-4C1C-B4FC-151E97AED4F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{7CC20DF6-14B5-4C1C-B4FC-151E97AED4F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -115,7 +109,6 @@ Global
 		{53B7933A-DD1B-4E75-90EC-94E46101C6CC} = {83E7E49D-8A28-45E8-9DBD-1F3AEDEF3E42}
 		{F64C6CC1-BD12-47B8-B6EA-D5609AC738DF} = {83E7E49D-8A28-45E8-9DBD-1F3AEDEF3E42}
 		{6C7B3CD8-21D0-447E-9034-8F72057F2ED7} = {83E7E49D-8A28-45E8-9DBD-1F3AEDEF3E42}
-		{894781CA-F508-43AE-8526-6AA6B6EDF613} = {83E7E49D-8A28-45E8-9DBD-1F3AEDEF3E42}
 		{7CC20DF6-14B5-4C1C-B4FC-151E97AED4F4} = {0CBF510D-D836-40BA-95EC-E93FDBB90632}
 		{7A5EAF7E-D6A6-4861-9488-F98E4AA00A3A} = {0CBF510D-D836-40BA-95EC-E93FDBB90632}
 		{56FE769E-6B09-462B-9947-A9B64161CD80} = {0CBF510D-D836-40BA-95EC-E93FDBB90632}

+ 1 - 1
build/SharpGLTF.CodeGen/SharpGLTF.CodeGen.csproj

@@ -8,7 +8,7 @@
 
   <ItemGroup>
     <PackageReference Include="LibGit2Sharp" Version="0.26.2" />    
-    <PackageReference Include="NJsonSchema.CodeGeneration.CSharp" Version="10.1.26" />
+    <PackageReference Include="NJsonSchema.CodeGeneration.CSharp" Version="10.2.0" />
   </ItemGroup>
 
 </Project>

+ 0 - 6
examples/MonoGameScene/App.config

@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<configuration>
-    <startup> 
-        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.1" />
-    </startup>
-</configuration>

+ 0 - 163
examples/MonoGameScene/Game1.cs

@@ -1,163 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-using Microsoft.Xna.Framework.Input;
-
-namespace MonoGameScene
-{
-    /// <summary>
-    /// This is the main type for your game.
-    /// </summary>
-    public class Game1 : Game
-    {
-        #region lifecycle
-
-        public Game1()
-        {
-            _Graphics = new GraphicsDeviceManager(this);
-
-            Content.RootDirectory = "Content";
-        }
-        
-        protected override void Initialize()
-        {
-            // TODO: Add your initialization logic here
-
-            this.Window.Title = "SharpGLTF - MonoGame Scene";
-            this.Window.AllowUserResizing = true;
-            this.Window.AllowAltF4 = true;
-
-            base.Initialize();
-        }
-
-        protected override void Dispose(bool disposing)
-        {
-            base.Dispose(disposing);
-        }
-
-        #endregion
-
-        #region resources
-
-        private readonly GraphicsDeviceManager _Graphics;
-        
-        // these are the actual hardware resources that represent every model's geometry.
-        
-        SharpGLTF.Runtime.MonoGameDeviceContent<SharpGLTF.Runtime.MonoGameModelTemplate> _AvodadoTemplate;
-        SharpGLTF.Runtime.MonoGameDeviceContent<SharpGLTF.Runtime.MonoGameModelTemplate> _BrainStemTemplate;
-        SharpGLTF.Runtime.MonoGameDeviceContent<SharpGLTF.Runtime.MonoGameModelTemplate> _CesiumManTemplate;
-        SharpGLTF.Runtime.MonoGameDeviceContent<SharpGLTF.Runtime.MonoGameModelTemplate> _HauntedHouseTemplate;
-
-        #endregion
-
-        #region content loading
-        
-        protected override void LoadContent()
-        {
-            _AvodadoTemplate = SharpGLTF.Runtime.MonoGameModelTemplate.LoadDeviceModel(this.GraphicsDevice, "Models\\Avocado.glb");
-            _BrainStemTemplate = SharpGLTF.Runtime.MonoGameModelTemplate.LoadDeviceModel(this.GraphicsDevice, "Models\\BrainStem.glb");
-            _CesiumManTemplate = SharpGLTF.Runtime.MonoGameModelTemplate.LoadDeviceModel(this.GraphicsDevice, "Models\\CesiumMan.glb");
-            _HauntedHouseTemplate = SharpGLTF.Runtime.MonoGameModelTemplate.LoadDeviceModel(this.GraphicsDevice, "Models\\haunted_house.glb");
-        }
-        
-        protected override void UnloadContent()
-        {
-            _AvodadoTemplate?.Dispose();
-            _AvodadoTemplate = null;
-
-            _BrainStemTemplate?.Dispose();
-            _BrainStemTemplate = null;
-
-            _CesiumManTemplate?.Dispose();
-            _CesiumManTemplate = null;
-
-            _HauntedHouseTemplate?.Dispose();
-            _HauntedHouseTemplate = null;
-        }
-
-        #endregion
-
-        #region game loop
-
-        // these are the scene instances we create for every glTF model we want to render on screen.
-        // Instances are designed to be as lightweight as possible, so it should not be a problem to
-        // create as many of them as you need at runtime.
-        private SharpGLTF.Runtime.MonoGameModelInstance _HauntedHouse;
-        private SharpGLTF.Runtime.MonoGameModelInstance _BrainStem;
-        private SharpGLTF.Runtime.MonoGameModelInstance _Avocado;
-        private SharpGLTF.Runtime.MonoGameModelInstance _CesiumMan1;
-        private SharpGLTF.Runtime.MonoGameModelInstance _CesiumMan2;
-        private SharpGLTF.Runtime.MonoGameModelInstance _CesiumMan3;
-        private SharpGLTF.Runtime.MonoGameModelInstance _CesiumMan4;       
-
-
-        protected override void Update(GameTime gameTime)
-        {
-            // For Mobile devices, this logic will close the Game when the Back button is pressed
-            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
-            {
-                Exit();
-            }
-
-            // create as many instances as we need from the templates
-
-            if (_Avocado == null) _Avocado = _AvodadoTemplate.Instance.CreateInstance();
-            if (_HauntedHouse == null) _HauntedHouse = _HauntedHouseTemplate.Instance.CreateInstance();
-            if (_BrainStem == null) _BrainStem = _BrainStemTemplate.Instance.CreateInstance();
-
-            if (_CesiumMan1 == null) _CesiumMan1 = _CesiumManTemplate.Instance.CreateInstance();
-            if (_CesiumMan2 == null) _CesiumMan2 = _CesiumManTemplate.Instance.CreateInstance();
-            if (_CesiumMan3 == null) _CesiumMan3 = _CesiumManTemplate.Instance.CreateInstance();
-            if (_CesiumMan4 == null) _CesiumMan4 = _CesiumManTemplate.Instance.CreateInstance();
-
-            // animate each instance individually.
-
-            var animTime = (float)gameTime.TotalGameTime.TotalSeconds;
-
-            _BrainStem.Controller.SetAnimationFrame(0, 0.7f* animTime);
-            _CesiumMan1.Controller.SetAnimationFrame(0, 0.3f);
-            _CesiumMan2.Controller.SetAnimationFrame(0, 0.5f * animTime);
-            _CesiumMan3.Controller.SetAnimationFrame(0, 1.0f * animTime);
-            _CesiumMan4.Controller.SetAnimationFrame(0, 1.5f * animTime);
-
-            base.Update(gameTime);
-        }        
-
-        
-        protected override void Draw(GameTime gameTime)
-        {
-            GraphicsDevice.Clear(Color.DarkSlateGray);
-
-            base.Draw(gameTime);
-
-            var animTime = (float)gameTime.TotalGameTime.TotalSeconds;
-
-            var lookAt = new Vector3(0, 2, 0);
-            var camPos = new Vector3((float)Math.Sin(animTime*0.5f) * 2, 2, 12);
-
-            var camera = Matrix.CreateWorld(camPos, lookAt - camPos, Vector3.UnitY);                      
-
-            // draw all the instances.
-
-            var ctx = new ModelDrawContext(_Graphics, camera);
-
-            ctx.DrawModelInstance(_Avocado, Matrix.CreateScale(30) * Matrix.CreateRotationY(animTime*0.3f) * Matrix.CreateTranslation(-5,5,-5));
-
-            ctx.DrawModelInstance(_HauntedHouse, Matrix.CreateScale(20) * Matrix.CreateRotationY(1));
-
-            ctx.DrawModelInstance(_BrainStem, Matrix.CreateTranslation(0,0.5f,8));
-
-            ctx.DrawModelInstance(_CesiumMan1, Matrix.CreateTranslation(-3, 0, 5));
-            ctx.DrawModelInstance(_CesiumMan2, Matrix.CreateTranslation(-2, 0, 5));
-            ctx.DrawModelInstance(_CesiumMan3, Matrix.CreateTranslation( 2, 0, 5));
-            ctx.DrawModelInstance(_CesiumMan4, Matrix.CreateTranslation( 3, 0, 5));
-        }
-        
-        #endregion
-    }
-}

+ 0 - 69
examples/MonoGameScene/ModelDrawContext.cs

@@ -1,69 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-using SharpGLTF.Runtime;
-
-namespace MonoGameScene
-{
-    /// <summary>
-    /// Small helper for rendering MonoGame models.
-    /// </summary>
-    class ModelDrawContext
-    {
-        #region lifecycle
-
-        public ModelDrawContext(GraphicsDeviceManager graphics, Matrix cameraMatrix)
-        {
-            _Device = graphics.GraphicsDevice;
-
-            _Device.DepthStencilState = DepthStencilState.Default;
-
-            _View = Matrix.Invert(cameraMatrix);            
-            
-            float fieldOfView = MathHelper.PiOver4;            
-            float nearClipPlane = 0.01f;            
-            float farClipPlane = 1000;
-
-            _Projection = Matrix.CreatePerspectiveFieldOfView(fieldOfView, graphics.GraphicsDevice.Viewport.AspectRatio, nearClipPlane, farClipPlane);            
-        }
-
-        #endregion
-
-        #region data
-
-        private GraphicsDevice _Device;
-        private Matrix _Projection;
-        private Matrix _View;        
-
-        #endregion        
-
-        #region API
-
-        public void DrawModelInstance(MonoGameModelInstance model, Matrix world)
-        {
-            foreach (var e in model.Template.Effects) UpdateMaterial(e);
-
-            model.Draw(_Projection, _View, world);
-        }
-
-        public static void UpdateMaterial(Effect effect)
-        {
-            if (effect is IEffectLights lights)
-            {
-                lights.EnableDefaultLighting();
-            }
-
-            if (effect is IEffectFog fog)
-            {
-                fog.FogEnabled = false;
-            }
-        }
-
-        #endregion
-    }
-}

BIN
examples/MonoGameScene/Models/Avocado.glb


BIN
examples/MonoGameScene/Models/BrainStem.glb


BIN
examples/MonoGameScene/Models/CesiumMan.glb


BIN
examples/MonoGameScene/Models/haunted_house.glb


+ 0 - 38
examples/MonoGameScene/MonoGameScene.csproj

@@ -1,38 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <TargetFramework>net471</TargetFramework>
-    <OutputType>WinExe</OutputType>
-    <LangVersion>7.1</LangVersion>    
-    <MonoGamePlatform>Windows</MonoGamePlatform>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.0.1641" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\SharpGLTF.Runtime.Monogame\SharpGLTF.Runtime.MonoGame.csproj" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Reference Include="System.Windows.Forms" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <None Update="Models\Avocado.glb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Update="Models\BrainStem.glb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Update="Models\CesiumMan.glb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-    <None Update="Models\haunted_house.glb">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None>
-  </ItemGroup>
-
-
-</Project>

+ 0 - 14
examples/MonoGameScene/Program.cs

@@ -1,14 +0,0 @@
-using System;
-using System.Linq;
-
-namespace MonoGameScene
-{
-    class Program
-    {
-        [STAThread]
-        static void Main()
-        {
-            using (var game = new Game1()) game.Run();
-        }
-    }
-}

+ 1 - 9
examples/MonoGameScene/readme.md

@@ -2,12 +2,4 @@
 
 ![MonoGame Demo](../MonoGameDemo.jpg)
 
-Notes on the demo:
-
-MonoGame typically preprocesses all graphics resources through its content pipeline, and all assets are converted to XNB files, which is what the runtime is able to load.
-
-This is not the case of this demo; the glTF files are loaded at runtime without any preprocessing.
-
-Also for simplicity, the demo uses the default in-built BasicEffect and SkinnedEffect shaders, which date from the years of DirectX9, so PBR rendering is not supported.
-
-The project depends on [SharpGLTF.Runtime.MonoGame](../SharpGLTF.Runtime.MonoGame), which also depends on [SharpGLTF.Core](../../src/SharpGLTF.Core), that's everything you need to load and render glTF models into MonoGame.
+Example and project development has moved to https://github.com/vpenades/SharpGLTF.Monogame.Example

+ 2 - 2
examples/SharpGLTF.Plotly/PlotlyToolkit.cs

@@ -22,8 +22,8 @@ namespace SharpGLTF
                 .CreateInstance();
 
             // set the node animations for our scene instance-
-            if (animation == null) { sceneInstance.SetPoseTransforms(); }
-            else { sceneInstance.SetAnimationFrame(animation.LogicalIndex, time); }
+            if (animation == null) { sceneInstance.Armature.SetPoseTransforms(); }
+            else { sceneInstance.Armature.SetAnimationFrame(animation.LogicalIndex, time); }
 
             // keep source meshes.
             var meshes = srcScene.LogicalParent.LogicalMeshes;

+ 1 - 2
examples/SharpGLTF.Runtime.MonoGame/MonoGameModelTemplate.cs

@@ -115,8 +115,7 @@ namespace SharpGLTF.Runtime
 
         private BoundingSphere CalculateBounds(SceneTemplate scene)
         {
-            var instance = scene.CreateInstance();
-            instance.SetPoseTransforms();
+            var instance = scene.CreateInstance();            
 
             var bounds = default(BoundingSphere);
 

+ 0 - 51
src/SharpGLTF.Core/Collections/NamedList.cs

@@ -1,51 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace SharpGLTF.Collections
-{
-    /// <summary>
-    /// Stores values by index, and optionally, by name.
-    /// </summary>
-    /// <typeparam name="T">Any type.</typeparam>
-    class NamedList<T> : List<T>
-    {
-        private Dictionary<string, int> _ByName;
-        private List<string> _ByIndex;
-
-        public IReadOnlyCollection<String> Names => _ByName != null ? _ByName.Keys : (IReadOnlyCollection<String>)Array.Empty<string>();
-
-        public void SetName(int index, string name, T value)
-        {
-            Guard.MustBeGreaterThanOrEqualTo(index, 0, nameof(index));
-
-            if (!string.IsNullOrEmpty(name))
-            {
-                if (_ByName == null) _ByName = new Dictionary<string, int>();
-                if (_ByIndex == null) _ByIndex = new List<string>();
-                while (_ByIndex.Count <= index) _ByIndex.Add(null);
-
-                _ByName[name] = index;
-                _ByIndex[index] = name;
-            }
-
-            while (this.Count <= index) this.Add(default);
-
-            this[index] = value;
-        }
-
-        public int IndexOf(string name)
-        {
-            if (_ByName == null) return default;
-            if (string.IsNullOrEmpty(name)) return default;
-            return _ByName.TryGetValue(name, out int index) ? index : -1;
-        }
-
-        public string NameOf(int index)
-        {
-            if (_ByIndex == null) return null;
-            if (index < 0 || index >= _ByIndex.Count) return null;
-            return _ByIndex[index];
-        }
-    }
-}

+ 10 - 11
src/SharpGLTF.Core/Runtime/AnimatableProperty.cs

@@ -26,7 +26,7 @@ namespace SharpGLTF.Runtime
 
         #region data
 
-        private Collections.NamedList<ICurveSampler<T>> _Animations;
+        private List<ICurveSampler<T>> _Curves;
 
         /// <summary>
         /// Gets the default value of this instance.
@@ -38,9 +38,7 @@ namespace SharpGLTF.Runtime
 
         #region properties
 
-        public bool IsAnimated => _Animations == null ? false : _Animations.Count > 0;
-
-        public IReadOnlyCollection<string> Tracks => _Animations?.Names;
+        public bool IsAnimated => _Curves == null ? false : _Curves.Count > 0;
 
         #endregion
 
@@ -54,21 +52,22 @@ namespace SharpGLTF.Runtime
         /// <returns>The evaluated value taken from the animation <paramref name="trackLogicalIndex"/>, or <see cref="Value"/> if a track was not found.</returns>
         public T GetValueAt(int trackLogicalIndex, float offset)
         {
-            if (_Animations == null) return this.Value;
+            if (_Curves == null) return this.Value;
 
-            if (trackLogicalIndex < 0 || trackLogicalIndex >= _Animations.Count) return this.Value;
+            if (trackLogicalIndex < 0 || trackLogicalIndex >= _Curves.Count) return this.Value;
 
-            return _Animations[trackLogicalIndex]?.GetPoint(offset) ?? this.Value;
+            return _Curves[trackLogicalIndex]?.GetPoint(offset) ?? this.Value;
         }
 
-        public void AddCurve(int logicalIndex, string name, ICurveSampler<T> sampler)
+        public void SetCurve(int logicalIndex, ICurveSampler<T> curveSampler)
         {
-            Guard.NotNull(sampler, nameof(sampler));
+            Guard.NotNull(curveSampler, nameof(curveSampler));
             Guard.MustBeGreaterThanOrEqualTo(logicalIndex, 0, nameof(logicalIndex));
 
-            if (_Animations == null) _Animations = new Collections.NamedList<ICurveSampler<T>>();
+            if (_Curves == null) _Curves = new List<ICurveSampler<T>>();
+            while (_Curves.Count <= logicalIndex) _Curves.Add(null);
 
-            _Animations.SetName(logicalIndex, name, sampler);
+            _Curves[logicalIndex] = curveSampler;
         }
 
         #endregion

+ 18 - 0
src/SharpGLTF.Core/Runtime/AnimationTrackInfo.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SharpGLTF.Runtime
+{
+    public class AnimationTrackInfo
+    {
+        public AnimationTrackInfo(string name, float duration)
+        {
+            Name = name;
+            Duration = duration;
+        }
+
+        public string Name { get; private set; }
+        public float Duration { get; private set; }
+    }
+}

+ 132 - 0
src/SharpGLTF.Core/Runtime/ArmatureInstance.cs

@@ -0,0 +1,132 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using SharpGLTF.Transforms;
+
+using XFORM = System.Numerics.Matrix4x4;
+
+namespace SharpGLTF.Runtime
+{
+    public class ArmatureInstance
+    {
+        #region constructor
+
+        internal ArmatureInstance(ArmatureTemplate template)
+        {
+            _AnimationTracks = template.Tracks;
+
+            _NodeTemplates = template.Nodes;
+            _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(template.Nodes));
+
+                var p = pidx < 0 ? null : _NodeInstances[pidx];
+
+                _NodeInstances[i] = new NodeInstance(n, p);
+            }
+        }
+
+        #endregion
+
+        #region data
+
+        private readonly NodeTemplate[] _NodeTemplates;
+        private readonly NodeInstance[] _NodeInstances;
+
+        private readonly AnimationTrackInfo[] _AnimationTracks;
+
+        #endregion
+
+        #region properties
+
+        /// <summary>
+        /// Gets a flattened collection of all the nodes of this armature.
+        /// </summary>
+        public IReadOnlyList<NodeInstance> LogicalNodes => _NodeInstances;
+
+        /// <summary>
+        /// Gets all the <see cref="NodeInstance"/> roots used by this <see cref="SceneInstance"/>.
+        /// </summary>
+        public IEnumerable<NodeInstance> VisualNodes => _NodeInstances.Where(item => item.VisualParent == null);
+
+        /// <summary>
+        /// Gets the total number of animation tracks for this instance.
+        /// </summary>
+        public IReadOnlyList<AnimationTrackInfo> AnimationTracks => _AnimationTracks;
+
+        #endregion
+
+        #region API
+
+        public int IndexOfTrack(string name)
+        {
+            return Array.FindIndex(_AnimationTracks, item => item.Name == name);
+        }
+
+        public void SetLocalMatrix(string name, XFORM localMatrix)
+        {
+            var n = LogicalNodes.FirstOrDefault(item => item.Name == name);
+            if (n == null) return;
+            n.LocalMatrix = localMatrix;
+        }
+
+        public void SetModelMatrix(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 void SetAnimationFrame(int trackLogicalIndex, float time, bool looped = true)
+        {
+            if (looped)
+            {
+                var duration = AnimationTracks[trackLogicalIndex].Duration;
+                if (duration > 0) time %= duration;
+            }
+
+            foreach (var n in _NodeInstances) n.SetAnimationFrame(trackLogicalIndex, time);
+        }
+
+        public void SetAnimationFrame(params (int TrackIdx, float Time, float Weight)[] blended)
+        {
+            SetAnimationFrame(_NodeInstances, blended);
+        }
+
+        public static void SetAnimationFrame(IEnumerable<NodeInstance> nodes, params (int TrackIdx, float Time, float Weight)[] blended)
+        {
+            Guard.NotNull(nodes, nameof(nodes));
+
+            Span<int> tracks = stackalloc int[blended.Length];
+            Span<float> times = stackalloc float[blended.Length];
+            Span<float> 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);
+        }
+
+        #endregion
+    }
+}

+ 88 - 0
src/SharpGLTF.Core/Runtime/ArmatureTemplate.cs

@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace SharpGLTF.Runtime
+{
+    class ArmatureTemplate
+    {
+        #region lifecycle
+
+        /// <summary>
+        /// Creates a new <see cref="ArmatureTemplate"/> from a given <see cref="Schema2.Scene"/>.
+        /// </summary>
+        /// <param name="srcScene">The source <see cref="Schema2.Scene"/> to templatize.</param>
+        /// <param name="isolateMemory">True if we want to copy data instead of sharing it.</param>
+        /// <returns>A new <see cref="ArmatureTemplate"/> instance.</returns>
+        public static ArmatureTemplate Create(Schema2.Scene srcScene, bool isolateMemory)
+        {
+            Guard.NotNull(srcScene, nameof(srcScene));
+
+            // gather scene nodes.
+
+            var srcNodes = Schema2.Node.Flatten(srcScene)
+                .Select((key, idx) => (key, idx))
+                .ToDictionary(pair => pair.key, pair => pair.idx);
+
+            int indexSolver(Schema2.Node srcNode)
+            {
+                if (srcNode == null) return -1;
+                return srcNodes[srcNode];
+            }
+
+            // create bones.
+
+            var dstNodes = new NodeTemplate[srcNodes.Count];
+
+            foreach (var srcNode in srcNodes)
+            {
+                var nidx = srcNode.Value;
+
+                // parent index
+                var pidx = indexSolver(srcNode.Key.VisualParent);
+                if (pidx >= nidx) throw new InvalidOperationException("parent indices should be below child indices");
+
+                // child indices
+                var cidx = srcNode.Key.VisualChildren
+                    .Select(n => indexSolver(n))
+                    .ToArray();
+
+                dstNodes[nidx] = new NodeTemplate(srcNode.Key, pidx, cidx, isolateMemory);
+            }
+
+            // gather animation durations.
+
+            var dstTracks = srcScene.LogicalParent
+                .LogicalAnimations
+                .Select(item => new AnimationTrackInfo(item.Name, item.Duration))
+                .ToArray();
+
+            return new ArmatureTemplate(dstNodes, dstTracks);
+        }
+
+        private ArmatureTemplate(NodeTemplate[] nodes, AnimationTrackInfo[] animTracks)
+        {
+            _NodeTemplates = nodes;
+            _AnimationTracks = animTracks;
+        }
+
+        #endregion
+
+        #region data
+
+        private readonly NodeTemplate[] _NodeTemplates;
+        private readonly AnimationTrackInfo[] _AnimationTracks;
+
+        #endregion
+
+        #region properties
+
+        public NodeTemplate[] Nodes => _NodeTemplates;
+
+        public AnimationTrackInfo[] Tracks => _AnimationTracks;
+
+        #endregion
+
+    }
+}

+ 1 - 0
src/SharpGLTF.Core/Runtime/MeshDecoder.Schema2.cs

@@ -52,6 +52,7 @@ namespace SharpGLTF.Runtime
         public string Name => _Name;
         public int LogicalIndex => _LogicalIndex;
         public IReadOnlyList<IMeshPrimitiveDecoder<TMaterial>> Primitives => _Primitives;
+        public Object Extras => _Extras;
 
         #endregion
 

+ 13 - 10
src/SharpGLTF.Core/Runtime/MeshDecoder.cs

@@ -15,6 +15,7 @@ namespace SharpGLTF.Runtime
         string Name { get; }
         int LogicalIndex { get; }
         IReadOnlyList<IMeshPrimitiveDecoder<TMaterial>> Primitives { get; }
+        Object Extras { get; }
     }
 
     public interface IMeshPrimitiveDecoder
@@ -158,23 +159,24 @@ namespace SharpGLTF.Runtime
             var decodedMeshes = scene.LogicalParent.LogicalMeshes.Decode();
             var sceneTemplate = SceneTemplate.Create(scene, false);
             var sceneInstance = sceneTemplate.CreateInstance();
+            var armatureInst = sceneInstance.Armature;
 
-            if (sceneInstance.AnimationTracksCount == 0)
+            if (armatureInst.AnimationTracks.Count == 0)
             {
-                sceneInstance.SetPoseTransforms();
+                armatureInst.SetPoseTransforms();
                 return sceneInstance.EvaluateBoundingBox(decodedMeshes);
             }
 
             var min = new XYZ(float.PositiveInfinity);
             var max = new XYZ(float.NegativeInfinity);
 
-            for (int trackIdx = 0; trackIdx < sceneInstance.AnimationTracksCount; ++trackIdx)
+            for (int trackIdx = 0; trackIdx < armatureInst.AnimationTracks.Count; ++trackIdx)
             {
-                var duration = sceneInstance.GetAnimationDuration(trackIdx);
+                var duration = armatureInst.AnimationTracks[trackIdx].Duration;
 
                 for (float time = 0; time < duration; time += samplingTimeStep)
                 {
-                    sceneInstance.SetAnimationFrame(trackIdx, time);
+                    armatureInst.SetAnimationFrame(trackIdx, time);
                     var (fMin, fMax) = sceneInstance.EvaluateBoundingBox(decodedMeshes);
 
                     min = XYZ.Min(min, fMin);
@@ -192,23 +194,24 @@ namespace SharpGLTF.Runtime
             var decodedMeshes = scene.LogicalParent.LogicalMeshes.Decode();
             var sceneTemplate = SceneTemplate.Create(scene, false);
             var sceneInstance = sceneTemplate.CreateInstance();
+            var armatureInst = sceneInstance.Armature;
 
-            if (sceneInstance.AnimationTracksCount == 0)
+            if (armatureInst.AnimationTracks.Count == 0)
             {
-                sceneInstance.SetPoseTransforms();
+                armatureInst.SetPoseTransforms();
                 return sceneInstance.EvaluateBoundingSphere(decodedMeshes);
             }
 
             var center = XYZ.Zero;
             float radius = -1f;
 
-            for (int trackIdx = 0; trackIdx < sceneInstance.AnimationTracksCount; ++trackIdx)
+            for (int trackIdx = 0; trackIdx < armatureInst.AnimationTracks.Count; ++trackIdx)
             {
-                var duration = sceneInstance.GetAnimationDuration(trackIdx);
+                var duration = armatureInst.AnimationTracks[trackIdx].Duration;
 
                 for (float time = 0; time < duration; time += samplingTimeStep)
                 {
-                    sceneInstance.SetAnimationFrame(trackIdx, time);
+                    armatureInst.SetAnimationFrame(trackIdx, time);
                     var (fc, fr) = sceneInstance.EvaluateBoundingSphere(decodedMeshes);
 
                     _MergeSphere(ref center, ref radius, fc, fr);

+ 4 - 5
src/SharpGLTF.Core/Runtime/NodeTemplate.cs

@@ -35,19 +35,18 @@ namespace SharpGLTF.Runtime
             foreach (var anim in srcNode.LogicalParent.LogicalAnimations)
             {
                 var index = anim.LogicalIndex;
-                var name = anim.Name;
 
                 var scaAnim = anim.FindScaleSampler(srcNode)?.CreateCurveSampler(isolateMemory);
-                if (scaAnim != null) _Scale.AddCurve(index, name, scaAnim);
+                if (scaAnim != null) _Scale.SetCurve(index, scaAnim);
 
                 var rotAnim = anim.FindRotationSampler(srcNode)?.CreateCurveSampler(isolateMemory);
-                if (rotAnim != null) _Rotation.AddCurve(index, name, rotAnim);
+                if (rotAnim != null) _Rotation.SetCurve(index, rotAnim);
 
                 var traAnim = anim.FindTranslationSampler(srcNode)?.CreateCurveSampler(isolateMemory);
-                if (traAnim != null) _Translation.AddCurve(index, name, traAnim);
+                if (traAnim != null) _Translation.SetCurve(index, traAnim);
 
                 var mrpAnim = anim.FindSparseMorphSampler(srcNode)?.CreateCurveSampler(isolateMemory);
-                if (mrpAnim != null) _Morphing.AddCurve(index, name, mrpAnim);
+                if (mrpAnim != null) _Morphing.SetCurve(index, mrpAnim);
             }
 
             _UseAnimatedTransforms = _Scale.IsAnimated | _Rotation.IsAnimated | _Translation.IsAnimated;

+ 7 - 113
src/SharpGLTF.Core/Runtime/SceneInstance.cs

@@ -17,24 +17,9 @@ namespace SharpGLTF.Runtime
     {
         #region lifecycle
 
-        internal SceneInstance(NodeTemplate[] nodeTemplates, DrawableTemplate[] drawables, Collections.NamedList<float> tracks)
+        internal SceneInstance(ArmatureTemplate armature, DrawableTemplate[] drawables)
         {
-            _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);
-            }
+            _Armature = new ArmatureInstance(armature);
 
             _DrawableReferences = drawables;
             _DrawableTransforms = new IGeometryTransform[_DrawableReferences.Length];
@@ -49,32 +34,16 @@ namespace SharpGLTF.Runtime
 
         #region data
 
-        private readonly NodeTemplate[] _NodeTemplates;
-        private readonly NodeInstance[] _NodeInstances;
+        private readonly ArmatureInstance _Armature;
 
         private readonly DrawableTemplate[] _DrawableReferences;
         private readonly IGeometryTransform[] _DrawableTransforms;
 
-        private readonly Collections.NamedList<float> _AnimationTracks;
-
         #endregion
 
         #region properties
 
-        /// <summary>
-        /// Gets a list of all the <see cref="NodeInstance"/> nodes used by this <see cref="SceneInstance"/>.
-        /// </summary>
-        public IReadOnlyList<NodeInstance> LogicalNodes => _NodeInstances;
-
-        /// <summary>
-        /// Gets all the <see cref="NodeInstance"/> roots used by this <see cref="SceneInstance"/>.
-        /// </summary>
-        public IEnumerable<NodeInstance> VisualNodes => _NodeInstances.Where(item => item.VisualParent == null);
-
-        /// <summary>
-        /// Gets the total number of animation tracks for this instance.
-        /// </summary>
-        public int AnimationTracksCount => _AnimationTracks.Count;
+        public ArmatureInstance Armature => _Armature;
 
         /// <summary>
         /// Gets the number of drawable instances.
@@ -99,94 +68,19 @@ namespace SharpGLTF.Runtime
 
         #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 string NameOfTrack(int trackIndex)
-        {
-            return _AnimationTracks.NameOf(trackIndex);
-        }
-
-        public int IndexOfTrack(string name)
-        {
-            return _AnimationTracks.IndexOf(name);
-        }
-
-        public float GetAnimationDuration(int trackLogicalIndex)
-        {
-            if (trackLogicalIndex < 0) return 0;
-            if (trackLogicalIndex >= _AnimationTracks.Count) return 0;
-
-            return _AnimationTracks[trackLogicalIndex];
-        }
-
-        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(params (int TrackIdx, float Time, float Weight)[] blended)
-        {
-            SetAnimationFrame(_NodeInstances, blended);
-        }
-
-        public static void SetAnimationFrame(IEnumerable<NodeInstance> nodes, params (int TrackIdx, float Time, float Weight)[] blended)
-        {
-            Guard.NotNull(nodes, nameof(nodes));
-
-            Span<int> tracks = stackalloc int[blended.Length];
-            Span<float> times = stackalloc float[blended.Length];
-            Span<float> 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);
-        }
-
         /// <summary>
         /// Gets a drawable reference pair, where:
         /// - MeshIndex is the logical Index of a <see cref="Schema2.Mesh"/> in <see cref="Schema2.ModelRoot.LogicalMeshes"/>.
         /// - Transform is an <see cref="IGeometryTransform"/> that can be used to transform the <see cref="Schema2.Mesh"/> into world space.
         /// </summary>
-        /// <param name="index">The index of the drawable reference, from 0 to <see cref="DrawableReferencesCount"/></param>
+        /// <param name="index">The index of the drawable reference, from 0 to <see cref="DrawableInstancesCount"/></param>
         /// <returns>A drawable reference</returns>
         [Obsolete("Use GetDrawableInstance")]
         public (int MeshIndex, IGeometryTransform Transform) GetDrawableReference(int index)
         {
             var dref = _DrawableReferences[index];
 
-            dref.UpdateGeometryTransform(_DrawableTransforms[index], _NodeInstances);
+            dref.UpdateGeometryTransform(_DrawableTransforms[index], _Armature.LogicalNodes);
 
             return (dref.LogicalMeshIndex, _DrawableTransforms[index]);
         }
@@ -203,7 +97,7 @@ namespace SharpGLTF.Runtime
         {
             var dref = _DrawableReferences[index];
 
-            dref.UpdateGeometryTransform(_DrawableTransforms[index], _NodeInstances);
+            dref.UpdateGeometryTransform(_DrawableTransforms[index], _Armature.LogicalNodes);
 
             return new DrawableInstance(dref, _DrawableTransforms[index]);
         }

+ 9 - 45
src/SharpGLTF.Core/Runtime/SceneTemplate.cs

@@ -25,6 +25,8 @@ namespace SharpGLTF.Runtime
         {
             Guard.NotNull(srcScene, nameof(srcScene));
 
+            var armature = ArmatureTemplate.Create(srcScene, isolateMemory);
+
             // gather scene nodes.
 
             var srcNodes = Schema2.Node.Flatten(srcScene)
@@ -37,26 +39,6 @@ namespace SharpGLTF.Runtime
                 return srcNodes[srcNode];
             }
 
-            // create bones.
-
-            var dstNodes = new NodeTemplate[srcNodes.Count];
-
-            foreach (var srcNode in srcNodes)
-            {
-                var nidx = srcNode.Value;
-
-                // parent index
-                var pidx = indexSolver(srcNode.Key.VisualParent);
-                if (pidx >= nidx) throw new InvalidOperationException("parent indices should be below child indices");
-
-                // child indices
-                var cidx = srcNode.Key.VisualChildren
-                    .Select(n => indexSolver(n))
-                    .ToArray();
-
-                dstNodes[nidx] = new NodeTemplate(srcNode.Key, pidx, cidx, isolateMemory);
-            }
-
             // create drawables.
 
             var instances = srcNodes.Keys
@@ -71,34 +53,19 @@ namespace SharpGLTF.Runtime
 
                 drawables[i] = srcInstance.Skin != null
                     ?
-                    (DrawableTemplate)new SkinnedDrawableTemplate(srcInstance, indexSolver)
+                    new SkinnedDrawableTemplate(srcInstance, indexSolver)
                     :
                     (DrawableTemplate)new RigidDrawableTemplate(srcInstance, indexSolver);
             }
 
-            // gather animation durations.
-
-            var dstTracks = new Collections.NamedList<float>();
-
-            foreach (var anim in srcScene.LogicalParent.LogicalAnimations)
-            {
-                var index = anim.LogicalIndex;
-                var name = anim.Name;
-
-                float duration = dstTracks.Count <= index ? 0 : dstTracks[index];
-                duration = Math.Max(duration, anim.Duration);
-                dstTracks.SetName(index, name, anim.Duration);
-            }
-
-            return new SceneTemplate(srcScene.Name, dstNodes, drawables, dstTracks);
+            return new SceneTemplate(srcScene.Name, armature, drawables);
         }
 
-        private SceneTemplate(string name, NodeTemplate[] nodes, DrawableTemplate[] drawables, Collections.NamedList<float> animTracks)
+        private SceneTemplate(string name, ArmatureTemplate armature, DrawableTemplate[] drawables)
         {
             _Name = name;
-            _NodeTemplates = nodes;
+            _Armature = armature;
             _DrawableReferences = drawables;
-            _AnimationTracks = animTracks;
         }
 
         #endregion
@@ -106,12 +73,9 @@ namespace SharpGLTF.Runtime
         #region data
 
         private readonly String _Name;
-
-        private readonly NodeTemplate[] _NodeTemplates;
+        private readonly ArmatureTemplate _Armature;
         private readonly DrawableTemplate[] _DrawableReferences;
 
-        private readonly Collections.NamedList<float> _AnimationTracks;
-
         #endregion
 
         #region properties
@@ -134,9 +98,9 @@ namespace SharpGLTF.Runtime
         /// <returns>A new <see cref="SceneInstance"/> object.</returns>
         public SceneInstance CreateInstance()
         {
-            var inst = new SceneInstance(_NodeTemplates, _DrawableReferences, _AnimationTracks);
+            var inst = new SceneInstance(_Armature, _DrawableReferences);
 
-            inst.SetPoseTransforms();
+            inst.Armature.SetPoseTransforms();
 
             return inst;
         }

+ 1 - 1
src/SharpGLTF.Core/Transforms/MeshTransforms.cs

@@ -227,7 +227,7 @@ namespace SharpGLTF.Transforms
 
         public Boolean FlipFaces => _FlipFaces;
 
-        public Matrix4x4 WorldMatrix => _WorldMatrix;
+        public TRANSFORM WorldMatrix => _WorldMatrix;
 
         #endregion
 

+ 4 - 4
src/SharpGLTF.Toolkit/Schema2/SceneExtensions.cs

@@ -219,11 +219,11 @@ namespace SharpGLTF.Schema2
 
             if (animation == null)
             {
-                instance.SetPoseTransforms();
+                instance.Armature.SetPoseTransforms();
             }
             else
             {
-                instance.SetAnimationFrame(animation.LogicalIndex, time);
+                instance.Armature.SetAnimationFrame(animation.LogicalIndex, time);
             }
 
             var meshes = scene.LogicalParent.LogicalMeshes;
@@ -255,11 +255,11 @@ namespace SharpGLTF.Schema2
 
             if (animation == null)
             {
-                instance.SetPoseTransforms();
+                instance.Armature.SetPoseTransforms();
             }
             else
             {
-                instance.SetAnimationFrame(animation.LogicalIndex, time);
+                instance.Armature.SetAnimationFrame(animation.LogicalIndex, time);
             }
 
             var meshes = scene.LogicalParent.LogicalMeshes;

+ 3 - 3
tests/SharpGLTF.Tests/Runtime/SceneTemplateTests.cs

@@ -68,8 +68,8 @@ namespace SharpGLTF.Runtime
             var sceneTemplate = SceneTemplate.Create(scene, false);
             var sceneInstance = sceneTemplate.CreateInstance();
 
-            var duration = sceneInstance.GetAnimationDuration(0);
-            sceneInstance.SetAnimationFrame(0, duration/2);
+            var duration = sceneInstance.Armature.AnimationTracks[0].Duration;
+            sceneInstance.Armature.SetAnimationFrame(0, duration/2);
 
             IEnumerable<(Vector3,Vector3,Vector3, int)> evaluateTriangles(DrawableInstance inst)
             {
@@ -110,7 +110,7 @@ namespace SharpGLTF.Runtime
             
             var sceneTemplate = SceneTemplate.Create(model.DefaultScene, false);
             var sceneInstance = sceneTemplate.CreateInstance();
-            sceneInstance.SetAnimationFrame(0, 0.1f);
+            sceneInstance.Armature.SetAnimationFrame(0, 0.1f);
 
             var vertices = sceneInstance.GetWorldVertices(model.LogicalMeshes.Decode()).ToList();
 

+ 1 - 1
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadSampleTests.cs

@@ -330,7 +330,7 @@ namespace SharpGLTF.Schema2.LoadAndSave
 
             for (float t = 0; t < 5; t+=0.25f)
             {
-                instance.SetAnimationFrame(anim.LogicalIndex, t);
+                instance.Armature.SetAnimationFrame(anim.LogicalIndex, t);
 
                 var nodexform = instance.GetDrawableInstance(0).Transform;
 

+ 2 - 0
tests/SharpGLTF.Tests/Schema2/LoadAndSave/LoadSpecialModelsTest.cs

@@ -145,6 +145,8 @@ namespace SharpGLTF.Schema2.LoadAndSave
 
             var model = ModelRoot.Load(path);
 
+            var boundingSphere = Runtime.MeshDecoder.EvaluateBoundingSphere(model.DefaultScene);
+
             var channel = model.LogicalAnimations[1].FindRotationSampler(model.LogicalNodes[5]);
 
             var node5_R_00 = channel.CreateCurveSampler(true).GetPoint(0);