Browse Source

major update migrated from GeonBit base, see description

- Added Clone() functions.
- Added some callbacks you can register on nodes.
- Moved the transformation order and rotation order into the
Transformations class.
- Added changeable rotation mode - Euler or Quaternion.
- Some bug fixes in culling node when having a deep tree.
- Extended API.
- Optimizations for culling node.
- Added LinkedNode - a node that copy transformations from external
source, used to intigrate with physics engine.
Ronen 8 years ago
parent
commit
7333e724c5

+ 1 - 1
LICENSE

@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2017 Ronen
+Copyright (c) 2017 Ronen Ness
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 63 - 1
MonoGameSceneGraph/Source/Nodes/CullingNode.cs

@@ -50,6 +50,18 @@ namespace MonoGameSceneGraph
         /// </summary>
         protected bool _isBoundingBoxDirty = true;
 
+        /// <summary>
+        /// Clone this scene node.
+        /// </summary>
+        /// <returns>Node copy.</returns>
+        public override Node Clone()
+        {
+            CullingNode ret = new CullingNode();
+            ret._transformations = _transformations.Clone();
+            ret.Visible = Visible;
+            return ret;
+        }
+
         /// <summary>
         /// Draw the node and its children.
         /// </summary>
@@ -75,8 +87,13 @@ namespace MonoGameSceneGraph
             UpdateBoundingBox();
 
             // if this node is out of screen, don't draw it
-            if (CameraFrustum.Contains(_boundingBox) == ContainmentType.Disjoint)
+            if (!IsInScreen)
             {
+                // update all child nodes (otherwise they might get stuck outside of screen and never update bounding box).
+                foreach (Node node in _childNodes)
+                {
+                    node.ForceUpdate();
+                }
                 return;
             }
 
@@ -88,6 +105,9 @@ namespace MonoGameSceneGraph
                 node.Draw();
             }
 
+            // call draw callback
+            Node.OnDraw?.Invoke(this);
+
             // draw all child entities
             foreach (IEntity entity in _childEntities)
             {
@@ -95,6 +115,28 @@ namespace MonoGameSceneGraph
             }
         }
 
+        /// <summary>
+        /// Get if this node is currently visible in camera.
+        /// </summary>
+        public bool IsInScreen
+        {
+            get
+            {
+                return (CameraFrustum.Contains(_boundingBox) != ContainmentType.Disjoint);
+            }
+        }
+
+        /// <summary>
+        /// Get if this node is partly inside screen (eg intersects with camera frustum).
+        /// </summary>
+        public bool IsPartlyInScreen
+        {
+            get
+            {
+                return (CameraFrustum.Contains(_boundingBox) == ContainmentType.Intersects);
+            }
+        }
+
         /// <summary>
         /// Called every time one of the child nodes recalculate world transformations.
         /// </summary>
@@ -160,5 +202,25 @@ namespace MonoGameSceneGraph
             // bounding box no longer dirty
             _isBoundingBoxDirty = false;
         }
+
+        /// <summary>
+        /// Called every time an entity was added / removed from this node.
+        /// </summary>
+        /// <param name="entity">Entity that was added / removed.</param>
+        /// <param name="wasAdded">If true its an entity that was added, if false, an entity that was removed.</param>
+        override protected void OnEntitiesListChange(IEntity entity, bool wasAdded)
+        {
+            _isBoundingBoxDirty = true;
+        }
+
+        /// <summary>
+        /// Called whenever an entity was added / removed from this node.
+        /// </summary>
+        /// <param name="node">Node that was added / removed.</param>
+        /// <param name="wasAdded">If true its a node that was added, if false, a node that was removed.</param>
+        override protected void OnChildNodesListChange(Node node, bool wasAdded)
+        {
+            _isBoundingBoxDirty = true;
+        }
     }
 }

+ 101 - 0
MonoGameSceneGraph/Source/Nodes/LinkedNode.cs

@@ -0,0 +1,101 @@
+#region File Description
+//-----------------------------------------------------------------------------
+// A scene node that can be linked to an external source and copy its transformations.
+//
+// Author: Ronen Ness.
+// Since: 2017.
+//-----------------------------------------------------------------------------
+#endregion
+using Microsoft.Xna.Framework;
+using System.Collections.Generic;
+
+namespace GeonBit.Core.Graphics
+{
+    /// <summary>
+    /// An external transformations source we can attach to a LinkedNode to update its transformations.
+    /// This is used to connect a node to a physical body, like a Bullet3d rigid body etc.
+    /// </summary>
+    public interface ITransformationsSource
+    {
+        /// <summary>
+        /// Return if transformations are dirty and need update.
+        /// </summary>
+        bool IsDirty { get; }
+
+        /// <summary>
+        /// Get body transformations.
+        /// </summary>
+        Matrix WorldTransform { get; }
+
+        /// <summary>
+        /// Invoked after the node took the transformations from the source.
+        /// </summary>
+        void NodeAcceptedTransform();
+    }
+
+    /// <summary>
+    /// A scene node designed to be integrated into GeonBit scene and receive updates from external source,
+    /// like physical body. This is the default node we use everywhere in the engine.
+    /// </summary>
+    public class LinkedNode : CullingNode
+    {
+        /// <summary>
+        /// Option to bind external transformations for this node, like a physical body etc.
+        /// </summary>
+        public ITransformationsSource TransformsBind = null;
+
+        /// <summary>
+        /// If true, will not copy scale transformations from source
+        /// </summary>
+        public bool KeepScale = true;
+
+        /// <summary>
+        /// Clone this scene node.
+        /// </summary>
+        /// <returns>Node copy.</returns>
+        public override Node Clone()
+        {
+            LinkedNode ret = new LinkedNode();
+            ret._transformations = _transformations.Clone();
+            ret.Visible = Visible;
+            return ret;
+        }
+
+        /// <summary>
+        /// Calc final transformations for current frame.
+        /// This uses an indicator to know if an update is needed, so no harm is done if you call it multiple times.
+        /// </summary>
+        protected override void UpdateTransformations()
+        {
+            // if got no transformations bind, call base update
+            if (TransformsBind == null)
+            {
+                base.UpdateTransformations();
+                return;
+            }
+
+            // if got here it means we need to get transformations from external source.
+            // check if dirty
+            if (TransformsBind.IsDirty)
+            {
+                // update world transform from bind
+                if (KeepScale)
+                {
+                    Vector3 prevScale = _parent.WorldScale * _transformations.Scale;
+                    _worldTransform = Matrix.CreateScale(prevScale) * TransformsBind.WorldTransform;
+                }
+                else
+                {
+                    _worldTransform = TransformsBind.WorldTransform;
+                }
+
+                // notify parent that we accepted transformation and send world matrix change event
+                TransformsBind.NodeAcceptedTransform();
+                OnWorldMatrixChange();
+            }
+
+            // no longer dirty
+            _isDirty = false;
+        }
+    }
+}

+ 103 - 35
MonoGameSceneGraph/Source/Nodes/Node.cs

@@ -13,6 +13,12 @@ using System.Collections.Generic;
 
 namespace MonoGameSceneGraph
 {
+    /// <summary>
+    /// A callback function you can register on different node-related events.
+    /// </summary>
+    /// <param name="node">The node instance the event came from.</param>
+    public delegate void NodeEventCallback(Node node);
+
     /// <summary>
     /// A node with transformations, you can attach renderable entities to it, or append
     /// child nodes to inherit transformations.
@@ -24,6 +30,17 @@ namespace MonoGameSceneGraph
         /// </summary>
         protected Node _parent = null;
 
+        /// <summary>
+        /// Callback that triggers every time a node updates its matrix.
+        /// </summary>
+        public static NodeEventCallback OnTransformationsUpdate;
+
+        /// <summary>
+        /// Callback that triggers every time a node is rendered.
+        /// Note: nodes that are culled out should not trigger this.
+        /// </summary>
+        public static NodeEventCallback OnDraw;
+
         /// <summary>
         /// Node's transformations.
         /// </summary>
@@ -49,16 +66,6 @@ namespace MonoGameSceneGraph
         /// </summary>
         private static readonly BoundingBox EmptyBoundingBox = new BoundingBox();
 
-        /// <summary>
-        /// The order in which we apply transformations when building the matrix for this node.
-        /// </summary>
-        protected TransformOrder _transformationsOrder = TransformOrder.ScaleRotationPosition;
-
-        /// <summary>
-        /// The order in which we apply rotation when building the matrix for this node.
-        /// </summary>
-        protected RotationOrder _rotationOrder = RotationOrder.RotateYXZ;
-
         /// <summary>
         /// Local transformations matrix, eg the result of the current local transformations.
         /// </summary>
@@ -116,6 +123,18 @@ namespace MonoGameSceneGraph
             Visible = true;
         }
 
+        /// <summary>
+        /// Clone this scene node.
+        /// </summary>
+        /// <returns>Node copy.</returns>
+        public virtual Node Clone()
+        {
+            Node ret = new Node();
+            ret._transformations = _transformations.Clone();
+            ret.Visible = Visible;
+            return ret;
+        }
+
         /// <summary>
         /// Draw the node and its children.
         /// </summary>
@@ -136,6 +155,9 @@ namespace MonoGameSceneGraph
                 node.Draw();
             }
 
+            // trigger draw event
+            OnDraw?.Invoke(this);
+
             // draw all child entities
             foreach (IEntity entity in _childEntities)
             {
@@ -150,6 +172,7 @@ namespace MonoGameSceneGraph
         public void AddEntity(IEntity entity)
         {
             _childEntities.Add(entity);
+            OnEntitiesListChange(entity, true);
         }
 
         /// <summary>
@@ -159,6 +182,25 @@ namespace MonoGameSceneGraph
         public void RemoveEntity(IEntity entity)
         {
             _childEntities.Remove(entity);
+            OnEntitiesListChange(entity, false);
+        }
+
+        /// <summary>
+        /// Called whenever a child node was added / removed from this node.
+        /// </summary>
+        /// <param name="entity">Entity that was added / removed.</param>
+        /// <param name="wasAdded">If true its an entity that was added, if false, an entity that was removed.</param>
+        virtual protected void OnEntitiesListChange(IEntity entity, bool wasAdded)
+        {
+        }
+
+        /// <summary>
+        /// Called whenever an entity was added / removed from this node.
+        /// </summary>
+        /// <param name="node">Node that was added / removed.</param>
+        /// <param name="wasAdded">If true its a node that was added, if false, a node that was removed.</param>
+        virtual protected void OnChildNodesListChange(Node node, bool wasAdded)
+        {
         }
 
         /// <summary>
@@ -178,6 +220,7 @@ namespace MonoGameSceneGraph
 
             // set self as node's parent
             node.SetParent(this);
+            OnChildNodesListChange(node, true);
         }
 
         /// <summary>
@@ -197,6 +240,7 @@ namespace MonoGameSceneGraph
 
             // clear node parent
             node.SetParent(null);
+            OnChildNodesListChange(node, false);
         }
 
         /// <summary>
@@ -252,7 +296,10 @@ namespace MonoGameSceneGraph
         {
             // update transformations version
             _transformVersion++;
-            
+
+            // trigger update event
+            OnTransformationsUpdate?.Invoke(this);
+
             // notify parent
             if (_parent != null)
             {
@@ -291,7 +338,7 @@ namespace MonoGameSceneGraph
             // if local transformations are dirty, we need to update them
             if (_isDirty)
             {
-                _localTransform = _transformations.BuildMatrix(_transformationsOrder, _rotationOrder);
+                _localTransform = _transformations.BuildMatrix();
             }
             
             // if local transformations are dirty, or parent transformations are out-of-date, update world transformations
@@ -344,9 +391,9 @@ namespace MonoGameSceneGraph
         {
             get
             {
-                Vector3 pos; Vector3 scale; Quaternion rot;
-                WorldTransformations.Decompose(out scale, out rot, out pos);
-                return pos;
+                //Vector3 pos; Vector3 scale; Quaternion rot;
+                //WorldTransformations.Decompose(out scale, out rot, out pos);
+                return WorldTransformations.Translation;
             }
         }
 
@@ -381,7 +428,8 @@ namespace MonoGameSceneGraph
         /// <summary>
         /// Force update transformations for this node and its children.
         /// </summary>
-        public void ForceUpdate()
+        /// <param name="recursive">If true, will also iterate and force-update children.</param>
+        public void ForceUpdate(bool recursive = true)
         {
             // not visible? skip
             if (!Visible)
@@ -392,10 +440,13 @@ namespace MonoGameSceneGraph
             // update transformations (only if needed, testing logic is inside)
             UpdateTransformations();
 
-            // draw all child nodes
-            foreach (Node node in _childNodes)
+            // force-update all child nodes
+            if (recursive)
             {
-                node.ForceUpdate();
+                foreach (Node node in _childNodes)
+                {
+                    node.ForceUpdate(recursive);
+                }
             }
         }
 
@@ -413,8 +464,17 @@ namespace MonoGameSceneGraph
         /// </summary>
         public TransformOrder TransformationsOrder
         {
-            get { return _transformationsOrder; }
-            set { _transformationsOrder = value;  OnTransformationsSet(); }
+            get { return _transformations.TransformOrder; }
+            set { _transformations.TransformOrder = value;  OnTransformationsSet(); }
+        }
+
+        /// <summary>
+        /// Get / Set the rotation type (euler / quaternion).
+        /// </summary>
+        public RotationType RotationType
+        {
+            get { return _transformations.RotationType; }
+            set { _transformations.RotationType = value; OnTransformationsSet(); }
         }
 
         /// <summary>
@@ -422,8 +482,8 @@ namespace MonoGameSceneGraph
         /// </summary>
         public RotationOrder RotationOrder
         {
-            get { return _rotationOrder; }
-            set { _rotationOrder = value; OnTransformationsSet(); }
+            get { return _transformations.RotationOrder; }
+            set { _transformations.RotationOrder = value; OnTransformationsSet(); }
         }
 
         /// <summary>
@@ -432,7 +492,7 @@ namespace MonoGameSceneGraph
         public Vector3 Position
         {
             get { return _transformations.Position; }
-            set { _transformations.Position = value; OnTransformationsSet(); }
+            set { if (_transformations.Position != value) OnTransformationsSet(); _transformations.Position = value; }
         }
 
         /// <summary>
@@ -441,7 +501,7 @@ namespace MonoGameSceneGraph
         public Vector3 Scale
         {
             get { return _transformations.Scale; }
-            set { _transformations.Scale = value; OnTransformationsSet(); }
+            set { if (_transformations.Scale != value) OnTransformationsSet(); _transformations.Scale = value; }
         }
 
         /// <summary>
@@ -450,7 +510,7 @@ namespace MonoGameSceneGraph
         public Vector3 Rotation
         {
             get { return _transformations.Rotation; }
-            set { _transformations.Rotation = value; OnTransformationsSet(); }
+            set { if (_transformations.Rotation != value) OnTransformationsSet(); _transformations.Rotation = value; }
         }
 
         /// <summary>
@@ -459,7 +519,7 @@ namespace MonoGameSceneGraph
         public float RotationX
         {
             get { return _transformations.Rotation.X; }
-            set { _transformations.Rotation.X = value; OnTransformationsSet(); }
+            set { if (_transformations.Rotation.X != value) OnTransformationsSet(); _transformations.Rotation.X = value; }
         }
 
         /// <summary>
@@ -468,7 +528,7 @@ namespace MonoGameSceneGraph
         public float RotationY
         {
             get { return _transformations.Rotation.Y; }
-            set { _transformations.Rotation.Y = value; OnTransformationsSet(); }
+            set { if (_transformations.Rotation.Y != value) OnTransformationsSet(); _transformations.Rotation.Y = value; }
         }
 
         /// <summary>
@@ -477,7 +537,7 @@ namespace MonoGameSceneGraph
         public float RotationZ
         {
             get { return _transformations.Rotation.Z; }
-            set { _transformations.Rotation.Z = value; OnTransformationsSet(); }
+            set { if (_transformations.Rotation.Z != value) OnTransformationsSet(); _transformations.Rotation.Z = value; }
         }
 
         /// <summary>
@@ -486,7 +546,7 @@ namespace MonoGameSceneGraph
         public float ScaleX
         {
             get { return _transformations.Scale.X; }
-            set { _transformations.Scale.X = value; OnTransformationsSet(); }
+            set { if (_transformations.Scale.X != value) OnTransformationsSet(); _transformations.Scale.X = value; }
         }
 
         /// <summary>
@@ -495,7 +555,7 @@ namespace MonoGameSceneGraph
         public float ScaleY
         {
             get { return _transformations.Scale.Y; }
-            set { _transformations.Scale.Y = value; OnTransformationsSet(); }
+            set { if (_transformations.Scale.Y != value) OnTransformationsSet(); _transformations.Scale.Y = value; }
         }
 
         /// <summary>
@@ -504,7 +564,7 @@ namespace MonoGameSceneGraph
         public float ScaleZ
         {
             get { return _transformations.Scale.Z; }
-            set { _transformations.Scale.Z = value; OnTransformationsSet(); }
+            set { if (_transformations.Scale.Z != value) OnTransformationsSet(); _transformations.Scale.Z = value; }
         }
 
 
@@ -514,7 +574,7 @@ namespace MonoGameSceneGraph
         public float PositionX
         {
             get { return _transformations.Position.X; }
-            set { _transformations.Position.X = value; OnTransformationsSet(); }
+            set { if (_transformations.Position.X != value) OnTransformationsSet(); _transformations.Position.X = value; }
         }
 
         /// <summary>
@@ -523,7 +583,7 @@ namespace MonoGameSceneGraph
         public float PositionY
         {
             get { return _transformations.Position.Y; }
-            set { _transformations.Position.Y = value; OnTransformationsSet(); }
+            set { if (_transformations.Position.Y != value) OnTransformationsSet(); _transformations.Position.Y = value; }
         }
 
         /// <summary>
@@ -532,7 +592,7 @@ namespace MonoGameSceneGraph
         public float PositionZ
         {
             get { return _transformations.Position.Z; }
-            set { _transformations.Position.Z = value; OnTransformationsSet(); }
+            set { if (_transformations.Position.Z != value) OnTransformationsSet(); _transformations.Position.Z = value; }
         }
 
         /// <summary>
@@ -561,6 +621,14 @@ namespace MonoGameSceneGraph
             get { return _childEntities.Count == 0 && _childNodes.Count == 0; }
         }
 
+        /// <summary>
+        /// Get if this node have any entities in it.
+        /// </summary>
+        public bool HaveEntities
+        {
+            get { return _childEntities.Count != 0; }
+        }
+
         /// <summary>
         /// Get bounding box of this node and all its child nodes.
         /// </summary>

+ 112 - 25
MonoGameSceneGraph/Source/Transformations.cs

@@ -20,6 +20,22 @@ namespace MonoGameSceneGraph
     {
     }
 
+    /// <summary>
+    /// How to apply rotation (euler vs quaternion).
+    /// </summary>
+    public enum RotationType
+    {
+        /// <summary>
+        /// Euler rotation.
+        /// </summary>
+        Euler,
+
+        /// <summary>
+        /// Quaternion rotation.
+        /// </summary>
+        Quaternion,
+    }
+
     /// <summary>
     /// Different way to build matrix from transformations.
     /// </summary>
@@ -97,7 +113,6 @@ namespace MonoGameSceneGraph
     /// </summary>
     public class Transformations
     {
-
         /// <summary>
         /// Node position / translation.
         /// </summary>
@@ -113,6 +128,21 @@ namespace MonoGameSceneGraph
         /// </summary>
         public Vector3 Scale;
 
+        /// <summary>
+        /// Order to apply different transformations to create the final matrix.
+        /// </summary>
+        public TransformOrder TransformOrder = TransformOrder.ScaleRotationPosition;
+
+        /// <summary>
+        /// Axis order to apply rotation.
+        /// </summary>
+        public RotationOrder RotationOrder = RotationOrder.RotateYXZ;
+
+        /// <summary>
+        /// What type of rotation to use.
+        /// </summary>
+        public RotationType RotationType = RotationType.Quaternion;
+
         /// <summary>
         /// Create new default transformations.
         /// </summary>
@@ -131,56 +161,113 @@ namespace MonoGameSceneGraph
             Position = other.Position;
             Rotation = other.Rotation;
             Scale = other.Scale;
+            TransformOrder = other.TransformOrder;
+            RotationOrder = other.RotationOrder;
+            RotationType = other.RotationType;
+        }
+
+        /// <summary>
+        /// Clone transformations.
+        /// </summary>
+        /// <returns>Copy of this transformations.</returns>
+        public Transformations Clone()
+        {
+            return new Transformations(this);
         }
 
         /// <summary>
         /// Build and return just the rotation matrix for this treansformations.
         /// </summary>
-        /// <param name="rotationOrder">In which order to apply rotation (axis order) when applying rotation.</param>
-        /// <returns></returns>
-        public Matrix BuildRotationMatrix(RotationOrder rotationOrder = RotationOrder.RotateYXZ)
+        /// <returns>Rotation matrix.</returns>
+        public Matrix BuildRotationMatrix()
         {
-            switch (rotationOrder)
+            // handle euler rotation
+            if (RotationType == RotationType.Euler)
             {
-                case RotationOrder.RotateXYZ:
-                    return Matrix.CreateRotationX(Rotation.X) * Matrix.CreateRotationY(Rotation.Y) * Matrix.CreateRotationZ(Rotation.Z);
+                switch (RotationOrder)
+                {
+                    case RotationOrder.RotateXYZ:
+                        return Matrix.CreateRotationX(Rotation.X) * Matrix.CreateRotationY(Rotation.Y) * Matrix.CreateRotationZ(Rotation.Z);
 
-                case RotationOrder.RotateXZY:
-                    return Matrix.CreateRotationX(Rotation.X) * Matrix.CreateRotationZ(Rotation.Z) * Matrix.CreateRotationY(Rotation.Y);
+                    case RotationOrder.RotateXZY:
+                        return Matrix.CreateRotationX(Rotation.X) * Matrix.CreateRotationZ(Rotation.Z) * Matrix.CreateRotationY(Rotation.Y);
 
-                case RotationOrder.RotateYXZ:
-                    return Matrix.CreateFromYawPitchRoll(Rotation.Y, Rotation.X, Rotation.Z);
+                    case RotationOrder.RotateYXZ:
+                        return Matrix.CreateFromYawPitchRoll(Rotation.Y, Rotation.X, Rotation.Z);
 
-                case RotationOrder.RotateYZX:
-                    return Matrix.CreateRotationY(Rotation.Y) * Matrix.CreateRotationZ(Rotation.Z) * Matrix.CreateRotationX(Rotation.X);
+                    case RotationOrder.RotateYZX:
+                        return Matrix.CreateRotationY(Rotation.Y) * Matrix.CreateRotationZ(Rotation.Z) * Matrix.CreateRotationX(Rotation.X);
 
-                case RotationOrder.RotateZXY:
-                    return Matrix.CreateRotationZ(Rotation.Z) * Matrix.CreateRotationX(Rotation.X) * Matrix.CreateRotationY(Rotation.Y);
+                    case RotationOrder.RotateZXY:
+                        return Matrix.CreateRotationZ(Rotation.Z) * Matrix.CreateRotationX(Rotation.X) * Matrix.CreateRotationY(Rotation.Y);
 
-                case RotationOrder.RotateZYX:
-                    return Matrix.CreateRotationZ(Rotation.Z) * Matrix.CreateRotationY(Rotation.Y) * Matrix.CreateRotationX(Rotation.X);
+                    case RotationOrder.RotateZYX:
+                        return Matrix.CreateRotationZ(Rotation.Z) * Matrix.CreateRotationY(Rotation.Y) * Matrix.CreateRotationX(Rotation.X);
 
-                default:
-                    throw new System.Exception("Unknown rotation order!");
+                    default:
+                        throw new System.Exception("Unknown rotation order!");
+                }
+            }
+            // handle quaternion rotation
+            else if (RotationType == RotationType.Quaternion)
+            {
+                // quaternion to use
+                Quaternion quat;
+
+                // build quaternion based on rotation order
+                switch (RotationOrder)
+                {
+                    case RotationOrder.RotateXYZ:
+                        quat = Quaternion.CreateFromAxisAngle(Vector3.UnitX, Rotation.X) * Quaternion.CreateFromAxisAngle(Vector3.UnitY, Rotation.Y) * Quaternion.CreateFromAxisAngle(Vector3.UnitZ, Rotation.Z);
+                        break;
+
+                    case RotationOrder.RotateXZY:
+                        quat = Quaternion.CreateFromAxisAngle(Vector3.UnitX, Rotation.X) * Quaternion.CreateFromAxisAngle(Vector3.UnitZ, Rotation.Z) * Quaternion.CreateFromAxisAngle(Vector3.UnitY, Rotation.Y);
+                        break;
+
+                    case RotationOrder.RotateYXZ:
+                        quat = Quaternion.CreateFromYawPitchRoll(Rotation.Y, Rotation.X, Rotation.Z);
+                        break;
+
+                    case RotationOrder.RotateYZX:
+                        quat = Quaternion.CreateFromAxisAngle(Vector3.UnitY, Rotation.Y) * Quaternion.CreateFromAxisAngle(Vector3.UnitZ, Rotation.Z) * Quaternion.CreateFromAxisAngle(Vector3.UnitX, Rotation.X);
+                        break;
+
+                    case RotationOrder.RotateZXY:
+                        quat = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, Rotation.Z) * Quaternion.CreateFromAxisAngle(Vector3.UnitX, Rotation.X) * Quaternion.CreateFromAxisAngle(Vector3.UnitY, Rotation.Y);
+                        break;
+
+                    case RotationOrder.RotateZYX:
+                        quat = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, Rotation.Z) * Quaternion.CreateFromAxisAngle(Vector3.UnitY, Rotation.Y) * Quaternion.CreateFromAxisAngle(Vector3.UnitX, Rotation.X);
+                        break;
+
+                    default:
+                        throw new System.Exception("Unknown rotation order!");
+                }
+
+                // convert to a matrix and return
+                return Matrix.CreateFromQuaternion(quat);
+            }
+            // should never happen.
+            else
+            {
+                throw new System.Exception("Unknown rotation type!");
             }
         }
 
         /// <summary>
         /// Build and return a matrix from current transformations.
         /// </summary>
-        /// <param name="transformOrder">In which order to apply transformations to produce final matrix.</param>
-        /// <param name="rotationOrder">In which order to apply rotation (axis order) when applying rotation.</param>
         /// <returns>Matrix with all transformations applied.</returns>
-        public Matrix BuildMatrix(TransformOrder transformOrder = TransformOrder.ScaleRotationPosition, 
-            RotationOrder rotationOrder = RotationOrder.RotateYXZ)
+        public Matrix BuildMatrix()
         {
             // create the matrix parts
             Matrix pos = Matrix.CreateTranslation(Position);
-            Matrix rot = BuildRotationMatrix(rotationOrder);
+            Matrix rot = BuildRotationMatrix();
             Matrix scale = Matrix.CreateScale(Scale);
 
             // build and return matrix based on order
-            switch (transformOrder)
+            switch (TransformOrder)
             {
                 case TransformOrder.PositionRotationScale:
                     return pos * rot * scale;

+ 16 - 1
README.md

@@ -209,6 +209,10 @@ In this doc we didn't cover much of the API, only the very basics needed to get
 ## Lisence
 MonoGame-SceneGraph is distributed with the permissive MIT License. For more info, check out the ```LICENSE``` file in this repo.
 
+## Contact
+
+```[email protected]```
+
 ## Changelog
 
 ### 1.0.0.1
@@ -226,4 +230,15 @@ MonoGame-SceneGraph is distributed with the permissive MIT License. For more inf
 
 ### 1.1.0.1
 
-- Changed default build target to AnyCPU. No actual changes in code.
+- Changed default build target to AnyCPU. No actual changes in code.
+
+### 1.2.0.0
+
+- Added Clone() functions.
+- Added some callbacks you can register on nodes.
+- Moved the transformation order and rotation order into the Transformations class.
+- Added changeable rotation mode - Euler or Quaternion.
+- Some bug fixes in culling node when having a deep tree.
+- Extended API.
+- Optimizations for culling node.
+- Added LinkedNode - a node that copy transformations from external source, used to intigrate with physics engine.