//--------------------------------------------------------------------------------------
// Copyright (C) 2008 http://shift.delblanco.es
//--------------------------------------------------------------------------------------
// SHIFT Videogame is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by the
// Free Software Foundation, either version 3 of the License, or later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
//--------------------------------------------------------------------------------------
// 3DObjVehicle.cpp
//--------------------------------------------------------------------------------------
#include "dxstdafx.h"
#include "3DObjVehicle.h"
#include "3DObjWeapon.h"
#include "Player.h"

#include "3DSoundMng.h"

//--------------------------------------------------------------------------------------
// Espacio de nombres
//--------------------------------------------------------------------------------------
using namespace std;

//--------------------------------------------------------------------------------------
// Variables Globales
//--------------------------------------------------------------------------------------
extern C3DSoundMng      *g_3DSoundMng;     // Instancia externa para manejar sonidos 3D (3DSoundMng.cpp)
extern bool				g_bInGame;         // Para saber si estamos durante el juego (Main.cpp)

//--------------------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------------------
C3DObjVehicle::C3DObjVehicle()
{
	m_pPlayer = NULL;

    m_awName = NULL;
    m_awDescription = NULL;
	m_bUpdate = false;

    m_fLastSpeed = 0.0f;
    m_fLastYRotation = 0.0f;
    m_bHandBrake = false;
    m_bAccelerate = false;
    m_fAcumTurn = 0.0f;

    m_iMaxCapacity = OBJVEHICLE_MAX_CAPACITY;
    m_iMaxHealth = OBJVEHICLE_MAX_HEALTH;
    m_fMaxSpeed = OBJVEHICLE_MAX_SPEED;
    m_fMaxAcceleration = OBJVEHICLE_MAX_ACCELERATION;
    m_fMaxTurnBodyZ = OBJVEHICLE_MAX_TURN_BODYZ;
    m_fMaxTurnBodyX = OBJVEHICLE_MAX_TURN_BODYX;
    m_fTurnBodyZ = OBJVEHICLE_TURN_BODYZ;
    m_fTurnBodyX = OBJVEHICLE_TURN_BODYX;
    m_fRecvBodyZ = OBJVEHICLE_RECOV_BODYZ;
    m_fRecvBodyX = OBJVEHICLE_RECOV_BODYX;
    m_fTurn = OBJVEHICLE_TURN;
    m_fMaxTurnWheelY = OBJVEHICLE_MAX_TURN_WHEELY;
    m_fTurnWheelZ = OBJVEHICLE_TURN_WHEELZ;
    m_fWheelDist = OBJVEHICLE_WHEEL_DISTANCE;
	m_fCameraY = OBJVEHICLE_CAMERAY;

	m_pPhyVehicle = NULL;
	m_pBody = NULL;
	m_pFRWheel = NULL;
	m_pFLWheel = NULL;
	m_pRRWheel = NULL;
	m_pRLWheel = NULL;

	m_pRocket = NULL;
	m_pMine = NULL;

	m_pSndSkid = NULL;
	m_pSndCrash = NULL;
}

//--------------------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------------------
C3DObjVehicle::~C3DObjVehicle()
{
    // Liberamos los elementos específicos del vehículo
    SAFE_DELETE_ARRAY(m_awName);
    SAFE_DELETE_ARRAY(m_awDescription);
    SAFE_DELETE(m_pPhyVehicle);
	m_pPhysics = NULL;

	// Las ruedas y el fuselaje se eliminarán en el destructor de la clase padre C3DObject

	// Eliminamos el cohete asociado del vehículo
    SAFE_DELETE(m_pRocket);
    SAFE_DELETE(m_pMine);

	// Liberamos sonidos
    if(m_pSndSkid)      g_3DSoundMng->Destroy(m_pSndSkid);
    if(m_pSndCrash)     g_3DSoundMng->Destroy(m_pSndCrash);
}

//--------------------------------------------------------------------------------------
// Inicializa el vehículo
//--------------------------------------------------------------------------------------
bool C3DObjVehicle::initialize(C3DObject *dParent, LPCWSTR ObjectPath, CPlayer *dPlayer)
{
	// Almacenamos el jugador asociado
	m_pPlayer = dPlayer;

	// Creamos la instancia física concreta del vehículo
	if(!m_pPhyVehicle) m_pPhyVehicle = new C3DPhyVehicle;
    if(!m_pPhyVehicle) return false;
	m_pPhysics = m_pPhyVehicle;
	m_pPhyVehicle->initialize(this);

    // Función de la clase padre
    if(!C3DObject::initialize(dParent, ObjectPath))
        return false;

    // Se usará una nueva instancia de CXMLIO
    CXMLIO *myXMLIO = new CXMLIO;
    if(!myXMLIO) return false;

    // Tomamos los elementos específicos del vehículo
    if(myXMLIO->LoadFile(ObjectPath))
    {
        WCHAR value[MAX_STRING+1];
        float v_float;

        // Nombre (atributo 'name')
        if(myXMLIO->GetAttribute(L"name", value)) m_awName = _wcsdup(value);

        // Descripción (atributo 'description')
        if(myXMLIO->GetAttribute(L"description", value)) m_awDescription = _wcsdup(value);

        // Sonidos adicionales del vehículo (nodo 'sound')
        if(myXMLIO->MoveSubElement(L"sound"))
        {
            // Derrape (atributo 'skid')
            if(myXMLIO->GetAttribute(L"skid", value))
                m_pSndSkid = g_3DSoundMng->Create(value, this);
            // Colisión (atributo 'crash')
            if(myXMLIO->GetAttribute(L"crash", value))
                m_pSndCrash = g_3DSoundMng->Create(value, this);

            myXMLIO->MoveParent();
        }

        // Características del vehículo (nodo 'features')
        if(myXMLIO->MoveSubElement(L"features"))
        {
            // Capacidad máxima (atributo 'capacity')
            if(myXMLIO->GetAttribute(L"capacity", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_iMaxCapacity = v_float;
            }
            // Vida máxima (atributo 'health')
            if(myXMLIO->GetAttribute(L"health", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_iMaxHealth = v_float;
            }
            // Velocidad máxima (atributo 'speed')
            if(myXMLIO->GetAttribute(L"speed", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fMaxSpeed = v_float;
            }
            // Aceleración máxima (atributo 'acceleration')
            if(myXMLIO->GetAttribute(L"acceleration", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fMaxAcceleration = v_float;
            }
            // Máxima rotación para la suspensión frontal (atributo 'maxbodyZ')
            if(myXMLIO->GetAttribute(L"maxbodyZ", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fMaxTurnBodyZ = v_float;
            }
            // Máxima rotación para la suspensión lateral (atributo 'maxbodyX')
            if(myXMLIO->GetAttribute(L"maxbodyX", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fMaxTurnBodyX = v_float;
            }
            // Dureza para la suspensión frontal (atributo 'bodyZ')
            if(myXMLIO->GetAttribute(L"bodyZ", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fTurnBodyZ = v_float;
            }
            // Dureza para la suspensión lateral (atributo 'bodyX')
            if(myXMLIO->GetAttribute(L"bodyX", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fTurnBodyX = v_float;
            }
            // Recuperación para la suspensión frontal (atributo 'recovZ')
            if(myXMLIO->GetAttribute(L"recovZ", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fRecvBodyZ = v_float;
            }
            // Recuperación para la suspensión lateral (atributo 'recovX')
            if(myXMLIO->GetAttribute(L"recovX", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fRecvBodyX = v_float;
            }
            // Giro del vehículo (atributo 'turn')
            if(myXMLIO->GetAttribute(L"turn", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fTurn = v_float;
            }
            // Giro máximo de las ruedas en Y (atributo 'wheelY')
            if(myXMLIO->GetAttribute(L"wheelY", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fMaxTurnWheelY = v_float;
            }
            // Giro máximo de las ruedas en Z (atributo 'wheelZ')
            if(myXMLIO->GetAttribute(L"wheelZ", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fTurnWheelZ = v_float;
            }
            // Longitud del centro del vehículo al tren trasero o delantero (atributo 'wheelDist')
            if(myXMLIO->GetAttribute(L"wheelDist", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fWheelDist = v_float;
            }
			// Altura de la cámara en el modo de vista interior
            if(myXMLIO->GetAttribute(L"cameraY", value))
            {
                v_float = (float)_wtof(value);
                if(v_float > 0) m_fCameraY = v_float;
            }

            myXMLIO->MoveParent();
        }

        // Tomamos el nodo fuselaje (nodo 'body')
        if(myXMLIO->MoveSubElement(L"body"))
        {
            // Se carga la configuración del objeto hijo
            if(!m_pBody) m_pBody = new C3DObject;
            if(!m_pBody) return false;
            if(!m_pBody->initialize(this, myXMLIO)) return false;

            // El objeto se almacena como hijo del actual
            if(!m_paObjets) m_paObjets = new vector<C3DObject*>;
            if(!m_paObjets) return false;
            m_paObjets->push_back(m_pBody);

            myXMLIO->MoveParent();
        }

        // Tomamos el nodo de la rueda frontal derecha (nodo 'frwheel')
        if(myXMLIO->MoveSubElement(L"frwheel"))
        {
            // Se carga la configuración del objeto hijo
            if(!m_pFRWheel) m_pFRWheel = new C3DObject;
            if(!m_pFRWheel) return false;
            if(!m_pFRWheel->initialize(this, myXMLIO)) return false;

            // Las ruedas de la derecha se giran PI grados sobre Y
            m_pFRWheel->setRotation(D3DXVECTOR3(0.0f, D3DX_PI, 0.0f));

            // El objeto se almacena como hijo del actual
            if(!m_paObjets) m_paObjets = new vector<C3DObject*>;
            if(!m_paObjets) return false;
            m_paObjets->push_back(m_pFRWheel);

            myXMLIO->MoveParent();
        }

        // Tomamos el nodo de la rueda frontal izquierda (nodo 'flwheel')
        if(myXMLIO->MoveSubElement(L"flwheel"))
        {
            // Se carga la configuración del objeto hijo
            if(!m_pFLWheel) m_pFLWheel = new C3DObject;
            if(!m_pFLWheel) return false;
            if(!m_pFLWheel->initialize(this, myXMLIO)) return false;

            // El objeto se almacena como hijo del actual
            if(!m_paObjets) m_paObjets = new vector<C3DObject*>;
            if(!m_paObjets) return false;
            m_paObjets->push_back(m_pFLWheel);

            myXMLIO->MoveParent();
        }

        // Tomamos el nodo de la rueda trasera derecha (nodo 'rrwheel')
        if(myXMLIO->MoveSubElement(L"rrwheel"))
        {
            // Se carga la configuración del objeto hijo
            if(!m_pRRWheel) m_pRRWheel = new C3DObject;
            if(!m_pRRWheel) return false;
            if(!m_pRRWheel->initialize(this, myXMLIO)) return false;

            // Las ruedas de la derecha se giran PI grados sobre Y
            m_pRRWheel->setRotation(D3DXVECTOR3(0.0f, D3DX_PI, 0.0f));

            // El objeto se almacena como hijo del actual
            if(!m_paObjets) m_paObjets = new vector<C3DObject*>;
            if(!m_paObjets) return false;
            m_paObjets->push_back(m_pRRWheel);

            myXMLIO->MoveParent();
        }

        // Tomamos el nodo de la rueda trasera izquierda (nodo 'rlwheel')
        if(myXMLIO->MoveSubElement(L"rlwheel"))
        {
            // Se carga la configuración del objeto hijo
            if(!m_pRLWheel) m_pRLWheel = new C3DObject;
            if(!m_pRLWheel) return false;
            if(!m_pRLWheel->initialize(this, myXMLIO)) return false;

            // El objeto se almacena como hijo del actual
            if(!m_paObjets) m_paObjets = new vector<C3DObject*>;
            if(!m_paObjets) return false;
            m_paObjets->push_back(m_pRLWheel);

            myXMLIO->MoveParent();
        }
    }

    // Liberamos el parseador de XML
    SAFE_DELETE(myXMLIO);

	// Recorremos la geometría del fuselaje del vehículo para
	// determinar los cuatro vértices del área de colisión
	if(m_pBody && m_pCollision)
		m_pCollision->setV(m_pBody->getMesh()->getMesh(), true);

	// Si estamos en modo juego cargamos los elementos del vehículo
	if(g_bInGame)
	{
		// Cargamos el cohete asociado (archivo 'rocket.xml') y lo desactivamos
        if(!m_pRocket) m_pRocket = new C3DObjWeapon;
        if(!m_pRocket) return false;
        if(!m_pRocket->initialize(this, L".\\models\\objects\\rocket.xml", OBJWEAPON_ROCKET)) return false;
		m_pRocket->setEnabled(false);

		// Cargamos la mina asociada (archivo 'mine.xml') y la desactivamos
        if(!m_pMine) m_pMine = new C3DObjWeapon;
        if(!m_pMine) return false;
        if(!m_pMine->initialize(this, L".\\models\\objects\\mine.xml", OBJWEAPON_MINE)) return false;
		m_pMine->setEnabled(false);
	}

    return true;
}

//--------------------------------------------------------------------------------------
// Toma o calcula la inversa de la transformacion actual del vehículo
//--------------------------------------------------------------------------------------
D3DXMATRIX *C3DObjVehicle::calculateTransInv(D3DXMATRIX *result)
{
	// Si el objeto tiene colisiones y se encuentra actualizado
	if (m_pCollision && !m_bModified)
	{
		// Se toma la matriz inversa ya calculada para colisiones
		*(result) = *(m_pCollision->getTransInv());
	}
	
	// Si el objeto no tiene colisiones o no se encuentra actualizado
	else
	{
		// Se calcula la matriz inversa a las tranformaciones propias del objeto actual
		D3DXMatrixInverse(result,NULL,&m_Transformation);
	}

	// Retornamos la matriz inversa calculada
	return result;
}

//--------------------------------------------------------------------------------------
// Actualiza el vehículo
//--------------------------------------------------------------------------------------
void C3DObjVehicle::update()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

	// Actualizamos la física propia del vehículo
	m_pPhyVehicle->update();

    // Tomamos las diferencias de velocidad y rotación
    float DiffSpeed = updateLastSpeed();
    float DiffRotation = updateLastYRotation();

    // Actualizamos la suspensión del fuselaje
    updateBody(DiffSpeed, DiffRotation);

    // Actualizamos el rodaje de las ruedas
    updateWheels();

    // Actualizamos la dirección de la velocidad
    updateDirection(DiffRotation);

    // Actualizamos el sonido
    updateSound(DiffSpeed);

	// Tomamos el último flag de modificaciones
	m_bLastModified = m_bModified;

    // Comprobamos si hubo cambios en su matriz de transformación
    C3DObjVehicle::refreshTransformation(DiffRotation);

	// Actualizamos el sistema de partículas primario y secundario
	if (m_bLastModified)
	{
		if (m_pParticles1)
		{
			if (m_bAccelerate) m_pParticles1->SetReleaseInterval(0.05f);
			else			   m_pParticles1->SetReleaseInterval(0.3f);
		}
		if (m_pParticles2)
		{
			if (m_bAccelerate) m_pParticles2->SetReleaseInterval(0.05f);
			else			   m_pParticles2->SetReleaseInterval(0.3f);
		}
	}

	// Actualizamos el cohete asociado del vehículo
    if (m_pRocket) m_pRocket->update();

	// Actualizamos la mina asociada al vehículo
    if (m_pMine) m_pMine->update();

    // Función de la clase padre
    C3DObject::update();

	// La primera vez forzamos su actualización
	if (m_bUpdate == false)
	{
		addRotation(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
		m_bUpdate = true;
	}
}

//--------------------------------------------------------------------------------------
// Renderiza el vehículo
//--------------------------------------------------------------------------------------
void C3DObjVehicle::render()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;

	// Renderizamos el cohete asociado del vehículo
    if (m_pRocket) m_pRocket->render();

	// Renderizamos la mina asociada al vehículo
    if (m_pMine) m_pMine->render();

    // Función de la clase padre
    C3DObject::render();
}

//--------------------------------------------------------------------------------------
// Función para actualizar la última velocidad del vehículo, limitándola a la máxima,
// retorna la diferencia respecto la anterior
//--------------------------------------------------------------------------------------
float C3DObjVehicle::updateLastSpeed(void)
{
    D3DXVECTOR3 VecRotation;
    float Speed, DiffSpeed;

	// Tomamos la diferencia de velocidad y actualizamos la última velocidad registrada
    // Comprobamos que el vehículo no se sobrepase el límite de velocidad lineal
    Speed = m_pPhysics->getLSpeed();
    if(Speed > m_fMaxSpeed)
    {
        getPhysics()->setLSpeed(m_fMaxSpeed);
        Speed = m_fMaxSpeed;
    }
    else if(Speed < -m_fMaxSpeed)
    {
        getPhysics()->setLSpeed(-m_fMaxSpeed);
        Speed = -m_fMaxSpeed;
    }
    DiffSpeed = Speed - m_fLastSpeed;
	m_fLastSpeed = Speed;

    return DiffSpeed;
}

//--------------------------------------------------------------------------------------
// Función para actualizar la última rotación del vehículo, retorna la diferencia respecto la anterior
//--------------------------------------------------------------------------------------
float C3DObjVehicle::updateLastYRotation(void)
{
    D3DXVECTOR3 VecRotation;
    float DiffRotation;

    // Tomamos la diferencia de giro en Y y actualizamos el último giro registrado
    getRotation(VecRotation);
    DiffRotation = VecRotation.y - m_fLastYRotation;
	m_fLastYRotation = VecRotation.y;

    return DiffRotation;
}

//--------------------------------------------------------------------------------------
// Función para actualizar la suspensión del fuselaje del vehículo (frontal y transversal)
//--------------------------------------------------------------------------------------
void C3DObjVehicle::updateBody(float DiffSpeed, float DiffRotation)
{
    D3DXVECTOR3 VecRotation, VecSpeed;

	// Tomamos la física del vehículo
	C3DPhysics *Physics = m_pBody->getPhysics();
    m_pBody->getRotation(VecRotation);
    Physics->getASpeed(VecSpeed);

    // Giramos el fuselaje frontalmente en función de la diferencia de velocidad
    if(VecRotation.z < -m_fMaxTurnBodyZ)
    {
        VecRotation.z = -m_fMaxTurnBodyZ;
        m_pBody->setRotation(VecRotation);
        VecSpeed.z = 0.0f;
        Physics->setASpeed(VecSpeed);
    }
	else if(VecRotation.z > m_fMaxTurnBodyZ)
    {
        VecRotation.z = m_fMaxTurnBodyZ;
        m_pBody->setRotation(VecRotation);
        VecSpeed.z = 0.0f;
        Physics->setASpeed(VecSpeed);
    }
    else if(DiffSpeed != 0.0f)
    {
        if(m_bHandBrake)    VecSpeed.z =   DiffSpeed * m_fTurnBodyZ;
        else                VecSpeed.z = - DiffSpeed * m_fTurnBodyZ;
        Physics->setASpeed(VecSpeed);
    }
    else if(VecRotation.z > 0.01f || VecRotation.z < -0.01f)
    {
        VecSpeed.z = -m_fRecvBodyZ * VecRotation.z;
        Physics->setASpeed(VecSpeed);
    }
    else if(VecSpeed.z != 0.0f)
    {
        VecSpeed.z = 0.0f;
        Physics->setASpeed(VecSpeed);
    }

	// Giramos el fuselaje transversalmente en función de la diferencia de giro y la velocidad actual
    if(VecRotation.x < -m_fMaxTurnBodyX)
    {
        VecRotation.x = -m_fMaxTurnBodyX;
        m_pBody->setRotation(VecRotation);
        VecSpeed.x = 0.0f;
        Physics->setASpeed(VecSpeed);
    }
	else if(VecRotation.x > m_fMaxTurnBodyX)
    {
        VecRotation.x = m_fMaxTurnBodyX;
        m_pBody->setRotation(VecRotation);
        VecSpeed.x = 0.0f;
        Physics->setASpeed(VecSpeed);
    }
    else if(DiffRotation != 0.0f)
    {
        VecSpeed.x = - DiffRotation * m_fLastSpeed * m_fTurnBodyX;
        Physics->setASpeed(VecSpeed);
    }
    else if(VecRotation.x > 0.01f || VecRotation.x < -0.01f)
    {
        VecSpeed.x = -m_fRecvBodyX * VecRotation.x;
        Physics->setASpeed(VecSpeed);
    }
    else if(VecSpeed.x != 0.0f)
    {
        VecSpeed.x = 0.0f;
        Physics->setASpeed(VecSpeed);
    }

}

//--------------------------------------------------------------------------------------
// Función para actualizar el rodaje de las ruedas del vehículo
//--------------------------------------------------------------------------------------
void C3DObjVehicle::updateWheels(void)
{
    // Giramos las ruedas en función de la velocidad
    // Hay que tener en cuenta que ruedas más grandes giran más despacio,
    // esto lo controlamos por medio de la posición en Y de la rueda (que es el radio)
    D3DXVECTOR3 VecPosition, VecSpeed;
    float Roll, Radius;
	C3DPhysics *PhysicsFR = m_pFRWheel->getPhysics();
	C3DPhysics *PhysicsFL = m_pFLWheel->getPhysics();
	C3DPhysics *PhysicsRR = m_pRRWheel->getPhysics();
	C3DPhysics *PhysicsRL = m_pRLWheel->getPhysics();

    // Actualizamos las dos ruedas frontales
    m_pFRWheel->getPosition(VecPosition);
    Radius = VecPosition.y;
    Roll =  m_fLastSpeed * m_fTurnWheelZ / Radius;
    PhysicsFR->getASpeed(VecSpeed); VecSpeed.z = -Roll;
    PhysicsFR->setASpeed(VecSpeed);
    PhysicsFL->getASpeed(VecSpeed); VecSpeed.z =  Roll;
    PhysicsFL->setASpeed(VecSpeed);

    // Actualizamos las dos ruedas traseras
    // Si está activado el freno de mano las ruedas quedarán bloqueadas
    if(m_bHandBrake)
    {
        PhysicsRR->getASpeed(VecSpeed); VecSpeed.z = 0.0f;
        PhysicsRR->setASpeed(VecSpeed);
        PhysicsRL->getASpeed(VecSpeed); VecSpeed.z = 0.0f;
        PhysicsRL->setASpeed(VecSpeed);
    }
    else
    {
        m_pRRWheel->getPosition(VecPosition);
        Radius = VecPosition.y;
        Roll =  m_fLastSpeed * m_fTurnWheelZ / Radius;
        PhysicsRR->getASpeed(VecSpeed); VecSpeed.z = -Roll;
        PhysicsRR->setASpeed(VecSpeed);
        PhysicsRL->getASpeed(VecSpeed); VecSpeed.z =  Roll;
        PhysicsRL->setASpeed(VecSpeed);
    }
}

//--------------------------------------------------------------------------------------
// Función para actualizar la dirección de la velocidad
//--------------------------------------------------------------------------------------
void C3DObjVehicle::updateDirection(float DiffRotation)
{
    // Tenemos que girar en Y la dirección de desplazamiento según indique DiffRotation
    float ElapsedTime = DXUTGetElapsedTime();
    D3DXVECTOR3 VecRot;
    float ActualRot, MaxRot, SelectedRot, Added = 0.0f, newAcumTurn;

    // A partir de cierto giro acumulado, permitiremos más giro
    if(m_fAcumTurn < -OBJVEHICLE_MAX_ACUM_TURN || m_fAcumTurn > OBJVEHICLE_MAX_ACUM_TURN)
           Added = m_fAcumTurn / 3.0f;

    // Calculamos el giro actual y el máximo giro permitido (la diferencia es el derrape)
    ActualRot = DiffRotation + m_fAcumTurn;
    if(ActualRot > 0.0f)
        MaxRot =   m_fTurn * m_fMaxSpeed * ElapsedTime + Added;
    else
        MaxRot = - m_fTurn * m_fMaxSpeed * ElapsedTime + Added;

    // Efectuaremos el menor giro en valor absoluto (actual o máximo)
    if(fabsf(ActualRot) > fabsf(MaxRot))
        SelectedRot = MaxRot;
    else
        SelectedRot = ActualRot;
    if(SelectedRot == 0.0f) return;
    VecRot = D3DXVECTOR3(0.0f, SelectedRot, 0.0f);
    getPhysics()->addRotLDirection(VecRot);

    // Calculamos el nuevo giro acumulado
    // Cada vez que el acumulado llegue a cero reseteamos la dirección
    newAcumTurn = m_fAcumTurn + DiffRotation - SelectedRot;
    if(m_fAcumTurn != 0.0f && newAcumTurn == 0.0f)
        getPhysics()->setRotLDirection(*m_pRotation);
    m_fAcumTurn = newAcumTurn;
}

//--------------------------------------------------------------------------------------
// Función para actualizar el sonido del vehículo
//--------------------------------------------------------------------------------------
void C3DObjVehicle::updateSound(float DiffSpeed)
{
    long newFrecuency;
    long added = 200 * (long)fabsf(m_fLastSpeed + m_pPhyVehicle->getAditionalLSpeed());

    // Cambiamos la frecuencia del sonido del motor en funcion de la velocidad
    // y del estado del acelerador
    if(m_pSound)
    {
        newFrecuency = m_pSound->getInitFrecuency() + added * 2;
        if(m_bAccelerate) newFrecuency += 10000;
        m_pSound->setFrecuency(newFrecuency);
    }

    // Si hubo derrape o movimientos adicionales emitimos el sonido correspondiente
	if(m_pSndSkid)
	{
		if(isSkidding() || m_pPhyVehicle->getAditionalLSpeed() != 0.0f)
		{
			if(!m_pSndSkid->isPlaying()) m_pSndSkid->Play(true);
			newFrecuency = m_pSndSkid->getInitFrecuency() + added;
			m_pSndSkid->setFrecuency(newFrecuency);
		}
		else if(m_pSndSkid->isPlaying()) m_pSndSkid->Stop();
	}

    // Actualizamos los sonidos específicos del vehículo
    if(m_pSndSkid) m_pSndSkid->update();
    if(m_pSndCrash) m_pSndCrash->update();
}

//--------------------------------------------------------------------------------------
// Refresca la transformación actual del objeto, sólo si es necesario
//--------------------------------------------------------------------------------------
void C3DObjVehicle::refreshTransformation(float DiffRotation)
{
    if(m_bModified)
    {
		// (0) Partimos de la matriz IDENTIDAD
        D3DXMATRIX Transformation;
        D3DXMatrixIdentity(&m_Transformation);

        // (1) Aplicamos el ESCALADO
        if(m_pScale)
        {
            D3DXMatrixScaling(&Transformation, m_pScale->x, m_pScale->y, m_pScale->z);
            D3DXMatrixMultiply(&m_Transformation, &m_Transformation, &Transformation);
        }

        // (2) Aplicamos la ROTACIÓN
        if(m_pRotation)
        {
			float LastDist, LastDistX, LastDistZ;

            // Si vamos a girar con el eje delantero, será cuando tenemos giro 
            // acumulado o el freno de mano y la velocidad es positiva
            if((m_fAcumTurn != 0.0f || m_bHandBrake) && m_fLastSpeed > 0.0f && m_pPosition)
            {
                // Si el freno de mano está activado la distacia al tren delantero es máxima
                if(m_bHandBrake)
                    LastDist = 2.0f * m_fWheelDist;
                // Si el freno de mano no está activado la distacia al tren delantero del
                // punto de rotación será proporcional al giro acumulado
                else
                {
                    LastDist = fabsf(m_fAcumTurn) / OBJVEHICLE_MAX_ACUM_TURN;
                    if(LastDist > 1.0f) LastDist = 1.0f;
                    LastDist = LastDist * 2.0f * m_fWheelDist;
                }

                // Vamos a transformar la rotación sobre el eje delantero
                // en un cambio de posición (y rotación que corresponda sobre el eje trasero)
                LastDistX = ( cosf(m_fLastYRotation + DiffRotation) - cosf(m_fLastYRotation)) * LastDist;
                LastDistZ = (-sinf(m_fLastYRotation + DiffRotation) + sinf(m_fLastYRotation)) * LastDist;

                m_pPosition->x += LastDistX;
                m_pPosition->z += LastDistZ;
            }

            // Por defecto siempre giramos con el eje trasero
            // Nos desplazamos al eje trasero
            m_Transformation._41 = -m_fWheelDist;

            // Aplicamos la rotación del eje trasero
            D3DXMatrixRotationYawPitchRoll(&Transformation, m_pRotation->y, m_pRotation->x, m_pRotation->z);
            D3DXMatrixMultiply(&m_Transformation, &m_Transformation, &Transformation);
        }

        // (3) Aplicamos la TRASLACIÓN
        if(m_pPosition)
        {
            D3DXMatrixTranslation(&Transformation, m_pPosition->x, m_pPosition->y, m_pPosition->z);
            D3DXMatrixMultiply(&m_Transformation, &m_Transformation, &Transformation);
        }

        // (4) Aplicamos la transformación del PADRE
        if(m_pParent)
            D3DXMatrixMultiply(&m_Transformation, &m_Transformation, m_pParent->getTransformation());

		// (5) Si el objeto tiene COLISIONES, recalculamos su matriz inversa de una forma eficiente
		if(m_pCollision)
			calculateTransInv(m_pCollision->getTransInv());

        m_bModified = false;
    }

}

//--------------------------------------------------------------------------------------
// Para activar el sonido de derrape del coche
//--------------------------------------------------------------------------------------
void C3DObjVehicle::playSkid(bool loop, long volume, long frecuency)
{
	if(!m_pSndSkid) return;

	m_pSndSkid->Play(loop);
	if(volume)    m_pSndSkid->setVolume(volume);
	if(frecuency) m_pSndSkid->setFrecuency(frecuency);
}

//--------------------------------------------------------------------------------------
// Para activar el sonido de colisiones del coche
//--------------------------------------------------------------------------------------
void C3DObjVehicle::playCrash(bool loop, long volume, long frecuency)
{
	if(!m_pSndCrash) return;

	m_pSndCrash->Play(loop);
	if(volume)    m_pSndCrash->setVolume(volume);
	if(frecuency) m_pSndCrash->setFrecuency(frecuency);
}
