Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Cambios En Mi Motor

Iniciado por DraKKaR, 12 de Septiembre de 2004, 01:22:24 PM

« anterior - próximo »

DraKKaR

 Bueno, el otro dia me decidi a aplicar unos cambios que se me ocurrieron al motor. El motor lo empece ya hace bastante tiempo, y como es el primero que he hecho, no sabia como ahcer un motor XD. Fui aprendiendo a medida que lo iba haciendo. Esa es la razon que haya cambiado varias veces cosas del mismo `core', a medida que he ido aprendiendo. Y bueno, 2 de las mejoras que se me han ocurrido son las que voy a explicaros ahora.

MEJORA 1: SOBRE LAS PRIMITIVAS DEL MOTOR

Desde el principio de los tiempos (de mi motor) han existido unas primitivas (objetos). Por ejemplo: el objeto Mesh, el objeto Billboard, el Overlay, etcetera.
Cada uno de estos objetos contenia la informaci*n para dibujarse y posicionarse en el mundo. Es decir, la clase Mesh tiene atributos: pos, rotmatrix, size, para posicionarlo y orientarlo en el mundo; y ademas tiene las funciones de dibujo (Render()).

Con este esquema todo hya ido bien, hasta que he tenido que hacer cosas como dibujar muchas veces un mismo objeto. Me explico, imaginad que en un juego de naves hay 10 naves iguales (usan la misma malla). Hacer esto con mi motor supondria, o bien cargar 10 veces la misma malla para tener 10 objetos independientes, cada uno con su malla (la forma); o bien cargar solo una malla, pero hacer trucos como:


bucle(i){
   malla(i).pos=nuevapos;
   malla(i).Render();
}


Como ninguna de las 2 formas me gustaba, el Blender (y el ODE) me dieron la idea de implementarlo de una nueva manera:  Separaria la info de posicionamiento de un objeto y la forma en que se puede representar un objeto en dos clases diferentes. Asi, a grosso modo, tengo 2 clases:

La clase SpaceObject (que contiene la informacion sobre posicionamiento de un objeto) y la clase DisplayObject (que indica como se va a representar ese objeto).
La clase SpaceObject tiene una referencia a un objeto DisplayObject (a su forma).
De esta forma, se pueden crear multiples instancias de tipo SpaceObject que representan todos los objetos del mundo posicionados en su lugar, y todos estos objetos pueden apuntar al mismo objeto de tipo DisplayObject, indicandoles que todos tienen la misma forma. De esta forma se soluciona el problema anterior y se considera el DisplayObject (una malla port ejemplo) como un recurso mas (como una textura o un sonido) que se puede "mapear" sobre un SpaceObject.

Lo unico que no me acaba mucho de este sistema es que antes para crear una malla simplemente tenia que hacer esto:

Mesh malla("malla.3ds");
malla.pos=nuevapos;


Y ahora tendria que hacer esto:

SpaceObject obj;
Mesh malla("malla.3ds");
obj.displayobj=&malla;
obj.pos=nuevapos;


Pero bueno, creo que el nuevo sistema es mejor que el que tenia antes y lo hace un poco mas potente y mejor estructurado.


MEJORA 2: HIJOS EN LOS OBJETOS
Hasta ahora los objetos no podian tener hijos de por si. Ademas, para la clase World contenia una lista de objetos que hay que representar/colisionar/actualizar. Una lista plana d objetos.

Se me ocurrio que podria hacer que cada objeto pudiera tener un numero indeterminado de hijos. Y que las propiedades de los hijos tuvioeran en cuenta las del padre. Por ejemplo: el objeto brazo tiene un hijo: mano. La posicion y orientacion del hijo se interpretaria pues, como relativa a la del padre, de esta manera, al mover simplemente el objeto brazo, se desplazarian tambien sus hijos (la mano).
Otro ejemplo seria la visibilidad: tengo un objeto cuerpo, que tiene 5 hijos: cabeza y 4 extremidades. Cada uno de estos hijos podria tener mas hijos. Por defecto, al a*adir al mundo el cuerpo, cuando se intente representar el objeto cuerpo, se representaran tambien todos los hijos y los nietos del obj. Esto tb es util porque si deshabilitas el render del objeto padre (el cuerpo) no se van a pintar tampoco los hijos.
De esta manera consigo una estructura jerarquica de objetos, y posaria de tener una lista de objetos de tipo vector, a tener un arbol de objetos jerarquizado que cuelga del nodo principal del mundo.



Bueno, estas son las mejoras que estoy haciendo. A ver que os parecen. Solo es por discutir un poco el tema.

PD: Perdonad por la longitud del post! XD

PD2: Tildes deshabilitadas voluntariamente (estoy en un terminal maldito).

Zaelsius

 
Citar
SpaceObject obj;
Mesh malla("malla.3ds");
obj.displayobj=&malla;
obj.pos=nuevapos;

¿Y cómo vas administrar la destrucción del objeto "malla.3ds"? En tu ejemplo el objeto se destruirá al salir de ámbito, pero lo normal será crear objetos dinámicamente(news). En ese caso.. ¿cúando y cómo sabrás que tienes que liberar la malla?

Mi consejo es que le eches un vistazo a Irrlicht:
Citar
IAnimatedMesh* mesh = smgr->getMesh("../../media/sydney.md2");
IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );

smgr corresponde a la clase SceneManager, y gestiona las mallas cargándolas sólo una vez. Como tambien gestiona la creación de nodos, sabe cuando ya no quedan nodos usando una malla determinada y puede descargarla.

Esta es una de las maneras de enfocarlo, pero la cuestión es tener al menos una clase encargada de la administrar automáticamente los recursos, con algun tipo de contador de referencias.

Haddd

 Primero caso:

Yo lo hago así. Tengo un objeto que es CXMalla donde sólo se almacen la información de la posición y demás. Dentro hay un objeto CXMesh, que es el que tiene la información de los vértices y demás. Además tengo un objeto CXSkin en CXMalla que dictamina las texturas y atributos de colores. Así puedo tener un cubo pero con diferentes texturas, sin tener que duplicar para nada la información de vértices.

Segundo caso:

Eso se llama Scene Graph. La verdad es que he pensado muuucho en ello, puesto que depende de la forma en que lo implementes, el código se hace más o menos claro. Por ejemplo, hay muchos objetos que no tienen jerarquía, pero quizás tu obligues a que haya al menos 1 jerarquía, con lo que obligas a referenciar 2 clases. Al final, creo que lo mejor es tener mallas por separado y jerarquías, de forma que si el programador necesita trabajar con una jerarquía, él ya accede a esa clase, y si necesita utilizar una malla, puede acceder directamente a ella.


DraKKaR

 Zaelsius: el quote que he puesto es solo un pseudo-codigo para que os hagais una idea. No os lo tomeis como ejemplo de uso del motor. En realidad ya habia pensado en esos temas de la destrucciobn de objetos segura. Por eso en todos los sitios donde sea importante no uso punteros normales, sino que uso la template shared_ptr<> de la biblioteca boost. En realidad el codigo correcto tal y como se usaria en el motor seria el siguiente:


shared_ptr<Mesh> malla=PrimitiveFactory::CreateMesh("malla.3ds");
shared_ptr<SpaceObject> obj=PrimitiveFactory::CreateSpaceObject();
obj.displayobj=malla;
mundo->Add(obj);


De esta forma, al usar shared_ptr<>, me aseguro de que siempre que haya una referencia a un objeto, este no se liberara. Y ademas, cuando todas las referencias al objeto se pierdan, este se liberara automaticamente.

Es mas, una vez anyadido al mundo el objeto y al haber usado shared_ptr<>, no hay manera de eliminar de forma insegura ni la malla ni el objeto, ya que siempre que haya alguna referencia al objeto, este no se eliminaria. Es una buena cosa esto de los shared_ptr<>.


Haddd: no he entendido demasiado bien lo que has comentado sobre el segundo caso. Podrias explicarlo un poco mas?.

Haddd

 En una de las primeras versiones del motor, las clases eran:

CXActor ->CXJerarquía ->CXMalla

De esta forma, los elementos de la escena eran actores y las mallas SIEMPRE formaban parte de una jerarquía. Lo malo es que para referirme a la malla debía usar:

actor->m_Jerarquia->m_Malla

Ahora, aunque no lo he implementado todavía, tengo:

Malla

Jerarquia

y la escena es una lista de mallas. A la hora de actualizar la escena, lo que se haría sería actualizar primero las jerarquías, que actualizarían automáticamente las mallas.

Pero, también se podría definir la jerarquía DENTRO de la malla. Ummm la verdad es que sería más óptimo, no sé quizás haya que pensarlo mejor.


Astat

 
Citaral usar shared_ptr<>, me aseguro de que siempre que haya una referencia a un objeto, este no se liberara. Y ademas, cuando todas las referencias al objeto se pierdan, este se liberara automaticamente.

Eso es mismamente lo que hago yo en mi motor, solo que sin usar boost. Uso una clase templatizada "Recurso" con un contador que aumenta al pedirle instancias, y disminuye cuando las liberas. Cuando ese contador llega a cero, es cuando se libera el recurso de verdad. Asi el motor sabe si al finalizar la aplicacion si el usuario (osea, programador que usa el motor) ha hecho todos los "Releases" o no (warning al canto).

Pogacha

 Mi estructura es:

class TEntidad
{
 int Tipo;  // para pseudo polimorfismo
 bool Eterea, Estatica ...
 TVolumen Volumen;

 TEntidad *Siguiente, *Anterior;
 TEntidad *Padre, *Hijo;

 TVector Posicion, Velocidad, Aceleracion;
 TVector Angulos;
 TMatriz Rotacion, Inversa;
 ...
 ..
};

class TLuz : public TEntidad      // directo
class TSprite : public TEntidad  // directo excepto la textura la cual es un TTextura *Textura
class TInstancia_Modelo : public TEntidad { TModelo *Base; ... } // indirectos directos
class TInstancia_Mesh : public TEntidad { TModelo *Mesh; TLightMaps *... }   // indirectos

Entonces puedes tener chorrosientasmil instancias de algo geometricamente igual pero en distintas variaciones.

La herencia de hijos la desactive, se complica demasiado la parte de fisica, y por ahora estoy peleando con otras cosas.

Saludos.

MA]Mestre

 Drakkar pq no le das la vuelta ?

Utilizando tu terminología, un 'DisplayObject' y dentro una lista de 'SpaceObject'.

DisplayObject obj("malla.3ds");

obj.addSpaceObject ( new SpaceObject(...)  );
obj.addSpaceObject ( new SpaceObject(...)  );
obj.Draw( );

Pogacha

 En realidad es ingenioso, no se me habia ocurrido. Pero el problema de esto es que no podria usar herencia padre-hijo como el quiere, o al menos no de una forma directa.

DraKKaR

 Haddd: la forma en que yo lo habia pensado es la que tu mismo has pensado al final; la jerarquía cuelga de un objeto posicionable, no hay un "objeto jerarquía" con objetos dentro.


Pogacha: lo que pasa es que después de usar los auto-punteros compartidos shared_ptr<> no me gusta nada usar punteros limpios de C++, a no ser que sepa inequívocamente que son iaccesibles desde fuera o que no suponen ningún riesgo.


MaMestre: Umm... es interesante lo que me propones. De esa forma sé a priori que recursos (mallas) usan los objetos que voy a pintar, de forma que podría usarse para optimizar el render. De todas dormas, como dice Pogacha, eso entraría en conflicto con la idea de jerarquías de objetos que pueden tener hijos que usen otras mallas diferentes a la del padre. La idea que yo tenía en un principio creo que es más "lógica" o directa, si quiero aprovecharme de eso.


Pogacha

 Por supuesto, son paradigmas distintos de programación, yo programo C+-.

MA]Mestre

 La primera vez que leí la " herencia padre-hijo " que expuso drakkar no lo acabe de entender, después de vuestros comentarios la volví a leer, y teneis razón, la solución que expuse no facilitá la "herencia padre-hijo".

Un saludo.

P.D: drakkar al fina que opción vas a tomar ?






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.