瀏覽代碼

+refactor and relocation of files to where they fit better.
+readmes

vpenades 4 年之前
父節點
當前提交
c5e281e41d
共有 48 個文件被更改,包括 918 次插入929 次删除
  1. 23 63
      README.md
  2. 19 8
      src/MonoScene.Pipeline.Assimp/AssimpModelFactory.cs
  3. 1 3
      src/MonoScene.Pipeline.Assimp/Decoders/MeshDecoders.cs
  4. 10 17
      src/MonoScene.Pipeline.Assimp/Factories/ArmatureFactory.Assimp.cs
  5. 1 0
      src/MonoScene.Pipeline.Assimp/MonoScene.Pipeline.Assimp.csproj
  6. 0 5
      src/MonoScene.Pipeline.GLTF/Converters.cs
  7. 1 1
      src/MonoScene.Pipeline.GLTF/Decoders/MeshDecoders.cs
  8. 13 18
      src/MonoScene.Pipeline.GLTF/Factories/ArmatureFactory.GLTF.cs
  9. 18 10
      src/MonoScene.Pipeline.GLTF/GltfModelFactory.cs
  10. 1 0
      src/MonoScene.Pipeline.GLTF/MonoScene.Pipeline.GLTF.csproj
  11. 8 11
      src/MonoScene.Pipeline/ArmatureFactory.cs
  12. 0 210
      src/MonoScene.Pipeline/MeshPrimitiveBuilder.cs
  13. 135 0
      src/MonoScene.Pipeline/Meshes/MeshCollectionBuilder.cs
  14. 2 11
      src/MonoScene.Pipeline/Meshes/MeshDecoder.cs
  15. 8 6
      src/MonoScene.Pipeline/Meshes/MeshPrimitiveDecoder.cs
  16. 5 5
      src/MonoScene.Pipeline/MonoScene.Pipeline.csproj
  17. 0 203
      src/MonoScene.Pipeline/VertexDecoder.cs
  18. 0 0
      src/MonoScene.Runtime.Content/Armature/AnimationTrackInfo.cs
  19. 0 0
      src/MonoScene.Runtime.Content/Armature/ArmatureContent.cs
  20. 4 1
      src/MonoScene.Runtime.Content/Armature/NodeContent.cs
  21. 6 0
      src/MonoScene.Runtime.Content/BaseContent.cs
  22. 12 6
      src/MonoScene.Runtime.Content/BoneInfluences.cs
  23. 14 0
      src/MonoScene.Runtime.Content/Meshes/IndexBufferContent.cs
  24. 30 104
      src/MonoScene.Runtime.Content/Meshes/MeshCollectionContent.cs
  25. 13 0
      src/MonoScene.Runtime.Content/Meshes/MeshContent.cs
  26. 160 0
      src/MonoScene.Runtime.Content/Meshes/VertexBufferContent.Evaluator.cs
  27. 28 6
      src/MonoScene.Runtime.Content/Meshes/VertexBufferContent.cs
  28. 130 0
      src/MonoScene.Runtime.Content/Model/DrawableContent.cs
  29. 43 0
      src/MonoScene.Runtime.Content/Model/ModelContent.cs
  30. 7 19
      src/MonoScene.Runtime.Content/ModelCollectionContent.cs
  31. 8 2
      src/MonoScene.Runtime.Content/MonoScene.Runtime.Content.csproj
  32. 43 8
      src/MonoScene.Runtime.Content/README.MD
  33. 1 1
      src/MonoScene.Runtime.EffectsPBR/README.MD
  34. 60 0
      src/MonoScene.Runtime.Model3D/Content/PostProcessor.cs
  35. 2 2
      src/MonoScene.Runtime.Model3D/ModelArchitecture.md
  36. 23 83
      src/MonoScene.Runtime.Model3D/ModelGraph/DrawableTemplate.cs
  37. 2 2
      src/MonoScene.Runtime.Model3D/ModelGraph/MeshTransforms.cs
  38. 12 0
      src/MonoScene.Runtime.Model3D/ModelGraph/ModelCollection.cs
  39. 6 5
      src/MonoScene.Runtime.Model3D/ModelGraph/ModelInstance.cs
  40. 21 3
      src/MonoScene.Runtime.Model3D/ModelGraph/ModelTemplate.cs
  41. 15 53
      src/MonoScene.Runtime.Model3D/Pipeline/DeviceMeshFactory.cs
  42. 0 0
      src/MonoScene.Runtime.Model3D/Pipeline/GraphicsResourceTracker.cs
  43. 0 0
      src/MonoScene.Runtime.Model3D/Pipeline/TextureFactory.cs
  44. 7 2
      src/MonoScene.sln
  45. 4 1
      src/MonoSceneViewer/MainScene.cs
  46. 22 22
      src/MonoSceneViewer/MonoSceneViewer.csproj
  47. 0 12
      src/MonoSceneViewer/WPF/LightSettingsBox.xaml
  48. 0 26
      src/MonoSceneViewer/WPF/LightSettingsBox.xaml.cs

+ 23 - 63
README.md

@@ -1,87 +1,47 @@
-# MonoGame realtime rendering demo
+# MonoScene Framework
+#### A MonoGame 3D Model alternative.
 
 ![MonoGame Demo](MonoGameDemoPBR.jpg)
 
-### Temporary pre-requirements
+### ⚠️ Temporary pre-requirements ⚠️
 
-During the development of this project, I stumbled upon a limitation of
-Monogame regarding some shader limitations that have been recently
-addressed.
+This project requires compiling complex effect shaders with +256 techniques.
 
-To continue developing this library, now it is required to use the
-development branch nugets of monogame, which supports FX Objects version
-10.
+Current version of MonoGame, 3.8.0.1641, is not able to do that and will fail to compile.
 
-So until monogame publishes a new official release, at which point I
-will switch back to the public packages, I temporarily discourage using
-this library for purposes other than research... That is... unless
-you're also using the development branch nugets.
+The issue has been recently resolved and it's available in MonoGame's development branch.
 
-### Overview
-
-This is an example of loading and rendering glTF files with MonoGame.
+MonoGame's minimum version required is: __3.8.1.1825-develop__
 
-This demo was originally intended to be an example of how to use SharpGLTF to load glTF models into MonoGame, since then, it has evolved into a full graphics library that helps loading and rendering PBR Animated models into monogame.
+### Overview
 
-The main features of the project, compared with in-built 3D features of MonoGame are:
+MonoScene is a framework that replaces the very outdated 3D model architecture
+currently available in MonoGame, providing a number of much needed features:
 
-- Loading glTF models at runtime, without any pipeline processing.
-- PBR materials support
-- Animated models, out of the box.
+- PBR effect shaders.
+- Full skeleton animation.
+- Loading asset models at runtime.
 
 
 ### Architecture:
 
-The project is split into 4 different packages that can be used independently.
-
-Notice that the package names and namespaces are **temporary** and subject to change.
-
-- MonoGame.Framework.Graphics.EffectsPBR
-- MonoGame.Framework.Graphics.Model3D
-- MonoGame.Framework.Graphics.Scene3D
-- MonoGame.Framework.Pipeline.GLTF
-
-##### Graphics.EffectsPBR
-
-Defines glTF compatible, PBR effects:
-
-- AnimatedEffect (abstract base class)
-  - UnlitEffect
-  - PBREffect (abstract)
-    - PBRMetallicRoughnessEffect
-    - PBRSpecularGlossinessEffect
+By design, the data flow of the framework looks like this:
 
-These effects can be used on their own, and dont' require anything from the rest of libraries.
+__3D Assets ⇒ Pipeline ⇒ Content ⇒ Runtime__
 
-##### Graphics.Model3D
+The project is split into 3 sections:
 
-Defines a number of classes to represent 3D models, using a new architecture that can handle
-animated models in a modern way.
-
-- [Model Architecture docs (WIP)](src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/ModelArchitecture.md)
-- [Skinning considerations (WIP)](src/MonoGame.Framework.Graphics.Toolkit3D/Graphics/Skinning.MD)
-
-##### Graphics.Scene3D
-
-This is a package that can be optinally used, and simplifies drawing scenes with multiple objects.
-
-##### Pipeline.GLTF
-
-This library depends on [SharpGLTF.Core](https://www.nuget.org/packages/SharpGLTF.Core) and loads
-glTF models and converts them to the structures defined in Graphics.Model3D.
-
-When loading the models, it is recomended to use EffectsPBR effects, because that would give
-the visual results expected by glTF, but it's also possible to fall back to BasicEffect and
-SkinnedEffect, in which case the loader will do a "best effort" to convert the materials from
-PBR to classic diffuse-specular materials.
+- Pipeline: utilities and classes to help importing 3D asset files.
+- Content: classes used to define an intermediate representation of 3D model.
+- Runtime: framework to consume and render 3D content objects.
 
 ### Limitations
 
-##### Pipeline
-
-Right now, It is **not possible** to load glTFs through the content pipeline, glTFs need to be loaded
-at runtime, so only projects able to consume Pipeline.GLTF library will be able to load glTFs.
+##### MonoGame's Content Pipeline
 
+Right now, It is **not possible** to load glTFs through Monogame's old content processing pipeline;
+glTFs need to be loaded at runtime, so only projects able to consume `MonoScene.Pipeline.GLTF` library
+will be able to load glTFs.
 ##### Animations
 
 - Due to limitations in the rendering API of MonoGame, glTF's morphing features are not supported.

+ 19 - 8
src/MonoScene.Pipeline.Assimp/AssimpModelFactory.cs

@@ -41,16 +41,18 @@ namespace MonoScene.Graphics.Pipeline
 
         public DeviceModelCollection ReadModel(Assimp.Scene scene)
         {
-            var factory = UseBasicEffects ? (MeshFactory)new ClassicMeshFactory(_Device) : new PBRMeshFactory(_Device);
+            var factory = UseBasicEffects ? (DeviceMeshFactory)new ClassicMeshFactory(_Device) : new PBRMeshFactory(_Device);
 
-            return ConvertToXna(scene, factory);
+            return ConvertToDevice(scene, factory);
         }
 
-        public static DeviceModelCollection ConvertToXna(Assimp.Scene scene, MeshFactory meshFactory)
+        public static DeviceModelCollection ConvertToDevice(Assimp.Scene srcScene, DeviceMeshFactory meshFactory)
         {
             if (meshFactory == null) throw new ArgumentNullException();
 
-            return ConvertToContent(scene).ToDeviceModelCollection(meshFactory);
+            var content = ConvertToContent(srcScene);
+
+            return DeviceModelCollection.CreateFrom(content, meshFactory.CreateMeshCollection);
         }
 
         public static ModelCollectionContent ConvertToContent(Assimp.Scene scene)
@@ -58,24 +60,33 @@ namespace MonoScene.Graphics.Pipeline
             // create a mesh decoder for each mesh
 
             var meshDecoders = scene.Meshes.ToXna(scene.Materials);
-            var meshContent = MeshCollectionContent.CreateFromMeshes(meshDecoders);
+            var meshContent = MeshCollectionBuilder.CreateContent(meshDecoders);
 
             // build the armatures and models
 
-            var models = new List<ModelTemplate>();
+            var models = new List<ModelContent>();
             var armatures = new List<ArmatureContent>();
 
             var armatureFactory = new AssimpArmatureFactory(scene);
             var armature = armatureFactory.CreateArmature();
             armatures.Add(armature);
 
-            var model = armatureFactory.CreateModel(scene, armature, meshDecoders);
+            var model = armatureFactory.CreateModelContent(scene, armatures.Count-1);
+
+            model.Name = "AssimpScene";
+            model.Tag = scene.Metadata;
+
+            // model.ModelBounds = 
 
             models.Add(model);            
 
             // coalesce all resources into a container class:
 
-            return new ModelCollectionContent(meshContent, armatures.ToArray(), models.ToArray(), 0);
+            var content = new ModelCollectionContent(meshContent, armatures.ToArray(), models.ToArray(), 0);
+
+            content = PostProcessor.Postprocess(content);
+
+            return content;
         }
     }
 }

+ 1 - 3
src/MonoScene.Pipeline.Assimp/Decoders/MeshDecoders.cs

@@ -3,9 +3,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 
-using MonoScene.Graphics;
-
-using VERTEXINFLUENCES = Microsoft.Xna.Framework.Graphics.PackedVector.VertexInfluences;
+using VERTEXINFLUENCES = Microsoft.Xna.Framework.Graphics.PackedVector.BoneInfluences;
 
 using XNAV2 = Microsoft.Xna.Framework.Vector2;
 using XNAV3 = Microsoft.Xna.Framework.Vector3;

+ 10 - 17
src/MonoScene.Pipeline.Assimp/Factories/ArmatureFactory.Assimp.cs

@@ -44,30 +44,23 @@ namespace MonoScene.Graphics.Pipeline
 
         #region API
 
-        public ModelTemplate CreateModel(Assimp.Scene scene, ArmatureContent armature, IReadOnlyList<IMeshDecoder<MaterialContent>> meshDecoders)
+        private static IEnumerable<Assimp.Node> Flatten(Assimp.Node node)
         {
-            var model = CreateModel(scene, armature);
-            model.ModelBounds = MeshFactory.EvaluateBoundingSphere(model.CreateInstance(), meshDecoders);
-            return model;
+            var flattenedChildren = node.Children.SelectMany(c => Flatten(c));
+            return new Assimp.Node[] { node }.Concat(flattenedChildren);
         }
 
-        public ModelTemplate CreateModel(Assimp.Scene scene, ArmatureContent armature)
+        public ModelContent CreateModelContent(Assimp.Scene scene, int armatureIndex)
         {
             var drawables = Flatten(scene.RootNode)
                 .Where(item => item.HasMeshes)
-                .SelectMany(item => CreateDrawables(item, scene.Meshes))
+                .SelectMany(item => CreateDrawableContent(item, scene.Meshes))
                 .ToArray();
 
-            return new ModelTemplate(null, armature, drawables);
-        }
-
-        private static IEnumerable<Assimp.Node> Flatten(Assimp.Node node)
-        {
-            var flattenedChildren = node.Children.SelectMany(c => Flatten(c));
-            return new Assimp.Node[] { node }.Concat(flattenedChildren);
+            return new ModelContent(armatureIndex, drawables);
         }
 
-        public IEnumerable<IDrawableTemplate> CreateDrawables(Assimp.Node node, IReadOnlyList<Assimp.Mesh> meshes)
+        public IEnumerable<DrawableContent> CreateDrawableContent(Assimp.Node node, IReadOnlyList<Assimp.Mesh> meshes)
         {
             if (node == null) throw new ArgumentNullException(nameof(node));
             if (!node.HasMeshes) throw new ArgumentNullException(nameof(node.HasMeshes));
@@ -96,7 +89,7 @@ namespace MonoScene.Graphics.Pipeline
             // process rigid meshes
             foreach (var meshIdx in rigidIndices)
             {
-                yield return CreateRigidDrawable(meshIdx, node);
+                yield return CreateRigidDrawableContent(meshIdx, node);
             }
 
             // process skinned meshes
@@ -104,9 +97,9 @@ namespace MonoScene.Graphics.Pipeline
             {
                 var skin = meshes[meshIdx].Bones
                     .Select(item => (rootNode.FindNode(item.Name), item.OffsetMatrix.ToXna()))
-                    .ToArray();                    
+                    .ToArray();
 
-                yield return CreateSkinnedDrawable(meshIdx, node, skin);
+                yield return CreateSkinnedDrawableContent(meshIdx, node, skin);
             }
         }
 

+ 1 - 0
src/MonoScene.Pipeline.Assimp/MonoScene.Pipeline.Assimp.csproj

@@ -7,6 +7,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\MonoScene.Pipeline\MonoScene.Pipeline.csproj" />
+    <ProjectReference Include="..\MonoScene.Runtime.Model3D\MonoScene.Runtime.Model3D.csproj" />
   </ItemGroup>
 
   <ItemGroup>

+ 0 - 5
src/MonoScene.Pipeline.GLTF/Converters.cs

@@ -18,11 +18,6 @@ namespace MonoScene.Graphics.Pipeline
 {
     static class Converters
     {
-        public static BoundingSphere ToXna(this (XYZ center, float radius) sphere)
-        {
-            return new BoundingSphere(sphere.center, sphere.radius);
-        }        
-
         public static (Vector3 U, Vector3 V) ToXna(this System.Numerics.Matrix3x2 m)
         {
             var u = new Vector3(m.M11, m.M21, m.M31);

+ 1 - 1
src/MonoScene.Pipeline.GLTF/Decoders/MeshDecoders.cs

@@ -10,7 +10,7 @@ using XNAV2 = Microsoft.Xna.Framework.Vector2;
 using XNAV3 = Microsoft.Xna.Framework.Vector3;
 using XNAV4 = Microsoft.Xna.Framework.Vector4;
 
-using VERTEXINFLUENCES = Microsoft.Xna.Framework.Graphics.PackedVector.VertexInfluences;
+using VERTEXINFLUENCES = Microsoft.Xna.Framework.Graphics.PackedVector.BoneInfluences;
 
 
 namespace MonoScene.Graphics.Pipeline

+ 13 - 18
src/MonoScene.Pipeline.GLTF/Factories/ArmatureFactory.GLTF.cs

@@ -108,39 +108,34 @@ namespace MonoScene.Graphics.Pipeline
 
         #region API
 
-        public ModelTemplate CreateModel(SharpGLTF.Schema2.Scene scene, ArmatureContent armature, IReadOnlyList<IMeshDecoder<MaterialContent>> meshDecoders)
+        public void AddSceneRoot(SharpGLTF.Schema2.Scene scene)
         {
-            var model = CreateModel(scene, armature);
-            model.ModelBounds = MeshFactory.EvaluateBoundingSphere(model.CreateInstance(), meshDecoders);
-            return model;
+            foreach (var root in scene.VisualChildren)
+            {
+                AddRoot(root);
+            }
         }
 
-        public ModelTemplate CreateModel(SharpGLTF.Schema2.Scene scene, ArmatureContent armature)
+        public ModelContent CreateModelContent(SharpGLTF.Schema2.Scene scene, int armatureIndex)
         {
             var drawables = GLTFNODE.Flatten(scene)
                 .Where(item => item.Mesh != null)
-                .Select(item => CreateDrawable(item))
+                .Select(item => CreateDrawableContent(item))
                 .ToArray();
 
-            return new ModelTemplate(scene.Name, armature, drawables);
-        }
+            var model = new ModelContent(armatureIndex, drawables);
 
-        public void AddSceneRoot(SharpGLTF.Schema2.Scene scene)
-        {
-            foreach (var root in scene.VisualChildren)
-            {
-                AddRoot(root);
-            }
+            return model;
         }
 
-        public IDrawableTemplate CreateDrawable(GLTFNODE node)
+        public DrawableContent CreateDrawableContent(GLTFNODE node)
         {
             if (node == null) throw new ArgumentNullException(nameof(GLTFNODE));
             if (node.Mesh == null) throw new ArgumentNullException(nameof(GLTFNODE.Mesh));
 
             if (node.Skin == null)
             {
-                return CreateRigidDrawable(node.Mesh.LogicalIndex, node);
+                return CreateRigidDrawableContent(node.Mesh.LogicalIndex, node);
             }
             else
             {
@@ -152,9 +147,9 @@ namespace MonoScene.Graphics.Pipeline
                     bones[i] = (joint, inverseBindMatrix);
                 }
 
-                return CreateSkinnedDrawable(node.Mesh.LogicalIndex, node, bones);
+                return CreateSkinnedDrawableContent(node.Mesh.LogicalIndex, node, bones);
             }
-        }        
+        }
 
         #endregion
     }

+ 18 - 10
src/MonoScene.Pipeline.GLTF/GltfModelFactory.cs

@@ -79,20 +79,22 @@ namespace MonoScene.Graphics.Pipeline
 
         public DeviceModelCollection ReadModel(SharpGLTF.Schema2.ModelRoot model)
         {
-            var factory = UseBasicEffects ? (MeshFactory)new ClassicMeshFactory(_Device) : new PBRMeshFactory(_Device);
+            var factory = UseBasicEffects ? (DeviceMeshFactory)new ClassicMeshFactory(_Device) : new PBRMeshFactory(_Device);
 
-            return ConvertToXna(model, factory);
+            return ConvertToDevice(model, factory);
         }
 
         #endregion
 
         #region static API
 
-        public DeviceModelCollection ConvertToXna(SharpGLTF.Schema2.ModelRoot srcModel, MeshFactory meshFactory)
+        public DeviceModelCollection ConvertToDevice(SharpGLTF.Schema2.ModelRoot srcModel, DeviceMeshFactory meshFactory)
         {
             if (meshFactory == null) throw new ArgumentNullException();
 
-            return ConvertToContent(srcModel).ToDeviceModelCollection(meshFactory);
+            var content = ConvertToContent(srcModel);
+
+            return DeviceModelCollection.CreateFrom(content, meshFactory.CreateMeshCollection);
         }
 
         public ModelCollectionContent ConvertToContent(SharpGLTF.Schema2.ModelRoot srcModel)
@@ -100,12 +102,12 @@ namespace MonoScene.Graphics.Pipeline
             // create a mesh decoder for each mesh
 
             var meshDecoders = srcModel.LogicalMeshes.ToXnaDecoders(TagConverter);
-            var meshContent = MeshCollectionContent.CreateFromMeshes(meshDecoders);
+            var meshContent = MeshCollectionBuilder.CreateContent(meshDecoders);
 
             // build the armatures and models
 
             var armatures = new List<ArmatureContent>();
-            var models = new List<ModelTemplate>();
+            var models = new List<ModelContent>();
 
             foreach (var scene in srcModel.LogicalScenes)
             {
@@ -121,22 +123,28 @@ namespace MonoScene.Graphics.Pipeline
                 var armature = armatureFactory.CreateArmature();
                 armatures.Add(armature);
 
-                var model = armatureFactory.CreateModel(scene, armature, meshDecoders);
-                model.Tag = TagConverter?.Invoke(scene);
+                var model = armatureFactory.CreateModelContent(scene, armatures.Count-1);
+
+                model.Name = scene.Name;
+                model.Tag = TagConverter?.Invoke(scene);                
 
                 models.Add(model);
             }
             
             // coalesce all resources into a container class:
 
-            return new ModelCollectionContent(meshContent, armatures.ToArray(), models.ToArray(), srcModel.DefaultScene.LogicalIndex);
+            var content = new ModelCollectionContent(meshContent, armatures.ToArray(), models.ToArray(), srcModel.DefaultScene.LogicalIndex);
+
+            content = PostProcessor.Postprocess(content);
+
+            return content;
         }              
 
         public MeshCollectionContent ReadMeshContent(IEnumerable<SharpGLTF.Schema2.Mesh> meshes)
         {
             var meshDecoders = meshes.ToXnaDecoders(TagConverter);
 
-            return MeshCollectionContent.CreateFromMeshes(meshDecoders);
+            return MeshCollectionBuilder.CreateContent(meshDecoders);
         }
 
         #endregion

+ 1 - 0
src/MonoScene.Pipeline.GLTF/MonoScene.Pipeline.GLTF.csproj

@@ -17,6 +17,7 @@
 
   <ItemGroup>
     <ProjectReference Include="..\MonoScene.Pipeline\MonoScene.Pipeline.csproj" />
+    <ProjectReference Include="..\MonoScene.Runtime.Model3D\MonoScene.Runtime.Model3D.csproj" />
   </ItemGroup>
 
 </Project>

+ 8 - 11
src/MonoScene.Pipeline/ArmatureFactory.cs

@@ -39,29 +39,26 @@ namespace MonoScene.Graphics.Pipeline
         protected abstract Object GetTag(TNode node);
         protected abstract IEnumerable<TNode> GetChildren(TNode node);
         protected abstract XNAMAT GetLocalMatrix(TNode node);
-        protected abstract Content.AnimatableProperty<XNAV3> GetScale(TNode node);
-        protected abstract Content.AnimatableProperty<XNAQUAT> GetRotation(TNode node);
-        protected abstract Content.AnimatableProperty<XNAV3> GetTranslation(TNode node);
+        protected abstract AnimatableProperty<XNAV3> GetScale(TNode node);
+        protected abstract AnimatableProperty<XNAQUAT> GetRotation(TNode node);
+        protected abstract AnimatableProperty<XNAV3> GetTranslation(TNode node);
 
 
-        public NodeContent GetNode(TNode srcNode) => _Map[srcNode];
+        public NodeContent GetNode(TNode srcNode) => _Map[srcNode];        
 
-        public IDrawableTemplate CreateRigidDrawable(int meshIndex, TNode node)
+        public DrawableContent CreateRigidDrawableContent(int meshIndex, TNode node)
         {
             var n = GetNode(node);
-            var d = new RigidDrawableTemplate(meshIndex, n);
-            return d;
+            return DrawableContent.CreateRigid(meshIndex, n);
         }
 
-        public IDrawableTemplate CreateSkinnedDrawable(int meshIndex, TNode container, (TNode, XNAMAT)[] bones)
+        public DrawableContent CreateSkinnedDrawableContent(int meshIndex, TNode container, (TNode, XNAMAT)[] bones)
         {
             var xbones = bones
                 .Select(item => (GetNode(item.Item1), item.Item2))
                 .ToArray();
 
-            var d = new SkinnedDrawableTemplate(meshIndex, null, GetNode(container).Name, xbones);
-
-            return d;
+            return DrawableContent.CreateSkinned(meshIndex, null, xbones);
         }
 
         public ArmatureContent CreateArmature() { return new ArmatureContent(_Nodes.ToArray(), _AnimationTracks.ToArray()); }

+ 0 - 210
src/MonoScene.Pipeline/MeshPrimitiveBuilder.cs

@@ -1,210 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-
-using Microsoft.Xna.Framework.Graphics;
-
-namespace MonoScene.Graphics.Pipeline
-{
-    /// <summary>
-    /// Accumulates all the primitives of all the meshes of a model, so they can be optimized/batched.
-    /// </summary>
-    sealed class MeshPrimitiveBuilder
-    {
-        #region data
-
-        // shared buffers
-        private readonly Dictionary<Type, IPrimitivesBuffers> _Buffers = new Dictionary<Type, IPrimitivesBuffers>();
-
-        // primitives
-        private readonly List<_MeshPrimitive> _MeshPrimitives = new List<_MeshPrimitive>();
-
-        #endregion
-
-        #region API
-
-        public void AppendMeshPrimitive(int logicalMeshIndex, Type vertexType, IMeshPrimitiveDecoder primitive, Effect effect, BlendState blending, bool doubleSided)
-        {
-            // this is a reflection hack to call a generic method from a non generic method.
-
-            var myMethod = typeof(MeshPrimitiveBuilder)
-              .GetMethods()
-              .FirstOrDefault(m => m.Name == nameof(AppendMeshPrimitive) && m.IsGenericMethodDefinition && m.GetGenericArguments().Length == 1);
-
-            myMethod = myMethod.MakeGenericMethod(vertexType);
-            myMethod.Invoke(this, new Object[] { logicalMeshIndex, primitive, effect, blending, doubleSided });
-        }
-
-        public void AppendMeshPrimitive<TVertex>(int logicalMeshIndex, IMeshPrimitiveDecoder primitive, Effect effect, BlendState blending, bool doubleSided)
-            where TVertex : unmanaged, IVertexType
-        {
-            if (!_Buffers.TryGetValue(typeof(TVertex), out IPrimitivesBuffers pb))
-            {
-                _Buffers[typeof(TVertex)] = pb = new _PrimitivesBuffers<TVertex>();
-            }
-
-            var part = (pb as _PrimitivesBuffers<TVertex>).Append(logicalMeshIndex, effect, blending, doubleSided, primitive);
-
-            _MeshPrimitives.Add(part);
-        }
-
-        internal IReadOnlyDictionary<int, Mesh> CreateRuntimeMeshes(GraphicsDevice device, GraphicsResourceTracker disposables)
-        {
-            // create shared vertex/index buffers
-
-            var vbuffers = _Buffers.Values.ToDictionary(key => key, val => val.CreateVertexBuffer(device));
-            var ibuffers = _Buffers.Values.ToDictionary(key => key, val => val.CreateIndexBuffer(device));
-            
-            foreach (var vb in vbuffers.Values) disposables.AddDisposable(vb);
-            foreach (var ib in ibuffers.Values) disposables.AddDisposable(ib);            
-
-            // create RuntimeModelMesh
-
-            Mesh _convert(IEnumerable<_MeshPrimitive> srcParts)
-            {
-                var dstMesh = new Mesh(device);
-
-                foreach (var srcPart in srcParts)
-                {
-                    var vb = vbuffers[srcPart.PrimitiveBuffers];
-                    var ib = ibuffers[srcPart.PrimitiveBuffers];                    
-
-                    var dstGeo = new MeshTriangles();
-                    dstGeo.SetCullingStates(srcPart.Material.DoubleSided);                    
-                    dstGeo.SetVertexBuffer(vb, srcPart.VertexOffset, srcPart.VertexCount);
-                    dstGeo.SetIndexBuffer(ib, srcPart.TriangleOffset * 3, srcPart.TriangleCount);
-
-                    var dstPart = dstMesh.CreateMeshPart();
-                    dstPart.Effect = srcPart.Material.PrimitiveEffect;
-                    dstPart.Blending = srcPart.Material.PrimitiveBlending;
-                    dstPart.Geometry = dstGeo;
-                }
-
-                return dstMesh;
-            }
-
-            return _MeshPrimitives
-                .GroupBy(item => item.LogicalMeshIndex)
-                .ToDictionary(k => k.Key, v => _convert(v));
-        }
-
-        #endregion
-
-        #region nested types
-
-        interface IPrimitivesBuffers
-        {
-            VertexBuffer CreateVertexBuffer(GraphicsDevice device);
-            IndexBuffer CreateIndexBuffer(GraphicsDevice device);
-        }
-
-        /// <summary>
-        /// Contains the shared vertex/index buffers of all the mesh primitive that share the same vertex type.
-        /// </summary>
-        /// <typeparam name="TVertex"></typeparam>
-        sealed class _PrimitivesBuffers<TVertex> : IPrimitivesBuffers
-            where TVertex : unmanaged, IVertexType
-        {
-            #region data
-
-            private readonly List<TVertex> _Vertices = new List<TVertex>();
-            private readonly List<(int, int, int)> _Triangles = new List<(int, int, int)>();
-
-            #endregion
-
-            #region API
-            public _MeshPrimitive Append(int meshKey, Effect effect, BlendState blending, bool doubleSided, IMeshPrimitiveDecoder primitive)
-            {
-                var partVertices = primitive.ToXnaVertices<TVertex>();
-                var partTriangles = primitive.TriangleIndices.ToList();
-
-                var material = new _Material
-                {
-                    PrimitiveEffect = effect,
-                    PrimitiveBlending = blending,
-                    DoubleSided = doubleSided
-                };
-
-                var part = new _MeshPrimitive
-                {
-                    LogicalMeshIndex = meshKey,
-                    Material = material,
-                    PrimitiveBuffers = this,
-                    VertexOffset = _Vertices.Count,
-                    VertexCount = partVertices.Length,
-                    TriangleOffset = _Triangles.Count,
-                    TriangleCount = partTriangles.Count
-                };
-
-                _Vertices.AddRange(partVertices);
-                _Triangles.AddRange(partTriangles);
-
-                return part;
-            }
-
-            public VertexBuffer CreateVertexBuffer(GraphicsDevice device)
-            {
-                var data = new VertexBuffer(device, typeof(TVertex), _Vertices.Count, BufferUsage.None);
-                data.SetData(_Vertices.ToArray());
-                return data;
-            }
-
-            public IndexBuffer CreateIndexBuffer(GraphicsDevice device)
-            {
-                return CreateIndexBuffer(device, _Triangles);
-            }
-
-            private static IndexBuffer CreateIndexBuffer(GraphicsDevice device, IEnumerable<(int A, int B, int C)> triangles)
-            {
-                var sequence32 = triangles
-                    .SelectMany(item => new[] { (UInt32)item.C, (UInt32)item.B, (UInt32)item.A })
-                    .ToArray();
-
-                var max = sequence32.Max();
-
-                if (max > 65535)
-                {
-                    var indices = new IndexBuffer(device, typeof(UInt32), sequence32.Length, BufferUsage.None);
-
-                    indices.SetData(sequence32);
-                    return indices;
-                }
-                else
-                {
-                    var sequence16 = sequence32.Select(item => (UInt16)item).ToArray();
-
-                    var indices = new IndexBuffer(device, typeof(UInt16), sequence16.Length, BufferUsage.None);
-
-                    indices.SetData(sequence16);
-                    return indices;
-                }
-            }
-
-            #endregion
-        }
-
-        /// <summary>
-        /// Represents a mesh primitive
-        /// </summary>
-        struct _MeshPrimitive
-        {
-            public int LogicalMeshIndex;
-            public _Material Material;
-            public IPrimitivesBuffers PrimitiveBuffers;
-            public int VertexOffset;
-            public int VertexCount;
-            public int TriangleOffset;
-            public int TriangleCount;            
-        }
-
-        struct _Material
-        {
-            public Effect PrimitiveEffect;
-            public BlendState PrimitiveBlending;
-            public bool DoubleSided;
-        }
-
-        #endregion
-    }
-}

+ 135 - 0
src/MonoScene.Pipeline/Meshes/MeshCollectionBuilder.cs

@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Microsoft.Xna.Framework.Graphics;
+
+using MonoScene.Graphics.Content;
+
+namespace MonoScene.Graphics.Pipeline
+{
+    /// <summary>
+    /// Helper class used to coalesce mesh resources and build a <see cref="MeshCollectionContent"/>
+    /// </summary>
+    public class MeshCollectionBuilder
+    {
+        #region lifecycle
+
+        public MeshCollectionBuilder() { }
+
+        public MeshCollectionBuilder(IReadOnlyList<IMeshDecoder<MaterialContent>> srcMeshes)
+        {
+            for (int i = 0; i < srcMeshes.Count; ++i)
+            {
+                var srcMesh = srcMeshes[i];
+                var hasSkin = srcMesh.Primitives.Any(item => item.JointsWeightsCount > 0);
+
+                this.AppendMesh(srcMesh);
+            }
+        }
+
+        #endregion
+
+        #region data
+
+        // shared buffers
+        internal readonly List<VertexBufferContent> _VertexBuffers = new List<VertexBufferContent>();
+        internal readonly List<IndexBufferContent> _IndexBuffers = new List<IndexBufferContent>();
+        internal readonly List<MaterialContent> _Materials = new List<MaterialContent>();
+        internal readonly List<MeshContent> _Meshes = new List<MeshContent>();
+
+        #endregion
+
+        #region API
+
+        public static MeshCollectionContent CreateContent(IReadOnlyList<IMeshDecoder<MaterialContent>> srcMeshes)
+        {
+            return new MeshCollectionBuilder(srcMeshes).ToContent();
+        }
+
+        public MeshCollectionContent ToContent()
+        {
+            var dstMeshes = new MeshCollectionContent();
+
+            dstMeshes._SharedVertexBuffers.AddRange(this._VertexBuffers);
+            dstMeshes._SharedIndexBuffers.AddRange(this._IndexBuffers);
+            dstMeshes._SharedMaterials.AddRange(this._Materials);
+            dstMeshes._Meshes.AddRange(this._Meshes);
+
+            return dstMeshes;
+        }
+
+        #endregion
+
+        #region core        
+
+        private int _UseMaterialIndex(MaterialContent material)
+        {
+            var idx = _Materials.IndexOf(material);
+            if (idx >= 0) return idx;
+
+            _Materials.Add(material);
+            return _Materials.Count - 1;
+        }
+
+        private int _UseVertexBuffer(VertexDeclaration vdecl)
+        {
+            var vbIndex = _VertexBuffers.FindIndex(item => item.IsCompatibleWith(vdecl));
+            if (vbIndex >= 0) return vbIndex;
+
+            _VertexBuffers.Add(new VertexBufferContent());
+            return _VertexBuffers.Count - 1;
+        }
+
+        private int _UseIndexBuffer()
+        {
+            var ibIndex = _IndexBuffers.FindIndex(item => true);
+            if (ibIndex >= 0) return ibIndex;
+
+            _IndexBuffers.Add(new IndexBufferContent());
+            return _IndexBuffers.Count - 1;
+        }
+
+        public void AppendMesh(IMeshDecoder<MaterialContent> srcMesh)
+        {
+            var dstMesh = new MeshContent();
+            dstMesh.Name = srcMesh.Name;
+            dstMesh.Tag = srcMesh.Tag;
+
+            foreach (var prim in srcMesh.Primitives)
+            {
+                var vdecl = MeshPrimitiveDecoder.GetVertexDeclaration(prim);
+
+                int vbIndex = _UseVertexBuffer(vdecl);
+                int ibIndex = _UseIndexBuffer();
+
+                var geometry = CreateGeometry(vbIndex, ibIndex, prim, vdecl);
+                if (geometry == null) continue;
+
+                dstMesh.AddMeshPart(geometry, _UseMaterialIndex(prim.Material));
+            }
+
+            _Meshes.Add(dstMesh);
+        }
+
+        private MeshGeometryContent CreateGeometry(int vbIndex, int ibIndex, IMeshPrimitiveDecoder<MaterialContent> primitive, VertexDeclaration vdecl)
+        {
+            var partVertices = MeshPrimitiveDecoder.ToXnaVertices(primitive, vdecl);
+            var partTriangles = primitive.TriangleIndices.ToList();
+
+            if (partTriangles.Count == 0) return null;
+
+            var vRange = _VertexBuffers[vbIndex].AddVertices(partVertices, vdecl);
+            var iRange = _IndexBuffers[ibIndex].AddTriangleIndices(partTriangles);
+
+            var geometry = new MeshGeometryContent();
+            geometry.SetVertices(vbIndex, vRange.VertexOffset, vRange.VertexCount);
+            geometry.SetIndices(ibIndex, iRange.Offset, iRange.Count, partTriangles.Count);
+
+            return geometry;
+        }
+
+        #endregion
+    }
+}

+ 2 - 11
src/MonoScene.Runtime.Content/Pipeline/MeshDecoder.cs → src/MonoScene.Pipeline/Meshes/MeshDecoder.cs

@@ -1,10 +1,6 @@
 using System;
 using System.Collections.Generic;
-using System.Linq;
-using System.Text;
 
-using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
 using Microsoft.Xna.Framework.Graphics.PackedVector;
 
 using XNAV2 = Microsoft.Xna.Framework.Vector2;
@@ -20,12 +16,7 @@ namespace MonoScene.Graphics.Pipeline
     public interface IMeshDecoder<TMaterial>
         where TMaterial : class
     {
-        /// <summary>
-        /// The mesh name.
-        /// </summary>
-        string Name { get; }
-
-        //
+        string Name { get; }        
         Object Tag { get; }
         IReadOnlyList<IMeshPrimitiveDecoder<TMaterial>> Primitives { get; }        
     }    
@@ -98,7 +89,7 @@ namespace MonoScene.Graphics.Pipeline
 
         XNAV4 GetColor(int vertexIndex, int colorSetIndex);
 
-        VertexInfluences GetSkinWeights(int vertexIndex);
+        BoneInfluences GetSkinWeights(int vertexIndex);
 
         #endregion
     }    

+ 8 - 6
src/MonoScene.Runtime.Content/Pipeline/MeshPrimitiveDecoder.cs → src/MonoScene.Pipeline/Meshes/MeshPrimitiveDecoder.cs

@@ -10,6 +10,8 @@ using XNAV2 = Microsoft.Xna.Framework.Vector2;
 using XNAV3 = Microsoft.Xna.Framework.Vector3;
 using XNAV4 = Microsoft.Xna.Framework.Vector4;
 
+using BYTES = System.Span<byte>;
+
 namespace MonoScene.Graphics.Pipeline
 {
     static class MeshPrimitiveDecoder
@@ -85,24 +87,24 @@ namespace MonoScene.Graphics.Pipeline
     readonly ref struct VertexEncoder
     {
         #region constructors
-        public VertexEncoder(Span<Byte> array, int vertexStride, int index)
+        public VertexEncoder(BYTES array, int vertexStride, int index)
         {
             _Vertex = array.Slice(index * vertexStride, vertexStride);
         }
 
-        public VertexEncoder(Span<Byte> vertex) { _Vertex = vertex; }
+        public VertexEncoder(BYTES vertex) { _Vertex = vertex; }
 
         #endregion
 
         #region data
 
-        private readonly Span<Byte> _Vertex;
+        private readonly BYTES _Vertex;
 
         #endregion
 
         #region API
 
-        public void Encode(VertexElement element, Vector2 value)
+        public void Encode(VertexElement element, XNAV2 value)
         {
             var dstVertex = _Vertex.Slice(element.Offset);
 
@@ -115,7 +117,7 @@ namespace MonoScene.Graphics.Pipeline
             throw new ArgumentException(nameof(element));
         }
 
-        public void Encode(VertexElement element, Vector3 value)
+        public void Encode(VertexElement element, XNAV3 value)
         {
             var dstVertex = _Vertex.Slice(element.Offset);
 
@@ -135,7 +137,7 @@ namespace MonoScene.Graphics.Pipeline
             throw new ArgumentException(nameof(element));
         }
 
-        public void Encode(VertexElement element, Vector4 value)
+        public void Encode(VertexElement element, XNAV4 value)
         {
             var dstVertex = _Vertex.Slice(element.Offset);
 

+ 5 - 5
src/MonoScene.Pipeline/MonoScene.Pipeline.csproj

@@ -3,14 +3,14 @@
   <PropertyGroup>
     <TargetFramework>netstandard2.0</TargetFramework>
     <RootNamespace>MonoScene.Graphics.Pipeline</RootNamespace>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\MonoScene.Runtime.Model3D\MonoScene.Runtime.Model3D.csproj" />
-  </ItemGroup>
+  </PropertyGroup>   
 
   <ItemGroup>    
     <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.1825-develop" PrivateAssets="all" />
+  </ItemGroup>  
+
+  <ItemGroup>
+    <ProjectReference Include="..\MonoScene.Runtime.Content\MonoScene.Runtime.Content.csproj" />
   </ItemGroup>
 
 </Project>

+ 0 - 203
src/MonoScene.Pipeline/VertexDecoder.cs

@@ -1,203 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
-
-using Microsoft.Xna.Framework.Graphics;
-
-using XNAV2 = Microsoft.Xna.Framework.Vector2;
-using XNAV3 = Microsoft.Xna.Framework.Vector3;
-using XNAV4 = Microsoft.Xna.Framework.Vector4;
-using XNACOLOR = Microsoft.Xna.Framework.Color;
-
-using PACKED = Microsoft.Xna.Framework.Graphics.PackedVector;
-
-
-namespace MonoScene.Graphics.Pipeline
-{
-    static class VertexDecoder
-    {
-        #region API
-
-        /// <summary>
-        /// Gets the current Vertex attributes as an array of <see cref="{TVertex}"/> vertices.
-        /// </summary>
-        /// <typeparam name="TVertex">A Vertex type implementing <see cref="IVertexType"/>.</typeparam>
-        /// <returns>A <see cref="{TVertex}"/> array</returns>
-        public static TVertex[] ToXnaVertices<TVertex>(this IMeshPrimitiveDecoder srcMesh)
-            where TVertex : unmanaged, IVertexType
-        {
-            var declaration = default(TVertex).VertexDeclaration;
-
-            var vsize = Marshal.SizeOf<TVertex>();
-
-            if (vsize != declaration.VertexStride) throw new ArgumentException(nameof(TVertex));
-
-            var dst = new TVertex[srcMesh.VertexCount];
-
-            PACKED.VertexInfluences skinning = default; // skin joints indices            
-
-            for (int i = 0; i < dst.Length; ++i)
-            {
-                var dstv = _VertexWriter.CreateFromArray(dst, i);
-
-                if (srcMesh.JointsWeightsCount > 0) skinning = srcMesh.GetSkinWeights(i);
-                
-                foreach (var element in declaration.GetVertexElements())
-                {
-                    switch (element.VertexElementUsage)
-                    {
-                        case VertexElementUsage.Position: dstv.SetValue(element, srcMesh.GetPosition(i)); break;
-                        case VertexElementUsage.Normal: dstv.SetValue(element, srcMesh.GetNormal(i)); break;
-                        case VertexElementUsage.Tangent: dstv.SetValue(element, srcMesh.GetTangent(i)); break;
-
-                        case VertexElementUsage.TextureCoordinate: dstv.SetValue(element, srcMesh.GetTextureCoord(i, element.UsageIndex)); break;
-                        case VertexElementUsage.Color: dstv.SetValue(element, srcMesh.GetColor(i, element.UsageIndex)); break;
-
-                        case VertexElementUsage.BlendIndices: dstv.SetValue(element, skinning.Indices); break;
-                        case VertexElementUsage.BlendWeight: dstv.SetValue(element, skinning.Weights); break;
-                    }
-                }
-            }
-
-            return dst;
-        }
-
-        #endregion
-
-        #region nested types
-        readonly ref struct _VertexWriter
-        {
-            #region constructor
-            public static _VertexWriter CreateFromArray<TVertex>(TVertex[] vvv, int idx)
-                where TVertex : unmanaged, IVertexType
-            {
-                var v = vvv.AsSpan().Slice(idx, 1);
-
-                var d = MemoryMarshal.Cast<TVertex, Byte>(v);
-
-                return new _VertexWriter(d);
-            }
-
-            public _VertexWriter(Span<Byte> vertex)
-            {
-                _Vertex = vertex;
-            }
-
-            #endregion
-
-            #region data
-
-            private readonly Span<Byte> _Vertex;
-
-            #endregion
-
-            #region API            
-
-            public void SetValue(VertexElement element, XNAV2 value)
-            {
-                if (element.VertexElementFormat == VertexElementFormat.Vector2)
-                {
-                    var dst = _Vertex.Slice(element.Offset, Marshal.SizeOf<XNAV2>());
-                    MemoryMarshal.Write(dst, ref value);
-                    return;
-                }
-
-                throw new NotImplementedException();
-            }
-
-            public void SetValue(VertexElement element, XNAV3 value)
-            {
-                if (element.VertexElementFormat == VertexElementFormat.Vector3)
-                {
-                    var dst = _Vertex.Slice(element.Offset, Marshal.SizeOf<XNAV3>());
-                    MemoryMarshal.Write(dst, ref value);
-                    return;
-                }
-
-                throw new NotImplementedException();
-            }
-
-            public void SetValue(VertexElement element, XNAV4 value)
-            {
-                var dst = _Vertex.Slice(element.Offset);
-
-                switch (element.VertexElementFormat)
-                {
-                    case VertexElementFormat.Vector4:
-                        MemoryMarshal.Write(dst, ref value);
-                        return;
-
-                    case VertexElementFormat.Color:
-                        SetValue(element, new XNACOLOR(value));
-                        return;
-
-                    case VertexElementFormat.Byte4:
-                        SetValue(element, new PACKED.Byte4(value));
-                        return;
-
-                    case VertexElementFormat.Short4:
-                        SetValue(element, new PACKED.Short4(value));
-                        return;
-
-                    case VertexElementFormat.NormalizedShort4:
-                        SetValue(element, new PACKED.NormalizedShort4(value));
-                        return;
-                }
-
-                throw new NotImplementedException();
-            }
-
-            public void SetValue(VertexElement element, PACKED.Byte4 value)
-            {
-                if (element.VertexElementFormat != VertexElementFormat.Byte4) throw new ArgumentException(nameof(element));
-
-                var dst = _Vertex.Slice(element.Offset, Marshal.SizeOf<PACKED.Byte4>());
-                MemoryMarshal.Write(dst, ref value);
-            }
-
-            public void SetValue(VertexElement element, XNACOLOR value)
-            {
-                if (element.VertexElementFormat != VertexElementFormat.Color) throw new ArgumentException(nameof(element));
-
-                var dst = _Vertex.Slice(element.Offset, Marshal.SizeOf<PACKED.Byte4>());
-                MemoryMarshal.Write(dst, ref value);
-            }
-
-            public void SetValue(VertexElement element, PACKED.Short4 value)
-            {
-                if (element.VertexElementFormat == VertexElementFormat.Short4)
-                {
-                    var dst = _Vertex.Slice(element.Offset, Marshal.SizeOf<PACKED.Short4>());
-                    MemoryMarshal.Write(dst, ref value);
-                    return;
-                }
-
-                if (element.VertexElementFormat == VertexElementFormat.Byte4)
-                {
-                    var xval = new PACKED.Byte4(value.ToVector4());
-
-                    var dst = _Vertex.Slice(element.Offset, Marshal.SizeOf<PACKED.Byte4>());
-                    MemoryMarshal.Write(dst, ref xval);
-                    return;
-                }
-
-                throw new ArgumentException(nameof(element));
-            }
-
-            public void SetValue(VertexElement element, PACKED.NormalizedShort4 value)
-            {
-                if (element.VertexElementFormat != VertexElementFormat.NormalizedShort4) throw new ArgumentException(nameof(element));
-
-                var dst = _Vertex.Slice(element.Offset, Marshal.SizeOf < PACKED.NormalizedShort4>());
-                MemoryMarshal.Write(dst, ref value);
-            }
-
-            #endregion
-        }
-
-        #endregion  
-    }
-}

+ 0 - 0
src/MonoScene.Runtime.Content/Model/AnimationTrackInfo.cs → src/MonoScene.Runtime.Content/Armature/AnimationTrackInfo.cs


+ 0 - 0
src/MonoScene.Runtime.Content/Model/ArmatureContent.cs → src/MonoScene.Runtime.Content/Armature/ArmatureContent.cs


+ 4 - 1
src/MonoScene.Runtime.Content/Model/NodeContent.cs → src/MonoScene.Runtime.Content/Armature/NodeContent.cs

@@ -71,7 +71,10 @@ namespace MonoScene.Graphics.Content
         /// </summary>
         public IReadOnlyList<int> ChildIndices => _ChildIndices;
 
-
+        /// <summary>
+        /// If true, use <see cref="LocalScale"/>, <see cref="LocalRotation"/> and <see cref="LocalTranslation"/><br/>
+        /// instead of <see cref="LocalMatrix"/>.
+        /// </summary>
         public bool UseAnimatedTransforms => _UseAnimatedTransforms;
         public XNAMAT LocalMatrix => _LocalMatrix;        
         public AnimatableProperty<XNAV3> LocalScale => _LocalScale;

+ 6 - 0
src/MonoScene.Runtime.Content/BaseContent.cs

@@ -12,6 +12,12 @@ namespace MonoScene.Graphics.Content
 
         public BaseContent(string name, Object tag) { Name = name; Tag = tag; }
 
+        public BaseContent(BaseContent other)
+        {
+            this.Name = other.Name;
+            this.Tag = other.Tag;
+        }
+
         public string Name { get; set; }
 
         /// <summary>

+ 12 - 6
src/MonoScene.Runtime.Content/VertexInfluences.cs → src/MonoScene.Runtime.Content/BoneInfluences.cs

@@ -8,12 +8,12 @@ using XNAV4 = Microsoft.Xna.Framework.Vector4;
 namespace Microsoft.Xna.Framework.Graphics.PackedVector
 {
     /// <summary>
-    /// Represents 4 vertex influences, defined as 4 bone indices and 4 weights.
+    /// Represents 4 bone influences, defined as 4 indices and 4 weights.
     /// </summary>
     [System.Diagnostics.DebuggerDisplay("{Indices} {Weights}")]
-    public struct VertexInfluences
+    public struct BoneInfluences
     {
-        public static VertexInfluences FromCollection(IEnumerable<(int Index, float Weight)> jointWeights)
+        public static BoneInfluences FromCollection(IEnumerable<(int Index, float Weight)> jointWeights)
         {
             if (jointWeights == null || !jointWeights.Any()) return default;
 
@@ -36,17 +36,23 @@ namespace Microsoft.Xna.Framework.Graphics.PackedVector
                 ++index;
             }
 
-            return new VertexInfluences(indices, weights / wsum);
+            return new BoneInfluences(indices, weights / wsum);
         }
 
-        public static readonly VertexInfluences Default = new VertexInfluences(XNAV4.Zero, XNAV4.UnitX);
+        public static readonly BoneInfluences Default = new BoneInfluences(XNAV4.Zero, XNAV4.UnitX);
 
-        public VertexInfluences(XNAV4 indices, XNAV4 weights)
+        public BoneInfluences(XNAV4 indices, XNAV4 weights)
         {
             Indices = new Short4(indices);
             Weights = weights;
         }
 
+        public BoneInfluences(Short4 indices, XNAV4 weights)
+        {
+            Indices = indices;
+            Weights = weights;
+        }
+
         public Short4 Indices;
         public XNAV4 Weights;
 

+ 14 - 0
src/MonoScene.Runtime.Content/Meshes/IndexBufferContent.cs

@@ -55,6 +55,20 @@ namespace MonoScene.Graphics.Content
             return ib;
         }
 
+        public IEnumerable<(int A, int B, int C)> EvaluateTriangles(int offset, int count, int primCount)
+        {
+            count = Math.Min(count, primCount * 3);
+
+            while(offset < count)
+            {
+                var a = (int)_Indices[offset++];
+                var b = (int)_Indices[offset++];
+                var c = (int)_Indices[offset++];
+
+                yield return (a, b, c);
+            }
+        }
+
         #endregion
     }
 }

+ 30 - 104
src/MonoScene.Runtime.Content/Meshes/MeshCollectionContent.cs

@@ -3,7 +3,8 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 
-using Microsoft.Xna.Framework.Graphics;
+using XNAV3 = Microsoft.Xna.Framework.Vector3;
+using VERTEXINFLUENCES = Microsoft.Xna.Framework.Graphics.PackedVector.BoneInfluences;
 
 namespace MonoScene.Graphics.Content
 {
@@ -12,39 +13,13 @@ namespace MonoScene.Graphics.Content
     /// </summary>
     public class MeshCollectionContent
     {
-        #region lifecycle
-
-        public static MeshCollectionContent CreateFromMeshes(IReadOnlyList<Pipeline.IMeshDecoder<MaterialContent>> srcMeshes)
-        {
-            var builder = new MeshCollectionBuilder();
-
-            for (int i = 0; i < srcMeshes.Count; ++i)
-            {
-                var srcMesh = srcMeshes[i];
-                var hasSkin = srcMesh.Primitives.Any(item => item.JointsWeightsCount > 0);
-
-                builder.AppendMesh(srcMesh);
-            }            
-
-            var dstMeshes = new MeshCollectionContent();
-            
-            dstMeshes._SharedVertexBuffers.AddRange(builder._VertexBuffers);
-            dstMeshes._SharedIndexBuffers.AddRange(builder._IndexBuffers);
-            dstMeshes._SharedMaterials.AddRange(builder._Materials);
-            dstMeshes._Meshes.AddRange(builder._Meshes);
-
-            return dstMeshes;
-        }
-
-        #endregion
-
         #region data
 
-        private readonly List<VertexBufferContent> _SharedVertexBuffers = new List<VertexBufferContent>();
-        private readonly List<IndexBufferContent> _SharedIndexBuffers = new List<IndexBufferContent>();
-        private readonly List<MaterialContent> _SharedMaterials = new List<MaterialContent>();
+        internal readonly List<VertexBufferContent> _SharedVertexBuffers = new List<VertexBufferContent>();
+        internal readonly List<IndexBufferContent> _SharedIndexBuffers = new List<IndexBufferContent>();
+        internal readonly List<MaterialContent> _SharedMaterials = new List<MaterialContent>();
 
-        private readonly List<MeshContent> _Meshes = new List<MeshContent>();
+        internal readonly List<MeshContent> _Meshes = new List<MeshContent>();
 
         #endregion
 
@@ -56,91 +31,42 @@ namespace MonoScene.Graphics.Content
         public IReadOnlyList<MeshContent> Meshes => _Meshes;
 
         #endregion
-    }
 
-    /// <summary>
-    /// Helper class used to coalesce mesh resources and build a <see cref="MeshCollectionContent"/>
-    /// </summary>
-    class MeshCollectionBuilder
-    {
-        #region data
+        #region API
 
-        // shared buffers
-        internal readonly List<VertexBufferContent> _VertexBuffers = new List<VertexBufferContent>();
-        internal readonly List<IndexBufferContent> _IndexBuffers = new List<IndexBufferContent>();        
-        internal readonly List<MaterialContent> _Materials = new List<MaterialContent>();
-        internal readonly List<MeshContent> _Meshes = new List<MeshContent>();
+        public IEnumerable<(XNAV3 A, XNAV3 B, XNAV3 C)> EvaluateTriangles(int meshIndex, Func<XNAV3, VERTEXINFLUENCES,XNAV3> vertexTransform)
+        {            
+            var srcMesh = Meshes[meshIndex];            
 
-        #endregion
-
-        #region API        
-
-        private int _UseMaterialIndex(MaterialContent material)
-        {
-            var idx = _Materials.IndexOf(material);
-            if (idx >= 0) return idx;
-
-            _Materials.Add(material);
-            return _Materials.Count - 1;
-        }        
-
-        private int _UseVertexBuffer(VertexDeclaration vdecl)
-        {
-            var vbIndex = _VertexBuffers.FindIndex(item => item.IsCompatibleWith(vdecl));
-            if (vbIndex >= 0) return vbIndex;
-
-            _VertexBuffers.Add(new VertexBufferContent());
-            return _VertexBuffers.Count - 1;
-        }
-
-        private int _UseIndexBuffer()
-        {
-            var ibIndex = _IndexBuffers.FindIndex(item => true);
-            if (ibIndex >= 0) return ibIndex;
-            
-            _IndexBuffers.Add(new IndexBufferContent());
-            return _IndexBuffers.Count -1;
-        }
-        public void AppendMesh(Pipeline.IMeshDecoder<MaterialContent> srcMesh)            
-        {
-            var dstMesh = new MeshContent();
-            dstMesh.Name = srcMesh.Name;
-            dstMesh.Tag = srcMesh.Tag;
-
-            foreach (var prim in srcMesh.Primitives)
+            foreach (var srcPart in srcMesh.Parts)
             {
-                var vdecl = Pipeline.MeshPrimitiveDecoder.GetVertexDeclaration(prim);
-
-                int vbIndex = _UseVertexBuffer(vdecl);
-                int ibIndex = _UseIndexBuffer();
+                var geo = srcPart.Geometry;
 
-                var geometry = CreateGeometry(vbIndex, ibIndex, prim, vdecl);
-                if (geometry == null) continue;
+                var srcTriangles = _SharedIndexBuffers[geo.IndexBufferIndex]
+                    .EvaluateTriangles(geo.IndexOffset,geo.IndexCount,geo.PrimitiveCount);
 
-                dstMesh.AddMeshPart(geometry, _UseMaterialIndex(prim.Material));
-            }
-
-            _Meshes.Add(dstMesh);
-        }
+                var srcVertices = _SharedVertexBuffers[geo.VertexBufferIndex].GetEvaluator(geo.VertexOffset, geo.VertexCount);                
 
-        private MeshGeometryContent CreateGeometry(int vbIndex, int ibIndex, Pipeline.IMeshPrimitiveDecoder<MaterialContent> primitive, VertexDeclaration vdecl)            
-        {
-            var partVertices = Pipeline.MeshPrimitiveDecoder.ToXnaVertices(primitive, vdecl);
-            var partTriangles = primitive.TriangleIndices.ToList();
+                foreach (var (idx0, idx1, idx2) in srcTriangles)
+                {
+                    var pos0 = srcVertices.GetPosition(idx0);
+                    var pos1 = srcVertices.GetPosition(idx1);
+                    var pos2 = srcVertices.GetPosition(idx2);
 
-            if (partTriangles.Count == 0) return null;
+                    var sjw0 = srcVertices.GetBlend(idx0);
+                    var sjw1 = srcVertices.GetBlend(idx1);
+                    var sjw2 = srcVertices.GetBlend(idx2);
 
-            var vRange = _VertexBuffers[vbIndex].AddVertices(partVertices, vdecl);
-            var iRange = _IndexBuffers[ibIndex].AddTriangleIndices(partTriangles);
+                    var a = vertexTransform(pos0, sjw0);
+                    var b = vertexTransform(pos1, sjw1);
+                    var c = vertexTransform(pos2, sjw2);
 
-            var geometry = new MeshGeometryContent();
-            geometry.SetVertices(vbIndex, vRange.Offset, vRange.Count);
-            geometry.SetIndices(ibIndex, iRange.Offset, iRange.Count, partTriangles.Count);
-
-            return geometry;
+                    yield return (a, b, c);
+                }
+            }
+            
         }
 
         #endregion
     }
-
 }

+ 13 - 0
src/MonoScene.Runtime.Content/Meshes/MeshContent.cs

@@ -7,6 +7,11 @@ namespace MonoScene.Graphics.Content
     /// <summary>
     /// Represents the geometry (vertex+index buffers) of a <see cref="MeshPartContent"/>
     /// </summary>
+    /// <remarks>
+    /// Notice that <see cref="VertexOffset"/> and <see cref="VertexCount"/><br/>
+    /// are measured in <i>vertices</i>; that allows reshaping the vertex format of<br/>
+    /// <see cref="VertexBufferContent"/> without requiring to update the byte offsets.
+    /// </remarks>
     public class MeshGeometryContent
     {
         #region data        
@@ -15,7 +20,15 @@ namespace MonoScene.Graphics.Content
         /// Logical Index into <see cref="MeshCollectionContent.SharedVertexBuffers"/>
         /// </summary>
         public int VertexBufferIndex { get; private set; }
+
+        /// <summary>
+        /// Index of the first vertex of <see cref="VertexBufferContent"/>
+        /// </summary>
         public int VertexOffset { get; private set; }
+
+        /// <summary>
+        /// Number of vertices within <see cref="VertexBufferContent"/> starting at <see cref="VertexOffset"/>.
+        /// </summary>
         public int VertexCount { get; private set; }        
 
         /// <summary>

+ 160 - 0
src/MonoScene.Runtime.Content/Meshes/VertexBufferContent.Evaluator.cs

@@ -0,0 +1,160 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using Microsoft.Xna.Framework.Graphics;
+
+using XNAV3 = Microsoft.Xna.Framework.Vector3;
+using XNAV4 = Microsoft.Xna.Framework.Vector4;
+
+using XNABYTE4 = Microsoft.Xna.Framework.Graphics.PackedVector.Byte4;
+using XNAINDICES = Microsoft.Xna.Framework.Graphics.PackedVector.Short4;
+using VERTEXINFLUENCES = Microsoft.Xna.Framework.Graphics.PackedVector.BoneInfluences;
+
+namespace MonoScene.Graphics.Content
+{
+    partial class VertexBufferContent
+    {
+        public Evaluator GetEvaluator(int vertexOffset, int vertexCount)
+        {
+            return new Evaluator(_VertexData, vertexOffset * _VertexStride, _VertexStride, vertexCount, _VertexElements);
+        }
+
+        public readonly struct Evaluator
+        {
+            #region constructor
+
+            public Evaluator(Byte[] vertexData, int byteOffset, int byteStride, int vertexCount, VertexElement[] vertexFormat)
+            {
+                if (vertexData == null) throw new ArgumentNullException(nameof(vertexData));
+                if (byteStride < 12) throw new ArgumentOutOfRangeException(nameof(byteStride));
+                if (vertexData.Length - byteOffset < byteStride * vertexCount) throw new ArgumentOutOfRangeException(nameof(vertexCount));
+                if (vertexFormat == null || vertexFormat.Length == 0) throw new ArgumentNullException(nameof(vertexFormat));
+
+                // initialize data
+
+                _VertexData = vertexData;
+                _ByteOffset = byteOffset;
+                _ByteStride = byteStride;
+                _VertexCount = vertexCount;
+
+                // initialize elements
+
+                _PositionElement = vertexFormat.FirstOrDefault(item => item.VertexElementUsage == VertexElementUsage.Position);
+                _BlendIndicesElement = vertexFormat.FirstOrDefault(item => item.VertexElementUsage == VertexElementUsage.BlendIndices);
+                _BlendWeightsElement = vertexFormat.FirstOrDefault(item => item.VertexElementUsage == VertexElementUsage.BlendWeight);
+
+                _HasSkinElements = true;
+                _HasSkinElements &= _BlendIndicesElement.VertexElementUsage == VertexElementUsage.BlendIndices;
+                _HasSkinElements &= _BlendWeightsElement.VertexElementUsage == VertexElementUsage.BlendWeight;
+
+                // initialize decoders                
+
+                var data = _VertexData;
+
+                unsafe T Cast<T>(int offset) where T : unmanaged
+                {
+                    var span = data.AsSpan(offset);
+                    #if DEBUG
+                    span = span.Slice(0, sizeof(T));
+                    #endif
+                    return System.Runtime.InteropServices.MemoryMarshal.Read<T>(span);
+                }
+
+                switch (_PositionElement.VertexElementFormat)
+                {
+                    case VertexElementFormat.Vector3: _PositionDecoder = idx => Cast<XNAV3>(idx); break;
+                    case VertexElementFormat.Vector4: _PositionDecoder = idx => Cast<XNAV3>(idx); break;                        
+                    default: _PositionDecoder = null; throw new NotSupportedException($"{_PositionElement.VertexElementFormat}");
+                }
+
+                _BlendIndicesDecoder = null;
+                _BlendWeightsDecoder = null;
+
+                if (_HasSkinElements)
+                {
+                    switch (_BlendIndicesElement.VertexElementFormat)
+                    {
+                        case VertexElementFormat.Vector4:
+                            _BlendIndicesDecoder = idx => new XNAINDICES(Cast<XNAV4>(idx)); break;
+                        case VertexElementFormat.Color:
+                        case VertexElementFormat.Byte4:
+                            _BlendIndicesDecoder = idx => new XNAINDICES(Cast<XNABYTE4>(idx).ToVector4()); break;
+                        case VertexElementFormat.Short4:
+                            _BlendIndicesDecoder = idx => Cast<XNAINDICES>(idx); break;
+                        default:
+                            _HasSkinElements = false; break;
+                    }
+
+                    switch (_BlendWeightsElement.VertexElementFormat)
+                    {
+                        case VertexElementFormat.Vector4:
+                            _BlendWeightsDecoder = idx => Cast<XNAV4>(idx); break;
+                        case VertexElementFormat.Color:
+                        case VertexElementFormat.Byte4:
+                            _BlendWeightsDecoder = idx => Cast<Microsoft.Xna.Framework.Graphics.PackedVector.NormalizedByte4>(idx).ToVector4(); break;
+                        case VertexElementFormat.NormalizedShort4:
+                            _BlendWeightsDecoder = idx => Cast<Microsoft.Xna.Framework.Graphics.PackedVector.NormalizedShort4>(idx).ToVector4(); break;
+                        default:
+                            _HasSkinElements = false; break;
+                    }
+                }
+
+                // if blend elements don't exist, or have unsupported format, fall back to default:
+
+                if (!_HasSkinElements)
+                {
+                    _BlendIndicesDecoder = idx => new XNAINDICES(0, 0, 0, 0);
+                    _BlendWeightsDecoder = idx => new XNAV4(1, 0, 0, 0);
+                }
+            }
+
+            #endregion
+
+            #region data
+
+            private readonly Byte[] _VertexData;
+            private readonly int _ByteOffset;
+            private readonly int _ByteStride;
+            private readonly int _VertexCount;
+
+            private readonly VertexElement _PositionElement;
+            private readonly VertexElement _BlendIndicesElement;
+            private readonly VertexElement _BlendWeightsElement;
+
+            private readonly bool _HasSkinElements;
+
+            private readonly Converter<int, XNAV3> _PositionDecoder;            
+            private readonly Converter<int, XNAINDICES> _BlendIndicesDecoder;           
+            private readonly Converter<int, XNAV4> _BlendWeightsDecoder;
+
+            #endregion
+
+            #region API
+
+            private int _GetVertexOffset(int index)
+            {
+                if (index < 0 || index >= _VertexCount) throw new ArgumentOutOfRangeException(nameof(index));
+                index *= _ByteStride;
+                index += _ByteOffset;
+                return index;
+            }
+
+            public XNAV3 GetPosition(int index) { return _PositionDecoder(_GetVertexOffset(index) + _PositionElement.Offset); }
+            public XNAINDICES GetBlendIndices(int index) { return _BlendIndicesDecoder(_GetVertexOffset(index) + _BlendIndicesElement.Offset); }
+            public XNAV4 GetBlendWeights(int index) { return _BlendWeightsDecoder(_GetVertexOffset(index) + _BlendWeightsElement.Offset); }
+
+            public VERTEXINFLUENCES GetBlend(int index)
+            {
+                if (!_HasSkinElements) return VERTEXINFLUENCES.Default;
+
+                var indices = GetBlendIndices(index);
+                var weights = GetBlendWeights(index);
+                return new VERTEXINFLUENCES(indices, weights);
+            }
+
+            #endregion
+        }
+    }
+}

+ 28 - 6
src/MonoScene.Runtime.Content/Meshes/VertexBufferContent.cs

@@ -10,13 +10,28 @@ namespace MonoScene.Graphics.Content
     /// <summary>
     /// Represents a sequence of vertices, and can be used to create <see cref="VertexBuffer"/> resources.
     /// </summary>
-    public class VertexBufferContent
+    public partial class VertexBufferContent
     {
         #region Data
 
+        /// <summary>
+        /// Vertex format
+        /// </summary>
         private VertexElement[] _VertexElements;
-        private int _VertexStride;        
+
+        /// <summary>
+        /// Number of bytes to advanced to the next vertex
+        /// </summary>
+        private int _VertexStride;
+
+        /// <summary>
+        /// Total vertex count.
+        /// </summary>
         private int _VertexCount;
+
+        /// <summary>
+        /// Raw vertex data
+        /// </summary>
         private Byte[] _VertexData;
 
         #endregion
@@ -29,6 +44,11 @@ namespace MonoScene.Graphics.Content
 
         #region API
 
+        /// <summary>
+        /// Checks if <typeparamref name="TVertex"/> declaration is compatible with this <see cref="VertexBufferContent"/>.
+        /// </summary>
+        /// <typeparam name="TVertex">The vertex type to check.</typeparam>
+        /// <returns>True if they're compatible.</returns>
         public bool IsCompatibleWith<TVertex>()
             where TVertex:struct, IVertexType
         {
@@ -49,14 +69,14 @@ namespace MonoScene.Graphics.Content
             
         }
 
-        public (int Offset, int Count) AddVertices<T>(T[] vertices)
+        public (int VertexOffset, int VertexCount) AddVertices<T>(T[] vertices)
             where T : struct, IVertexType
         {
             var data = System.Runtime.InteropServices.MemoryMarshal.Cast<T, Byte>(vertices);
             return AddVertices(data, default(T).VertexDeclaration);
         }
 
-        public (int Offset, int Count) AddVertices(ReadOnlySpan<Byte> vertexData, VertexDeclaration vertexFormat)
+        public (int VertexOffset, int VertexCount) AddVertices(ReadOnlySpan<Byte> vertexData, VertexDeclaration vertexFormat)
         {
             var count = vertexData.Length / vertexFormat.VertexStride;
 
@@ -87,16 +107,18 @@ namespace MonoScene.Graphics.Content
 
         public VertexBuffer CreateVertexBuffer(GraphicsDevice graphics)
         {
-            // https://github.com/MonoGame/MonoGame/blob/655b4f05c4aeb50fe416c2dc92a9afcf294e2fd8/MonoGame.Framework/Content/ContentReaders/VertexBufferReader.cs#L12
+            // https://github.com/MonoGame/MonoGame/blob/develop/MonoGame.Framework/Content/ContentReaders/VertexBufferReader.cs#L12
 
             var vdecl = new VertexDeclaration(_VertexStride, _VertexElements);
             var vb = new VertexBuffer(graphics, vdecl, _VertexCount, BufferUsage.None);            
 
-            vb.SetData(_VertexData, 0, _VertexCount * vdecl.VertexStride );            
+            vb.SetData(_VertexData, 0, _VertexCount * vdecl.VertexStride);            
 
             return vb;
         }
 
         #endregion
     }
+
+    
 }

+ 130 - 0
src/MonoScene.Runtime.Content/Model/DrawableContent.cs

@@ -0,0 +1,130 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+using XNAMAT = Microsoft.Xna.Framework.Matrix;
+
+namespace MonoScene.Graphics.Content
+{
+    public abstract class DrawableContent : BaseContent
+    {
+        #region lifecycle
+
+        public static DrawableContent CreateRigid(int meshIndex, NodeContent node)
+        {
+            return new RigidDrawableContent(meshIndex, node);
+        }
+
+        public static DrawableContent CreateSkinned(int meshIndex, NodeContent morphNode, (NodeContent, XNAMAT)[] skinNodes)
+        {
+            return new SkinnedDrawableContent(meshIndex, morphNode, skinNodes);
+        }
+
+        protected DrawableContent(DrawableContent other)
+            : base(other)
+        {
+            this._MeshIndex = other._MeshIndex;
+        }
+
+        protected DrawableContent(int logicalMeshIndex)            
+        {
+            _MeshIndex = logicalMeshIndex;
+        }
+
+        #endregion
+
+        #region data
+        
+        private readonly int _MeshIndex;
+
+        #endregion
+
+        #region properties
+
+        /// <summary>
+        /// An index into a <see cref="MeshCollectionContent.Meshes"/>
+        /// </summary>
+        public int MeshIndex => _MeshIndex;
+
+        #endregion
+    }
+
+    public class RigidDrawableContent : DrawableContent
+    {
+        #region lifecycle
+
+        protected RigidDrawableContent(RigidDrawableContent other)
+            : base(other)
+        {
+            this.NodeIndex = other.NodeIndex;
+        }
+
+        internal RigidDrawableContent(int meshIndex, NodeContent node)
+            : base(meshIndex)
+        {
+            NodeIndex = node.ThisIndex;
+        }
+
+        #endregion
+
+        #region data
+
+        /// <summary>
+        /// An index into a <see cref="MeshCollectionContent.Meshes"/>
+        /// </summary>
+        protected readonly int NodeIndex;
+
+        #endregion
+    }
+
+    public class SkinnedDrawableContent : DrawableContent
+    {
+        #region lifecycle
+
+        protected SkinnedDrawableContent(SkinnedDrawableContent other)
+            : base(other)
+        {
+            this.MorphNodeIndex = other.MorphNodeIndex;
+            this.JointsNodeIndices = other.JointsNodeIndices;
+            this.JointsBindMatrices = other.JointsBindMatrices;
+        }
+
+        internal SkinnedDrawableContent(int meshIndex, NodeContent morphNode, (NodeContent, XNAMAT)[] skinNodes)
+            : base(meshIndex)
+        {
+            // _MorphNodeIndex = indexFunc(morphNode);
+
+            JointsNodeIndices = new int[skinNodes.Length];
+            JointsBindMatrices = new XNAMAT[skinNodes.Length];
+
+            for (int i = 0; i < JointsNodeIndices.Length; ++i)
+            {
+                var (j, ibm) = skinNodes[i];
+
+                JointsNodeIndices[i] = j.ThisIndex;
+                JointsBindMatrices[i] = ibm;
+            }
+        }
+
+        #endregion
+
+        #region data
+
+        /// <summary>
+        /// An index into a <see cref="ArmatureContent"/> which holds the morph state.
+        /// </summary>
+        protected readonly int MorphNodeIndex;
+
+        /// <summary>
+        /// Bone indices into <see cref="ArmatureContent"/>.
+        /// </summary>
+        protected readonly int[] JointsNodeIndices;
+
+        /// <summary>
+        /// Inverse Bind Matrices associated to <see cref="JointsNodeIndices"/>.
+        /// </summary>
+        protected readonly XNAMAT[] JointsBindMatrices;
+
+        #endregion                
+    }
+}

+ 43 - 0
src/MonoScene.Runtime.Content/Model/ModelContent.cs

@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MonoScene.Graphics.Content
+{
+    /// <summary>
+    /// Represents a 3D model within <see cref="ModelCollectionContent"/>.
+    /// </summary>
+    /// <remarks>
+    /// A <see cref="ModelContent"/> just contains<br/>
+    /// a batch of <see cref="DrawableContent"/> commands.<br/>
+    /// Actual geometry, materials and texture data is stored<br/>
+    /// in <see cref="ModelCollectionContent"/> and might be<br/>
+    /// shared by other <see cref="ModelContent"/> elements<br/>
+    /// within the collection.
+    /// </remarks>
+    public class ModelContent : BaseContent
+    {
+        #region lifecycle        
+
+        public ModelContent(int armatureIndex, IEnumerable<DrawableContent> drawables)            
+        {
+            ArmatureLogicalIndex = armatureIndex;
+            _DrawableReferences.AddRange(drawables);
+        }
+
+        #endregion
+
+        #region data
+
+        private readonly List<DrawableContent> _DrawableReferences = new List<DrawableContent>();
+
+        #endregion
+
+        #region properties
+        public int ArmatureLogicalIndex { get; set; }
+        public IReadOnlyList<DrawableContent> DrawableReferences => _DrawableReferences;
+        public Microsoft.Xna.Framework.BoundingSphere ModelBounds { get; set; }        
+
+        #endregion        
+    }
+}

+ 7 - 19
src/MonoScene.Pipeline/ModelCollectionContent.cs → src/MonoScene.Runtime.Content/ModelCollectionContent.cs

@@ -2,9 +2,7 @@
 using System.Collections.Generic;
 using System.Text;
 
-using MonoScene.Graphics.Content;
-
-namespace MonoScene.Graphics.Pipeline
+namespace MonoScene.Graphics.Content
 {
     /// <summary>
     /// Represents a data only representation of a collection of models with shared data resources.
@@ -13,7 +11,7 @@ namespace MonoScene.Graphics.Pipeline
     {
         #region constructor
 
-        public ModelCollectionContent(MeshCollectionContent meshes, ArmatureContent[] armatures, ModelTemplate[] models, int defaultModelIndex)
+        public ModelCollectionContent(MeshCollectionContent meshes, ArmatureContent[] armatures, ModelContent[] models, int defaultModelIndex)
         {
             _SharedMeshes = meshes;
             _SharedArmatures = armatures;
@@ -28,33 +26,23 @@ namespace MonoScene.Graphics.Pipeline
         /// <summary>
         /// Multiple <see cref="ModelTemplate"/> at <see cref="_Models"/> might share the same meshes.
         /// </summary>
-        private MeshCollectionContent _SharedMeshes;
+        public MeshCollectionContent _SharedMeshes;
 
         /// <summary>
         /// Multiple <see cref="ModelTemplate"/> at <see cref="_Models"/> might share the same <see cref="ArmatureTemplate"/>.
         /// </summary>
-        private ArmatureContent[] _SharedArmatures;
+        public ArmatureContent[] _SharedArmatures;
 
         /// <summary>
         /// Models available in this collection.
         /// </summary>
-        private ModelTemplate[] _Models;
+        public ModelContent[] _Models;
 
         /// <summary>
         /// Default model index
         /// </summary>
-        private readonly int _DefaultModelIndex;
-
-        #endregion
-
-        #region API
+        public readonly int _DefaultModelIndex;
 
-        public DeviceModelCollection ToDeviceModelCollection(MeshFactory factory)
-        {
-            var meshes = factory.CreateMeshCollection(_SharedMeshes);
-            return new DeviceModelCollection(meshes, _SharedArmatures, _Models, _DefaultModelIndex);
-        }
-
-        #endregion
+        #endregion        
     }
 }

+ 8 - 2
src/MonoScene.Runtime.Content/MonoScene.Runtime.Content.csproj

@@ -2,9 +2,15 @@
 
   <PropertyGroup>
     <TargetFramework>netstandard2.0</TargetFramework>
-    <RootNamespace>Microsoft.Xna.Framework.RuntimeContent.Graphics</RootNamespace>
+    <RootNamespace>MonoScene.Graphics.Content</RootNamespace>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-  </PropertyGroup>
+  </PropertyGroup>  
+
+  <ItemGroup>
+    <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
+      <_Parameter1>MonoScene.Pipeline</_Parameter1>
+    </AssemblyAttribute>
+  </ItemGroup>
 
   <ItemGroup>
     <PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.1825-develop" PrivateAssets="all" />

+ 43 - 8
src/MonoScene.Runtime.Content/README.MD

@@ -1,11 +1,46 @@
-#### Microsoft.Xna.Framework.Content.Runtime.Graphics
+#### MonoScene.Runtime.Content
 
-In this project you can find the helper classes that can be used to build the meshes
-and materials of a 3D model.
+This project contains all the classes required to define a 3d model in memory,
+and eventually, to serialize it.
 
-Why `Content.Runtime.Graphics` namespace, instead of the classic `Content.Pipeline.Graphics` ?
+The architecture loosely replicates the architecture of the glTF standard,
+which is a feature rich model architecture.
 
-The classes you usually find in the **Pipeline** namespace are designed so they can be used only within the
-context of the MonoGame Content Builder tool (MGCB), which runs at compile time only. But The classes found in
-the **Runtime** should be used not only at compile time, but also at runtime. This will allow, with some
-limitations, to load third party formats at runtime.
+Architecture overview:
+
+- [ModelCollectionContent](ModelCollectionContent.cs)
+  - Resources
+    - [MeshCollectionContent](Meshes/MeshCollectionContent.cs)
+      - [MeshContent](Meshes/MeshContent.cs) [n]
+        - [MeshPartContent](Meshes/MeshContent.cs) [n]
+          - [MeshGeometryContent](Meshes/MeshContent.cs)
+      - [VertexBufferContent](Meshes/VertexBufferContent.cs) [n]
+      - [IndexBufferContent](Meshes/IndexBufferContent.cs) [n]
+      - [MaterialContent](Materials/MaterialContent.cs) [n]
+        - [MaterialChannelContent](Materials/MaterialChannelContent.cs) [n]
+          - [SamplerStateContent](Materials/SamplerStateContent.cs)
+          - Texture
+    - [ArmatureContent](Armature/ArmatureContent.cs) [n]
+      - [NodeContent](Armature/NodeContent.cs) [n] 
+        - [Scale Animations](AnimatableProperty.cs)    
+        - [Rotation Animations](AnimatableProperty.cs)    
+        - [Translation Animations](AnimatableProperty.cs)    
+  - Graph
+    - [ModelContent](Model/ModelContent.cs) [n] __<- entry point for a single model__
+      - [DrawableContent](Model/DrawableContent.cs) [n]
+        - RigidDrawableContent - References a rigid mesh in resources
+        - SkinnedDrawableContent - References a skinned mesh in resources
+
+
+#### Comparing with MonoGame architecture
+
+MonoGame's content classes are designed to be used by the content processor pipeline,
+which can only be used at build time. This prevents loading public models at runtime.
+
+MonoScene's content classes are designed to be used both for content processing at
+build time, and also for content loading and procedural creation at runtime.
+
+One important aspect of 3D models architecture is _resource sharing_; in MonoGame, this
+is resolved at build time by the content processor pipeline, but since we require to load
+models at runtime too, we cannot process the models as deeply as an offline pipeline, that
+means that resource sharing is isolated to the context of a single source model.

+ 1 - 1
src/MonoScene.Runtime.EffectsPBR/README.MD

@@ -1,4 +1,4 @@
-### MonoGame.Framework.Graphics.PBR
+### MonoScene.Runtime.EffectsPBR
 
 Extends Monogame with additional PBR effects:
 

+ 60 - 0
src/MonoScene.Runtime.Model3D/Content/PostProcessor.cs

@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+using XNAV3 = Microsoft.Xna.Framework.Vector3;
+
+namespace MonoScene.Graphics.Content
+{
+    /// <summary>
+    /// Utility class for <see cref="ModelCollectionContent"/> post processing.
+    /// </summary>
+    /// <remarks>
+    /// This class should be located in the MonoScene.Pipeline project, but we internally
+    /// require <see cref="ModelInstance"/> to do the evalution, so until a better solution is
+    /// found, this will stay here.
+    /// </remarks>
+    public static class PostProcessor
+    {
+        public static ModelCollectionContent Postprocess(ModelCollectionContent models)
+        {
+            for(int i=0; i < models._Models.Length; ++i)
+            {
+                var srcModel = new ModelTemplate(models, i).CreateInstance();
+                var bounds = EvaluateBoundingSphere(srcModel, models._SharedMeshes);
+
+                var dstModel = models._Models[i];
+                dstModel.ModelBounds = bounds;
+            }
+
+            return models;
+        }
+
+        private static Microsoft.Xna.Framework.BoundingSphere EvaluateBoundingSphere(ModelInstance srcModel, MeshCollectionContent srcMeshes)
+        {
+            var triangles = EvaluateTriangles(srcModel, srcMeshes)
+                .SelectMany(item => new[] { item.A, item.B, item.C });
+
+            return Microsoft.Xna.Framework.BoundingSphere.CreateFromPoints(triangles);
+        }
+
+        private static IEnumerable<(XNAV3 A, XNAV3 B, XNAV3 C)> EvaluateTriangles(ModelInstance srcModel, MeshCollectionContent srcMeshes)
+        {
+            foreach (var drawable in srcModel.DrawableInstances)
+            {
+                var controller = drawable.Transform;
+
+                XNAV3 transform(XNAV3 pos, Microsoft.Xna.Framework.Graphics.PackedVector.BoneInfluences blend)
+                {
+                    return controller.TransformPosition(pos, null, blend);
+                }
+
+                foreach (var tri in srcMeshes.EvaluateTriangles(drawable.Template.MeshIndex, transform))
+                {
+                    yield return tri;
+                }
+            }
+        }        
+    }
+}

+ 2 - 2
src/MonoScene.Runtime.Model3D/ModelArchitecture.md

@@ -67,8 +67,8 @@ for some classes, there's two class variants
 |-|-|
 |[ModelTemplate](ModelGraph/ModelTemplate.cs)|[ModelInstance](ModelGraph/ModelInstance.cs)|
 |[DrawableTemplate](ModelGraph/DrawableTemplate.cs)|[DrawableInstance](ModelGraph/DrawableInstance.cs)|
-|[ArmatureTemplate](ModelGraph/ArmatureTemplate.cs)|[ArmatureInstance](ModelGraph/ArmatureInstance.cs)|
-|[NodeTemplate](ModelGraph/NodeTemplate.cs)|[NodeInstance](ModelGraph/NodeInstance.cs)|
+|[ArmatureContent](../MonoScene.Runtime.Content/Armature/ArmatureContent.cs)|[ArmatureInstance](ModelGraph/ArmatureInstance.cs)|
+|[NodeContent](../MonoScene.Runtime.Content/Armature/NodeContent.cs)|[NodeInstance](ModelGraph/NodeInstance.cs)|
 
 
 So what's this about?

+ 23 - 83
src/MonoScene.Runtime.Model3D/ModelGraph/DrawableTemplate.cs

@@ -37,77 +37,37 @@ namespace MonoScene.Graphics
     }
        
 
-    /// <summary>
-    /// Defines a reference to a drawable mesh
-    /// </summary>
-    /// <remarks>
-    /// This class is the 'glue' that binds a mesh with a <see cref="NodeTemplate"/> so we
-    /// can calculate the local transform matrix of the mesh we want to render.
-    /// </remarks>    
-    abstract class DrawableTemplate : BaseContent, IDrawableTemplate
-    {
-        #region lifecycle
+    
 
-        protected DrawableTemplate(string name, int logicalMeshIndex)
-            : base(name)
-        {            
-            _LogicalMeshIndex = logicalMeshIndex;            
+    static class DrawableTemplateFactory
+    {
+        public static IDrawableTemplate UpcastToTemplate(this DrawableContent content)
+        {
+            if (content is RigidDrawableContent rigid) return new RigidDrawableTemplate(rigid);
+            if (content is SkinnedDrawableContent skinned) return new SkinnedDrawableTemplate(skinned);
+            throw new NotImplementedException();
         }
-
-        #endregion
-
-        #region data
-
-        private readonly int _LogicalMeshIndex;
-
-        #endregion
-
-        #region properties
-        
-        /// <summary>
-        /// An index into a <see cref="MeshCollection"/>
-        /// </summary>
-        public int MeshIndex => _LogicalMeshIndex;
-
-        #endregion
-
-        #region API
-
-        public abstract IMeshTransform CreateGeometryTransform();
-
-        public abstract void UpdateGeometryTransform(IMeshTransform geoxform, ArmatureInstance armature);
-
-        #endregion
     }
 
     /// <summary>
     /// Defines a reference to a drawable rigid mesh
     /// </summary>
-    sealed class RigidDrawableTemplate : DrawableTemplate
+    sealed class RigidDrawableTemplate : RigidDrawableContent , IDrawableTemplate
     {
         #region lifecycle
 
-        public RigidDrawableTemplate(int meshIndex, NodeContent node)
-            : base(node.Name, meshIndex)
-        {
-            _NodeIndex = node.ThisIndex;
-        }
-
-        #endregion
-
-        #region data
-
-        private readonly int _NodeIndex;
+        public RigidDrawableTemplate(RigidDrawableContent content)
+            : base(content) { }
 
-        #endregion
+        #endregion        
 
         #region API
 
-        public override IMeshTransform CreateGeometryTransform() { return new MeshRigidTransform(); }
+        public IMeshTransform CreateGeometryTransform() { return new MeshRigidTransform(); }
 
-        public override void UpdateGeometryTransform(IMeshTransform rigidTransform, ArmatureInstance armature)
+        public void UpdateGeometryTransform(IMeshTransform rigidTransform, ArmatureInstance armature)
         {
-            var node = armature.LogicalNodes[_NodeIndex];
+            var node = armature.LogicalNodes[NodeIndex];
 
             var statxform = (MeshRigidTransform)rigidTransform;
             statxform.Update(node.ModelMatrix);
@@ -120,46 +80,26 @@ namespace MonoScene.Graphics
     /// <summary>
     /// Defines a reference to a drawable skinned mesh
     /// </summary>
-    sealed class SkinnedDrawableTemplate : DrawableTemplate
+    sealed class SkinnedDrawableTemplate : SkinnedDrawableContent, IDrawableTemplate
     {
         #region lifecycle
 
-        public SkinnedDrawableTemplate(int meshIndex, NodeContent morphNode, string ownerNname, (NodeContent, XNAMAT)[] skinNodes)
-            : base(ownerNname, meshIndex)
-        {
-            // _MorphNodeIndex = indexFunc(morphNode);
-
-            _JointsNodeIndices = new int[skinNodes.Length];
-            _BindMatrices = new XNAMAT[skinNodes.Length];
-
-            for (int i = 0; i < _JointsNodeIndices.Length; ++i)
-            {
-                var (j, ibm) = skinNodes[i];
-
-                _JointsNodeIndices[i] = j.ThisIndex;
-                _BindMatrices[i] = ibm;
-            }
-        }
-
-        #endregion
-
-        #region data
-
-        private readonly int _MorphNodeIndex;
-        private readonly int[] _JointsNodeIndices;
-        private readonly XNAMAT[] _BindMatrices;
+        public SkinnedDrawableTemplate(SkinnedDrawableContent content)
+            : base(content) { }
 
         #endregion
 
         #region API
 
-        public override IMeshTransform CreateGeometryTransform() { return new MeshSkinTransform(); }
+        public IMeshTransform CreateGeometryTransform() { return new MeshSkinTransform(); }
 
-        public override void UpdateGeometryTransform(IMeshTransform skinnedTransform, ArmatureInstance armature)
+        public void UpdateGeometryTransform(IMeshTransform skinnedTransform, ArmatureInstance armature)
         {
             var skinxform = (MeshSkinTransform)skinnedTransform;
 
-            skinxform.Update(_JointsNodeIndices.Length, idx => _BindMatrices[idx], idx => armature.LogicalNodes[_JointsNodeIndices[idx]].ModelMatrix);
+            skinxform.Update(this.JointsNodeIndices.Length
+                , idx => this.JointsBindMatrices[idx]
+                , idx => armature.LogicalNodes[this.JointsNodeIndices[idx]].ModelMatrix);
 
             // skinxform.Update(instances[_MorphNodeIndex].MorphWeights, false);
         }

+ 2 - 2
src/MonoScene.Runtime.Model3D/ModelGraph/MeshTransforms.cs

@@ -5,8 +5,8 @@ using System.Text;
 using V3 = Microsoft.Xna.Framework.Vector3;
 using V4 = Microsoft.Xna.Framework.Vector4;
 using TRANSFORM = Microsoft.Xna.Framework.Matrix;
-using VERTEXINFLUENCES = Microsoft.Xna.Framework.Graphics.PackedVector.VertexInfluences;
-using MORPHINFLUENCES = Microsoft.Xna.Framework.Graphics.PackedVector.VertexInfluences;
+using VERTEXINFLUENCES = Microsoft.Xna.Framework.Graphics.PackedVector.BoneInfluences;
+using MORPHINFLUENCES = Microsoft.Xna.Framework.Graphics.PackedVector.BoneInfluences;
 
 namespace MonoScene.Graphics
 {

+ 12 - 0
src/MonoScene.Runtime.Model3D/ModelGraph/ModelCollection.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Linq;
 using System.Text;
 
 using MonoScene.Graphics.Content;
@@ -17,6 +18,17 @@ namespace MonoScene.Graphics
     {
         #region lifecycle
 
+        public static DeviceModelCollection CreateFrom(ModelCollectionContent content, Converter<MeshCollectionContent, MeshCollection> meshesConverter)
+        {
+            var meshes = meshesConverter(content._SharedMeshes);
+
+            var models = content._Models
+                .Select((item, idx) => new ModelTemplate(content,idx))
+                .ToArray();
+            
+            return new DeviceModelCollection(meshes, content._SharedArmatures, models, content._DefaultModelIndex);           
+        }
+
         public DeviceModelCollection(MeshCollection meshes, ArmatureContent[] armatures, ModelTemplate[] models, int defaultModelIndex)
             : base(meshes, armatures, models,defaultModelIndex)
         {

+ 6 - 5
src/MonoScene.Runtime.Model3D/ModelGraph/ModelInstance.cs

@@ -7,6 +7,7 @@ using Microsoft.Xna.Framework.Graphics;
 using XNAV3 = Microsoft.Xna.Framework.Vector3;
 using XNAMAT = Microsoft.Xna.Framework.Matrix;
 
+
 namespace MonoScene.Graphics
 {
     /// <summary>
@@ -23,11 +24,11 @@ namespace MonoScene.Graphics
         internal ModelInstance(ModelTemplate parent)
         {
             _Parent = parent;
-            
+
             _Armature = new ArmatureInstance(_Parent._Armature);
             _Armature.SetPoseTransforms();
 
-            _WorldMatrix = XNAMAT.Identity;            
+            _WorldMatrix = XNAMAT.Identity;
 
             _DrawableTemplates = _Parent._DrawableReferences;
             _DrawableTransforms = new IMeshTransform[_DrawableTemplates.Length];
@@ -35,7 +36,7 @@ namespace MonoScene.Graphics
             for (int i = 0; i < _DrawableTransforms.Length; ++i)
             {
                 _DrawableTransforms[i] = _DrawableTemplates[i].CreateGeometryTransform();
-            }            
+            }
         }
 
         #endregion
@@ -59,7 +60,7 @@ namespace MonoScene.Graphics
 
         public string Name => _Parent.Name;
 
-        public Object Tag => _Parent.Tag;        
+        public Object Tag => _Parent.Tag;
 
         public ArmatureInstance Armature => _Armature;
 
@@ -301,6 +302,6 @@ namespace MonoScene.Graphics
             }
         }
 
-        #endregion
+        #endregion                
     }
 }

+ 21 - 3
src/MonoScene.Runtime.Model3D/ModelGraph/ModelTemplate.cs

@@ -17,7 +17,21 @@ namespace MonoScene.Graphics
     /// </summary>
     public class ModelTemplate : BaseContent
     {
-        #region lifecycle        
+        #region lifecycle
+
+        public ModelTemplate(ModelCollectionContent collection, int index)
+        {
+            var srcModel = collection._Models[index];
+
+            _Armature = collection._SharedArmatures[srcModel.ArmatureLogicalIndex];
+
+            _ModelBounds = srcModel.ModelBounds;
+
+            _DrawableReferences = srcModel
+                .DrawableReferences
+                .Select(item => item.UpcastToTemplate())
+                .ToArray();
+        }
 
         public ModelTemplate(string modelName, ArmatureContent armature, IDrawableTemplate[] drawables)
             : base(modelName)
@@ -35,14 +49,16 @@ namespace MonoScene.Graphics
         // this is the collection of "what needs to be rendered", and it binds meshes with armatures
         internal readonly IDrawableTemplate[] _DrawableReferences;
 
+        private readonly Microsoft.Xna.Framework.BoundingSphere _ModelBounds;
+
         private IMeshCollection _SharedMeshes;
-        private Effect[] _SharedEffects;
+        private Effect[] _SharedEffects;        
 
         #endregion
 
         #region properties        
 
-        public Microsoft.Xna.Framework.BoundingSphere ModelBounds { get; set; }
+        public Microsoft.Xna.Framework.BoundingSphere ModelBounds => _ModelBounds;
 
         public IMeshCollection Meshes
         {
@@ -51,6 +67,8 @@ namespace MonoScene.Graphics
             {
                 _SharedMeshes = value;
                 _SharedEffects = null;
+
+                // if we change the meshes, we probably need to rebuild _ModelBounds.
             }
         }
 

+ 15 - 53
src/MonoScene.Pipeline/MeshFactory.cs → src/MonoScene.Runtime.Model3D/Pipeline/DeviceMeshFactory.cs

@@ -11,12 +11,12 @@ using XNAV3 = Microsoft.Xna.Framework.Vector3;
 
 namespace MonoScene.Graphics.Pipeline
 {
-    public abstract class MeshFactory<TMaterial>
+    public abstract class DeviceMeshFactory<TMaterial>
         where TMaterial : class
     {
         #region lifecycle
 
-        public MeshFactory(GraphicsDevice device)
+        public DeviceMeshFactory(GraphicsDevice device)
         {
             _Device = device;
 
@@ -29,7 +29,7 @@ namespace MonoScene.Graphics.Pipeline
 
         private readonly GraphicsDevice _Device;
         
-        private readonly Dictionary<TMaterial, MeshPrimitiveMaterial> _Materials = new Dictionary<TMaterial, MeshPrimitiveMaterial>();        
+        private readonly Dictionary<TMaterial, DeviceMeshPrimitiveMaterial> _Materials = new Dictionary<TMaterial, DeviceMeshPrimitiveMaterial>();        
 
         /// <summary>
         /// Gathers all disposable resources shared by the collection of meshes:
@@ -55,28 +55,27 @@ namespace MonoScene.Graphics.Pipeline
 
         protected TextureFactory<Byte[]> FileContentTextureFactory => _TextureFactory;        
 
-        protected abstract MeshPrimitiveMaterial ConvertMaterial(TMaterial material, bool mustSupportSkinning);
+        protected abstract DeviceMeshPrimitiveMaterial ConvertMaterial(TMaterial material, bool mustSupportSkinning);
 
         #endregion        
     }
 
-    public abstract class MeshFactory : MeshFactory<MaterialContent>
+    public abstract class DeviceMeshFactory : DeviceMeshFactory<MaterialContent>
     {
         #region lifecycle
 
-        public MeshFactory(GraphicsDevice device) : base(device)
-        {
-        }
+        public DeviceMeshFactory(GraphicsDevice device)
+            : base(device) { }
 
         #endregion
 
         #region overridable API
 
-        protected override MeshPrimitiveMaterial ConvertMaterial(MaterialContent srcMaterial, bool isSkinned)
+        protected override DeviceMeshPrimitiveMaterial ConvertMaterial(MaterialContent srcMaterial, bool isSkinned)
         {
             var effect = CreateEffect(srcMaterial, isSkinned);
 
-            var material = new MeshPrimitiveMaterial();
+            var material = new DeviceMeshPrimitiveMaterial();
 
             material.Effect = effect;
             material.DoubleSided = srcMaterial.DoubleSided;
@@ -89,44 +88,7 @@ namespace MonoScene.Graphics.Pipeline
 
         #endregion
 
-        #region
-
-        public static IEnumerable<(XNAV3 A, XNAV3 B, XNAV3 C)> EvaluateTriangles(ModelInstance instance, IReadOnlyList<IMeshDecoder<MaterialContent>> meshes)
-        {
-            foreach(var drawable in instance.DrawableInstances)
-            {
-                var srcMesh = meshes[drawable.Template.MeshIndex];
-                var srcXfrm = drawable.Transform;
-
-                foreach(var prim in srcMesh.Primitives)
-                {
-                    foreach (var (idx0, idx1, idx2) in prim.TriangleIndices)
-                    {
-                        var pos0 = prim.GetPosition(idx0);
-                        var pos1 = prim.GetPosition(idx1);
-                        var pos2 = prim.GetPosition(idx2);
-
-                        var sjw0 = prim.GetSkinWeights(idx0);
-                        var sjw1 = prim.GetSkinWeights(idx1);
-                        var sjw2 = prim.GetSkinWeights(idx2);
-
-                        var a = srcXfrm.TransformPosition(pos0, null, sjw0);
-                        var b = srcXfrm.TransformPosition(pos1, null, sjw1);
-                        var c = srcXfrm.TransformPosition(pos2, null, sjw2);
-
-                        yield return (a, b, c);
-                    }
-                }
-            }
-        }
-
-        public static Microsoft.Xna.Framework.BoundingSphere EvaluateBoundingSphere(ModelInstance instance, IReadOnlyList<IMeshDecoder<MaterialContent>> meshes)
-        {
-            var triangles = EvaluateTriangles(instance, meshes)
-                .SelectMany(item => new[] { item.A, item.B, item.C });
-
-            return Microsoft.Xna.Framework.BoundingSphere.CreateFromPoints(triangles);
-        }
+        #region static API        
 
         public MeshCollection CreateMeshCollection(MeshCollectionContent srcMeshes)
         {
@@ -188,7 +150,7 @@ namespace MonoScene.Graphics.Pipeline
         #endregion
     }
 
-    public class PBRMeshFactory : MeshFactory
+    public class PBRMeshFactory : DeviceMeshFactory
     {
         public PBRMeshFactory(GraphicsDevice device)
             : base(device) { }
@@ -199,7 +161,7 @@ namespace MonoScene.Graphics.Pipeline
         }
     }
 
-    public class ClassicMeshFactory : MeshFactory
+    public class ClassicMeshFactory : DeviceMeshFactory
     {
         #region lifecycle
 
@@ -218,17 +180,17 @@ namespace MonoScene.Graphics.Pipeline
         #endregion        
     }
 
-    public class MeshPrimitiveMaterial
+    public class DeviceMeshPrimitiveMaterial
     {
         public Effect Effect;
         public BlendState Blend;
         public bool DoubleSided;
 
-        public class MeshFactory : MeshFactory<MeshPrimitiveMaterial>
+        public class MeshFactory : DeviceMeshFactory<DeviceMeshPrimitiveMaterial>
         {
             public MeshFactory(GraphicsDevice device) : base(device) { }
 
-            protected override MeshPrimitiveMaterial ConvertMaterial(MeshPrimitiveMaterial material, bool mustSupportSkinning)
+            protected override DeviceMeshPrimitiveMaterial ConvertMaterial(DeviceMeshPrimitiveMaterial material, bool mustSupportSkinning)
             {
                 return material;
             }

+ 0 - 0
src/MonoScene.Pipeline/GraphicsResourceTracker.cs → src/MonoScene.Runtime.Model3D/Pipeline/GraphicsResourceTracker.cs


+ 0 - 0
src/MonoScene.Pipeline/TextureFactory.cs → src/MonoScene.Runtime.Model3D/Pipeline/TextureFactory.cs


+ 7 - 2
src/MonoScene.sln

@@ -22,16 +22,21 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo3", "Demo3\Demo3.csproj
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoScene.Pipeline.Assimp", "MonoScene.Pipeline.Assimp\MonoScene.Pipeline.Assimp.csproj", "{53F32A7E-EA67-408E-92FC-D6B3D46AC9B1}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGameViewer", "MonoSceneViewer\MonoGameViewer.csproj", "{5CA89BF6-7EF0-4DAF-81F1-0534C56136BF}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoSceneViewer", "MonoSceneViewer\MonoSceneViewer.csproj", "{5CA89BF6-7EF0-4DAF-81F1-0534C56136BF}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AndroidDemo1", "AndroidDemo1\AndroidDemo1.csproj", "{9CE794E3-3DEC-491B-8AFB-28833107D8A8}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoScene.Pipeline", "MonoScene.Pipeline\MonoScene.Pipeline.csproj", "{BF0082A1-44BE-45B6-8679-BD22E5899050}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoScene.Pipeline", "MonoScene.Pipeline\MonoScene.Pipeline.csproj", "{BF0082A1-44BE-45B6-8679-BD22E5899050}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Demos", "Demos", "{891DFE27-C481-4D2C-9F55-91CFC08635DF}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Runtime", "Runtime", "{C5BB2248-5C6A-4895-BE07-F9A53D566E9B}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F0B1912B-E726-4B4E-8FF5-5ADC7FC18824}"
+	ProjectSection(SolutionItems) = preProject
+		..\README.md = ..\README.md
+	EndProjectSection
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU

+ 4 - 1
src/MonoSceneViewer/MainScene.cs

@@ -39,7 +39,7 @@ namespace MonoGameViewer
 
         #region properties
 
-        [PropertyTools.DataAnnotations.Description("If enabled, it will use BasicEffect and SkinnedEffect.")]
+        [PropertyTools.DataAnnotations.Description("If enabled, it will use BasicEffect and SkinnedEffect.\r\n⚠ Switching this checkbox will reload the model!")]        
         public bool UseClassicEffects
         {
             get => _UseClassicEffects;
@@ -86,6 +86,9 @@ namespace MonoGameViewer
 
                     _ModelTemplate = assimpFactory.LoadModel(filePath);
                     _ModelSphere = _ModelTemplate.DefaultModel.ModelBounds;
+
+                    if (_ModelSphere.Radius == 0) throw new ArgumentException();
+
                     _ModelInstance = null;
                 }
             }

+ 22 - 22
src/MonoSceneViewer/MonoGameViewer.csproj → src/MonoSceneViewer/MonoSceneViewer.csproj

@@ -1,23 +1,23 @@
-<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
-
-  <PropertyGroup>
-    <OutputType>WinExe</OutputType>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
-    <UseWPF>true</UseWPF>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="MonoGame.Framework.WindowsDX" Version="3.8.1.1825-develop" />
-    <PackageReference Include="PropertyTools.Wpf" Version="3.1.0-alpha0041" />
-    <PackageReference Include="SharpGLTF.Toolkit" Version="1.0.0-alpha0021" />    
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\MonoScene.Pipeline.GLTF\MonoScene.Pipeline.GLTF.csproj" />
-    <ProjectReference Include="..\MonoScene.Pipeline.Assimp\MonoScene.Pipeline.Assimp.csproj" />
-    <ProjectReference Include="..\MonoScene.Runtime.Scene3D\MonoScene.Runtime.Scene3D.csproj" />
-
-  </ItemGroup>
-
-
+<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
+
+  <PropertyGroup>
+    <OutputType>WinExe</OutputType>
+    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <UseWPF>true</UseWPF>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MonoGame.Framework.WindowsDX" Version="3.8.1.1825-develop" />
+    <PackageReference Include="PropertyTools.Wpf" Version="3.1.0-alpha0041" />
+    <PackageReference Include="SharpGLTF.Toolkit" Version="1.0.0-alpha0021" />    
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\MonoScene.Pipeline.GLTF\MonoScene.Pipeline.GLTF.csproj" />
+    <ProjectReference Include="..\MonoScene.Pipeline.Assimp\MonoScene.Pipeline.Assimp.csproj" />
+    <ProjectReference Include="..\MonoScene.Runtime.Scene3D\MonoScene.Runtime.Scene3D.csproj" />
+
+  </ItemGroup>
+
+
 </Project>

+ 0 - 12
src/MonoSceneViewer/WPF/LightSettingsBox.xaml

@@ -1,12 +0,0 @@
-<UserControl x:Class="MonoGameViewer.WPF.LightSettingsBox"
-             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
-             xmlns:local="clr-namespace:MonoGameViewer.WPF"
-             mc:Ignorable="d" 
-             d:DesignHeight="450" d:DesignWidth="800">
-    <Grid>
-            
-    </Grid>
-</UserControl>

+ 0 - 26
src/MonoSceneViewer/WPF/LightSettingsBox.xaml.cs

@@ -1,26 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Navigation;
-using System.Windows.Shapes;
-
-namespace MonoGameViewer.WPF
-{
-    /// <summary>
-    /// Interaction logic for LightSettingsBox.xaml
-    /// </summary>
-    public partial class LightSettingsBox : UserControl
-    {
-        public LightSettingsBox()
-        {
-            InitializeComponent();
-        }
-    }
-}