///
/// 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
}
}