2
0
Эх сурвалжийг харах

More libraries reorganization.

Vicente Penades 5 жил өмнө
parent
commit
ea97afbcc0
30 өөрчлөгдсөн 731 нэмэгдсэн , 131 устгасан
  1. 1 0
      src/Demo1/Demo1.csproj
  2. 1 0
      src/Demo2/Demo2.csproj
  3. 1 1
      src/Demo2/Game1.cs
  4. 1 0
      src/Demo3/Demo3.csproj
  5. 6 6
      src/Demo3/Game1.cs
  6. 60 8
      src/MonoGame.Framework.Graphics.GLTF/Factories/MeshFactory.Basic.cs
  7. 5 5
      src/MonoGame.Framework.Graphics.GLTF/FormatGLTF.cs
  8. 2 2
      src/MonoGame.Framework.Graphics.GLTF/MonoGame.Framework.Pipeline.GLTF.csproj
  9. 0 2
      src/MonoGame.Framework.Graphics.PBR/Interfaces.cs
  10. 4 0
      src/MonoGame.Framework.Graphics.PBR/MonoGame.Framework.Graphics.EffectsPBR.csproj
  11. 200 0
      src/MonoGame.Framework.Graphics.Scene3D/ModelDrawingContext.cs
  12. 23 0
      src/MonoGame.Framework.Graphics.Scene3D/MonoGame.Framework.Graphics.Scene3D.csproj
  13. 64 0
      src/MonoGame.Framework.Graphics.Scene3D/PBREnvironment.cs
  14. 26 3
      src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/Meshes/MeshCollection.cs
  15. 84 0
      src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/Meshes/MeshGeometry.cs
  16. 16 54
      src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/Meshes/MeshPart.cs
  17. 1 1
      src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelDrawingContext.cs
  18. 3 1
      src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelGraph/ArmatureInstance.cs
  19. 13 4
      src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelGraph/ArmatureTemplate.cs
  20. 16 9
      src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelGraph/ModelLayerInstance.cs
  21. 4 8
      src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelGraph/ModelLayerTemplate.cs
  22. 31 10
      src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelGraph/ModelTemplate.cs
  23. 3 0
      src/MonoGame.Framework.Graphics.Toolkit3D/ICurveEvaluator.cs
  24. 6 2
      src/MonoGame.Framework.Graphics.Toolkit3D/MonoGame.Framework.Graphics.Model3D.csproj
  25. 8 5
      src/MonoGame.Framework.Graphics.Toolkit3D/Pipeline/MeshPrimitiveBuilder.cs
  26. 141 3
      src/MonoGameScene.sln
  27. 1 1
      src/MonoGameScene/MonoGameScene.csproj
  28. 7 5
      src/MonoGameViewer/MainScene.cs
  29. 1 0
      src/MonoGameViewer/MonoGameViewer.csproj
  30. 2 1
      src/SharpGLTF.Runtime.MonoGame/SharpGLTF.Runtime.MonoGame.csproj

+ 1 - 0
src/Demo1/Demo1.csproj

@@ -26,6 +26,7 @@
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MonoGame.Framework.Graphics.GLTF\MonoGame.Framework.Pipeline.GLTF.csproj" />
     <ProjectReference Include="..\MonoGame.Framework.Graphics.GLTF\MonoGame.Framework.Pipeline.GLTF.csproj" />
+    <ProjectReference Include="..\MonoGame.Framework.Graphics.Scene3D\MonoGame.Framework.Graphics.Scene3D.csproj" />
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>

+ 1 - 0
src/Demo2/Demo2.csproj

@@ -26,6 +26,7 @@
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MonoGame.Framework.Graphics.GLTF\MonoGame.Framework.Pipeline.GLTF.csproj" />
     <ProjectReference Include="..\MonoGame.Framework.Graphics.GLTF\MonoGame.Framework.Pipeline.GLTF.csproj" />
+    <ProjectReference Include="..\MonoGame.Framework.Graphics.Scene3D\MonoGame.Framework.Graphics.Scene3D.csproj" />
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>

+ 1 - 1
src/Demo2/Game1.cs

@@ -39,7 +39,7 @@ namespace Demo2
 
 
         private PBREnvironment _LightsAndFog = PBREnvironment.CreateDefault();
         private PBREnvironment _LightsAndFog = PBREnvironment.CreateDefault();
 
 
-        private ModelTemplate _ModelTemplate;
+        private ModelTemplateContent _ModelTemplate;
 
 
         #endregion
         #endregion
 
 

+ 1 - 0
src/Demo3/Demo3.csproj

@@ -26,6 +26,7 @@
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MonoGame.Framework.Graphics.GLTF\MonoGame.Framework.Pipeline.GLTF.csproj" />
     <ProjectReference Include="..\MonoGame.Framework.Graphics.GLTF\MonoGame.Framework.Pipeline.GLTF.csproj" />
+    <ProjectReference Include="..\MonoGame.Framework.Graphics.Scene3D\MonoGame.Framework.Graphics.Scene3D.csproj" />
   </ItemGroup>
   </ItemGroup>
   
   
   <ItemGroup>
   <ItemGroup>

+ 6 - 6
src/Demo3/Game1.cs

@@ -45,11 +45,11 @@ namespace Demo3
 
 
         // these are the actual hardware resources that represent every model's geometry.        
         // these are the actual hardware resources that represent every model's geometry.        
 
 
-        ModelTemplate _AvodadoTemplate;
-        ModelTemplate _BrainStemTemplate;
-        ModelTemplate _CesiumManTemplate;
-        ModelTemplate _HauntedHouseTemplate;
-        ModelTemplate _SharkTemplate;
+        ModelTemplateContent _AvodadoTemplate;
+        ModelTemplateContent _BrainStemTemplate;
+        ModelTemplateContent _CesiumManTemplate;
+        ModelTemplateContent _HauntedHouseTemplate;
+        ModelTemplateContent _SharkTemplate;
 
 
         #endregion
         #endregion
 
 
@@ -57,7 +57,7 @@ namespace Demo3
 
 
         protected override void LoadContent()
         protected override void LoadContent()
         {
         {
-            ModelTemplate _load(string filePath)
+            ModelTemplateContent _load(string filePath)
             {
             {
                 return Microsoft.Xna.Framework.Content.Pipeline.Graphics.FormatGLTF.LoadModel(filePath, this.GraphicsDevice);
                 return Microsoft.Xna.Framework.Content.Pipeline.Graphics.FormatGLTF.LoadModel(filePath, this.GraphicsDevice);
             }            
             }            

+ 60 - 8
src/MonoGame.Framework.Graphics.GLTF/Factories/MeshFactory.Basic.cs

@@ -8,19 +8,24 @@ using GLTFMATERIAL = SharpGLTF.Schema2.Material;
 
 
 namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
 namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
 {
 {
+    /// <summary>
+    /// Gltf loading factory using in built monogame's effects like <see cref="BasicEffect"/> and <see cref="SkinnedEffect"/>
+    /// </summary>
+    /// <remarks>
+    /// Monogame's BasicEffect and SkinnedEffect use Phong's shading, while glTF uses PBR shading,
+    /// so given monogame's limitations, we try to guess the most appropiate values to have a
+    /// reasonably good looking renders.
+    /// 
+    /// Also, for SkinnedEffect, skinning is limited to 72 bones.
+    /// </remarks>
     public class BasicMeshFactory : GLTFMeshFactory
     public class BasicMeshFactory : GLTFMeshFactory
     {
     {
-        public BasicMeshFactory(GraphicsDevice device) : base(device)
-        {
-        }
-
-        // Monogame's BasicEffect uses Phong's shading, while glTF uses PBR shading, so
-        // given monogame's limitations, we try to guess the most appropiate values
-        // to have a reasonably good looking renders.
+        public BasicMeshFactory(GraphicsDevice device)
+            : base(device) { }
 
 
         protected override Type GetPreferredVertexType(IMeshPrimitiveDecoder<GLTFMATERIAL> srcPrim)
         protected override Type GetPreferredVertexType(IMeshPrimitiveDecoder<GLTFMATERIAL> srcPrim)
         {
         {
-            return base.GetPreferredVertexType(srcPrim);
+            return srcPrim.JointsWeightsCount > 0 ? typeof(VertexBasicSkinned) : typeof(VertexPositionNormalTexture);
         }
         }
 
 
         protected override MeshPrimitiveMaterial ConvertMaterial(GLTFMATERIAL srcMaterial, bool mustSupportSkinning)
         protected override MeshPrimitiveMaterial ConvertMaterial(GLTFMATERIAL srcMaterial, bool mustSupportSkinning)
@@ -115,5 +120,52 @@ namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
         }
         }
 
 
         #endregion
         #endregion
+
+        #region vertex types
+
+        struct VertexBasicSkinned : IVertexType
+        {
+            #region static
+
+            private static VertexDeclaration _VDecl = CreateVertexDeclaration();
+
+            public static VertexDeclaration CreateVertexDeclaration()
+            {
+                int offset = 0;
+
+                var a = new VertexElement(offset, VertexElementFormat.Vector3, VertexElementUsage.Position, 0);
+                offset += 3 * 4;
+
+                var b = new VertexElement(offset, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0);
+                offset += 3 * 4;
+
+                var c = new VertexElement(offset, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0);
+                offset += 2 * 4;
+
+                var d = new VertexElement(offset, VertexElementFormat.Byte4, VertexElementUsage.BlendIndices, 0);
+                offset += 4 * 1;
+
+                var e = new VertexElement(offset, VertexElementFormat.Vector4, VertexElementUsage.BlendWeight, 0);
+                offset += 4 * 4;
+
+                return new VertexDeclaration(a, b, c, d, e);
+            }
+
+            #endregion
+
+            #region data
+
+            public VertexDeclaration VertexDeclaration => _VDecl;
+
+            public Vector3 Position;
+            public Vector3 Normal;
+            public Vector2 TextureCoordinate;
+            public Framework.Graphics.PackedVector.Byte4 BlendIndices;
+            public Vector4 BlendWeight;
+
+            #endregion
+        }
+
+        #endregion
     }
     }
 }
 }

+ 5 - 5
src/MonoGame.Framework.Graphics.GLTF/FormatGLTF.cs

@@ -10,28 +10,28 @@ namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
 {
 {
     public static class FormatGLTF
     public static class FormatGLTF
     {
     {
-        public static ModelTemplate LoadModel(string filePath, GraphicsDevice graphics, bool useBasicEffects = false)
+        public static ModelTemplateContent LoadModel(string filePath, GraphicsDevice graphics, bool useBasicEffects = false)
         {
         {
             var model = SharpGLTF.Schema2.ModelRoot.Load(filePath, SharpGLTF.Validation.ValidationMode.TryFix);
             var model = SharpGLTF.Schema2.ModelRoot.Load(filePath, SharpGLTF.Validation.ValidationMode.TryFix);
 
 
             return ReadModel(model, graphics, useBasicEffects);
             return ReadModel(model, graphics, useBasicEffects);
         }
         }
 
 
-        public static ModelTemplate LoadModel(System.IO.FileInfo finfo, GraphicsDevice graphics, bool useBasicEffects = false)
+        public static ModelTemplateContent LoadModel(System.IO.FileInfo finfo, GraphicsDevice graphics, bool useBasicEffects = false)
         {
         {
             var model = SharpGLTF.Schema2.ModelRoot.Load(finfo.FullName, SharpGLTF.Validation.ValidationMode.TryFix);
             var model = SharpGLTF.Schema2.ModelRoot.Load(finfo.FullName, SharpGLTF.Validation.ValidationMode.TryFix);
 
 
             return ReadModel(model, graphics, useBasicEffects);
             return ReadModel(model, graphics, useBasicEffects);
         }
         }
 
 
-        public static ModelTemplate ReadModel(SharpGLTF.Schema2.ModelRoot model, GraphicsDevice graphics, bool useBasicEffects = false)
+        public static ModelTemplateContent ReadModel(SharpGLTF.Schema2.ModelRoot model, GraphicsDevice graphics, bool useBasicEffects = false)
         {
         {
             var factory = useBasicEffects ? (GLTFMeshFactory)new BasicMeshFactory(graphics) : new PBRMeshFactory(graphics);
             var factory = useBasicEffects ? (GLTFMeshFactory)new BasicMeshFactory(graphics) : new PBRMeshFactory(graphics);
 
 
             return ConvertToXna(model, factory);
             return ConvertToXna(model, factory);
         }
         }
 
 
-        public static ModelTemplate ConvertToXna(SharpGLTF.Schema2.ModelRoot srcModel, GLTFMeshFactory meshFactory)
+        public static ModelTemplateContent ConvertToXna(SharpGLTF.Schema2.ModelRoot srcModel, GLTFMeshFactory meshFactory)
         {
         {
             if (meshFactory == null) throw new ArgumentNullException();
             if (meshFactory == null) throw new ArgumentNullException();
 
 
@@ -56,7 +56,7 @@ namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
                 layers.Add(layer);
                 layers.Add(layer);
             }
             }
 
 
-            return new ModelTemplate(meshCollection, layers.ToArray(), srcModel.DefaultScene.LogicalIndex);
+            return new ModelTemplateContent(meshCollection, layers.ToArray(), srcModel.DefaultScene.LogicalIndex);
         }       
         }       
     }
     }
 }
 }

+ 2 - 2
src/MonoGame.Framework.Graphics.GLTF/MonoGame.Framework.Pipeline.GLTF.csproj

@@ -6,8 +6,8 @@
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="..\MonoGame.Framework.Graphics.PBR\MonoGame.Framework.Graphics.PBR.csproj" />
-    <ProjectReference Include="..\MonoGame.Framework.Graphics.Toolkit3D\MonoGame.Framework.Graphics.Toolkit3D.csproj" />
+    <ProjectReference Include="..\MonoGame.Framework.Graphics.PBR\MonoGame.Framework.Graphics.EffectsPBR.csproj" />
+    <ProjectReference Include="..\MonoGame.Framework.Graphics.Toolkit3D\MonoGame.Framework.Graphics.Model3D.csproj" />
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>

+ 0 - 2
src/MonoGame.Framework.Graphics.PBR/Interfaces.cs

@@ -2,8 +2,6 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Text;
 using System.Text;
 
 
-using Microsoft.Xna.Framework;
-
 namespace Microsoft.Xna.Framework.Graphics
 namespace Microsoft.Xna.Framework.Graphics
 {
 {
     public interface IEffectBones // it could be great if SkinnedEffect implemented this.
     public interface IEffectBones // it could be great if SkinnedEffect implemented this.

+ 4 - 0
src/MonoGame.Framework.Graphics.PBR/MonoGame.Framework.Graphics.PBR.csproj → src/MonoGame.Framework.Graphics.PBR/MonoGame.Framework.Graphics.EffectsPBR.csproj

@@ -11,6 +11,10 @@
 
 
   <ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Effects\Shaders\Resources\*.mgfxo" />
     <EmbeddedResource Include="Effects\Shaders\Resources\*.mgfxo" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Remove="PBREnvironment.cs" />
   </ItemGroup>  
   </ItemGroup>  
 
 
   <ItemGroup>
   <ItemGroup>

+ 200 - 0
src/MonoGame.Framework.Graphics.Scene3D/ModelDrawingContext.cs

@@ -0,0 +1,200 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.Xna.Framework.Graphics
+{
+    /// <summary>
+    /// Helper class for rendering <see cref="ModelLayerInstance"/> models.
+    /// </summary>
+    public struct ModelDrawingContext
+    {
+        #region lifecycle
+
+        public ModelDrawingContext(GraphicsDevice graphics)
+        {
+            _Device = graphics;
+
+            _Device.DepthStencilState = DepthStencilState.Default;            
+
+            float fieldOfView = MathHelper.PiOver4;
+            float nearClipPlane = 1f;
+                    
+            _Projection = CreatePerspectiveFieldOfView(fieldOfView, graphics.Viewport.AspectRatio, nearClipPlane, float.PositiveInfinity);
+
+            _View = Matrix.Invert(Matrix.Identity);
+            _DistanceComparer = ModelLayerInstance.GetDistanceComparer(-_View.Translation);
+        }
+
+        #endregion
+
+        #region data
+
+        private GraphicsDevice _Device;
+        private Matrix _Projection;
+        private Matrix _View;
+        private IComparer<ModelLayerInstance> _DistanceComparer;
+
+        private static readonly HashSet<Effect> _SceneEffects = new HashSet<Effect>();
+        private static readonly List<ModelLayerInstance> _SceneInstances = new List<ModelLayerInstance>();
+
+        #endregion
+
+        #region API
+
+        public void SetCamera(Matrix cameraMatrix)
+        {
+            _View = Matrix.Invert(cameraMatrix);
+
+            _DistanceComparer = ModelLayerInstance.GetDistanceComparer(-_View.Translation);
+        }
+
+        public void SetProjection(Matrix projectionMatrix)
+        {
+            _Projection = projectionMatrix;
+        }
+
+        public void DrawMesh(PBREnvironment environment, RuntimeModelMesh mesh, Matrix worldMatrix)
+        {
+            foreach (var e in mesh.OpaqueEffects)
+            {               
+                ModelLayerInstance.UpdateProjViewTransforms(e, _Projection, _View);
+                ModelLayerInstance.UpdateWorldTransforms(e, worldMatrix);
+                environment.ApplyTo(e);
+            }
+
+            mesh.DrawOpaque();
+
+            foreach (var e in mesh.TranslucidEffects)
+            {                
+                ModelLayerInstance.UpdateProjViewTransforms(e, _Projection, _View);
+                ModelLayerInstance.UpdateWorldTransforms(e, worldMatrix);
+                environment.ApplyTo(e);
+            }
+
+            mesh.DrawTranslucid();
+        }
+
+        /// <summary>
+        /// Draw a single model instance
+        /// </summary>
+        /// <param name="environment">Defines the athmospheric and lighting environment to use for the render.</param>
+        /// <param name="modelInstance">Defines the instance that is going to be rendered.</param>
+        /// <remarks>
+        /// Rendering models one by one is accepted, but some features like translucent parts sortings will not work
+        /// unless you manually render the models in the correct order.
+        /// </remarks>
+        public void DrawModelInstance(PBREnvironment environment, ModelLayerInstance modelInstance)
+        {
+            foreach (var e in modelInstance.Template.SharedEffects)
+            {
+                environment.ApplyTo(e);
+                ModelLayerInstance.UpdateProjViewTransforms(e, _Projection, _View);
+            }
+
+            modelInstance.DrawAllParts(_Projection, _View);
+        }
+
+        /// <summary>
+        /// Draws a batch of model instances.
+        /// </summary>
+        /// <param name="environment">Defines the athmospheric and lighting environment to use for the render.</param>
+        /// <param name="modelInstances">A batch of model instances.</param>
+        /// <remarks>
+        /// Rendering multiple models in a batch has a number of advantages over rendering models one by one:
+        /// - It allows splitting the rendering between opaque and translucent parts, which are rendered in the correct
+        ///   order to preserve rendering correctness.
+        /// - Less redundant calls.
+        /// - Futher optimizations are possible, like batching instances that share the same template model in a single
+        ///   drawing call.
+        /// - Possibility to add shadows, where some instances cast shadows over others.
+        /// </remarks>
+        public void DrawSceneInstances(PBREnvironment environment, params ModelLayerInstance[] modelInstances)
+        {
+            // todo: fustrum culling goes here
+
+            _SceneInstances.Clear();
+            _SceneInstances.AddRange(modelInstances);
+            _SceneInstances.Sort(_DistanceComparer);
+
+            // gather all effects from all visible instances.
+            _SceneEffects.Clear();
+            _SceneEffects.UnionWith(_SceneInstances.SelectMany(item => item.Template.SharedEffects));
+
+            // set Projection & View on all visible effects.
+
+            foreach (var e in _SceneEffects)
+            {
+                ModelLayerInstance.UpdateProjViewTransforms(e, _Projection, _View);
+                // todo: set env.Exposure
+                // todo: set env.AmbientLight
+            }
+
+            // todo: find the closest lights for each visible instance.
+
+            // render opaque parts from closest to farthest
+
+            foreach (var instance in _SceneInstances)
+            {
+                foreach (var e in instance.Template.SharedEffects) environment.ApplyTo(e);
+                instance.DrawOpaqueParts();
+            }
+
+            // render translucid parts from farthest to closest
+
+            _SceneInstances.Reverse();
+
+            foreach (var instance in _SceneInstances)
+            {
+                foreach (var e in instance.Template.SharedEffects) environment.ApplyTo(e);
+                instance.DrawTranslucidParts();
+            }
+        }
+
+        #endregion
+
+        #region helpers
+
+        public static Matrix CreatePerspectiveFieldOfView(float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance)
+        {
+            CreatePerspectiveFieldOfView(fieldOfView, aspectRatio, nearPlaneDistance, farPlaneDistance, out Matrix m);
+            return m;
+        }
+
+        // Microsoft recently updated this method in System.Numerics.Vectors to support farPlaneDistance infinity
+        // https://github.com/dotnet/runtime/blob/e64bc548c609455652fcd4107f1f4a2ac3084ff3/src/libraries/System.Private.CoreLib/src/System/Numerics/Matrix4x4.cs#L860
+        public static void CreatePerspectiveFieldOfView(float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance, out Matrix result)
+        {
+            if (fieldOfView <= 0.0f || fieldOfView >= MathHelper.Pi)
+                throw new ArgumentOutOfRangeException(nameof(fieldOfView));
+
+            if (nearPlaneDistance <= 0.0f)
+                throw new ArgumentOutOfRangeException(nameof(nearPlaneDistance));
+
+            if (farPlaneDistance <= 0.0f)
+                throw new ArgumentOutOfRangeException(nameof(farPlaneDistance));
+
+            if (nearPlaneDistance >= farPlaneDistance)
+                throw new ArgumentOutOfRangeException(nameof(nearPlaneDistance));
+
+            float yScale = 1.0f / (float)Math.Tan(fieldOfView * 0.5f);
+            float xScale = yScale / aspectRatio;            
+
+            result.M11 = xScale;
+            result.M12 = result.M13 = result.M14 = 0.0f;
+
+            result.M22 = yScale;
+            result.M21 = result.M23 = result.M24 = 0.0f;
+
+            result.M31 = result.M32 = 0.0f;
+            float negFarRange = float.IsPositiveInfinity(farPlaneDistance) ? -1.0f : farPlaneDistance / (nearPlaneDistance - farPlaneDistance);
+            result.M33 = negFarRange;
+            result.M34 = -1.0f;
+
+            result.M41 = result.M42 = result.M44 = 0.0f;
+            result.M43 = nearPlaneDistance * negFarRange;            
+        }
+
+        #endregion
+    }
+}

+ 23 - 0
src/MonoGame.Framework.Graphics.Scene3D/MonoGame.Framework.Graphics.Scene3D.csproj

@@ -0,0 +1,23 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFrameworks>netstandard2.0;net452</TargetFrameworks>
+    <RootNamespace>Microsoft.Xna.Framework.Graphics</RootNamespace>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.0.1375-develop" PrivateAssets="all" />
+    <PackageReference Include="System.Memory" Version="4.5.4" />
+  </ItemGroup>
+
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net452' ">
+    <PackageReference Include="System.ValueTuple" Version="4.5.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\MonoGame.Framework.Graphics.PBR\MonoGame.Framework.Graphics.EffectsPBR.csproj" />
+    <ProjectReference Include="..\MonoGame.Framework.Graphics.Toolkit3D\MonoGame.Framework.Graphics.Model3D.csproj" />
+  </ItemGroup>
+
+</Project>

+ 64 - 0
src/MonoGame.Framework.Graphics.Scene3D/PBREnvironment.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.Xna.Framework.Graphics
+{
+    /// <summary>
+    /// Defines all athmospheric and lighting properties for a given scene setup.
+    /// </summary>
+    public class PBREnvironment
+    {
+        #region constants
+
+        public static PBREnvironment CreateDefault()
+        {
+            var env = new PBREnvironment();
+            env.SetDirectLight(0, (20, 30), Color.White, 3.5f);
+            env.SetDirectLight(1, (-70, 60), Color.DeepSkyBlue, 1.5f);
+            env.SetDirectLight(2, (50, -50), Color.OrangeRed, 0.5f);
+            return env;
+        }
+
+        #endregion
+
+        #region data
+
+        private float _Exposure = 2.5f;
+        private Vector3 _AmbientLight = Vector3.Zero;
+        private readonly List<PBRPunctualLight> _PunctualLights = new List<PBRPunctualLight>();
+
+        #endregion
+
+        #region API
+
+        public void SetExposure(float exposure) { _Exposure = exposure; }
+
+        public void SetAmbientLight(Vector3 color) { _AmbientLight = color; }
+
+        public void SetDirectLight(int idx, (int direction, int elevation) degrees, Color color, float intensity)
+        {
+            _SetPunctualLight(idx, PBRPunctualLight.Directional(degrees, color.ToVector3(), intensity));
+        }
+
+        public void SetDirectLight(int idx, Vector3 direction, Color color, float intensity)
+        {
+            _SetPunctualLight(idx, PBRPunctualLight.Directional(direction, color.ToVector3(), intensity));
+        }
+
+        private void _SetPunctualLight(int idx, PBRPunctualLight l)
+        {
+            while (_PunctualLights.Count <= idx) _PunctualLights.Add(default);
+            _PunctualLights[idx] = l;
+        }
+
+        public void ApplyTo(Effect effect)
+        {
+            if (effect is IEffectFog fog) { fog.FogEnabled = false; }
+
+            PBRPunctualLight.ApplyLights(effect, _Exposure, _AmbientLight, _PunctualLights);
+        }
+
+        #endregion
+    }
+}

+ 26 - 3
src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/Meshes/MeshCollection.cs

@@ -1,12 +1,18 @@
 using System;
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Text;
 using System.Text;
 
 
 namespace Microsoft.Xna.Framework.Graphics
 namespace Microsoft.Xna.Framework.Graphics
 {
 {
+    public interface IMeshCollection : IReadOnlyList<RuntimeModelMesh>
+    {
+        Effect[] GetSharedEffects(IEnumerable<int> meshIndices);
+    }
+
     [System.Diagnostics.DebuggerDisplay("{Count} Meshes {SharedEffects.Count} Shared effects.")]
     [System.Diagnostics.DebuggerDisplay("{Count} Meshes {SharedEffects.Count} Shared effects.")]
-    public class MeshCollection : IDisposable
+    public class MeshCollection : IDisposable, IMeshCollection
     {
     {
         #region lifecycle
         #region lifecycle
 
 
@@ -29,7 +35,7 @@ namespace Microsoft.Xna.Framework.Graphics
             }
             }
 
 
             _Disposables = null;            
             _Disposables = null;            
-        }
+        }        
 
 
         #endregion
         #endregion
 
 
@@ -48,8 +54,25 @@ namespace Microsoft.Xna.Framework.Graphics
         public int Count => _Meshes.Length;
         public int Count => _Meshes.Length;
 
 
         public RuntimeModelMesh this[int index] => _Meshes[index];
         public RuntimeModelMesh this[int index] => _Meshes[index];
+        
+        #endregion
+
+        #region API
+
+        public IEnumerator<RuntimeModelMesh> GetEnumerator() { return (IEnumerator<RuntimeModelMesh>)_Meshes.GetEnumerator(); }
 
 
-        public IReadOnlyCollection<Effect> SharedEffects => _SharedEffects;
+        IEnumerator IEnumerable.GetEnumerator() { return _Meshes.GetEnumerator(); }
+
+        public Effect[] GetSharedEffects(IEnumerable<int> meshIndices)
+        {
+            // gather all effects used by the meshes indexed by meshIndices.
+
+            return meshIndices
+                .Select(item => _Meshes[item])
+                .SelectMany(item => item.OpaqueEffects.Concat(item.TranslucidEffects))
+                .Distinct()
+                .ToArray();
+        }
 
 
         #endregion
         #endregion
     }
     }

+ 84 - 0
src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/Meshes/MeshGeometry.cs

@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.Xna.Framework.Graphics
+{
+    public class MeshGeometry
+    {
+        #region lifecycle       
+
+        public void SetVertexBuffer(VertexBuffer vb, int offset, int count)
+        {
+            this._SharedVertexBuffer = vb;
+            this._VertexOffset = offset;
+            this._VertexCount = count;
+        }
+
+        public void SetIndexBuffer(IndexBuffer ib, int offset, int count)
+        {
+            this._SharedIndexBuffer = ib;
+            this._IndexOffset = offset;
+            this._PrimitiveCount = count;
+        }
+
+        public void SetCullingStates(bool doubleSided)
+        {
+            FrontRasterizer = doubleSided ? RasterizerState.CullNone : RasterizerState.CullCounterClockwise;
+            BackRasterizer = doubleSided ? RasterizerState.CullNone : RasterizerState.CullClockwise;
+        }
+
+        #endregion
+
+        #region data
+
+        // state used for normal rendering
+        private RasterizerState _FrontRasterizer = RasterizerState.CullCounterClockwise;
+
+        // state used for mirrored tranform. This must be the same as _FrontRasterizer with reversed CullMode.
+        private RasterizerState _BackRasterizer = RasterizerState.CullClockwise;
+
+        private IndexBuffer _SharedIndexBuffer;
+        private int _IndexOffset;
+        private int _PrimitiveCount;
+
+        private VertexBuffer _SharedVertexBuffer;
+        private int _VertexOffset;
+        private int _VertexCount;
+
+        #endregion
+
+        #region properties        
+
+        public RasterizerState FrontRasterizer
+        {
+            get => _FrontRasterizer;
+            set => _FrontRasterizer = value;
+        }
+
+        public RasterizerState BackRasterizer
+        {
+            get => _BackRasterizer;
+            set => _BackRasterizer = value;
+        }
+
+        #endregion
+
+        #region API
+
+        public void Bind(GraphicsDevice device, bool isMirrorTransform)
+        {
+            device.SetVertexBuffer(_SharedVertexBuffer);
+            device.Indices = _SharedIndexBuffer;
+
+            device.RasterizerState = isMirrorTransform ? _BackRasterizer : _FrontRasterizer;
+        }
+
+        public void Draw(GraphicsDevice device)
+        {
+            device.DrawIndexedPrimitives(PrimitiveType.TriangleList, _VertexOffset, _IndexOffset, _PrimitiveCount);            
+        }
+
+        #endregion
+    }
+}

+ 16 - 54
src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/Meshes/MeshPart.cs

@@ -16,21 +16,7 @@ namespace Microsoft.Xna.Framework.Graphics
         internal RuntimeModelMeshPart(RuntimeModelMesh parent)
         internal RuntimeModelMeshPart(RuntimeModelMesh parent)
         {
         {
             _Parent = parent;
             _Parent = parent;
-        }
-
-        internal void SetVertexBuffer(VertexBuffer vb, int offset, int count)
-        {
-            this._SharedVertexBuffer = vb;
-            this._VertexOffset = offset;
-            this._VertexCount = count;
-        }
-
-        internal void SetIndexBuffer(IndexBuffer ib, int offset, int count)
-        {
-            this._SharedIndexBuffer = ib;
-            this._IndexOffset = offset;
-            this._PrimitiveCount = count;
-        }
+        }        
 
 
         #endregion
         #endregion
 
 
@@ -39,27 +25,13 @@ namespace Microsoft.Xna.Framework.Graphics
         private readonly RuntimeModelMesh _Parent;
         private readonly RuntimeModelMesh _Parent;
 
 
         private Effect _Effect;
         private Effect _Effect;
-        private BlendState _Blend = BlendState.Opaque;        
-
-        // state used for normal rendering
-        private RasterizerState _FrontRasterizer = RasterizerState.CullCounterClockwise;
-
-        // state used for mirrored tranform. This must be the same as _FrontRasterizer with reversed CullMode.
-        private RasterizerState _BackRasterizer = RasterizerState.CullClockwise;
-
-        private IndexBuffer _SharedIndexBuffer;
-        private int _IndexOffset;
-        private int _PrimitiveCount;        
+        private BlendState _Blend = BlendState.Opaque;
 
 
-        private VertexBuffer _SharedVertexBuffer;
-        private int _VertexOffset;
-        private int _VertexCount;       
+        private MeshGeometry _Geometry;
 
 
         #endregion
         #endregion
 
 
-        #region properties
-
-        public GraphicsDevice Device => _Parent._GraphicsDevice;
+        #region properties        
 
 
         public Effect Effect
         public Effect Effect
         {
         {
@@ -77,17 +49,11 @@ namespace Microsoft.Xna.Framework.Graphics
             get => _Blend;
             get => _Blend;
             set => _Blend = value;
             set => _Blend = value;
         }
         }
-
-        public RasterizerState FrontRasterizer
-        {
-            get => _FrontRasterizer;
-            set => _FrontRasterizer = value;
-        }
-
-        public RasterizerState BackRasterizer
+        
+        public MeshGeometry Geometry
         {
         {
-            get => _BackRasterizer;
-            set => _BackRasterizer = value;
+            get => _Geometry;
+            set => _Geometry = value;
         }
         }
 
 
         #endregion
         #endregion
@@ -98,20 +64,16 @@ namespace Microsoft.Xna.Framework.Graphics
         {
         {
             bool isMirrorTransform = _Effect is AnimatedEffect animEffect && animEffect.WorldIsMirror;
             bool isMirrorTransform = _Effect is AnimatedEffect animEffect && animEffect.WorldIsMirror;
 
 
-            if (_PrimitiveCount > 0)
-            {
-                device.SetVertexBuffer(_SharedVertexBuffer);
-                device.Indices = _SharedIndexBuffer;
+            _Geometry.Bind(device, isMirrorTransform);            
 
 
-                device.BlendState = _Blend;
-                device.RasterizerState = isMirrorTransform ? _BackRasterizer : _FrontRasterizer;
+            device.BlendState = _Blend;            
 
 
-                foreach(var pass in _Effect.CurrentTechnique.Passes)
-                {
-                    pass.Apply();
-                    device.DrawIndexedPrimitives(PrimitiveType.TriangleList, _VertexOffset, _IndexOffset, _PrimitiveCount);
-                }
-            }
+            foreach(var pass in _Effect.CurrentTechnique.Passes)
+            {
+                pass.Apply();
+
+                _Geometry.Draw(device);
+            }            
         }
         }
 
 
         #endregion
         #endregion

+ 1 - 1
src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelDrawingContext.cs

@@ -93,7 +93,7 @@ namespace Microsoft.Xna.Framework.Graphics
                 ModelLayerInstance.UpdateProjViewTransforms(e, _Projection, _View);
                 ModelLayerInstance.UpdateProjViewTransforms(e, _Projection, _View);
             }
             }
 
 
-            modelInstance.Draw(_Projection, _View);
+            modelInstance.DrawAllParts(_Projection, _View);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 3 - 1
src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelGraph/ArmatureInstance.cs

@@ -25,10 +25,12 @@ namespace Microsoft.Xna.Framework.Graphics
             _Template = armature;
             _Template = armature;
             _NodeInstances = new NodeInstance[armature.Count];
             _NodeInstances = new NodeInstance[armature.Count];
 
 
+            // no need to check arguments since they're supposedly pre-checked by ArmatureTemplate's constructor.
+
             for (var i = 0; i < _NodeInstances.Length; ++i)
             for (var i = 0; i < _NodeInstances.Length; ++i)
             {
             {
                 var n = armature[i];
                 var n = armature[i];
-                var pidx = armature[i].ParentIndex;
+                var pidx = n.ParentIndex;
                 var p = pidx < 0 ? null : _NodeInstances[pidx];
                 var p = pidx < 0 ? null : _NodeInstances[pidx];
                 _NodeInstances[i] = new NodeInstance(n, p);
                 _NodeInstances[i] = new NodeInstance(n, p);
             }            
             }            

+ 13 - 4
src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelGraph/ArmatureTemplate.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Linq;
 using System.Text;
 using System.Text;
 
 
 using Microsoft.Xna.Framework.Graphics.Graphics.ModelGraph;
 using Microsoft.Xna.Framework.Graphics.Graphics.ModelGraph;
@@ -19,17 +20,25 @@ namespace Microsoft.Xna.Framework.Graphics
         {
         {
             if (nodes == null) throw new ArgumentNullException(nameof(nodes));
             if (nodes == null) throw new ArgumentNullException(nameof(nodes));
 
 
+            // check that child nodes always follow parent nodes
+
             for(int i=0; i < nodes.Length; ++i)
             for(int i=0; i < nodes.Length; ++i)
             {
             {
                 var n = nodes[i];
                 var n = nodes[i];
-                if (n == null) throw new ArgumentNullException($"{nameof(nodes)}[{i}] is null.");
 
 
-                var parentIndex = n.ParentIndex;
-                if (parentIndex >= i) throw new ArgumentException($"{nameof(nodes)}[{i}].ParentIndex must point to a preceding node in the array.");
+                if (n == null) throw new ArgumentNullException(nameof(nodes));                
+                if (n.ParentIndex >= i) throw new ArgumentOutOfRangeException(nameof(nodes), $"[{i}].ParentIndex must be lower than {i}, but found {n.ParentIndex}");
+
+                for(int j=0; j < n.ChildIndices.Count; ++j)
+                {
+                    var cidx = n.ChildIndices[j];
+                    if (cidx >= nodes.Length) throw new ArgumentOutOfRangeException(nameof(nodes), $"[{i}].ChildIndices[{j}] must be lower than {nodes.Length}, but found {cidx}");
+                    if (cidx <= i) throw new ArgumentOutOfRangeException(nameof(nodes), $"[{i}].ChildIndices[{j}] must be heigher than {i}, but found {cidx}");
+                }                
             }
             }
 
 
             _NodeTemplates = nodes;
             _NodeTemplates = nodes;
-            _AnimationTracks = atracks == null ? new AnimationTrackInfo[0] : atracks;
+            _AnimationTracks = atracks ?? (new AnimationTrackInfo[0]);
         }
         }
 
 
         #endregion
         #endregion

+ 16 - 9
src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelGraph/ModelLayerInstance.cs

@@ -18,7 +18,7 @@ namespace Microsoft.Xna.Framework.Graphics
         internal ModelLayerInstance(ModelLayerTemplate parent)
         internal ModelLayerInstance(ModelLayerTemplate parent)
         {
         {
             _Parent = parent;
             _Parent = parent;
-
+            
             _Armature = new ArmatureInstance(_Parent._Armature);
             _Armature = new ArmatureInstance(_Parent._Armature);
             _Armature.SetPoseTransforms();
             _Armature.SetPoseTransforms();
 
 
@@ -38,15 +38,15 @@ namespace Microsoft.Xna.Framework.Graphics
         #region data        
         #region data        
 
 
         private readonly ModelLayerTemplate _Parent;
         private readonly ModelLayerTemplate _Parent;
+
+        private IMeshCollection _Meshes => _Parent.Meshes;
+
         private readonly ArmatureInstance _Armature;
         private readonly ArmatureInstance _Armature;
 
 
         private readonly IDrawableTemplate[] _DrawableTemplates;
         private readonly IDrawableTemplate[] _DrawableTemplates;
         private readonly IMeshTransform[] _DrawableTransforms;
         private readonly IMeshTransform[] _DrawableTransforms;
 
 
-        private Matrix _WorldMatrix;
-
-        // pre-allocated bone arrays to update the IEffectBones
-        private static readonly List<Matrix[]> _BoneArrays = new List<Matrix[]>();
+        private Matrix _WorldMatrix;        
 
 
         #endregion
         #endregion
 
 
@@ -106,7 +106,7 @@ namespace Microsoft.Xna.Framework.Graphics
         /// </summary>
         /// </summary>
         /// <param name="nodeIndex">The index of the node/bone.</param>
         /// <param name="nodeIndex">The index of the node/bone.</param>
         /// <returns>A matrix in world space.</returns>
         /// <returns>A matrix in world space.</returns>
-        public Matrix GetWorldMatrix(int nodeIndex) { return _Armature.LogicalNodes[nodeIndex].ModelMatrix * _WorldMatrix; }
+        public Matrix GetWorldMatrix(int nodeIndex) { return GetModelMatrix(nodeIndex) * _WorldMatrix; }
 
 
         #endregion
         #endregion
 
 
@@ -134,7 +134,7 @@ namespace Microsoft.Xna.Framework.Graphics
         /// </summary>
         /// </summary>
         /// <param name="projection">The projection matrix.</param>
         /// <param name="projection">The projection matrix.</param>
         /// <param name="view">The view matrix.</param>        
         /// <param name="view">The view matrix.</param>        
-        public void Draw(Matrix projection, Matrix view)
+        public void DrawAllParts(Matrix projection, Matrix view)
         {
         {
             foreach (var e in this.Template.SharedEffects)
             foreach (var e in this.Template.SharedEffects)
             {
             {
@@ -152,7 +152,7 @@ namespace Microsoft.Xna.Framework.Graphics
         {
         {
             foreach (var d in DrawableInstances)
             foreach (var d in DrawableInstances)
             {
             {
-                var mesh = _Parent.Meshes[d.Template.MeshIndex];
+                var mesh = _Meshes[d.Template.MeshIndex];
                 if (mesh.TranslucidEffects.Count == 0) continue;
                 if (mesh.TranslucidEffects.Count == 0) continue;
 
 
                 SetEffectsTransforms(mesh.TranslucidEffects, _WorldMatrix, d.Transform);
                 SetEffectsTransforms(mesh.TranslucidEffects, _WorldMatrix, d.Transform);
@@ -165,7 +165,7 @@ namespace Microsoft.Xna.Framework.Graphics
         {
         {
             foreach (var d in DrawableInstances)
             foreach (var d in DrawableInstances)
             {
             {
-                var mesh = _Parent.Meshes[d.Template.MeshIndex];
+                var mesh = _Meshes[d.Template.MeshIndex];
                 if (mesh.OpaqueEffects.Count == 0) continue;
                 if (mesh.OpaqueEffects.Count == 0) continue;
 
 
                 SetEffectsTransforms(mesh.OpaqueEffects, _WorldMatrix, d.Transform);
                 SetEffectsTransforms(mesh.OpaqueEffects, _WorldMatrix, d.Transform);
@@ -214,6 +214,13 @@ namespace Microsoft.Xna.Framework.Graphics
             }
             }
         }
         }
 
 
+        #endregion
+
+        #region effect utils
+
+        // pre-allocated bone arrays to update the IEffectBones
+        private static readonly List<Matrix[]> _BoneArrays = new List<Matrix[]>();
+
         // Since SkinnedEffect has such a flexible and GC friendly API,
         // Since SkinnedEffect has such a flexible and GC friendly API,
         // we have to do this to have a reusable bone matrix pool.
         // we have to do this to have a reusable bone matrix pool.
         private static Matrix[] UseArray(int count)
         private static Matrix[] UseArray(int count)

+ 4 - 8
src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelGraph/ModelLayerTemplate.cs

@@ -32,7 +32,7 @@ namespace Microsoft.Xna.Framework.Graphics
 
 
         internal readonly ArmatureTemplate _Armature;
         internal readonly ArmatureTemplate _Armature;
         
         
-        private MeshCollection _Meshes;        
+        private IMeshCollection _Meshes;        
 
 
         // this is the collection of "what needs to be rendered", and it binds meshes with armatures
         // this is the collection of "what needs to be rendered", and it binds meshes with armatures
         internal readonly IDrawableTemplate[] _DrawableReferences;
         internal readonly IDrawableTemplate[] _DrawableReferences;
@@ -47,7 +47,7 @@ namespace Microsoft.Xna.Framework.Graphics
 
 
         public BoundingSphere ModelBounds { get; set; }
         public BoundingSphere ModelBounds { get; set; }
 
 
-        public MeshCollection Meshes
+        public IMeshCollection Meshes
         {
         {
             get => _Meshes;
             get => _Meshes;
             set
             set
@@ -63,12 +63,8 @@ namespace Microsoft.Xna.Framework.Graphics
             {
             {
                 if (_SharedEffects != null) return _SharedEffects;
                 if (_SharedEffects != null) return _SharedEffects;
 
 
-                // gather all effects used by all the meshes used by all the drawable calls in this layer.
-                _SharedEffects = _DrawableReferences                    
-                    .Select(item => _Meshes[item.MeshIndex])
-                    .SelectMany(item => item.OpaqueEffects.Concat(item.TranslucidEffects))
-                    .Distinct()
-                    .ToArray();
+                var meshIndices = _DrawableReferences.Select(item => item.MeshIndex);
+                _SharedEffects = _Meshes.GetSharedEffects(meshIndices);
 
 
                 return _SharedEffects;
                 return _SharedEffects;
             }
             }

+ 31 - 10
src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelGraph/ModelTemplate.cs

@@ -7,15 +7,12 @@ using MODELMESH = Microsoft.Xna.Framework.Graphics.RuntimeModelMesh;
 
 
 namespace Microsoft.Xna.Framework.Graphics
 namespace Microsoft.Xna.Framework.Graphics
 {
 {
-    public class ModelTemplate : IDisposable
+    public class ModelTemplateContent : ModelTemplate, IDisposable
     {
     {
         #region lifecycle
         #region lifecycle
 
 
-        public ModelTemplate(MeshCollection meshes, ModelLayerTemplate[] layers, int defaultLayer)
-        {            
-            _Layers = layers;
-            _DefaultLayerIndex = defaultLayer;
-
+        public ModelTemplateContent(MeshCollection meshes, ModelLayerTemplate[] layers, int defaultLayer) :base(meshes,layers,defaultLayer)
+        {
             SharedMeshes = meshes;
             SharedMeshes = meshes;
         }
         }
 
 
@@ -23,7 +20,6 @@ namespace Microsoft.Xna.Framework.Graphics
         {
         {
             _SharedMeshes?.Dispose();
             _SharedMeshes?.Dispose();
             _SharedMeshes = null;
             _SharedMeshes = null;
-            _Layers = null;
         }
         }
 
 
         #endregion
         #endregion
@@ -33,12 +29,37 @@ namespace Microsoft.Xna.Framework.Graphics
         /// <summary>
         /// <summary>
         /// Meshes shared by all the <see cref="_Layers"/>.
         /// Meshes shared by all the <see cref="_Layers"/>.
         /// </summary>
         /// </summary>
-        internal MeshCollection _SharedMeshes;        
+        private MeshCollection _SharedMeshes;
+
+        #endregion
+    }
+
+
+    public class ModelTemplate
+    {
+        #region lifecycle
+
+        public ModelTemplate(IMeshCollection meshes, ModelLayerTemplate[] layers, int defaultLayer)
+        {            
+            _Layers = layers;
+            _DefaultLayerIndex = defaultLayer;
+
+            SharedMeshes = meshes;
+        }        
+
+        #endregion
+
+        #region data
+
+        /// <summary>
+        /// Meshes shared by all the <see cref="_Layers"/>.
+        /// </summary>
+        private IMeshCollection _SharedMeshes;        
 
 
         /// <summary>
         /// <summary>
         /// Layers available in this template
         /// Layers available in this template
         /// </summary>
         /// </summary>
-        internal ModelLayerTemplate[] _Layers;        
+        private ModelLayerTemplate[] _Layers;        
 
 
         /// <summary>
         /// <summary>
         /// Default layer index
         /// Default layer index
@@ -53,7 +74,7 @@ namespace Microsoft.Xna.Framework.Graphics
 
 
         public ModelLayerTemplate DefaultLayer => _Layers[_DefaultLayerIndex];
         public ModelLayerTemplate DefaultLayer => _Layers[_DefaultLayerIndex];
 
 
-        public MeshCollection SharedMeshes
+        public IMeshCollection SharedMeshes
         {
         {
             get => _SharedMeshes;
             get => _SharedMeshes;
             set
             set

+ 3 - 0
src/MonoGame.Framework.Graphics.Toolkit3D/ICurveEvaluator.cs

@@ -22,6 +22,9 @@ namespace Microsoft.Xna.Framework
     /// 3- In general, curves can be implemented in so many ways that demand an abstract interface.
     /// 3- In general, curves can be implemented in so many ways that demand an abstract interface.
     /// After all, animatable objects only need to evaluate the curve at a given time and don't care
     /// After all, animatable objects only need to evaluate the curve at a given time and don't care
     /// about the internal logic of the curve.
     /// about the internal logic of the curve.
+    /// 
+    /// For example: an extreme case of curve implementation is the case of Collada, which has curves controlled
+    /// by actual math formulas. As far as I know, it reached the collada schema, but I don't think nobody ever used it...
     /// </remarks>
     /// </remarks>
     public interface ICurveEvaluator<T>
     public interface ICurveEvaluator<T>
     {        
     {        

+ 6 - 2
src/MonoGame.Framework.Graphics.Toolkit3D/MonoGame.Framework.Graphics.Toolkit3D.csproj → src/MonoGame.Framework.Graphics.Toolkit3D/MonoGame.Framework.Graphics.Model3D.csproj

@@ -4,7 +4,11 @@
     <TargetFrameworks>netstandard2.0;net452</TargetFrameworks>
     <TargetFrameworks>netstandard2.0;net452</TargetFrameworks>
     <RootNamespace>Microsoft.Xna.Framework.Graphics</RootNamespace>
     <RootNamespace>Microsoft.Xna.Framework.Graphics</RootNamespace>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-  </PropertyGroup>  
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Compile Remove="Graphics\ModelDrawingContext.cs" />
+  </ItemGroup>  
 
 
   <ItemGroup>
   <ItemGroup>
     <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.0.1375-develop" PrivateAssets="all" />
     <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.0.1375-develop" PrivateAssets="all" />
@@ -16,7 +20,7 @@
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="..\MonoGame.Framework.Graphics.PBR\MonoGame.Framework.Graphics.PBR.csproj" />
+    <ProjectReference Include="..\MonoGame.Framework.Graphics.PBR\MonoGame.Framework.Graphics.EffectsPBR.csproj" />
   </ItemGroup>  
   </ItemGroup>  
 
 
 </Project>
 </Project>

+ 8 - 5
src/MonoGame.Framework.Graphics.Toolkit3D/Pipeline/MeshPrimitiveBuilder.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Threading;
 
 
 using Microsoft.Xna.Framework.Graphics;
 using Microsoft.Xna.Framework.Graphics;
 
 
@@ -67,15 +68,17 @@ namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
                 foreach (var srcPart in srcParts)
                 foreach (var srcPart in srcParts)
                 {
                 {
                     var vb = vbuffers[srcPart.PrimitiveBuffers];
                     var vb = vbuffers[srcPart.PrimitiveBuffers];
-                    var ib = ibuffers[srcPart.PrimitiveBuffers];
+                    var ib = ibuffers[srcPart.PrimitiveBuffers];                    
+
+                    var dstGeo = new MeshGeometry();
+                    dstGeo.SetCullingStates(srcPart.Material.DoubleSided);                    
+                    dstGeo.SetVertexBuffer(vb, srcPart.VertexOffset, srcPart.VertexCount);
+                    dstGeo.SetIndexBuffer(ib, srcPart.TriangleOffset * 3, srcPart.TriangleCount);
 
 
                     var dstPart = dstMesh.CreateMeshPart();
                     var dstPart = dstMesh.CreateMeshPart();
                     dstPart.Effect = srcPart.Material.PrimitiveEffect;
                     dstPart.Effect = srcPart.Material.PrimitiveEffect;
                     dstPart.Blending = srcPart.Material.PrimitiveBlending;
                     dstPart.Blending = srcPart.Material.PrimitiveBlending;
-                    dstPart.FrontRasterizer = srcPart.Material.DoubleSided ? RasterizerState.CullNone : RasterizerState.CullCounterClockwise;
-                    dstPart.BackRasterizer = srcPart.Material.DoubleSided ? RasterizerState.CullNone : RasterizerState.CullClockwise;                    
-                    dstPart.SetVertexBuffer(vb, srcPart.VertexOffset, srcPart.VertexCount);
-                    dstPart.SetIndexBuffer(ib, srcPart.TriangleOffset * 3, srcPart.TriangleCount);
+                    dstPart.Geometry = dstGeo;
                 }
                 }
 
 
                 return dstMesh;
                 return dstMesh;

+ 141 - 3
src/MonoGameScene.sln

@@ -9,9 +9,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpGLTF.Runtime.MonoGame"
 EndProject
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGameViewer", "MonoGameViewer\MonoGameViewer.csproj", "{15155062-6A75-48EC-B73A-A7DCCB5423B6}"
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGameViewer", "MonoGameViewer\MonoGameViewer.csproj", "{15155062-6A75-48EC-B73A-A7DCCB5423B6}"
 EndProject
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGame.Framework.Graphics.PBR", "MonoGame.Framework.Graphics.PBR\MonoGame.Framework.Graphics.PBR.csproj", "{6D3141B5-4747-4D58-B773-4EA96DA8887B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGame.Framework.Graphics.EffectsPBR", "MonoGame.Framework.Graphics.PBR\MonoGame.Framework.Graphics.EffectsPBR.csproj", "{6D3141B5-4747-4D58-B773-4EA96DA8887B}"
 EndProject
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGame.Framework.Graphics.Toolkit3D", "MonoGame.Framework.Graphics.Toolkit3D\MonoGame.Framework.Graphics.Toolkit3D.csproj", "{50241C0B-7275-4B2D-AA65-6D2EB643D138}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGame.Framework.Graphics.Model3D", "MonoGame.Framework.Graphics.Toolkit3D\MonoGame.Framework.Graphics.Model3D.csproj", "{50241C0B-7275-4B2D-AA65-6D2EB643D138}"
 EndProject
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGame.Framework.Pipeline.GLTF", "MonoGame.Framework.Graphics.GLTF\MonoGame.Framework.Pipeline.GLTF.csproj", "{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}"
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGame.Framework.Pipeline.GLTF", "MonoGame.Framework.Graphics.GLTF\MonoGame.Framework.Pipeline.GLTF.csproj", "{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}"
 EndProject
 EndProject
@@ -19,54 +19,192 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo1", "Demo1\Demo1.csproj
 EndProject
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo2", "Demo2\Demo2.csproj", "{9B0C0AF4-F505-4EFA-A707-6AE469224A98}"
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo2", "Demo2\Demo2.csproj", "{9B0C0AF4-F505-4EFA-A707-6AE469224A98}"
 EndProject
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo3", "Demo3\Demo3.csproj", "{C617C831-E727-411A-901B-0302C7CE5AA2}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo3", "Demo3\Demo3.csproj", "{C617C831-E727-411A-901B-0302C7CE5AA2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame.Framework.Graphics.Scene3D", "MonoGame.Framework.Graphics.Scene3D\MonoGame.Framework.Graphics.Scene3D.csproj", "{844F2DCB-B4F3-48BD-9412-D326008C3A1A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Obsolete", "Obsolete", "{174B99F1-9DF1-49DE-84C7-BFE019AE78DE}"
 EndProject
 EndProject
 Global
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
 		Debug|Any CPU = Debug|Any CPU
+		Debug|ARM = Debug|ARM
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
 		Release|Any CPU = Release|Any CPU
 		Release|Any CPU = Release|Any CPU
+		Release|ARM = Release|ARM
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Debug|ARM.Build.0 = Debug|Any CPU
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Debug|x64.Build.0 = Debug|Any CPU
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Debug|x86.Build.0 = Debug|Any CPU
 		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Release|Any CPU.Build.0 = Release|Any CPU
 		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Release|ARM.ActiveCfg = Release|Any CPU
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Release|ARM.Build.0 = Release|Any CPU
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Release|x64.ActiveCfg = Release|Any CPU
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Release|x64.Build.0 = Release|Any CPU
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Release|x86.ActiveCfg = Release|Any CPU
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97}.Release|x86.Build.0 = Release|Any CPU
 		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Debug|ARM.Build.0 = Debug|Any CPU
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Debug|x64.Build.0 = Debug|Any CPU
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Debug|x86.Build.0 = Debug|Any CPU
 		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Release|Any CPU.Build.0 = Release|Any CPU
 		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Release|ARM.ActiveCfg = Release|Any CPU
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Release|ARM.Build.0 = Release|Any CPU
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Release|x64.ActiveCfg = Release|Any CPU
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Release|x64.Build.0 = Release|Any CPU
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Release|x86.ActiveCfg = Release|Any CPU
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE}.Release|x86.Build.0 = Release|Any CPU
 		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Debug|ARM.Build.0 = Debug|Any CPU
+		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Debug|x64.Build.0 = Debug|Any CPU
+		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Debug|x86.Build.0 = Debug|Any CPU
 		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Release|Any CPU.Build.0 = Release|Any CPU
 		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Release|ARM.ActiveCfg = Release|Any CPU
+		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Release|ARM.Build.0 = Release|Any CPU
+		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Release|x64.ActiveCfg = Release|Any CPU
+		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Release|x64.Build.0 = Release|Any CPU
+		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Release|x86.ActiveCfg = Release|Any CPU
+		{15155062-6A75-48EC-B73A-A7DCCB5423B6}.Release|x86.Build.0 = Release|Any CPU
 		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Debug|ARM.Build.0 = Debug|Any CPU
+		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Debug|x64.Build.0 = Debug|Any CPU
+		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Debug|x86.Build.0 = Debug|Any CPU
 		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Release|Any CPU.Build.0 = Release|Any CPU
 		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Release|ARM.ActiveCfg = Release|Any CPU
+		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Release|ARM.Build.0 = Release|Any CPU
+		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Release|x64.ActiveCfg = Release|Any CPU
+		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Release|x64.Build.0 = Release|Any CPU
+		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Release|x86.ActiveCfg = Release|Any CPU
+		{6D3141B5-4747-4D58-B773-4EA96DA8887B}.Release|x86.Build.0 = Release|Any CPU
 		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Debug|ARM.Build.0 = Debug|Any CPU
+		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Debug|x64.Build.0 = Debug|Any CPU
+		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Debug|x86.Build.0 = Debug|Any CPU
 		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Release|Any CPU.Build.0 = Release|Any CPU
 		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Release|Any CPU.Build.0 = Release|Any CPU
+		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Release|ARM.ActiveCfg = Release|Any CPU
+		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Release|ARM.Build.0 = Release|Any CPU
+		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Release|x64.ActiveCfg = Release|Any CPU
+		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Release|x64.Build.0 = Release|Any CPU
+		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Release|x86.ActiveCfg = Release|Any CPU
+		{50241C0B-7275-4B2D-AA65-6D2EB643D138}.Release|x86.Build.0 = Release|Any CPU
 		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Debug|ARM.Build.0 = Debug|Any CPU
+		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Debug|x64.Build.0 = Debug|Any CPU
+		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Debug|x86.Build.0 = Debug|Any CPU
 		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Release|Any CPU.Build.0 = Release|Any CPU
 		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Release|ARM.ActiveCfg = Release|Any CPU
+		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Release|ARM.Build.0 = Release|Any CPU
+		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Release|x64.ActiveCfg = Release|Any CPU
+		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Release|x64.Build.0 = Release|Any CPU
+		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Release|x86.ActiveCfg = Release|Any CPU
+		{B3E6CDA2-F721-430E-BD3C-55D78E6B8C6D}.Release|x86.Build.0 = Release|Any CPU
 		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Debug|ARM.Build.0 = Debug|Any CPU
+		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Debug|x64.Build.0 = Debug|Any CPU
+		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Debug|x86.Build.0 = Debug|Any CPU
 		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Release|Any CPU.Build.0 = Release|Any CPU
 		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Release|Any CPU.Build.0 = Release|Any CPU
+		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Release|ARM.ActiveCfg = Release|Any CPU
+		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Release|ARM.Build.0 = Release|Any CPU
+		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Release|x64.ActiveCfg = Release|Any CPU
+		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Release|x64.Build.0 = Release|Any CPU
+		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Release|x86.ActiveCfg = Release|Any CPU
+		{2C4B5022-8BA0-44CB-A612-995518C216D2}.Release|x86.Build.0 = Release|Any CPU
 		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Debug|ARM.Build.0 = Debug|Any CPU
+		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Debug|x64.Build.0 = Debug|Any CPU
+		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Debug|x86.Build.0 = Debug|Any CPU
 		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Release|Any CPU.Build.0 = Release|Any CPU
 		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Release|ARM.ActiveCfg = Release|Any CPU
+		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Release|ARM.Build.0 = Release|Any CPU
+		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Release|x64.ActiveCfg = Release|Any CPU
+		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Release|x64.Build.0 = Release|Any CPU
+		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Release|x86.ActiveCfg = Release|Any CPU
+		{9B0C0AF4-F505-4EFA-A707-6AE469224A98}.Release|x86.Build.0 = Release|Any CPU
 		{C617C831-E727-411A-901B-0302C7CE5AA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{C617C831-E727-411A-901B-0302C7CE5AA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{C617C831-E727-411A-901B-0302C7CE5AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{C617C831-E727-411A-901B-0302C7CE5AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{C617C831-E727-411A-901B-0302C7CE5AA2}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{C617C831-E727-411A-901B-0302C7CE5AA2}.Debug|ARM.Build.0 = Debug|Any CPU
+		{C617C831-E727-411A-901B-0302C7CE5AA2}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{C617C831-E727-411A-901B-0302C7CE5AA2}.Debug|x64.Build.0 = Debug|Any CPU
+		{C617C831-E727-411A-901B-0302C7CE5AA2}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{C617C831-E727-411A-901B-0302C7CE5AA2}.Debug|x86.Build.0 = Debug|Any CPU
 		{C617C831-E727-411A-901B-0302C7CE5AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{C617C831-E727-411A-901B-0302C7CE5AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{C617C831-E727-411A-901B-0302C7CE5AA2}.Release|Any CPU.Build.0 = Release|Any CPU
 		{C617C831-E727-411A-901B-0302C7CE5AA2}.Release|Any CPU.Build.0 = Release|Any CPU
+		{C617C831-E727-411A-901B-0302C7CE5AA2}.Release|ARM.ActiveCfg = Release|Any CPU
+		{C617C831-E727-411A-901B-0302C7CE5AA2}.Release|ARM.Build.0 = Release|Any CPU
+		{C617C831-E727-411A-901B-0302C7CE5AA2}.Release|x64.ActiveCfg = Release|Any CPU
+		{C617C831-E727-411A-901B-0302C7CE5AA2}.Release|x64.Build.0 = Release|Any CPU
+		{C617C831-E727-411A-901B-0302C7CE5AA2}.Release|x86.ActiveCfg = Release|Any CPU
+		{C617C831-E727-411A-901B-0302C7CE5AA2}.Release|x86.Build.0 = Release|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Debug|ARM.Build.0 = Debug|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Debug|x64.Build.0 = Debug|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Debug|x86.Build.0 = Debug|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Release|ARM.ActiveCfg = Release|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Release|ARM.Build.0 = Release|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Release|x64.ActiveCfg = Release|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Release|x64.Build.0 = Release|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Release|x86.ActiveCfg = Release|Any CPU
+		{844F2DCB-B4F3-48BD-9412-D326008C3A1A}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 		HideSolutionNode = FALSE
 	EndGlobalSection
 	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{3479C8CD-AE00-429E-BA88-1332B1E75F97} = {174B99F1-9DF1-49DE-84C7-BFE019AE78DE}
+		{83A2BD11-5186-4595-8B7E-191B5DE0BEFE} = {174B99F1-9DF1-49DE-84C7-BFE019AE78DE}
+	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {A5475DEB-D7F5-4A81-84EB-5C61A84246EB}
 		SolutionGuid = {A5475DEB-D7F5-4A81-84EB-5C61A84246EB}
 	EndGlobalSection
 	EndGlobalSection

+ 1 - 1
src/MonoGameScene/MonoGameScene.csproj

@@ -11,7 +11,7 @@
     <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.0.1375-develop" />
     <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.0.1375-develop" />
   </ItemGroup>
   </ItemGroup>
 
 
-  <ItemGroup>
+  <ItemGroup>    
     <ProjectReference Include="..\SharpGLTF.Runtime.MonoGame\SharpGLTF.Runtime.MonoGame.csproj" />
     <ProjectReference Include="..\SharpGLTF.Runtime.MonoGame\SharpGLTF.Runtime.MonoGame.csproj" />
   </ItemGroup>
   </ItemGroup>
 
 

+ 7 - 5
src/MonoGameViewer/MainScene.cs

@@ -18,7 +18,8 @@ namespace MonoGameViewer
         #region data
         #region data
 
 
         SharpGLTF.Schema2.ModelRoot _Model;
         SharpGLTF.Schema2.ModelRoot _Model;
-        ModelTemplate _ModelTemplate;
+        ModelTemplateContent _ModelTemplate;
+        BoundingSphere _ModelSphere;
 
 
         private bool _UseClassicEffects;
         private bool _UseClassicEffects;
 
 
@@ -57,7 +58,7 @@ namespace MonoGameViewer
 
 
         public void LoadModel(string filePath)
         public void LoadModel(string filePath)
         {
         {
-            SharpGLTF.Schema2.ModelRoot model = null;
+            SharpGLTF.Schema2.ModelRoot model;
 
 
             if (filePath.ToLower().EndsWith(".zip"))
             if (filePath.ToLower().EndsWith(".zip"))
             {
             {
@@ -79,6 +80,7 @@ namespace MonoGameViewer
             if (_ModelTemplate != null) { _ModelTemplate.Dispose(); _ModelTemplate = null; }
             if (_ModelTemplate != null) { _ModelTemplate.Dispose(); _ModelTemplate = null; }
 
 
             _ModelTemplate = Microsoft.Xna.Framework.Content.Pipeline.Graphics.FormatGLTF.ReadModel(_Model, GraphicsDevice, _UseClassicEffects);
             _ModelTemplate = Microsoft.Xna.Framework.Content.Pipeline.Graphics.FormatGLTF.ReadModel(_Model, GraphicsDevice, _UseClassicEffects);
+            _ModelSphere = _ModelTemplate.DefaultLayer.ModelBounds;
             _ModelInstance = null;
             _ModelInstance = null;
         }
         }
 
 
@@ -109,10 +111,10 @@ namespace MonoGameViewer
 
 
             _ModelInstance.Armature.SetAnimationFrame(0, (float)gameTime.TotalGameTime.TotalSeconds);
             _ModelInstance.Armature.SetAnimationFrame(0, (float)gameTime.TotalGameTime.TotalSeconds);
 
 
-            var bounds = _ModelInstance.ModelBounds;
+            
 
 
-            var lookAt = bounds.Center;
-            var camPos = bounds.Center + new Vector3(0, 0, bounds.Radius * 3);
+            var lookAt = _ModelSphere.Center;
+            var camPos = _ModelSphere.Center + new Vector3(0, 0, _ModelSphere.Radius * 3);
 
 
             var camera = Matrix.CreateWorld(camPos, lookAt - camPos, Vector3.UnitY);
             var camera = Matrix.CreateWorld(camPos, lookAt - camPos, Vector3.UnitY);
 
 

+ 1 - 0
src/MonoGameViewer/MonoGameViewer.csproj

@@ -14,6 +14,7 @@
 
 
   <ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\MonoGame.Framework.Graphics.GLTF\MonoGame.Framework.Pipeline.GLTF.csproj" />
     <ProjectReference Include="..\MonoGame.Framework.Graphics.GLTF\MonoGame.Framework.Pipeline.GLTF.csproj" />
+    <ProjectReference Include="..\MonoGame.Framework.Graphics.Scene3D\MonoGame.Framework.Graphics.Scene3D.csproj" />
   </ItemGroup>
   </ItemGroup>
 
 
 
 

+ 2 - 1
src/SharpGLTF.Runtime.MonoGame/SharpGLTF.Runtime.MonoGame.csproj

@@ -13,7 +13,8 @@
   </ItemGroup>  
   </ItemGroup>  
 
 
   <ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="..\MonoGame.Framework.Graphics.PBR\MonoGame.Framework.Graphics.PBR.csproj" />
+    <ProjectReference Include="..\MonoGame.Framework.Graphics.PBR\MonoGame.Framework.Graphics.EffectsPBR.csproj" />
+    <ProjectReference Include="..\MonoGame.Framework.Graphics.Scene3D\MonoGame.Framework.Graphics.Scene3D.csproj" />
   </ItemGroup>  
   </ItemGroup>  
 
 
 </Project>
 </Project>