Entity.cs :  » Game » RealmForge » Axiom » Core » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Game » RealmForge 
RealmForge » Axiom » Core » Entity.cs
#region LGPL License
/*
Axiom Game Engine Library
Copyright (C) 2003  Axiom Project Team

The overall design, and a majority of the core engine and rendering code 
contained within this library is a derivative of the open source Object Oriented 
Graphics Engine OGRE, which can be found at http://ogre.sourceforge.net.  
Many thanks to the OGRE team for maintaining such a high quality project.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#endregion

using System;
using System.Collections;
using System.Diagnostics;
using Axiom.Animating;
using Axiom.Collections;
using Axiom.MathLib;
using Axiom.Graphics;

namespace Axiom.Core{
  /// <summary>
  ///    Defines an instance of a discrete, movable object based on a Mesh.
  /// </summary>
  /// <remarks>
  ///    Axiom generally divides renderable objects into 2 groups, discrete
  ///    (separate) and relatively small objects which move around the world,
  ///    and large, sprawling geometry which makes up generally immovable
  ///    scenery, aka 'level geometry'.
  ///    <para>
  ///    The <see cref="Mesh"/> and <see cref="SubMesh"/> classes deal with the definition of the geometry
  ///    used by discrete movable objects. Entities are actual instances of
  ///    objects based on this geometry in the world. Therefore there is
  ///    usually a single set <see cref="Mesh"/> for a car, but there may be multiple
  ///    entities based on it in the world. Entities are able to override
  ///    aspects of the Mesh it is defined by, such as changing material
  ///    properties per instance (so you can have many cars using the same
  ///    geometry but different textures for example). Because a <see cref="Mesh"/> is split
  ///    into a list of <see cref="SubMesh"/> objects for this purpose, the Entity class is a grouping class
  ///    (much like the <see cref="Mesh"/> class) and much of the detail regarding
  ///    individual changes is kept in the <see cref="SubEntity"/> class. There is a 1:1
  ///    relationship between <see cref="SubEntity"/> instances and the <see cref="SubMesh"/> instances
  ///    associated with the <see cref="Mesh"/> the Entity is based on.
  ///    </para>
  ///    <para>
  ///    Entity and <see cref="SubEntity"/> classes are never created directly. 
  ///    Use <see cref="SceneManager.CreateEntity"/> (passing a model name) to
  ///    create one.
  ///    </para>
  ///    <para>
  ///    Entities are included in the scene by using <see cref="SceneNode.AttachObject"/>
  ///    to associate them with a scene node.
  ///    </para>
  /// </remarks>
  public class Entity : SceneObject, IDisposable {
    #region Fields

    /// <summary>
    ///    3D Mesh that represents this entity.
    /// </summary>
    protected Mesh mesh;
    /// <summary>
    ///    List of sub entities.
    /// </summary>
    protected SubEntityCollection subEntityList = new SubEntityCollection();
    public ICollection SubEntities { get { return subEntityList; } }
    /// <summary>
    ///    SceneManager responsible for creating this entity.
    /// </summary>
    protected SceneManager sceneMgr;
    /// <summary>
    ///    Name of the material to be used for this entity.
    /// </summary>
    protected string materialName;
    /// <summary>
    ///    Bounding box that 'contains' all the meshes of each child entity.
    /// </summary>
    protected AxisAlignedBox fullBoundingBox;
    /// <summary>
    ///    State of animation for animable meshes.
    /// </summary>
    protected AnimationStateCollection animationState = new AnimationStateCollection();
    /// <summary>
    ///    Cached bone matrices, including and world transforms.
    /// </summary>
    protected internal Matrix4[] boneMatrices;
    /// <summary>
    ///    Number of matrices associated with this entity.
    /// </summary>
    protected internal int numBoneMatrices;
    /// <summary>
    ///    Flag that determines whether or not to display skeleton.
    /// </summary>
    protected bool displaySkeleton;
    /// <summary>
    ///    The LOD number of the mesh to use, calculated by NotifyCurrentCamera.
    /// </summary>
    protected int meshLodIndex;
    /// <summary>
    ///    LOD bias factor, inverted for optimization when calculating adjusted depth.
    /// </summary>
    protected float meshLodFactorInv;
    /// <summary>
    ///    Index of minimum detail LOD (higher index is lower detail).
    /// </summary>
    protected int minMeshLodIndex;
    /// <summary>
    ///    Index of maximum detail LOD (lower index is higher detail).
    /// </summary>
    protected int maxMeshLodIndex;
    /// <summary>
    ///    Flag indicating that mesh uses manual LOD and so might have multiple SubEntity versions.
    /// </summary>
    protected bool usingManualLod;
    /// <summary>
    ///    Render detail to be used for this entity (solid, wireframe, point).
    /// </summary>
    protected SceneDetailLevel renderDetail;
    /// <summary>
    ///    Temp blend buffer details for shared geometry.
    /// </summary>
    protected TempBlendedBufferInfo tempBlendedBuffer = new TempBlendedBufferInfo();
    /// <summary>
    ///    Temp blend buffer details for shared geometry.
    /// </summary>
    protected internal VertexData sharedBlendedVertexData;
    /// <summary>
    ///    Flag indicating whether hardware skinning is supported by this entity's materials.
    /// </summary>
    protected bool useHardwareSkinning;
        /// <summary>
        ///     Flag indicating whether we have a vertex program in use on any of our subentities.
        /// </summary>
        protected bool vertexProgramInUse;
        /// <summary>
    ///    Records the last frame in which animation was updated.
    /// </summary>
    protected ulong frameAnimationLastUpdated;
    /// <summary>
    ///    This entity's personal copy of a master skeleton.
    /// </summary>
    protected SkeletonInstance skeletonInstance;
    /// <summary>
    ///    List of child objects attached to this entity.
    /// </summary>
    protected SceneObjectCollection childObjectList = new SceneObjectCollection();
    /// <summary>
    ///    List of shadow renderables for this entity.
    /// </summary>
    protected ShadowRenderableList shadowRenderables = new ShadowRenderableList();
    /// <summary>
    ///    LOD bias factor, inverted for optimisation when calculating adjusted depth.
    /// </summary>
    protected float materialLodFactorInv;
    /// <summary>
    ///    Index of minimum detail LOD (NB higher index is lower detail).
    /// </summary>
    protected int minMaterialLodIndex;
    /// <summary>
    ///    Index of maximum detail LOD (NB lower index is higher detail).
    /// </summary>
    protected int maxMaterialLodIndex;
        /// <summary>
        ///     Frame the bones were last update.
        /// </summary>
        /// <remarks>
        ///     Stored as an array so the reference can be shared amongst skeleton instances.
        /// </remarks>
        protected ulong[] frameBonesLastUpdated = new ulong[] { 0 };
        /// <summary>
        ///     List of entities with various levels of detail.
        /// </summary>
        protected EntityList lodEntityList = new EntityList();
    public ICollection SubEntityMaterials 
    {
      get
      {
        Material[] materials = new Material[subEntityList.Count];
        for(int i = 0; i<subEntityList.Count; i++) 
        {
          materials[i] = subEntityList[i].Material;
        }
        return materials;
      }
    }
    public ICollection SubEntityMaterialNames
    {
      get
      {
        string[] materials = new string[subEntityList.Count];
        for(int i = 0; i<subEntityList.Count; i++) 
        {
          materials[i] = subEntityList[i].MaterialName;
        }
        return materials;
      }
    }

    #endregion Fields

    #region Constructors

    /// <summary>
    /// 
    /// </summary>
    /// <param name="name"></param>
    /// <param name="mesh"></param>
    /// <param name="creator"></param>
    internal Entity(string name, Mesh mesh, SceneManager creator) {
      this.name = name;
      this.sceneMgr = creator;
      //defaults to Points if not set
      this.renderDetail = SceneDetailLevel.Solid;

      SetMesh(mesh);

            
        }

    protected void SetMesh(Mesh mesh) {
      this.mesh = mesh;
      

      if(mesh.HasSkeleton && mesh.Skeleton != null) {
        skeletonInstance = new SkeletonInstance(mesh.Skeleton);
        skeletonInstance.Load();
      } else
        skeletonInstance = null;

      this.subEntityList.Clear();
      BuildSubEntities();

      lodEntityList.Clear();
      // Check if mesh is using manual LOD
      if (mesh.IsLodManual) {
        for (int i = 1; i < mesh.LodLevelCount; i++) {
          MeshLodUsage usage = mesh.GetLodLevel(i);

          // manually create entity
          Entity lodEnt = new Entity(string.Format("{0}Lod{1}", name, i), usage.manualMesh, sceneMgr);
          lodEntityList.Add(lodEnt);
        }
      }

      animationState.Clear();
      // init the AnimationState, if the mesh is animated
      if(mesh.HasSkeleton) {
        mesh.InitAnimationState(animationState);
        numBoneMatrices = skeletonInstance.BoneCount;
        boneMatrices = new Matrix4[numBoneMatrices];
        PrepareTempBlendedBuffers();
      }

      ReevaluateVertexProcessing();

      
      // LOD default settings
      meshLodFactorInv = 1.0f;
      // Backwards, remember low value = high detail
      minMeshLodIndex = 99;
      maxMeshLodIndex = 0;

      // Material LOD default settings
      materialLodFactorInv = 1.0f;
      maxMaterialLodIndex = 0;
      minMaterialLodIndex = 99;

      // Do we have a mesh where edge lists are not going to be available?
      if (!mesh.IsEdgeListBuilt && !mesh.AutoBuildEdgeLists) {
        this.CastShadows = false;
      }
    }

    #endregion

    #region Properties

    public SkeletonInstance Skeleton {
      get{ return this.skeletonInstance; } 
    }
    
    /// <summary>
    ///    Local bounding radius of this entity.
    /// </summary>
    public override float BoundingRadius {
      get {
        float radius = mesh.BoundingSphereRadius;

        // scale by the largest scale factor
        if(parentNode != null) {
          Vector3 s = parentNode.DerivedScale;
          radius *= MathUtil.Max(s.x, MathUtil.Max(s.y, s.z));
        }

        return radius;
      }
    }

    /// <summary>
    ///    Merge all the child object Bounds and return it.
    /// </summary>
    /// <returns></returns>
    public AxisAlignedBox ChildObjectsBoundingBox {
      get {
        AxisAlignedBox box;
        AxisAlignedBox fullBox = AxisAlignedBox.Null;

        for(int i = 0; i < childObjectList.Count; i++) {
          SceneObject child = childObjectList[i];
          box = child.BoundingBox;
          TagPoint tagPoint = (TagPoint)child.ParentNode;

          box.Transform(tagPoint.FullLocalTransform);

          fullBox.Merge(box);
        }

        return fullBox;
      }
    }

    /// <summary>
    ///    Gets/Sets the flag to render the skeleton of this entity.
    /// </summary>
    public bool DisplaySkeleton {
      get {
        return displaySkeleton;
      }
      set {
        displaySkeleton = value;
      }
    }


    /// <summary>
    ///    Returns true if this entity has a skeleton.
    /// </summary>
    public bool HasSkeleton {
      get {
        return skeletonInstance != null;
      }
    }

    /// <summary>
    ///    Returns whether or not hardware skinning is enabled.
    /// </summary>
    /// <remarks>
    ///    Because fixed-function indexed vertex blending is rarely supported
    ///    by existing graphics cards, hardware skinning can only be done if
    ///    the vertex programs in the materials used to render an entity support
    ///    it. Therefore, this method will only return true if all the materials
    ///    assigned to this entity have vertex programs assigned, and all those
    ///    vertex programs must support 'include_skeletal_animation true'.
    /// </remarks>
    public bool IsHardwareSkinningEnabled {
      get {
        return useHardwareSkinning;
      }
    }
            
    /// <summary>
    /// 
    /// </summary>
    /// DOC
    public int MeshLodIndex {
      get { 
        return meshLodIndex; 
      }
      set { 
        meshLodIndex = value; 
      }
    }

    /// <summary>
    ///    Gets the 3D mesh associated with this entity.
    /// </summary>
    public Mesh Mesh {
      get { 
        return mesh; 
      }
      set{
        SetMesh(value);
      }
    }

    /// <summary>
    /// 
    /// </summary>
    public string MaterialName {
      get{ return materialName; }
      set {
        materialName = value;
        //if null or empty string then reset the material to that defined by the mesh
        if(value == null || value == string.Empty) {
          foreach(SubEntity ent in subEntityList) {
            string defaultMaterial = ent.SubMesh.MaterialName;
            if(defaultMaterial != null && defaultMaterial != string.Empty)
              ent.MaterialName = defaultMaterial;
          }
        } else {

          // assign the material name to all sub entities
          for (int i = 0; i < subEntityList.Count; i++) {
            subEntityList[i].MaterialName = materialName;
          }
        }
            }
    }

    /// <summary>
    ///    Sets the rendering detail of this entire entity (solid, wireframe etc).
    /// </summary>
    public SceneDetailLevel RenderDetail {
      get {
        return renderDetail;
      }
      set {
        renderDetail = value;

        // also set for all sub entities
        for(int i = 0; i < subEntityList.Count; i++) {
          GetSubEntity(i).RenderDetail = renderDetail;
        }
      }
    }

    /// <summary>
    ///    Gets the number of sub entities that belong to this entity.
    /// </summary>
    public int SubEntityCount {
      get {
        return subEntityList.Count;
      }
    }

    #endregion

    #region Methods

    /// <summary>
    ///    Attaches another object to a certain bone of the skeleton which this entity uses.
    /// </summary>
    /// <remarks>
    ///    This method can be used to attach another object to an animated part of this entity,
    ///    by attaching it to a bone in the skeleton (with an offset if required). As this entity 
    ///    is animated, the attached object will move relative to the bone to which it is attached.
    /// </remarks>
    /// <param name="boneName">The name of the bone (in the skeleton) to attach this object.</param>
    /// <param name="sceneObject">Reference to the object to attach.</param>
    public TagPoint AttachObjectToBone(string boneName, SceneObject sceneObject) {
      return AttachObjectToBone(boneName, sceneObject, Quaternion.Identity);
    }

    /// <summary>
    ///    Attaches another object to a certain bone of the skeleton which this entity uses.
    /// </summary>
    /// <param name="boneName">The name of the bone (in the skeleton) to attach this object.</param>
    /// <param name="sceneObject">Reference to the object to attach.</param>
    /// <param name="offsetOrientation">An adjustment to the orientation of the attached object, relative to the bone.</param>
    public TagPoint AttachObjectToBone(string boneName, SceneObject sceneObject, Quaternion offsetOrientation) {
      return AttachObjectToBone(boneName, sceneObject, Quaternion.Identity, Vector3.UnitScale);
    }

    /// <summary>
    ///    Attaches another object to a certain bone of the skeleton which this entity uses.
    /// </summary>
    /// <param name="boneName">The name of the bone (in the skeleton) to attach this object.</param>
    /// <param name="sceneObject">Reference to the object to attach.</param>
    /// <param name="offsetOrientation">An adjustment to the orientation of the attached object, relative to the bone.</param>
    /// <param name="offsetPosition">An adjustment to the position of the attached object, relative to the bone.</param>
    public TagPoint AttachObjectToBone(string boneName, SceneObject sceneObject, Quaternion offsetOrientation, Vector3 offsetPosition) {
      if(sceneObject.IsAttached) {
        throw new AxiomException("SceneObject '{0}' is already attached to '{1}'", sceneObject.Name, sceneObject.ParentNode.Name);
      }

      if(!this.HasSkeleton) {
        throw new AxiomException("Entity '{0}' has no skeleton to attach an object to.", this.name);
      }

      Bone bone = skeletonInstance.GetBone(boneName);

      if(bone == null) {
        throw new AxiomException("Entity '{0}' does not have a skeleton with a bone named '{1}'.", this.name, boneName);
      }

      TagPoint tagPoint = 
        skeletonInstance.CreateTagPointOnBone(bone, offsetOrientation, offsetPosition);

      tagPoint.ParentEntity = this;
      tagPoint.ChildObject = sceneObject;

      AttachObjectImpl(sceneObject, tagPoint);

      return tagPoint;
    }

    /// <summary>
    ///    Internal implementation of attaching a 'child' object to this entity and assign 
    ///    the parent node to the child entity.
    /// </summary>
    /// <param name="sceneObject">Object to attach.</param>
    /// <param name="tagPoint">TagPoint to attach the object to.</param>
    protected void AttachObjectImpl(SceneObject sceneObject, TagPoint tagPoint) {
      childObjectList[sceneObject.Name] = sceneObject;
      sceneObject.NotifyAttached(tagPoint, true);
    }

    /// <summary>
    ///    Used to build a list of sub-entities from the meshes located in the mesh.
    /// </summary>
    protected void BuildSubEntities() {
      // loop through the models meshes and create sub entities from them
      for(int i = 0; i < mesh.SubMeshCount; i++) {
        SubMesh subMesh = mesh.GetSubMesh(i);
        SubEntity sub = new SubEntity();
        sub.Parent = this;
        sub.SubMesh = subMesh;

                if (subMesh.IsMaterialInitialized) {
                    sub.MaterialName = subMesh.MaterialName;
                }

                subEntityList.Add(sub);
      }
    }

    /// <summary>
    ///    Protected method to cache bone matrices from a skeleton.
    /// </summary>
    protected void CacheBoneMatrices() {
            ulong currentFrameCount = Root.Instance.CurrentFrameCount;

            if (frameBonesLastUpdated[0] == currentFrameCount) {
                return;
            }

            // Get the appropriate meshes skeleton here
      // Can use lower LOD mesh skeleton if mesh LOD is manual
      // We make the assumption that lower LOD meshes will have
      //   fewer bones than the full LOD, therefore marix stack will be
      //   big enough.

      // Check for LOD usage
            if (mesh.IsLodManual && meshLodIndex > 1) {
                // use lower detail skeleton
                Mesh lodMesh = mesh.GetLodLevel(meshLodIndex).manualMesh;

                if (!lodMesh.HasSkeleton) {
                    numBoneMatrices = 0;
                    return;
                }
            }

            skeletonInstance.SetAnimationState(animationState);
      skeletonInstance.GetBoneMatrices(boneMatrices);
            frameBonesLastUpdated[0] = currentFrameCount;

            // TODO: Skeleton instance sharing

      // update the child object's transforms
      for(int i = 0; i < childObjectList.Count; i++) {
        SceneObject child = childObjectList[i];
        child.ParentNode.Update(true, true);
      }

      // apply the current world transforms to these too, since these are used as
      // replacement world matrices
      Matrix4 worldXform = this.ParentNodeFullTransform;
      numBoneMatrices = skeletonInstance.BoneCount;

      for(int i = 0; i < numBoneMatrices; i++) {
        boneMatrices[i] = worldXform * boneMatrices[i];
      }
    }

    /// <summary>
    ///    Internal method - given vertex data which could be from the <see cref="Mesh"/> or 
    ///    any <see cref="SubMesh"/>, finds the temporary blend copy.
    /// </summary>
    /// <param name="originalData"></param>
    /// <returns></returns>
    protected VertexData FindBlendedVertexData(VertexData originalData) {
      if (originalData == mesh.SharedVertexData) {
        return sharedBlendedVertexData;
      }

      for(int i = 0; i < subEntityList.Count; i++) {
        SubEntity se = subEntityList[i];
        if (originalData == se.SubMesh.vertexData) {
          return se.blendedVertexData;
        }
      }

      throw new Exception("Cannot find blended version of the vertex data specified.");
    }

    /// <summary>
    ///    Internal method - given vertex data which could be from the  Mesh or 
    ///    any SubMesh, finds the corresponding SubEntity.
    /// </summary>
    /// <param name="original"></param>
    /// <returns></returns>
    protected SubEntity FindSubEntityForVertexData(VertexData original) {
      if(original == mesh.SharedVertexData) {
        return null;
      }

      for(int i = 0; i < subEntityList.Count; i++) {
        SubEntity subEnt = subEntityList[i];

        if(original == subEnt.SubMesh.vertexData) {
          return subEnt;
        }
      }

      // none found
      return null;
    }

    /// <summary>
    ///    Perform all the updates required for an animated entity.
    /// </summary>
    protected void UpdateAnimation() {
      // we only do these tasks if they have not already been done this frame
      ulong currentFrameNumber = Root.Instance.CurrentFrameCount;

      if(frameAnimationLastUpdated != currentFrameNumber) {
        CacheBoneMatrices();

        // software blend?
        bool hwSkinning = this.IsHardwareSkinningEnabled;

        if(!hwSkinning 
          || Root.Instance.SceneManager.ShadowTechnique == ShadowTechnique.StencilAdditive
          || Root.Instance.SceneManager.ShadowTechnique == ShadowTechnique.StencilModulative) {

          // Ok, we need to do a software blend
          // Blend normals in s/w only if we're not using h/w skinning,
          // since shadows only require positions
          bool blendNormals = !hwSkinning;

          if(sharedBlendedVertexData != null) {
            // blend shared geometry
            tempBlendedBuffer.CheckoutTempCopies(true, blendNormals);
            tempBlendedBuffer.BindTempCopies(sharedBlendedVertexData, hwSkinning);

            Mesh.SoftwareVertexBlend(mesh.SharedVertexData, sharedBlendedVertexData, boneMatrices, blendNormals);
          }

          // blend dedicated geometry for each submesh if need be
          for(int i = 0; i < subEntityList.Count; i++) {
            SubEntity subEntity = subEntityList[i];

            if(subEntity.IsVisible && subEntity.blendedVertexData != null) {
              subEntity.tempBlendedBuffer.CheckoutTempCopies(true, blendNormals);
              subEntity.tempBlendedBuffer.BindTempCopies(subEntity.blendedVertexData, hwSkinning);

              Mesh.SoftwareVertexBlend(subEntity.SubMesh.vertexData, subEntity.blendedVertexData, boneMatrices, blendNormals);
            }
          }
        }

        // trigger update of bounding box if necessary
        if(childObjectList.Count != 0) {
          parentNode.NeedUpdate();
        }

        // remember the last frame count
        frameAnimationLastUpdated = currentFrameNumber;
      }
    }

        /// <summary>
        ///    For entities based on animated meshes, gets the AnimationState object for a single animation.
        /// </summary>
        /// <remarks>
        ///    You animate an entity by updating the animation state objects. Each of these represents the
        ///    current state of each animation available to the entity. The AnimationState objects are
        ///    initialized from the Mesh object.
        /// </remarks>
        /// <param name="name"></param>
        /// <returns></returns>
        public AnimationStateCollection GetAllAnimationStates() {
            return animationState;
        }

        /// <summary>
        ///    For entities based on animated meshes, gets the AnimationState object for a single animation.
        /// </summary>
        /// <remarks>
        ///    You animate an entity by updating the animation state objects. Each of these represents the
        ///    current state of each animation available to the entity. The AnimationState objects are
        ///    initialized from the Mesh object.
        /// </remarks>
        /// <param name="name"></param>
        /// <returns></returns>
        public AnimationState GetAnimationState(string name) {
            Debug.Assert(animationState.ContainsKey(name), "animationState.ContainsKey(name)");

            return animationState[name];
        }
      
    #endregion Methods

    #region Implementation of IDisposable

    /// <summary>
    ///    
    /// </summary>
    public void Dispose() {
    }

    #endregion

    #region Implementation of SceneObject

        public override EdgeData GetEdgeList(int lodIndex) {
            return mesh.GetEdgeList(lodIndex);
        }

    public override void NotifyCurrentCamera(Camera camera) {
      if(parentNode != null) {
        float squaredDepth = parentNode.GetSquaredViewDepth(camera);
        
        // Adjust this depth by the entity bias factor
        float temp = squaredDepth * meshLodFactorInv;

        // Now adjust it by the camera bias
        temp = temp * camera.InverseLodBias;
                
        // Get the index at this biased depth
        meshLodIndex = mesh.GetLodIndexSquaredDepth(temp);
                
        // Apply maximum detail restriction (remember lower = higher detail)
        meshLodIndex = (int)MathUtil.Max(maxMeshLodIndex, meshLodIndex);
                
        // Apply minimum detail restriction (remember higher = lower detail)
        meshLodIndex = (int)MathUtil.Min(minMeshLodIndex, meshLodIndex);

        // now do material LOD
        // adjust this depth by the entity bias factor
        temp = squaredDepth * materialLodFactorInv;

        // now adjust it by the camera bias
        temp = temp * camera.InverseLodBias;

        // apply the material LOD to all sub entities
        for(int i = 0; i < subEntityList.Count; i++) {
          // get the index at this biased depth
          SubEntity subEnt = subEntityList[i];

          int idx = subEnt.Material.GetLodIndexSquaredDepth(temp);

          // Apply maximum detail restriction (remember lower = higher detail)
          idx = (int)MathUtil.Max(maxMaterialLodIndex, idx);
          // Apply minimum detail restriction (remember higher = lower detail)
          subEnt.materialLodIndex = (int)MathUtil.Min(minMaterialLodIndex, idx);
        }
      }

      // Notify child objects (tag points)
      for(int i = 0; i < childObjectList.Count; i++) {
        SceneObject child = childObjectList[i];
        child.NotifyCurrentCamera(camera);
      }
    }

    /// <summary>
    ///    Gets the SubEntity at the specified index.
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public SubEntity GetSubEntity(int index) {
      Debug.Assert(index < subEntityList.Count, "index < subEntityList.Count");

      return subEntityList[index];
    }

        /// <summary>
        ///     Gets a sub entity of this mesh with the specified name.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public SubEntity GetSubEntity(string name) {
            for (int i = 0; i < subEntityList.Count; i++) {
                SubEntity sub = subEntityList[i];

                if (sub.SubMesh.name == name) {
                    return sub;
                }
            }

            // not found
            throw new AxiomException("A SubEntity with the name '{0}' does not exist in Entity '{1}'", name, this.name);
        }

        /// <summary>
    ///    Trigger an evaluation of whether hardware skinning is supported for this entity.
    /// </summary>
    protected internal void ReevaluateVertexProcessing() {
            // init
            useHardwareSkinning = false;
            vertexProgramInUse = false;

            bool firstPass = true;

            // check for each sub entity
      for(int i = 0; i < this.SubEntityCount; i++, firstPass = false) {
        SubEntity subEntity = GetSubEntity(i);

        // grab the material and make sure it is loaded first
        Material m = subEntity.Material;
        m.Load();

        Technique t = m.GetBestTechnique();

        if(t == null) {
                    // no supported techniques
          continue;
        }

        Pass p = t.GetPass(0);

        if(p == null) {
          // no passes, so invalid
                    continue;
                }

        if(p.HasVertexProgram) {
                    // If one material uses a vertex program, set this flag 
                    // Causes some special processing like forcing a separate light cap
                    vertexProgramInUse = true;

                    // All materials must support skinning for us to consider using
                    // hardware skinning - if one fails we use software
                    if (firstPass) {
                        useHardwareSkinning = p.VertexProgram.IsSkeletalAnimationIncluded;
                    }
                    else {
                        useHardwareSkinning = useHardwareSkinning && p.VertexProgram.IsSkeletalAnimationIncluded;
                    }
        }
      }
    }

    /// <summary>
    ///    Sets a level-of-detail bias for the material detail of this entity.
    /// </summary>
    /// <remarks>
    ///    Level of detail reduction is normally applied automatically based on the Material 
    ///    settings. However, it is possible to influence this behavior for this entity
    ///    by adjusting the LOD bias. This 'nudges' the material level of detail used for this 
    ///    entity up or down depending on your requirements. You might want to use this
    ///    if there was a particularly important entity in your scene which you wanted to
    ///    detail better than the others, such as a player model.
    ///    <p/>
    ///    There are three parameters to this method; the first is a factor to apply; it 
    ///    defaults to 1.0 (no change), by increasing this to say 2.0, this model would 
    ///    take twice as long to reduce in detail, whilst at 0.5 this entity would use lower
    ///    detail versions twice as quickly. The other 2 parameters are hard limits which 
    ///    let you set the maximum and minimum level-of-detail version to use, after all
    ///    other calculations have been made. This lets you say that this entity should
    ///    never be simplified, or that it can only use LODs below a certain level even
    ///    when right next to the camera.
    /// </remarks>
    /// <param name="factor">Proportional factor to apply to the distance at which LOD is changed. 
    ///    Higher values increase the distance at which higher LODs are displayed (2.0 is 
    ///    twice the normal distance, 0.5 is half).</param>
    /// <param name="maxDetailIndex">The index of the maximum LOD this entity is allowed to use (lower
    ///    indexes are higher detail: index 0 is the original full detail model).</param>
    /// <param name="minDetailIndex">The index of the minimum LOD this entity is allowed to use (higher
    ///    indexes are lower detail. Use something like 99 if you want unlimited LODs (the actual
    ///    LOD will be limited by the number in the material)</param>
    public void SetMaterialLodBias(float factor, int maxDetailIndex, int minDetailIndex) {
      Debug.Assert(factor > 0.0f, "Bias factor must be > 0!");
      materialLodFactorInv = 1.0f / factor;
      maxMaterialLodIndex = maxDetailIndex;
      minMaterialLodIndex = minDetailIndex;
    }

    /// <summary>
    ///    Sets a level-of-detail bias on this entity.
    /// </summary>
    /// <remarks>
    ///    Level of detail reduction is normally applied automatically based on the Mesh 
    ///    settings. However, it is possible to influence this behavior for this entity
    ///    by adjusting the LOD bias. This 'nudges' the level of detail used for this 
    ///    entity up or down depending on your requirements. You might want to use this
    ///    if there was a particularly important entity in your scene which you wanted to
    ///    detail better than the others, such as a player model.
    ///    <p/>
    ///    There are three parameters to this method; the first is a factor to apply; it 
    ///    defaults to 1.0 (no change), by increasing this to say 2.0, this model would 
    ///    take twice as long to reduce in detail, whilst at 0.5 this entity would use lower
    ///    detail versions twice as quickly. The other 2 parameters are hard limits which 
    ///    let you set the maximum and minimum level-of-detail version to use, after all
    ///    other calculations have been made. This lets you say that this entity should
    ///    never be simplified, or that it can only use LODs below a certain level even
    ///    when right next to the camera.
    /// </remarks>
    /// <param name="factor">Proportional factor to apply to the distance at which LOD is changed. 
    ///    Higher values increase the distance at which higher LODs are displayed (2.0 is 
    ///    twice the normal distance, 0.5 is half).</param>
    /// <param name="maxDetailIndex">The index of the maximum LOD this entity is allowed to use (lower
    ///    indexes are higher detail: index 0 is the original full detail model).</param>
    /// <param name="minDetailIndex">The index of the minimum LOD this entity is allowed to use (higher
    ///    indexes are lower detail. Use something like 99 if you want unlimited LODs (the actual
    ///    LOD will be limited by the number in the Mesh)</param>
    public void SetMeshLodBias(float factor, int maxDetailIndex, int minDetailIndex) {
      Debug.Assert(factor > 0.0f, "Bias factor must be > 0!");
      meshLodFactorInv = 1.0f / factor;
      maxMeshLodIndex = maxDetailIndex;
      minMeshLodIndex = minDetailIndex;
    }

        /// <summary>
        ///     Copies a subset of animation states from source to target.
        /// </summary>
        /// <remarks>
        ///     This routine assume target is a subset of source, it will copy all animation state
        ///     of the target with the settings from source.
        /// </remarks>
        /// <param name="target">Reference to animation state set which will receive the states.</param>
        /// <param name="source">Reference to animation state set which will use as source.</param>
        public void CopyAnimationStateSubset(AnimationStateCollection target, AnimationStateCollection source) {
            for (int i = 0; i < target.Count; i++) {
                AnimationState targetState = target[i];
                AnimationState sourceState = source[targetState.Name];

                if (sourceState == null) {
                    throw new AxiomException("No animation entry found named '{0}'.", targetState.Name);
                }
                else {
                    sourceState.CopyTo(targetState);
                }
            }
        }

        /// <summary>
    ///    
    /// </summary>
    /// <param name="queue"></param>
    public override void UpdateRenderQueue(RenderQueue queue) {
      // Manual LOD sub entities
            if (meshLodIndex > 0 && mesh.IsLodManual) {
                Debug.Assert(meshLodIndex - 1 < lodEntityList.Count, "No LOD EntityList - did you build the manual LODs after creating the entity?");

                Entity lodEnt = lodEntityList[meshLodIndex - 1];

                // index - 1 as we skip index 0 (original LOD)
                if (this.HasSkeleton && lodEnt.HasSkeleton) {
                    // Copy the animation state set to lod entity, we assume the lod
                    // entity only has a subset animation states
                    CopyAnimationStateSubset(lodEnt.animationState, animationState);
                }

                lodEnt.UpdateRenderQueue(queue);
                return;
            }

            // add all visible sub entities to the render queue
      for(int i = 0; i < subEntityList.Count; i++) {
        if(subEntityList[i].IsVisible) {
          queue.AddRenderable(subEntityList[i], RenderQueue.DEFAULT_PRIORITY, renderQueueID);
        }
      }

      // Since we know we're going to be rendered, take this opportunity to 
      // update the animation
      if(mesh.HasSkeleton) {
        UpdateAnimation();

        // Update render queue with child objects (tag points)
        for(int i = 0; i < childObjectList.Count; i++) {
          SceneObject child = childObjectList[i];

                    if (child.IsVisible) {
                        child.UpdateRenderQueue(queue);
                    }
                }
      }

      // TODO: Add skeleton itself to the render queue
    }

    /// <summary>
    ///    Internal method for preparing this Entity for use in animation.
    /// </summary>
    protected internal void PrepareTempBlendedBuffers() {
      if(this.HasSkeleton) {
        // shared data
        if(mesh.SharedVertexData != null) {
          // Create temporary vertex blend info
          // Prepare temp vertex data if needed
          // Clone without copying data, remove blending info
          // (since blend is performed in software)
          sharedBlendedVertexData = CloneVertexDataRemoveBlendInfo(mesh.SharedVertexData);
          ExtractTempBufferInfo(sharedBlendedVertexData, tempBlendedBuffer);
        }

        // prepare temp blending buffers for subentites as well
        for(int i = 0; i < this.SubEntityCount; i++) {
          subEntityList[i].PrepareTempBlendBuffers();
        }
      }
    }

    /// <summary>
    ///    Internal method to clone vertex data definitions but to remove blend buffers.
    /// </summary>
    /// <param name="sourceData">Vertex data to clone.</param>
    /// <returns>A cloned instance of 'source' without blending information.</returns>
    protected internal VertexData CloneVertexDataRemoveBlendInfo(VertexData source) {
      // Clone without copying data
      VertexData ret = source.Clone(false);
      VertexElement blendIndexElem = 
        source.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.BlendIndices);
      VertexElement blendWeightElem = 
        source.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.BlendWeights);

      // Remove blend index
      if (blendIndexElem != null) {
        // Remove buffer reference
        ret.vertexBufferBinding.UnsetBinding(blendIndexElem.Source);
      }

      if (blendWeightElem != null && 
        blendWeightElem.Source != blendIndexElem.Source) {

        // Remove buffer reference
        ret.vertexBufferBinding.UnsetBinding(blendWeightElem.Source);
      }
      // remove elements from declaration
      ret.vertexDeclaration.RemoveElement(VertexElementSemantic.BlendIndices);
      ret.vertexDeclaration.RemoveElement(VertexElementSemantic.BlendWeights);

            // copy reference to w-coord buffer
            if (source.hardwareShadowVolWBuffer != null) {
                ret.hardwareShadowVolWBuffer = source.hardwareShadowVolWBuffer;
            }

            return ret;
    }

    /// <summary>
    ///    Internal method for extracting metadata out of source vertex data
    ///    for fast assignment of temporary buffers later.
    /// </summary>
    /// <param name="sourceData"></param>
    /// <param name="info"></param>
    protected internal void ExtractTempBufferInfo(VertexData sourceData, TempBlendedBufferInfo info) {
      VertexDeclaration decl = sourceData.vertexDeclaration;
      VertexBufferBinding bind = sourceData.vertexBufferBinding;
      VertexElement posElem = decl.FindElementBySemantic(VertexElementSemantic.Position);
      VertexElement normElem = decl.FindElementBySemantic(VertexElementSemantic.Normal);

      Debug.Assert(posElem != null, "Positions are required!");

      info.posBindIndex = posElem.Source;
      info.srcPositionBuffer = bind.GetBuffer(info.posBindIndex);

      if (normElem == null) {
        info.posNormalShareBuffer = false;
        info.srcNormalBuffer = null;
      }
      else {
        info.normBindIndex = normElem.Source;

        if (info.normBindIndex == info.posBindIndex) {
          info.posNormalShareBuffer = true;
          info.srcNormalBuffer = null;
        }
        else {
          info.posNormalShareBuffer = false;
          info.srcNormalBuffer = bind.GetBuffer(info.normBindIndex);
        }
      }
    }

    public override IEnumerator GetShadowVolumeRenderableEnumerator(ShadowTechnique technique, Light light, 
      HardwareIndexBuffer indexBuffer, bool extrudeVertices, float extrusionDistance, int flags) {

            Debug.Assert(indexBuffer != null, "Only external index buffers are supported right now");
      Debug.Assert(indexBuffer.Type == IndexType.Size16, "Only 16-bit indexes supported for now");

      // Prep mesh if required
      // NB This seems to result in memory corruptions, having problems
      // tracking them down. For now, ensure that shadows are enabled
      // before any entities are created
      if(!mesh.IsPreparedForShadowVolumes) {
        mesh.PrepareForShadowVolume();
        // reset frame last updated to force update of buffers
        frameAnimationLastUpdated = 0;
        // re-prepare buffers
        PrepareTempBlendedBuffers();
      }

      // Update any animation 
      if(this.HasSkeleton) {
        UpdateAnimation();
      }

      // Calculate the object space light details
      Vector4 lightPos = light.GetAs4DVector();

      // Only use object-space light if we're not doing transforms
      // Since when animating the positions are already transformed into 
      // world space so we need world space light position
      if (!this.HasSkeleton) {
        Matrix4 world2Obj = parentNode.FullTransform.Inverse();

        lightPos = world2Obj * lightPos; 
      }

      // We need to search the edge list for silhouette edges
            EdgeData edgeList = GetEdgeList();

            // Init shadow renderable list if required
      bool init = (shadowRenderables.Count == 0);

      if(init) {
        shadowRenderables.Capacity = edgeList.edgeGroups.Count;
      }

      bool updatedSharedGeomNormals = false;

      EntityShadowRenderable esr = null;
      EdgeData.EdgeGroup egi;

      // note: using capacity for the loop since no items are in the list yet.
      // capacity is set to how large the collection will be in the end
      for(int i = 0; i < shadowRenderables.Capacity; i++) {
        egi = (EdgeData.EdgeGroup)edgeList.edgeGroups[i];

        if (init) {
          VertexData data = null;

          if(this.HasSkeleton) {
            // Use temp buffers
            data = FindBlendedVertexData(egi.vertexData);
          }
          else {
            data = egi.vertexData;
          }

          // Try to find corresponding SubEntity; this allows the 
          // linkage of visibility between ShadowRenderable and SubEntity
          SubEntity subEntity = FindSubEntityForVertexData(egi.vertexData);

          // Create a new renderable, create a separate light cap if
          // we're using hardware skinning since otherwise we get
          // depth-fighting on the light cap
          esr = new EntityShadowRenderable(this, indexBuffer, data, vertexProgramInUse || !extrudeVertices, subEntity);

          shadowRenderables.Add(esr);
        }
        else {
          esr = (EntityShadowRenderable)shadowRenderables[i];

                    if(this.HasSkeleton) {
              // If we have a skeleton, we have no guarantee that the position
              // buffer we used last frame is the same one we used last frame
              // since a temporary buffer is requested each frame
              // therefore, we need to update the EntityShadowRenderable
              // with the current position buffer
              esr.RebindPositionBuffer();
                    }
        }

        // For animated entities we need to recalculate the face normals
        if(this.HasSkeleton) {
          if (egi.vertexData != mesh.SharedVertexData || !updatedSharedGeomNormals) {
            // recalculate face normals
            edgeList.UpdateFaceNormals(egi.vertexSet, esr.PositionBuffer);

                        // If we're not extruding in software we still need to update 
                        // the latter part of the buffer (the hardware extruded part)
                        // with the latest animated positions
                        if (!extrudeVertices) {
                            IntPtr srcPtr = esr.PositionBuffer.Lock(BufferLocking.Normal);
                            IntPtr destPtr = new IntPtr(srcPtr.ToInt32() + (egi.vertexData.vertexCount * 12));

                            // 12 = sizeof(float) * 3
                            Memory.Copy(srcPtr, destPtr, 12 * egi.vertexData.vertexCount);

                            esr.PositionBuffer.Unlock();
                        }

                        if (egi.vertexData == mesh.SharedVertexData) {
              updatedSharedGeomNormals = true;
            }
          }
        }
        // Extrude vertices in software if required
        if(extrudeVertices) {
          ExtrudeVertices(esr.PositionBuffer, egi.vertexData.vertexCount, lightPos, extrusionDistance);
        }

        // Stop suppressing hardware update now, if we were
        esr.PositionBuffer.SuppressHardwareUpdate(false);
      }

      // Calc triangle light facing
      UpdateEdgeListLightFacing(edgeList, lightPos);

      // Generate indexes and update renderables
      GenerateShadowVolume(edgeList, indexBuffer, light, shadowRenderables, flags);

      return shadowRenderables.GetEnumerator();
    }

    public override IEnumerator GetLastShadowVolumeRenderableEnumerator() {
      return shadowRenderables.GetEnumerator();
    }

    #endregion Methods

    #region Properties

    /// <summary>
    ///    Gets the number of bone matrices for this entity if it has a skeleton attached.
    /// </summary>
    public int BoneMatrixCount {
      get {
        return numBoneMatrices;
      }
    }

    /// <summary>
    ///    Gets the full local bounding box of this entity.
    /// </summary>
    public override AxisAlignedBox BoundingBox {
      // return the bounding box of our mesh
      get {   
        fullBoundingBox = mesh.BoundingBox;
        fullBoundingBox.Merge(this.ChildObjectsBoundingBox);

        // don't need to scale here anymore

        return fullBoundingBox;
      }
    }

    #endregion Properties

    #region ICloneable Members

    /// <summary>
    /// 
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public Entity Clone(string newName) {
      // create a new entity using the current mesh (uses same instance, not a copy for speed)
      Entity clone = sceneMgr.CreateEntity(newName, mesh.Name);

      // loop through each subentity and set the material up for the clone
      for(int i = 0; i < subEntityList.Count; i++) {
        SubEntity subEntity = subEntityList[i];
        clone.GetSubEntity(i).MaterialName = subEntity.MaterialName;
      }

      // copy the animation state as well
            if (animationState != null) {
                clone.animationState = animationState.Clone();
            }

            return clone;
    }

    #endregion

    #region Nested Classes

    /// <summary>
    ///    Nested class to allow entity shadows.
    /// </summary>
    protected class EntityShadowRenderable : ShadowRenderable {
      #region Fields

      protected Entity parent;
      /// <summary>
      ///    Shared ref to the position buffer.
      /// </summary>
      protected HardwareVertexBuffer positionBuffer;
      /// <summary>
      ///    Shared ref to w-coord buffer (optional).
      /// </summary>
      protected HardwareVertexBuffer wBuffer;
      /// <summary>
      ///    Ref to original vertex data.
      /// </summary>
      protected VertexData originalVertexData;
      /// <summary>
      ///    Original position buffer source binding.
      /// </summary>
      protected short originalPosBufferBinding;
      /// <summary>
      ///    Link to SubEntity, only present if SubEntity has it's own geometry.
      /// </summary>
      protected SubEntity subEntity;

      #endregion Fields

      #region Constructor

      public EntityShadowRenderable(Entity parent, HardwareIndexBuffer indexBuffer, 
        VertexData vertexData, bool createSeperateLightCap, SubEntity subEntity)
        : this(parent, indexBuffer, vertexData, createSeperateLightCap, subEntity, false) {}
  
      public EntityShadowRenderable(Entity parent, HardwareIndexBuffer indexBuffer, 
        VertexData vertexData, bool createSeparateLightCap, SubEntity subEntity, bool isLightCap) {

        this.parent = parent;

        // Save link to vertex data
        originalVertexData = vertexData;

        // Initialise render op
        renderOp.indexData = new IndexData();
        renderOp.indexData.indexBuffer = indexBuffer;
        renderOp.indexData.indexStart = 0;
        // index start and count are sorted out later

        // Create vertex data which just references position component (and 2 component)
        renderOp.vertexData = new VertexData();
        renderOp.vertexData.vertexDeclaration = 
          HardwareBufferManager.Instance.CreateVertexDeclaration();
        renderOp.vertexData.vertexBufferBinding = 
          HardwareBufferManager.Instance.CreateVertexBufferBinding();

        // Map in position data
        renderOp.vertexData.vertexDeclaration.AddElement(0, 0, VertexElementType.Float3, VertexElementSemantic.Position);
        originalPosBufferBinding = 
          vertexData.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.Position).Source;

        positionBuffer = vertexData.vertexBufferBinding.GetBuffer(originalPosBufferBinding);
        renderOp.vertexData.vertexBufferBinding.SetBinding(0, positionBuffer);

        // Map in w-coord buffer (if present)
        if(vertexData.hardwareShadowVolWBuffer != null) {
          renderOp.vertexData.vertexDeclaration.AddElement(1, 0, VertexElementType.Float1, VertexElementSemantic.TexCoords, 0);
          wBuffer = vertexData.hardwareShadowVolWBuffer;
          renderOp.vertexData.vertexBufferBinding.SetBinding(1, wBuffer);
        }

        // Use same vertex start as input
        renderOp.vertexData.vertexStart = vertexData.vertexStart;

        if (isLightCap) {
          // Use original vertex count, no extrusion
          renderOp.vertexData.vertexCount = vertexData.vertexCount;
        }
        else {
          // Vertex count must take into account the doubling of the buffer,
          // because second half of the buffer is the extruded copy
          renderOp.vertexData.vertexCount = vertexData.vertexCount * 2;

          if(createSeparateLightCap) {
            // Create child light cap
            lightCap = new EntityShadowRenderable(parent, indexBuffer, vertexData, false, subEntity, true);
          }
        }
      }

      #endregion Constructor

      #region Properties

      /// <summary>
      ///    Gets a reference to the position buffer in use by this renderable.
      /// </summary>
      public HardwareVertexBuffer PositionBuffer {
        get {
          return positionBuffer;
        }
      }

      /// <summary>
      ///    Gets a reference to the w-buffer in use by this renderable.
      /// </summary>
      public HardwareVertexBuffer WBuffer {
        get {
          return wBuffer;
        }
      }

      #endregion Properties

      #region Methods

      /// <summary>
      ///    Rebind the source positions for temp buffer users.
      /// </summary>
      public void RebindPositionBuffer() {
        positionBuffer = originalVertexData.vertexBufferBinding.GetBuffer(originalPosBufferBinding);
        renderOp.vertexData.vertexBufferBinding.SetBinding(0, positionBuffer);

        if(lightCap != null) {
          ((EntityShadowRenderable)lightCap).RebindPositionBuffer();
        }
      }

      #endregion Methods

      #region ShadowRenderable Members

      public override void GetWorldTransforms(Matrix4[] matrices) {
        if(parent.BoneMatrixCount == 0) {
          matrices[0] = parent.ParentNodeFullTransform;
        }
        else {
          // pretransformed
          matrices[0] = Matrix4.Identity;
        }
      }

      public override Quaternion WorldOrientation {
        get {
          return parent.ParentNode.DerivedOrientation;
        }
      }

      public override Vector3 WorldPosition {
        get {
          return parent.ParentNode.DerivedPosition;
        }
      }

      public override bool IsVisible {
        get {
          if(subEntity != null) {
            return subEntity.IsVisible;
          }

          return base.IsVisible;
        }
      }

      #endregion ShadowRenderable Members
    }

    #endregion Nested Classes
  }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.