Browse Source

added Culling Node, added 'Nodes' folder, updated Entity and Node API, and fixed dest dll path

Ronen 8 years ago
parent
commit
fe8cf80fcc

+ 1 - 1
MonoGameSceneGraph.nuspec

@@ -17,6 +17,6 @@
   </metadata>
   </metadata>
   <files>
   <files>
 	<file src="readme.txt" target="" />
 	<file src="readme.txt" target="" />
-	<file src="MonoGameSceneGraph\bin\DesktopGL\x86\Release\MonoGameSceneGraph.dll" target="lib\ronenness\MonoGameSceneGraph.dll"/>
+	<file src="MonoGameSceneGraph\bin\DesktopGL\x86\Release\MonoGameSceneGraph.dll" target="lib\MonoGameSceneGraph.dll"/>
   </files>
   </files>
 </package>
 </package>

+ 2 - 1
MonoGameSceneGraph/MonoGameSceneGraph.csproj

@@ -50,7 +50,8 @@
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Source\Entities\ModelEntity.cs" />
     <Compile Include="Source\Entities\ModelEntity.cs" />
     <Compile Include="Source\Entities\IEntity.cs" />
     <Compile Include="Source\Entities\IEntity.cs" />
-    <Compile Include="Source\Node.cs" />
+    <Compile Include="Source\Nodes\CullingNode.cs" />
+    <Compile Include="Source\Nodes\Node.cs" />
     <Compile Include="Source\Transformations.cs" />
     <Compile Include="Source\Transformations.cs" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>

+ 9 - 2
MonoGameSceneGraph/Source/Entities/IEntity.cs

@@ -9,8 +9,6 @@
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 #endregion
 #endregion
 using Microsoft.Xna.Framework;
 using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Graphics;
-using System.Collections.Generic;
 
 
 
 
 namespace MonoGameSceneGraph
 namespace MonoGameSceneGraph
@@ -27,5 +25,14 @@ namespace MonoGameSceneGraph
         /// <param name="localTransformations">Local transformations from the direct parent node.</param>
         /// <param name="localTransformations">Local transformations from the direct parent node.</param>
         /// <param name="worldTransformations">World transformations to apply on this entity (this is what you should use to draw this entity).</param>
         /// <param name="worldTransformations">World transformations to apply on this entity (this is what you should use to draw this entity).</param>
         void Draw(Node parent, Matrix localTransformations, Matrix worldTransformations);
         void Draw(Node parent, Matrix localTransformations, Matrix worldTransformations);
+
+        /// <summary>
+        /// Get the bounding box of this entity.
+        /// </summary>
+        /// <param name="parent">Parent node that's currently drawing this entity.</param>
+        /// <param name="localTransformations">Local transformations from the direct parent node.</param>
+        /// <param name="worldTransformations">World transformations to apply on this entity (this is what you should use to draw this entity).</param>
+        /// <returns>Bounding box of the entity.</returns>
+        BoundingBox GetBoundingBox(Node parent, Matrix localTransformations, Matrix worldTransformations);
     }
     }
 }
 }

+ 48 - 0
MonoGameSceneGraph/Source/Entities/ModelEntity.cs

@@ -110,5 +110,53 @@ namespace MonoGameSceneGraph
                 mesh.Draw();
                 mesh.Draw();
             }
             }
         }
         }
+
+        /// <summary>
+        /// Get the bounding box of this entity.
+        /// </summary>
+        /// <param name="parent">Parent node that's currently drawing this entity.</param>
+        /// <param name="localTransformations">Local transformations from the direct parent node.</param>
+        /// <param name="worldTransformations">World transformations to apply on this entity (this is what you should use to draw this entity).</param>
+        /// <returns>Bounding box of the entity.</returns>
+        public BoundingBox GetBoundingBox(Node parent, Matrix localTransformations, Matrix worldTransformations)
+        {
+            // apply model transformations on world transform (note: don't support animations)
+            worldTransformations = worldTransformations * Model.Bones[0].Transform;
+
+            // initialize minimum and maximum corners of the bounding box to max and min values
+            Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
+            Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
+
+            // iterate over mesh parts
+            foreach (ModelMesh mesh in Model.Meshes)
+            {
+                foreach (ModelMeshPart meshPart in mesh.MeshParts)
+                {
+                    // vertex buffer parameters
+                    int vertexStride = meshPart.VertexBuffer.VertexDeclaration.VertexStride;
+                    int vertexBufferSize = meshPart.NumVertices * vertexStride;
+
+                    // get vertex data as float
+                    float[] vertexData = new float[vertexBufferSize / sizeof(float)];
+                    meshPart.VertexBuffer.GetData<float>(vertexData);
+
+                    // iterate through vertices (possibly) growing bounding box
+                    for (int i = 0; i < vertexBufferSize / sizeof(float); i += vertexStride / sizeof(float))
+                    {
+                        // get curr position and update min / max
+                        Vector3 currPosition = new Vector3(vertexData[i], vertexData[i + 1], vertexData[i + 2]);
+                        min = Vector3.Min(min, currPosition);
+                        max = Vector3.Max(max, currPosition);
+                    }
+                }
+            }
+
+            // transform min and max position so it will be in world space
+            min = Vector3.Transform(min, worldTransformations);
+            max = Vector3.Transform(max, worldTransformations);
+
+            // return the bounding box
+            return new BoundingBox(min, max);
+        }
     }
     }
 }
 }

+ 90 - 0
MonoGameSceneGraph/Source/Nodes/CullingNode.cs

@@ -0,0 +1,90 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// This type of node implements basic Bounding-Box based culling.
+//
+// Author: Ronen Ness.
+// Since: 2017.
+//-----------------------------------------------------------------------------
+#endregion
+using Microsoft.Xna.Framework;
+using System.Collections.Generic;
+
+namespace MonoGameSceneGraph
+{
+    /// <summary>
+    /// A culling node is a special node that cull itself if out of screen, based on camera frustum and node bounding box.
+    /// </summary>
+    /// <remarks>
+    /// It works as follows:
+    ///     1. Every time the camera changes, the static member CameraFrustum must be updated. The Culling Node will use this frustum to detect weather or not its in screen.
+    ///     2. Whenever the transformations of this node update, it will also calculate the Bounding Box of itself with all its child nodes and entities.
+    ///     3. When the node is drawn, it will first test collision between its bounding box and the camera frustum. If its found to be out of screen, the node will not draw itself or its children.
+    ///     
+    /// Its important to note that using Culling Nodes properly require some non-trivial designing for the scene graph.
+    ///
+    /// For example, if you have a tilemap that is made out of 100x100 nodes grid (every node = single tile), and you put all the tile nodes under a single node (eg a node with 10,000 children), 
+    /// the result would be that the parent node will have a huge bounding box that will most likely always be in screen, and then the Culling Nodes will have to test for 10,000 bounding boxes to detect
+    /// exactly which tiles are in screen and which are not.. Not only that this won't really boost up performance, it might even slow your game down (depending on CPU/GPU strength).
+    /// So in the example above, in order to really enjoy the culling, you should break the 100x100 grid into smaller grids and break those grids as well.
+    /// For example, break the parent node into 4 nodes with 50x50 tiles each, and then break those nodes into 5x5 nodes with 10 tiles each. This way, the Culling Node will first test 4 large bounding box,
+    /// with an actual chance of culling some of them. Then we'll have 25 bounding boxes to check per chunk, and some of them will cull out as well. For larger maps, just break into more chunks.
+    /// 
+    /// Another thing you need to remember is that you can combine Culling Nodes with regular nodes. For example if you implemet particles or doodads (like grass) with lots of small nodes, sometimes its
+    /// enough to just test their parent node.
+    /// </remarks>
+    public class CullingNode : Node
+    {
+        /// <summary>
+        /// The camera frustum to cull by. You need to update this every time the camera frustum changes in order
+        /// to make the culling work currectly.
+        /// </summary>
+        public static BoundingFrustum CameraFrustum;
+
+        /// <summary>
+        /// Last calculated bounding box for this node.
+        /// </summary>
+        protected BoundingBox _currBoundingBox;
+
+        /// <summary>
+        /// Do we need to recalculate the bounding box of this node?
+        /// </summary>
+        private bool _isBoundingBoxDirty = true;
+
+        /// <summary>
+        /// Called when the world matrix of this node is actually recalculated (invoked after the calculation).
+        /// </summary>
+        protected override void OnWorldMatrixChange()
+        {
+            base.OnWorldMatrixChange();
+            _isBoundingBoxDirty = true;
+        }
+
+        /// <summary>
+        /// Draw the node and its children.
+        /// </summary>
+        public override void Draw()
+        {
+            // if not visible or camera frustum is not set, skip (eg don't draw it)
+            if (!IsVisible || CameraFrustum == null)
+            {
+                return;
+            }
+
+            // check if need to recalculate bounding box
+            if (_isBoundingBoxDirty)
+            {
+                _currBoundingBox = GetBoundingBox(true);
+                _isBoundingBoxDirty = false;
+            }
+
+            // if this node is out of screen, don't draw it
+            if (!_currBoundingBox.Intersects(CameraFrustum))
+            {
+                return;
+            }
+
+            // if got here it means this node is in screen and should be rendered. draw it.
+            base.Draw();
+        }
+    }
+}

+ 62 - 27
MonoGameSceneGraph/Source/Node.cs → MonoGameSceneGraph/Source/Nodes/Node.cs

@@ -13,14 +13,6 @@ using System.Collections.Generic;
 
 
 namespace MonoGameSceneGraph
 namespace MonoGameSceneGraph
 {
 {
-    /// <summary>
-    /// MonoGameSceneGraph is the main namespace that contains all the MonoGame-SceneGraph entities.
-    /// </summary>
-    [System.Runtime.CompilerServices.CompilerGenerated]
-    class NamespaceDoc
-    {
-    }
-
     /// <summary>
     /// <summary>
     /// A node with transformations, you can attach renderable entities to it, or append
     /// A node with transformations, you can attach renderable entities to it, or append
     /// child nodes to inherit transformations.
     /// child nodes to inherit transformations.
@@ -241,9 +233,18 @@ namespace MonoGameSceneGraph
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// Set this node as "dirty", eg that we need to update local transformations.
+        /// Called when the world matrix of this node is actually recalculated (invoked after the calculation).
         /// </summary>
         /// </summary>
         protected virtual void OnWorldMatrixChange()
         protected virtual void OnWorldMatrixChange()
+        {
+            _transformVersion++;
+        }
+
+        /// <summary>
+        /// Called when local transformations are set, eg when Position, Rotation, Scale etc. is changed.
+        /// We use this to set this node as "dirty", eg that we need to update local transformations.
+        /// </summary>
+        protected virtual void OnTransformationsSet()
         {
         {
             _isDirty = true;
             _isDirty = true;
         }
         }
@@ -291,8 +292,8 @@ namespace MonoGameSceneGraph
                     _parentLastTransformVersion = 0;
                     _parentLastTransformVersion = 0;
                 }
                 }
 
 
-                // increase transformation version
-                _transformVersion++;
+                // called the function that mark world matrix change (increase transformation version etc)
+                OnWorldMatrixChange();
             }
             }
 
 
             // no longer dirty
             // no longer dirty
@@ -321,7 +322,7 @@ namespace MonoGameSceneGraph
         public void ResetTransformations()
         public void ResetTransformations()
         {
         {
             _transformations = new Transformations();
             _transformations = new Transformations();
-            OnWorldMatrixChange();
+            OnTransformationsSet();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -330,7 +331,7 @@ namespace MonoGameSceneGraph
         public TransformOrder TransformationsOrder
         public TransformOrder TransformationsOrder
         {
         {
             get { return _transformationsOrder; }
             get { return _transformationsOrder; }
-            set { _transformationsOrder = value;  OnWorldMatrixChange(); }
+            set { _transformationsOrder = value;  OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -339,7 +340,7 @@ namespace MonoGameSceneGraph
         public RotationOrder RotationOrder
         public RotationOrder RotationOrder
         {
         {
             get { return _rotationOrder; }
             get { return _rotationOrder; }
-            set { _rotationOrder = value; OnWorldMatrixChange(); }
+            set { _rotationOrder = value; OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -348,7 +349,7 @@ namespace MonoGameSceneGraph
         public Vector3 Position
         public Vector3 Position
         {
         {
             get { return _transformations.Position; }
             get { return _transformations.Position; }
-            set { _transformations.Position = value; OnWorldMatrixChange(); }
+            set { _transformations.Position = value; OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -357,7 +358,7 @@ namespace MonoGameSceneGraph
         public Vector3 Scale
         public Vector3 Scale
         {
         {
             get { return _transformations.Scale; }
             get { return _transformations.Scale; }
-            set { _transformations.Scale = value; OnWorldMatrixChange(); }
+            set { _transformations.Scale = value; OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -366,7 +367,7 @@ namespace MonoGameSceneGraph
         public Vector3 Rotation
         public Vector3 Rotation
         {
         {
             get { return _transformations.Rotation; }
             get { return _transformations.Rotation; }
-            set { _transformations.Rotation = value; OnWorldMatrixChange(); }
+            set { _transformations.Rotation = value; OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -375,7 +376,7 @@ namespace MonoGameSceneGraph
         public float RotationX
         public float RotationX
         {
         {
             get { return _transformations.Rotation.X; }
             get { return _transformations.Rotation.X; }
-            set { _transformations.Rotation.X = value; OnWorldMatrixChange(); }
+            set { _transformations.Rotation.X = value; OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -384,7 +385,7 @@ namespace MonoGameSceneGraph
         public float RotationY
         public float RotationY
         {
         {
             get { return _transformations.Rotation.Y; }
             get { return _transformations.Rotation.Y; }
-            set { _transformations.Rotation.Y = value; OnWorldMatrixChange(); }
+            set { _transformations.Rotation.Y = value; OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -393,7 +394,7 @@ namespace MonoGameSceneGraph
         public float RotationZ
         public float RotationZ
         {
         {
             get { return _transformations.Rotation.Z; }
             get { return _transformations.Rotation.Z; }
-            set { _transformations.Rotation.Z = value; OnWorldMatrixChange(); }
+            set { _transformations.Rotation.Z = value; OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -402,7 +403,7 @@ namespace MonoGameSceneGraph
         public float ScaleX
         public float ScaleX
         {
         {
             get { return _transformations.Scale.X; }
             get { return _transformations.Scale.X; }
-            set { _transformations.Scale.X = value; OnWorldMatrixChange(); }
+            set { _transformations.Scale.X = value; OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -411,7 +412,7 @@ namespace MonoGameSceneGraph
         public float ScaleY
         public float ScaleY
         {
         {
             get { return _transformations.Scale.Y; }
             get { return _transformations.Scale.Y; }
-            set { _transformations.Scale.Y = value; OnWorldMatrixChange(); }
+            set { _transformations.Scale.Y = value; OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -420,7 +421,7 @@ namespace MonoGameSceneGraph
         public float ScaleZ
         public float ScaleZ
         {
         {
             get { return _transformations.Scale.Z; }
             get { return _transformations.Scale.Z; }
-            set { _transformations.Scale.Z = value; OnWorldMatrixChange(); }
+            set { _transformations.Scale.Z = value; OnTransformationsSet(); }
         }
         }
 
 
 
 
@@ -430,7 +431,7 @@ namespace MonoGameSceneGraph
         public float PositionX
         public float PositionX
         {
         {
             get { return _transformations.Position.X; }
             get { return _transformations.Position.X; }
-            set { _transformations.Position.X = value; OnWorldMatrixChange(); }
+            set { _transformations.Position.X = value; OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -439,7 +440,7 @@ namespace MonoGameSceneGraph
         public float PositionY
         public float PositionY
         {
         {
             get { return _transformations.Position.Y; }
             get { return _transformations.Position.Y; }
-            set { _transformations.Position.Y = value; OnWorldMatrixChange(); }
+            set { _transformations.Position.Y = value; OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -448,7 +449,7 @@ namespace MonoGameSceneGraph
         public float PositionZ
         public float PositionZ
         {
         {
             get { return _transformations.Position.Z; }
             get { return _transformations.Position.Z; }
-            set { _transformations.Position.Z = value; OnWorldMatrixChange(); }
+            set { _transformations.Position.Z = value; OnTransformationsSet(); }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -458,7 +459,41 @@ namespace MonoGameSceneGraph
         public void Translate(Vector3 moveBy)
         public void Translate(Vector3 moveBy)
         {
         {
             _transformations.Position += moveBy;
             _transformations.Position += moveBy;
-            OnWorldMatrixChange();
+            OnTransformationsSet();
+        }
+
+        /// <summary>
+        /// Get bounding box of this node and all its child nodes.
+        /// </summary>
+        /// <param name="includeChildNodes">If true, will include bounding box of child nodes. If false, only of entities directly attached to this node.</param>
+        /// <returns>Bounding box of the node and its children.</returns>
+        public virtual BoundingBox GetBoundingBox(bool includeChildNodes = true)
+        {
+            // initialize minimum and maximum corners of the bounding box to max and min values
+            Vector3 min = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
+            Vector3 max = new Vector3(float.MinValue, float.MinValue, float.MinValue);
+
+            // apply all child nodes bounding boxes
+            if (includeChildNodes)
+            {
+                foreach (Node child in _childNodes)
+                {
+                    BoundingBox curr = child.GetBoundingBox();
+                    min = Vector3.Min(min, curr.Min);
+                    max = Vector3.Max(max, curr.Max);
+                }
+            }
+
+            // apply all entities directly under this node
+            foreach (IEntity entity in _childEntities)
+            {
+                BoundingBox curr = entity.GetBoundingBox(this, _localTransform, _worldTransform);
+                min = Vector3.Min(min, curr.Min);
+                max = Vector3.Max(max, curr.Max);
+            }
+
+            // return final bounding box
+            return new BoundingBox(min, max);
         }
         }
     }
     }
 }
 }

+ 8 - 0
MonoGameSceneGraph/Source/Transformations.cs

@@ -12,6 +12,14 @@ using Microsoft.Xna.Framework.Graphics;
 
 
 namespace MonoGameSceneGraph
 namespace MonoGameSceneGraph
 {
 {
+    /// <summary>
+    /// MonoGameSceneGraph is the main namespace that contains all the MonoGame-SceneGraph entities.
+    /// </summary>
+    [System.Runtime.CompilerServices.CompilerGenerated]
+    class NamespaceDoc
+    {
+    }
+
     /// <summary>
     /// <summary>
     /// Different way to build matrix from transformations.
     /// Different way to build matrix from transformations.
     /// </summary>
     /// </summary>