/// /// SceneItem is a generic scene graph item that acts as the /// basic building block for making a scene graph for a 3D world. /// public class SceneItem { /* * CLASS PROPERTIES */ /// /// visible state of the scene item /// protected bool visible; /// /// the scene item's parent (or null if none) /// protected SceneItem parent; /// /// the scene item's children /// protected List children; /// /// the transfromation matrix that describes the /// translation, rotation and scale of this scene item /// private Matrix worldMatrix; /// /// indicates if the inverse matrix property is up to date /// private bool invNeedsUpdate; /// /// the inverse matrix of the world matrix /// private Matrix invWorld; /* * CONSTRUCTORS */ /// /// main constructor for a scene item /// public SceneItem() { init(); } private void init() { visible = true; parent = null; children = new List(); worldMatrix = Matrix.Identity; invWorld = Matrix.Identity; invNeedsUpdate = false; } /* * UNLOADING */ /// /// unloads this scene item and all of its children /// /// public virtual void Unload(bool disposeResources) { //remove from parent's hierarchy if (parent != null) parent.RemoveChild(this); if (children != null) { //unload all children... SceneItem[] childArray = children.ToArray(); for (int i = 0; i < childArray.Length; i++) childArray[i].Unload(disposeResources); children.Clear(); } children = null; parent = null; visible = false; } /* * ENCAPSULATORS */ /// /// gets the current transformed position of this scene /// item in world coordinates /// public virtual Vector3 WorldPosition { get { return worldMatrix.Translation; } } /// /// gets /sets the world matrix /// transformation for this SceneItem /// public virtual Matrix WorldMatrix { get { return worldMatrix; } set { worldMatrix = value; invNeedsUpdate = true; //children inherit world matrix property foreach (SceneItem child in children) child.WorldMatrix = worldMatrix; } } /// /// the inverse of the world matrix /// public virtual Matrix WorldInverse { get { if (invNeedsUpdate) { invWorld = Matrix.Invert(worldMatrix); invNeedsUpdate = false; } return invWorld; } } /// /// indicates if this scene item is the child of a /// TransformNode object. /// public bool HasParentTransform { get { return (parent is TransformNode); } } /// /// gets the direct parent of this /// scene item as a TransformNode. /// Or null if there is not one. /// public virtual TransformNode ParentTransform { get { return (parent as TransformNode); } } /* * POSITION AND NORMAL TRANSFORMATIONS */ /// /// transforms a position from the local space of this SceneItem node to the global space /// /// a position in local space /// a position in global space public Vector3 LocalToGlobal(Vector3 localPos) { return Vector3.Transform(localPos, worldMatrix); } /// /// transforms a position from a global space to the local space of this SceneItem node /// /// a position in global space /// a position in local space public Vector3 GlobalToLocal(Vector3 globalPos) { return Vector3.Transform(globalPos, this.WorldInverse); } /// /// transforms a normal from the local space of this SceneItem node to the global space /// /// a normal in local space /// a normal in global space public Vector3 LocalToGlobalNormal(Vector3 localNormal) { return Vector3.TransformNormal(localNormal, worldMatrix); } /// /// transforms a normal from a global space to the local space of this SceneItem node /// /// a normal in global space /// a normal in local space public Vector3 GlobalToLocalNormal(Vector3 globalNormal) { return Vector3.TransformNormal(globalNormal, this.WorldInverse); } /// /// transforms a position from the local space of on SceneItem node to another /// /// the local position in the space of the SceneItem node "localSpace" /// the SceneItem node that has the "localPos" /// the returned position will be in the local space of this SceneItem node /// the position in the local space of "targetSpace" public static Vector3 LocalToLocal(Vector3 localPos, SceneItem localSpace, SceneItem targetSpace) { return targetSpace.GlobalToLocal(localSpace.LocalToGlobal(localPos)); } /// /// transforms a position from the local space of one SceneItem node to another /// /// the local position in the space of the SceneItem node "localSpace" /// the SceneItem node that has the "localPos" /// the returned position will be in the local space of this SceneItem node /// the position in the local space of "targetSpace" public static Vector3 LocalToLocalNormal(Vector3 localDirection, SceneItem currentSpace, SceneItem targetSpace) { return targetSpace.GlobalToLocalNormal(currentSpace.LocalToGlobalNormal(localDirection)); } /// /// gets the matrix that will transform points /// from the current space to the target space /// /// the SceneItem node that is the current local space /// the trnasformable node that is the target local space /// a matrix that will transform points or directions from the current space to the target space public static Matrix GetTransform(SceneItem currentSpace, SceneItem targetSpace) { return (targetSpace.WorldMatrix * currentSpace.WorldInverse); } /// /// gets / sets the visiblity of /// this item and its children /// public bool Visible { get { return visible; } set { visible = value; } } /* * HIERARCHY CREATION / MANIPULATION */ /// /// gets or sets the parent of this scene item /// public virtual SceneItem Parent { get { return parent; } set { if (value == null) parent = value; else value.AddChild(this); } } /// /// add achild to this scene item /// /// the child to add public virtual void AddChild(SceneItem newChild) { if (newChild == this) throw new ArgumentException("Attempted to parent a scene item to itself."); if (newChild.descendant(this, 1, -1)) throw new ArgumentException("Cannot make a scene item as one of it's own descendants"); children.Add(newChild); //remove from old parent if (newChild.HasParent) newChild.parent.RemoveChild(newChild); //add to hierarchy newChild.parent = this; newChild.WorldMatrix = this.WorldMatrix; } /// /// remove all children from this scene item /// public virtual void RemoveAllChildren() { while (children.Count > 0) { //remove children from the top down RemoveChild(children[children.Count - 1]); } children.Clear(); } /// /// remove the specified child from this scene item /// /// the child to be remove public virtual void RemoveChild(SceneItem xChild) { if (children.Contains(xChild)) { xChild.parent = null; xChild.WorldMatrix = Matrix.Identity; children.Remove(xChild); } } /// /// removes the child at the specified index from this scene item /// /// the index of the child to remove public virtual void RemoveChildAt(int index) { if (index >= 0 && index < children.Count) this.RemoveChild(children[index]); } /// /// gets the child at the specified index /// /// the index in the children list /// the child, or null if index is invalid public virtual SceneItem GetChildAt(int index) { if (index >= 0 && index < this.children.Count) return children[index]; return null; } /// /// returns true if the specified scene item is /// an immediate child of this scene item /// public virtual bool Contains(SceneItem curChild) { return children.Contains(curChild); } /// /// returns true if the specified scene item is a descendant of this scene item /// /// setting this value to 1 will cause the check to skip the first /// level of the hierarchy, 2 will skip the first two levels and so on /// the maximum depth into the hierarchy past the start depth /// to check or -1 to check entire hirearchy protected virtual bool descendant(SceneItem item, int startDepth, int maxDepth) { if (startDepth <= 0 && this == item) return true; startDepth--; if (maxDepth >= 0 && startDepth < -maxDepth) return false; for (int i = 0; i < children.Count; i++) { if (children[i].descendant(item, startDepth, maxDepth)) return true; } return false; } /* * HIERARCHY ACCESORS */ /// /// gets the list of children for this node /// public List Children { get { return this.children; } } /// /// Gets the number of immediate /// children of this scene item /// public int Nuchildren { get { return children.Count; } } /// /// gets the total number of children, /// in the hierachy of this scene item /// public int NumTotalChildren { get { int totalChildren = 0; foreach(SceneItem child in children) totalChildren += child.NumTotalChildren; return totalChildren; } } /// /// gets true if this scene item has a parent node /// public bool HasParent { get { return (parent != null); } } /* * SIBLING SORTING */ /// /// swap the children at the specified indeces /// /// the index of the first child in the swap /// the index of the second child in the swap public virtual void SwapChildrenAt(int index1, int index2) { if (index1 >= 0 && index1 < children.Count && index2 >= 0 && index2 < children.Count) { SceneItem temp = children[index1]; children[index1] = children[index2]; children[index2] = temp; } } /// /// swap the render order of the two specified children /// /// the first child in the swap /// the second child in the swap public virtual void SwapChildren(SceneItem item1, SceneItem item2) { int index1 = children.IndexOf(item1); int index2 = children.IndexOf(item2); if (index1 >= 0 && index2 >= 0) { children[index1] = item2; children[index2] = item1; } } /// /// sets the specified child to be drawn last in /// the render order effectively bringing it to the front /// public virtual void BringToFront(SceneItem curChild) { if (children.Contains(curChild)) { int curIndex = children.IndexOf(curChild); if (curIndex < children.Count - 1) { children[curIndex] = children[children.Count - 1]; children[children.Count - 1] = curChild; } } } /// /// sets the specified child to be drawn first in /// the render order effectively send it to the back /// public virtual void SendToBack(SceneItem curChild) { if (children.Contains(curChild)) { int curIndex = children.IndexOf(curChild); if (curIndex > 0) { children[curIndex] = children[0]; children[0] = curChild; } } } /// /// bring this item to the front /// of its parent's display order /// public virtual void BringToFront() { if (this.HasParent) parent.BringToFront(this); } /// /// send this item to the back of /// its parent's display order /// public virtual void SendToBack() { if (this.HasParent) parent.SendToBack(this); } /* * UPDATE AND RENDER */ /// /// updates this node and all its children /// public virtual void Update(Matrix viewMatrix, Matrix projMatrix) { foreach (SceneItem child in children) child.Update(viewMatrix, projMatrix); } /// /// Renders this object and all its children if visibiltiy is on. /// public virtual void RenderChildren() { if (visible) { //render the object content itself Render(); //render any children of this object foreach (SceneItem child in children) child.RenderChildren(); } } /// /// Renders the content of just this scene item (not its children). /// This method is called by the RenderChild method if the object /// is visible. /// public virtual void Render() { //overwritten by any class with content to render } }