//--------------------------------------------------------------------------------------
// 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.
//--------------------------------------------------------------------------------------
// 3DObject.cpp
//--------------------------------------------------------------------------------------
#include "dxstdafx.h"
#include "3DObject.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)
extern float			g_fFactor;		   // Factor de fricción del suelo de la arena (3DObjArena.cpp)

//--------------------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------------------
C3DObject::C3DObject()
{
    m_bInit = false;

    m_pParent = NULL;

    m_pMesh = NULL;
    m_pCollision = NULL;
    m_pPhysics = NULL;
    m_pSound = NULL;
    m_paObjets = NULL;

    m_pScale = NULL;
    m_pRotation = NULL;
    m_pPosition = NULL;

	m_pParticles1 = NULL;
	m_pParticles1Pos = NULL;
	m_pParticles2 = NULL;
	m_pParticles2Pos = NULL;

    m_bModified = true;
	m_bLastModified = false;
    m_bEnabled = false;
}

//--------------------------------------------------------------------------------------
// Destructor
//--------------------------------------------------------------------------------------
C3DObject::~C3DObject()
{
    Release();
}

//--------------------------------------------------------------------------------------
// Inicializa el objeto a partir de la ruta del fichero XML
// (colisiones + física + sonido + modelo gráfico + resto de objetos)
//--------------------------------------------------------------------------------------
bool C3DObject::initialize(C3DObject *dParent, LPCWSTR ObjectPath)
{
    // Salimos si ya se ha inicializado
    if(m_bInit) return false;
    m_bInit = true;
    m_bEnabled = true;

    // Cargamos la configuración y la estructura de objetos asociados
    m_pParent = dParent;
    if(ObjectPath) return load(ObjectPath);
    else           return false;
}

//--------------------------------------------------------------------------------------
// Inicializa el objeto a partir de un lector de XML
// (colisiones + física + sonido + modelo gráfico + resto de objetos)
//--------------------------------------------------------------------------------------
bool C3DObject::initialize(C3DObject *dParent, CXMLIO *myXMLIO)
{
    // Salimos si ya se ha inicializado
    if(m_bInit) return false;
    m_bInit = true;
    m_bEnabled = true;

    // Cargamos la configuración y la estructura de objetos asociados
    m_pParent = dParent;
    if(myXMLIO) return load(myXMLIO);
    else        return false;
}

//--------------------------------------------------------------------------------------
// Carga la configuración a partir de la ruta del fichero XML
// (colisiones + física + sonido + modelo gráfico + resto de objetos)
//--------------------------------------------------------------------------------------
bool C3DObject::load(LPCWSTR ObjectPath)
{
    // Podremos tener varias llamadas recursivas a este método

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

    // Abrimos el fichero
    if(!myXMLIO->LoadFile(ObjectPath))
    {
        // Si falla salimos
        SAFE_DELETE(myXMLIO);
        return false;
    }

    // Llamamos a la función que realiza el proceso a partir del CXMLIO
    bool res = load(myXMLIO);

    // Salimos
    SAFE_DELETE(myXMLIO);
    return res;
}

//--------------------------------------------------------------------------------------
// Carga la configuración a partir de un lector de XML
// (colisiones + física + sonido + modelo gráfico + resto de objetos)
//--------------------------------------------------------------------------------------
bool C3DObject::load(CXMLIO *myXMLIO)
{
    WCHAR value[MAX_STRING+1];

    // Partimos de la raíz de un objeto (nodo 'object')
    // que no tiene por qué ser la raíz del documento

    // Dependencias de otro fichero (atributo 'src')
    if(myXMLIO->GetAttribute(L"src", value))
        // En este caso se cargará el fichero de dependencias antes del actual
        // TODO: Cuidado con las referencias cruzadas
        load(value);

    // Modelo gráfico asociado (nodo 'mesh')
    if(myXMLIO->MoveSubElement(L"mesh"))
    {
		// Si debe hacerse o no transparente ante una cámara baja (atributo 'alpha')
        int alpha = 0;
        if(myXMLIO->GetAttribute(L"alpha", value)) alpha = (int)_wtoi(value);
		
		// Fichero de la geomería del objeto (atributo 'src')
        if(myXMLIO->GetAttribute(L"src", value))
        {
            if(!m_pMesh) m_pMesh = new C3DMesh;
            if(!m_pMesh) return false;
            if(!m_pMesh->initialize(this, value, alpha)) return false;
        }
        myXMLIO->MoveParent();
    }

    // Posición inicial (nodo 'position')
    if(myXMLIO->MoveSubElement(L"position"))
    {
        // Valores X, Y, Z (atributos 'x', 'y', 'z')
        float x=0.0f, y=0.0f, z=0.0f;
        if(myXMLIO->GetAttribute(L"x", value))  x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"y", value))  y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"z", value))  z = (float)_wtof(value);
        if(x!=0.0f || y!=0.0f || z!=0.0f)
            this->setPosition(D3DXVECTOR3(x,y,z));
        myXMLIO->MoveParent();
    }

    // Rotación inicial (nodo 'rotation')
    if(myXMLIO->MoveSubElement(L"rotation"))
    {
        // Valores X, Y, Z (atributos 'x', 'y', 'z')
        float x=0.0f, y=0.0f, z=0.0f;
        if(myXMLIO->GetAttribute(L"x", value))  x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"y", value))  y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"z", value))  z = (float)_wtof(value);
        if(x!=0.0f || y!=0.0f || z!=0.0f)
            this->setRotation(D3DXVECTOR3(x,y,z));
        myXMLIO->MoveParent();
    }

    // Escalado inicial (nodo 'scale')
    if(myXMLIO->MoveSubElement(L"scale"))
    {
        // Valores X, Y, Z (atributos 'x', 'y', 'z')
        float x=1.0f, y=1.0f, z=1.0f;
        if(myXMLIO->GetAttribute(L"x", value))  x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"y", value))  y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"z", value))  z = (float)_wtof(value);
        if(x!=1.0f || y!=1.0f || z!=1.0f)
            this->setScale(D3DXVECTOR3(x,y,z));
        myXMLIO->MoveParent();
    }

    // Área de colisión asociado (nodo 'collision')
	// Si no estamos en modo juego no cargamos las colisiones
    if(	g_bInGame &&
		myXMLIO->MoveSubElement(L"collision"))
    {
        // Masa del objeto (atributo 'mass')
        float mass=0.0f;
        if(myXMLIO->GetAttribute(L"mass", value))	mass = (float)_wtof(value);

		if(!m_pCollision) m_pCollision = new C3DCollision;
        if(!m_pCollision) return false;
        if(!m_pCollision->initialize(this, mass)) return false;

		// Recorremos la geometría del objeto para determinar los cuatro vértices del área de colisión
		// En el caso de los vehículos, el área de colisión se tomará del fuselaje en su función "load"
		if(m_pMesh)
			m_pCollision->setV(m_pMesh->getMesh(), false);

        myXMLIO->MoveParent();
    }

    // Sistema físico asociado (nodo 'physic')
    if(myXMLIO->MoveSubElement(L"physic"))
    {
        // Velocidades, aceleraciones y fricciones lineares y angulares (atributos '[la]_[vaf][xyz]')
        float l_v=0.0f, l_a=0.0f, l_f=0.0f;
        float l_dx=0.0f, l_dy=0.0f, l_dz=0.0f;
        float a_vx=0.0f, a_vy=0.0f, a_vz=0.0f;
        float a_ax=0.0f, a_ay=0.0f, a_az=0.0f;
        float a_fx=0.0f, a_fy=0.0f, a_fz=0.0f;

        if(!m_pPhysics) m_pPhysics = new C3DPhysics;
        if(!m_pPhysics) return false;
        if(!m_pPhysics->initialize(this)) return false;

        if(myXMLIO->GetAttribute(L"l_v", value))    l_v = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"l_a", value))    l_a = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"l_f", value))    l_f = (float)_wtof(value);

        if(myXMLIO->GetAttribute(L"l_dx", value))   l_dx = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"l_dy", value))   l_dy = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"l_dz", value))   l_dz = (float)_wtof(value);

        if(myXMLIO->GetAttribute(L"a_vx", value))   a_vx = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"a_vy", value))   a_vy = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"a_vz", value))   a_vz = (float)_wtof(value);

        if(myXMLIO->GetAttribute(L"a_ax", value))   a_ax = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"a_ay", value))   a_ay = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"a_az", value))   a_az = (float)_wtof(value);

        if(myXMLIO->GetAttribute(L"a_fx", value))   a_fx = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"a_fy", value))   a_fy = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"a_fz", value))   a_fz = (float)_wtof(value);

		// Aplico los cambios de fricción según el factor de la arena
		l_f = g_fFactor * l_f;
		a_fx = (g_fFactor * a_fx + a_fx) / 2.0f;
		a_fy = (g_fFactor * a_fy + a_fy) / 2.0f;
		a_fz = (g_fFactor * a_fz + a_fz) / 2.0f;

        if(l_v!=0.0f) m_pPhysics->setLSpeed(l_v);
        if(l_a!=0.0f) m_pPhysics->setLAcceleration(l_a);
        if(l_f!=0.0f) m_pPhysics->setLFriction(l_f);
        if(l_dx!=0.0f || l_dy!=0.0f || l_dz!=0.0f)
            m_pPhysics->setRotLDirection(D3DXVECTOR3(l_dx,l_dy,l_dz));
        if(a_vx!=0.0f || a_vy!=0.0f || a_vz!=0.0f)
            m_pPhysics->setASpeed(D3DXVECTOR3(a_vx,a_vy,a_vz));
        if(a_ax!=0.0f || a_ay!=0.0f || a_az!=0.0f)
            m_pPhysics->setAAcceleration(D3DXVECTOR3(a_ax,a_ay,a_az));
        if(a_fx!=0.0f || a_fy!=0.0f || a_fz!=0.0f)
            m_pPhysics->setAFriction(D3DXVECTOR3(a_fx,a_fy,a_fz));

        myXMLIO->MoveParent();
    }

    // Sistema de partículas primario asociado (nodo 'particles1')
	// Si no estamos en modo juego no cargamos el sistema de partículas primario
    if(	g_bInGame &&
		myXMLIO->MoveSubElement(L"particles1"))
    {
		// Creo la instancia
		if(!m_pParticles1) m_pParticles1 = new C3DParticles;
        if(!m_pParticles1) return false;

        // Textura de las partículas (atributo 'tex')
        if(myXMLIO->GetAttribute(L"tex", value)) m_pParticles1->SetTexture(value);

        // Máximo número de particulas (atributo 'max')
        DWORD max = 40;
        if(myXMLIO->GetAttribute(L"max", value)) max = (DWORD)_wtol(value);
		m_pParticles1->SetMaxParticles(max);

        // Número de partículas a liberar cada vez (atributo 'release_n')
        DWORD release_n = 2;
        if(myXMLIO->GetAttribute(L"release_n", value)) release_n = (DWORD)_wtol(value);
		m_pParticles1->SetNumToRelease(release_n);

        // Tiempo de liberación de partículas (atributo 'release_t')
        float release_t = 0.05f;
        if(myXMLIO->GetAttribute(L"release_t", value)) release_t = (float)_wtof(value);
		m_pParticles1->SetReleaseInterval(release_t);

        // Tiempo de vida de partículas (atributo 'life')
        float life = 0.15f;
        if(myXMLIO->GetAttribute(L"life", value)) life = (float)_wtof(value);
		m_pParticles1->SetLifeCycle(life);

        // Tamaño de partículas (atributo 'size')
        float size = 3.0f;
        if(myXMLIO->GetAttribute(L"size", value)) size = (float)_wtof(value);
		m_pParticles1->SetSize(size);

        // Dispersión de las partículas (atributo 'vel_var')
        float vel_var = 5.0f;
        if(myXMLIO->GetAttribute(L"vel_var", value)) vel_var = (float)_wtof(value);
		m_pParticles1->SetVelocityVar(vel_var);

        // Color de las partículas (atributos 'color_x' 'color_y' 'color_z' 'color_u')
        float color_x = 0.5f; float color_y = 0.5f; float color_z = 0.5f; float color_u = 1.0f;
        if(myXMLIO->GetAttribute(L"color_x", value)) color_x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"color_y", value)) color_y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"color_z", value)) color_z = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"color_u", value)) color_u = (float)_wtof(value);
		m_pParticles1->SetColor(D3DXCOLOR(color_x, color_y, color_z, color_u));

        // Posición de las partículas (atributos 'pos_x' 'pos_y' 'pos_z')
        float pos_x = 0.0f; float pos_y = 0.0f; float pos_z = 0.0f;
        if(myXMLIO->GetAttribute(L"pos_x", value)) pos_x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"pos_y", value)) pos_y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"pos_z", value)) pos_z = (float)_wtof(value);
		if (pos_x != 0.0f || pos_y != 0.0f || pos_z != 0.0f)
		{
			if(!m_pParticles1Pos) m_pParticles1Pos = new D3DXVECTOR3(pos_x, pos_y, pos_z);
			if(!m_pParticles1Pos) return false;
			m_pParticles1->SetPosition(*m_pParticles1Pos);
		}

        // Velocidad de las partículas (atributos 'vel_x' 'vel_y' 'vel_z')
        float vel_x = 0.0f; float vel_y = 0.0f; float vel_z = 0.0f;
        if(myXMLIO->GetAttribute(L"vel_x", value)) vel_x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"vel_y", value)) vel_y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"vel_z", value)) vel_z = (float)_wtof(value);
		m_pParticles1->SetVelocity(D3DXVECTOR3(vel_x, vel_y, vel_z));

        // Gravedad de las partículas (atributos 'gra_x' 'gra_y' 'gra_z')
        float gra_x = 0.0f; float gra_y = 0.0f; float gra_z = 0.0f;
        if(myXMLIO->GetAttribute(L"gra_x", value)) gra_x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"gra_y", value)) gra_y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"gra_z", value)) gra_z = (float)_wtof(value);
		m_pParticles1->SetGravity(D3DXVECTOR3(gra_x, gra_y, gra_z));

        // Viento de las partículas (atributos 'wind_x' 'wind_y' 'wind_z')
        float wind_x = 0.0f; float wind_y = 0.0f; float wind_z = 0.0f;
        if(myXMLIO->GetAttribute(L"wind_x", value)) wind_x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"wind_y", value)) wind_y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"wind_z", value)) wind_z = (float)_wtof(value);
		m_pParticles1->SetWind(D3DXVECTOR3(wind_x, wind_y, wind_z));

		// Inicializamos el sistema de partículas
		m_pParticles1->initialize();

        myXMLIO->MoveParent();
    }

    // Sistema de partículas secundario asociado (nodo 'particles2')
	// Si no estamos en modo juego no cargamos el sistema de partículas secundario
    if(	g_bInGame &&
		myXMLIO->MoveSubElement(L"particles2"))
	{
		// Creo la instancia
		if(!m_pParticles2) m_pParticles2 = new C3DParticles;
        if(!m_pParticles2) return false;

        // Textura de las partículas (atributo 'tex')
        if(myXMLIO->GetAttribute(L"tex", value)) m_pParticles2->SetTexture(value);

        // Máximo número de particulas (atributo 'max')
        DWORD max = 40;
        if(myXMLIO->GetAttribute(L"max", value)) max = (DWORD)_wtol(value);
		m_pParticles2->SetMaxParticles(max);

        // Número de partículas a liberar cada vez (atributo 'release_n')
        DWORD release_n = 2;
        if(myXMLIO->GetAttribute(L"release_n", value)) release_n = (DWORD)_wtol(value);
		m_pParticles2->SetNumToRelease(release_n);

        // Tiempo de liberación de partículas (atributo 'release_t')
        float release_t = 0.05f;
        if(myXMLIO->GetAttribute(L"release_t", value)) release_t = (float)_wtof(value);
		m_pParticles2->SetReleaseInterval(release_t);

        // Tiempo de vida de partículas (atributo 'life')
        float life = 0.15f;
        if(myXMLIO->GetAttribute(L"life", value)) life = (float)_wtof(value);
		m_pParticles2->SetLifeCycle(life);

        // Tamaño de partículas (atributo 'size')
        float size = 3.0f;
        if(myXMLIO->GetAttribute(L"size", value)) size = (float)_wtof(value);
		m_pParticles2->SetSize(size);

        // Dispersión de las partículas (atributo 'vel_var')
        float vel_var = 5.0f;
        if(myXMLIO->GetAttribute(L"vel_var", value)) vel_var = (float)_wtof(value);
		m_pParticles2->SetVelocityVar(vel_var);

        // Color de las partículas (atributos 'color_x' 'color_y' 'color_z' 'color_u')
        float color_x = 0.5f; float color_y = 0.5f; float color_z = 0.5f; float color_u = 1.0f;
        if(myXMLIO->GetAttribute(L"color_x", value)) color_x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"color_y", value)) color_y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"color_z", value)) color_z = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"color_u", value)) color_u = (float)_wtof(value);
		m_pParticles2->SetColor(D3DXCOLOR(color_x, color_y, color_z, color_u));

        // Posición de las partículas (atributos 'pos_x' 'pos_y' 'pos_z')
        float pos_x = 0.0f; float pos_y = 0.0f; float pos_z = 0.0f;
        if(myXMLIO->GetAttribute(L"pos_x", value)) pos_x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"pos_y", value)) pos_y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"pos_z", value)) pos_z = (float)_wtof(value);
		if (pos_x != 0.0f || pos_y != 0.0f || pos_z != 0.0f)
		{
			if(!m_pParticles2Pos) m_pParticles2Pos = new D3DXVECTOR3(pos_x, pos_y, pos_z);
			if(!m_pParticles2Pos) return false;
			m_pParticles2->SetPosition(*m_pParticles2Pos);
		}

        // Velocidad de las partículas (atributos 'vel_x' 'vel_y' 'vel_z')
        float vel_x = 0.0f; float vel_y = 0.0f; float vel_z = 0.0f;
        if(myXMLIO->GetAttribute(L"vel_x", value)) vel_x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"vel_y", value)) vel_y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"vel_z", value)) vel_z = (float)_wtof(value);
		m_pParticles2->SetVelocity(D3DXVECTOR3(vel_x, vel_y, vel_z));

        // Gravedad de las partículas (atributos 'gra_x' 'gra_y' 'gra_z')
        float gra_x = 0.0f; float gra_y = 0.0f; float gra_z = 0.0f;
        if(myXMLIO->GetAttribute(L"gra_x", value)) gra_x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"gra_y", value)) gra_y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"gra_z", value)) gra_z = (float)_wtof(value);
		m_pParticles2->SetGravity(D3DXVECTOR3(gra_x, gra_y, gra_z));

        // Viento de las partículas (atributos 'wind_x' 'wind_y' 'wind_z')
        float wind_x = 0.0f; float wind_y = 0.0f; float wind_z = 0.0f;
        if(myXMLIO->GetAttribute(L"wind_x", value)) wind_x = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"wind_y", value)) wind_y = (float)_wtof(value);
        if(myXMLIO->GetAttribute(L"wind_z", value)) wind_z = (float)_wtof(value);
		m_pParticles2->SetWind(D3DXVECTOR3(wind_x, wind_y, wind_z));

		// Inicializamos el sistema de partículas
		m_pParticles2->initialize();

        myXMLIO->MoveParent();
    }

    // Sonido asociado (nodo 'sound')
    if(myXMLIO->MoveSubElement(L"sound"))
    {
        if(myXMLIO->GetAttribute(L"src", value))
            m_pSound = g_3DSoundMng->Create(value, this);
        if(m_pSound)
            m_pSound->Play(true);

        myXMLIO->MoveParent();
    }

    // Objetos asociados (nodos 'object')
    for(long i=0 ; myXMLIO->MoveSubElement(L"object", i) ; i++)
    {
        // Se carga la configuración del objeto hijo
        C3DObject *new_obj = new C3DObject;
        if(!new_obj) return false;
        if(!new_obj->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(new_obj);

        myXMLIO->MoveParent();
    }

	// Calculamos el tipo de objeto
	if (!m_pCollision) m_iTypeObject = OBJECT_TYPE_NOCOLL;								// No colisionable
	else if (!m_pPhysics) m_iTypeObject = OBJECT_TYPE_FIXED;							// Fijo sin física
	else if (m_pPhysics->getLFriction() == 0.0f) m_iTypeObject = OBJECT_TYPE_PHYSICS;	// Fijo con física
	else m_iTypeObject = OBJECT_TYPE_MOBILE;											// Móvil

	// Retornamos
    return true;
}

//--------------------------------------------------------------------------------------
// Renderiza el objeto
// (modelo gráfico + resto de objetos)
//--------------------------------------------------------------------------------------
void C3DObject::render()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;
    // Salimos si el objeto está desactivado
    if(!m_bEnabled) return;

    // Renderizamos modelo gráfico + resto de objetos
    if(m_pMesh)
        m_pMesh->render();
    if(m_paObjets)
        for (vector<C3DObject*>::iterator itr = m_paObjets->begin() ; itr != m_paObjets->end() ; itr++)
            (*itr)->render();
}

//--------------------------------------------------------------------------------------
// Actualiza el objeto
// (colisiones + física + sonido + resto de objetos)
//--------------------------------------------------------------------------------------
void C3DObject::update()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;
    // Salimos si el objeto está desactivado
    if(!m_bEnabled) return;

	// Tomamos el último flag de modificaciones (sólo si no es un vehículo)
	if( this->m_pCollision == NULL || !(this->m_pCollision->getIsVehicle()) )
		m_bLastModified = m_bModified;

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

	// Actualizamos el sistema de partículas primario
	if (m_pParticles1)
	{
		if (m_bLastModified)
		{
			if (m_pParticles1Pos)
			{
				D3DXVECTOR4 pos4 (m_pParticles1Pos->x, m_pParticles1Pos->y, m_pParticles1Pos->z, 1.0f);
				D3DXVec4Transform(&pos4, &pos4, &m_Transformation);
				D3DXVECTOR3 pos3 (pos4.x, pos4.y, pos4.z);
				m_pParticles1->SetPosition(pos3);
			}
			else if (m_pPosition) m_pParticles1->SetPosition(*m_pPosition);
		}
		m_pParticles1->update();
	}

	// Actualizamos el sistema de partículas secundario
	if (m_pParticles2)
	{
		if (m_bLastModified)
		{
			if (m_pParticles2Pos)
			{
				D3DXVECTOR4 pos4 (m_pParticles2Pos->x, m_pParticles2Pos->y, m_pParticles2Pos->z, 1.0f);
				D3DXVec4Transform(&pos4, &pos4, &m_Transformation);
				D3DXVECTOR3 pos3 (pos4.x, pos4.y, pos4.z);
				m_pParticles2->SetPosition(pos3);
			}
			else if (m_pPosition) m_pParticles2->SetPosition(*m_pPosition);
		}
		m_pParticles2->update();
	}

    // Actualizamos colisiones
    if(m_pCollision)
        m_pCollision->update();

    // Actualizamos física
	if(m_pPhysics)
        m_pPhysics->update();

	// Actualizamos sonido
    if(m_pSound)
        m_pSound->update();

	// Actualizamos resto de objetos de la jerarquía
    if(m_paObjets)
        for (vector<C3DObject*>::iterator itr = m_paObjets->begin() ; itr != m_paObjets->end() ; itr++)
            (*itr)->update();
}

//--------------------------------------------------------------------------------------
// Libera el objeto (colisiones + física + sonido + modelo gráfico + resto de objetos)
//--------------------------------------------------------------------------------------
void C3DObject::Release()
{
    // Salimos si no se ha inicializado
    if(!m_bInit) return;
    m_bInit = false;

    // Liberamos componentes asociados
    SAFE_DELETE(m_pMesh);
    SAFE_DELETE(m_pCollision);
    SAFE_DELETE(m_pPhysics);

    // Para liberar el sonido hay que hacerlo a través del manejador
    g_3DSoundMng->Destroy(m_pSound);

    if(m_paObjets)
    {
        for (vector<C3DObject*>::iterator itr = m_paObjets->begin() ; itr != m_paObjets->end() ; itr++)
            SAFE_DELETE(*itr);
        SAFE_DELETE(m_paObjets);
    }

    // Liberamos datos internos
    SAFE_DELETE(m_pScale);
    SAFE_DELETE(m_pRotation);
    SAFE_DELETE(m_pPosition);

	// Liberamos sistema de partículas primario y secundario
	SAFE_DELETE(m_pParticles1);
	SAFE_DELETE(m_pParticles1Pos);
	SAFE_DELETE(m_pParticles2);
	SAFE_DELETE(m_pParticles2Pos);
}

//--------------------------------------------------------------------------------------
// Para indicar al objeto que debe de reactualizar las matrices
//--------------------------------------------------------------------------------------
void C3DObject::setModified(void)
{
    // Objeto raíz
    m_bModified = true;

    // Objetos hijos
    if(m_paObjets)
    for (vector<C3DObject*>::iterator itr = m_paObjets->begin() ; itr != m_paObjets->end() ; itr++)
        (*itr)->setModified();
}

//--------------------------------------------------------------------------------------
// Función observadora del modelo gráfico asociado
//--------------------------------------------------------------------------------------
C3DMesh *C3DObject::getMesh(void)
{
    // Si no lo tenemos, no lo creamos, devolverá NULL
    return m_pMesh;
}

//--------------------------------------------------------------------------------------
// Función observadora de la colisión asociada
//--------------------------------------------------------------------------------------
C3DCollision *C3DObject::getCollision(void)
{
    // Si no lo tenemos, no lo creamos, devolverá NULL
    return m_pCollision;
}

//--------------------------------------------------------------------------------------
// Función observadora de la física asociada
//--------------------------------------------------------------------------------------
C3DPhysics *C3DObject::getPhysics(void)
{
    if(!m_pPhysics)
    {
        m_pPhysics = new C3DPhysics;
        assert(m_pPhysics != NULL);
        m_pPhysics->initialize(this);
    }
    return m_pPhysics;
}

//--------------------------------------------------------------------------------------
// Función observadora del sonido asociado
//--------------------------------------------------------------------------------------
C3DSound *C3DObject::getSound(void)
{
    // Si no lo tenemos, no lo creamos, devolverá NULL
    return m_pSound;
}

//--------------------------------------------------------------------------------------
// Función observadora de la lista de objetos que componen el objeto
//--------------------------------------------------------------------------------------
vector<C3DObject*> *C3DObject::getObjects(void)
{
    if(!m_paObjets)
    {
        m_paObjets = new vector<C3DObject*>;
        assert(m_paObjets != NULL);
    }

    return m_paObjets;
}

//--------------------------------------------------------------------------------------
// Toma o calcula la inversa de la transformacion actual del objeto
//--------------------------------------------------------------------------------------
D3DXMATRIX *C3DObject::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

		// (0) Partimos de la matriz IDENTIDAD
        D3DXMATRIX Transformation;
        D3DXMatrixIdentity(result);

		// (1) Aplicamos la TRASLACIÓN inversa del objeto (-tx, -ty, -tz)
        if(m_pPosition)
        {
            D3DXMatrixTranslation(&Transformation, -(m_pPosition->x), -(m_pPosition->y), -(m_pPosition->z));
            D3DXMatrixMultiply(result, result, &Transformation);
        }

		// (2) Aplicamos la ROTACIÓN inversa del objeto (-rx, -ry, -rz)
        if(m_pRotation)
        {
            D3DXMatrixRotationYawPitchRoll(&Transformation, -(m_pRotation->y), -(m_pRotation->x), -(m_pRotation->z));
            D3DXMatrixMultiply(result, result, &Transformation);
        }

		// (3) Aplicamos el ESCALADO inverso del objeto (1/ex, 1/ey, 1/ey)
        if(m_pScale)
        {
            D3DXMatrixScaling(&Transformation, 1/(m_pScale->x), 1/(m_pScale->y), 1/(m_pScale->z));
            D3DXMatrixMultiply(result, result, &Transformation);
        }

		// (4) Si tiene PADRE
		if (m_pParent)
		{
			// Se toma la matriz inversa del padre (recursivamente)
			m_pParent->calculateTransInv(&Transformation);

			// Se aplica la del padre a la ya calculada del hijo (Mpadre x Mhijo)
			D3DXMatrixMultiply(result, &Transformation, result);
		}
	}

	// Retornamos la matriz inversa calculada
	return result;
}

//--------------------------------------------------------------------------------------
// Función para cambiar el estado de procesamiento de un objeto, activado/desactivado
//--------------------------------------------------------------------------------------
void C3DObject::setEnabled(bool enabled)
{
	// Salimos si es lo mismo que lo que tengo
	if (m_bEnabled == enabled) return;

	// Cambiamos el estado del objeto
    m_bEnabled = enabled;

    // Desactivamos o ReActivamos el sonido cuando se activa o no el objeto
    if(m_pSound) m_pSound->Pause(enabled);

	// Reiniciamos los sistemas de partículas si se ha activado
	if (m_bEnabled)
	{
		if (m_pParticles1) m_pParticles1->RestartParticleSystem();
		if (m_pParticles2) m_pParticles2->RestartParticleSystem();
	}

    // Realizamos la operación con los hijos
    if(m_paObjets)
        for (vector<C3DObject*>::iterator itr = m_paObjets->begin() ; itr != m_paObjets->end() ; itr++)
            (*itr)->setEnabled(enabled);
}

//--------------------------------------------------------------------------------------
// Refresca la transformación actual del objeto, sólo si es necesario
//--------------------------------------------------------------------------------------
void C3DObject::refreshTransformation(void)
{
    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)
        {
            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 (Mhijo x Mpadre)
        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;
    }

}

//--------------------------------------------------------------------------------------
// Establece el escalado del objeto
//--------------------------------------------------------------------------------------
void C3DObject::setScale(D3DXVECTOR3 &Vec)
{
    if(!m_pScale)
    {
        m_pScale = new D3DXVECTOR3(1.0f, 1.0f, 1.0f);
        assert(m_pScale != NULL);
    }
    *(m_pScale) = Vec;
    m_bModified = true;

    if(m_paObjets)
    for (vector<C3DObject*>::iterator itr = m_paObjets->begin() ; itr != m_paObjets->end() ; itr++)
        (*itr)->setModified();
}

//--------------------------------------------------------------------------------------
// Toma el escalado actual del objeto
//--------------------------------------------------------------------------------------
void C3DObject::getScale(D3DXVECTOR3 &Vec)
{
    if(!m_pScale)
    {
        Vec = D3DXVECTOR3(1.0f, 1.0f, 1.0f);
        return;
    }
    Vec = *(m_pScale);
}

//--------------------------------------------------------------------------------------
// Establece la rotación del objeto
//--------------------------------------------------------------------------------------
void C3DObject::setRotation(D3DXVECTOR3 &Vec)
{
    if(!m_pRotation)
    {
        m_pRotation = new D3DXVECTOR3(0.0f, 0.0f, 0.0f);
        assert(m_pRotation != NULL);
    }
    *(m_pRotation) = Vec;
    m_bModified = true;

    if(m_paObjets)
    for (vector<C3DObject*>::iterator itr = m_paObjets->begin() ; itr != m_paObjets->end() ; itr++)
        (*itr)->setModified();
}

//--------------------------------------------------------------------------------------
// Toma la rotación actual del objeto
//--------------------------------------------------------------------------------------
void C3DObject::getRotation(D3DXVECTOR3 &Vec)
{
    if(!m_pRotation)
    {
        Vec = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
        return;
    }
    Vec = *(m_pRotation);
}

//--------------------------------------------------------------------------------------
// Establece la posición de del objeto
//--------------------------------------------------------------------------------------
void C3DObject::setPosition(D3DXVECTOR3 &Vec)
{
    if(!m_pPosition)
    {
        m_pPosition = new D3DXVECTOR3(0.0f, 0.0f, 0.0f);
        assert(m_pPosition != NULL);
    }
    *(m_pPosition) = Vec;
    m_bModified = true;

    if(m_paObjets)
    for (vector<C3DObject*>::iterator itr = m_paObjets->begin() ; itr != m_paObjets->end() ; itr++)
        (*itr)->setModified();
}

//--------------------------------------------------------------------------------------
// Toma la posición actual del objeto
//--------------------------------------------------------------------------------------
void C3DObject::getPosition(D3DXVECTOR3 &Vec)
{
    if(!m_pPosition)
    {
        Vec = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
        return;
    }
    Vec = *(m_pPosition);
}

//--------------------------------------------------------------------------------------
// Añade un escalado al objeto
//--------------------------------------------------------------------------------------
void C3DObject::addScale(D3DXVECTOR3 &Vec)
{
    if(!m_pScale)
    {
        m_pScale = new D3DXVECTOR3(0.0f, 0.0f, 0.0f);
        assert(m_pScale != NULL);
    }
    *(m_pScale) += Vec;
    m_bModified = true;

    if(m_paObjets)
    for (vector<C3DObject*>::iterator itr = m_paObjets->begin() ; itr != m_paObjets->end() ; itr++)
        (*itr)->setModified();
}

//--------------------------------------------------------------------------------------
// Añade una rotación al objeto
//--------------------------------------------------------------------------------------
void C3DObject::addRotation(D3DXVECTOR3 &Vec)
{
    if(!m_pRotation)
    {
        m_pRotation = new D3DXVECTOR3(0.0f, 0.0f, 0.0f);
        assert(m_pRotation != NULL);
    }
    *(m_pRotation) += Vec;
    m_bModified = true;

    if(m_paObjets)
    for (vector<C3DObject*>::iterator itr = m_paObjets->begin() ; itr != m_paObjets->end() ; itr++)
        (*itr)->setModified();
}

//--------------------------------------------------------------------------------------
// Añade una posición al objeto
//--------------------------------------------------------------------------------------
void C3DObject::addPosition(D3DXVECTOR3 &Vec)
{
    if(!m_pPosition)
    {
        m_pPosition = new D3DXVECTOR3(0.0f, 0.0f, 0.0f);
        assert(m_pPosition != NULL);
    }
    *(m_pPosition) += Vec;
    m_bModified = true;

    if(m_paObjets)
    for (vector<C3DObject*>::iterator itr = m_paObjets->begin() ; itr != m_paObjets->end() ; itr++)
        (*itr)->setModified();
}
