//-----------------------------------------------------------------------------
// Copyright (c) 2013 GarageGames, LLC
//
// 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.
//-----------------------------------------------------------------------------

#ifndef _SCENE_OBJECT_H_
#include "2d/sceneobject/SceneObject.h"
#endif

#ifndef _DGL_H_
#include "graphics/dgl.h"
#endif

#ifndef _COLOR_H_
#include "graphics/color.h"
#endif

#ifndef _BITSTREAM_H_
#include "io/bitStream.h"
#endif

#ifndef _MMATHFN_H_
#include "math/mMathFn.h"
#endif

#ifndef _CONSOLETYPES_H_
#include "console/consoleTypes.h"
#endif

#ifndef _SIMBASE_H_
#include "sim/simBase.h"
#endif

#ifndef _BEHAVIORTEMPLATE_H_
#include "component/behaviors/behaviorTemplate.h"
#endif

#ifndef _SCENE_OBJECT_MOVE_TO_EVENT_H_
#include "2d/sceneobject/SceneObjectMoveToEvent.h"
#endif

#ifndef _SCENE_OBJECT_ROTATE_TO_EVENT_H_
#include "2d/sceneobject/SceneObjectRotateToEvent.h"
#endif

#ifndef _RENDER_PROXY_H_
#include "2d/core/RenderProxy.h"
#endif

#ifndef _STRINGUNIT_H_
#include "string/stringUnit.h"
#endif

// Script bindings.
#include "SceneObject_ScriptBinding.h"

// Debug Profiling.
#include "debug/profiler.h"

//-----------------------------------------------------------------------------

// Scene-Object counter.
static U32 sGlobalSceneObjectCount = 0;
static U32 sSceneObjectMasterSerialId = 0;

// Collision shapes custom node names.
static StringTableEntry shapeCustomNodeName     = StringTable->insert( "CollisionShapes" );

static StringTableEntry shapeDensityName        = StringTable->insert( "Density" );
static StringTableEntry shapeFrictionName       = StringTable->insert( "Friction" );
static StringTableEntry shapeRestitutionName    = StringTable->insert( "Restitution" );
static StringTableEntry shapeSensorName         = StringTable->insert( "Sensor" );
static StringTableEntry shapePointName          = StringTable->insert( "Point" );
static StringTableEntry shapePrevPointName      = StringTable->insert( "PreviousPoint" );
static StringTableEntry shapeNextPointName      = StringTable->insert( "NextPoint" );
static StringTableEntry circleTypeName          = StringTable->insert( "Circle" );
static StringTableEntry circleRadiusName        = StringTable->insert( "Radius" );
static StringTableEntry circleOffsetName        = StringTable->insert( "Offset" );
static StringTableEntry polygonTypeName         = StringTable->insert( "Polygon" );
static StringTableEntry chainTypeName           = StringTable->insert( "Chain" );
static StringTableEntry edgeTypeName            = StringTable->insert( "Edge" );

//------------------------------------------------------------------------------

// Important: If these defaults are changed then modify the associated "write" field protected methods to ensure
// that the associated field is persisted if not the default.
SceneObject::SceneObject() :
    /// Scene.
    mpScene(NULL),
    mpTargetScene(NULL),

    /// Lifetime.
    mLifetime(0.0f),
    mLifetimeActive(false),

    /// Layers.
    mSceneLayer(0),
    mSceneLayerMask(BIT(mSceneLayer)),
    mSceneLayerDepth(0.0f),

    /// Scene groups.
    mSceneGroup(0),
    mSceneGroupMask(BIT(mSceneGroup)),

    /// Area.
    mWorldProxyId(-1),

    /// Position / Angle.
    mPreTickPosition( 0.0f, 0.0f ),
    mPreTickAngle( 0.0f ),
    mRenderPosition( 0.0f, 0.0f ),
    mRenderAngle( 0.0f ),
    mSpatialDirty( true ),

    /// Body.
    mpBody(NULL),
    mWorldQueryKey(0),

    /// Collision control.
    mCollisionLayerMask(MASK_ALL),
    mCollisionGroupMask(MASK_ALL),
    mCollisionSuppress(false),
    mGatherContacts(false),
    mpCurrentContacts(NULL),

    /// Render visibility.                                        
    mVisible(true),

    /// Render blending.
    mBlendMode(true),
    mSrcBlendFactor(GL_SRC_ALPHA),
    mDstBlendFactor(GL_ONE_MINUS_SRC_ALPHA),
    mBlendColor(ColorF(1.0f,1.0f,1.0f,1.0f)),
    mAlphaTest(-1.0f),

    /// Render sorting.
    mSortPoint(0.0f,0.0f),

    /// Input events.
    mUseInputEvents(false),

    /// Script callbacks.
    mUpdateCallback(false),
    mCollisionCallback(false),
    mSleepingCallback(false),

    /// Debug mode.
    mDebugMask(0X00000000),

    /// Camera mounting.
    mpAttachedCamera(NULL),

    /// GUI attachment.
    mAttachedGuiSizeControl(false),
    mpAttachedGui(NULL),
    mpAttachedGuiSceneWindow(NULL),

    /// Safe deletion.
    mBeingSafeDeleted(false),
    mSafeDeleteReady(true),

    /// Miscellaneous.
    mBatchIsolated(false),
    mSerialiseKey(0),
    mEditorTickAllowed(true),
    mPickingAllowed(true),
    mAlwaysInScope(false),
    mMoveToEventId(0),
    mRotateToEventId(0),
    mSerialId(0),
    mRenderGroup( StringTable->EmptyString )
{
    // Set Vector Associations.
    VECTOR_SET_ASSOCIATION( mDestroyNotifyList );
    VECTOR_SET_ASSOCIATION( mCollisionFixtureDefs );
    VECTOR_SET_ASSOCIATION( mCollisionFixtures );

    // Assign scene-object index.
    mSerialId = ++sSceneObjectMasterSerialId;
    sGlobalSceneObjectCount++;

    // Initialize the body definition.
    // Important: If these defaults are changed then modify the associated "write" field protected methods to ensure
    // that the associated field is persisted if not the default.
    mBodyDefinition.userData        = static_cast<PhysicsProxy*>(this);
    mBodyDefinition.position.Set(0.0f, 0.0f);
    mBodyDefinition.angle           = 0.0f;
    mBodyDefinition.linearVelocity.Set(0.0f, 0.0f);
    mBodyDefinition.angularVelocity = 0.0f;
    mBodyDefinition.linearDamping   = 0.0f;
    mBodyDefinition.angularDamping  = 0.0f;
    mBodyDefinition.allowSleep      = true;
    mBodyDefinition.awake           = true;
    mBodyDefinition.fixedRotation   = false;
    mBodyDefinition.bullet          = false;
    mBodyDefinition.type            = b2_dynamicBody;
    mBodyDefinition.active          = true;
    mBodyDefinition.gravityScale    = 1.0f;

    // Initialize the default fixture definition.
    // Important: If these defaults are changed then modify the associated "write" field protected methods to ensure
    // that the associated field is persisted if not the default.
    mDefaultFixture.userData = static_cast<PhysicsProxy*>(this);
    mDefaultFixture.density     = 1.0f;
    mDefaultFixture.friction    = 0.2f;
    mDefaultFixture.restitution = 0.0f;
    mDefaultFixture.isSensor    = false;
    mDefaultFixture.shape       = NULL;

    // Set last awake state.
    mLastAwakeState = !mBodyDefinition.allowSleep || mBodyDefinition.awake;

    // Turn-off auto-sizing.
    mAutoSizing = false;

    // Set size.
    setSize( Vector2::getOne() );
}

//-----------------------------------------------------------------------------

SceneObject::~SceneObject()
{
    // Are we in a Scene?
    if ( mpScene )
    {
        // Yes, so remove from Scene.
        mpScene->removeFromScene( this );
    }

    // Decrease scene-object count.
    --sGlobalSceneObjectCount;
}

//-----------------------------------------------------------------------------

void SceneObject::initPersistFields()
{
    // Call parent.
    Parent::initPersistFields();

    /// Lifetime.
    addProtectedField("Lifetime", TypeF32, Offset(mLifetime, SceneObject), &setLifetime, &defaultProtectedGetFn, &writeLifetime, "");

    /// Scene Layers.
    addProtectedField("SceneLayer", TypeS32, Offset(mSceneLayer, SceneObject), &setSceneLayer, &defaultProtectedGetFn, &writeSceneLayer, "");
    addProtectedField("SceneLayerDepth", TypeF32, Offset(mSceneLayerDepth, SceneObject), &setSceneLayerDepth, &defaultProtectedGetFn, &writeSceneLayerDepth, "" );
    
    // Scene Groups.
    addProtectedField("SceneGroup", TypeS32, Offset(mSceneGroup, SceneObject), &setSceneGroup, &defaultProtectedGetFn, &writeSceneGroup, "");

    /// Area.
    addProtectedField("Size", TypeVector2, Offset( mSize, SceneObject), &setSize, &defaultProtectedGetFn, &writeSize, "");

    /// Position / Angle.
    addProtectedField("Position", TypeVector2, NULL, &setPosition, &getPosition, &writePosition, "");
    addProtectedField("Angle", TypeF32, NULL, &setAngle, &getAngle, &writeAngle, "");
    addProtectedField("FixedAngle", TypeBool, NULL, &setFixedAngle, &getFixedAngle, &writeFixedAngle, "");

    /// Body.
    addProtectedField("BodyType", TypeEnum, NULL, &setBodyType, &getBodyType, &writeBodyType, 1, &bodyTypeTable, "" );
    addProtectedField("Active", TypeBool, NULL, &setActive, &getActive, &writeActive, "" );
    addProtectedField("Awake", TypeBool, NULL, &setAwake, &getAwake, &writeAwake, "" );
    addProtectedField("Bullet", TypeBool, NULL, &setBullet, &getBullet, &writeBullet, "" );
    addProtectedField("SleepingAllowed", TypeBool, NULL, &setSleepingAllowed, &getSleepingAllowed, &writeSleepingAllowed, "" );

    /// Collision control.
    addProtectedField("CollisionGroups", TypeS32, Offset(mCollisionGroupMask, SceneObject), &setCollisionGroups, &defaultProtectedGetFn, &writeCollisionGroups, "");
    addProtectedField("CollisionLayers", TypeS32, Offset(mCollisionLayerMask, SceneObject), &setCollisionLayers, &defaultProtectedGetFn, &writeCollisionLayers, "");
    addField("CollisionSuppress", TypeBool, Offset(mCollisionSuppress, SceneObject), &writeCollisionSuppress, "");
    addProtectedField("GatherContacts", TypeBool, NULL, &setGatherContacts, &defaultProtectedGetFn, &writeGatherContacts, "");
    addProtectedField("DefaultDensity", TypeF32, Offset( mDefaultFixture.density, SceneObject), &setDefaultDensity, &defaultProtectedGetFn, &writeDefaultDensity, "");
    addProtectedField("DefaultFriction", TypeF32, Offset( mDefaultFixture.friction, SceneObject), &setDefaultFriction, &defaultProtectedGetFn, &writeDefaultFriction, "");
    addProtectedField("DefaultRestitution", TypeF32, Offset( mDefaultFixture.restitution, SceneObject), &setDefaultRestitution, &defaultProtectedGetFn, &writeDefaultRestitution, "");

    /// Velocities.
    addProtectedField("LinearVelocity", TypeVector2, NULL, &setLinearVelocity, &getLinearVelocity, &writeLinearVelocity, "");
    addProtectedField("AngularVelocity", TypeF32, NULL, &setAngularVelocity, &getAngularVelocity, &writeAngularVelocity, "");
    addProtectedField("LinearDamping", TypeF32, NULL, &setLinearDamping, &getLinearDamping, &writeLinearDamping, "");
    addProtectedField("AngularDamping", TypeF32, NULL, &setAngularDamping, &getAngularDamping, &writeAngularDamping, "");

    /// Gravity scaling.
    addProtectedField("GravityScale", TypeF32, NULL, &setGravityScale, &getGravityScale, &writeGravityScale, "");

    /// Render visibility.
    addField("Visible", TypeBool, Offset(mVisible, SceneObject), &writeVisible, "");

    /// Render blending.
    addField("BlendMode", TypeBool, Offset(mBlendMode, SceneObject), &writeBlendMode, "");
    addField("SrcBlendFactor", TypeEnum, Offset(mSrcBlendFactor, SceneObject), &writeSrcBlendFactor, 1, &srcBlendFactorTable);
    addField("DstBlendFactor", TypeEnum, Offset(mDstBlendFactor, SceneObject), &writeDstBlendFactor, 1, &dstBlendFactorTable);
    addField("BlendColor", TypeColorF, Offset(mBlendColor, SceneObject), &writeBlendColor, "");
    addField("AlphaTest", TypeF32, Offset(mAlphaTest, SceneObject), &writeAlphaTest, "");

    /// Render sorting.
    addField("SortPoint", TypeVector2, Offset(mSortPoint, SceneObject), &writeSortPoint, "");
    addField("RenderGroup", TypeString, Offset(mRenderGroup, SceneObject), &writeRenderGroup, "");

    /// Input events.
    addField("UseInputEvents", TypeBool, Offset(mUseInputEvents, SceneObject), &writeUseInputEvents, "");

    addField("PickingAllowed", TypeBool, Offset(mPickingAllowed, SceneObject), &writePickingAllowed, "");

    // Script callbacks.
    addField("UpdateCallback", TypeBool, Offset(mUpdateCallback, SceneObject), &writeUpdateCallback, "");
    addField("CollisionCallback", TypeBool, Offset(mCollisionCallback, SceneObject), &writeCollisionCallback, "");
    addField("SleepingCallback", TypeBool, Offset(mSleepingCallback, SceneObject), &writeSleepingCallback, "");

    /// Scene.
    addProtectedField("scene", TypeSimObjectPtr, Offset(mpScene, SceneObject), &setScene, &defaultProtectedGetFn, &writeScene, "");
}

//-----------------------------------------------------------------------------

bool SceneObject::onAdd()
{
    // Call Parent.
    if(!Parent::onAdd())
        return false;

    // Add to any target scene.
    if ( mpTargetScene )
    {
        // Add to the target scene.
        mpTargetScene->addToScene(this);

        mpTargetScene = NULL;
    }
   
    // Return Okay.
    return true;
}

//-----------------------------------------------------------------------------

void SceneObject::onRemove()
{
    // Detach Any GUI Control.
    detachGui();

    // Remove from Scene.
    if ( getScene() )
        getScene()->removeFromScene( this );

    // Call Parent.
    Parent::onRemove();
}

//-----------------------------------------------------------------------------

void SceneObject::onDestroyNotify( SceneObject* pSceneObject )
{
}

//-----------------------------------------------------------------------------

void SceneObject::OnRegisterScene( Scene* pScene )
{
    // Sanity!
    AssertFatal( mpScene == NULL, "Cannot register to a scene if already registered." );
    AssertFatal( mpBody == NULL, "Cannot create a physics body if one already exists." );

    // Initialize contact gathering.
    initializeContactGathering();

    // Set scene.
    mpScene = pScene;

    // Create the physics body.
    mpBody = pScene->getWorld()->CreateBody( &mBodyDefinition );

    // Set active status.
    if ( !isEnabled() ) mpBody->SetActive( false );

    // Create fixtures.
    for( typeCollisionFixtureDefVector::iterator itr = mCollisionFixtureDefs.begin(); itr != mCollisionFixtureDefs.end(); itr++ )
    {
        // Fetch fixture definition.
        b2FixtureDef* pFixtureDef = (*itr);

        // Create fixture.
        b2Fixture* pFixture = mpBody->CreateFixture( pFixtureDef );

        // Push fixture.
        mCollisionFixtures.push_back( pFixture );

        // Destroy fixture shape.
        delete pFixtureDef->shape;

        // Destroy fixture definition.
        delete pFixtureDef;          
    }

    // Clear offline fixture definitions.
    mCollisionFixtureDefs.clear();

    // Calculate current AABB.
    CoreMath::mCalculateAABB( getLocalSizedOOBB(), mpBody->GetTransform(), &mCurrentAABB );

    // Create world proxy Id.
    mWorldProxyId = pScene->getWorldQuery()->add( this );

    // Reset the spatials.
    resetTickSpatials();

    // Notify components.
    notifyComponentsAddToScene();
}

//-----------------------------------------------------------------------------

void SceneObject::OnUnregisterScene( Scene* pScene )
{
    // Sanity!
    AssertFatal( mpScene == pScene, "Cannot unregister from a scene that is not registered." );
    AssertFatal( mpBody != NULL, "Cannot unregister physics body as it does not exist." );

    // Notify components.
    notifyComponentsRemoveFromScene();

    // Copy fixtures to fixture definitions.
    for( typeCollisionFixtureVector::iterator itr = mCollisionFixtures.begin(); itr != mCollisionFixtures.end(); itr++ )
    {
        // Fetch fixture.
        b2Fixture* pFixture = (*itr);

        // Create fixture definition.
        b2FixtureDef* pFixtureDef = new b2FixtureDef();
        pFixtureDef->density      = pFixture->GetDensity();
        pFixtureDef->friction     = pFixture->GetFriction();
        pFixtureDef->restitution  = pFixture->GetRestitution();
        pFixtureDef->isSensor     = pFixture->IsSensor();        
        pFixtureDef->userData     = this;
        pFixtureDef->shape        = pFixture->GetShape()->Clone( pScene->getBlockAllocator() );

        // Push fixture definition.
        mCollisionFixtureDefs.push_back( pFixtureDef );
    }

    // Clear our fixture references.
    // The actual fixtures will get destroyed when the body is destroyed so no need to destroy them here.
    mCollisionFixtures.clear();

    // Transfer physics body configuration back to definition.
    mBodyDefinition.type            = getBodyType();
    mBodyDefinition.position        = getPosition();
    mBodyDefinition.angle           = getAngle();
    mBodyDefinition.linearVelocity  = getLinearVelocity();
    mBodyDefinition.angularVelocity = getAngularVelocity();
    mBodyDefinition.linearDamping   = getLinearDamping();
    mBodyDefinition.angularDamping  = getAngularDamping();
    mBodyDefinition.gravityScale    = getGravityScale();
    mBodyDefinition.allowSleep      = getSleepingAllowed();
    mBodyDefinition.awake           = getAwake();
    mBodyDefinition.fixedRotation   = getFixedAngle();
    mBodyDefinition.bullet          = getBullet();
    mBodyDefinition.active          = getActive();

    // Destroy current contacts.
    delete mpCurrentContacts;
    mpCurrentContacts = NULL;

    // Destroy the physics body.
    mpScene->getWorld()->DestroyBody( mpBody );
    mpBody = NULL;

    // Destroy world proxy Id.
    if ( mWorldProxyId != -1 )
    {

        mpScene->getWorldQuery()->remove( this );
        mWorldProxyId = -1;
    }

    // Reset scene.
    mpScene = NULL;
}

//-----------------------------------------------------------------------------

void SceneObject::resetTickSpatials( const bool resize )
{
    // Set coincident pre-tick, current & render.
    mPreTickPosition = mRenderPosition = getPosition();
    mPreTickAngle = mRenderAngle = getAngle();

    // Fetch body transform.
    b2Transform bodyXform = getTransform();

    // Calculate current AABB.
    CoreMath::mCalculateAABB( getLocalSizedOOBB(), bodyXform, &mCurrentAABB );

    // Set coincident AABBs.
    mPreTickAABB = mCurrentAABB;

    // Calculate render OOBB.
    CoreMath::mCalculateOOBB( getLocalSizedOOBB(), bodyXform, mRenderOOBB );

    // Update world proxy (if in scene).
    if ( mpScene )
    {
        // Fetch world query.
        WorldQuery* pWorldQuery = mpScene->getWorldQuery();

        // Are we resizing the object?
        if ( resize )
        {
            // Yes, so we need to recreate a proxy.
            pWorldQuery->remove( this );
            mWorldProxyId = pWorldQuery->add( this );

        }
        else
        {
            // No, so we can simply update the proxy.
            pWorldQuery->update( this, mCurrentAABB, b2Vec2( 0.0f, 0.0f ) );
        }
    }

    // Flag spatial changed.
    mSpatialDirty = true;
}

//-----------------------------------------------------------------------------

void SceneObject::preIntegrate( const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_PreIntegrate);

   // Finish if nothing is dirty.
    if ( !mSpatialDirty )
        return;

    // Reset spatial changed.
    mSpatialDirty = false;

    mPreTickPosition = mRenderPosition = getPosition();
    mPreTickAngle    = mRenderAngle = getAngle();
    mPreTickAABB     = mCurrentAABB;

    // Calculate render OOBB.
    CoreMath::mCalculateOOBB( getLocalSizedOOBB(), getTransform(), mRenderOOBB );
}

//-----------------------------------------------------------------------------

void SceneObject::integrateObject( const F32 totalTime, const F32 elapsedTime, DebugStats* pDebugStats )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_IntegrateObject);

    // Fetch position.
    const b2Vec2 position = getPosition();

    // Has the angle or position changed?
    if (    mPreTickAngle != getAngle() ||
            mPreTickPosition.x != position.x ||
            mPreTickPosition.y != position.y )
    {
        // Yes, so flag spatial dirty.
        mSpatialDirty = true;

        // Calculate current AABB.
        CoreMath::mCalculateAABB( getLocalSizedOOBB(), getTransform(), &mCurrentAABB );

        // Calculate tick AABB.
        b2AABB tickAABB;
        tickAABB.Combine( mPreTickAABB, mCurrentAABB );

        // Calculate tick displacement.
        b2Vec2 tickDisplacement = position - mPreTickPosition;
            
        // Update world proxy.
        mpScene->getWorldQuery()->update( this, tickAABB, tickDisplacement );
    }

    // Update Lifetime.
    if ( mLifetimeActive && !getScene()->getIsEditorScene() )
    {
        updateLifetime( elapsedTime );
    }

    // Update Any Attached GUI.
    if ( mpAttachedGui && mpAttachedGuiSceneWindow )
    {
        updateAttachedGui();
    }

    // Are we attached to a camera?
    if ( mpAttachedCamera )
    {
        // Yes, so calculate camera mount.
        mpAttachedCamera->calculateCameraMount( elapsedTime );
    }
}

//-----------------------------------------------------------------------------

void SceneObject::postIntegrate(const F32 totalTime, const F32 elapsedTime, DebugStats *pDebugStats)
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_PostIntegrate);

    // Finish if no scene.
    if( !getScene() )
        return;

    // Notify components.
    notifyComponentsUpdate();

    // Script "onUpdate".
    if ( mUpdateCallback )
    {
        PROFILE_SCOPE(SceneObject_onUpdateCallback);
        Con::executef(this, 1, "onUpdate");
    }

    // Are we using the sleeping callback?
    if ( mSleepingCallback )
    {
        PROFILE_SCOPE(SceneObject_onWakeSleepCallback);

        // Yes, so fetch the current awake state.
        const bool currentAwakeState = getAwake();

        // Did we change awake state?
        if ( currentAwakeState != mLastAwakeState )
        {
            // Yes, so update last awake state.
            mLastAwakeState = currentAwakeState;

            // Perform the appropriate callback.
            if ( currentAwakeState )
                Con::executef(this, 1, "onWake");
            else
                Con::executef(this, 1, "onSleep");
        }
    }
}

//-----------------------------------------------------------------------------

void SceneObject::interpolateObject( const F32 timeDelta )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_InterpolateObject);

    if ( mSpatialDirty )
    {
        // Debug Profiling.
        PROFILE_SCOPE(SceneObject_InterpolatePositionAngle);

        if ( timeDelta < 1.0f )
        {
            // Calculate render position.
            const b2Vec2 position = getPosition();
            b2Vec2 positionDelta = position - mPreTickPosition;
            positionDelta *= timeDelta;
            mRenderPosition = position - positionDelta;

            // Calculate render angle.
            const F32 angle = getAngle();

            F32 relativeAngle = angle - mPreTickAngle;
            if ( relativeAngle > b2_pi )
                relativeAngle -= b2_pi2;
            else if ( relativeAngle < -b2_pi )
                relativeAngle += b2_pi2;
            mRenderAngle = angle - (relativeAngle * timeDelta);
        }
        else
        {
            mRenderPosition = mPreTickPosition;
            mRenderAngle    = mPreTickAngle;
        }
     
        // Calculate render transform.
        b2Transform renderXF( mRenderPosition, b2Rot(mRenderAngle) );

        // Calculate render OOBB.
        CoreMath::mCalculateOOBB( getLocalSizedOOBB(), renderXF, mRenderOOBB );
    }

    // Update Any Attached GUI.
    if ( mpAttachedGui && mpAttachedGuiSceneWindow )
    {
        updateAttachedGui();
    }

    // Are we attached to a camera?
    if ( mpAttachedCamera )
    {
        // Yes, so interpolate camera mount.
        mpAttachedCamera->interpolateCameraMount( timeDelta );
    }
};

//-----------------------------------------------------------------------------

void SceneObject::sceneRenderFallback( const SceneRenderState* pSceneRenderState, const SceneRenderRequest* pSceneRenderRequest, BatchRender* pBatchRenderer )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_SceneRenderFallback);

    // Fetch the 'cannot render' proxy.
    RenderProxy* pNoImageRenderProxy = Sim::findObject<RenderProxy>( CANNOT_RENDER_PROXY_NAME );

    // Finish if no render proxy available or it can't render.
    if ( pNoImageRenderProxy == NULL || !pNoImageRenderProxy->validRender() )
        return;

    // Fetch render AABB.
    const b2Vec2* pRenderOOBB = getRenderOOBB();

    // Render using render proxy.
    pNoImageRenderProxy->render(
        false, false,
        pRenderOOBB[0], pRenderOOBB[1], pRenderOOBB[2], pRenderOOBB[3],
        pBatchRenderer );
}

//-----------------------------------------------------------------------------

void SceneObject::sceneRenderOverlay( const SceneRenderState* sceneRenderState )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_SceneRenderOverlay);

    // Get Scene.
    Scene* pScene = getScene();

    // Cannot do anything without scene!
    if ( !pScene )
        return;

    // Don't draw debug if not enabled.
    if ( !isEnabled() )
        return;

    // Get merged Local/Scene Debug Mask.
    U32 debugMask = getDebugMask() | pScene->getDebugMask();

    // Finish here if we're not rendering any debug info or only have scene-rendered debug options.
    if ( !debugMask || (debugMask & ~(Scene::SCENE_DEBUG_METRICS | Scene::SCENE_DEBUG_JOINTS)) == 0 )
        return;

    // Clear blending.
    glDisable( GL_BLEND );
    glDisable( GL_TEXTURE_2D );

    // AABB debug draw.
    if ( debugMask & Scene::SCENE_DEBUG_AABB )
    {
        pScene->mDebugDraw.DrawAABB( mCurrentAABB, ColorF(0.7f, 0.7f, 0.9f) );
    }

    // OOBB debug draw.
    if ( debugMask & Scene::SCENE_DEBUG_OOBB )
    {
        pScene->mDebugDraw.DrawOOBB( mRenderOOBB, ColorF(0.9f, 0.9f, 1.0f) );
    }

    // Asleep debug draw.
    if ( !getAwake() && debugMask & Scene::SCENE_DEBUG_SLEEP )
    {
        pScene->mDebugDraw.DrawAsleep( mRenderOOBB, ColorF( 0.0f, 1.0f, 0.0f ) );
    }

    // Collision Shapes.
    if ( debugMask & Scene::SCENE_DEBUG_COLLISION_SHAPES )
    {
        pScene->mDebugDraw.DrawCollisionShapes( getRenderTransform(), getBody() );
    }

    // Position and local center of mass.
    if ( debugMask & Scene::SCENE_DEBUG_POSITION_AND_COM )
    {
        const b2Vec2 renderPosition = getRenderPosition();

        pScene->mDebugDraw.DrawPoint( renderPosition + getLocalCenter(), 6, ColorF( 0.0f, 1.0f, 0.4f ) );
        pScene->mDebugDraw.DrawPoint( renderPosition, 4, ColorF( 0.0f, 0.4f, 1.0f ) );
    }

    // Sort Points.
    if ( debugMask & Scene::SCENE_DEBUG_SORT_POINTS )
    {
        pScene->mDebugDraw.DrawSortPoint( getRenderPosition(), getSize(), mSortPoint );
    }
}

//-----------------------------------------------------------------------------

U32 SceneObject::packUpdate(NetConnection * conn, U32 mask, BitStream *stream)
{
    return 0;
}

//-----------------------------------------------------------------------------

void SceneObject::unpackUpdate(NetConnection * conn, BitStream *stream)
{
}

//-----------------------------------------------------------------------------

void SceneObject::setEnabled( const bool enabled )
{
    // Call parent.
    Parent::setEnabled( enabled );

    // If we have a scene, modify active.
    if ( mpScene )
    {
        mpBody->SetActive( enabled );
    }
}

//-----------------------------------------------------------------------------

void SceneObject::setLifetime( const F32 lifetime )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_setLifetime);

    // Usage Flag.
    mLifetimeActive = mGreaterThanZero( lifetime );

    // Is life active?
    if ( mLifetimeActive )
    {
        // Yes, so set to incoming lifetime.
        mLifetime = lifetime;
    }
    else
    {
        // No, so reset it to be safe.
        mLifetime = 0.0f;
    }
}

//-----------------------------------------------------------------------------

void SceneObject::updateLifetime( const F32 elapsedTime )
{
    // Update Lifetime.
    if ( mLifetimeActive )
    {
        // Reduce Lifetime.
        setLifetime( getLifetime() - elapsedTime );

        // Are we now dead?
        if ( mLessThanOrEqual( getLifetime(), 0.0f) )
        {
            // Yes, so reset lifetime.
            setLifetime( 0.0f );

            // Initiate Death!
            safeDelete();
        }
    }
}

//-----------------------------------------------------------------------------

void SceneObject::setSceneLayer( const U32 sceneLayer )
{
    // Check Layer.
    if ( sceneLayer > (MAX_LAYERS_SUPPORTED-1) )
    {
        Con::warnf("SceneObject::setSceneLayer() - Invalid scene layer '%d' (0-31).", sceneLayer);
        return;
    }

    // Set Layer.
    mSceneLayer = sceneLayer;

    // Set Layer Mask.
    mSceneLayerMask = BIT( mSceneLayer );
}

//-----------------------------------------------------------------------------

bool SceneObject::setSceneLayerDepthFront( void )
{
    // Fetch the scene.
    Scene* pScene = getScene();

    // Finish if no scene or only a single object.
    if ( pScene == NULL || pScene->getSceneObjectCount() == 1 )
        return false;

    // Fetch scene objects.
    typeSceneObjectVector layerList;
    pScene->getSceneObjects( layerList, getSceneLayer() );

    // Fetch layer object count.
    const U32 layerObjectCount = (U32)layerList.size();

    // Sort the layer.
    dQsort(layerList.address(), layerObjectCount, sizeof(SceneObject*), sceneObjectLayerDepthSort);

    // Reset object index.
    U32 objectIndex = 0;

    // Find object index.
    for ( ; objectIndex < layerObjectCount; ++objectIndex )
    {
        // Stop if this is the current object index.
        if ( layerList[objectIndex] == this )
            break;
    }

    // Finish if already at front of layer.
    if ( objectIndex == layerObjectCount-1 )
        return false;

    // Fetch furthest front depth.
    const F32 frontDepth = layerList.last()->getSceneLayerDepth();

    // Adjust depth to be in front.
    setSceneLayerDepth( frontDepth - 1.0f );

    return true;
}

//-----------------------------------------------------------------------------

bool SceneObject::setSceneLayerDepthBack( void )
{
    // Fetch the scene.
    Scene* pScene = getScene();

    // Finish if no scene or only a single object.
    if ( pScene == NULL || pScene->getSceneObjectCount() == 1 )
        return false;

    // Fetch scene objects.
    typeSceneObjectVector layerList;
    pScene->getSceneObjects( layerList, getSceneLayer() );

    // Fetch layer object count.
    const U32 layerObjectCount = (U32)layerList.size();

    // Sort the layer.
    dQsort(layerList.address(), layerObjectCount, sizeof(SceneObject*), sceneObjectLayerDepthSort);

    // Reset object index.
    U32 objectIndex = 0;

    // Find object index.
    for ( ; objectIndex < layerObjectCount; ++objectIndex )
    {
        // Stop if this is the current object index.
        if ( layerList[objectIndex] == this )
            break;
    }

    // Finish if already at back of layer.
    if ( objectIndex == 0 )
        return false;

    // Fetch furthest back depth.
    const F32 backDepth = layerList.first()->getSceneLayerDepth();

    // Adjust depth to be in back.
    setSceneLayerDepth( backDepth + 1.0f );

    return true;
}

//-----------------------------------------------------------------------------

bool SceneObject::setSceneLayerDepthForward( void )
{
    // Fetch the scene.
    Scene* pScene = getScene();

    // Finish if no scene or only a single object.
    if ( pScene == NULL || pScene->getSceneObjectCount() == 1 )
        return false;

    // Fetch scene objects.
    typeSceneObjectVector layerList;
    pScene->getSceneObjects( layerList, getSceneLayer() );

    // Fetch layer object count.
    const U32 layerObjectCount = (U32)layerList.size();

    // Sort the layer.
    dQsort(layerList.address(), layerObjectCount, sizeof(SceneObject*), sceneObjectLayerDepthSort);

    // Reset object index.
    U32 objectIndex = 0;

    // Find object index.
    for ( ; objectIndex < layerObjectCount; ++objectIndex )
    {
        // Stop if this is the current object index.
        if ( layerList[objectIndex] == this )
            break;
    }

    // Finish if already at the front of the layer.
    if ( objectIndex == layerObjectCount-1 )
        return false;

    // Fetch forwards depth.
    const F32 forwardDepth = layerList[objectIndex+1]->getSceneLayerDepth();

    // Adjust depth to be forward.
    setSceneLayerDepth( forwardDepth - 0.5f );

    // Finish if we were almost at the front anyway.
    if ( objectIndex == layerObjectCount-2 )
        return true;

    // Adjust next objects forwards.
    for( U32 forwardIndex = objectIndex+2; forwardIndex < layerObjectCount; ++forwardIndex )
    {
        // Fetch scene object.
        SceneObject* forwardSceneObject = layerList[forwardIndex];

        // Adjust depth to be forwards.
        forwardSceneObject->setSceneLayerDepth( forwardSceneObject->getSceneLayerDepth() - 1.0f );
    }

    return true;
}

//-----------------------------------------------------------------------------

bool SceneObject::setSceneLayerDepthBackward( void )
{
    // Fetch the scene.
    Scene* pScene = getScene();

    // Finish if no scene or only a single object.
    if ( pScene == NULL || pScene->getSceneObjectCount() == 1 )
        return false;

    // Fetch scene objects.
    typeSceneObjectVector layerList;
    pScene->getSceneObjects( layerList, getSceneLayer() );

    // Fetch layer object count.
    const U32 layerObjectCount = (U32)layerList.size();

    // Sort the layer.
    dQsort(layerList.address(), layerObjectCount, sizeof(SceneObject*), sceneObjectLayerDepthSort);

    // Reset object index.
    U32 objectIndex = 0;

    // Find object index.
    for ( ; objectIndex < layerObjectCount; ++objectIndex )
    {
        // Stop if this is the current object index.
        if ( layerList[objectIndex] == this )
            break;
    }

    // Finish if already at the back of the layer.
    if ( objectIndex == 0 )
        return false;

    // Fetch backwards depth.
    const F32 backDepth = layerList[objectIndex-1]->getSceneLayerDepth();

    // Adjust depth to be backwards.
    setSceneLayerDepth( backDepth + 0.5f );

    // Finish if we were almost at the back anyway.
    if ( objectIndex == 1 )
        return true;

    // Adjust previous objects backwards.
    for( U32 backIndex = 0; backIndex < (objectIndex-1); ++backIndex )
    {
        // Fetch scene object.
        SceneObject* backSceneObject = layerList[backIndex];

        // Adjust depth to be backwards.
        backSceneObject->setSceneLayerDepth( backSceneObject->getSceneLayerDepth() + 1.0f );
    }

    return true;
}

//-----------------------------------------------------------------------------

void SceneObject::setSceneGroup( const U32 sceneGroup )
{
    // Check Group.
    if ( sceneGroup > 31 )
    {
        Con::warnf("SceneObject::setSceneGroup() - Invalid scene group '%d' (0-31).", sceneGroup);
        return;
    }

    // Set Group.
    mSceneGroup = sceneGroup;

    // Set Group Mask.
    mSceneGroupMask = BIT( mSceneGroup );
}

//-----------------------------------------------------------------------------

void SceneObject::setArea( const Vector2& corner1, const Vector2& corner2 )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_SetArea);

   // Calculate Normalized region.
   const Vector2 topLeft((corner1.x <= corner2.x) ? corner1.x : corner2.x, (corner1.y <= corner2.y) ? corner1.y : corner2.y);
   const Vector2 bottomRight((corner1.x > corner2.x) ? corner1.x : corner2.x, (corner1.y > corner2.y) ? corner1.y : corner2.y);

   // Calculate New Size.
   const Vector2 size = bottomRight - topLeft;

   // Calculate New Position.
   const Vector2 position = topLeft + (size * 0.5f);

   // Reset angle.
   setAngle( 0.0f );

   // Set Position/Size.
   setPosition( position );
   setSize( size );
}

//-----------------------------------------------------------------------------

void SceneObject::setSize( const Vector2& size )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_SetSize);

    mSize = size;

    // Calculate half size.
    const F32 halfWidth = size.x * 0.5f;
    const F32 halfHeight = size.y * 0.5f;

    // Set local size OOBB.
    mLocalSizeOOBB[0].Set( -halfWidth, -halfHeight );
    mLocalSizeOOBB[1].Set( +halfWidth, -halfHeight );
    mLocalSizeOOBB[2].Set( +halfWidth, +halfHeight );
    mLocalSizeOOBB[3].Set( -halfWidth, +halfHeight );

    if ( mpScene )
    {
        // Reset tick spatials.
        resetTickSpatials( true );
    }
}

//-----------------------------------------------------------------------------
  
void SceneObject::setPosition( const Vector2& position )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_SetPosition);

    if ( mpScene )
    {
        mpBody->SetTransform( position, mpBody->GetAngle() );

        // Reset tick spatials.
        resetTickSpatials();
    }
    else
    {
        mBodyDefinition.position = position;
    }
}

//-----------------------------------------------------------------------------

void SceneObject::setAngle( const F32 radians )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_SetAngle);

    if ( mpScene )
    {
        mpBody->SetTransform( mpBody->GetPosition(), radians );

        // Reset tick spatials.
        resetTickSpatials();
    }
    else
    {
        mBodyDefinition.angle = radians;
    }
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getLocalPoint( const Vector2 &worldPoint )
{
    if ( mpScene )
    {
        return mpBody->GetLocalPoint( worldPoint );
    }

    // Calculate body definition transform.
    const b2Transform transform( mBodyDefinition.position, b2Rot(mBodyDefinition.angle) );

    return b2MulT(transform, worldPoint);
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getWorldPoint( const Vector2 &localPoint )
{
    if ( mpScene )
    {
        return mpBody->GetWorldPoint( localPoint );
    }

    // Calculate body definition transform.
    const b2Transform transform( mBodyDefinition.position, b2Rot(mBodyDefinition.angle) );

    return b2Mul(transform, localPoint);
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getLocalVector( const Vector2& worldVector )
{
    if ( mpScene )
    {
        return mpBody->GetLocalVector( worldVector );
    }

    // Calculate body definition transform.
    const b2Transform transform( mBodyDefinition.position, b2Rot(mBodyDefinition.angle) );

    return b2MulT(transform.q, worldVector);
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getWorldVector( const Vector2& localVector )
{
    if ( mpScene )
    {
        return mpBody->GetWorldVector( localVector );
    }

    // Calculate body definition transform.
    const b2Transform transform( mBodyDefinition.position, b2Rot(mBodyDefinition.angle) );

    return b2Mul(transform.q, localVector);
}

//-----------------------------------------------------------------------------

bool SceneObject::getIsPointInOOBB( const Vector2& worldPoint )
{
    // Fetch local point.
    Vector2 localPoint = getLocalPoint( worldPoint );

    // Turn the local point into an absolute component distance.
    localPoint.absolute();

    const Vector2 halfSize = getHalfSize();

    // Check distance.
    return !( localPoint.x > halfSize.x || localPoint.y > halfSize.y );
}

//-----------------------------------------------------------------------------

bool SceneObject::getIsPointInCollisionShape( const U32 shapeIndex, const Vector2& worldPoint )
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::getIsPointInCollisionShape() - Invalid shape index." );

    // Sanity!
    if ( !mpScene )
    {
        Con::warnf( "SceneObject::getIsPointInCollisionShape() - Cannot test for point, object not in scene." );
        return false;
    }

    // Fetch fixture.
    b2Fixture* pFixture = mCollisionFixtures[shapeIndex];

    // Fetch local point.
    const Vector2 localPoint = getLocalPoint( worldPoint );

    // Test point.
    return pFixture->TestPoint( localPoint );
}

//-----------------------------------------------------------------------------

void SceneObject::setBodyType( const b2BodyType type )
{
    // Sanity!
    AssertFatal( type == b2_staticBody || type == b2_kinematicBody || type == b2_dynamicBody, "Invalid body type." );

    if ( mpScene )
    {
        mpBody->SetType( type );
        return;
    }
    else
    {
        mBodyDefinition.type = type;
    }
}


//-----------------------------------------------------------------------------

void SceneObject::applyForce( const Vector2& worldForce, const bool wake )
{
    // Ignore if not in scene.
    if ( !mpScene )
        return;

    applyForce( worldForce, getPosition() + getLocalCenter(), wake );
}

//-----------------------------------------------------------------------------

void SceneObject::applyForce( const Vector2& worldForce, const Vector2& worldPoint, const bool wake )
{
    // Ignore if not in scene.
    if ( !mpScene )
        return;

    getBody()->ApplyForce( worldForce, worldPoint, wake );
}

//-----------------------------------------------------------------------------

void SceneObject::applyTorque( const F32 torque, const bool wake )
{
    // Ignore if not in scene.
    if ( !mpScene )
        return;

    getBody()->ApplyTorque( torque, wake );
}

//-----------------------------------------------------------------------------

void SceneObject::applyLinearImpulse( const Vector2& worldImpulse, const bool wake )
{
    // Ignore if not in scene.
    if ( !mpScene )
        return;

    applyLinearImpulse( worldImpulse, getPosition() + getLocalCenter(), wake );
}

//-----------------------------------------------------------------------------

void SceneObject::applyLinearImpulse( const Vector2& worldImpulse, const Vector2& worldPoint, const bool wake )
{
    // Ignore if not in scene.
    if ( !mpScene )
        return;

    getBody()->ApplyLinearImpulse( worldImpulse, worldPoint, wake );
}

//-----------------------------------------------------------------------------

void SceneObject::applyAngularImpulse( const F32 impulse, const bool wake )
{
    // Ignore if not in scene.
    if ( !mpScene )
        return;

    getBody()->ApplyAngularImpulse( impulse, wake );
}

//-----------------------------------------------------------------------------

void SceneObject::setCollisionAgainst( const SceneObject* pSceneObject, const bool clearMasks )
{
    // Do we need to clear existing masks?
    if ( clearMasks )
    {
        // Yes, so just set the masks to the referenced-objects' masks.
        setCollisionGroupMask( pSceneObject->getSceneGroupMask() );
        setCollisionLayerMask( pSceneObject->getSceneLayerMask() ); 
    }
    else
    {
        // No, so merge with existing masks.
        setCollisionGroupMask( getCollisionGroupMask() | pSceneObject->getSceneGroupMask() );
        setCollisionLayerMask( getCollisionLayerMask() | pSceneObject->getSceneLayerMask() ); 
    }
}

//-----------------------------------------------------------------------------

void SceneObject::setDefaultDensity( const F32 density, const bool updateShapes )
{
    mDefaultFixture.density = density;

    // Early-out if not updating shapes.
    if ( !updateShapes )
        return;

    if ( mpScene )
    {
        // Update live fixtures.
        for( U32 index = 0; index < (U32)mCollisionFixtures.size(); ++index )
        {
            mCollisionFixtures[index]->SetDensity( density );
        }

        // Update the body mass data.
        mpBody->ResetMassData();

        return;
    }

    // Update offline fixture definitions.
    for( U32 index = 0; index < (U32)mCollisionFixtureDefs.size(); ++index )
    {
        mCollisionFixtureDefs[index]->density = density;
    }
}

//-----------------------------------------------------------------------------

void SceneObject::setDefaultFriction( const F32 friction, const bool updateShapes )
{
    mDefaultFixture.friction = friction;

    // Early-out if not updating shapes.
    if ( !updateShapes )
        return;

    if ( mpScene )
    {
        // Update live fixtures.
        for( U32 index = 0; index < (U32)mCollisionFixtures.size(); ++index )
        {
            mCollisionFixtures[index]->SetFriction( friction );
        }
        return;
    }

    // Update offline fixture definitions.
    for( U32 index = 0; index < (U32)mCollisionFixtureDefs.size(); ++index )
    {
        mCollisionFixtureDefs[index]->friction = friction;
    }
}

//-----------------------------------------------------------------------------

void SceneObject::setDefaultRestitution( const F32 restitution, const bool updateShapes )
{
    mDefaultFixture.restitution = restitution;

    // Early-out if not updating shapes.
    if ( !updateShapes )
        return;

    if ( mpScene )
    {
        // Update live fixtures.
        for( U32 index = 0; index < (U32)mCollisionFixtures.size(); ++index )
        {
            mCollisionFixtures[index]->SetRestitution( restitution );
        }
        return;
    }

    // Update offline fixture definitions.
    for( U32 index = 0; index < (U32)mCollisionFixtureDefs.size(); ++index )
    {
        mCollisionFixtureDefs[index]->restitution = restitution;
    }
}

//-----------------------------------------------------------------------------

void SceneObject::initializeContactGathering( void )
{
    // Are we gathering contacts?
    if ( !mGatherContacts )
    {
        // No, so do we have any current contacts?
        if ( mpCurrentContacts )
        {
            // Yes, so destroy them.
            delete mpCurrentContacts;
            mpCurrentContacts = NULL;
        }
        return;
    }

    // Clear current contacts if already present.
    if ( mpCurrentContacts != NULL )
    {
        mpCurrentContacts->clear();
        return;
    }

    // Generate current contacts.
    mpCurrentContacts = new Scene::typeContactVector();
}

//-----------------------------------------------------------------------------

void SceneObject::onBeginCollision( const TickContact& tickContact )
{
    // Finish if we're not gathering contacts.
    if ( !mGatherContacts )
        return;

    // Sanity!
    AssertFatal( mpCurrentContacts != NULL, "SceneObject::onBeginCollision() - Contacts not initialized correctly." );
    AssertFatal( tickContact.mpSceneObjectA == this || tickContact.mpSceneObjectB == this, "SceneObject::onBeginCollision() - Contact does not involve this scene object." );

    // Keep contact.
    mpCurrentContacts->push_back( tickContact );
}

//-----------------------------------------------------------------------------

void SceneObject::onEndCollision( const TickContact& tickContact )
{
    // Finish if we're not gathering contacts.
    if ( !mGatherContacts )
        return;

    // Sanity!
    AssertFatal( mpCurrentContacts != NULL, "SceneObject::onBeginCollision() - Contacts not initialized correctly." );
    AssertFatal( tickContact.mpSceneObjectA == this || tickContact.mpSceneObjectB == this, "SceneObject::onEndCollision() - Contact does not involve this scene object." );

    // Remove contact.
    for( Scene::typeContactVector::iterator contactItr = mpCurrentContacts->begin(); contactItr != mpCurrentContacts->end(); ++contactItr )
    {
        // Is this is the correct contact?
        if ( contactItr->mpContact == tickContact.mpContact )
        {
            // Yes, so remove it.
            mpCurrentContacts->erase_fast( contactItr );
            return;
        }
    }
}

//-----------------------------------------------------------------------------

bool SceneObject::moveTo( const Vector2& targetWorldPoint, const F32 speed, const bool autoStop, const bool warpToTarget )
{
    // Check in a scene.
    if ( !getScene() )
    {
        Con::warnf("SceneObject::moveTo() - Cannot move object (%d) to a point as it is not in a scene.", getId() );
        return false;
    }

    // Check not a static body.
    if ( getBodyType() == b2_staticBody )
    {
        Con::warnf("SceneObject::moveTo() - Cannot move object (%d) to a point as it is a static body.", getId() );
        return false;
    }

    // Check speed.
    if ( speed <= 0.0f )
    {
        Con::warnf("SceneObject::moveTo() - Speed '%f' is invalid.", speed );
        return false;
    }

    // Cancel any previous event.
    if ( mMoveToEventId != 0 )
    {
        Sim::cancelEvent( mMoveToEventId );
        mMoveToEventId = 0;
    }

    // Calculate the linear velocity for the specified speed.
    Vector2 linearVelocity = targetWorldPoint - getPosition();
    const F32 distance = linearVelocity.Normalize( speed );

    // Calculate the time it will take to reach the target.
    const U32 time = (U32)((distance / speed) * 1000.0f);

    // Set the linear velocity.
    setLinearVelocity( linearVelocity );

    // Create and post event.
    SceneObjectMoveToEvent* pEvent = new SceneObjectMoveToEvent( targetWorldPoint, autoStop, warpToTarget );
    mMoveToEventId = Sim::postEvent(this, pEvent, Sim::getCurrentTime() + time );

    return true;
}

//-----------------------------------------------------------------------------

bool SceneObject::rotateTo( const F32 targetAngle, const F32 speed, const bool autoStop, const bool warpToTarget )
{
    // Check in a scene.
    if ( !getScene() )
    {
        Con::warnf("SceneObject::rotateTo() - Cannot rotate object (%d) to an angle as it is not in a scene.", getId() );
        return false;
    }

    // Check not a static body.
    if ( getBodyType() == b2_staticBody )
    {
        Con::warnf("SceneObject::rotateTo() - Cannot move object (%d) to an angle as it is a static body.", getId() );
        return false;
    }

    // Check speed.
    if ( speed <= 0.0f )
    {
        Con::warnf("SceneObject::rotateTo() - Speed '%f' is invalid.", speed );
        return false;
    }

    // Cancel any previous event.
    if ( mRotateToEventId != 0 )
    {
        Sim::cancelEvent( mRotateToEventId );
        mRotateToEventId = 0;
    }

    // Calculate relative angle.
    const F32 relativeAngle = targetAngle - getAngle();

    // Calculate delta angle.
    const F32 deltaAngle = mAtan( mCos( relativeAngle ), mSin( relativeAngle ) );

    // Set angular velocity.
    setAngularVelocity( deltaAngle > 0.0f ? speed : -speed );

    // Calculate the time it will take to reach the angle.
    const U32 time = (U32)(mFabs(deltaAngle / speed) * 1000.0f);

    // Create and post event.
    SceneObjectRotateToEvent* pEvent = new SceneObjectRotateToEvent( targetAngle, autoStop, warpToTarget );
    mRotateToEventId = Sim::postEvent(this, pEvent, Sim::getCurrentTime() + time );

    return true;
}

//-----------------------------------------------------------------------------

void SceneObject::cancelMoveTo( const bool autoStop )
{
    // Only cancel an active moveTo event
    if ( mMoveToEventId != 0 )
    {
        Sim::cancelEvent( mMoveToEventId );
        mMoveToEventId = 0;

        // Should we auto stop?
        if ( autoStop )
        {
            // Yes, so remove linear velocity
            setLinearVelocity( Vector2::getZero() );
        }
    }
}

//-----------------------------------------------------------------------------

void SceneObject::cancelRotateTo( const bool autoStop )
{
    // Only cancel an active rotateTo event
    if ( mRotateToEventId != 0 )
    {
        Sim::cancelEvent( mRotateToEventId );
        mRotateToEventId = 0;

        // Should we auto stop?
        if ( autoStop )
        {
            // Yes, so remove angular velocity
            setAngularVelocity( 0.0f );
        }
    }
}

//-----------------------------------------------------------------------------

b2Shape::Type SceneObject::getCollisionShapeType( U32 shapeIndex ) const
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::getCollisionShapeType() - Invalid shape index." );

    if ( mpScene )
    {
        return mCollisionFixtures[shapeIndex]->GetType();
    }

    return mCollisionFixtureDefs[shapeIndex]->shape->GetType();
}

//-----------------------------------------------------------------------------

S32 SceneObject::getCollisionShapeIndex( const b2Fixture* pFixture ) const
{
    // Iterate collision shapes.
    S32 collisionShapeIndex = 0;
    for( typeCollisionFixtureVector::const_iterator collisionShapeItr = mCollisionFixtures.begin(); collisionShapeItr != mCollisionFixtures.end(); ++collisionShapeItr, ++collisionShapeIndex )
    {
        // Return index if this is the collision shape we are searching for.
        if ( pFixture == *collisionShapeItr )
            return collisionShapeIndex;
    }

    // Not found.
    return -1;
}

//-----------------------------------------------------------------------------

void SceneObject::setCollisionShapeDefinition( const U32 shapeIndex, const b2FixtureDef& fixtureDef )
{
    // We only set specific features of a fixture definition here.
    setCollisionShapeDensity( shapeIndex, fixtureDef.density );
    setCollisionShapeFriction( shapeIndex, fixtureDef.friction );
    setCollisionShapeRestitution( shapeIndex, fixtureDef.restitution );
    setCollisionShapeIsSensor( shapeIndex, fixtureDef.isSensor );
}

//-----------------------------------------------------------------------------

b2FixtureDef SceneObject::getCollisionShapeDefinition( const U32 shapeIndex ) const
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::getCollisionShapeDefinition() - Invalid shape index." );

    if ( mpScene )
    {       
        // Fetch fixture.
        const b2Fixture* pFixture = mCollisionFixtures[shapeIndex];

        b2FixtureDef fixtureDef;
        fixtureDef.density     = pFixture->GetDensity();
        fixtureDef.friction    = pFixture->GetFriction();
        fixtureDef.restitution = pFixture->GetRestitution();
        fixtureDef.isSensor    = pFixture->IsSensor();
        fixtureDef.filter      = pFixture->GetFilterData();
        fixtureDef.shape       = pFixture->GetShape();

        return fixtureDef;
    }

    return *mCollisionFixtureDefs[shapeIndex];
}

//-----------------------------------------------------------------------------

const b2CircleShape* SceneObject::getCollisionCircleShape( const U32 shapeIndex ) const
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::getCollisionCircleShape() - Invalid shape index." );

    // Fetch shape definition.
    const b2FixtureDef fixtureDef = getCollisionShapeDefinition( shapeIndex );

    // Sanity!
    AssertFatal( fixtureDef.shape->GetType() == b2Shape::e_circle, "SceneObject::getCollisionCircleShape() - Shape is not a circle shape." );

    // Fetch circle shape.
    const b2CircleShape* pShape = dynamic_cast<const b2CircleShape*>( fixtureDef.shape );

    // Sanity!
    AssertFatal( pShape != NULL, "SceneObject::getCollisionCircleShape() - Invalid circle shape." );

    return pShape;
}

//-----------------------------------------------------------------------------

const b2PolygonShape* SceneObject::getCollisionPolygonShape( const U32 shapeIndex ) const
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::getCollisionPolygonShape() - Invalid shape index." );

    // Fetch shape definition.
    const b2FixtureDef fixtureDef = getCollisionShapeDefinition( shapeIndex );

    // Sanity!
    AssertFatal( fixtureDef.shape->GetType() == b2Shape::e_polygon, "SceneObject::getCollisionPolygonShape() - Shape is not a polygon shape." );

    // Fetch shape.
    const b2PolygonShape* pShape = dynamic_cast<const b2PolygonShape*>( fixtureDef.shape );

    // Sanity!
    AssertFatal( pShape != NULL, "SceneObject::getCollisionPolygonShape() - Invalid polygon shape." );

    return pShape;
}

//-----------------------------------------------------------------------------

const b2ChainShape* SceneObject::getCollisionChainShape( const U32 shapeIndex ) const
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::getCollisionChainShape() - Invalid shape index." );

    // Fetch shape definition.
    const b2FixtureDef fixtureDef = getCollisionShapeDefinition( shapeIndex );

    // Sanity!
    AssertFatal( fixtureDef.shape->GetType() == b2Shape::e_chain, "SceneObject::getCollisionChainShape() - Shape is not a chain shape." );

    // Fetch shape.
    const b2ChainShape* pShape = dynamic_cast<const b2ChainShape*>( fixtureDef.shape );

    // Sanity!
    AssertFatal( pShape != NULL, "SceneObject::getCollisionChainShape() - Invalid chain shape." );

    return pShape;
}

//-----------------------------------------------------------------------------

const b2EdgeShape* SceneObject::getCollisionEdgeShape( const U32 shapeIndex ) const
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::getCollisionEdgeShape() - Invalid shape index." );

    // Fetch shape definition.
    const b2FixtureDef fixtureDef = getCollisionShapeDefinition( shapeIndex );

    // Sanity!
    AssertFatal( fixtureDef.shape->GetType() == b2Shape::e_edge, "SceneObject::getCollisionEdgeShape() - Shape is not a edge shape." );

    // Fetch shape.
    const b2EdgeShape* pShape = dynamic_cast<const b2EdgeShape*>( fixtureDef.shape );

    // Sanity!
    AssertFatal( pShape != NULL, "SceneObject::getCollisionEdgeShape() - Invalid edge shape." );

    return pShape;
}

//-----------------------------------------------------------------------------

void SceneObject::setCollisionShapeDensity( const U32 shapeIndex, const F32 density )
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::setCollisionShapeDensity() - Invalid shape index." );

    if ( mpScene )
    {
        // Update live fixture.
        mCollisionFixtures[shapeIndex]->SetDensity( density );

        // Update the body mass data.
        mpBody->ResetMassData();

        return;
    }

    // Update offline fixture definition.
    mCollisionFixtureDefs[shapeIndex]->density = density;
}

//-----------------------------------------------------------------------------

F32 SceneObject::getCollisionShapeDensity( const U32 shapeIndex ) const
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::getCollisionShapeDensity() - Invalid shape index." );

    if ( mpScene )
    {
        // Use live fixture.
        return mCollisionFixtures[shapeIndex]->GetDensity();
    }

    // Use offline fixture definition.
    return mCollisionFixtureDefs[shapeIndex]->density;
}

//-----------------------------------------------------------------------------

void SceneObject::setCollisionShapeFriction( const U32 shapeIndex, const F32 friction )
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::setCollisionShapeFriction() - Invalid shape index." );

    if ( mpScene )
    {
        // Fetch the fixture.
        b2Fixture* pFixture = mCollisionFixtures[shapeIndex];

        // Update live fixture.
        pFixture->SetFriction( friction );

        // Re-filter fixture.
        pFixture->Refilter();

        return;
    }

    // Update offline fixture definition.
    mCollisionFixtureDefs[shapeIndex]->friction = friction;
}

//-----------------------------------------------------------------------------

F32 SceneObject::getCollisionShapeFriction( const U32 shapeIndex ) const
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::getCollisionShapeFriction() - Invalid shape index." );

    if ( mpScene )
    {
        // Use live fixture.
        return mCollisionFixtures[shapeIndex]->GetFriction();
    }

    // Use offline fixture definition.
    return mCollisionFixtureDefs[shapeIndex]->friction;
}

//-----------------------------------------------------------------------------

void SceneObject::setCollisionShapeRestitution( const U32 shapeIndex, const F32 restitution )
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::setCollisionShapeRestitution() - Invalid shape index." );

    if ( mpScene )
    {
        // Fetch the fixture.
        b2Fixture* pFixture = mCollisionFixtures[shapeIndex];

        // Update live fixture.
        pFixture->SetRestitution( restitution );

        // Re-filter fixture.
        pFixture->Refilter();

        return;
    }

    // Update offline fixture definition.
    mCollisionFixtureDefs[shapeIndex]->restitution = restitution;
}

//-----------------------------------------------------------------------------

F32 SceneObject::getCollisionShapeRestitution( const U32 shapeIndex ) const
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::getCollisionShapeRestitution() - Invalid shape index." );

    if ( mpScene )
    {
        // Use live fixture.
        return mCollisionFixtures[shapeIndex]->GetRestitution();
    }

    // Use offline fixture definition.
    return mCollisionFixtureDefs[shapeIndex]->restitution;
}

//-----------------------------------------------------------------------------

void SceneObject::setCollisionShapeIsSensor( const U32 shapeIndex, const bool isSensor )
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::setCollisionShapeIsSensor() - Invalid shape index." );

    if ( mpScene )
    {
        // Fetch the fixture.
        b2Fixture* pFixture = mCollisionFixtures[shapeIndex];

        // Update live fixture.
        pFixture->SetSensor( isSensor );

        // Re-filter fixture.
        pFixture->Refilter();

        return;
    }

    // Update offline fixture definition.
    mCollisionFixtureDefs[shapeIndex]->isSensor = isSensor;
}

//-----------------------------------------------------------------------------

bool SceneObject::getCollisionShapeIsSensor( const U32 shapeIndex ) const
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::getCollisionShapeIsSensor() - Invalid shape index." );

    if ( mpScene )
    {
        // Use live fixture.
        return mCollisionFixtures[shapeIndex]->IsSensor();
    }

    // Use offline fixture definition.
    return mCollisionFixtureDefs[shapeIndex]->isSensor;
}

//-----------------------------------------------------------------------------

void SceneObject::deleteCollisionShape( const U32 shapeIndex )
{
    // Sanity!
    AssertFatal( shapeIndex < getCollisionShapeCount(), "SceneObject::deleteCollisionShape() - Invalid shape index." );

    if ( mpScene )
    {
        mpBody->DestroyFixture( mCollisionFixtures[ shapeIndex ] );
        mCollisionFixtures.erase_fast( shapeIndex );
        return;
    }

    mCollisionFixtureDefs.erase_fast( shapeIndex );
}

//-----------------------------------------------------------------------------

void SceneObject::clearCollisionShapes( void )
{
    // Delete all collision shapes.
    while ( getCollisionShapeCount() > 0 )
    {
        deleteCollisionShape( 0 );
    }
}

//-----------------------------------------------------------------------------

S32 SceneObject::createCircleCollisionShape( const F32 radius )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_CreateCircleCollisionShape);

    return createCircleCollisionShape( radius, b2Vec2(0.0f, 0.0f) );
}

//-----------------------------------------------------------------------------

S32 SceneObject::createCircleCollisionShape( const F32 radius, const b2Vec2& localPosition )
{
    // Sanity!
    if ( radius <= 0.0f )
    {
        Con::errorf("SceneObject::createCircleCollisionShape() - Invalid radius of %g.", radius);
        return -1;
    }

    // Configure fixture definition.
    b2FixtureDef* pFixtureDef = new b2FixtureDef( mDefaultFixture );
    b2CircleShape* pShape = new b2CircleShape();
    pShape->m_p = localPosition;
    pShape->m_radius = radius;
    pFixtureDef->shape = pShape;

    if ( mpScene )
    {
        // Create and push fixture.
        mCollisionFixtures.push_back( mpBody->CreateFixture( pFixtureDef ) );

        // Destroy shape and fixture.
        delete pShape;
        delete pFixtureDef;

        return mCollisionFixtures.size()-1;
    }

    // Push fixture definition.
    mCollisionFixtureDefs.push_back( pFixtureDef );

    return mCollisionFixtureDefs.size()-1;
}

//-----------------------------------------------------------------------------

F32 SceneObject::getCircleCollisionShapeRadius( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2CircleShape* pShape = getCollisionCircleShape( shapeIndex );

    return pShape->m_radius;
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getCircleCollisionShapeLocalPosition( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2CircleShape* pShape = getCollisionCircleShape( shapeIndex );

    return pShape->m_p;
}

//-----------------------------------------------------------------------------

S32 SceneObject::createPolygonCollisionShape( const U32 pointCount, const b2Vec2* localPoints )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_CreatePolygonCollisionShape);

    // Sanity!
    if ( pointCount < 3 || pointCount > b2_maxPolygonVertices )
    {
        Con::errorf("SceneObject::createPolygonCollisionShape() - Invalid point count of %d.", pointCount);
        return -1;
    }

    // Configure fixture definition.
    b2FixtureDef* pFixtureDef = new b2FixtureDef( mDefaultFixture );
    b2PolygonShape* pShape    = new b2PolygonShape();
    pShape->Set( localPoints, pointCount );
    pFixtureDef->shape = pShape;

    if ( mpScene )
    {
        // Create and push fixture.
        mCollisionFixtures.push_back( mpBody->CreateFixture( pFixtureDef ) );

        // Destroy shape and fixture.
        delete pShape;
        delete pFixtureDef;

        return mCollisionFixtures.size()-1;
    }

    // Push fixture definition.
    mCollisionFixtureDefs.push_back( pFixtureDef );

    return mCollisionFixtureDefs.size()-1;
}

//-----------------------------------------------------------------------------

S32 SceneObject::createPolygonBoxCollisionShape( const F32 width, const F32 height )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_CreatePolygonBoxCollisionShapeWidthHeight);

    // Sanity!
    if ( width <= 0.0f )
    {
        Con::errorf("SceneObject::createPolygonBoxCollisionShape() - Invalid width of %g.", width);
        return -1;
    }
    if ( height <= 0.0f )
    {
        Con::errorf("SceneObject::createPolygonBoxCollisionShape() - Invalid height of %g.", height);
        return -1;
    }

    // Configure fixture definition.
    b2FixtureDef* pFixtureDef = new b2FixtureDef( mDefaultFixture );
    b2PolygonShape* pShape    = new b2PolygonShape();
    pShape->SetAsBox( width * 0.5f, height * 0.5f );
    pFixtureDef->shape = pShape;

    if ( mpScene )
    {
        // Create and push fixture.
        mCollisionFixtures.push_back( mpBody->CreateFixture( pFixtureDef ) );

        // Destroy shape and fixture.
        delete pShape;
        delete pFixtureDef;

        return mCollisionFixtures.size()-1;
    }

    // Push fixture definition.
    mCollisionFixtureDefs.push_back( pFixtureDef );

    return mCollisionFixtureDefs.size()-1;
}

//-----------------------------------------------------------------------------

S32 SceneObject::createPolygonBoxCollisionShape( const F32 width, const F32 height, const b2Vec2& localCentroid )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_CreatePolygonBoxCollisionShapeWidthHeightCentroid);

    // Sanity!
    if ( width <= 0.0f )
    {
        Con::errorf("SceneObject::createPolygonBoxCollisionShape() - Invalid width of %g.", width);
        return -1;
    }
    if ( height <= 0.0f )
    {
        Con::errorf("SceneObject::createPolygonBoxCollisionShape() - Invalid height of %g.", height);
        return -1;
    }

    // Configure fixture definition.
    b2FixtureDef* pFixtureDef = new b2FixtureDef( mDefaultFixture );
    b2PolygonShape* pShape    = new b2PolygonShape();
    pShape->SetAsBox( width * 0.5f, height * 0.5f, localCentroid, 0.0f );
    pFixtureDef->shape = pShape;

    if ( mpScene )
    {
        // Create and push fixture.
        mCollisionFixtures.push_back( mpBody->CreateFixture( pFixtureDef ) );

        // Destroy shape and fixture.
        delete pShape;
        delete pFixtureDef;

        return mCollisionFixtures.size()-1;
    }

    // Push fixture definition.
    mCollisionFixtureDefs.push_back( pFixtureDef );

    return mCollisionFixtureDefs.size()-1;
}

//-----------------------------------------------------------------------------

S32 SceneObject::createPolygonBoxCollisionShape( const F32 width, const F32 height, const b2Vec2& localCentroid, const F32 rotation )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_CreatePolygonBoxCollisionShapeWidthHeightCentroidRotation);

    // Sanity!
    if ( width <= 0.0f )
    {
        Con::errorf("SceneObject::createPolygonBoxCollisionShape() - Invalid width of %g.", width);
        return -1;
    }
    if ( height <= 0.0f )
    {
        Con::errorf("SceneObject::createPolygonBoxCollisionShape() - Invalid height of %g.", height);
        return -1;
    }

    // Configure fixture definition.
    b2FixtureDef* pFixtureDef = new b2FixtureDef( mDefaultFixture );
    b2PolygonShape* pShape    = new b2PolygonShape();
    pShape->SetAsBox( width * 0.5f, height * 0.5f, localCentroid, rotation );
    pFixtureDef->shape = pShape;

    if ( mpScene )
    {
        // Create and push fixture.
        mCollisionFixtures.push_back( mpBody->CreateFixture( pFixtureDef ) );

        // Destroy shape and fixture.
        delete pShape;
        delete pFixtureDef;

        return mCollisionFixtures.size()-1;
    }

    // Push fixture definition.
    mCollisionFixtureDefs.push_back( pFixtureDef );

    return mCollisionFixtureDefs.size()-1;
}

//-----------------------------------------------------------------------------

U32 SceneObject::getPolygonCollisionShapePointCount( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2PolygonShape* pShape = getCollisionPolygonShape( shapeIndex );

    return pShape->GetVertexCount();
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getPolygonCollisionShapeLocalPoint( const U32 shapeIndex, const U32 pointIndex ) const
{
    // Fetch shape.
    const b2PolygonShape* pShape = getCollisionPolygonShape( shapeIndex );

    // Sanity!
    AssertFatal( pointIndex < (U32)pShape->GetVertexCount(), "SceneObject::getPolygonCollisionShapeLocalPoint() - Invalid local point index." );

    return pShape->GetVertex( pointIndex );
}

//-----------------------------------------------------------------------------

S32 SceneObject::createChainCollisionShape( const U32 pointCount, const b2Vec2* localPoints )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_CreateChainCollisionShape);

    // Sanity!
    if ( pointCount < 2 )
    {
        Con::errorf("SceneObject::createChainCollisionShape() - Invalid point count of %d.", pointCount);
        return -1;
    }

    // Configure fixture definition.
    b2FixtureDef* pFixtureDef = new b2FixtureDef( mDefaultFixture );
    b2ChainShape* pShape      = new b2ChainShape();
    pShape->CreateChain( localPoints, pointCount );
    pFixtureDef->shape = pShape;

    if ( mpScene )
    {
        // Create and push fixture.
        mCollisionFixtures.push_back( mpBody->CreateFixture( pFixtureDef ) );

        // Destroy shape and fixture.
        delete pShape;
        delete pFixtureDef;

        return mCollisionFixtures.size()-1;
    }

    // Push fixture definition.
    mCollisionFixtureDefs.push_back( pFixtureDef );

    return mCollisionFixtureDefs.size()-1;
}

//-----------------------------------------------------------------------------

S32 SceneObject::createChainCollisionShape(  const U32 pointCount, const b2Vec2* localPoints,
                                                const bool hasAdjacentLocalPositionStart, const bool hasAdjacentLocalPositionEnd,
                                                const b2Vec2& adjacentLocalPositionStart, const b2Vec2& adjacentLocalPositionEnd )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_CreateChainCollisionShapeAdjacents);

    // Sanity!
    if ( pointCount < 2 )
    {
        Con::errorf("SceneObject::createChainCollisionShape() - Invalid point count of %d.", pointCount);
        return -1;
    }

    // Configure fixture definition.
    b2FixtureDef* pFixtureDef = new b2FixtureDef( mDefaultFixture );
    b2ChainShape* pShape      = new b2ChainShape();
    pShape->CreateChain( localPoints, pointCount );
    
    if ( hasAdjacentLocalPositionStart )
        pShape->SetPrevVertex( adjacentLocalPositionStart );

    if ( hasAdjacentLocalPositionEnd )
        pShape->SetNextVertex( adjacentLocalPositionEnd );

    pFixtureDef->shape = pShape;

    if ( mpScene )
    {
        // Create and push fixture.
        mCollisionFixtures.push_back( mpBody->CreateFixture( pFixtureDef ) );

        // Destroy shape and fixture.
        delete pShape;
        delete pFixtureDef;

        return mCollisionFixtures.size()-1;
    }

    // Push fixture definition.
    mCollisionFixtureDefs.push_back( pFixtureDef );

    return mCollisionFixtureDefs.size()-1;
}

//-----------------------------------------------------------------------------

U32 SceneObject::getChainCollisionShapePointCount( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2ChainShape* pShape = getCollisionChainShape( shapeIndex );

    return pShape->m_count;
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getChainCollisionShapeLocalPoint( const U32 shapeIndex, const U32 pointIndex ) const
{
    // Fetch shape.
    const b2ChainShape* pShape = getCollisionChainShape( shapeIndex );

    // Sanity!
    AssertFatal( pointIndex < (U32)pShape->m_count, "SceneObject::getChainCollisionShapeLocalPoint() - Invalid local point index." );

    return pShape->m_vertices[ pointIndex ];
}

//-----------------------------------------------------------------------------

bool SceneObject::getChainCollisionShapeHasAdjacentStart( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2ChainShape* pShape = getCollisionChainShape( shapeIndex );

    return pShape->m_hasPrevVertex;
}

//-----------------------------------------------------------------------------

bool SceneObject::getChainCollisionShapeHasAdjacentEnd( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2ChainShape* pShape = getCollisionChainShape( shapeIndex );

    return pShape->m_hasNextVertex;
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getChainCollisionShapeAdjacentStart( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2ChainShape* pShape = getCollisionChainShape( shapeIndex );

    return pShape->m_prevVertex;
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getChainCollisionShapeAdjacentEnd( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2ChainShape* pShape = getCollisionChainShape( shapeIndex );

    return pShape->m_nextVertex;
}

//-----------------------------------------------------------------------------

S32 SceneObject::createEdgeCollisionShape( const b2Vec2& localPositionStart, const b2Vec2& localPositionEnd )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_CreateEdgeCollisionShape);

    // Configure fixture definition.
    b2FixtureDef* pFixtureDef = new b2FixtureDef( mDefaultFixture );
    b2EdgeShape* pShape       = new b2EdgeShape();
    pShape->Set( localPositionStart, localPositionEnd );
    pFixtureDef->shape = pShape;

    if ( mpScene )
    {
        // Create and push fixture.
        mCollisionFixtures.push_back( mpBody->CreateFixture( pFixtureDef ) );

        // Destroy shape and fixture.
        delete pShape;
        delete pFixtureDef;

        return mCollisionFixtures.size()-1;
    }

    // Push fixture definition.
    mCollisionFixtureDefs.push_back( pFixtureDef );

    return mCollisionFixtureDefs.size()-1;
}

//-----------------------------------------------------------------------------

S32 SceneObject::createEdgeCollisionShape(   const b2Vec2& localPositionStart, const b2Vec2& localPositionEnd,
                                                const bool hasAdjacentLocalPositionStart, const bool hasAdjacentLocalPositionEnd,
                                                const b2Vec2& adjacentLocalPositionStart, const b2Vec2& adjacentLocalPositionEnd )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_CreateEdgeCollisionShapeAdjacents);

    // Configure fixture definition.
    b2FixtureDef* pFixtureDef = new b2FixtureDef( mDefaultFixture );
    b2EdgeShape* pShape       = new b2EdgeShape();
    pShape->Set( localPositionStart, localPositionEnd );
    pShape->m_hasVertex0      = hasAdjacentLocalPositionStart;
    pShape->m_hasVertex3      = hasAdjacentLocalPositionEnd;
    pShape->m_vertex0         = adjacentLocalPositionStart;
    pShape->m_vertex3         = adjacentLocalPositionEnd;
    pFixtureDef->shape        = pShape;

    if ( mpScene )
    {
        // Create and push fixture.
        mCollisionFixtures.push_back( mpBody->CreateFixture( pFixtureDef ) );

        // Destroy shape and fixture.
        delete pShape;
        delete pFixtureDef;

        return mCollisionFixtures.size()-1;
    }

    // Push fixture definition.
    mCollisionFixtureDefs.push_back( pFixtureDef );

    return mCollisionFixtureDefs.size()-1;
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getEdgeCollisionShapeLocalPositionStart( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2EdgeShape* pShape = getCollisionEdgeShape( shapeIndex );

    return pShape->m_vertex1;
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getEdgeCollisionShapeLocalPositionEnd( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2EdgeShape* pShape = getCollisionEdgeShape( shapeIndex );

    return pShape->m_vertex2;
}

//-----------------------------------------------------------------------------

bool SceneObject::getEdgeCollisionShapeHasAdjacentStart( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2EdgeShape* pShape = getCollisionEdgeShape( shapeIndex );

    return pShape->m_hasVertex0;
}

//-----------------------------------------------------------------------------

bool SceneObject::getEdgeCollisionShapeHasAdjacentEnd( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2EdgeShape* pShape = getCollisionEdgeShape( shapeIndex );

    return pShape->m_hasVertex3;
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getEdgeCollisionShapeAdjacentStart( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2EdgeShape* pShape = getCollisionEdgeShape( shapeIndex );

    return pShape->m_vertex0;
}

//-----------------------------------------------------------------------------

Vector2 SceneObject::getEdgeCollisionShapeAdjacentEnd( const U32 shapeIndex ) const
{
    // Fetch shape.
    const b2EdgeShape* pShape = getCollisionEdgeShape( shapeIndex );

    return pShape->m_vertex3;
}

//-----------------------------------------------------------------------------

void SceneObject::setBlendOptions( void )
{
    // Set Blend Status.
    if ( mBlendMode )
    {
        // Enable Blending.
        glEnable( GL_BLEND );
        // Set Blend Function.
        glBlendFunc( mSrcBlendFactor, mDstBlendFactor );

        // Set color.
        glColor4f(mBlendColor.red,mBlendColor.green,mBlendColor.blue,mBlendColor.alpha );
    }
    else
    {
        // Disable Blending.
        glDisable( GL_BLEND );
        // Reset color.
        glColor4f(1,1,1,1);
    }

    // Set Alpha Test.
    if ( mAlphaTest >= 0.0f )
    {
        // Enable Test.
        glEnable( GL_ALPHA_TEST );
        glAlphaFunc( GL_GREATER, mAlphaTest );
    }
    else
    {
        // Disable Test.
        glDisable( GL_ALPHA_TEST );
    }
}

//-----------------------------------------------------------------------------

void SceneObject::resetBlendOptions( void )
{
    // Disable Blending.
    glDisable( GL_BLEND );

    glDisable( GL_ALPHA_TEST);

    // Reset color.
    glColor4f(1,1,1,1);
}

//---------------------------------------------------------------------------------------------

void SceneObject::onInputEvent( StringTableEntry name, const GuiEvent& event, const Vector2& worldMousePosition )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_OnInputEvent);

    // Argument Buffers.
    char argBuffer[3][32];

    // ID
    dSprintf(argBuffer[0], 32, "%d", event.eventID);
    
    // Format Mouse-Position Buffer.
    dSprintf(argBuffer[1], 32, "%g %g", worldMousePosition.x, worldMousePosition.y);

    // Optional double click
    dSprintf(argBuffer[2], 32, "%d", event.mouseClickCount);

    // Call Scripts.
    Con::executef(this, 4, name, argBuffer[0], argBuffer[1], argBuffer[2]);
}

//-----------------------------------------------------------------------------

void SceneObject::attachGui( GuiControl* pGuiControl, SceneWindow* pSceneWindow, const bool sizeControl )
{
    // Attach Gui Control.
    mpAttachedGui = pGuiControl;

    // Attach SceneWindow.
    mpAttachedGuiSceneWindow = pSceneWindow;

    // Set Size Gui Flag.
    mAttachedGuiSizeControl = sizeControl;

    // Register Gui Control/Window References.
    mpAttachedGui->registerReference( (SimObject**)&mpAttachedGui );
    mpAttachedGuiSceneWindow->registerReference( (SimObject**)&mpAttachedGuiSceneWindow );

    // Check/Adjust Parentage.
    if ( mpAttachedGui->getParent() != mpAttachedGuiSceneWindow )
    {
        // Warn.
        // Remove GuiControl from existing parent (if it has one).
        if ( mpAttachedGui->getParent() )
        {
            mpAttachedGui->getParent()->removeObject( mpAttachedGui );
        }

        // Add it to the scene-window.
        mpAttachedGuiSceneWindow->addObject( mpAttachedGui );
    }
    
}

//-----------------------------------------------------------------------------

void SceneObject::detachGui( void )
{
    // Unregister Gui Control Reference.
    if ( mpAttachedGui )
    {
       // [neo, 5/7/2007 - #2997]
       // Changed to UNregisterReference was registerReference which would crash later
       mpAttachedGui->unregisterReference( (SimObject**)&mpAttachedGui );
        mpAttachedGui = NULL;
    }

    // Unregister Gui Control Reference.
    if ( mpAttachedGuiSceneWindow )
    {
        mpAttachedGuiSceneWindow->registerReference( (SimObject**)&mpAttachedGuiSceneWindow );
        mpAttachedGuiSceneWindow = NULL;
    }
}

//-----------------------------------------------------------------------------

void SceneObject::updateAttachedGui( void )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_updateAttachedGui);

    // Finish if either Gui Control or Window is invalid.
    if ( !mpAttachedGui || !mpAttachedGuiSceneWindow )
        return;

    // Ignore if we're not in the scene that the scene-window is attached to.
    if ( getScene() != mpAttachedGuiSceneWindow->getScene() )
    {
        // Warn.
        Con::warnf("SceneObject::updateAttachedGui() - SceneWindow is not attached to my Scene!");
        // Detach from GUI Control.
        detachGui();
        // Finish Here.
        return;
    }

    // Calculate the GUI Controls' dimensions.
    Point2I topLeftI, extentI;

    // Size Control?
    if ( mAttachedGuiSizeControl )
    {
        // Yes, so fetch Clip Rectangle; this forms the area we want to fix the Gui-Control to.
        const RectF objAABB = getAABBRectangle();
        // Fetch Top-Left.
        Vector2 upperLeft = Vector2( objAABB.point.x, objAABB.point.y + objAABB.extent.y );
        Vector2 lowerRight = Vector2( objAABB.point.x + objAABB.extent.x, objAABB.point.y );

        // Convert Scene to Window Coordinates.
        mpAttachedGuiSceneWindow->sceneToWindowPoint( upperLeft, upperLeft );
        mpAttachedGuiSceneWindow->sceneToWindowPoint( lowerRight, lowerRight );
        // Convert Control Dimensions.
        topLeftI.set( S32(upperLeft.x), S32(upperLeft.y) );
        extentI.set( S32(lowerRight.x-upperLeft.x), S32(lowerRight.y-upperLeft.y) );
    }
    else
    {
        // No, so center GUI-Control on objects position but don't resize it.

        // Calculate Position from World Clip.
        const RectF clipRectangle = getAABBRectangle();
        // Calculate center position.
        const Vector2 centerPosition = clipRectangle.point + Vector2(clipRectangle.len_x()*0.5f, clipRectangle.len_y()*0.5f);

        // Convert Scene to Window Coordinates.
        Vector2 positionI;
        mpAttachedGuiSceneWindow->sceneToWindowPoint( centerPosition, positionI );
        // Fetch Control Extents (which don't change here).
        extentI = mpAttachedGui->getExtent();
        // Calculate new top-left.
        topLeftI.set( S32(positionI.x-extentI.x/2), S32(positionI.y-extentI.y/2) );
    }

    // Set Control Dimensions.
    mpAttachedGui->resize( topLeftI, extentI );
}

//-----------------------------------------------------------------------------

void SceneObject::copyFrom( SceneObject* pSceneObject, const bool copyDynamicFields )
{
    // Copy the specified object.
    pSceneObject->copyTo( this );

    // Copy over dynamic fields if requested.
    if ( copyDynamicFields )
        pSceneObject->assignDynamicFieldsFrom( this );
}

//-----------------------------------------------------------------------------

void SceneObject::copyTo( SimObject* obj )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_CopyTo);

    // Call parent.
    Parent::copyTo(obj);

    // Fetch object.
    SceneObject* pSceneObject = dynamic_cast<SceneObject*>(obj);

    // Sanity!
    AssertFatal(pSceneObject != NULL, "SceneObject::copyTo() - Object is not the correct type.");

    /// Lifetime.
    pSceneObject->setLifetime( getLifetime() );

    /// Scene Layers.
    pSceneObject->setSceneLayer( getSceneLayer() );

    /// Scene groups.
    pSceneObject->setSceneGroup( getSceneGroup() );

    /// Area.
    pSceneObject->setSize( getSize() );

    /// Position / Angle.
    pSceneObject->setPosition( getPosition() );
    pSceneObject->setAngle( getAngle() );
    pSceneObject->setFixedAngle( getFixedAngle() );

    /// Body.
    pSceneObject->setBodyType( getBodyType() );
    pSceneObject->setActive( getActive() );
    pSceneObject->setAwake( getAwake() );
    pSceneObject->setBullet( getBullet() );
    pSceneObject->setSleepingAllowed( getSleepingAllowed() );

    /// Collision control.
    pSceneObject->setCollisionGroupMask( getCollisionGroupMask() );
    pSceneObject->setCollisionLayerMask( getCollisionLayerMask() );
    pSceneObject->setCollisionSuppress( getCollisionSuppress() );
    pSceneObject->setGatherContacts( getGatherContacts() );
    pSceneObject->setDefaultDensity( getDefaultDensity() );
    pSceneObject->setDefaultFriction( getDefaultFriction() );
    pSceneObject->setDefaultRestitution( getDefaultRestitution() );

    /// Velocities.
    pSceneObject->setLinearVelocity( getLinearVelocity() );
    pSceneObject->setAngularVelocity( getAngularVelocity() );
    pSceneObject->setLinearDamping( getLinearDamping() );
    pSceneObject->setAngularDamping( getAngularDamping() );

    /// Gravity scaling.
    pSceneObject->setGravityScale( getGravityScale() );

    /// Collision shapes.
    copyCollisionShapes( pSceneObject, true );

    /// Render visibility.
    pSceneObject->setVisible( getVisible() );

    /// Render blending.
    pSceneObject->setBlendMode( getBlendMode() );
    pSceneObject->setSrcBlendFactor( getSrcBlendFactor() );
    pSceneObject->setDstBlendFactor( getDstBlendFactor() );
    pSceneObject->setBlendColor( getBlendColor() );
    pSceneObject->setAlphaTest( getAlphaTest() );

    /// Render sorting.
    pSceneObject->setSortPoint( getSortPoint() );

    /// Input events.
    pSceneObject->setUseInputEvents( getUseInputEvents() );

    // Script callbacks.
    pSceneObject->setUpdateCallback( getUpdateCallback() );   
    pSceneObject->setCollisionCallback( getCollisionCallback() );
    pSceneObject->setSleepingCallback( getSleepingCallback() );

    /// Misc.
    pSceneObject->setBatchIsolated( getBatchIsolated() );
   
    /// Debug mode.
    setDebugOn( getDebugMask() );
}

//-----------------------------------------------------------------------------

S32 SceneObject::copyCollisionShapes( SceneObject* pSceneObject, const bool clearTargetShapes, const S32 shapeIndex )
{
    // Sanity!
    AssertFatal( pSceneObject != NULL, "SceneObject::copyCollisionShapes() - Cannot copy to a NULL scene object." );

    // Clear the collision shapes.
    if ( clearTargetShapes )
        pSceneObject->clearCollisionShapes();

    // Fetch collision shape count.
    const U32 collisionShapeCount = getCollisionShapeCount();

    // If a shape index is specified, is it valid?
    if ( shapeIndex != INVALID_COLLISION_SHAPE_INDEX && shapeIndex >= (S32)collisionShapeCount )
    {
        // No, so warn.
        Con::warnf( "SceneObject::copyCollisionShapes() - Invalid shape index '%d'.", shapeIndex );
        return INVALID_COLLISION_SHAPE_INDEX;
    }

    // Finish if there are no collision shapes.
    if ( collisionShapeCount == 0 )
        return INVALID_COLLISION_SHAPE_INDEX;

    // Calculate shape range.
    const U32 startShapeIndex = shapeIndex >= 0 ? shapeIndex : 0;
    const U32 endShapeIndex = shapeIndex >= 0 ? shapeIndex : collisionShapeCount -1;

    // Iterate collision shapes.
    for ( U32 index = startShapeIndex; index <= endShapeIndex; ++index )
    {
        b2FixtureDef fixtureDef;

        if ( mpScene )
        {
            // Fetch fixture.
            b2Fixture* pFixture = mCollisionFixtures[index];

            // Fetch common details.
            fixtureDef.density     = pFixture->GetDensity();
            fixtureDef.friction    = pFixture->GetFriction();
            fixtureDef.restitution = pFixture->GetRestitution();
            fixtureDef.isSensor    = pFixture->IsSensor();
            fixtureDef.shape       = pFixture->GetShape();
        }
        else
        {
            // Fetch fixture def.
            b2FixtureDef* pFixtureDef = mCollisionFixtureDefs[index];

            // Fetch common details.
            fixtureDef = *pFixtureDef;
        }

        S32 newShapeIndex;

        // Fetch shape type.
        const b2Shape::Type shapeType = fixtureDef.shape->GetType();

        // Copy appropriate shape type.
        switch( shapeType )
        {
            case b2Shape::e_circle:
                newShapeIndex = copyCircleCollisionShapeTo( pSceneObject, fixtureDef );

                // Return the new shape if we're copying a specific index.
                if ( shapeIndex >= 0 )
                    return newShapeIndex;

                continue;

            case b2Shape::e_polygon:
                newShapeIndex = copyPolygonCollisionShapeTo( pSceneObject, fixtureDef );

                // Return the new shape if we're copying a specific index.
                if ( shapeIndex >= 0 )
                    return newShapeIndex;

                continue;

            case b2Shape::e_chain:
                newShapeIndex = copyChainCollisionShapeTo( pSceneObject, fixtureDef );

                // Return the new shape if we're copying a specific index.
                if ( shapeIndex >= 0 )
                    return newShapeIndex;

                continue;

            case b2Shape::e_edge:
                newShapeIndex = copyEdgeCollisionShapeTo( pSceneObject, fixtureDef );

                // Return the new shape if we're copying a specific index.
                if ( shapeIndex >= 0 )
                    return newShapeIndex;

                continue;

            default:
                AssertFatal( false, "SceneObject::copyCollisionShapes() - Unsupported collision shape type encountered." );
        }
    }

    // Return the first index if we're copying all the shapes.
    if ( shapeIndex < 0 )
        return 0;

    return INVALID_COLLISION_SHAPE_INDEX;
}

//-----------------------------------------------------------------------------

S32 SceneObject::copyCircleCollisionShapeTo( SceneObject* pSceneObject, const b2FixtureDef& fixtureDef ) const
{
    // Fetch shape.
    const b2CircleShape* pShape = dynamic_cast<const b2CircleShape*>( fixtureDef.shape );

    // Check shape.
    if ( !pShape )
    {
        Con::errorf("SceneObject::copyCircleCollisionShapeTo() - Invalid shape.");
        return INVALID_COLLISION_SHAPE_INDEX;
    }

    // Fetch shape details.
    const F32 radius           = pShape->m_radius;
    const b2Vec2 localPosition = pShape->m_p;

    // Copy shape.
    const S32 shapeIndex = pSceneObject->createCircleCollisionShape( radius, localPosition );

    // Was shape created.
    if ( shapeIndex != -1 )
    {
        // Yes, so configure shape.
        pSceneObject->setCollisionShapeDefinition( shapeIndex, fixtureDef );
    }

    return shapeIndex;
}

//-----------------------------------------------------------------------------

S32 SceneObject::copyPolygonCollisionShapeTo( SceneObject* pSceneObject, const b2FixtureDef& fixtureDef ) const
{
    // Fetch shape.
    const b2PolygonShape* pShape = dynamic_cast<const b2PolygonShape*>( fixtureDef.shape );

    // Check shape.
    if ( !pShape )
    {
        Con::errorf("SceneObject::copyPolygonCollisionShapeTo() - Invalid shape.");
        return INVALID_COLLISION_SHAPE_INDEX;
    }

    // Fetch point count.
    const U32 pointCount = pShape->GetVertexCount();

    // Fetch local points.
    const b2Vec2* plocalPoints = pShape->m_vertices;

    // Copy shape.
    const S32 shapeIndex = pSceneObject->createPolygonCollisionShape( pointCount, plocalPoints );

    // Was shape created.
    if ( shapeIndex != -1 )
    {
        // Yes, so configure shape.
        pSceneObject->setCollisionShapeDefinition( shapeIndex, fixtureDef );
    }

    return shapeIndex;
}

//-----------------------------------------------------------------------------

S32 SceneObject::copyChainCollisionShapeTo( SceneObject* pSceneObject, const b2FixtureDef& fixtureDef ) const
{
    // Fetch shape.
    const b2ChainShape* pShape = dynamic_cast<const b2ChainShape*>( fixtureDef.shape );

    // Check shape.
    if ( !pShape )
    {
        Con::errorf("SceneObject::copyChainCollisionShapeTo() - Invalid shape.");
        return INVALID_COLLISION_SHAPE_INDEX;
    }

    // Fetch point count.
    const U32 pointCount = pShape->m_count;

    // Fetch local points.
    b2Vec2* localPoints = pShape->m_vertices;

    // Fetch adjacent positions.
    const bool hasAdjacentLocalPositionStart = pShape->m_hasPrevVertex;
    const bool hasAdjacentLocalPositionEnd   = pShape->m_hasNextVertex;
    const b2Vec2 adjacentLocalPositionStart  = pShape->m_prevVertex;
    const b2Vec2 adjacentLocalPositionEnd    = pShape->m_nextVertex;

    // Create shape.
    const S32 shapeIndex = pSceneObject->createChainCollisionShape(
        pointCount, localPoints,
        hasAdjacentLocalPositionStart, hasAdjacentLocalPositionEnd,
        adjacentLocalPositionStart, adjacentLocalPositionEnd);

    // Was shape created.
    if ( shapeIndex != -1 )
    {
        // Yes, so configure shape.
        pSceneObject->setCollisionShapeDefinition( shapeIndex, fixtureDef );
    }

    return shapeIndex;
}

//-----------------------------------------------------------------------------

S32 SceneObject::copyEdgeCollisionShapeTo( SceneObject* pSceneObject, const b2FixtureDef& fixtureDef ) const
{
    // Fetch shape.
    const b2EdgeShape* pShape = dynamic_cast<const b2EdgeShape*>( fixtureDef.shape );

    // Check shape.
    if ( !pShape )
    {
        Con::errorf("SceneObject::copyEdgeCollisionShapeTo() - Invalid shape.");
        return INVALID_COLLISION_SHAPE_INDEX;
    }

    // Fetch positions.
    const b2Vec2 localPosition1          = pShape->m_vertex1;
    const b2Vec2 localPosition2          = pShape->m_vertex2;
    const bool hasAdjacentLocalPosition1 = pShape->m_hasVertex0;
    const bool hasAdjacentLocalPosition2 = pShape->m_hasVertex3;
    const b2Vec2 adjacentLocalPosition1  = pShape->m_vertex0;
    const b2Vec2 adjacentLocalPosition2  = pShape->m_vertex3;

    // Create shape.
    const S32 shapeIndex = pSceneObject->createEdgeCollisionShape(
        localPosition1, localPosition2,
        hasAdjacentLocalPosition1, hasAdjacentLocalPosition2,
        adjacentLocalPosition1, adjacentLocalPosition2 );

    // Was shape created.
    if ( shapeIndex != -1 )
    {
        // Yes, so configure shape.
        pSceneObject->setCollisionShapeDefinition( shapeIndex, fixtureDef );
    }

    return shapeIndex;
}

//-----------------------------------------------------------------------------

void SceneObject::safeDelete( void )
{
    // Are we in a scene?
    if ( getScene() )
    {
        // Yes, so add a delete-request to the scene.
        getScene()->addDeleteRequest( this );
    }
    else
    {
        // No, so use standard SimObject helper.
        deleteObject();
    }
}

//-----------------------------------------------------------------------------

void SceneObject::addDestroyNotification( SceneObject* pSceneObject )
{
    // Search list to see if we're already in it (finish if we are).
    for ( U32 n = 0; n < (U32)mDestroyNotifyList.size(); n++ )
    {
        // In the list already?
        if ( mDestroyNotifyList[n].mpSceneObject == pSceneObject )
        {
            // Yes, so just bump-up the reference count.
            mDestroyNotifyList[n].mRefCount++;

            // Finish here.
            return;
        }
    }

    // Add Destroy Notification.
    tDestroyNotification notification;
    notification.mpSceneObject = pSceneObject;
    notification.mRefCount = 1;

    // Add Notification.
    mDestroyNotifyList.push_back( notification );
}

//-----------------------------------------------------------------------------

void SceneObject::removeDestroyNotification( SceneObject* pSceneObject )
{
    // Find object in notification list.
    for ( U32 n = 0; n < (U32)mDestroyNotifyList.size(); n++ )
    {
        // Our object?
        if ( mDestroyNotifyList[n].mpSceneObject == pSceneObject )
        {
            // Yes, so reduce reference count.
            mDestroyNotifyList[n].mRefCount--;
            // Finish Here.
            return;
        }
    }
}

//-----------------------------------------------------------------------------

void SceneObject::processDestroyNotifications( void )
{
    // Find object in notification list.
    while( mDestroyNotifyList.size() )
    {
        // Fetch Notification Item.
        tDestroyNotification notification = mDestroyNotifyList.first();
        // Only action if we've got a reference active.
        if ( notification.mRefCount > 0 )
            // Call Destroy Notification.
            notification.mpSceneObject->onDestroyNotify( this );

        // Remove it.
        mDestroyNotifyList.pop_front();
    }

    // Sanity!
    AssertFatal( mDestroyNotifyList.size() == 0, "SceneObject::processDestroyNotifications() - Notifications still pending!" );
}

//-----------------------------------------------------------------------------

void SceneObject::notifyComponentsAddToScene( void )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_NotifyComponentsAddToScene);

    // Notify components.
    VectorPtr<SimComponent*>& componentList = lockComponentList();
    for( SimComponentIterator itr = componentList.begin(); itr != componentList.end(); ++itr )
    {
        SimComponent *pComponent = *itr;
        if( pComponent != NULL )
            pComponent->onAddToScene();
    }
    unlockComponentList();
}

//-----------------------------------------------------------------------------

void SceneObject::notifyComponentsRemoveFromScene( void )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_NotifyComponentsRemoveFromScene);

    // Notify components.
    VectorPtr<SimComponent*>& componentList = lockComponentList();
    for( SimComponentIterator itr = componentList.begin(); itr != componentList.end(); ++itr )
    {
        SimComponent *pComponent = *itr;
        if( pComponent != NULL )
            pComponent->onRemoveFromScene();
    }
    unlockComponentList();
}

//-----------------------------------------------------------------------------

void SceneObject::notifyComponentsUpdate( void )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_NotifyComponentsUpdate);

    // Notify components.
    VectorPtr<SimComponent*>& componentList = lockComponentList();
    for( SimComponentIterator itr = componentList.begin(); itr != componentList.end(); ++itr )
    {
        SimComponent *pComponent = *itr;
        if( pComponent != NULL )
            pComponent->onUpdate();
    }
    unlockComponentList();
}

//-----------------------------------------------------------------------------

U32 SceneObject::getGlobalSceneObjectCount( void )
{
    return sGlobalSceneObjectCount;
}

//-----------------------------------------------------------------------------

void SceneObject::onTamlCustomWrite( TamlCustomNodes& customNodes )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_OnTamlCustomWrite);

    // Call parent.
    Parent::onTamlCustomWrite( customNodes );

    // Fetch collision shape count.
    const U32 collisionShapeCount = getCollisionShapeCount();

    // Finish if no collision shapes.
    if ( collisionShapeCount == 0 )
        return;

    // Add collision shape node.
    TamlCustomNode* pCustomCollisionShapes = customNodes.addNode( shapeCustomNodeName );

    // Iterate collision shapes.
    for ( U32 shapeIndex = 0; shapeIndex < collisionShapeCount; ++shapeIndex )
    {
        // Fetch collision shape definition.
        b2FixtureDef fixtureDef = getCollisionShapeDefinition( shapeIndex );

        // Add collision shape node.
        // NOTE:    The name of the node will get updated shortly.
        TamlCustomNode* pCollisionShapeNode = pCustomCollisionShapes->addNode( StringTable->EmptyString );

        // Add common collision shape fields.
        if ( mNotEqual( getDefaultDensity(), fixtureDef.density ) )
            pCollisionShapeNode->addField( shapeDensityName, fixtureDef.density );

        if ( mNotEqual( getDefaultFriction(), fixtureDef.friction ) )
            pCollisionShapeNode->addField( shapeFrictionName, fixtureDef.friction );

        if ( mNotEqual( getDefaultRestitution(), fixtureDef.restitution ) )
            pCollisionShapeNode->addField( shapeRestitutionName, fixtureDef.restitution );

        if ( fixtureDef.isSensor == true )
            pCollisionShapeNode->addField( shapeSensorName, fixtureDef.isSensor );

        // Populate collision shape appropriately.
        switch( fixtureDef.shape->GetType() )
        {
        case b2Shape::e_circle:
            {
                // Set node name.
                pCollisionShapeNode->setNodeName( circleTypeName );

                // Fetch shape.
                const b2CircleShape* pShape = dynamic_cast<const b2CircleShape*>( fixtureDef.shape );

                // Sanity!
                AssertFatal( pShape != NULL, "SceneObject::onTamlCustomWrite() - Invalid circle shape type returned." );

                // Add radius property.
                pCollisionShapeNode->addField( circleRadiusName, pShape->m_radius );

                // Add offset property (if not zero).
                if ( !Vector2(pShape->m_p).isZero() )
                    pCollisionShapeNode->addField( circleOffsetName, pShape->m_p );
            }
            break;

        case b2Shape::e_polygon:
            {
                // Set node name.
                pCollisionShapeNode->setNodeName( polygonTypeName );

                // Fetch shape.
                const b2PolygonShape* pShape = dynamic_cast<const b2PolygonShape*>( fixtureDef.shape );

                // Sanity!
                AssertFatal( pShape != NULL, "SceneObject::onTamlCustomWrite() - Invalid polygon shape type returned." );

                // Fetch point count.
                const U32 pointCount = pShape->GetVertexCount();

                // Add shape properties.
                for ( U32 pointIndex = 0; pointIndex < pointCount; ++pointIndex )
                {
                    // Add point node.
                    TamlCustomNode* pPointNode = pCollisionShapeNode->addNode( shapePointName );

                    // Fetch point.
                    const b2Vec2& point = pShape->GetVertex( pointIndex );

                    // Add point fields.
                    pPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, point );
                }
            }
            break;

        case b2Shape::e_chain:
            {
                // Set node name.
                pCollisionShapeNode->setNodeName( chainTypeName );

                // Fetch shape.
                const b2ChainShape* pShape = dynamic_cast<const b2ChainShape*>( fixtureDef.shape );

                // Sanity!
                AssertFatal( pShape != NULL, "SceneObject::onTamlCustomWrite() - Invalid chain shape type returned." );

                // Fetch point count.
                const U32 pointCount = pShape->m_count;

                // Add shape properties.
                for ( U32 pointIndex = 0; pointIndex < pointCount; ++pointIndex )
                {
                    // Add point node.
                    TamlCustomNode* pPointNode = pCollisionShapeNode->addNode( shapePointName );

                    // Add point fields.
                    pPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertices[pointIndex] );
                }

                // Add adjacent start point (if specified).
                if ( pShape->m_hasPrevVertex )
                {
                    TamlCustomNode* pPrevPointNode = pCollisionShapeNode->addNode( shapePrevPointName );
                    pPrevPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_prevVertex );
                }

                // Add adjacent end point (if specified).
                if ( pShape->m_hasNextVertex )
                {
                    TamlCustomNode* pNextPointNode = pCollisionShapeNode->addNode( shapeNextPointName );
                    pNextPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_nextVertex );
                }
            }
            break;

        case b2Shape::e_edge:
            {
                // Set node name.
                pCollisionShapeNode->setNodeName( edgeTypeName );

                // Fetch shape.
                const b2EdgeShape* pShape = dynamic_cast<const b2EdgeShape*>( fixtureDef.shape );

                // Sanity!
                AssertFatal( pShape != NULL, "SceneObject::onTamlCustomWrite() - Invalid edge shape type returned." );

                // Add start point.
                TamlCustomNode* pStartPointNode = pCollisionShapeNode->addNode( shapePointName );
                pStartPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertex1 );

                // Add end point.
                TamlCustomNode* pEndPointNode = pCollisionShapeNode->addNode( shapePointName );
                pEndPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertex2 );

                // Add adjacent start point (if specified).
                if ( pShape->m_hasVertex0 )
                {
                    TamlCustomNode* pPrevPointNode = pCollisionShapeNode->addNode( shapePrevPointName );
                    pPrevPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertex0 );
                }

                // Add adjacent end point (if specified).
                if ( pShape->m_hasVertex3 )
                {
                    TamlCustomNode* pNextPointNode = pCollisionShapeNode->addNode( shapeNextPointName );
                    pNextPointNode->getNodeTextField().setFieldValue( StringTable->EmptyString, pShape->m_vertex3 );
                }
            }
            break;

        default:
            // Sanity!
            AssertFatal( false, "SceneObject::onTamlCustomWrite() - Unknown shape type detected." );
        }
    }
}

//-----------------------------------------------------------------------------

void SceneObject::onTamlCustomRead( const TamlCustomNodes& customNodes )
{
    // Debug Profiling.
    PROFILE_SCOPE(SceneObject_OnTamlCustomRead);

    // Call parent.
    Parent::onTamlCustomRead( customNodes );

    // Find collision shape custom node.
    const TamlCustomNode* pCustomCollisionShapes = customNodes.findNode( shapeCustomNodeName );

    // Finish if we don't have collision shapes.
    if ( pCustomCollisionShapes == NULL )
        return;

    // Fetch children shapes.
    const TamlCustomNodeVector& collisionShapeChildren = pCustomCollisionShapes->getChildren();

    // Iterate collision shapes.
    for( TamlCustomNodeVector::const_iterator shapeNodeItr = collisionShapeChildren.begin(); shapeNodeItr != collisionShapeChildren.end(); ++shapeNodeItr )
    {
        // Fetch shape node.
        TamlCustomNode* pShapeNode = *shapeNodeItr;

        // Fetch shape name.
        StringTableEntry shapeName = pShapeNode->getNodeName();

        // Ready common fields.
        F32 shapeDensity     = getDefaultDensity();
        F32 shapeFriction    = getDefaultFriction();
        F32 shapeRestitution = getDefaultRestitution();
        bool shapeSensor     = false;

        S32 shapeIndex;

        // Is this a circle shape?
        if ( shapeName == circleTypeName )
        {
            // Yes, so ready fields.
            F32 radius = 0.0f;
            b2Vec2 offset( 0.0f, 0.0f );

            // Fetch shape children.
            const TamlCustomFieldVector& shapeFields = pShapeNode->getFields();

            // Iterate property fields.
            for ( TamlCustomFieldVector::const_iterator shapeFieldItr = shapeFields.begin(); shapeFieldItr != shapeFields.end(); ++shapeFieldItr )
            {
                // Fetch field.
                const TamlCustomField* pField = *shapeFieldItr;

                // Fetch property field name.
                StringTableEntry fieldName = pField->getFieldName();

                // Check common fields.
                if ( fieldName == shapeDensityName )
                {
                    pField->getFieldValue( shapeDensity );
                }
                else if ( fieldName == shapeFrictionName )
                {
                    pField->getFieldValue( shapeFriction );
                }
                else if ( fieldName == shapeRestitutionName )
                {
                    pField->getFieldValue( shapeRestitution );
                }
                else if ( fieldName == shapeSensorName )
                {
                    pField->getFieldValue( shapeSensor );
                }
                // Check circle fields.
                else if ( fieldName == circleRadiusName )
                {
                    pField->getFieldValue( radius );
                }
                else if ( fieldName == circleOffsetName )
                {
                    pField->getFieldValue( offset );
                }                   
            }

            // Is radius valid?
            if ( radius <= 0.0f )
            {
                // No, so warn.
                Con::warnf( "SceneObject::onTamlCustomRead() - Invalid radius on circle collision shape '%g'.  Using default.", radius );

                // Set default.
                radius = 1.0f;
            }

            // Create shape.
            shapeIndex = createCircleCollisionShape( radius, offset );
        }
        // Is this a polygon shape?
        else if ( shapeName == polygonTypeName )
        {
            // Yes, so fetch shape fields.
            const TamlCustomFieldVector& shapeFields = pShapeNode->getFields();

            // Iterate property fields.
            for ( TamlCustomFieldVector::const_iterator shapeFieldItr = shapeFields.begin(); shapeFieldItr != shapeFields.end(); ++shapeFieldItr )
            {
                // Fetch field.
                const TamlCustomField* pField = *shapeFieldItr;

                // Fetch property field name.
                StringTableEntry fieldName = pField->getFieldName();

                // Check common fields.
                if ( fieldName == shapeDensityName )
                {
                    pField->getFieldValue( shapeDensity );
                }
                else if ( fieldName == shapeFrictionName )
                {
                    pField->getFieldValue( shapeFriction );
                }
                else if ( fieldName == shapeRestitutionName )
                {
                    pField->getFieldValue( shapeRestitution );
                }
                else if ( fieldName == shapeSensorName )
                {
                    pField->getFieldValue( shapeSensor );
                }
            }

            // Fetch shape children.
            const TamlCustomNodeVector& shapeChildren = pShapeNode->getChildren();

            // Fetch shape children count.
            const U32 shapeChildrenCount = (U32)shapeChildren.size();

            // Reset points.
            b2Vec2 points[b2_maxPolygonVertices];
            U32 pointCount = 0;

            // Do we have any shape children.
            if ( shapeChildrenCount > 0 )
            {
                // Yes, so iterate them.
                for( TamlCustomNodeVector::const_iterator childItr = shapeChildren.begin(); childItr != shapeChildren.end(); ++childItr )
                {
                    TamlCustomNode* pChildNode = *childItr;

                    // Skip if it's not a point.
                    if ( pChildNode->getNodeName() != shapePointName )
                        continue;
                    
                    // Skip if it's empty.
                    if ( pChildNode->getNodeTextField().isValueEmpty() )
                        continue;

                    // Read point.
                    b2Vec2 point;
                    pChildNode->getNodeTextField().getFieldValue( point );
                    points[pointCount++] = point;
                }
            }

            // Is point count valid?
            if ( pointCount == 0 )
            {
                // No, so warn.
                Con::warnf( "SceneObject::onTamlCustomRead() - No points on polygon collision shape." );

                continue;
            }

            // Create shape.
            shapeIndex = createPolygonCollisionShape( pointCount, points );
        }
        // Is this a chain shape?
        else if ( shapeName == chainTypeName )
        {
            // Yes, so ready fields.
            Vector<b2Vec2> points;
            bool hasAdjacentStartPoint = false;
            bool hasAdjacentEndPoint = false;
            b2Vec2 adjacentStartPoint;
            b2Vec2 adjacentEndPoint;

            // Fetch shape children.
            const TamlCustomFieldVector& shapeFields = pShapeNode->getFields();

            // Iterate property fields.
            for ( TamlCustomFieldVector::const_iterator shapeFieldItr = shapeFields.begin(); shapeFieldItr != shapeFields.end(); ++shapeFieldItr )
            {
                // Fetch field.
                const TamlCustomField* pField = *shapeFieldItr;

                // Fetch property field name.
                StringTableEntry fieldName = pField->getFieldName();

                // Check common fields.
                if ( fieldName == shapeDensityName )
                {
                    pField->getFieldValue( shapeDensity );
                }
                else if ( fieldName == shapeFrictionName )
                {
                    pField->getFieldValue( shapeFriction );
                }
                else if ( fieldName == shapeRestitutionName )
                {
                    pField->getFieldValue( shapeRestitution );
                }
                else if ( fieldName == shapeSensorName )
                {
                    pField->getFieldValue( shapeSensor );
                }
            }

            // Fetch shape children.
            const TamlCustomNodeVector& shapeChildren = pShapeNode->getChildren();

            // Fetch shape children count.
            const U32 shapeChildrenCount = (U32)shapeChildren.size();

            // Do we have any shape children.
            // NOTE: Only do this if the old methods has not been used.
            if ( points.size() == 0 && shapeChildrenCount > 0 )
            {
                // Yes, so iterate them.
                for( TamlCustomNodeVector::const_iterator childItr = shapeChildren.begin(); childItr != shapeChildren.end(); ++childItr )
                {
                    TamlCustomNode* pChildNode = *childItr;

                    // Fetch the node name.
                    StringTableEntry nodeName = pChildNode->getNodeName();

                    // Skip if it's not a point.
                    if ( !(nodeName == shapePointName || nodeName == shapePrevPointName || nodeName == shapeNextPointName) )
                        continue;
                    
                    // Skip if it's empty.
                    if ( pChildNode->getNodeTextField().isValueEmpty() )
                        continue;

                    if ( nodeName == shapePointName )
                    {
                        // Read point.
                        b2Vec2 point;
                        pChildNode->getNodeTextField().getFieldValue( point );
                        points.push_back( point );
                    }
                    else if ( nodeName == shapePrevPointName )
                    {
                        // Read adjacent point.
                        pChildNode->getNodeTextField().getFieldValue( adjacentStartPoint );
                        hasAdjacentStartPoint = true;
                    }
                    else if ( nodeName == shapeNextPointName )
                    {
                        // Read adjacent point.
                        pChildNode->getNodeTextField().getFieldValue( adjacentEndPoint );
                        hasAdjacentEndPoint = true;
                    }
                }
            }

            // Is point count valid?
            if ( points.size() == 0 || points.size() < 2 )
            {
                // No, so warn.
                Con::warnf( "SceneObject::onTamlCustomRead() - No points (or less than two) on chain collision shape." );

                continue;
            }

            // Create shape.
            shapeIndex = createChainCollisionShape( points.size(), points.address(), hasAdjacentStartPoint, hasAdjacentEndPoint, adjacentStartPoint, adjacentEndPoint );
        }
        // Is this an edge shape?
        else if ( shapeName == edgeTypeName )
        {
            // Yes, so ready fields.
            b2Vec2 point0;
            b2Vec2 point1;
            U32 pointCount = 0;
            bool hasAdjacentStartPoint = false;
            bool hasAdjacentEndPoint = false;
            b2Vec2 adjacentStartPoint;
            b2Vec2 adjacentEndPoint;

            // Fetch shape children.
            const TamlCustomFieldVector& shapeFields = pShapeNode->getFields();

            // Iterate property fields.
            for ( TamlCustomFieldVector::const_iterator shapeFieldItr = shapeFields.begin(); shapeFieldItr != shapeFields.end(); ++shapeFieldItr )
            {
                // Fetch field.
                const TamlCustomField* pField = *shapeFieldItr;

                // Fetch property field name.
                StringTableEntry fieldName = pField->getFieldName();

                // Check common fields.
                if ( fieldName == shapeDensityName )
                {
                    pField->getFieldValue( shapeDensity );
                }
                else if ( fieldName == shapeFrictionName )
                {
                    pField->getFieldValue( shapeFriction );
                }
                else if ( fieldName == shapeRestitutionName )
                {
                    pField->getFieldValue( shapeRestitution );
                }
                else if ( fieldName == shapeSensorName )
                {
                    pField->getFieldValue( shapeSensor );
                }
            }

            // Fetch shape children.
            const TamlCustomNodeVector& shapeChildren = pShapeNode->getChildren();

            // Fetch shape children count.
            const U32 shapeChildrenCount = (U32)shapeChildren.size();

            // Do we have any shape children.
            if ( shapeChildrenCount > 0 )
            {
                // Yes, so iterate them.
                for( TamlCustomNodeVector::const_iterator childItr = shapeChildren.begin(); childItr != shapeChildren.end(); ++childItr )
                {
                    TamlCustomNode* pChildNode = *childItr;

                    // Fetch the node name.
                    StringTableEntry nodeName = pChildNode->getNodeName();

                    // Skip if it's not a point.
                    if ( !(nodeName == shapePointName || nodeName == shapePrevPointName || nodeName == shapeNextPointName) )
                        continue;
                    
                    // Skip if it's empty.
                    if ( pChildNode->getNodeTextField().isValueEmpty() )
                        continue;

                    if ( nodeName == shapePointName )
                    {
                        // Ignore if too many points.
                        if ( pointCount >= 2 )
                            continue;

                        // Read point.               
                        if ( pointCount == 0 )
                            pChildNode->getNodeTextField().getFieldValue( point0 );
                        else
                            pChildNode->getNodeTextField().getFieldValue( point1 );

                        pointCount++;
                    }
                    else if ( nodeName == shapePrevPointName )
                    {
                        // Read adjacent point.
                        pChildNode->getNodeTextField().getFieldValue( adjacentStartPoint );
                        hasAdjacentStartPoint = true;
                    }
                    else if ( nodeName == shapeNextPointName )
                    {
                        // Read adjacent point.
                        pChildNode->getNodeTextField().getFieldValue( adjacentEndPoint );
                        hasAdjacentEndPoint = true;
                    }
                }
            }

            // Is point count valid?
            if ( pointCount == 0 || pointCount != 2 )
            {
                // No, so warn.
                Con::warnf( "SceneObject::onTamlCustomRead() - No points (or not two points) on edge collision shape." );

                continue;
            }

            // Create shape.
            shapeIndex = createEdgeCollisionShape( point0, point1, hasAdjacentStartPoint, hasAdjacentEndPoint, adjacentStartPoint, adjacentEndPoint );
        }
        // Unknown shape type!
        else
        {
            // Warn.
            Con::warnf( "Unknown shape type of '%s' encountered.", shapeName );

            // Sanity!
            AssertFatal( false, "SceneObject::onTamlCustomRead() - Unknown shape type detected." );

            continue;
        }

        // Set common properties.
        setCollisionShapeDensity( shapeIndex, shapeDensity );
        setCollisionShapeFriction( shapeIndex, shapeFriction );
        setCollisionShapeRestitution( shapeIndex, shapeRestitution );
        setCollisionShapeIsSensor( shapeIndex, shapeSensor );
    }        
}

//-----------------------------------------------------------------------------

bool SceneObject::writeField(StringTableEntry fieldname, const char* value)
{
   if (!Parent::writeField(fieldname, value))
      return false;

   // Never save the scene field.
   if (dStricmp(fieldname, "scene") == 0)
      return false;

   return true;
}

//------------------------------------------------------------------------------

S32 QSORT_CALLBACK SceneObject::sceneObjectLayerDepthSort(const void* a, const void* b)
{
    // Fetch scene objects.
    SceneObject* pSceneObjectA  = *((SceneObject**)a);
    SceneObject* pSceneObjectB  = *((SceneObject**)b);

    // Fetch layers.
    const U32 layerA = pSceneObjectA->getSceneLayer();
    const U32 layerB = pSceneObjectB->getSceneLayer();

    if ( layerA < layerB )
        return -1;

    if ( layerA > layerB )
        return 1;

    // Fetch layer depths.
    const F32 depthA = pSceneObjectA->getSceneLayerDepth();
    const F32 depthB = pSceneObjectB->getSceneLayerDepth();

    return depthA < depthB ? 1 : depthA > depthB ? -1 : pSceneObjectA->getSerialId() - pSceneObjectB->getSerialId();
}

//-----------------------------------------------------------------------------

static EnumTable::Enums bodyTypeLookup[] =
                {
                { b2_staticBody,    "Static"    },
                { b2_kinematicBody, "Kinematic" },
                { b2_dynamicBody,   "Dynamic"   },
                };

EnumTable bodyTypeTable(sizeof(bodyTypeLookup) / sizeof(EnumTable::Enums), &bodyTypeLookup[0]);

//-----------------------------------------------------------------------------

static EnumTable::Enums collisionShapeTypeLookup[] =
                {
                { b2Shape::e_circle,             "Circle"   },
                { b2Shape::e_edge,               "Edge"     },
                { b2Shape::e_polygon,            "Polygon"  },
                { b2Shape::e_chain,              "Chain"    },
                };

EnumTable collisionShapeTypeTable(sizeof(collisionShapeTypeLookup) / sizeof(EnumTable::Enums), &collisionShapeTypeLookup[0]);

//-----------------------------------------------------------------------------

static EnumTable::Enums srcBlendFactorLookup[] =
                {
                { GL_ZERO,                  "ZERO"                  },
                { GL_ONE,                   "ONE"                   },
                { GL_DST_COLOR,             "DST_COLOR"             },
                { GL_ONE_MINUS_DST_COLOR,   "ONE_MINUS_DST_COLOR"   },
                { GL_SRC_ALPHA,             "SRC_ALPHA"             },
                { GL_ONE_MINUS_SRC_ALPHA,   "ONE_MINUS_SRC_ALPHA"   },
                { GL_DST_ALPHA,             "DST_ALPHA"             },
                { GL_ONE_MINUS_DST_ALPHA,   "ONE_MINUS_DST_ALPHA"   },
                { GL_SRC_ALPHA_SATURATE,    "SRC_ALPHA_SATURATE"    },
                };

EnumTable srcBlendFactorTable(sizeof(srcBlendFactorLookup) / sizeof(EnumTable::Enums), &srcBlendFactorLookup[0]);

//-----------------------------------------------------------------------------

static EnumTable::Enums dstBlendFactorLookup[] =
                {
                { GL_ZERO,                  "ZERO" },
                { GL_ONE,                   "ONE" },
                { GL_SRC_COLOR,             "SRC_COLOR" },
                { GL_ONE_MINUS_SRC_COLOR,   "ONE_MINUS_SRC_COLOR" },
                { GL_SRC_ALPHA,             "SRC_ALPHA" },
                { GL_ONE_MINUS_SRC_ALPHA,   "ONE_MINUS_SRC_ALPHA" },
                { GL_DST_ALPHA,             "DST_ALPHA" },
                { GL_ONE_MINUS_DST_ALPHA,   "ONE_MINUS_DST_ALPHA" },
                };

EnumTable dstBlendFactorTable(sizeof(dstBlendFactorLookup) / sizeof(EnumTable::Enums), &dstBlendFactorLookup[0]);

//-----------------------------------------------------------------------------

b2BodyType SceneObject::getBodyTypeEnum(const char* label)
{
    // Search for Mnemonic.
    for (U32 i = 0; i < (sizeof(bodyTypeLookup) / sizeof(EnumTable::Enums)); i++)
    {
        if( dStricmp(bodyTypeLookup[i].label, label) == 0)
            return (b2BodyType)bodyTypeLookup[i].index;
    }

    // Warn.
    Con::warnf("SceneObject::getBodyTypeEnum() - Invalid body type of '%s'", label );

    return (b2BodyType)-1;
}

//-----------------------------------------------------------------------------

const char* SceneObject::getBodyTypeDescription(const b2BodyType bodyType)
{
    // Search for Mnemonic.
    for (U32 i = 0; i < (sizeof(bodyTypeLookup) / sizeof(EnumTable::Enums)); i++)
    {
        if( bodyTypeLookup[i].index == bodyType )
            return bodyTypeLookup[i].label;
    }

    // Warn.
    Con::warnf( "SceneObject::getBodyTypeDescription() - Invalid body type." );

    return StringTable->EmptyString;
}

//-----------------------------------------------------------------------------

b2Shape::Type SceneObject::getCollisionShapeTypeEnum(const char* label)
{
    // Search for Mnemonic.
    for (U32 i = 0; i < (sizeof(collisionShapeTypeLookup) / sizeof(EnumTable::Enums)); i++)
    {
        if( dStricmp(collisionShapeTypeLookup[i].label, label) == 0)
            return (b2Shape::Type)collisionShapeTypeLookup[i].index;
    }

    // Warn!
    Con::warnf("SceneObject::getCollisionShapeTypeEnum() - Invalid collision shape type of '%s'", label );

    return b2Shape::e_typeCount;
}

//-----------------------------------------------------------------------------

const char* SceneObject::getCollisionShapeTypeDescription(const b2Shape::Type collisionShapeType)
{
    // Search for Mnemonic.
    for (U32 i = 0; i < (sizeof(collisionShapeTypeLookup) / sizeof(EnumTable::Enums)); i++)
    {
        if( collisionShapeTypeLookup[i].index == collisionShapeType )
            return collisionShapeTypeLookup[i].label;
    }

    // Warn.
    Con::warnf( "SceneObject::getCollisionShapeTypeDescription() - Invalid collision shape type." );

    return StringTable->EmptyString;
}

//-----------------------------------------------------------------------------

S32 SceneObject::getSrcBlendFactorEnum(const char* label)
{
    // Search for Mnemonic.
    for (U32 i = 0; i < (sizeof(srcBlendFactorLookup) / sizeof(EnumTable::Enums)); i++)
    {
        if( dStricmp(srcBlendFactorLookup[i].label, label) == 0)
            return(srcBlendFactorLookup[i].index);
    }

    // Warn.
    Con::warnf("SceneObject::getSrcBlendFactorEnum() - Invalid source blend factor of '%s'", label );

    return GL_INVALID_BLEND_FACTOR;
}

//-----------------------------------------------------------------------------

const char* SceneObject::getSrcBlendFactorDescription(const GLenum factor)
{
    // Search for Mnemonic.
    for (U32 i = 0; i < (sizeof(srcBlendFactorLookup) / sizeof(EnumTable::Enums)); i++)
    {
        if( srcBlendFactorLookup[i].index == (S32)factor )
            return srcBlendFactorLookup[i].label;
    }

    // Warn.
    Con::warnf( "SceneObject::getSrcBlendFactorDescription() - Invalid source blend factor." );

    return StringTable->EmptyString;
}

//-----------------------------------------------------------------------------

S32 SceneObject::getDstBlendFactorEnum(const char* label)
{
    // Search for Mnemonic.
    for (U32 i = 0; i < (sizeof(dstBlendFactorLookup) / sizeof(EnumTable::Enums)); i++)
    {
        if( dStricmp(dstBlendFactorLookup[i].label, label) == 0)
            return(dstBlendFactorLookup[i].index);
    }

    // Warn.
    Con::warnf("SceneObject::getSrcBlendFactorEnum() - Invalid destination blend factor of '%s'", label );

    return GL_INVALID_BLEND_FACTOR;
}

//-----------------------------------------------------------------------------

const char* SceneObject::getDstBlendFactorDescription(const GLenum factor)
{
    // Search for Mnemonic.
    for(U32 i = 0; i < (sizeof(dstBlendFactorLookup) / sizeof(EnumTable::Enums)); i++)
    {
        if( dstBlendFactorLookup[i].index == (S32)factor )
            return dstBlendFactorLookup[i].label;
    }

    // Warn.
    Con::warnf( "SceneObject::getDstBlendFactorDescription() - Invalid destination blend factor." );

    return StringTable->EmptyString;
}

//-----------------------------------------------------------------------------

static void WriteCircleCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlElement* pParentElement )
{
    // Sanity!
    AssertFatal( pClassRep != NULL,  "SceneObject::WriteCircleCustomTamlSchema() - ClassRep cannot be NULL." );
    AssertFatal( pParentElement != NULL,  "SceneObject::WriteCircleCustomTamlSchema() - Parent Element cannot be NULL." );

    // Create circle element.
    TiXmlElement* pCircleElement = new TiXmlElement( "xs:element" );
    pCircleElement->SetAttribute( "name", circleTypeName );
    pCircleElement->SetAttribute( "minOccurs", 0 );
    pCircleElement->SetAttribute( "maxOccurs", 1 );
    pParentElement->LinkEndChild( pCircleElement );

    // Create complex type Element.
    TiXmlElement* pCircleComplexTypeElement = new TiXmlElement( "xs:complexType" );
    pCircleElement->LinkEndChild( pCircleComplexTypeElement );

    // Create "Radius" attribute.
    TiXmlElement* pCircleElementA = new TiXmlElement( "xs:attribute" );
    pCircleElementA->SetAttribute( "name", circleRadiusName );
    pCircleComplexTypeElement->LinkEndChild( pCircleElementA );
    TiXmlElement* pCircleElementB = new TiXmlElement( "xs:simpleType" );
    pCircleElementA->LinkEndChild( pCircleElementB );
    TiXmlElement* pCircleElementC = new TiXmlElement( "xs:restriction" );
    pCircleElementC->SetAttribute( "base", "xs:float" );
    pCircleElementB->LinkEndChild( pCircleElementC );
    TiXmlElement* pCircleElementD = new TiXmlElement( "xs:minExclusive" );
    pCircleElementD->SetAttribute( "value", "0" );
    pCircleElementC->LinkEndChild( pCircleElementD );

    // Create "Offset" attribute.
    pCircleElementA = new TiXmlElement( "xs:attribute" );
    pCircleElementA->SetAttribute( "name", circleOffsetName );
    pCircleElementA->SetAttribute( "type", "Vector2_ConsoleType" );
    pCircleComplexTypeElement->LinkEndChild( pCircleElementA );

    // Create "IsSensor" attribute.
    pCircleElementA = new TiXmlElement( "xs:attribute" );
    pCircleElementA->SetAttribute( "name", shapeSensorName );
    pCircleElementA->SetAttribute( "type", "xs:boolean" );
    pCircleComplexTypeElement->LinkEndChild( pCircleElementA );

    // Create "Density" attribute.
    pCircleElementA = new TiXmlElement( "xs:attribute" );
    pCircleElementA->SetAttribute( "name", shapeDensityName );
    pCircleComplexTypeElement->LinkEndChild( pCircleElementA );
    pCircleElementB = new TiXmlElement( "xs:simpleType" );
    pCircleElementA->LinkEndChild( pCircleElementB );
    pCircleElementC = new TiXmlElement( "xs:restriction" );
    pCircleElementC->SetAttribute( "base", "xs:float" );
    pCircleElementB->LinkEndChild( pCircleElementC );
    pCircleElementD = new TiXmlElement( "xs:minInclusive" );
    pCircleElementD->SetAttribute( "value", "0" );
    pCircleElementC->LinkEndChild( pCircleElementD );

    // Create "Friction" attribute.
    pCircleElementA = new TiXmlElement( "xs:attribute" );
    pCircleElementA->SetAttribute( "name", shapeFrictionName );
    pCircleComplexTypeElement->LinkEndChild( pCircleElementA );
    pCircleElementB = new TiXmlElement( "xs:simpleType" );
    pCircleElementA->LinkEndChild( pCircleElementB );
    pCircleElementC = new TiXmlElement( "xs:restriction" );
    pCircleElementC->SetAttribute( "base", "xs:float" );
    pCircleElementB->LinkEndChild( pCircleElementC );
    pCircleElementD = new TiXmlElement( "xs:minInclusive" );
    pCircleElementD->SetAttribute( "value", "0" );
    pCircleElementC->LinkEndChild( pCircleElementD );

    // Create "Restitution" attribute.
    pCircleElementA = new TiXmlElement( "xs:attribute" );
    pCircleElementA->SetAttribute( "name", shapeRestitutionName );
    pCircleComplexTypeElement->LinkEndChild( pCircleElementA );
    pCircleElementB = new TiXmlElement( "xs:simpleType" );
    pCircleElementA->LinkEndChild( pCircleElementB );
    pCircleElementC = new TiXmlElement( "xs:restriction" );
    pCircleElementC->SetAttribute( "base", "xs:float" );
    pCircleElementB->LinkEndChild( pCircleElementC );
    pCircleElementD = new TiXmlElement( "xs:minInclusive" );
    pCircleElementD->SetAttribute( "value", "0" );
    pCircleElementC->LinkEndChild( pCircleElementD );
}

//-----------------------------------------------------------------------------

static void WritePolygonCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlElement* pParentElement )
{
    // Sanity!
    AssertFatal( pClassRep != NULL,  "SceneObject::WritePolygonCustomTamlSchema() - ClassRep cannot be NULL." );
    AssertFatal( pParentElement != NULL,  "SceneObject::WritePolygonCustomTamlSchema() - Parent Element cannot be NULL." );

    // Create polygon element.
    TiXmlElement* pPolygonElement = new TiXmlElement( "xs:element" );
    pPolygonElement->SetAttribute( "name", polygonTypeName );
    pPolygonElement->SetAttribute( "minOccurs", 0 );
    pPolygonElement->SetAttribute( "maxOccurs", 1 );
    pParentElement->LinkEndChild( pPolygonElement );

    // Create complex type Element.
    TiXmlElement* pPolygonComplexTypeElement = new TiXmlElement( "xs:complexType" );
    pPolygonElement->LinkEndChild( pPolygonComplexTypeElement );

    // Create "polygon" child.
    TiXmlElement* pPolygonElementA = new TiXmlElement( "xs:choice" );
    pPolygonElementA->SetAttribute( "minOccurs", 0 );
    pPolygonElementA->SetAttribute( "maxOccurs", "unbounded" );
    pPolygonComplexTypeElement->LinkEndChild( pPolygonElementA );
    TiXmlElement* pPolygonElementB = new TiXmlElement( "xs:element" );
    pPolygonElementB->SetAttribute( "name", shapePointName );
    pPolygonElementB->SetAttribute( "type", "Vector2_ConsoleType" );
    pPolygonElementA->LinkEndChild( pPolygonElementB );

    // Create "IsSensor" attribute.
    pPolygonElementA = new TiXmlElement( "xs:attribute" );
    pPolygonElementA->SetAttribute( "name", shapeSensorName );
    pPolygonElementA->SetAttribute( "type", "xs:boolean" );
    pPolygonComplexTypeElement->LinkEndChild( pPolygonElementA );

    // Create "Density" attribute.
    pPolygonElementA = new TiXmlElement( "xs:attribute" );
    pPolygonElementA->SetAttribute( "name", shapeDensityName );
    pPolygonComplexTypeElement->LinkEndChild( pPolygonElementA );
    pPolygonElementB = new TiXmlElement( "xs:simpleType" );
    pPolygonElementA->LinkEndChild( pPolygonElementB );
    TiXmlElement* pPolygonElementC = new TiXmlElement( "xs:restriction" );
    pPolygonElementC->SetAttribute( "base", "xs:float" );
    pPolygonElementB->LinkEndChild( pPolygonElementC );
    TiXmlElement* pPolygonElementD = new TiXmlElement( "xs:minInclusive" );
    pPolygonElementD->SetAttribute( "value", "0" );
    pPolygonElementC->LinkEndChild( pPolygonElementD );

    // Create "Friction" attribute.
    pPolygonElementA = new TiXmlElement( "xs:attribute" );
    pPolygonElementA->SetAttribute( "name", shapeFrictionName );
    pPolygonComplexTypeElement->LinkEndChild( pPolygonElementA );
    pPolygonElementB = new TiXmlElement( "xs:simpleType" );
    pPolygonElementA->LinkEndChild( pPolygonElementB );
    pPolygonElementC = new TiXmlElement( "xs:restriction" );
    pPolygonElementC->SetAttribute( "base", "xs:float" );
    pPolygonElementB->LinkEndChild( pPolygonElementC );
    pPolygonElementD = new TiXmlElement( "xs:minInclusive" );
    pPolygonElementD->SetAttribute( "value", "0" );
    pPolygonElementC->LinkEndChild( pPolygonElementD );

    // Create "Restitution" attribute.
    pPolygonElementA = new TiXmlElement( "xs:attribute" );
    pPolygonElementA->SetAttribute( "name", shapeRestitutionName );
    pPolygonComplexTypeElement->LinkEndChild( pPolygonElementA );
    pPolygonElementB = new TiXmlElement( "xs:simpleType" );
    pPolygonElementA->LinkEndChild( pPolygonElementB );
    pPolygonElementC = new TiXmlElement( "xs:restriction" );
    pPolygonElementC->SetAttribute( "base", "xs:float" );
    pPolygonElementB->LinkEndChild( pPolygonElementC );
    pPolygonElementD = new TiXmlElement( "xs:minInclusive" );
    pPolygonElementD->SetAttribute( "value", "0" );
    pPolygonElementC->LinkEndChild( pPolygonElementD );
}

//-----------------------------------------------------------------------------

static void WriteChainCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlElement* pParentElement )
{
    // Sanity!
    AssertFatal( pClassRep != NULL,  "SceneObject::WriteChainCustomTamlSchema() - ClassRep cannot be NULL." );
    AssertFatal( pParentElement != NULL,  "SceneObject::WriteChainCustomTamlSchema() - Parent Element cannot be NULL." );

    // Create chain element.
    TiXmlElement* pChainElement = new TiXmlElement( "xs:element" );
    pChainElement->SetAttribute( "name", chainTypeName );
    pChainElement->SetAttribute( "minOccurs", 0 );
    pChainElement->SetAttribute( "maxOccurs", 1 );
    pParentElement->LinkEndChild( pChainElement );

    // Create complex type Element.
    TiXmlElement* pChainComplexTypeElement = new TiXmlElement( "xs:complexType" );
    pChainElement->LinkEndChild( pChainComplexTypeElement );

    // Create "Chain" child.
    TiXmlElement* pChainElementA = new TiXmlElement( "xs:sequence" );
    pChainComplexTypeElement->LinkEndChild( pChainElementA );
    TiXmlElement* pChainElementB = new TiXmlElement( "xs:choice" );
    pChainElementB->SetAttribute( "minOccurs", 0 );
    pChainElementB->SetAttribute( "maxOccurs", "unbounded" );
    pChainElementA->LinkEndChild( pChainElementB );
    TiXmlElement* pChainElementC = new TiXmlElement( "xs:element" );
    pChainElementC->SetAttribute( "name", shapePointName );
    pChainElementC->SetAttribute( "type", "Vector2_ConsoleType" );
    pChainElementB->LinkEndChild( pChainElementC );
    TiXmlElement* pChainElementD = new TiXmlElement( "xs:element" );
    pChainElementD->SetAttribute( "name", shapePrevPointName );
    pChainElementD->SetAttribute( "type", "Vector2_ConsoleType" );
    pChainElementD->SetAttribute( "minOccurs", 0 );
    pChainElementD->SetAttribute( "maxOccurs", 1 );
    pChainElementA->LinkEndChild( pChainElementD );
    TiXmlElement* pChainElementE = new TiXmlElement( "xs:element" );
    pChainElementE->SetAttribute( "name", shapeNextPointName );
    pChainElementE->SetAttribute( "type", "Vector2_ConsoleType" );
    pChainElementE->SetAttribute( "minOccurs", 0 );
    pChainElementE->SetAttribute( "maxOccurs", 1 );
    pChainElementA->LinkEndChild( pChainElementE );

    // Create "IsSensor" attribute.
    pChainElementA = new TiXmlElement( "xs:attribute" );
    pChainElementA->SetAttribute( "name", shapeSensorName );
    pChainElementA->SetAttribute( "type", "xs:boolean" );
    pChainComplexTypeElement->LinkEndChild( pChainElementA );

    // Create "Density" attribute.
    pChainElementA = new TiXmlElement( "xs:attribute" );
    pChainElementA->SetAttribute( "name", shapeDensityName );
    pChainComplexTypeElement->LinkEndChild( pChainElementA );
    pChainElementB = new TiXmlElement( "xs:simpleType" );
    pChainElementA->LinkEndChild( pChainElementB );
    pChainElementC = new TiXmlElement( "xs:restriction" );
    pChainElementC->SetAttribute( "base", "xs:float" );
    pChainElementB->LinkEndChild( pChainElementC );
    pChainElementD = new TiXmlElement( "xs:minInclusive" );
    pChainElementD->SetAttribute( "value", "0" );
    pChainElementC->LinkEndChild( pChainElementD );

    // Create "Friction" attribute.
    pChainElementA = new TiXmlElement( "xs:attribute" );
    pChainElementA->SetAttribute( "name", shapeFrictionName );
    pChainComplexTypeElement->LinkEndChild( pChainElementA );
    pChainElementB = new TiXmlElement( "xs:simpleType" );
    pChainElementA->LinkEndChild( pChainElementB );
    pChainElementC = new TiXmlElement( "xs:restriction" );
    pChainElementC->SetAttribute( "base", "xs:float" );
    pChainElementB->LinkEndChild( pChainElementC );
    pChainElementD = new TiXmlElement( "xs:minInclusive" );
    pChainElementD->SetAttribute( "value", "0" );
    pChainElementC->LinkEndChild( pChainElementD );

    // Create "Restitution" attribute.
    pChainElementA = new TiXmlElement( "xs:attribute" );
    pChainElementA->SetAttribute( "name", shapeRestitutionName );
    pChainComplexTypeElement->LinkEndChild( pChainElementA );
    pChainElementB = new TiXmlElement( "xs:simpleType" );
    pChainElementA->LinkEndChild( pChainElementB );
    pChainElementC = new TiXmlElement( "xs:restriction" );
    pChainElementC->SetAttribute( "base", "xs:float" );
    pChainElementB->LinkEndChild( pChainElementC );
    pChainElementD = new TiXmlElement( "xs:minInclusive" );
    pChainElementD->SetAttribute( "value", "0" );
    pChainElementC->LinkEndChild( pChainElementD );
}

//-----------------------------------------------------------------------------

static void WriteEdgeCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlElement* pParentElement )
{
    // Sanity!
    AssertFatal( pClassRep != NULL,  "SceneObject::WriteEdgeCustomTamlSchema() - ClassRep cannot be NULL." );
    AssertFatal( pParentElement != NULL,  "SceneObject::WriteCustomTamlSchema() - Parent Element cannot be NULL." );

    // Create edge element.
    TiXmlElement* pEdgeElement = new TiXmlElement( "xs:element" );
    pEdgeElement->SetAttribute( "name", edgeTypeName );
    pEdgeElement->SetAttribute( "minOccurs", 0 );
    pEdgeElement->SetAttribute( "maxOccurs", 1 );
    pParentElement->LinkEndChild( pEdgeElement );

    // Create complex type Element.
    TiXmlElement* pEdgeComplexTypeElement = new TiXmlElement( "xs:complexType" );
    pEdgeElement->LinkEndChild( pEdgeComplexTypeElement );

    // Create "Edge" child.
    TiXmlElement* pEdgeElementA = new TiXmlElement( "xs:sequence" );
    pEdgeComplexTypeElement->LinkEndChild( pEdgeElementA );
    TiXmlElement* pEdgeElementB = new TiXmlElement( "xs:element" );
    pEdgeElementB->SetAttribute( "name", shapePointName );
    pEdgeElementB->SetAttribute( "type", "Vector2_ConsoleType" );
    pEdgeElementB->SetAttribute( "minOccurs", 0 );
    pEdgeElementB->SetAttribute( "maxOccurs", 2 );
    pEdgeElementA->LinkEndChild( pEdgeElementB );
    TiXmlElement* pEdgeElementC = new TiXmlElement( "xs:element" );
    pEdgeElementC->SetAttribute( "name", shapePrevPointName );
    pEdgeElementC->SetAttribute( "type", "Vector2_ConsoleType" );
    pEdgeElementC->SetAttribute( "minOccurs", 0 );
    pEdgeElementC->SetAttribute( "maxOccurs", 1 );
    pEdgeElementA->LinkEndChild( pEdgeElementC );
    TiXmlElement* pEdgeElementD = new TiXmlElement( "xs:element" );
    pEdgeElementD->SetAttribute( "name", shapeNextPointName );
    pEdgeElementD->SetAttribute( "type", "Vector2_ConsoleType" );
    pEdgeElementD->SetAttribute( "minOccurs", 0 );
    pEdgeElementD->SetAttribute( "maxOccurs", 1 );
    pEdgeElementA->LinkEndChild( pEdgeElementD );

    // Create "IsSensor" attribute.
    pEdgeElementA = new TiXmlElement( "xs:attribute" );
    pEdgeElementA->SetAttribute( "name", shapeSensorName );
    pEdgeElementA->SetAttribute( "type", "xs:boolean" );
    pEdgeComplexTypeElement->LinkEndChild( pEdgeElementA );

    // Create "Density" attribute.
    pEdgeElementA = new TiXmlElement( "xs:attribute" );
    pEdgeElementA->SetAttribute( "name", shapeDensityName );
    pEdgeComplexTypeElement->LinkEndChild( pEdgeElementA );
    pEdgeElementB = new TiXmlElement( "xs:simpleType" );
    pEdgeElementA->LinkEndChild( pEdgeElementB );
    pEdgeElementC = new TiXmlElement( "xs:restriction" );
    pEdgeElementC->SetAttribute( "base", "xs:float" );
    pEdgeElementB->LinkEndChild( pEdgeElementC );
    pEdgeElementD = new TiXmlElement( "xs:minInclusive" );
    pEdgeElementD->SetAttribute( "value", "0" );
    pEdgeElementC->LinkEndChild( pEdgeElementD );

    // Create "Friction" attribute.
    pEdgeElementA = new TiXmlElement( "xs:attribute" );
    pEdgeElementA->SetAttribute( "name", shapeFrictionName );
    pEdgeComplexTypeElement->LinkEndChild( pEdgeElementA );
    pEdgeElementB = new TiXmlElement( "xs:simpleType" );
    pEdgeElementA->LinkEndChild( pEdgeElementB );
    pEdgeElementC = new TiXmlElement( "xs:restriction" );
    pEdgeElementC->SetAttribute( "base", "xs:float" );
    pEdgeElementB->LinkEndChild( pEdgeElementC );
    pEdgeElementD = new TiXmlElement( "xs:minInclusive" );
    pEdgeElementD->SetAttribute( "value", "0" );
    pEdgeElementC->LinkEndChild( pEdgeElementD );

    // Create "Restitution" attribute.
    pEdgeElementA = new TiXmlElement( "xs:attribute" );
    pEdgeElementA->SetAttribute( "name", shapeRestitutionName );
    pEdgeComplexTypeElement->LinkEndChild( pEdgeElementA );
    pEdgeElementB = new TiXmlElement( "xs:simpleType" );
    pEdgeElementA->LinkEndChild( pEdgeElementB );
    pEdgeElementC = new TiXmlElement( "xs:restriction" );
    pEdgeElementC->SetAttribute( "base", "xs:float" );
    pEdgeElementB->LinkEndChild( pEdgeElementC );
    pEdgeElementD = new TiXmlElement( "xs:minInclusive" );
    pEdgeElementD->SetAttribute( "value", "0" );
    pEdgeElementC->LinkEndChild( pEdgeElementD );
}

//-----------------------------------------------------------------------------

static void WriteCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlElement* pParentElement )
{
    // Sanity!
    AssertFatal( pClassRep != NULL,  "SceneObject::WriteCustomTamlSchema() - ClassRep cannot be NULL." );
    AssertFatal( pParentElement != NULL,  "SceneObject::WriteCustomTamlSchema() - Parent Element cannot be NULL." );

    char buffer[1024];

    // Create shapes node element.
    TiXmlElement* pShapesNodeElement = new TiXmlElement( "xs:element" );
    dSprintf( buffer, sizeof(buffer), "%s.%s", pClassRep->getClassName(), shapeCustomNodeName );
    pShapesNodeElement->SetAttribute( "name", buffer );
    pShapesNodeElement->SetAttribute( "minOccurs", 0 );
    pShapesNodeElement->SetAttribute( "maxOccurs", 1 );
    pParentElement->LinkEndChild( pShapesNodeElement );
    
    // Create complex type.
    TiXmlElement* pShapesNodeComplexTypeElement = new TiXmlElement( "xs:complexType" );
    pShapesNodeElement->LinkEndChild( pShapesNodeComplexTypeElement );
    
    // Create choice element.
    TiXmlElement* pShapesNodeChoiceElement = new TiXmlElement( "xs:choice" );
    pShapesNodeChoiceElement->SetAttribute( "minOccurs", 0 );
    pShapesNodeChoiceElement->SetAttribute( "maxOccurs", "unbounded" );
    pShapesNodeComplexTypeElement->LinkEndChild( pShapesNodeChoiceElement );

    // Write collision shapes.
    WriteCircleCustomTamlSchema( pClassRep, pShapesNodeChoiceElement );
    WritePolygonCustomTamlSchema( pClassRep, pShapesNodeChoiceElement );
    WriteChainCustomTamlSchema( pClassRep, pShapesNodeChoiceElement );
    WriteEdgeCustomTamlSchema( pClassRep, pShapesNodeChoiceElement );
}

//-----------------------------------------------------------------------------

IMPLEMENT_CONOBJECT_SCHEMA(SceneObject, WriteCustomTamlSchema);
