//BSD License  
//DXGfx® - http://www.eteractions.com
//Copyright (c) 2005
//by Guillaume Randon
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software
//and associated documentation files (the "Software"), to deal in the Software without restriction,
//including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
//and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
//subject to the following conditions:
//The above copyright notice and this permission notice shall be included in all copies or substantial
//portions of the Software.
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
//AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
//DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.Samples.DirectX.UtilityToolkit;

namespace DXGfxLib
{
    /// <summary>
    /// This delegate exist to define a NodeMoved event to which user code can subscribe. This is neeeded as some
    /// code part might need to be notified of such an event. For example in some scene graph like the QuadTree you need to
    /// reposition the node in the tree when such an event arise.
    /// </summary>
    /// <param name="node"></param>
    public delegate void NodeMovedEventHandler(SceneNode node);

    /// <summary>
    /// A SceneNode is the basic building block of the scene graph. It keep track of its parent node and child nodes can be
    /// attached to it. It keep track of its bounding information and can update these when needed based on the children
    /// bounding information and positions. Also a scene node can be moved in several ways with or without moving the children
    /// in the same manner.
    /// </summary>
    public class SceneNode
    {
        /// <summary>
        /// Checking if a lot of objects are in the viewing frustrum can be more expensive that sending them
        /// to the video card. All depend of the object and of the scene. For example with the quadtree it 
        /// can be a good solution to check the frustrum culling on quadtreenode and don't check on the objects
        /// that are children of the node in the viewing frustrum. Whether this will be faster or not depends
        /// of the situation and of your object. This boolean allow to turn off frustrum culling on some object
        /// if it is though that it will be more efficient!
        /// </summary>
        public bool useFrustrumCulling;

        /// <summary>
        /// This is the default setting for any newly created SceneObject. By default frustrum culling is used.
        /// This is a static variable, if you change this to false, any newly created scene object will have this turn
        /// off by default except derived classes that might override this (like quadtreenodes)!
        /// </summary>
        public static bool defaultForFrustrumCulling = true;

        /// <summary>
        /// If this field is not null we have a parent node and this is a ref to it.
        /// </summary>
        public SceneNode parent = null;

        /// <summary>
        /// We want our scene node to which scene it is attached to. We keep that information in the below member
        /// variable.
        /// </summary>
        public Scene scene = null;

        /// <summary>
        /// We might want to give a name to our scene, we keep that name in the below string.
        /// </summary>
        public string name;

        /// <summary>
        /// Local axes of the scene node. Can be useful among other things for collision detection based on bounding box
        /// information using the method of separating axes.
        /// </summary>
        public Vector3[] axes = new Vector3[3];

        /// <summary>
        /// In case the dimensions of the scene node bounding box are needed they can be read here.
        /// </summary>
        public Vector3 extents = new Vector3();

        /// <summary>
        /// NodeMoved event to which user code can subscribe. This is neeeded as some
        /// code part might need to be notified of such an event. For example in some scene graph like the QuadTree you need to
        /// reposition the node in the tree when such an event arise.
        /// </summary>
        public event NodeMovedEventHandler Moved;

        /// <summary>
        /// Boolean indicating whether we have moved recently or not (since last update!)
        /// </summary>
        public bool moved = true;

        /// <summary>
        /// Each scene node has a world relative 4x4 matrix for positionning of the node in 3D space. These are DX 4x4 matrices,
        /// position of the node are x = M41 y = M42 z = M43
        /// </summary>
        internal Matrix worldMat = Matrix.Identity;

        /// <summary>
        /// In some situation we want to enforce constraints on the position of the scene node. This can be done by implementing
        /// IPositionConstraint interface and passing a ref to an implementation of that interface to the scene node. Check documentation
        /// of that specific interface to get a better idea of how it can be used.
        /// </summary>
        public IPositionConstraint positionConstraint = null;

        /// <summary>
        /// This is the public accessor for the WorldMatrix. If you set the WorldMatrix value through this accessor all children
        /// of the scene node will also be repositionned accordingly.
        /// </summary>
        [System.ComponentModel.BrowsableAttribute(false)]
        public Matrix WorldMat
        {
            get { return worldMat; }
            set
            {
                Matrix equivalentWorldTransform = Matrix.Multiply(Matrix.Invert(worldMat), value);
                ApplyWorldTransform(equivalentWorldTransform);
            }
        }

        /// <summary>
        /// Public accessor for position of scene node in 3D space. Using this accessor to set the position will also
        /// move all the children nodes.
        /// </summary>
        [System.ComponentModel.BrowsableAttribute(false)]
        public Vector3 Position
        {
            get { return MathUtil.PositionFromMat(worldMat); }
            set
            {
                Matrix translation = Matrix.Translation(value.X - worldMat.M41,
                    value.Y - worldMat.M42,
                    value.Z - worldMat.M43);
                ApplyWorldTransform(translation);
            }
        }

        /// <summary>
        /// World position on X axis.
        /// </summary>
        [System.ComponentModel.CategoryAttribute("Position"),
		System.ComponentModel.RefreshProperties( System.ComponentModel.RefreshProperties.Repaint )]
        public float X
        {
            get { return worldMat.M41; }
            set
            {
                Matrix translation = Matrix.Translation(value - worldMat.M41, 0.0f, 0.0f);
                ApplyWorldTransform(translation);
            }
        }

        /// <summary>
        /// World position on Y axis.
        /// </summary>
        [System.ComponentModel.CategoryAttribute("Position"),
        System.ComponentModel.RefreshProperties(System.ComponentModel.RefreshProperties.Repaint)]
        public float Y
        {
            get { return worldMat.M42; }
            set
            {
                Matrix translation = Matrix.Translation(0.0f, value - worldMat.M42, 0.0f);
                ApplyWorldTransform(translation);
            }
        }

        /// <summary>
        /// World position on Z axis.
        /// </summary>
        [System.ComponentModel.CategoryAttribute("Position"),
        System.ComponentModel.RefreshProperties(System.ComponentModel.RefreshProperties.Repaint)]
        public float Z
        {
            get { return worldMat.M43; }
            set
            {
                Matrix translation = Matrix.Translation(0.0f, 0.0f, value - worldMat.M43);
                ApplyWorldTransform(translation);
            }
        }

        /// <summary>
        /// This gives the position relative to the parent node if it does exist. Chaning this position through this accessor
        /// will also move all the children nodes accordingly.
        /// </summary>
        [System.ComponentModel.BrowsableAttribute(false)]
        public Vector3 PositionToParent
        {
            get { return MathUtil.PositionFromMat(LocalMat); }
            set
            {
                Matrix localMat = LocalMat;
                Matrix translation = Matrix.Translation(value.X - localMat.M41,
                    value.Y - localMat.M42,
                    value.Z - localMat.M43);
                ApplyLocalTransform(translation);
            }
        }

        /// <summary>
        /// If our Yaw, Pitch or Roll values are too small we want to reset them to zero. We take the decision by comparing
        /// the value with the below value.
        /// </summary>
        private double threshold = 0.001;

        /// <summary>
        /// This return a Vector3 containing the Yaw Pitch Roll value in this order in radian. Chaing the orientation through
        /// this accessor will also change the orientation of the whole scene node hierarchy under this node.
        /// </summary>
        [System.ComponentModel.BrowsableAttribute(false)]
        public Vector3 Orientation
        {
            get
            {
                Vector3 ypr = MathUtil.YawPitchRollFromMat(worldMat);

                ypr.X = MathUtil.ResetToNullIfTooSmall(ypr.X, threshold);
                ypr.Y = MathUtil.ResetToNullIfTooSmall(ypr.Y, threshold);
                ypr.Z = MathUtil.ResetToNullIfTooSmall(ypr.Z, threshold);

                return ypr;
            }
            set
            {
                Vector3 currentOrientation = Orientation;

                float yaw = value.X - currentOrientation.X;
                float pitch = value.Y - currentOrientation.Y;
                float roll = value.Z - currentOrientation.Z;

                yaw = MathUtil.ResetToNullIfTooSmall(yaw, threshold);
                pitch = MathUtil.ResetToNullIfTooSmall(pitch, threshold);
                roll = MathUtil.ResetToNullIfTooSmall(roll, threshold);

                Matrix rotation = Matrix.RotationYawPitchRoll(yaw,
                    pitch,
                    roll);
                ApplyWorldTransform(rotation);
            }
        }

        /// <summary>
        /// The name says everything. Exception this value is expressed in degrees.
        /// </summary>
        [System.ComponentModel.CategoryAttribute("Orientation"),
        System.ComponentModel.RefreshProperties(System.ComponentModel.RefreshProperties.Repaint)]
        public float Yaw
        {
            get
            {
                return MathUtil.YawPitchRollFromMatInDegree(worldMat).X;
            }
            set
            {
                Vector3 current = Orientation;
                Orientation = new Vector3(Geometry.DegreeToRadian(value),
                    current.Y,
                    current.Z);
            }
        }

        /// <summary>
        /// The name says everything. Exception this value is expressed in degrees.
        /// </summary>
        [System.ComponentModel.CategoryAttribute("Orientation"),
        System.ComponentModel.RefreshProperties(System.ComponentModel.RefreshProperties.Repaint)]
        public float Pitch
        {
            get
            {
                return MathUtil.YawPitchRollFromMatInDegree(worldMat).Y;
            }
            set
            {
                Vector3 current = Orientation;
                Orientation = new Vector3(current.X,
                    Geometry.DegreeToRadian(value),
                    current.Z);
            }
        }

        /// <summary>
        /// The name says everything. Exception this value is expressed in degrees.
        /// </summary>
        [System.ComponentModel.CategoryAttribute("Orientation"),
        System.ComponentModel.RefreshProperties(System.ComponentModel.RefreshProperties.Repaint)]
        public float Roll
        {
            get
            {
                return MathUtil.YawPitchRollFromMatInDegree(worldMat).Z;
            }
            set
            {
                Vector3 current = Orientation;
                Orientation = new Vector3(current.X,
                    current.Y,
                    Geometry.DegreeToRadian(value));
            }
        }

        /// <summary>
        /// This is the orientation (yaw pitch roll) in radian relative to the parent node. Changing the orientation of this
        /// node through this accessor will also affect the whole node hierarchy under this node.
        /// </summary>
        [System.ComponentModel.BrowsableAttribute(false)]
        public Vector3 OrientationToParent
        {
            get
            {
                return MathUtil.YawPitchRollFromMat(LocalMat);
            }
            set
            {
                Vector3 currentOrientation = Orientation;
                Matrix rotation = Matrix.RotationYawPitchRoll(value.X - currentOrientation.X,
                    value.Y - currentOrientation.Y,
                    value.Z - currentOrientation.Z);
                ApplyLocalTransform(rotation);
            }
        }

        /// <summary>
        /// This method force a new position for this scene node without affecting position of children nodes in world space.
        /// Which means children node won't move in the global space only this node will. Thus relative positions of the children
        /// to this node (their parent) do change.
        /// </summary>
        /// <param name="position"></param>
        public void ForcePositionWithoutUpdate(Vector3 position)
        {
            Matrix translation = Matrix.Translation(position.X - worldMat.M41,
                position.Y - worldMat.M42,
                position.Z - worldMat.M43);
            Vector3 orientation = Orientation;
            worldMat = Matrix.Multiply(Matrix.RotationYawPitchRoll(orientation.X, orientation.Y, orientation.Z),
                translation);
            OnMove();
        }

        /// <summary>
        /// This is the pendant of ForcePositionWithoutUpdate for the node orientation. Children node position and orientation in
        /// global 3D space is unchanged.
        /// </summary>
        /// <param name="orientation"></param>
        public void ForceOrientationWithoutUpdate(Vector3 orientation)
        {
            worldMat = Matrix.Multiply(Matrix.RotationYawPitchRoll(orientation.X, orientation.Y, orientation.Z),
                Matrix.Translation(Position));
            OnMove();
        }

        /// <summary>
        /// This will set the world matrix for this node without affecting descendants. Also this won't call the OnMove event
        /// so even if you have registered for the OnMove event you won't be notified if the node is just repositionned through
        /// a call to ForceWorldMatValueWithoutUpdate!
        /// </summary>
        /// <param name="desiredWorldMat"></param>
        public void ForceWorldMatValueWithoutUpdate(Matrix desiredWorldMat)
        {
            worldMat = desiredWorldMat;
        }

        /// <summary>
        /// This method return what is called the localMat which in fact is the position/orientation matrix in the space
        /// defined by the parent of this node. Relation to the worldMat are detailed below:
        /// 
        /// WorldMat = LocalMat * parent.WorldMat
        /// LocalMat = WorldMat * inv(parent.WorldMat)
        /// 
        /// Changing the LocalMat through this accessor will affect all the scene node tree below this node accordingly.
        /// </summary>
        [System.ComponentModel.BrowsableAttribute(false)]
        public Matrix LocalMat
        {
            get 
            {
                Matrix parentMat;

                if(parent==null)
                {
                    parentMat = Matrix.Identity;
                }
                else
                {
                    parentMat = parent.WorldMat;
                }
                return Matrix.Multiply(worldMat, Matrix.Invert(parentMat)); 
            }
            set
            {
                Matrix newWorldMat;

                if (parent == null)
                {
                    newWorldMat = value;
                }
                else
                {
                    newWorldMat = Matrix.Multiply(value, parent.worldMat);
                }

                Matrix equivalentWorldTransform = Matrix.Multiply(Matrix.Invert(worldMat), newWorldMat);
                ApplyWorldTransform(equivalentWorldTransform);
            }
        }

        /// <summary>
        /// This method through the Moved event as well as it update Axes and extents. This is called by the ApplyWorldTransform
        /// method which is in fact the sole method used to move or reorient the scene node. 
        /// </summary>
        public virtual void OnMove()
        {
            moved = true;

            if (Moved != null)
            {
                Moved(this);
            }
            UpdateAxes();
            UpdateExtents();
        }

        /// <summary>
        /// List of children scene nodes.
        /// </summary>
        protected List<SceneNode> children = null;

        /// <summary>
        /// World axes align bounding box which should encompass this node and all children node bounding volumes.
        /// </summary>
        public AABBox worldAABBox = null;

        /// <summary>
        /// Public constructor for the scene node.
        /// </summary>
        public SceneNode()
        {
            Initialize();
        }

        public virtual void Initialize()
        {
            if (children == null)
            {
                children = new List<SceneNode>();
            }
            if (worldAABBox == null)
            {
                worldAABBox = new AABBox();
            }
            useFrustrumCulling = defaultForFrustrumCulling;
        }

        /// <summary>
        /// This constructor will build an instance a scene node which is a copy of the scene node passed
        /// as an argument. Everything is copied including childrend attached to the node.
        /// </summary>
        /// <param name="sceneNode"></param>
        public SceneNode(SceneNode sceneNode)
        {
            children = new List<SceneNode>(sceneNode.children);
            worldAABBox = sceneNode.worldAABBox;
            useFrustrumCulling = sceneNode.useFrustrumCulling;

            worldMat = sceneNode.worldMat;

            parent = sceneNode.parent;
            scene = sceneNode.scene;
        }

        public virtual void Dispose()
        {
            foreach (SceneNode node in children)
            {
                node.Dispose();
            }
            children.Clear();
            children = null;
        }

        /// <summary>
        /// We don't want any 'external' code to access directly the children collection.
        /// Also classes inheriting from SceneNode might want to implement different behaviors
        /// upon addition of children so we mark the method virtual.
        /// </summary>
        /// <param name="node"></param>
        public virtual void AddChild(SceneNode node)
        {
            if (children != null)
            {
                children.Add(node);
                node.parent = this;
                node.scene = scene;
            }
        }

        /// <summary>
        /// We don't want any 'external' code to access directly the children collection.
        /// Also classes inheriting from SceneNode might want to implement different behaviors
        /// when removing children so we mark the method virtual.
        /// </summary>
        /// <param name="node"></param>
        public virtual void RemoveChild(SceneNode node)
        {
            if (children != null)
            {
                children.Remove(node);
                node.parent = null;
                node.scene = null;
            }
        }

        /// <summary>
        /// This is in fact the unique method in charge of modifying position and orientation of the scene node. Most of the
        /// other method and accessor dealing with position are relying on this one except the methods were children are not
        /// updated!
        /// </summary>
        /// <param name="transform"></param>
        public virtual void ApplyWorldTransform(Matrix transform)
        {
            worldMat = Matrix.Multiply(worldMat, transform);

            // Let's update our children if we have any.
            // We apply the same world transform on the children.
            // !!!!!!!!!!! This need to be done after the update of the worldMat, as in any
            // children we will use parent.worldMat to compute localMat !!!!!!!!!!!!!!!
            foreach (SceneNode node in children)
            {
                node.ApplyWorldTransform(transform);
            }

            OnMove();
        }

        /// <summary>
        /// This method does in fact call into ApplyWorldTransform after having computed the world transform matrix to
        /// apply to get the local transform that was passed as a parameter.
        /// </summary>
        /// <param name="transform"></param>
        public void ApplyLocalTransform(Matrix transform)
        {
            Matrix newWorldMat = Matrix.Multiply(transform, worldMat);

            Matrix equivalentWorldTransform = Matrix.Multiply(Matrix.Invert(worldMat), newWorldMat);
            ApplyWorldTransform(equivalentWorldTransform);
        }

        /// <summary>
        /// Currently this method is used only in classes deriving from SceneNode which also have to load ressources (most of the
        /// time DX Resources). So at the moment this is used only after loading a scene but it could potentially be use when
        /// you want parts of your scene to be ready for rendering.
        /// </summary>
        public virtual void GetPrepared()
        {
            Initialize();
            if (children == null)
            {
                children = new List<SceneNode>();
            }

            foreach (SceneNode child in children)
            {
                child.GetPrepared();
            }
        }

        /// <summary>
        /// This is the method that should be called by any external code willing to update the scene. When looking in the details
        /// this method does ask the nodes to update and then call the AttachForRendering method that will add SceneObjects to
        /// the list of objets to be rendered.
        /// </summary>
        /// <param name="frustrum"></param>
        /// <param name="elapsedTime"></param>
        /// <param name="appTime"></param>
        /// <param name="objectsToBeRendered"></param>
        public virtual void Update(Frustrum frustrum, float elapsedTime, double appTime, List<IDrawable> objectsToBeRendered)
        {
            Update(frustrum, appTime, elapsedTime);
            AttachForRendering(frustrum, objectsToBeRendered);
        }

        /// <summary>
        /// This will update all children and all hierarchy bounding information. After we have gone through this method
        /// bounding volume are in synch with what is contained in the scene graph below the current node. Scene objects need
        /// to override this method to provide their own local bounding information to enter into calculation of the 
        /// world axis aligned bounding box.
        /// </summary>
        /// <param name="frustrum"></param>
        /// <param name="appTime"></param>
        /// <param name="elapsedTime"></param>
        public virtual void Update(Frustrum frustrum, double appTime, float elapsedTime)
        {

            worldAABBox.Reset();
            worldAABBox.Transform(worldMat);
            foreach (SceneNode child in children)
            {
                child.Update(frustrum, appTime, elapsedTime);
                worldAABBox.BoxFromBoxes(child.worldAABBox);
            }
        }

        /// <summary>
        /// A scene node is only a position, bounding volume and connecting information entity, it doesn't have anything to display
        /// on itself so this call only propagate the call to descendent. Scene objects or derived class can then determine based
        /// on the frustrum information if they need to render during this pass or not. If they do need to render, they should add
        /// themselves to the list of objects to be rendered.
        /// </summary>
        /// <param name="frustrum"></param>
        /// <param name="objectsToBeRendered"></param>
        public virtual void AttachForRendering(Frustrum frustrum, List<IDrawable> objectsToBeRendered)
        {
            // If we are in the frustrum let's our children decide for
            // themselves.
            if (useFrustrumCulling)
            {
                if(!frustrum.IsInFrustrum(worldAABBox))
                    return;
            }

            foreach (SceneNode child in children)
            {
                child.AttachForRendering(frustrum, objectsToBeRendered);
            }
        }

        /// <summary>
        /// Will detach all children from the scene graph.
        /// </summary>
        public virtual void DetachAll()
        {
            //foreach (SceneNode child in children)
            //{
            //    child.DetachAll();
            //}
        }

        /// <summary>
        /// Will detach a specific scene node from the graph below the current scene node.
        /// </summary>
        /// <param name="sceneNodeToDetach"></param>
        public virtual void Detach(SceneNode sceneNodeToDetach)
        {
            bool directChild = false;

            foreach (SceneNode child in children)
            {
                if (child == sceneNodeToDetach)
                {
                    directChild = true;
                }
            }

            if (directChild)
            {
                sceneNodeToDetach.DetachAll();
                children.Remove(sceneNodeToDetach);
            }
            else
            {
                foreach (SceneNode child in children)
                {
                    child.Detach(sceneNodeToDetach);
                }
            }
        }

        /// <summary>
        /// If there is a collision between the give SceneObject bounding box and the hierarchy of nodes
        /// below the node on which you are calling this method this method will return true and all the node
        /// that are potentially colliding with our node are going to be added to the ArrayList collidingSceneObject.
        /// </summary>
        /// <param name="SceneObject"></param>
        /// <param name="collidingSceneObject"></param>
        /// <returns></returns>
        public virtual bool Collide(SceneNode sceneNode, ref List<SceneObject> collidingSceneObjects)
        {
            bool res = false;

            if (CollideWithAABox(sceneNode))
            {
                SceneObject obj = this as SceneObject;
                if (obj != null)
                {
                    res = true;
                    collidingSceneObjects.Add(obj);
                }

                foreach (SceneNode childNode in children)
                {
                    SceneObject obj2 = childNode as SceneObject;
                    if (obj2 != null)
                    {
                        if (obj2.Collide(sceneNode, ref collidingSceneObjects))
                            res = true;
                    }
                }
            }

            return res;
        }

        /// <summary>
        /// If there is a collision between the give SceneObject bounding box and the hierarchy of nodes
        /// below the node on which you are calling this method this method will return true and all the node
        /// that are potentially colliding with our node are going to be added to the ArrayList collidingSceneObject.
        /// 
        /// This is the same as the Collide method with the same signature except that we return only SceneObjects that
        /// are flaged as solid!
        /// </summary>
        /// <param name="SceneObject"></param>
        /// <param name="collidingSceneObject"></param>
        /// <returns></returns>
        public virtual bool CollideSolid(SceneNode sceneNode, ref List<SceneObject> collidingSceneObjects)
        {
            bool res = false;

            if (CollideWithAABox(sceneNode))
            {
                SceneObject obj = this as SceneObject;
                if ((obj != null)&&(obj.Solid))
                {
                    res = true;
                    collidingSceneObjects.Add(obj);
                }

                foreach (SceneNode childNode in children)
                {
                    SceneObject obj2 = childNode as SceneObject;
                    if (obj2 != null)
                    {
                        if (obj2.Collide(sceneNode, ref collidingSceneObjects))
                            res = true;
                    }
                }
            }

            return res;
        }

        /// <summary>
        /// Same as above except you test only for collision with entities of a specified desired type.
        /// </summary>
        /// <param name="SceneObject"></param>
        /// <param name="collidingSceneObjects"></param>
        /// <param name="desiredType"></param>
        /// <returns></returns>
        public virtual bool Collide(SceneNode sceneNode, ref List<SceneObject> collidingSceneObjects, System.Type desiredType)
        {
            bool res = false;

            if (CollideWithAABox(sceneNode))
            {
                SceneObject obj = this as SceneObject;

                if (obj != null)
                {
                    if (this == sceneNode)
                        return res;
                    if (!desiredType.IsInstanceOfType(this))
                        return res;

                    res = true;
                    collidingSceneObjects.Add(obj);

                }

                foreach (SceneNode childNode in children)
                {
                    if (childNode.Collide(sceneNode, ref collidingSceneObjects, desiredType))
                        res = true;
                }
            }

            return res;
        }

        /// <summary>
        /// Same as above except that only sceneObjects flaged as solid are returned and considered valid for collision detection.
        /// </summary>
        /// <param name="SceneObject"></param>
        /// <param name="collidingSceneObjects"></param>
        /// <param name="desiredType"></param>
        /// <returns></returns>
        public virtual bool CollideSolid(SceneNode sceneNode, ref List<SceneObject> collidingSceneObjects, System.Type desiredType)
        {
            bool res = false;

            if (CollideWithAABox(sceneNode))
            {
                SceneObject obj = this as SceneObject;

                if ((obj != null)&&(obj.Solid))
                {
                    if (this == sceneNode)
                        return res;
                    if (!desiredType.IsInstanceOfType(this))
                        return res;

                    res = true;
                    collidingSceneObjects.Add(obj);

                }

                foreach (SceneNode childNode in children)
                {
                    if (childNode.Collide(sceneNode, ref collidingSceneObjects, desiredType))
                        res = true;
                }
            }

            return res;
        }

        /// <summary>
        /// Check if the given scene node collide with this scene node worl axis aligned bounding box. This is evaluated using
        /// the world axis aligned bounding box of the given scene node against the one of the current node.
        /// </summary>
        /// <param name="sceneNode"></param>
        /// <returns></returns>
        public bool CollideWithAABox(SceneNode sceneNode)
        {
            return this.worldAABBox.Collide(sceneNode.worldAABBox);
        }

        /// <summary>
        /// This method will update the node axes information. This is called each time the node is updated.
        /// </summary>
        public void UpdateAxes()
        {
            if(axes==null)
                axes = new Vector3[3];

            axes[0].X = worldMat.M11;
            axes[0].Y = worldMat.M12;
            axes[0].Z = worldMat.M13;

            axes[1].X = worldMat.M21;
            axes[1].Y = worldMat.M22;
            axes[1].Z = worldMat.M23;

            axes[2].X = worldMat.M31;
            axes[2].Y = worldMat.M32;
            axes[2].Z = worldMat.M33;
        }

        /// <summary>
        /// Public accessor for the axes information.
        /// </summary>
        /// <returns></returns>
        public Vector3[] Axes()
        {
            return axes;
        }

        /// <summary>
        /// This update the dimensions against the local axes of the scene node. When you call this method you need to have the
        /// axes up to date. Both methods are called in the right order when the node is moved. UpdateExtents is only called when
        /// the node is moved so it won't reflect changes in dimensions if node in the graph below move to much and change the bounding volume.
        /// </summary>
        public void UpdateExtents()
        {
            Vector3 diag = worldAABBox.vMax - worldAABBox.vMin;

            extents.X = System.Math.Abs(Vector3.Dot(diag, axes[0]));
            extents.Y = System.Math.Abs(Vector3.Dot(diag, axes[1]));
            extents.Z = System.Math.Abs(Vector3.Dot(diag, axes[2]));
        }

        /// <summary>
        /// Public accessor for the Extents information!
        /// </summary>
        /// <returns></returns>
        public Vector3 Extents()
        {
            return extents;
        }

        /// <summary>
        /// This will check if the 3D ray does intersect with any scene object attached to the graph below this scene node.
        /// </summary>
        /// <param name="rayOri"></param>
        /// <param name="rayDir"></param>
        /// <param name="collidingSceneObjects"></param>
        /// <returns></returns>
        public virtual bool Intersect(Vector3 rayOri, Vector3 rayDir, ref List<SceneObject> collidingSceneObjects)
        {
            bool any = false;

            AABBox aabox = worldAABBox;
            bool collision = true;
            if (((rayOri.X < aabox.vMin.X) && (rayDir.X <= 0))
                || ((rayOri.X > aabox.vMax.X) && (rayDir.X >= 0)))
                collision = false;
            if (((rayOri.Y < aabox.vMin.Y) && (rayDir.Y <= 0))
                || ((rayOri.Y > aabox.vMax.Y) && (rayDir.Y >= 0)))
                collision = false;
            if (((rayOri.Z < aabox.vMin.Z) && (rayDir.Z <= 0))
                || ((rayOri.Z > aabox.vMax.Z) && (rayDir.Z >= 0)))
                collision = false;

            if (collision)
            {
                foreach (SceneNode child in children)
                {
                    if (child.Intersect(rayOri, rayDir, ref collidingSceneObjects))
                        any = true;
                }
            }

            return any;
        }

        /// <summary>
        /// See above.
        /// </summary>
        /// <param name="rayOri"></param>
        /// <param name="rayDir"></param>
        /// <param name="collidingSceneObjects"></param>
        /// <param name="desiredType"></param>
        /// <returns></returns>
        public virtual bool Intersect(Vector3 rayOri, Vector3 rayDir, ref List<SceneObject> collidingSceneObjects, System.Type desiredType)
        {
            bool any = false;

            AABBox aabox = worldAABBox;
            bool collision = true;
            if (((rayOri.X < aabox.vMin.X) && (rayDir.X <= 0))
                || ((rayOri.X > aabox.vMax.X) && (rayDir.X >= 0)))
                collision = false;
            if (((rayOri.Y < aabox.vMin.Y) && (rayDir.Y <= 0))
                || ((rayOri.Y > aabox.vMax.Y) && (rayDir.Y >= 0)))
                collision = false;
            if (((rayOri.Z < aabox.vMin.Z) && (rayDir.Z <= 0))
                || ((rayOri.Z > aabox.vMax.Z) && (rayDir.Z >= 0)))
                collision = false;

            if (collision)
            {
                foreach (SceneNode child in children)
                {
                    if (child.Intersect(rayOri, rayDir, ref collidingSceneObjects, desiredType))
                        any = true;
                }
            }

            return any;
        }
    }
}
