Como hacer juegos profesionales: Linux + OGRE + PhysX 2ª Parte

1ª Parte : Introducción
2ª Parte : Nuestra Primera aplicación

Nuestra primera aplicación

Vamos a crear 2 cubos, de manera que lo dejaremos caer, y se comportan con una física real. Al principio los tutoriales serán mucho de copy & paste, para entrar a la acción rapidamente, despues se ira explicando más cada cosa.

Empezamos a escribir código

  • Creamos una carpeta para empezar nuestro proyecto : mkdir -p ~/proyectos/juegosProfesionales
  • Nos cambiamos a nuestro nuevo directorio de trabajo : cd ~/proyectos/juegosProfesionales
  • Creamos un main.hpp : gedit main.hpp main.cpp
  • En el main.hpp pegamos esto :

    #ifndef _MAIN_HPP
    #define _MAIN_HPP
    
    #include "CAplicacion.hpp"
    #include <Ogre.h>
    int main(int argc, char *argv[]);
    
    #endif
  • En el main.cpp pegamos esto:
    #include "main.hpp"
    
    using namespace std;
    
    int main(int argc, char **argv)
    {
        CAplicacion app;
        try
        {
            app.go();
        }
        catch( Exception & e )
        {
            std::cerr << "Excepción: " << e.getFullDescription();
        }
        return 0;
    }
  • Hasta aqui, simplemente es un main que instancia el objeto CAplicacion y lanza su metodo público go(). Toda aplicacion OGRE tiene un objeto que representa la Aplicacion y un Escuchador(Listener), crearemos la clase CAplicacion, ahi vamos a definir todo sobre nuestra aplicación, y crearemos la clase CFrameListener que tendra eventos de teclado y raton por ejemplo, ambas heredaran de clases base ya escritos en el SDK de ogre, pero estos includes no forman parte del SDK de OGRE, sino que son usados para los ejemplos, asi debemos copiar 2 ficheros a nuestra carpeta ~/proyectos/juegosProfesionales, estos son ExampleApplication.h y ExampleFrameListener.h.
    cp $INSTALACION_OGRE/Samples/Common/include/ExampleApplication.h ~/proyectos/juegosProfesionales
    cp $INSTALACION_OGRE/Samples/Common/include/ExampleFrameListener.h ~/proyectos/juegosProfesionales
    Sustituir $INSTALACION_OGRE por la ruta donde tengais OGRE instalado.
    Debereis ir familiarizando con el codigo de estos 2 archivos, irlos mirando aunque al principio no os entereis de nada. El go viene de ExampleApplication, toda aplicación de OGRE comienza con un go().
  • Creamos estas 2 clases que toda aplicación ogre tiene: gedit CAplicacion.cpp CAplicacion.hpp CFrameListener.cpp CFrameListener.hpp
  • Pasteamos en CAplicacion.cpp, aqui esta la posicion de nuestra camara y los 2 cubos con fisica:
    #include "CAplicacion.hpp"
    
    /*
    Inicializamos los atributos estaticos
    */
    CAplicacion * CAplicacion::singleton=NULL;
    
    CAplicacion::CAplicacion()
    {
    	singleton = this;
    
    	//PhysX
    	NxPhysicsSDKDesc desc;
    	NxSDKCreateError errorCode = NXCE_NO_ERROR;
    	gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, NULL, new ErrorStream() , desc, &errorCode);
    	if(gPhysicsSDK == NULL)
    	{
    		cerr << "Tienes el PhysX v2.8.1 ?" << endl;
    		salir(0);
    	}
    
    	gPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.01f);
    	//gPhysicsSDK->setParameter(NX_ADAPTIVE_FORCE, 0);
    
    	// Create a scene
    	NxSceneDesc sceneDesc;
    	//sceneDesc.flags |= NX_SF_SIMULATE_SEPARATE_THREAD;
    	//sceneDesc.flags |= NX_SF_ENABLE_ACTIVETRANSFORMS;
    	sceneDesc.gravity = NxVec3(0.0f, -98.8f, 0.0f);
    
    	gScene = gPhysicsSDK->createScene(sceneDesc);
    	if(gScene == NULL)
    	{
    		cerr << "No se crear la escena" << endl;
    		salir(1);
    	}
    
    	// Establecer material por defecto
    	NxMaterial * defectoMaterial = gScene->getMaterialFromIndex(0);
    	defectoMaterial->setRestitution(0.5f);
    	defectoMaterial->setStaticFriction(1.0f);
    	defectoMaterial->setDynamicFriction(0.5f);
    }   
    
    CAplicacion::~CAplicacion()
    {
    	salir( 0 );
    }
    
    void CAplicacion::salir( int retorno )
    {
    	if(gPhysicsSDK != NULL)
    	{
    		if(gScene != NULL) gPhysicsSDK->releaseScene(*gScene);
    		gScene = NULL;
    		NxReleasePhysicsSDK(gPhysicsSDK);
    		gPhysicsSDK = NULL;
    
    	}
    	exit( retorno );
    }
    
    NxPhysicsSDK * CAplicacion::getNxPhysicsSDK()
    {
    	return gPhysicsSDK;
    }
    
    NxScene * CAplicacion::getNxScene()
    {
    	return gScene;
    }
    
    SceneManager * CAplicacion::getSceneManager()
    {
    	return mSceneMgr;
    }
    
    Root * CAplicacion::getRoot()
    {
    	return mRoot;
    }
    
    RenderWindow * CAplicacion::getRenderWindow()
    {
    	return mWindow;
    }
    
    Camera * CAplicacion::getCamera()
    {
    	return mCamera;
    }
    
    void CAplicacion::createScene(void)
    {
        CEntidad * cubo1 = new CEntidad(Vector3(0,300,0));
        cubo1->anadirFormaCubo("cubo1" , MaderaPocoDensa , 200,200,200);
    
        CEntidad * cubo2 = new CEntidad(Vector3(50,600,50));
        cubo2->anadirFormaCubo("cubo2" , MaderaPocoDensa , 200,200,200);
    
        CEntidad * suelo1 = new CEntidad(Vector3(-1500,0,-1000));
        suelo1->anadirFormaSuelo("suelo1", MaderaPocoDensa , 3000,3000);
    }
    
    void CAplicacion::createCamera(void)
    {
    	mCamera = mSceneMgr->createCamera("CamaraPrincipal");
    	mCamera->setPosition(Vector3(20,200,500));
    	mCamera->lookAt(Vector3(0,0,-300));
    	mCamera->setNearClipDistance(5);
    }
    
    // Create new frame listener
    void CAplicacion::createFrameListener(void)
    {
        mFrameListener= new CFrameListener();
        mRoot->addFrameListener(mFrameListener);
    }
    
    CAplicacion * CAplicacion::getSingleton()
    {
    	return singleton;
    }
  • En CAplicacion.hpp pasteamos:
    #ifndef CAPLICACION_hpp
    #define CAPLICACION_hpp
    
    class ExampleApplication;
    class CFrameListener;
    class CEntidad;
    class ErrorStream;
    
    #include "ExampleApplication.h"
    #include "CFrameListener.hpp"
    #include "CEntidad.hpp"
    #include "ErrorStream.h"
    
    #include <Ogre.h>
    #include <NxPhysics.h>
    
    using namespace std;
    using namespace Ogre;
    
    /*
    * CAplicacion hereda de ExampleApplication para añadir una caracteristica a la aplicación,
    * esta caracteristica será fisica mediante PhysX
    */
    
    class CAplicacion : public ExampleApplication
    {
    public:
    	CAplicacion();
    	virtual ~CAplicacion();
    
    	//Singletones de PhysX
    	NxPhysicsSDK * getNxPhysicsSDK();
    	NxScene * getNxScene();
    
    	//Singletones de OGRE
    	SceneManager * getSceneManager();
    	Root * getRoot();
    	RenderWindow * getRenderWindow();
    	Camera * getCamera();
    
    	void salir(int retorno);
    
    	static CAplicacion * getSingleton();
    
    protected:
    
    	virtual void createScene(void);//override
    	virtual void createCamera(void);//override
    	virtual void createFrameListener(void);//override
    
    private:
    
    	NxScene * gScene;
    	NxPhysicsSDK * gPhysicsSDK;
    	static CAplicacion * singleton;
    };
    
    #endif
  • Esto es un listener con física, CFrameListener.cpp:
    #include "CFrameListener.hpp"
    
    float CFrameListener::dt = 1000/MAX_FPS;
    CFrameListener * CFrameListener::singleton=NULL;
    
    CFrameListener::CFrameListener():ExampleFrameListener(CAplicacion::getSingleton()->getRenderWindow() , CAplicacion::getSingleton()->getCamera())
    {
    	singleton = this;
    
    	this->errorFisica=0;
    	this->velocidadFisica = 2;
    	this->mediaFPS = MAX_FPS / velocidadFisica;
    	this->numPasos = mediaFPS / 15.0f; //Cada 15 fps un paso
    	this->tiempoPaso = 1.0f/mediaFPS;
    
    	CAplicacion::getSingleton()->getNxScene()->setTiming(tiempoPaso / numPasos, numPasos, NX_TIMESTEP_FIXED);
    
    	MaterialManager::getSingleton().setDefaultTextureFiltering(TFO_ANISOTROPIC);
    	MaterialManager::getSingleton().setDefaultAnisotropy(8);
    }
    
    CFrameListener::~CFrameListener()
    {
    
    }
    
    bool CFrameListener::frameStarted(const FrameEvent& evt)
    {
    	//Empezamos a contar
    	empiezaFrame = SDL_GetTicks();
    
    	//Llamamos al padre, por tante no es un override realmente
    	if (!ExampleFrameListener::frameStarted( evt )) return false;
    
    	//Empieza fisica
    	if(!errorFisica)
    	{
    		CAplicacion::getSingleton()->getNxScene()->simulate(tiempoPaso);
    		CAplicacion::getSingleton()->getNxScene()->flushStream();
    	}
    
    	return true;
    }
    
    bool CFrameListener::frameEnded(const FrameEvent& evt)
    {
    	//Fin fisica
    	CAplicacion::getSingleton()->getNxScene()->fetchResults(NX_ALL_FINISHED, true , &errorFisica);
    	if(errorFisica) cout << "No dio tiempo a calcular la fisica en este frame, se hara en el siguiente" << endl;
    
    	refrescarTodo(evt.timeSinceLastFrame);
    
    	updateStats();
    	terminaFrame = SDL_GetTicks();
    	dt = terminaFrame - empiezaFrame;
    	SDL_Delay(max((1000/MAX_FPS) - dt , 0.0f));
    	terminaFrame = SDL_GetTicks();
    	dt = terminaFrame - empiezaFrame;
    	return true;
    }
    
    void CFrameListener::refrescarTodo(Real tiempoDesdeUltimoFrame)
    {
    	/*
    	 * Sincronizamos el render y la fisica
    	 */
    	int nbActors = CAplicacion::getSingleton()->getNxScene()->getNbActors();
    	NxActor** actors = CAplicacion::getSingleton()->getNxScene()->getActors();
    	while(nbActors--)
    	{
    		NxActor * actor = *actors++;
    		if(actor->userData != NULL)
    		{
    			CEntidad * entidad = (CEntidad *)actor->userData;
    			if(entidad != NULL && entidad->getActor()->isDynamic())
    			{
    				entidad->refrescar(tiempoDesdeUltimoFrame);
    			}
    		}
    	}
    }
    
    CFrameListener * CFrameListener::getSingleton()
    {
    	return singleton;
    }
  • En CFrameListener.hpp pasteamos:
    #ifndef CFRAMELISTENER_HPP_
    #define CFRAMELISTENER_HPP_
    
    class CAplicacion;
    class ExampleFrameListener;
    
    #include "CAplicacion.hpp"
    #include "ExampleFrameListener.h"
    
    #include <SDL.h>
    #include <Ogre.h>
    #include <NxPhysics.h>
    
    const int MAX_FPS = 60;
    
    class CFrameListener : public ExampleFrameListener
    {
    public:
    	CFrameListener();
    	virtual ~CFrameListener();
    	virtual bool frameStarted(const FrameEvent& evt);//override
    	virtual bool frameEnded(const FrameEvent& evt);//override
    	void refrescarTodo(Real tiempoDesdeUltimoFrame);
    	static CFrameListener * getSingleton();
    
    public:
    	//Incremento de tiempo del frame actual
    	static float dt;
    
    private:
    
    	NxU32 errorFisica;
    	NxReal mediaFPS;
    	NxReal tiempoPaso;
    	NxReal numPasos;
    	NxReal velocidadFisica;
    	double empiezaFrame , terminaFrame;
    
    	static CFrameListener * singleton;
    
    };
    
    #endif
  • Si observais en CAplicacion aparece la clase CEntidad, la idea de esta es crear entidades, que abstraiga la creación de entidades en OGRE y PhysX, además de la sincronización. Sincronización porque PhysX le dice a OGRE la posicion y orientacion de las entidades. En el siguiente código de CEntidad, teneis más código del necesario para crear cubos o suelos, además podreis crear esferas, capsulas, planos y lo mas importante Mesh (traidas de blender, 3d studio max …). Creamos los ficheros gedit CEntidad.cpp CEntidad.hpp:
  • Pasteamos en CEntidad.cpp:
    #include "CEntidad.hpp"
    
    CEntidad::CEntidad()
    {
    	this->posActor = Vector3(0,0,0);
    	postConstruccionComun();
    }
    
    CEntidad::CEntidad(Vector3 posActor)
    {
    	this->posActor = posActor;
    	postConstruccionComun();
    }
    
    void CEntidad::postConstruccionComun()
    {
    	this->raizSceneNode = CAplicacion::getSingleton()->getSceneManager()->getRootSceneNode()->createChildSceneNode();
    	this->raizSceneNode->translate(posActor.x , posActor.y , posActor.z);
    	this->hijoSceneNode = NULL;
    	this->actorFisica = NULL;
    	tmpTranslacion.zero();
    }
    
    CEntidad::~CEntidad()
    {
    
    }
    
    void CEntidad::anadirFormaCubo(string nombreForma , DENSIDAD densidad , float ancho , float alto , float fondo)
    {
    	NxBodyDesc bodyDesc;
    	bodyDesc.angularDamping	= 0.0f;
    	bodyDesc.linearDamping = 0.0f;
    
    	NxBoxShapeDesc cuboDesc;
    	cuboDesc.dimensions = NxVec3(ancho/2, alto/2, fondo/2);//Se divide por que dimensiones se miden desde el centro
    	cuboDesc.localPose.t = NxVec3(0,alto/2,0);//Para dejar el cubo en el suelo
    	cuboDesc.density = densidad;
    
    	NxActorDesc actorDesc;
    	actorDesc.shapes.pushBack(&cuboDesc);
    	actorDesc.body		= &bodyDesc;
    	actorDesc.density = densidad;
    	actorDesc.globalPose.t  = CUtiles::toNxVec3(posActor);
    
    	actorFisica = CAplicacion::getSingleton()->getNxScene()->createActor(actorDesc);
    	postAnadirFormaComun();
    
    	//Render
    	hijoSceneNode = raizSceneNode->createChildSceneNode(nombreForma);
    	hijoSceneNode->translate(0,alto/2,0);
    	hijoSceneNode->scale(ancho/100 , alto/100 , fondo/100);
    	ultimaEntidad = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma , CAplicacion::getSingleton()->getSceneManager()->PT_CUBE);
    	//ultimaEntidad->setMaterialName("Ogre/Compositor/BrightPass2/");
    	ultimaEntidad->setMaterialName("Examples/RustySteel");
    	hijoSceneNode->attachObject( ultimaEntidad );
    }
    
    void CEntidad::anadirFormaEsfera(string nombreForma , DENSIDAD densidad , float radio)
    {
    	//Fisica
    	NxBodyDesc bodyDesc;
    	bodyDesc.angularDamping	= 0.0f;
    	bodyDesc.linearDamping = 0.0f;
    
    	NxSphereShapeDesc esferaDesc;
    	esferaDesc.radius		= radio;
    	esferaDesc.density = densidad;
    	esferaDesc.localPose.t = NxVec3(0,radio,0);
    
    	NxActorDesc actorDesc;
    	actorDesc.shapes.pushBack(&esferaDesc);
    	actorDesc.body		= &bodyDesc;
    	actorDesc.density		= densidad;
    	actorDesc.globalPose.t  = CUtiles::toNxVec3(posActor);
    
    	actorFisica = CAplicacion::getSingleton()->getNxScene()->createActor(actorDesc);
    	postAnadirFormaComun();
    
    	//Render
    	hijoSceneNode = raizSceneNode->createChildSceneNode(nombreForma);
    	hijoSceneNode->translate(0,radio,0);
    	hijoSceneNode->scale(radio/45 , radio/45 , radio/45);
    	ultimaEntidad = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma , CAplicacion::getSingleton()->getSceneManager()->PT_SPHERE);
    	ultimaEntidad->setMaterialName("Material.001/SOLID");
    	ultimaEntidad->setNormaliseNormals(true);
    	hijoSceneNode->attachObject( ultimaEntidad );
    }
    
    void CEntidad::anadirFormaMesh(string nombreForma , string rutaMesh , Quaternion orientacion , NxVec3 escalaRender , DENSIDAD densidad)
    {
    	//Render
    	hijoSceneNode = raizSceneNode->createChildSceneNode(nombreForma);
    	hijoSceneNode->scale(escalaRender.x , escalaRender.y , escalaRender.z);
    	hijoSceneNode->setOrientation(orientacion);
    	//hijoSceneNode->showBoundingBox(true);
    	ultimaEntidad = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma ,  rutaMesh);
    	float ancho = ultimaEntidad->getBoundingBox().getSize().x * escalaRender.x;
    	float alto = ultimaEntidad->getBoundingBox().getSize().y * escalaRender.y;
    	float fondo = ultimaEntidad->getBoundingBox().getSize().z * escalaRender.z;
    	ultimaEntidad->setNormaliseNormals(true);
    	hijoSceneNode->attachObject( ultimaEntidad );
    
    	//Fisica
    	NxBodyDesc bodyDesc;
    	bodyDesc.angularDamping	= 0.0f;
    	bodyDesc.linearDamping = 0.0f;
    
    	NxBoxShapeDesc cuboDesc;
    	cuboDesc.dimensions = NxVec3(ancho/2, alto/2, fondo/2);//Se divide por que dimensiones se miden desde el centro
    	cuboDesc.localPose.t = NxVec3(0,alto/2,0);//Para dejar el cubo en el suelo
    	cuboDesc.density = densidad;
    
    	NxActorDesc actorDesc;
    	actorDesc.shapes.pushBack(&cuboDesc);
    	actorDesc.density = densidad;
    	actorDesc.body		= &bodyDesc;
    	actorDesc.globalPose.t  = CUtiles::toNxVec3(posActor);
    
    	actorFisica = CAplicacion::getSingleton()->getNxScene()->createActor(actorDesc);
    	postAnadirFormaComun();
    }
    
    void CEntidad::anadirFormaPlano(string nombreForma , DENSIDAD densidad , float ancho , float fondo)
    {
    	//Fisica
    	actorFisica = NULL;
    
    	//Render
    	hijoSceneNode = raizSceneNode->createChildSceneNode(nombreForma);
    	hijoSceneNode->scale(ancho , 1.0f , fondo);
    	Quaternion orientation;
    	orientation.FromAngleAxis(Degree(-90),Vector3::UNIT_X);
    	hijoSceneNode->setOrientation(orientation);
    	ultimaEntidad = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma , CAplicacion::getSingleton()->getSceneManager()->PT_PLANE);
    	ultimaEntidad->setMaterialName("Examples/GrassFloor");
    	hijoSceneNode->attachObject( ultimaEntidad );
    }
    
    void CEntidad::anadirFormaSuelo(string nombreForma , DENSIDAD densidad , float ancho , float fondo)
    {
    	//Fisica
    	NxPlaneShapeDesc planeDesc;
    	planeDesc.d = 0;
    
    	NxActorDesc actorDesc;
    	actorDesc.shapes.pushBack(&planeDesc);
    	actorFisica = CAplicacion::getSingleton()->getNxScene()->createActor(actorDesc);
    
    	//Render
    	Plane plane;
    	plane.normal = Vector3::UNIT_Y;
    	plane.d = 0;
    	MeshManager::getSingleton().createPlane(nombreForma, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,ancho,fondo,20,20,true,1,10,10,Vector3::UNIT_Z);
    	ultimaEntidad = CAplicacion::getSingleton()->getSceneManager()->createEntity( nombreForma,nombreForma);
    	ultimaEntidad->setMaterialName("Examples/GrassFloor");
    	hijoSceneNode = raizSceneNode->createChildSceneNode(nombreForma);
    	hijoSceneNode->translate(ancho/2,0,fondo/2);
    	hijoSceneNode->attachObject(ultimaEntidad);
    }
    
    void CEntidad::anadirFormaCapsula(string nombreForma , DENSIDAD densidad , float radio , float altura)
    {
    	//Fisica
    	NxBodyDesc bodyDesc;
    	bodyDesc.angularDamping	= 0.0f;
    	bodyDesc.linearDamping = 0.0f;
    
    	NxCapsuleShapeDesc capsuleDesc;
    	capsuleDesc.density = densidad;
    	capsuleDesc.height = altura;
    	capsuleDesc.radius = radio;
    	capsuleDesc.localPose.t = NxVec3(0,altura/2+radio,0);;
    
    	NxActorDesc actorDesc;
    	actorDesc.shapes.pushBack(&capsuleDesc);
    	actorDesc.body = &bodyDesc;
    	actorDesc.density = densidad;
    	actorDesc.globalPose.t  = CUtiles::toNxVec3(posActor);
    
    	actorFisica = CAplicacion::getSingleton()->getNxScene()->createActor(actorDesc);
    	postAnadirFormaComun();
    
    	//Render
    	SceneNode * esferaArriba;
    	SceneNode * esferabajo;
    
    	esferaArriba = raizSceneNode->createChildSceneNode(nombreForma+"_esfera1");
    	esferaArriba->translate(0,altura/2+radio,0);
    	esferaArriba->translate(0,-altura/2,0);
    	esferaArriba->scale(radio/45 , radio/45 , radio/45);
    	Entity * actorRender = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma+"_esfera1" , CAplicacion::getSingleton()->getSceneManager()->PT_SPHERE);
    	actorRender->setMaterialName("Material.001/SOLID");
    	actorRender->setNormaliseNormals(true);
    	esferaArriba->attachObject( actorRender );
    
    	hijoSceneNode = raizSceneNode->createChildSceneNode(nombreForma+"_cuerpo");
    	hijoSceneNode->translate(0,altura/2+radio,0);
    	hijoSceneNode->scale(radio , altura , radio);
    	ultimaEntidad = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma+"_cuerpo" ,  "cilindro.mesh");
    	ultimaEntidad->setNormaliseNormals(true);
    	hijoSceneNode->attachObject( ultimaEntidad );
    
    	esferabajo = raizSceneNode->createChildSceneNode(nombreForma+"_esfera2");
    	esferabajo->translate(0,altura/2+radio,0);
    	esferabajo->translate(0,altura/2,0);
    	esferabajo->scale(radio/45 , radio/45 , radio/45);
    	Entity * actorRender3 = CAplicacion::getSingleton()->getSceneManager()->createEntity(nombreForma +"_esfera2", CAplicacion::getSingleton()->getSceneManager()->PT_SPHERE);
    	actorRender3->setMaterialName("Material.001/SOLID");
    	actorRender3->setNormaliseNormals(true);
    	esferabajo->attachObject( actorRender3 );
    }
    
    void CEntidad::postAnadirFormaComun()
    {
    	actorFisica->userData = (void *)this;
    }
    
    void CEntidad::quitarSombras()
    {
    	getUltimaEntidad()->setCastShadows( false );
    }
    
    void CEntidad::reportarContacto(NxContactPairFlag flags)
    {
    	getActor()->setContactReportFlags(flags);
    	getActor()->setContactReportThreshold(100);
    }
    
    void CEntidad::refrescar(Real tiempoDesdeUltimoFrame)
    {
    	//Aplicar vector translacion
    	if(actorFisica->readBodyFlag(NX_BF_KINEMATIC) && !tmpTranslacion.isZero())
    	{
    		actorFisica->moveGlobalPosition( actorFisica->getGlobalPosition() + actorFisica->getGlobalOrientation() * tmpTranslacion);
    		tmpTranslacion.zero();
    	}
    	//PhysX le dice a OGRE la posicion y orientacion  de la entidad
    	getRaizSceneNode()->setPosition(CUtiles::toVector3(actorFisica->getGlobalPosition()));
    	getRaizSceneNode()->setOrientation(CUtiles::toQuaternion(actorFisica->getGlobalOrientationQuat()));
    }
    
    SceneNode * CEntidad::getHijoSceneNode()
    {
    	return hijoSceneNode;
    }
    
    SceneNode * CEntidad::getRaizSceneNode()
    {
    	return raizSceneNode;
    }
    
    void CEntidad::sinFisica()
    {
    	if(actorFisica!=NULL)
    	{
    		actorFisica->raiseBodyFlag(NX_BF_KINEMATIC);
    	}
    }
    
    void CEntidad::conFisica()
    {
    	if(actorFisica!=NULL)
    	{
    		actorFisica->clearBodyFlag(NX_BF_KINEMATIC);
    	}
    }
    
    void CEntidad::noPuedeVolcar()
    {
        getActor()->raiseBodyFlag(NX_BF_FROZEN_ROT_X);
        getActor()->raiseBodyFlag(NX_BF_FROZEN_ROT_Z);
    }
    
    NxActor * CEntidad::getActor()
    {
    	return actorFisica;
    }
    
    Entity * CEntidad::getUltimaEntidad()
    {
    	return ultimaEntidad;
    }
    
    /*
    NX_FORCE,                   //!< parameter has unit of mass * distance/ time^2, i.e. a force
    NX_IMPULSE,                 //!< parameter has unit of mass * distance /time
    NX_VELOCITY_CHANGE,	//!< parameter has unit of distance / time, i.e. the effect is mass independent: a velocity change.
    NX_SMOOTH_IMPULSE,          //!< same as NX_IMPULSE but the effect is applied over all substeps. Use this for motion controllers that repeatedly apply an impulse.
    NX_SMOOTH_VELOCITY_CHANGE,	//!< same as NX_VELOCITY_CHANGE but the effect is applied over all substeps. Use this for motion controllers that repeatedly apply an impulse.
    NX_ACCELERATION				//!addLocalForce(f * CFrameListener::dt , tipoFuerza);
    */
    
    void CEntidad::anadirTorsion(NxVec3 f , NxForceMode tipoFuerza)
    {
    	actorFisica->addLocalTorque(f * CFrameListener::dt , tipoFuerza);
    }
    
    void CEntidad::setAcelerar(float paso)
    {
    	tmpTranslacion += NxVec3(0,0,-paso);
    }
    
    void CEntidad::setFrenar(float paso)
    {
    	tmpTranslacion += NxVec3(0,0,paso);
    }
    
    void CEntidad::setSubir(float paso)
    {
    	tmpTranslacion += NxVec3(0,paso,0);
    }
    
    void CEntidad::setBajar(float paso)
    {
    	tmpTranslacion += NxVec3(0,-paso,0);
    }
    
    void CEntidad::setEstrafearDerecha(float paso)
    {
    	tmpTranslacion += NxVec3(paso,0,0);
    }
    
    void CEntidad::setEstrafearIzquierda(float paso)
    {
    	tmpTranslacion += NxVec3(-paso,0,0);
    }
    
    void CEntidad::setGiroYawAbsoluto(float radianes)
    {
    	NxMat33 orientacion( NX_IDENTITY_MATRIX );
    	NxMat33 rotacionLocalY( NX_IDENTITY_MATRIX );
    
    	rotacionLocalY.rotY( radianes );
    	orientacion.multiply(orientacion , rotacionLocalY);
    
    	actorFisica->setGlobalOrientation(orientacion);
    }
    
    void CEntidad::setGirarDerecha(float paso)
    {
    	NxMat33 orientacion = actorFisica->getGlobalOrientation();
    	NxMat33 rotacionLocalY( NX_IDENTITY_MATRIX );
    
    	rotacionLocalY.rotY( NxMath::degToRad(-paso) );
    	orientacion.multiply(orientacion , rotacionLocalY);
    
    	actorFisica->setGlobalOrientation(orientacion);
    }
    
    void CEntidad::setGirarIzquierda(float paso)
    {
    	NxMat33 orientacion = actorFisica->getGlobalOrientation();
    	NxMat33 rotacionLocalY( NX_IDENTITY_MATRIX );
    
    	rotacionLocalY.rotY( NxMath::degToRad(paso) );
    	orientacion.multiply(orientacion , rotacionLocalY);
    
    	actorFisica->setGlobalOrientation(orientacion);
    }
    
    void CEntidad::setMirarArriba(float paso)
    {
    	NxMat33 orientacion = actorFisica->getGlobalOrientation();
    	NxMat33 rotacionLocalX( NX_IDENTITY_MATRIX );
    
    	rotacionLocalX.rotX( NxMath::degToRad(paso) );
    	orientacion.multiply(orientacion , rotacionLocalX);
    
    	actorFisica->setGlobalOrientation(orientacion);
    }
    
    void CEntidad::setMirarAbajo(float paso)
    {
    	NxMat33 orientacion = actorFisica->getGlobalOrientation();
    	NxMat33 rotacionLocalX( NX_IDENTITY_MATRIX );
    
    	rotacionLocalX.rotX( NxMath::degToRad(-paso) );
    	orientacion.multiply(orientacion , rotacionLocalX);
    
    	actorFisica->setGlobalOrientation(orientacion);
    }
    
    void CEntidad::setInclinarMiradaDerecha(float paso)
    {
    	NxMat33 orientacion = actorFisica->getGlobalOrientation();
    	NxMat33 rotacionLocalZ( NX_IDENTITY_MATRIX );
    
    	rotacionLocalZ.rotZ( NxMath::degToRad(-paso) );
    	orientacion.multiply(orientacion , rotacionLocalZ);
    
    	actorFisica->setGlobalOrientation(orientacion);
    }
    
    void CEntidad::setInclinarMiradaIzquierda(float paso)
    {
    	NxMat33 orientacion = actorFisica->getGlobalOrientation();
    	NxMat33 rotacionLocalZ( NX_IDENTITY_MATRIX );
    
    	rotacionLocalZ.rotZ( NxMath::degToRad(-paso) );
    	orientacion.multiply(orientacion , rotacionLocalZ);
    
    	actorFisica->setGlobalOrientation(orientacion);
    }
    
    void CEntidad::setPlano(float x , float z)
    {
    	setX(x);
    	setZ(z);
    }
    
    void CEntidad::setAltura(float y)
    {
    	setY(y);
    }
    
    float CEntidad::getX()
    {
    	return actorFisica->getGlobalPosition().x;
    }
    
    float CEntidad::getY()
    {
    	return actorFisica->getGlobalPosition().y;
    }
    
    float CEntidad::getZ()
    {
    	return actorFisica->getGlobalPosition().z;
    }
    
    NxVec3 CEntidad::getPos()
    {
    	//return matriz.GetTranslate();
    	return actorFisica->getGlobalPosition();
    }
    
    void CEntidad::setX(float x)
    {
    	NxVec3 nuevaPos = actorFisica->getGlobalPosition();
    	nuevaPos.x = x;
    	actorFisica->setGlobalPosition(nuevaPos);
    }
    
    void CEntidad::setY(float y)
    {
    	NxVec3 nuevaPos = actorFisica->getGlobalPosition();
    	nuevaPos.y = y;
    	actorFisica->setGlobalPosition(nuevaPos);
    }
    
    void CEntidad::setZ(float z)
    {
    	NxVec3 nuevaPos = actorFisica->getGlobalPosition();
    	nuevaPos.z = z;
    	actorFisica->setGlobalPosition(nuevaPos);
    }
    
    string CEntidad::getNombre()
    {
    	return getHijoSceneNode()->getName();
    }
  • Pastear en CEntidad.hpp:
    #ifndef CEntidad_hpp
    #define CEntidad_hpp
    
    class CAplicacion;
    class CUtiles;
    
    #include "CAplicacion.hpp"
    #include "CUtiles.hpp"
    
    #include <Ogre.h>
    #include <NxPhysics.h>
    
    using namespace std;
    using namespace Ogre;
    
    //Unidad : KG/m3
    //http://www.kalipedia.com/fisica-quimica/tema/estructura-materia/tabla-densidades-25-%B0c.html?x1=20070924klpcnafyq_22.Kes&x=20070924klpcnafyq_25.Kes
    enum DENSIDAD
    {
    	//Solidos
    	Aluminio = 2700,
    	Corcho = 250,
    	Cobre = 8960,
    	Hielo = 920,
    	Hierro = 7900,
    	MaderaPocoDensa = 200,
    	MaderaDensidadMedia = 500,
    	MaderaMuyDensa = 800,
    	Plomo = 11300 ,
    	VidrioPocoDenso = 3000,
    	VidrioMuyDenso = 3600,
    
    	//Liquidos
    	Acetona = 790 ,
    	Aceite = 920 ,
    	AguaDeMar = 1025 ,
    	AguaDestilada = 1000 ,
    	AlcoholEtilico = 790 ,
    	Gasolina = 680 ,
    	Leche = 1030 ,
    	Mercurio = 13600,
    
    	//Gas
    	Aire = 1,
    	Butano = 3
    };
    
    enum GRUPOS_DE_COLISION
    {
    	DEFECTO,
    	CAMARA,
    	TERRENO
    };
    
    class CEntidad
    {
    public:
    
    	//En 0,0,0
    	CEntidad();
    
    	//Constructor forma cubo
    	CEntidad(Vector3 posActor);
    
    	//Destructor
    	virtual ~CEntidad();
    
    	void anadirFormaCubo(string nombreForma , DENSIDAD densidad , float ancho , float alto , float fondo);
    	void anadirFormaEsfera(string nombreForma , DENSIDAD densidad , float radio);
    	void anadirFormaMesh(string nombreForma , string rutaMesh , Quaternion orientacion , NxVec3 escalaRender , DENSIDAD densidad);
    	void anadirFormaPlano(string nombreForma , DENSIDAD densidad , float ancho , float fondo);
    	void anadirFormaSuelo(string nombreForma , DENSIDAD densidad , float ancho , float fondo);
    	void anadirFormaCapsula(string nombreForma , DENSIDAD densidad , float radio , float altura);
    
    	void quitarSombras();
    
    	void reportarContacto(NxContactPairFlag flags);
    
    	virtual void refrescar(Real tiempoDesdeUltimoFrame = 0);
    
    	//Modifican la entidad por simulacion
    	void anadirFuerza(NxVec3 fuerza , NxForceMode tipoFuerza);
    	void anadirTorsion(NxVec3 fuerza , NxForceMode tipoFuerza);
    
    	//Movimeintos por fuerzas
    	void setFuerzaAcelerar(float paso , NxForceMode tipoFuerza){anadirFuerza(NxVec3(0,0,-paso),tipoFuerza);}
    	void setFuerzaFrenar(float paso , NxForceMode tipoFuerza){anadirFuerza(NxVec3(0,0,paso),tipoFuerza);}
    	void setFuerzaSubir(float paso , NxForceMode tipoFuerza){anadirFuerza(NxVec3(0,paso,0),tipoFuerza);}
    	void setFuerzaBajar(float paso , NxForceMode tipoFuerza){anadirFuerza(NxVec3(0,-paso,0),tipoFuerza);}
    	void setFuerzaEstrafearDerecha(float paso , NxForceMode tipoFuerza){anadirFuerza(NxVec3(paso,0,0),tipoFuerza);}
    	void setFuerzaEstrafearIzquierda(float paso , NxForceMode tipoFuerza){anadirFuerza(NxVec3(-paso,0,0),tipoFuerza);}
    
    	//Giros por fuerzas
    	void setTorsionGirarDerecha(float paso , NxForceMode tipoFuerza){anadirTorsion(NxVec3(0,0,-paso),tipoFuerza);}
    	void setTorsionGirarIzquierda(float paso , NxForceMode tipoFuerza){anadirTorsion(NxVec3(0,0,paso),tipoFuerza);}
    	void setTorsionMirarArriba(float paso , NxForceMode tipoFuerza){anadirTorsion(NxVec3(0,paso,0),tipoFuerza);}
    	void setTorsionMirarAbajo(float paso , NxForceMode tipoFuerza){anadirTorsion(NxVec3(0,-paso,0),tipoFuerza);}
    	void setTorsionInclinarMiradaDerecha(float paso , NxForceMode tipoFuerza){anadirTorsion(NxVec3(paso,0,0),tipoFuerza);}
    	void setTorsionInclinarMiradaIzquierda(float paso , NxForceMode tipoFuerza){anadirTorsion(NxVec3(-paso,0,0),tipoFuerza);}
    
    	//Modifican la entidad instantaneamente y siendo ABSOLUTO
    	void setPlano(float x , float z);
    	void setAltura(float y);
    
    	//Modifican la entidad instantaneamente y siendo RELATIVO
    	void setAcelerar(float paso);
    	void setFrenar(float paso);
    	void setSubir(float paso);
    	void setBajar(float paso);
    	void setEstrafearDerecha(float paso);
    	void setEstrafearIzquierda(float paso);
    
    	//Giros instantaneos
    	void setGiroYawAbsoluto(float radianes);
    	void setGirarDerecha(float paso);
    	void setGirarIzquierda(float paso);
    	void setMirarArriba(float paso);
    	void setMirarAbajo(float paso);
    	void setInclinarMiradaDerecha(float paso);
    	void setInclinarMiradaIzquierda(float paso);
    
    	void sinFisica();
    	void conFisica();
    	void noPuedeVolcar();
    
    	NxActor * getActor();
    	SceneNode * getRaizSceneNode();
    	SceneNode * getHijoSceneNode();
    	Entity * getUltimaEntidad();
    
    	float getX();
    	float getY();
    	float getZ();
    	NxVec3 getPos(void);
    	string getNombre();
    
    private:
    
    	void postConstruccionComun();
    	void postAnadirFormaComun();
    
    	void setX(float x);
    	void setY(float y);
    	void setZ(float z);
    
    protected:
    
    	SceneNode * raizSceneNode;
    	SceneNode * hijoSceneNode;
    	NxActor * actorFisica;
    	Vector3 posActor;
    	Entity * ultimaEntidad;
    	NxVec3 tmpTranslacion;
    
    };
    
    #endif
  • Surgen 2 clases más por dependencia, toda aplicacion de PhysX, necesita una clase para reportar errores, como en los ejemplos que vienen con PhysX la llamaremos ErrorStream. Ademas surge otro problema PhysX usa Vectores llamados NxVec3, mientras que Ogre es de tipo Vector3, existe un paralelismo con varios tipos de datos, como los cuaterniones, PhysX lo llamo NxQuat , mientras que ogre lo llama Quaternion, crearemos una clase con metodos estaticos para realizar conversiones con todos estos tipos. Empezemos escribimos: gedit ErrorStream.h CUtiles.cpp CUtiles.hpp
  • En ErrorStream.h escribimos :
    #ifndef ERRORSTREAM_H
    #define ERRORSTREAM_H
    #ifdef WIN32
    #define NOMINMAX
    #include <windows.h>
    #endif
    
    class ErrorStream : public NxUserOutputStream
    {
    
    	public:
    
    	void reportError(NxErrorCode e, const char* message, const char* file, int line)
    	{
    
    		printf("%s (%d) :", file, line);
    		switch (e)
    		{
    			case NXE_INVALID_PARAMETER:
    				printf( "invalid parameter");
    				break;
    			case NXE_INVALID_OPERATION:
    				printf( "invalid operation");
    				break;
    			case NXE_OUT_OF_MEMORY:
    				printf( "out of memory");
    				break;
    			case NXE_DB_INFO:
    				printf( "info");
    				break;
    			case NXE_DB_WARNING:
    				printf( "warning");
    				break;
    			default:
    				printf("unknown error");
    			}
    
    			printf(" : %s\n", message);
    		}
    
    	NxAssertResponse reportAssertViolation(const char* message, const char* file, int line)
    	{
    		printf("access violation : %s (%s line %d)\n", message, file, line);
    		assert(0);
    	}
    
    	void print(const char* message)
    	{
    		printf(message);
    	}
    };
    
    #endif
  • En CUtiles.cpp ponemos:
    #include "CUtiles.hpp"
    
    CUtiles::CUtiles()
    {
    }
    
    int CUtiles::str2int (const string &str)
    {
    	stringstream ss(str);
    	int n;
    	ss >> n;
    	return n;
    }
    
    string CUtiles::int2str (int n)
    {
    	stringstream ss;
    	ss << n;
    	return ss.str();
    }
    
    NxVec3 CUtiles::esferaPolar(float radio,float pitch,float yaw)
    {
    	NxVec3 temp;
    	temp.x=radio*sinf(pitch)*sinf(yaw);
    	temp.y=radio*cosf(pitch);
    	temp.z=radio*sinf(pitch)*cosf(yaw);
    	return temp;
    }
    
    NxVec3 CUtiles::toNxVec3(Vector3 vec3)
    {
    	NxVec3 temp;
    	temp.x = vec3.x;
    	temp.y = vec3.y;
    	temp.z = vec3.z;
    	return temp;
    }
    
    Vector3 CUtiles::toVector3(NxVec3 vec3)
    {
    	Vector3 temp;
    	temp.x = vec3.x;
    	temp.y = vec3.y;
    	temp.z = vec3.z;
    	return temp;
    }
    
    NxQuat CUtiles::toNxQuat(Quaternion q)
    {
    	NxQuat orientacion;
    	orientacion.x = q.x;
    	orientacion.y = q.y;
    	orientacion.z = q.z;
    	orientacion.w = q.w;
    	return orientacion;
    }
    
    Quaternion CUtiles::toQuaternion(NxQuat q)
    {
    	Quaternion orientacion;
    	orientacion.x = q.x;
    	orientacion.y = q.y;
    	orientacion.z = q.z;
    	orientacion.w = q.w;
    	return orientacion;
    }
    
    Quaternion CUtiles::yawToQuaternion(float yaw)
    {
    	Quaternion orientacion;
    	orientacion.FromAngleAxis(Degree(yaw),Vector3::UNIT_Y);
    	return orientacion;
    }
  • En CUtiles.hpp ponemos:
    #ifndef _CUTILES_HPP
    #define _CUTILES_HPP
    
    class CAplicacion;
    
    #include "CAplicacion.hpp"
    
    #include <string>
    #include <sstream>
    #include <NxPhysics.h>
    #include <Ogre.h>
    
    const float PI = M_PIl;
    const float TO_RAD = PI / 180.0f;
    const float TO_DEG = 180.0f / PI;
    
    using namespace std;
    using namespace Ogre;
    
    class CUtiles
    {
    public:
    	//Constructor de utiles (aunque curiosamente es inútil)
    	CUtiles();
    
    	static int str2int (const string &str);
    
    	static string int2str (int n);
    
    	static NxVec3 esferaPolar(float radio,float pitch,float yaw);
    
    	static NxVec3 toNxVec3(Vector3 vec3);
    	static Vector3 toVector3(NxVec3 vec3);
    
    	static NxQuat toNxQuat(Quaternion q);
    	static Quaternion toQuaternion(NxQuat q);
    
    	static Quaternion yawToQuaternion(float yaw);
    };
    
    #endif

Compilando todo

Si has seguido la 1ª parte de esta serie, ya tendras instalado OGRE y PhysX. Casi todas las aplicaciones basadas en OGRE dependen de OIS para entrada/salida de teclado. Además hemos usado SDL. Antes de empezar a compilar, debemos comprobar que esta todo bien, y generar los Makefiles.

Para generar los Makefiles vamos a utilizar qmake, pero si cuando habeis leido esto ya existe la 3ª parte os recomiendo que os salteis directamente a la tercera parte, o leereslo por culturilla, en la tercera parte haré un tutorial para compilar mucho más facilmente con cmake.

  • Instalamos SDL y OIS : sudo apt-get install libsdl1.2-dev libois-dev
  • Comprobamos que teneis todos los ficheros necesarios:
    CAplicacion.cpp     CUtiles.hpp
    CAplicacion.hpp     ErrorStream.h
    CEntidad.cpp        ExampleApplication.h
    CEntidad.hpp        ExampleFrameListener.h
    CFrameListener.cpp  main.cpp
    CFrameListener.hpp  main.hpp
    CUtiles.cpp
  • Vamos a crear los 2 ultimos ficheros para poder crear la compilacion, los llamaremos generarMakefile.sh y librerias.txt. El primero genera un makefile de forma inteligente hacia un analisis de todo el codigo fuente, además el script añade al final de Makefile un “include librerias.txt”, por tanto Makefile sera automatico, y librerias.txt sera realmente nuestro Makefile. Cada vez que añadamos una nueva clase o se requiera por tanto un reanalisis de la estructura se deberá hacer un ./generarMakefile.sh en librerias.txt definimos los includes, librerias que usamos … Creamos los ficheros necesarios gedit generarMakefile.sh librerias.txt
  • En generarMakefile.sh escribimos:
    #!/bin/sh
    
    make clean
    qmake -project
    qmake -makefile
    echo include librerias.txt >> Makefile
    echo Escribe make para compilar.
    echo Escribe make run para compilar y ejecutar

    Recardar darles permisos de ejecución chmod +x generarMakefile.sh

  • En librerias.txt escribimos:
    #
    # Pon include librerias.txt al final del makefile
    #
    
    #Las siguientos 2 lineas reemplazan a las que genera qmake eliminando los warnings, si quieres warnings borralas
    CFLAGS   = -pipe -O2 -D_REENTRANT  -DQT_NO_DEBUG -DQT_THREAD_SUPPORT -DQT_SHARED -DQT_TABLET_SUPPORT
    CXXFLAGS = -pipe -O2 -D_REENTRANT  -DQT_NO_DEBUG -DQT_THREAD_SUPPORT -DQT_SHARED -DQT_TABLET_SUPPORT
    
    PHYSX_LINUX_FIX = -DLINUX -DCORELIB -DNX32 -DNX_DISABLE_FLUIDS -DNX_DISABLE_HARDWARE
    CFLAGS   += `sdl-config --cflags` `pkg-config --cflags OGRE` $(PHYSX_LINUX_FIX)
    CXXFLAGS += `sdl-config --cflags` `pkg-config --cflags OGRE` $(PHYSX_LINUX_FIX)
    INCPATH  += -I/usr/include/PhysX/v2.8.1/SDKs/Foundation/include -I/usr/include/PhysX/v2.8.1/SDKs/Physics/include -I/usr/include/PhysX/v2.8.1/SDKs/PhysXLoader/include -I/usr/include/PhysX/v2.8.1/SDKs/Cooking/include -I/usr/include/PhysX/v2.8.1/SDKs/NxCharacter/include -I/usr/local/include/OGRE
    LIBS     += `sdl-config --libs` -L/usr/lib/PhysX/v2.8.1/ -lPhysXLoader -lNxCharacter -lNxCooking `pkg-config --libs OGRE` -lOIS
    
    run: juegosProfesionales
    	./juegosProfesionales
  • Generamos los makefiles: ./generarMakefile.sh
  • Compilamos escribiendo : make, si todo te ha compilado bien enhorabuena, es normal que hayas cometido algún fallo, debido a que son muchos pasos, ves analizando los errores y trata de resolverlos por ti mismo, si no lo consigues, pon aqui un comentario y tratamos de resolverlo
  • Si teneis algún problema de linkado, poner las librerías de physX en /usr/lib mediante enlaces simbólicos mediante los siguientes comandos :
    sudo ln -s /usr/lib/PhysX/v2.8.1/libNxCharacter.so.1 /usr/lib/libNxCharacter.so.1
    sudo ln -s /usr/lib/PhysX/v2.8.1/libNxCooking.so.1 /usr/lib/libNxCooking.so.1
    sudo ln -s /usr/lib/PhysX/v2.8.1/libPhysXCore.so.1 /usr/lib/libPhysXCore.so.1
  • Para actualizar las nuevas librerias escribimsos : sudo ldconfig

Ejecutando

  • Si ejecutais vuestros cubos vais a ver que os da errores por que faltan archivos: y es así, toda aplicación debe tener al menos 3 archivos de configuración: ogre.cfg , plugins.cfg y resources.cfg. Por tanto escribimos gedit ogre.cfg plugins.cfg resources.cfg
  • En ogre.cfg pasteamos:
    Render System=OpenGL Rendering Subsystem
    
    [OpenGL Rendering Subsystem]
    FSAA=0
    Full Screen=Yes
    RTT Preferred Mode=FBO
    Video Mode=1280 x 800
  • En plugins.cfg pasteamos:
    # Directorio de plugins
    PluginFolder=/usr/local/lib/OGRE
    
    # Plugins
    Plugin=RenderSystem_GL.so
    Plugin=Plugin_ParticleFX.so
    Plugin=Plugin_BSPSceneManager.so
    Plugin=Plugin_OctreeSceneManager.so
    Plugin=Plugin_CgProgramManager.so
  • En resources.cfg pasteamos:
    [Bootstrap]
    Zip=Media/packs/OgreCore.zip
    
    [General]
    FileSystem=Media
    FileSystem=Media/fonts
    FileSystem=Media/materials/programs
    FileSystem=Media/materials/scripts
    FileSystem=Media/materials/textures
    FileSystem=Media/models
    FileSystem=Media/overlays
    FileSystem=Media/particle
    FileSystem=Media/gui
    FileSystem=Media/DeferredShadingMedia
    Zip=Media/packs/cubemap.zip
    Zip=Media/packs/cubemapsJS.zip
    Zip=Media/packs/dragon.zip
    Zip=Media/packs/fresneldemo.zip
    Zip=Media/packs/ogretestmap.zip
    Zip=Media/packs/skybox.zip
  • En este ultimo fichero, indicamos una lista con todos los recursos (texturas, materiales , etc…) que utiliza nuestra aplicación. Todos esos recursos son los ejemplos que vienen con OGRE y para que se vean son relativos al ejecutable, podemos copiarlos pero mejor haremos un link simbolico de la siguiente forma: ln -s $INSTALACION_OGRE/Samples/Media Media
    $INSTALACION_OGRE lo sustituimos por donde hayais instalado ogre.

Bueno, por fin termino, me lo repasare el post. Este post sera muy importante para este serie, en este capitulo hemos desarrollado la estructura y la metodología que usaremos en siguientes tutoriales. Se que hay pocos explicaciones, por eso si hay alguien interesado, acepto sugerencias para ir mejorando el post, en este y sucesivos.

34 comentarios

  1. thanks for ur help, but i cannot see all code (cut at left) , is there a download link? :)

  2. i need hosting. When i have then will upload code. Although you can select code hide or view source code HTML. Ctrl + U (fiefox). Sorry or wait for link

  3. Buenas, Si en el post anterior me refería a este código del tutorial 2. Ya hoy conseguí copiar el ejemplo y que arrancará en ogre. La ver lo vi por encima y me gustó como está programado, el problema es entenderlo. Le faltan unos cuantos comentarios pero se irá descubriendo poco a poco.

    Felicidades y gracias

    Un saludo

  4. hola yormanh:

    El código es 100% mio cualquier duda que tengas sobre este, te la comento. Cuando me de por escribir el proximo tuto intentare comentar más, aunque si comento será en el interfaz(.hpp) ya que escribir código en mitad de la implementación es molesto para mi.

    Actualmente he cambiado mi metodo de trabajo, gracias a cmake ya no solo me compila en linux, sino que he conseguido portarlo a windows. En linux uso como IDE eclipse+CDT y compilo con makefiles que genera cmake. Y en windows cmake tambien me genera proyectos y soluciones .sln para visual studio 2008. Se programa más rápido en eclipse sin duda, pero en windows esta el visual debugger(que es como un visor de física).

    Un saludo

  5. nosotros ahora mismo estamos programando en VS2005, y casi no tuve problemas en pasar tu código al VS. El eclipse también lo he usado, aunque solo para java, nunca para c++.

    Miraré lo que me dices. Por cierto, el ¿nxogre lo has probado?

    un saludo

  6. Desde que he empezado con el physx+ogre para mi lo más importante es conseguir la multiplataforma. Desgraciadamente physx no funciona sobre mac, cosa que tampoco me preocupa mucho, me conformo con el combo linux+windows.

    Betajaen se lo ha currado un montón con el NxOgre pero siempre le he criticado porque ni siquiera sabe usar linux (esta aprendiendo dijo en unos post de hace pocos meses), he intentado portar su código a linux y es realmente dificil, usa muchas plantillas no estandar, que lógicamente solo se acomodan al compilador del visual studio, parece ser que hay una versión vieja de nxogre portable, pero eso es vieja.
    Y a mi me gusta que cuando nvidia(ageia) sake por ejemplo el PhysX 2.9.0 inmediatamente vaya a probar las nuevas features sin tener esperar a Betajaen a que actualize para la nueva versión.
    Al fin de al cabo en este tutorial estamos programando un wrapper (como NxOgre) que junta physx con ogre, con la ventaja, al menos para mi, que lo hemos hecho nosotros y tenemos control total.
    De todas maneras nxogre es una gran opción que posiblemente al menos probare(incluso adaptaré) cuando Betajaen o alguien de la comunidad la porte a linux.

    p.d: quienes soys nosotros ? y que proyecto teneis pensado para empezar ? soy un cotilla :D

  7. La verdad es que si, en la carrera prácticamente siempre hemos trabajado y programado en linux, y la verdad es que le tengo mucho “cariño”, pero últimamente estamos en el imperio del monopolio y la verdad es que está muy bien integrado con VS el ogre.

    Nosotros, la verdad es que somos por ahora 2 personas, estamos aprendiendo poco a poco ogre, physx, nxogre (está mi compañero), llevamos unos meses pero a un ritmo muy lento, ya que entre el trabajo y los estudios y la vida uno no le dedica el tiempo que uno quisiera a esto, además que tampoco hay tutoriales o manuales “claros” de como hacer videojuegos (o no hemos encontrado).

    De todas formas mi correo es yormanh_arroba_gmail_punto_com, para ti o cualquiera si quieres que hablemos mas sobre el tema no hay problema.

    El código tuyo si vi el wrapper, la verdad que me gustó bastante, todavía estoy en proceso de asimilarlo y entenderlo pero bien, y claro por eso te decía lo de los comentarios, para entenderlo. Pero tienes razón en que es más engorroso a la hora de leer el código.

  8. La industria del juego sigue totalmente monopolizada y quería hacer estos tutoriales como forma de aportar un grano de arena ya que tambien como gamer estoy cansado de esta situación.

    Yo tambien trabajo y estudio (ahora empiezo 3º) y la verdad es que es dificil sacar tiempo para la que realmente te gusta.

    De hecho 1º lo sake limpio pero 2º he suspendio algunas simplemente por que estaba muy enviciado a esto y cosas parecidas.

    También por eso últimamente actualizo muy descontinuado, pero espero poner pronto el proximo tuto.

    Me alegro que haya españoles con las mismas motivaciones que yo, evidentemente no somos los unicos me he encontrado con bastantes en foros, pero esta claro que la documentación en español brilla por su ausencia xD

  9. En español no hay nada, pero a veces en inglés tampoco es que abunde.

    Imagino que estás estudiando ingeniería informática en alguna facultad española no?

    Tranquilo cuando puedas, y la verdad es que ahora mismo no puedo aportar mucho al tema, quizás algún día con mas experiencia.

    Un saludo

  10. sis, ITIG en Alcala de henares (Madrid), estoy peleandome con el debug viewer en windows, cuando lo entienda bien haré la tercera parte

    Repecto a lo otro, no problem, pa eso estamos, para aprender entre todos compartiendo conocimiento.

    Un saludo

  11. Si eso es cierto, aprender, que mira que es grande el tema y tiene muchas cosas la programacion de videojuegos.

    Nada, tendríamos que “vivir” en el renacimiento y poder dedicarte a “todas las artes”, porque en estos tiempos ya es dificil dedicarte a una sola.

    Un saludo

  12. para crear un juego

  13. se te corto la frase ? xD

  14. La verdad es que es bastante complicado esto de crear un juego, yo soy estudiante y me interesa mucho este tema de hecho estoy en un proyecto para crear un laboratorio virtual y creo que todo este contenido me va a ser de gran ayuda, solo necesito hacer las modificaciones para el codeblocks que es lo que uso….

  15. Es interesante eso del laboratorio virtual.
    Actualmente uso CMake y te lo recomiendo como generador de proyectos, puede generar Makefiles, o proyectos de CodeBlocks incluso proyectos de visual studio. Con CMake pierdes la dependencia de un IDE, cosa que cuando no tienes claro que IDE usar se agradece.

    La verdad que el código de este post se me ha quedado anticuado, lo he cambiado mucho y mejorado bastante. No obstante las ideas principales se mantienen.

  16. La verdad esta muy bueno eso de Physx + Ogre …ya lo probe en ambos sistemas Linux-Windows pero tengo un problema cuando creo muchos(alrededor de 20) elementos dinamicos la aplicacion se cierra(crash) si alguien sabe pq… por favor

  17. That’s a great advance, I’ve been looking for exactly that, great work!
    Any chance this can be translated into english (or even the class & variable names would be of great use in english) because I can’t speak (or understand) spanish

    Best regards, keep up the good work!

  18. Hace un mes que empecé a usar ogre, y hace un par de dias que me decanté por usar physx en mi proyecto.

    La idea es usar NxOgre (pero ambos sabemos el problema que tenemos si nos obligan a usar windows y encima visual studio). Asique gracias a ti he visto que hay una alternativa a nxogre, usar physx a pelo.

    Bien, me asaltan un montón de dudas, espero que me encamines un poco…

    1- physx es un motor de físicas, no de render, pero en los binarios que bienen con el sdk se ven escenarios. Supongo que tiene su propio render muy básico (sin sombras ni texturas) y que está preparado para funcionar por ejemplo con ogre. ¿me confundo?

    2- Creo haber visto que necesita un formato específico de mallas, ¿que pasa con los mesh de ogre? puedo seguir usando el dotscene para las escenas?. Y en este punto me pregunto, como funciona la comunicación entre ogre y physx si usan diferentes formatos de mallas ¿?¿?

    Voy a probar tu código y a estudiarlo, espero entenderlo, pero me asaltan muchas dudas

    Esto es parte de mi proyecto de fin de carrera, para mi es muy útil este tipo de tutos, espero que sigas adelante con ellos

    Saludos y felicidades por tu trabajo

  19. Referente al punto 1, usa opengl o directx a pelo no?

  20. The debug renderer mechanism is intended to let the SDK communicate potential problems to the user in a visual way, rather than just textual error messages. This is very useful as many problems arise due to the incorrect spatial layout of objects within this software. For example, a car will not roll if its wheels’ axes are set up to be pointing in the wrong direction; however, since this configuration of the axes is not strictly illegal, and thus will not produce an error message, the problem may not be immediately obvious to the user.

    The SDK can help to quickly identify this problem by letting the user visualize dozens of settings with just a few lines of code. Because the SDK does not know or care what sort of graphics API you are using, you have to render the geometry it generates.

    Note: Objects in compartments cannot currently be visualized.

    The data is a container holding a list of points, lines, and triangles. Below is a complete implementation using the OpenGL graphics library. It reads the data out of the NxDebugRenderable and sends it to OpenGL.

    Si, usa opengl :) ya he entendido el tuto, ya lo he implementado en mi proyecto, y ya estoy leyendo la documentació de physx.

    Ahora solo me toca modificar el dotSceneLoader para añadir actores de physx.

  21. Hola kjuanlu!:

    Me alegra tu interés.

    Evidentemente el creador de NxOgre no nos ayuda mucho a los que usamos Linux, por tanto me decante a hacerme mi propio wrapper, que además me da más control, y me entero mejor de lo que estoy haciendo. Aunque admito que NxOgre me aceleraría algunas cosas.

    Los ejemplos de PhysX es OpenGL tal cual.

    Aun no he usado nunca los mesh de PhysX, siempre envuelvo a los personajes de mis juegos en capsulas, cubos, esferas, etc … que son más eficientes, aunque creo que un día probe un mesh concavo que siendo concavo tiene un rendimiento muy bueno.

    Este tuto tiene tiempo, rediseñe la estructura de las clases y la herencia bastante mejor. Por tanto, preguntame cualquier duda del código.

    Yo pronto tendré que empezar el proyecto de fin de carrera, ¿ cual es el tema ? me gustaría proponer un tema que usará physx, tenía la idea desde antes de empezar la carrera xD

    El debug renderer que viene en los ejemplos es para openGL, pero con ayuda de los foros de Ogre, hice uno que vale para Ogre, luego lo busco si te interesa.

    Si algún día te gustaría portar tu código a Windows, te recomiendo que estudies “CMake”. Con una especie de Makefile genérico te puede generar automaticamente Makefiles de unix o proyectos de visual estudio / eclipse / codeblocks … todo automático.

    Un saludo!

  22. Gracias por responder :)), me alegra mucho saber lo de cmake, porque después de saber que Physx no soporta MinGW (para windows) es la mejor opción, (me gustaría ir proobando en ambos SO según voy avanzando).

    Sobre el código, hoy lo he estado estudiando con los manuales de physx al lado, asique está todo entendido, además usé SDL para hacer un minijuego en 2D antes de meterme en el mundillo:

    Por lo que he visto, CEntidad crea las mallas, cubos etc… y le pasas a la estructura de Physx las coordenadas y dimensiones (recubrimiento en un cubo) , aparte de sus variables de fisica como densidad, después se hace la simulación de Physx, y cuando termina, se devuelve los valores de coordenadas a ogre para renderizar.

    Sobre el debug render, de momento me vale asi, con este framework es un buen punto de partida.

    Sobre el tema, les vale casi cualquier cosa. Tengo que tener una segunda reunión con un profesor, después de enseñarle alguna demo con ogre+physx, y el tema es un videojuego (ya me dijo que era perfectamente válido, pero de aquellas no sabía nada de ogre).

    Tengo un “equipo” de artistas, asique por el diseño blender/3d Studio no me preocupo, yo soy el programdor

    De momento no me preocupo, pero seguramente tendré que modificar el DotSceneLoader para añadir recubrimiento en mallas cóncavas.

    Pues si sigues con esto ya nos veremos en los foros de ogre y physx, y gracias por el framework, que me ha despejado todas las dudas.

    Saludos

  23. ¿Que hace updateStats(); ?
    ¿De dónde ha salido? supongo que es SDL, en tu framework no hay mas referencias a esta, y como no hay un :: delante, supongo que es de SDL que es C

    Saludos

  24. Viene heredado de “ExampleFrameListener”. Y lo vas a ver en cualquier ejemplo de Ogre. Por que lo que hace es actualizar las estadisticas del cuadro de ogre verde de abajo a la izquierda.

    Parte de lo que hace como ejemplo es: a partir de un singleton obtiene el “OverlayElement” (que es algo estático 2D que ignora la prespectiva 3D):
    OverlayElement* guiAvg = OverlayManager::getSingleton().getOverlayElement(“Core/AverageFps”);

    Para despues actualizar los FPS medios por ejemplo:
    guiAvg->setCaption(avgFps + StringConverter::toString(stats.avgFPS));

  25. Se me olvidaba:

    En ogre, olvidate de SDL(desgraciadamente), aunque para mi es la mejor librería para render y entrada/salida, los creadores de Ogre optaron por OIS. Hay tutoriales para implementar Ogre+SDL pero estan muy anticuados, si lo consigues pasame el código. :P

    El uso de SDL en este ejemplo es absurdo, solomente uso el dormir un proceso en microsegundos de SDL. En caso de que un frame se renderize demasiado rápido, descansamos el proceso para evitar un 100% CPU.

    Por tanto haz los cambios necesarios para no tener SDL como dependencia.

    • mmm, por eso no veia updateStats(); porque en mi app no heredo ni ExampleApplication ni ExampleFrameListener (prefiero implenetarlo yo todo).

      Una pequeña duda, cuando intentaste usar mallas convexas, las cocinaste con el SDK de physx, o usaste la herramienta Flour de betajean ??

  26. :)) Ya funciona en mi poyecto¡¡¡

  27. Estoy instalando todas las bibliotecas y SDK para compilar en linux (

    Pues me he dado cuenta que el SDK de physx carece de documentación. Haciendo un dpkg –contents del deb de doc y samples de physx, lo único que te instala es son archivos chm (los de doc de windows) /usr/share/doc/libphysx-2.8.1/PhysXDocumentation.chm

    Sin embargo el SDK de physx de windows tiene un montón de ejemplos con código y un doc por cada uno de ellos, a modo de Tutorial ¿los que no han probado el SDK en windows como saben de la existencia de estos tutoriales? en la página ni en los foros se hace referencia.

    root@core:/windows/physx# ls
    Bin Graphics LowLevel Samples SDKs SysRelease Tools TrainingProgramsroot@core:/windows/physx/TrainingPrograms/Docs# ls
    Chapter10_Cloth Chapter1_Rigid_Bodies Chapter4_PhysicsEffects Chapter7_Vehicles SDK_Integration_Guide
    Chapter11_SoftBody Chapter2_Joints Chapter5_Tools Chapter8_Integration Training_Programs_Overview
    Chapter12_Other Chapter3_User_Reports Chapter6_Forcefield Chapter9_Fluids

    y cada directorio con un monton de .docs

    ¿?¿?

  28. Lo de las mallas convexas, lo intente hacer con PhysX, pero al final lo deje, porque con forma de capsulas y variantes me servía.

    Es curioso, siendo linux de siempre donde se distribuye todo en source, es en windows donde las sources de los ejemplos y en linux dan los binarios de los mismos ejemplos, esta en /usr/sbin/PhysX_Samples_2.8.1_FC4/Bin/linux . ¿ Será que los de AGEIA no sabian hacer un Makefile ? No creo xD Usa el source de windows, ese source no lo vas a conseguir compilar, no compila con gcc ni con mingw, solo visual studio, lo curioso es que te den los binarios ….
    Te darás cuenta solamente viendo los foros, que el soporte de linux deja que desear, pero por lo menos existe. NVIDIA no se esta esforzando en que haya una aceleración por hardware en linux, aunque supongo que algún día la habrá.

    Esta bien tu video de youtube, cuando tengas más dominio del API empezarás hacer ragdolls, k es lo más divertido xD

  29. hey! que interesante bro!!!!
    apenas ando empezando con esto de librerias graficas ‘profesionales’ y apenas si soy estudiante de primeros semestres de universidad xD … en todo caso, me viene basatnte bien lo que hay en tu blog en especial estas entradas de ogre ya que ando empapandome como loco sobre todo esto, te felicito le he dado un buen pason a tu blog y me dejas muuuy sorprendido. te seguire mas de cerca aunque veo que hace rato no sigues con el tema, espero ( si todo sale bien, y sigo con esto ), talvez aportar algo ^^;

    ahh si, olvide mencionarlo, es que andamos en la universidad ( algunnos desarrolladores y diseñadores) buscando moldear el campus universitario, crear un recorrido y talvez algun juego y usaremos ogre como motor grafico sabemos poco, pero hay ganas ;D

    saludos!!!

  30. Buenas,
    después de leer todos los comentarios, quisiera saber si podríais resolver una duda.
    Tengo en mi pc WinXp y instalado y configurado el code::blocks para trabajar con Ogre3d. Quisiera trabajar con physx pero he leído por arriba que no funciona, bueno, que no se puede compilar con minGW. La pregunta es como lo tengo que hacer para trabajar con code::blocks con ogre y physx bajo winXp. He leido que tal vez se tenga que utilizar cmake, en caso afirmativo, como se tendria que hacer.
    Venga saludos y adelante con estos tutots!

    • Este tutorial tiene tiempo, desde hace mucho uso CMake, además de que he rediseñado todo el código mucho mejor etc …

      CMake es muy fácil y potente, facilita la portabilidad a tus proyectos. Solo necesitas un CMakeLists.txt configurado para tu proyecto y pones “cmake .” y te genera los makefiles. Con el parametro -G puedes decirle que en lugar de generar Makefiles, te genere proyectos de eclipse + makefiles(que es lo que uso yo), de codebloks o de visual studio entre otros.

      Si no tienes ni idea de lo que te hablo, ya pondré un post con un CMakeLists.txt funcional y explicado.

      Un saludo.

      • hey! Gracias por responder tan rápido. Estudiaré este programa. En todo caso, si pones un post con un ejemplo de utilización del cmake estaría de lujo, y más aún si fuera un ejemplo para code blocks.
        Bueno, gracias y saludos!

      • Hola,

        soy brasileño,a continuación, pedir disculpas por cualquier error justo que pueda tener.

        Usted podría hacer la versión actual de su código? Me gustaría probar esta combinación en Linux =)

        la versión de Ogre está utilizando actualmente?

        ¿Cree usted que usted puede usar su biblioteca en un proyecto que ya utiliza NxOgre (sé que se necesitan cambios, pero cómo se debe tener mucho los cambios)?

        Un saludo

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: