Browse Source

Regression: fixed camera and lights SceneBuilder roundtrip. Fixes #168

Vicente Penades 2 years ago
parent
commit
8cae54509c

+ 7 - 2
src/SharpGLTF.Toolkit/Scenes/CameraBuilder.cs

@@ -8,6 +8,11 @@ namespace SharpGLTF.Scenes
     /// <summary>
     /// <summary>
     /// Represents an camera object.
     /// Represents an camera object.
     /// </summary>
     /// </summary>
+    /// <remarks>
+    /// Derived types are:<br/>
+    /// - <see cref="Orthographic"/><br/>
+    /// - <see cref="Perspective"/><br/>
+    /// </remarks>
     public abstract class CameraBuilder : BaseBuilder
     public abstract class CameraBuilder : BaseBuilder
     {
     {
         #region lifecycle
         #region lifecycle
@@ -75,7 +80,7 @@ namespace SharpGLTF.Scenes
         #pragma warning disable CA1034 // Nested types should not be visible
         #pragma warning disable CA1034 // Nested types should not be visible
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
-        [System.Diagnostics.DebuggerDisplay("Orthographic ({XMag},{YMag})  {ZNear} < {ZFar}")]
+        [System.Diagnostics.DebuggerDisplay("CameraBuilder.Orthographic ({XMag},{YMag})  {ZNear} < {ZFar}")]
         public sealed class Orthographic : CameraBuilder
         public sealed class Orthographic : CameraBuilder
         {
         {
             #region lifecycle
             #region lifecycle
@@ -133,7 +138,7 @@ namespace SharpGLTF.Scenes
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
-        [System.Diagnostics.DebuggerDisplay("Perspective {AspectRatio} {VerticalFOV}   {ZNear} < {ZFar}")]
+        [System.Diagnostics.DebuggerDisplay("CameraBuilder.Perspective {AspectRatio} {VerticalFOV}   {ZNear} < {ZFar}")]
         public sealed partial class Perspective : CameraBuilder
         public sealed partial class Perspective : CameraBuilder
         {
         {
             #region lifecycle
             #region lifecycle

+ 2 - 2
src/SharpGLTF.Toolkit/Scenes/Content.Schema2.cs

@@ -24,7 +24,7 @@ namespace SharpGLTF.Scenes
 
 
     partial class CameraContent : SCHEMA2NODE
     partial class CameraContent : SCHEMA2NODE
     {
     {
-        public void ApplyTo(Node dstNode, Schema2SceneBuilder context)
+        void SCHEMA2NODE.ApplyTo(Node dstNode, Schema2SceneBuilder context)
         {
         {
             if (_Camera is CameraBuilder.Orthographic ortho)
             if (_Camera is CameraBuilder.Orthographic ortho)
             {
             {
@@ -42,7 +42,7 @@ namespace SharpGLTF.Scenes
 
 
     partial class LightContent : SCHEMA2NODE
     partial class LightContent : SCHEMA2NODE
     {
     {
-        public void ApplyTo(Node dstNode, Schema2SceneBuilder context)
+        void SCHEMA2NODE.ApplyTo(Node dstNode, Schema2SceneBuilder context)
         {
         {
             if (_Light is LightBuilder.Directional directional)
             if (_Light is LightBuilder.Directional directional)
             {
             {

+ 4 - 4
src/SharpGLTF.Toolkit/Scenes/Content.cs

@@ -13,7 +13,7 @@ namespace SharpGLTF.Scenes
     /// <summary>
     /// <summary>
     /// Represents a dummy, empty content of <see cref="ContentTransformer.Content"/>.
     /// Represents a dummy, empty content of <see cref="ContentTransformer.Content"/>.
     /// </summary>
     /// </summary>
-    [System.Diagnostics.DebuggerDisplay("Custom")]
+    [System.Diagnostics.DebuggerDisplay("EmptyContent")]
     partial class EmptyContent : ICloneable
     partial class EmptyContent : ICloneable
     {
     {
         #region lifecycle
         #region lifecycle
@@ -30,7 +30,7 @@ namespace SharpGLTF.Scenes
     /// <summary>
     /// <summary>
     /// Represents a <see cref="MESHBUILDER"/> content of <see cref="ContentTransformer.Content"/>.
     /// Represents a <see cref="MESHBUILDER"/> content of <see cref="ContentTransformer.Content"/>.
     /// </summary>
     /// </summary>
-    [System.Diagnostics.DebuggerDisplay("Mesh")]
+    [System.Diagnostics.DebuggerDisplay("MeshContent => {_Mesh}")]
     partial class MeshContent :
     partial class MeshContent :
         IRenderableContent,
         IRenderableContent,
         ICloneable,
         ICloneable,
@@ -92,7 +92,7 @@ namespace SharpGLTF.Scenes
     /// <summary>
     /// <summary>
     /// Represents a <see cref="CameraBuilder"/> content of <see cref="ContentTransformer.Content"/>.
     /// Represents a <see cref="CameraBuilder"/> content of <see cref="ContentTransformer.Content"/>.
     /// </summary>
     /// </summary>
-    [System.Diagnostics.DebuggerDisplay("Camera")]
+    [System.Diagnostics.DebuggerDisplay("CameraContent => {_Camera}")]
     partial class CameraContent : ICloneable
     partial class CameraContent : ICloneable
     {
     {
         #region lifecycle
         #region lifecycle
@@ -135,7 +135,7 @@ namespace SharpGLTF.Scenes
     /// <summary>
     /// <summary>
     /// Represents a <see cref="LightBuilder"/> content of <see cref="ContentTransformer.Content"/>.
     /// Represents a <see cref="LightBuilder"/> content of <see cref="ContentTransformer.Content"/>.
     /// </summary>
     /// </summary>
-    [System.Diagnostics.DebuggerDisplay("Light")]
+    [System.Diagnostics.DebuggerDisplay("LightContent => {_Light}")]
     partial class LightContent : ICloneable
     partial class LightContent : ICloneable
     {
     {
         #region lifecycle
         #region lifecycle

+ 3 - 1
src/SharpGLTF.Toolkit/Scenes/InstanceBuilder.cs

@@ -82,7 +82,9 @@ namespace SharpGLTF.Scenes
             get
             get
             {
             {
                 var asset = Content.GetGeometryAsset();
                 var asset = Content.GetGeometryAsset();
-                return asset != null ? asset.Materials : Enumerable.Empty<Materials.MaterialBuilder>();
+                return asset != null
+                    ? asset.Materials
+                    : Enumerable.Empty<Materials.MaterialBuilder>();
             }
             }
         }
         }
 
 

+ 3 - 3
src/SharpGLTF.Toolkit/Scenes/LightBuilder.cs

@@ -68,7 +68,7 @@ namespace SharpGLTF.Scenes
         #region Nested types
         #region Nested types
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
-        [System.Diagnostics.DebuggerDisplay("Directional")]
+        [System.Diagnostics.DebuggerDisplay("LightBuilder.Directional")]
         public sealed class Directional : LightBuilder
         public sealed class Directional : LightBuilder
         {
         {
             #region lifecycle
             #region lifecycle
@@ -95,7 +95,7 @@ namespace SharpGLTF.Scenes
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
-        [System.Diagnostics.DebuggerDisplay("Point")]
+        [System.Diagnostics.DebuggerDisplay("LightBuilder.Point")]
         public sealed class Point : LightBuilder
         public sealed class Point : LightBuilder
         {
         {
             #region lifecycle
             #region lifecycle
@@ -140,7 +140,7 @@ namespace SharpGLTF.Scenes
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
-        [System.Diagnostics.DebuggerDisplay("Spot")]
+        [System.Diagnostics.DebuggerDisplay("LightBuilder.Spot")]
         public sealed class Spot : LightBuilder
         public sealed class Spot : LightBuilder
         {
         {
             #region lifecycle
             #region lifecycle

+ 82 - 4
src/SharpGLTF.Toolkit/Scenes/SceneBuilder.Schema2.cs

@@ -200,7 +200,7 @@ namespace SharpGLTF.Scenes
                 .Where(item => item.PunctualLight != null)
                 .Where(item => item.PunctualLight != null)
                 .ToList();
                 .ToList();
 
 
-            _AddLightInstances(dstScene, dstNodes, srcCameraInstances);
+            _AddLightInstances(dstScene, dstNodes, srcLightInstances);
 
 
             // process empty nodes with at least name or extras
             // process empty nodes with at least name or extras
 
 
@@ -502,6 +502,38 @@ namespace SharpGLTF.Scenes
             {
             {
                 throw new InvalidOperationException($"Expected {this.Instances.Count}, but found {renderableViewCount}");
                 throw new InvalidOperationException($"Expected {this.Instances.Count}, but found {renderableViewCount}");
             }
             }
+
+            // compare lights:
+
+            var srcLights = this.Instances
+                .Select(item => item.Content.Content)
+                .OfType<LightContent>()
+                .ToList();
+
+            var dstLights = Node.Flatten(gltfScene)
+                .Where(item => item.PunctualLight != null)
+                .ToList();
+
+            if (srcLights.Count != dstLights.Count)
+            {
+                throw new InvalidOperationException($"Expected {srcLights.Count}, but found {dstLights.Count}");
+            }
+
+            // compare cameras:
+
+            var srcCameras = this.Instances
+                .Select(item => item.Content.Content)
+                .OfType<CameraContent>()
+                .ToList();
+
+            var dstCameras = Node.Flatten(gltfScene)
+                .Where(item => item.Camera != null)
+                .ToList();
+
+            if (srcCameras.Count != dstCameras.Count)
+            {
+                throw new InvalidOperationException($"Expected {srcCameras.Count}, but found {dstCameras.Count}");
+            }
         }
         }
 
 
         #endregion
         #endregion
@@ -672,6 +704,18 @@ namespace SharpGLTF.Scenes
             _Nodes.Clear();
             _Nodes.Clear();
             AddArmatureResources(new[] { srcScene }, () => dstScene.CreateNode());
             AddArmatureResources(new[] { srcScene }, () => dstScene.CreateNode());
 
 
+            
+
+            AddMeshes(dstScene, srcScene);
+            AddLightsAndCameras(dstScene, srcScene);
+
+            #if DEBUG
+            srcScene._VerifyConversion(dstScene);
+            #endif
+        }
+
+        private void AddMeshes(Scene dstScene, SceneBuilder srcScene)
+        {
             // gather single operators (RigidTransformer and SkinnedTransformer)
             // gather single operators (RigidTransformer and SkinnedTransformer)
 
 
             var srcSingleOperators = srcScene
             var srcSingleOperators = srcScene
@@ -698,10 +742,44 @@ namespace SharpGLTF.Scenes
             {
             {
                 op.ApplyTo(dstScene, this);
                 op.ApplyTo(dstScene, this);
             }
             }
+        }
 
 
-            #if DEBUG
-            srcScene._VerifyConversion(dstScene);
-            #endif
+        private void AddLightsAndCameras(Scene dstScene, SceneBuilder srcScene)
+        {
+            bool isCameraOrLight(ContentTransformer xformer)
+            {
+                if (xformer?.Content is CameraContent) return true;
+                if (xformer?.Content is LightContent) return true;
+                return false;
+            }
+
+            // gather operators (RigidTransformer)
+
+            var srcSingleOperators = srcScene
+                .Instances
+                .Select(item => item.Content)
+                .Where(isCameraOrLight)
+                .OfType<RigidTransformer>()
+                .Cast<IOperator<Scene>>();
+
+            // gather multi operators (Fixed Transformer)
+
+            var srcChildren = srcScene
+                .Instances
+                .Select(item => item.Content)
+                .Where(isCameraOrLight)
+                .OfType<FixedTransformer>()
+                .Select(item => new _FixedIntance(item))
+                .Cast<IOperator<Scene>>();
+
+            // apply operators
+
+            var srcOperators = srcSingleOperators.Concat(srcChildren);
+
+            foreach (var op in srcOperators)
+            {
+                op.ApplyTo(dstScene, this);
+            }
         }
         }
 
 
         #endregion
         #endregion

+ 61 - 2
src/SharpGLTF.Toolkit/Scenes/Transformers.Schema2.cs

@@ -1,6 +1,8 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Security.Cryptography;
+using System.Xml.Linq;
 
 
 using SharpGLTF.Schema2;
 using SharpGLTF.Schema2;
 
 
@@ -12,7 +14,7 @@ namespace SharpGLTF.Scenes
     /// <summary>
     /// <summary>
     /// Groups instances of the same mesh being attached to the same node.
     /// Groups instances of the same mesh being attached to the same node.
     /// </summary>
     /// </summary>
-    [System.Diagnostics.DebuggerDisplay("Rigid Node[{_DebugName,nq}] = GpuMeshInstances[{_Children.Count}]")]
+    [System.Diagnostics.DebuggerDisplay("_MeshInstancing Node[{_DebugName,nq}] = GpuMeshInstances[{_Children.Count}]")]
     internal readonly struct _MeshInstancing : SCHEMA2SCENE
     internal readonly struct _MeshInstancing : SCHEMA2SCENE
     {
     {
         #region debug
         #region debug
@@ -125,6 +127,8 @@ namespace SharpGLTF.Scenes
         {
         {
             if (_Children.Count < _GpuMinCount)
             if (_Children.Count < _GpuMinCount)
             {
             {
+                // add fixed instances as individual nodes.
+
                 foreach (var srcChild in _Children)
                 foreach (var srcChild in _Children)
                 {
                 {
                     if (srcChild.Content is SCHEMA2NODE srcOperator)
                     if (srcChild.Content is SCHEMA2NODE srcOperator)
@@ -142,6 +146,8 @@ namespace SharpGLTF.Scenes
             }
             }
             else
             else
             {
             {
+                // add fixed instances using GPU instancing extension.
+
                 if (_Children[0].Content is SCHEMA2NODE srcOperator)
                 if (_Children[0].Content is SCHEMA2NODE srcOperator)
                 {
                 {
                     var xforms = _Children
                     var xforms = _Children
@@ -177,6 +183,56 @@ namespace SharpGLTF.Scenes
         #endregion
         #endregion
     }
     }
 
 
+    /// <summary>
+    /// helper class used to build <see cref="FixedTransformer"/> into a <see cref="Scene"/>
+    /// </summary>
+    [System.Diagnostics.DebuggerDisplay("_FixedIntance Node[{_DebugName,nq}] = {Content}")]
+    internal readonly struct _FixedIntance : SCHEMA2SCENE
+    {
+        #region debug
+
+        [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
+        private string _DebugName => _srcChild?.Name ?? "*";
+
+        #endregion
+
+        #region lifecycle
+
+        public _FixedIntance(FixedTransformer fixedXformer)
+        {
+            _srcChild = fixedXformer;
+        }
+
+        #endregion
+
+        #region data
+
+        private readonly FixedTransformer _srcChild;
+
+        #endregion
+
+        #region API
+
+        void SCHEMA2SCENE.ApplyTo(Scene dstScene, Schema2SceneBuilder context)
+        {
+            if (!(_srcChild.Content is SCHEMA2NODE schema2Target))
+            {
+                throw new InvalidOperationException("Operator expected");
+            }
+
+            var dstNode = dstScene.CreateNode();
+            dstNode.Name = _srcChild.Name;
+            dstNode.Extras = _srcChild.Extras.DeepClone();
+            dstNode.LocalTransform = _srcChild.ChildTransform;
+
+            schema2Target.ApplyTo(dstNode, context);
+
+            System.Diagnostics.Debug.Assert(dstNode.WorldMatrix == _srcChild.GetPoseWorldMatrix(), "Transform mismatch!");
+        }
+
+        #endregion
+    }
+
     partial class RigidTransformer : SCHEMA2SCENE
     partial class RigidTransformer : SCHEMA2SCENE
     {
     {
         void SCHEMA2SCENE.ApplyTo(Scene dstScene, Schema2SceneBuilder context)
         void SCHEMA2SCENE.ApplyTo(Scene dstScene, Schema2SceneBuilder context)
@@ -187,7 +243,10 @@ namespace SharpGLTF.Scenes
 
 
                 schema2Target.ApplyTo(dstNode, context);
                 schema2Target.ApplyTo(dstNode, context);
 
 
-                Schema2SceneBuilder.SetMorphAnimation(dstNode, this.Morphings);
+                if (schema2Target is MeshContent)
+                {
+                    Schema2SceneBuilder.SetMorphAnimation(dstNode, this.Morphings);
+                }                                
 
 
                 System.Diagnostics.Debug.Assert(dstNode.WorldMatrix == this.GetPoseWorldMatrix(), "Transform mismatch!");
                 System.Diagnostics.Debug.Assert(dstNode.WorldMatrix == this.GetPoseWorldMatrix(), "Transform mismatch!");
             }
             }

+ 20 - 4
src/SharpGLTF.Toolkit/Scenes/Transformers.cs

@@ -88,6 +88,10 @@ namespace SharpGLTF.Scenes
         /// <summary>
         /// <summary>
         /// Gets the content of this transformer.<br/>
         /// Gets the content of this transformer.<br/>
         /// </summary>
         /// </summary>
+        /// <remarks>
+        /// Available types for this object are:
+        /// <see cref="MeshContent"/>, <see cref="CameraContent"/>, <see cref="LightContent"/>.
+        /// </remarks>
         internal Object Content => _Content;
         internal Object Content => _Content;
 
 
         public Animations.AnimatableProperty<ArraySegment<float>> Morphings => _Morphings;
         public Animations.AnimatableProperty<ArraySegment<float>> Morphings => _Morphings;
@@ -105,7 +109,19 @@ namespace SharpGLTF.Scenes
         /// If this <see cref="ContentTransformer"/> contains a <see cref="MESHBUILDER"/>.
         /// If this <see cref="ContentTransformer"/> contains a <see cref="MESHBUILDER"/>.
         /// </summary>
         /// </summary>
         /// <returns>A <see cref="MESHBUILDER"/> instance, or NULL.</returns>
         /// <returns>A <see cref="MESHBUILDER"/> instance, or NULL.</returns>
-        public virtual MESHBUILDER GetGeometryAsset() { return (_Content as IRenderableContent)?.GetGeometryAsset(); }
+        public MESHBUILDER GetGeometryAsset() { return (_Content as IRenderableContent)?.GetGeometryAsset(); }
+
+        /// <summary>
+        /// It this <see cref="ContentTransformer"/> contains a <see cref="CameraBuilder"/>
+        /// </summary>
+        /// <returns>A <see cref="CameraBuilder"/> instance, or NULL.</returns>
+        public CameraBuilder GetCameraAsset() { return (_Content as CameraContent)?.Camera; }
+
+        /// <summary>
+        /// It this <see cref="ContentTransformer"/> contains a <see cref="LightBuilder"/>
+        /// </summary>
+        /// <returns>A <see cref="LightBuilder"/> instance, or NULL.</returns>
+        public LightBuilder GetLightAsset() { return (_Content as LightContent)?.Light; }
 
 
         /// <summary>
         /// <summary>
         /// If this <see cref="ContentTransformer"/> uses a <see cref="NodeBuilder"/> armature, it returns the root of the armature.
         /// If this <see cref="ContentTransformer"/> uses a <see cref="NodeBuilder"/> armature, it returns the root of the armature.
@@ -173,7 +189,7 @@ namespace SharpGLTF.Scenes
     /// Represents the transform of a <see cref="InstanceBuilder.Content"/>.<br/>
     /// Represents the transform of a <see cref="InstanceBuilder.Content"/>.<br/>
     /// Applies a fixed <see cref="Matrix4x4"/> transform to the underlaying content.
     /// Applies a fixed <see cref="Matrix4x4"/> transform to the underlaying content.
     /// </summary>
     /// </summary>
-    [System.Diagnostics.DebuggerDisplay("Fixed Node[{_DebugName,nq}] = {Content}")]
+    [System.Diagnostics.DebuggerDisplay("FixedTransformer Node[{_DebugName,nq}] = {Content}")]
     public partial class FixedTransformer : ContentTransformer
     public partial class FixedTransformer : ContentTransformer
     {
     {
         #region lifecycle
         #region lifecycle
@@ -270,7 +286,7 @@ namespace SharpGLTF.Scenes
     /// Represents the transform of a <see cref="InstanceBuilder.Content"/>.<br/>
     /// Represents the transform of a <see cref="InstanceBuilder.Content"/>.<br/>
     /// Applies the transform of a single <see cref="NodeBuilder"/> to the underlaying content.
     /// Applies the transform of a single <see cref="NodeBuilder"/> to the underlaying content.
     /// </summary>
     /// </summary>
-    [System.Diagnostics.DebuggerDisplay("Rigid Node[{_DebugName,nq}] = {Content}")]
+    [System.Diagnostics.DebuggerDisplay("RigidTransformer Node[{_DebugName,nq}] = {Content}")]
     public partial class RigidTransformer : ContentTransformer
     public partial class RigidTransformer : ContentTransformer
     {
     {
         #region lifecycle
         #region lifecycle
@@ -340,7 +356,7 @@ namespace SharpGLTF.Scenes
     /// Represents the transform of a <see cref="InstanceBuilder.Content"/>.<br/>
     /// Represents the transform of a <see cref="InstanceBuilder.Content"/>.<br/>
     /// Applies the transforms of many <see cref="NodeBuilder"/> to the underlaying content.
     /// Applies the transforms of many <see cref="NodeBuilder"/> to the underlaying content.
     /// </summary>
     /// </summary>
-    [System.Diagnostics.DebuggerDisplay("Skinned Node[{_DebugName,nq}] = {Content}")]
+    [System.Diagnostics.DebuggerDisplay("SkinnedTransformer Node[{_DebugName,nq}] = {Content}")]
     public partial class SkinnedTransformer : ContentTransformer
     public partial class SkinnedTransformer : ContentTransformer
     {
     {
         #region lifecycle
         #region lifecycle

+ 39 - 0
tests/SharpGLTF.Toolkit.Tests/Scenes/SceneBuilderTests.cs

@@ -40,6 +40,45 @@ namespace SharpGLTF.Scenes
             scene.AttachToCurrentTest("cube.plotly");
             scene.AttachToCurrentTest("cube.plotly");
         }
         }
 
 
+        [Test(Description = "Creates a simple cube with a light.")]
+        public void CreateCubeWithLightScene()
+        {
+            TestContext.CurrentContext.AttachGltfValidatorLinks();
+
+            var material = MaterialBuilder.CreateDefault();
+
+            var mesh = new Cube<MaterialBuilder>(material).ToMesh(Matrix4x4.Identity);
+
+            var scene = new SceneBuilder();
+
+            scene.AddRigidMesh(mesh, Matrix4x4.Identity);
+
+            var light = new LightBuilder.Point
+            {
+                Color = new Vector3(1, 0, 0),
+                Intensity = 3,
+                Range = 10,
+            };
+            
+            scene.AddLight(light, new NodeBuilder("light").WithLocalTranslation(new Vector3(0, 100, 0)) );
+            scene.AddLight(light, Matrix4x4.CreateTranslation(0, -100, 0));
+
+            var lightInstances = scene.Instances
+                .Select(item => item.Content.Content)
+                .OfType<LightContent>()
+                .ToList();
+
+            Assert.AreEqual(2, lightInstances.Count);
+
+            var gltf = scene.ToGltf2();
+
+            Assert.AreEqual(2, gltf.LogicalPunctualLights.Count);
+
+            gltf.AttachToCurrentTest("cube.glb");
+            gltf.AttachToCurrentTest("cube.gltf");
+            gltf.AttachToCurrentTest("cube.plotly");
+        }
+
         [Test(Description = "Creates a simple cube.")]
         [Test(Description = "Creates a simple cube.")]
         public void CreateCubeSceneWithExtras()
         public void CreateCubeSceneWithExtras()
         {            
         {