Browse Source

Improved SceneBuilder.
Updated Nugets.

Vicente Penades 6 years ago
parent
commit
fabaa05f84

+ 2 - 2
Analyzers.targets

@@ -10,8 +10,8 @@
   </ItemGroup>
 
   <ItemGroup>    
-    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" PrivateAssets="all" />
-    <PackageReference Include="Microsoft.CodeQuality.Analyzers" Version="2.9.4" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.6" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeQuality.Analyzers" Version="2.9.6" PrivateAssets="all" />
     <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" />
   </ItemGroup>
 	

+ 1 - 0
SharpGLTF.sln

@@ -5,6 +5,7 @@ VisualStudioVersion = 15.0.28307.329
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{29566B60-311D-42A0-9E8D-C48DECDD587F}"
 	ProjectSection(SolutionItems) = preProject
+		Analyzers.targets = Analyzers.targets
 		README.md = README.md
 		SharpGLTF.ruleset = SharpGLTF.ruleset
 	EndProjectSection

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

@@ -8,8 +8,8 @@
 
   <ItemGroup>
     <PackageReference Include="LibGit2Sharp" Version="0.26.1" />
-    <PackageReference Include="NJsonSchema.CodeGeneration" Version="10.0.23" />
-    <PackageReference Include="NJsonSchema.CodeGeneration.CSharp" Version="10.0.23" />
+    <PackageReference Include="NJsonSchema.CodeGeneration" Version="10.0.24" />
+    <PackageReference Include="NJsonSchema.CodeGeneration.CSharp" Version="10.0.24" />
   </ItemGroup>
 
 </Project>

+ 18 - 6
examples/SharpGLTF.Runtime.MonoGame/MonoGameModelInstance.cs

@@ -7,14 +7,14 @@ using Microsoft.Xna.Framework.Graphics;
 
 namespace SharpGLTF.Runtime
 {
-    public class MonoGameModelInstance
+    public sealed class MonoGameModelInstance
     {
         #region lifecycle
 
-        internal MonoGameModelInstance(MonoGameModelTemplate template, Runtime.SceneInstance instance)
+        internal MonoGameModelInstance(MonoGameModelTemplate template, SceneInstance instance)
         {
             _Template = template;
-            _Instance = instance;
+            _Controller = instance;
         }
 
         #endregion
@@ -22,23 +22,35 @@ namespace SharpGLTF.Runtime
         #region data
 
         private readonly MonoGameModelTemplate _Template;
-        private readonly Runtime.SceneInstance _Instance;
+        private readonly SceneInstance _Controller;
 
         #endregion
 
         #region properties
 
+        /// <summary>
+        /// Gets a reference to the template used to create this <see cref="MonoGameModelInstance"/>.
+        /// </summary>
         public MonoGameModelTemplate Template => _Template;
 
-        public Runtime.SceneInstance Controller => _Instance;
+        /// <summary>
+        /// Gets a reference to the animation controller of this <see cref="MonoGameModelInstance"/>.
+        /// </summary>
+        public SceneInstance Controller => _Controller;
 
         #endregion
 
         #region API
 
+        /// <summary>
+        /// Draws this <see cref="MonoGameModelInstance"/> into the current <see cref="GraphicsDevice"/>.
+        /// </summary>
+        /// <param name="projection">The projection matrix.</param>
+        /// <param name="view">The view matrix.</param>
+        /// <param name="world">The world matrix.</param>
         public void Draw(Matrix projection, Matrix view, Matrix world)
         {
-            foreach (var d in _Instance.DrawableReferences)
+            foreach (var d in _Controller.DrawableReferences)
             {
                 Draw(_Template._Meshes[d.Item1], projection, view, world, d.Item2);
             }

+ 19 - 10
src/SharpGLTF.Core/Runtime/SceneInstance.cs

@@ -14,7 +14,7 @@ namespace SharpGLTF.Runtime
     /// Defines a node of a scene graph in <see cref="SceneInstance"/>
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("{Name}")]
-    public class NodeInstance
+    public sealed class NodeInstance
     {
         #region lifecycle
 
@@ -121,7 +121,7 @@ namespace SharpGLTF.Runtime
     /// <summary>
     /// Represents a specific and independent state of a <see cref="SceneTemplate"/>.
     /// </summary>
-    public class SceneInstance
+    public sealed class SceneInstance
     {
         #region lifecycle
 
@@ -169,10 +169,19 @@ namespace SharpGLTF.Runtime
 
         #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 all the names of the animations tracks.
+        /// </summary>
         public IEnumerable<String> AnimationTracks => _AnimationTracks.Names;
 
         /// <summary>
@@ -201,14 +210,14 @@ namespace SharpGLTF.Runtime
 
         #region API
 
-        public void SetLocalMatrix(string name, System.Numerics.Matrix4x4 localMatrix)
+        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, System.Numerics.Matrix4x4 worldMatrix)
+        public void SetWorldMatrix(string name, XFORM worldMatrix)
         {
             var n = LogicalNodes.FirstOrDefault(item => item.Name == name);
             if (n == null) return;
@@ -249,12 +258,12 @@ namespace SharpGLTF.Runtime
             SetAnimationFrame(_AnimationTracks.IndexOf(trackName), time, looped);
         }
 
-        public void SetAnimationFrame(params (int, float, float)[] blended)
+        public void SetAnimationFrame(params (int TrackIdx, float Time, float Weight)[] blended)
         {
             SetAnimationFrame(_NodeInstances, blended);
         }
 
-        public static void SetAnimationFrame(IEnumerable<NodeInstance> nodes, params (int, float, float)[] blended)
+        public static void SetAnimationFrame(IEnumerable<NodeInstance> nodes, params (int TrackIdx, float Time, float Weight)[] blended)
         {
             Guard.NotNull(nodes, nameof(nodes));
 
@@ -262,15 +271,15 @@ namespace SharpGLTF.Runtime
             Span<float> times = stackalloc float[blended.Length];
             Span<float> weights = stackalloc float[blended.Length];
 
-            float w = blended.Sum(item => item.Item3);
+            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].Item1;
-                times[i] = blended[i].Item2;
-                weights[i] = blended[i].Item3 * w;
+                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);

+ 0 - 2
src/SharpGLTF.Toolkit/Geometry/VertexTypes/VertexPreprocessors.cs

@@ -36,8 +36,6 @@ namespace SharpGLTF.Geometry.VertexTypes
     /// The vertex fragment type with Skin Joint Weights.
     /// Valid types are:
     /// <see cref="VertexEmpty"/>,
-    /// <see cref="VertexJoints8x4"/>,
-    /// <see cref="VertexJoints8x8"/>,
     /// <see cref="VertexJoints4"/>,
     /// <see cref="VertexJoints8"/>.
     /// </typeparam>

+ 118 - 0
src/SharpGLTF.Toolkit/Scenes/CameraBuilder.cs

@@ -0,0 +1,118 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text;
+
+namespace SharpGLTF.Scenes
+{
+    public abstract class CameraBuilder
+    {
+        #region properties
+
+        public static Vector3 LocalDirection => -Vector3.UnitZ;
+
+        /// <summary>
+        /// Gets or sets the near plane distance in the Z axis.
+        /// </summary>
+        public float ZNear { get; set; }
+
+        /// <summary>
+        /// Gets or sets the far plane distance in the Z axis.
+        /// </summary>
+        public float ZFar { get; set; }
+
+        public abstract Matrix4x4 Matrix { get; }
+
+        #endregion
+
+        #region types
+
+        [System.Diagnostics.DebuggerDisplay("Orthographic ({XMag},{YMag})  {ZNear} < {ZFar}")]
+        public sealed class Orthographic : CameraBuilder
+        {
+            #region lifecycle
+
+            public Orthographic(float xmag, float ymag, float znear, float zfar)
+            {
+                this.XMag = xmag;
+                this.YMag = ymag;
+                this.ZNear = znear;
+                this.ZFar = zfar;
+            }
+
+            internal Orthographic(Schema2.CameraOrthographic ortho)
+            {
+                this.XMag = ortho.XMag;
+                this.YMag = ortho.YMag;
+                this.ZNear = ortho.ZNear;
+                this.ZFar = ortho.ZFar;
+            }
+
+            #endregion
+
+            #region properties
+
+            /// <summary>
+            /// Gets or sets the magnification factor in the X axis
+            /// </summary>
+            public float XMag { get; set; }
+
+            /// <summary>
+            /// Gets or sets the magnification factor in the Y axis
+            /// </summary>
+            public float YMag { get; set; }
+
+            /// <summary>
+            /// Gets the projection matrix for the current settings
+            /// </summary>
+            public override Matrix4x4 Matrix => Transforms.Projection.CreateOrthographicMatrix(XMag, YMag, ZNear, ZFar);
+
+            #endregion
+        }
+
+        [System.Diagnostics.DebuggerDisplay("Perspective {AspectRatio} {VerticalFOV}   {ZNear} < {ZFar}")]
+        public sealed partial class Perspective : CameraBuilder
+        {
+            #region lifecycle
+
+            public Perspective(float? aspectRatio, float fovy, float znear, float zfar = float.PositiveInfinity)
+            {
+                this.AspectRatio = aspectRatio;
+                this.VerticalFOV = fovy;
+                this.ZNear = znear;
+                this.ZFar = zfar;
+            }
+
+            internal Perspective(Schema2.CameraPerspective persp)
+            {
+                this.AspectRatio = persp.AspectRatio;
+                this.VerticalFOV = persp.VerticalFOV;
+                this.ZNear = persp.ZNear;
+                this.ZFar = persp.ZFar;
+            }
+
+            #endregion
+
+            #region properties
+
+            /// <summary>
+            /// Gets or sets the aspect ratio between horizontal window size and vertical window size.
+            /// </summary>
+            public float? AspectRatio { get; set; }
+
+            /// <summary>
+            /// Gets or sets the vertical field of view, in radians
+            /// </summary>
+            public float VerticalFOV { get; set; }
+
+            /// <summary>
+            /// Gets the projection matrix for the current settings
+            /// </summary>
+            public override Matrix4x4 Matrix => Transforms.Projection.CreateOrthographicMatrix(AspectRatio ?? 1, VerticalFOV, ZNear, ZFar);
+
+            #endregion
+        }
+
+        #endregion
+    }
+}

+ 39 - 14
src/SharpGLTF.Toolkit/Scenes/Content.Schema2.cs

@@ -34,29 +34,54 @@ namespace SharpGLTF.Scenes
         }
     }
 
-    partial class OrthographicCameraContent : SCHEMA2NODE
+    partial class CameraContent : SCHEMA2NODE
     {
-        void SCHEMA2NODE.Setup(Node dstNode, Schema2SceneBuilder context)
+        public void Setup(Node dstNode, Schema2SceneBuilder context)
         {
-            // we try to assign our camera to the target node.
-            // but if the target node already has a mesh, we need to create
-            // a child node that will contain our camera.
+            if (_Camera is CameraBuilder.Orthographic ortho)
+            {
+                if (dstNode.Camera != null) dstNode = dstNode.CreateNode();
+                dstNode.WithOrthographicCamera(ortho.XMag, ortho.YMag, ortho.ZNear, ortho.ZFar);
+            }
 
-            if (dstNode.Camera != null) dstNode = dstNode.CreateNode();
-            dstNode.WithOrthographicCamera(_XMag, _YMag, _ZNear, _ZFar);
+            if (_Camera is CameraBuilder.Perspective persp)
+            {
+                if (dstNode.Camera != null) dstNode = dstNode.CreateNode();
+                dstNode.WithPerspectiveCamera(persp.AspectRatio, persp.VerticalFOV, persp.ZNear, persp.ZFar);
+            }
         }
     }
 
-    partial class PerspectiveCameraContent : SCHEMA2NODE
+    partial class LightContent : SCHEMA2NODE
     {
-        void SCHEMA2NODE.Setup(Node dstNode, Schema2SceneBuilder context)
+        public void Setup(Node dstNode, Schema2SceneBuilder context)
         {
-            // we try to assign our camera to the target node.
-            // but if the target node already has a mesh, we need to create
-            // a child node that will contain our camera.
+            if (_Light is LightBuilder.Directional directional)
+            {
+                if (dstNode.Camera != null) dstNode = dstNode.CreateNode();
+                dstNode.PunctualLight = dstNode.LogicalParent.CreatePunctualLight(PunctualLightType.Directional);
+                dstNode.PunctualLight.Color = directional.Color;
+                dstNode.PunctualLight.Intensity = directional.Intensity;
+            }
+
+            if (_Light is LightBuilder.Point point)
+            {
+                if (dstNode.Camera != null) dstNode = dstNode.CreateNode();
+                dstNode.PunctualLight = dstNode.LogicalParent.CreatePunctualLight(PunctualLightType.Point);
+                dstNode.PunctualLight.Color = point.Color;
+                dstNode.PunctualLight.Intensity = point.Intensity;
+                dstNode.PunctualLight.Range = point.Range;
+            }
 
-            if (dstNode.Camera != null) dstNode = dstNode.CreateNode();
-            dstNode.WithPerspectiveCamera(_AspectRatio, _FovY, _ZNear, _ZFar);
+            if (_Light is LightBuilder.Spot spot)
+            {
+                if (dstNode.Camera != null) dstNode = dstNode.CreateNode();
+                dstNode.PunctualLight = dstNode.LogicalParent.CreatePunctualLight(PunctualLightType.Spot);
+                dstNode.PunctualLight.Color = spot.Color;
+                dstNode.PunctualLight.Intensity = spot.Intensity;
+                dstNode.PunctualLight.Range = spot.Range;
+                dstNode.PunctualLight.SetSpotCone(spot.InnerConeAngle, spot.OuterConeAngle);
+            }
         }
     }
 }

+ 24 - 21
src/SharpGLTF.Toolkit/Scenes/Content.cs

@@ -53,36 +53,39 @@ namespace SharpGLTF.Scenes
         #endregion
     }
 
-    partial class OrthographicCameraContent
+    partial class CameraContent
     {
-        public OrthographicCameraContent(float xmag, float ymag, float znear, float zfar)
+        #region lifecycle
+
+        public CameraContent(CameraBuilder camera)
         {
-            _XMag = xmag;
-            _YMag = ymag;
-            _ZNear = znear;
-            _ZFar = zfar;
+            _Camera = camera;
         }
 
-        private float _XMag;
-        private float _YMag;
-        private float _ZNear;
-        private float _ZFar;
+        #endregion
+
+        #region data
+
+        private CameraBuilder _Camera;
+
+        #endregion
     }
 
-    partial class PerspectiveCameraContent
+    partial class LightContent
     {
-        public PerspectiveCameraContent(float? aspectRatio, float fovy, float znear, float zfar = float.PositiveInfinity)
+        #region lifecycle
+
+        public LightContent(LightBuilder light)
         {
-            _AspectRatio = aspectRatio;
-            _FovY = fovy;
-            _ZNear = znear;
-            _ZFar = zfar;
+            _Light = light;
         }
 
-        float? _AspectRatio;
-        float _FovY;
-        float _ZNear;
-        float _ZFar;
-    }
+        #endregion
+
+        #region data
+
+        private LightBuilder _Light;
 
+        #endregion
+    }
 }

+ 89 - 0
src/SharpGLTF.Toolkit/Scenes/LightBuilder.cs

@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Text;
+
+namespace SharpGLTF.Scenes
+{
+    public abstract class LightBuilder
+    {
+        protected LightBuilder(Schema2.PunctualLight light)
+        {
+            this.Color = light.Color;
+            this.Intensity = light.Intensity;
+        }
+
+        public static Vector3 LocalDirection => -Vector3.UnitZ;
+
+        /// <summary>
+        /// Gets or sets the RGB value for light's color in linear space.
+        /// </summary>
+        public Vector3 Color { get; set; }
+
+        /// <summary>
+        /// Gets or sets the Brightness of light in. The units that this is defined in depend on the type of light.
+        /// point and spot lights use luminous intensity in candela (lm/sr) while directional
+        /// lights use illuminance in lux (lm/m2)
+        /// </summary>
+        public Single Intensity { get; set; }
+
+        #region types
+
+        [System.Diagnostics.DebuggerDisplay("Directional")]
+        public sealed class Directional : LightBuilder
+        {
+            internal Directional(Schema2.PunctualLight light)
+                : base(light) { }
+        }
+
+        [System.Diagnostics.DebuggerDisplay("Point")]
+        public sealed class Point : LightBuilder
+        {
+            internal Point(Schema2.PunctualLight light)
+                : base(light)
+            {
+                this.Range = light.Range;
+            }
+
+            /// <summary>
+            /// Gets or sets a Hint defining a distance cutoff at which the light's intensity may be considered
+            /// to have reached zero. Supported only for point and spot lights. Must be > 0.
+            /// When undefined, range is assumed to be infinite.
+            /// </summary>
+            public Single Range { get; set; }
+        }
+
+        [System.Diagnostics.DebuggerDisplay("Spot")]
+        public sealed class Spot : LightBuilder
+        {
+            internal Spot(Schema2.PunctualLight light)
+                : base(light)
+            {
+                this.Range = light.Range;
+                this.InnerConeAngle = light.InnerConeAngle;
+                this.OuterConeAngle = light.OuterConeAngle;
+            }
+
+            /// <summary>
+            /// Gets or sets a Hint defining a distance cutoff at which the light's intensity may be considered
+            /// to have reached zero. Supported only for point and spot lights. Must be > 0.
+            /// When undefined, range is assumed to be infinite.
+            /// </summary>
+            public Single Range { get; set; }
+
+            /// <summary>
+            /// Gets or sets the Angle, in radians, from centre of spotlight where falloff begins.
+            /// Must be greater than or equal to 0 and less than outerConeAngle.
+            /// </summary>
+            public Single InnerConeAngle { get; set; }
+
+            /// <summary>
+            /// Gets or sets Angle, in radians, from centre of spotlight where falloff ends.
+            /// Must be greater than innerConeAngle and less than or equal to PI / 2.0.
+            /// </summary>
+            public Single OuterConeAngle { get; set; }
+        }
+
+        #endregion
+    }
+}

+ 32 - 9
src/SharpGLTF.Toolkit/Scenes/SceneBuilder.Schema2.cs

@@ -239,6 +239,12 @@ namespace SharpGLTF.Scenes
 
             _AddCameraInstances(dstScene, dstNodes, srcCameraInstances);
 
+            var srcLightInstances = Node.Flatten(srcScene)
+                .Where(item => item.PunctualLight != null)
+                .ToList();
+
+            _AddLightInstances(dstScene, dstNodes, srcCameraInstances);
+
             return dstScene;
         }
 
@@ -284,17 +290,34 @@ namespace SharpGLTF.Scenes
             foreach (var srcInstance in srcInstances)
             {
                 var dstNode = dstNodes[srcInstance];
-                var cam = srcInstance.Camera;
+                var srcCam = srcInstance.Camera;
+                if (srcCam == null) continue;
 
-                if (cam.Settings is CameraPerspective perspective)
-                {
-                    dstScene.AddPerspectiveCamera(dstNode, perspective.AspectRatio, perspective.VerticalFOV, perspective.ZNear, perspective.ZFar);
-                }
+                CameraBuilder dstCam = null;
 
-                if (cam.Settings is CameraOrthographic orthographic)
-                {
-                    dstScene.AddOrthographicCamera(dstNode, orthographic.XMag, orthographic.YMag, orthographic.ZNear, orthographic.ZFar);
-                }
+                if (srcCam.Settings is CameraPerspective perspective) dstCam = new CameraBuilder.Perspective(perspective);
+                if (srcCam.Settings is CameraOrthographic orthographic) dstCam = new CameraBuilder.Orthographic(orthographic);
+
+                if (dstCam != null) dstScene.AddCamera(dstCam, dstNode);
+            }
+        }
+
+        private static void _AddLightInstances(SceneBuilder dstScene, IReadOnlyDictionary<Node, NodeBuilder> dstNodes, IReadOnlyList<Node> srcInstances)
+        {
+            if (srcInstances.Count == 0) return;
+
+            foreach (var srcInstance in srcInstances)
+            {
+                var dstNode = dstNodes[srcInstance];
+                var srcLight = srcInstance.PunctualLight;
+                if (srcLight == null) continue;
+
+                LightBuilder dstLight = null;
+                if (srcLight.LightType == PunctualLightType.Directional) dstLight = new LightBuilder.Directional(srcLight);
+                if (srcLight.LightType == PunctualLightType.Point) dstLight = new LightBuilder.Point(srcLight);
+                if (srcLight.LightType == PunctualLightType.Spot) dstLight = new LightBuilder.Spot(srcLight);
+
+                if (dstLight != null) dstScene.AddLight(dstLight, dstNode);
             }
         }
 

+ 29 - 5
src/SharpGLTF.Toolkit/Scenes/SceneBuilder.cs

@@ -85,7 +85,7 @@ namespace SharpGLTF.Scenes
             return instance;
         }
 
-        public InstanceBuilder AddSkinnedMesh(MESHBUILDER mesh, params (NodeBuilder, Matrix4x4)[] joints)
+        public InstanceBuilder AddSkinnedMesh(MESHBUILDER mesh, params (NodeBuilder Joint, Matrix4x4 InverseBindMatrix)[] joints)
         {
             var instance = new InstanceBuilder(this);
             instance.Content = new SkinTransformer(mesh, joints);
@@ -95,24 +95,48 @@ namespace SharpGLTF.Scenes
             return instance;
         }
 
-        public InstanceBuilder AddOrthographicCamera(NodeBuilder node, float xmag, float ymag, float znear, float zfar)
+        public InstanceBuilder AddCamera(CameraBuilder camera, NodeBuilder node)
         {
-            var content = new OrthographicCameraContent(xmag, ymag, znear, zfar);
+            var content = new CameraContent(camera);
             var instance = new InstanceBuilder(this);
             instance.Content = new NodeTransformer(content, node);
             _Instances.Add(instance);
             return instance;
         }
 
-        public InstanceBuilder AddPerspectiveCamera(NodeBuilder node, float? aspectRatio, float fovy, float znear, float zfar = float.PositiveInfinity)
+        public InstanceBuilder AddCamera(CameraBuilder camera, Matrix4x4 cameraWorldMatrix)
         {
-            var content = new PerspectiveCameraContent(aspectRatio, fovy, znear, zfar);
+            var content = new CameraContent(camera);
+            var instance = new InstanceBuilder(this);
+            instance.Content = new StaticTransformer(content, cameraWorldMatrix);
+            _Instances.Add(instance);
+            return instance;
+        }
+
+        public InstanceBuilder AddCamera(CameraBuilder camera, Vector3 position, Vector3 lookat)
+        {
+            var xform = Matrix4x4.CreateWorld(position, Vector3.Normalize(lookat - position), Vector3.UnitY);
+            return AddCamera(camera, xform);
+        }
+
+        public InstanceBuilder AddLight(LightBuilder light, NodeBuilder node)
+        {
+            var content = new LightContent(light);
             var instance = new InstanceBuilder(this);
             instance.Content = new NodeTransformer(content, node);
             _Instances.Add(instance);
             return instance;
         }
 
+        public InstanceBuilder AddLight(LightBuilder light, Matrix4x4 lightWorldMatrix)
+        {
+            var content = new LightContent(light);
+            var instance = new InstanceBuilder(this);
+            instance.Content = new StaticTransformer(content, lightWorldMatrix);
+            _Instances.Add(instance);
+            return instance;
+        }
+
         public void RenameAllNodes(string namePrefix)
         {
             var allNodes = Instances

+ 14 - 13
src/SharpGLTF.Toolkit/Scenes/Transformers.cs

@@ -200,13 +200,14 @@ namespace SharpGLTF.Scenes
             SetJoints(meshWorldMatrix, joints);
         }
 
-        public SkinTransformer(MESHBUILDER mesh, (NodeBuilder, Matrix4x4)[] joints)
+        public SkinTransformer(MESHBUILDER mesh, (NodeBuilder Joint, Matrix4x4 InverseBindMatrix)[] joints)
             : base(mesh)
         {
             SetJoints(joints);
         }
 
-        protected SkinTransformer(SkinTransformer other) : base(other)
+        protected SkinTransformer(SkinTransformer other)
+            : base(other)
         {
             this._TargetBindMatrix = other._TargetBindMatrix;
             this._Joints.AddRange(other._Joints);
@@ -224,7 +225,7 @@ namespace SharpGLTF.Scenes
         private Matrix4x4? _TargetBindMatrix;
 
         // condition: all NodeBuilder objects must have the same root.
-        private readonly List<(NodeBuilder, Matrix4x4?)> _Joints = new List<(NodeBuilder, Matrix4x4?)>();
+        private readonly List<(NodeBuilder Joints, Matrix4x4? InverseBindMatrix)> _Joints = new List<(NodeBuilder, Matrix4x4?)>();
 
         #endregion
 
@@ -240,24 +241,24 @@ namespace SharpGLTF.Scenes
             _Joints.AddRange(joints.Select(item => (item, (Matrix4x4?)null)));
         }
 
-        private void SetJoints((NodeBuilder, Matrix4x4)[] joints)
+        private void SetJoints((NodeBuilder Joint, Matrix4x4 InverseBindMatrix)[] joints)
         {
             Guard.NotNull(joints, nameof(joints));
-            Guard.IsTrue(NodeBuilder.IsValidArmature(joints.Select(item => item.Item1)), nameof(joints));
+            Guard.IsTrue(NodeBuilder.IsValidArmature(joints.Select(item => item.Joint)), nameof(joints));
 
             _TargetBindMatrix = null;
             _Joints.Clear();
-            _Joints.AddRange(joints.Select(item => (item.Item1, (Matrix4x4?)item.Item2)));
+            _Joints.AddRange(joints.Select(item => (item.Joint, (Matrix4x4?)item.InverseBindMatrix)));
         }
 
-        public (NodeBuilder, Matrix4x4)[] GetJointBindings()
+        public (NodeBuilder Joint, Matrix4x4 InverseBindMatrix)[] GetJointBindings()
         {
-            var jb = new (NodeBuilder, Matrix4x4)[_Joints.Count];
+            var jb = new (NodeBuilder Joint, Matrix4x4 InverseBindMatrix)[_Joints.Count];
 
             for (int i = 0; i < jb.Length; ++i)
             {
-                var j = _Joints[i].Item1;
-                var m = _Joints[i].Item2 ?? Transforms.SkinTransform.CalculateInverseBinding(_TargetBindMatrix ?? Matrix4x4.Identity, j.WorldMatrix);
+                var j = _Joints[i].Joints;
+                var m = _Joints[i].InverseBindMatrix ?? Transforms.SkinTransform.CalculateInverseBinding(_TargetBindMatrix ?? Matrix4x4.Identity, j.WorldMatrix);
 
                 jb[i] = (j, m);
             }
@@ -268,7 +269,7 @@ namespace SharpGLTF.Scenes
         public override NodeBuilder GetArmatureAsset()
         {
             return _Joints
-                .Select(item => item.Item1.Root)
+                .Select(item => item.Joints.Root)
                 .Distinct()
                 .FirstOrDefault();
         }
@@ -280,8 +281,8 @@ namespace SharpGLTF.Scenes
             return new Transforms.SkinTransform
                 (
                 jb.Length,
-                idx => jb[idx].Item2,
-                idx => jb[idx].Item1.GetWorldMatrix(animationTrack, time),
+                idx => jb[idx].InverseBindMatrix,
+                idx => jb[idx].Joint.GetWorldMatrix(animationTrack, time),
                 default, false
                 );
         }