Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Avances En El Motor (md5)

Iniciado por BeRSeRKeR, 09 de Enero de 2005, 02:23:19 PM

« anterior - próximo »

8tintin

 Joer, se ve de fábula  (ole)

Lo de mezclar canales de bones para conseguir una acción nueva me ha encantado. Es algo que siempre he querido tener en el NLA de blender, un mezclador por huesos.

En las transiciones a mi me ha dado la sensación de que cuando el personaje iba cogiendo velocidad los 6 frames de interpolación eran suficientes, una interpolación rápida que queda muy bien. Sin embargo al pasar del movimiento a parar del todo me han parecido pocos frames, el personaje para precipitadamente. Igual podríais aumentar el número de frames solo en el caso de pasar de movimiento a parado. Ponerle 8 ó 10 frames (sería cuestión de probar) porque así parecería que el personaje empieza a parar hasta que se queda quieto.

Seguid así que da gusto "veros"

Saludos.

BeRSeRKeR

 
Cita de: "8tintin"Sin embargo al pasar del movimiento a parar del todo me han parecido pocos frames, el personaje para precipitadamente. Igual podríais aumentar el número de frames solo en el caso de pasar de movimiento a parado. Ponerle 8 ó 10 frames (sería cuestión de probar) porque así parecería que el personaje empieza a parar hasta que se queda quieto.
Sí, donde más se nota es en el paso de walk a idle pero bueno, como es totalmente configurable, se puede especificar el número de frames que dure la transición en cualquier momento. Además, el que la transición quede mejor o peor también depende del frame donde comience la mezcla. Si por ejemplo en la animación actual el modelo tiene la pierna atrás y la nueva animacón la tiene adelantada, el transición será más brusca pero evidentemente aumentando el número de frames de transición se arreglaría.

Cita de: "8tintin"Seguid así que da gusto "veros"
¡Intentaremos mantener el ritmo!. :D

Gracias.
¡Si te buscan en nombre de la ley, huye en nombre de la libertad!!

BeRSeRKeR

 English text below...
--------------------------

Hola.

Resulta que estoy revisando la animación esquelética que tenía implementada hace ya tiempo (utilizando el formato MD5 de Doom3) y que quiero acabar de aquí a no mucho tiempo para poder añadirla al motor. El caso es que una de las cosas que me tenían mosca es el pobre rendimiento que obtenía. Por ejemplo, el modelo Imp, me daba unos 30 fps. ¡Lo cual, como es de suponer, es inaceptable!.

Así que esta noche he estado haciendo pruebas para ver cómo mejorar el rendimiento sin tener que hacer el skinning con la GPU. Al final he obtenido un incremento del frame rate de un 800%. ¿Qué es lo que ocurría?.

Pues como es lógico, las llamadas a funciones ocasionan una cierta penalización. Pero ciertamente, ¡¡jamás pensé que las llamadas que hace MDX a las funciones nativas de D3DX llegara a esos extremos!!. Mirad los resultados:

Utilizando las funciones de D3DX Matrix.Multiply, Vector3.TransformCoordinate y Matrix.TransformNormal: ~30fps
Utilizando funciones equivalentes a las anteriores pero programadas por mí: ~190fps
Metiendo el código directamente (no se hacen llamadas a funciones): ~240fps

En fin, jamás pensé que la penalización pudiera ser tan brutal. Lo cierto es que viendo lo visto, habría que repasar el motor en busca de fragmentos de código que pudieran estar causando un descenso del rendimiento tan bestia.

Saludos.

-------------------------

Hello.

I am reviewing the skeletal animation that I implemented a long time ago (using format MD5 of Doom3) and that I want to finish to be able to add it to the engine.

The case is that one of the things that I was dissapointed with was the poor performance obtained. For example, the D3s Imp model, gave about 30 fps.  Which it was totally unacceptable!.

So tonight I've been testing to see how improve the performance without having to do skinning with the GPU. In the end I have obtained an increase of 800% in the frame rate.  So what was happening?.

As it is logical, calls to functions cause a certain penalty. But certainly, I never thought that the calls that MDX does to the native functions of D3DX could cause such performance hit!!. The results:

Using the functions of D3DX Matrix.Multiply, Vector3.TransformCoordinate and Matrix.TransformNormal: ~30fps
Using functions equivalent to the previous ones but programmed by me: ~190fps
Putting the code directly (without function calls): ~240fps

In short, I never thought that the penalty could be that high. The truth is that we should take a look to the source code looking for fragments of code that could be causing a similar performance hit.

Greetings.  
¡Si te buscan en nombre de la ley, huye en nombre de la libertad!!

BeRSeRKeR

 Nada, olvidé que estaba probando en modo debug. En modo release el frame rate ronda los 340 en los tres casos.

Sin skinning el frame rate es de aproximadamente 530 fps.

-----------------------

Nevermind, I was testing on debug mode. In release mode the frame rate is around 340 in three cases mentioned in the message above.

Without skinning the frame rate is around 530 fps.

-----------------------

PD: ¿el foro va lento o es cosa mía?. :)
¡Si te buscan en nombre de la ley, huye en nombre de la libertad!!

zupervaca

 Ber si necesitas ayuda con los md5 ya sabes que yo los tengo implementados hace tiempo, lo unico que lo hago con quaterniones y no con matrices, a mi ya sabes que un ciberdemon animado me daba ~1000fps y con unos 50 me queda en 60fps.

CitarPD: ¿el foro va lento o es cosa mía?.
Llevo intentando postear esto todo el dia asi que ya ves (grrr)  

BeRSeRKeR

 
Cita de: "zupervaca"Ber si necesitas ayuda con los md5 ya sabes que yo los tengo implementados hace tiempo, lo unico que lo hago con quaterniones y no con matrices, a mi ya sabes que un ciberdemon animado me daba ~1000fps y con unos 50 me queda en 60fps.

CitarPD: ¿el foro va lento o es cosa mía?.
Llevo intentando postear esto todo el dia asi que ya ves (grrr)
No si los md5 los tenía implementados (en C++) desde que salieron los primeros tests de Doom3. Y la verdad es que el sistema que desarrollé soporta muchas de las características de Doom3. Por ejemplo soporta blending entre sets de animación (pasar de una animación a otra haciendo una mezcla de x frames) y canales de animación (reproducción de diferentes animaciones al mismo tiempo para diferentes grupos de bones). Lo que pasa es que no me gustó como me quedó y por eso quiero reescribirlo.

Lo que sí estaría bien es saber cómo haces la conversión de los datos del md5 (rotaciones, por ejemplo) a Direct3D. Yo por ejemplo multiplico el bone raiz por una matriz de conversión pero lo ideal es hacerlo en tiempo de carga. Aunque es posible que al final me decante por crear un formato propio con soporte de escalado de bones.

Saludos.

EDIT

Si desactivo la transformación de las normales y la tangente me da unos 440fps. Está claro que todavía hay que optimizar más y tal vez hacer el skinning en la GPU.
¡Si te buscan en nombre de la ley, huye en nombre de la libertad!!

zupervaca

 Me imagino que te refieres a esto: x,y,z = x,z,-y.
Este es el codigo que uso en tiempo de carga para pasar de doom3 a mi sistema de quaterniones ya que no uso los de direct3d, no obstante me parece que uso el mismo.
        // Obtener flag y puntero al inicio de los floats de este joint
        int sourceFlags = source->flags;
        float *frameStartIndex = startFrame + source->startIndex;
        // NOTA: x,y,z = x,z,-y

        // Posicion X (Tx)
        if( sourceFlags & 1 )
        {
         destJoint->pos.x = *frameStartIndex;
         frameStartIndex++;
        }
        else
        {
         destJoint->pos.x = source->pos.x;
        }
        // Posicion Y (Ty)
        if( sourceFlags & 2 )
        {
         destJoint->pos.z = -*frameStartIndex;
         frameStartIndex++;
        }
        else
        {
         destJoint->pos.z = -source->pos.y;
        }
        // Posicion Z (Tz)
        if( sourceFlags & 4 )
        {
         destJoint->pos.y = *frameStartIndex;
         frameStartIndex++;
        }
        else
        {
         destJoint->pos.y = source->pos.z;
        }
        // Rotacion X (Qx)
        if( sourceFlags & 8 )
        {
         destJoint->rot.x = *frameStartIndex;
         frameStartIndex++;
        }
        else
        {
         destJoint->rot.x = source->rot.x;
        }
        // Rotacion Y (Qy)
        if( sourceFlags & 16 )
        {
         destJoint->rot.z = -*frameStartIndex;
         frameStartIndex++;
        }
        else
        {
         destJoint->rot.z = -source->rot.y;
        }
        // Rotacion Z (Qz)
        if( sourceFlags & 32 )
        {
         destJoint->rot.y = *frameStartIndex;
         frameStartIndex++;
        }
        else
        {
         destJoint->rot.y = source->rot.z;
        }
        // Calcular el componente W
        destJoint->rot.ComputeW();


PD: Antes de postear copiar el mensaje por que hoy el foro esta remolon :)


 Pero... al final publicareis un tuto explicando las caracteristicas del formato md5 , el codigo etc

La verdad, lo que hubiesemos podido aprender con vosotros... es una pena que decidais dejar el motor y no continuar con las enseñanzas ....

De todas formas, mi reconocimiento y admiracion por vuestro trabajo !!!

BeRSeRKeR

 
Cita de: "Guest"Pero... al final publicareis un tuto explicando las caracteristicas del formato md5 , el codigo etc
Mi intención es añadir la animación esquelética al motor y en todo caso sacar un tutorial tipo manejo de un personaje con el teclado y el ratón (o algo similar).

Pero vamos que no sé cuándo estará terminado. :)

Saludos.
¡Si te buscan en nombre de la ley, huye en nombre de la libertad!!

BeRSeRKeR

 
Cita de: "zupervaca"Me imagino que te refieres a esto: x,y,z = x,z,-y.
Bueno, esa era la conversión más evidente y la primera que probé en su día, pero si no recuerdo mal, las rotaciones no funcionaban bien. ¿Tú no tienes que hacer ninguna conversión?. O tal vez metía la gamba en alguna parte, que también es posible... jeje

Y pasando al tema del skinning, he seguido haciendo pruebas y la verdad es que sí hay diferencia entre llamar a las funciones de D3DX o utilizar las mias o directamente meter el código de transformación en la función de skinning. Aquí tenéis los resultados:

Haciendo uso de D3DX: 350 fps
Haciendo uso de mis funciones: 370 fps (pasando por referencia las matrices y vectores) / 335 fps (pasando por valor las matrices y vectores)
Sin llamadas a funciones: 480 fps

O sea que sí que hay bastante diferencia entre los tres casos mencionados. Parece que el hecho de que las funciones de D3DX estén (en teoría) muy optimizadas es contrarestado con creces por la penalización producida por la llamada a sus funciones. Si no, no se entiende que mi código de transformación sea más óptimo que el de D3DX.

Saludos.
¡Si te buscan en nombre de la ley, huye en nombre de la libertad!!

zupervaca

 Ber yo uso unas mias por que las llamadas a objetos com comen mucho, se gana mucho teniendo unas propias en c++ ya que pueden compilarse como inline, me imagino que en csharp pasara lo mismo, no obstante usando la libreria como dll puede que no se note la diferencia.

Todas las conversiones que hago estan en el codigo que te paso, no obstante debes tambien realizarla en la lectura de la malla para que salga correctamente.
Este es el codigo que tengo para crear el indexbuffer y el vertexbuffer de un mesh:

  void Mesh_Close( dib::Graphics::View *view, dib::Graphics::SkinInfo **skinInfo, dib::Collection::DinArray<dib::Graphics::Mesh> *meshes )
  {
   // Crear el indexbuffer
   dib::Graphics::IndexBuffer *indexBuffer = view->CreateIndexBuffer( this->tris, this->numTris * 3, 16 );
   if( indexBuffer != NULL )
   {
    // Crear el vertexbuffer
    VertexMD5 *verticesVB = new VertexMD5[this->numVerts];
    if( verticesVB != NULL )
    {
     // Obtener los dos vertices
     Vertex *source = &this->vertices[0];
     Vertex *sourceEnd = source + this->numVerts;
     VertexMD5 *dest = &verticesVB[0];
     while( source != sourceEnd )
     {
      // Actualizar el vertice destino
      dest->u = source->u;
      dest->v = source->v;
      dest->pos.Zero();
      Weight *curWeight = &this->weights[source->weight];
      Weight *lastWeight = curWeight + source->countWeight;
      while( curWeight != lastWeight )
      {
       // Obtener su joint
       Joint *curJoint = &this->joints[curWeight->joint];
       dib::Math::Vector3 wv;
       dib::Math::Quat q( curJoint->rot.x, curJoint->rot.y, curJoint->rot.z );
       q.RotationVector( curWeight->pos, wv );
       // NOTA: x,y,z = x,z,-y
       dest->pos.x += (curJoint->pos.x + wv.x) * curWeight->weight;
       dest->pos.y += (curJoint->pos.z + wv.z) * curWeight->weight;
       dest->pos.z -= (curJoint->pos.y + wv.y) * curWeight->weight;
       // Siguiente
       curWeight++;
      }
      // Siguiente
      source++;
      dest++;
     }
     dib::Graphics::VertexBuffer *vertexBuffer = view->CreateVertexBuffer( verticesVB, this->numVerts, sizeof(VertexMD5) );
     delete verticesVB;
     if( vertexBuffer != NULL )
     {
      // Crear la informacion de la malla (Skin)
      dib::Graphics::Skin *skin = new dib::Graphics::Skin( this->numVerts );
      if( skin != NULL )
      {
       Vertex *source = &this->vertices[0];
       Vertex *sourceEnd = source + this->numVerts;
       dib::Graphics::Skin::VertexInfo *vertexInfo = &skin->vertexInfo[0];
       while( source != sourceEnd )
       {
        int startWeight = source->weight;
        int endWeight = source->countWeight + startWeight;
        vertexInfo->weights.Create( source->countWeight );
        Weight *weight = &this->weights[startWeight];
        Weight *weightEnd = &this->weights[endWeight];
        dib::Graphics::Skin::Weight *dest = &vertexInfo->weights[0];
        while( weight != weightEnd )
        {
         // NOTA: x,y,z = x,z,-y
         dest->Set( weight->joint, weight->weight, dib::Math::Vector3(weight->pos.x, weight->pos.z, -weight->pos.y) );
         weight++;
         dest++;
        }
        // Siguiente
        source++;
        vertexInfo++;
       }
       // Crear la malla para almacenar todos los valores
       dib::Graphics::Mesh *mesh = new dib::Graphics::Mesh( view );
       if( mesh != NULL )
       {
        // Agregar al array de mallas
        if( meshes->Add(mesh) )
        {
         // Actualizar la malla
         mesh->indexBuffer = indexBuffer;
         mesh->material = NULL;
         mesh->skinInfo = skinInfo != NULL ? *skinInfo : NULL;
         mesh->skin = skin;
         mesh->vertexBuffer = vertexBuffer;
         mesh->vertexDeclaration = NULL;
         // Continuar procesando las siguientes mallas
         return;
        }
        delete mesh;
       }
       delete skin;
      }
      delete vertexBuffer;
     }
    }
    delete indexBuffer;
   }
   this->reader.Abort();
  }

Lo primero es meter todos los vertices en un array de vertices para crear el vertexbuffer, ya en el aprovecho para realizar la conversion, despues memorizo en estructuras propias los frames, es decir, la informacion de la animacion tambien convertida y por ultimo lo meto todo en una clase mesh.
La verdad es que leer esto en el foro es imposible ya que se come colores y tabulaciones por todas partes.
Se me olvidaba, esta funcion es llamada por cada mesh del objeto, si un md5 tiene dentro 5 mallas se crean las 5 y las mete en el array, de este tipo de archivo no saco la informacion de los huesos.
Otra cosa muy importante, yo no tengo los joints globales, los tengo jerarquicamente, es decir, un joint hijo depende de su joint padre para colocarse correctamente en el espacio, por eso en la demo de hace tiempo cuando se presiona la tecla "V" (me parece que era esa) el modelo camina ya que dejo de posicionar el joint padre de todos al valor 0,0,0

PD: Y pensar que esto ya no me compila con la nueva version de la libreria :(
PD2: Si quieres para que te sea mas facil leer todo me das un correo y te paso los cuatro archivos que tengo del md5 o si quieres ethernet se lo paso a el y lo ponga como el codigo del mes, hacia tiempo habia dicho que iba a dar el codigo, pero como se quedo obsoleto para la libreria pase de este codigo.

 Hola, al tratar de bajar los videos la pagina donde estan alojados me daba error, es posible? como puedo hacer para verlos? los puedes publicar nuevamente?

Saludos Matias

PD: por el revuelo que arme con mis sugerencias en el otro thread del foro pido disculpas si el tema se fue de las manos, mi intencion solo era aportar sugerencias para beneficio del desarrollo de la engine. Estoy completamente de acuerdo que el destino de los fuentes y los tiempos los deciden exclusivamente ustedes... (los desarrolladore de la engine..) Saludos nuevamente Matias

 PD: no se que pasa con el foro, soy Matute.. me loguee correctamente .. pero en el foro salgo como invitado..

Saludos

BeRSeRKeR

 Gracias zupervaca, le echaré un ojo. Por cierto, ¿tú qué haces con los frames de animación, los construyes al cargar el modelo (consume más memoria) o en cada frame (en tiempo de ejecución) reconstruyes todo en base al frame base?.

Cita de: "Guest"Hola, al tratar de bajar los videos la pagina donde estan alojados me daba error, es posible? como puedo hacer para verlos? los puedes publicar nuevamente?
Sí, los quité hace tiempo porque los tenía alojados en mi FTP y me faltaba espacio para meter otras cosas. Veré lo que puedo hacer (si es que los conservo).

Cita de: "Guest"PD: por el revuelo que arme con mis sugerencias en el otro thread del foro pido disculpas si el tema se fue de las manos, mi intencion solo era aportar sugerencias para beneficio del desarrollo de la engine. Estoy completamente de acuerdo que el destino de los fuentes y los tiempos los deciden exclusivamente ustedes... (los desarrolladore de la engine..) Saludos nuevamente Matias
Fastidia un poco, pero nada que no se pueda olvidar. :)

Gracias.
¡Si te buscan en nombre de la ley, huye en nombre de la libertad!!






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.