Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Scene Graph Y Animationes

Iniciado por Haddd, 03 de Febrero de 2006, 12:40:05 PM

« anterior - próximo »

zupervaca

 El problema es que realmente no sabeis como se almacenan los valores en una pista de tiempo, estoy seguro que pensais que se almacena en un array de punteros o en un array con los valores, pues esto no es asi, ya que si una pista de tiempo tiene una duracion de 100000 estariamos ocupando solo con los punteros 400KB por cada pista de tiempo que quereamos tener (ahora meterle valores de las llaves y todas las pistas de tiempo que podemos llegar a tener, podriamos quedarnos sin memoria en cuestion de nada), la solucion a esto es tener las llaves de la pistas de tiempo en una lista enlazada y cada vez que se indica que se quiere ir a un tiempo directamente recorrer desde el principio todas las llaves hasta encontrar una igual o superior a ese tiempo y asi saber las llaves a interpolar, no obstante ese es el peor de los casos ya que existen optimizaciones como la que os indicaba antes, memorizar el ultimo tiempo en el que estuvimos en esa pista de tiempo y comenzar a buscar las llaves desde ese sitio, espero que ahora me entendais.

Ithaqua

El hecho de tener que saber entre que keys (llaves? claves en todo caso) te encuentras para un momento dado dista bastante de significar "calcular toda la animación desde 0". No veo que problema tiene eso la verdad, así trabajan casi todos los motores de animación. Hay muchas soluciones para eso :)

Citar
El problema es que realmente no sabeis como se almacenan los valores en una pista de tiempo

No puedo hablar por los demás pero yo al menos suelo responder cuando tengo conocimientos prácticos de lo que hablo. Por los muchos posts que he leido tuyos creo que contigo no ocurre lo mismo ;)
thaqua^Stravaganza
http://ithaqua.stravaganza.org

zupervaca

 Bonito flame, ¿ahora quien quiere tener la ultima palabra?  creo que ha quedado bien claro todo lo que digo con mi ultimo post y es algo que no podeis decir que es mentira.
Si no se de lo que hablo no me leas, asi de facil.

BeRSeRKeR

 La verdad es que no conozco bien la arquitectura de MAX pero creo que los parámetros pueden marcarse como "animatable" y entonces se pueden crear claves de animación para dichos parámetros.

Por ejemplo, podemos crear una trayectoria curva solo con 3 claves de animación y utilizando un "controller" de tipo TCB o Bezier. O por ejemplo podemos animar el color de un objeto asignándole cualquier interpolador (lineal, bezier, TCB, etc). Es decir, que con muy pocas claves podemos crear animaciones más complejas. Evidentemente hay que implementar los diferentes tipos de interpolación que soporte MAX (si se utiliza MAX) para calcular los valores intermedios.

Así que yo creo que los atributos de los nodos del scene graph deberían poder marcarse como animatables y en tal caso, si existen claves de animación para dicho atributo, animarlo en base al método de interpolación asignado.

En fin, no sé si es eso de lo que se está hablando o no.

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

BeRSeRKeR

 Profundizo algo más en la idea.

Tenemos una interfaz IAnimatable que tendrá entre otras cosas una propiedad que será "Controller". Este controller será una clase base, pongamos por caso HController, de la que derivarán los controllers específicos como puedan ser HLinear, HBezier, HTCB, etc. Evidentemente cada controller implementará sus métodos de interpolación dependiendo del tipo que sea (lineal, bezier TCB, etc)

Los controllers a su vez contendrán (entre otras cosas) las claves de animación. Estas claves también partirán de una clase base HKey, que tendrá propiedades como el valor de esa clave de animación y el tiempo. Así, se implementarán distintas clases de clave para las distintas clases de controladores. De esta forma tendriamos HKeyLinear, HKeyBezier, HKeyTCB, etc. Cada una de las claves tendrá las propiedades características de cada tipo de interpolación. Por ejemplo, en HKeyBezier tendríamos InTangent, OutTangent, ConstantVelocity, etc. En HTCB tendremos Tension, Continuity, Bias, EaseIn, EaseOut, etc.

Finalmente, cada atributo podrá implementar la interfaz IAnimatable si se requiere que dicho atributo pueda ser animable. Por ejemplo pongamos que hacemos el color "animatable". Tan sólo tendríamos que hacer algo así:

model.Material.Diffuse.Controller.CalculateSample(time)

Eso nos devolvería el valor interpolado en el tiempo "time".

Bueno, ese es el sistema que se me ha ocurrido, muy básicamente.

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

zupervaca

 Explicare un poco como va el sistema de pista de tiempo en el 3dsmax (algunas cosas pueden diferir), también intentare explicar las diferentes formas de hacer pistas de tiempo.

Lo primero es explicar que el 3dsmax tiene un reproductor general que por el momento digamos que solo almacena el tiempo actual de la animación.

Struct Reproductor
{
Int tiempo;
};

Cada vez que cambiamos el tiempo al reproductor, este, automáticamente se lo notifica al nodo principal de la escena, también a su vez notifica a todos sus nodos hijos que el tiempo actual es otro y estos a su vez a sus hermanos y otra vez a sus hijos hasta llegar al final de las ramificaciones.

Struct Nodo
{
Nodo *padre;
Nodo *hermano;
Nodo *primerHijo;
...
};

La forma en que se actualiza un nodo puede diferir ya que dependiendo el tipo de nodo (geometría, luz, etc.) deberá de actualizar unos u otros valores, no obstante existen unos valores comunes para todos los nodos, que son la posición, rotación y escala del nodo que son los que explicare en los ejemplos que ponga.
Si observamos la forma de edición del trackview del 3dsmax veremos que podemos modificar los valores de posición XYZ de un nodo individualmente y a su vez modificar estos por separado, esto significa que tiene tres secuencias por separado pero que almacena los datos de posición juntos, estas secuencias tienen diferentes tipos de controladores, bezier, color bezier, etc., esto realmente son los diferentes tipos de llaves que existen.

Class Llave
{
Virtual void Interpolar( Llave *inter, Llave *dest, int tiempoActual ) = 0;
Int tiempo;
Llave *siguiente;
Llave *anterior;
};

La función Interpolar es la que llama la secuencia cada vez que queremos interpolar dos llaves, el puntero "inter" es la llave contra la que se interpola, en el puntero "dest" es en la que se almacena el resultado de la interpolación, la variable tiempoActual es el tiempo basado en cero, es decir: tiempoReproductor-tiempoLlave.
Los punteros siguiente y anterior sirven para saber las llaves contiguas a esta, el motivo de por que se le pasa a la función de interpolación la llave contra la que hacemos la interpolación es que queremos evitar el tener que hacerlo dentro de la función, ya que son varios ifs que nos ahorremos.
Como se observara la llave almacena su tiempo en la secuencia, esto nos sirve para luego realizar interpolaciones por periodos, ejemplo:

Class LlavePruebas : public Llave
{
void Interpolar(Llave *inter, Llave *dest, int tiempoActual )
{
Int periodo = inter->tiempo – this->tiempo;
dest->valor = this->valor + tiempoActual * (inter->valor-this->valor) / periodo;
}
Float valor;
};

Muchos os estaréis preguntando por que memorizar el valor devuelto por esta función en una llave, la razón es que la secuencia llama a esta función de forma abstracta, también indicar que una secuencia solo puede tener un tipo de llave (controlador), a su vez las llaves realmente solo interpolan valores que no saben para que son o servirán.

class Secuencia
{
Llave *primeraLlave;
Llave *llaveActual;
Int ultimoTiempo;
Void IndicarTiempo( int tiempo, Llave *resultado )
{
 ...
}
};

En la estructura de secuencias nos encontramos con varios valores, primeraLlave es donde se almacena la primera llave de la secuencia, el puntero llaveActual es donde se memoriza la ultima llave con la que se trabajo desde que se llamo por ultima vez a IndicarTiempo, ultimoTiempo es el tiempo que se indico desde la ultima vez que se llamo a IndicarTiempo, esto nos sirve para saber la dirección en que va la reproducción de la secuencia (luego se vera en el código).
La función IndicarTiempo nos sirve para indicar el tiempo y obtener el resultado de la interpolación mediante el puntero resultado.

boolean IndicarTiempo( int tiempo, Llave *resultado )
{
Int direccion = tiempo – ultimoTiempo;
If(direccion == 0 ) return false;
ultimoTiempo = tiempo;
If( llaveActual == NULL )
{
 llaveActual = primeraLlave;
}
If( dirección > 0 )
{
While( llaveActual ¡= NULL )
{
 If( llaveActual->tiempo > tiempo )
 {
  If(llaveActual->anterior == NULL )
  {
   If(llaveActual->siguiente == NULL )
   {
    Return false;
   }
   llaveActual->Interpolar( llaveActual->siguiente, resultado, tiempo – llaveActual->tiempo );
   return true;
  }
   llaveActual->anterior->Interpolar( llaveActual, resultado, tiempo – llaveActual->anterior->tiempo );
  return true;
 }
 llaveActual = llaveActual->siguiente;
}
}
Else
{
/* Exactamente igual que dirección > 0 pero cambiando los siguiente por anterior */
}
Return false;
};

Explicare la función, IndicarTiempo puede devolver true o false y así saber si resultado tiene el valor interpolado; calculamos la dirección en que va la secuencia y así saber como buscar la llave actual y contra la que interpolamos; si la llaveActual es nula debemos indicar que la actual es la primera; según la dirección usaremos el puntero siguiente o anterior de las llaves; Ahora buscamos una llave que tenga un tiempo mayor al tiempo indicado, cuando encontremos este caso deberemos de saber si tiene una llave anterior, si esto fuera así debemos de interpolar la llave actual contra su siguiente, si no tiene siguiente significa que solo existe una llave en toda la secuencia y es imposible realizar una interpolación, si por el contrario tiene un llave siguiente interpolaremos la actual contra la siguiente; en el caso de que la llave actual tuviera una llave anterior significa que nos hemos pasado de tiempo y debemos de interpolar la anterior contra la actual; después de esto la función retorna true, luego cuando volvamos a ejecutar esta función las variables ultimoTiempo y llaveActual ayudaran a no tener que buscar desde el principio la llave y ahorrarnos mucho tiempo de calculo.
Para el caso de que no sea una dirección > 0 deberemos de cambiar todos los punteros siguiente por anterior y la comprobación de llaveActual->tiempo > tiempo deberá de cambiarse por llaveActual->tiempo < tiempo.
Bien una vez explicado todo el sistema de almacenamiento de las llaves y su forma de calculo toca llevarlo a los nodos, la estructura nodo que antes vimos no nos vale ya que debemos de agregar unos cuantos valores mas, quedaría así:

Struct Nodo
{
Nodo *padre;
Nodo *hermano;
Nodo *primerHijo;
Float x,y,z;
Secuencia secuenciax, secuenciay, secuenciaz;
};

Como se vera los valores x,y,z del nodo están juntos en una misma estructura, pero las secuencias que las modifican no, el tipo de llave que se usa en estas secuencias es como la que puse de ejemplo llamada "LlavePruebas", bien, ahora siempre que cambiemos el tiempo de la escena debemos de indicar a los nodos que el tiempo ha cambiado, solo se actualizara las secuencias que tengan alguna llave, es decir, primeraLlave ¡= NULL.
Para actualizar x debemos de realizar lo siguiente:

KeyPruebas keyPruebas;
Int tiempo = ...;
Nodo *nodo = ...;
If( nodo->secuenciax->IndicarTiempo( tiempo, &keyPruebas ) )
{
nodo->x = keyPruebas->valor;
}

Si en vez de x, y o z qusieramos cambiar el color, el 3dsmax por ejemplo, tiene un controlador llamado beziercolor que en vez de ser una llave con un solo valor es con tres, de aquí el motivo de por que obtengamos el valor en una llave.

If( nodo->secuenciacolor->IndicarTiempo( tiempo, &keyColor ) )
{
 //nodo->color.r = keyColor.color.r;
 //nodo->color.g = keyColor.color.g;
 //nodo->color.b = keyColor.color.b;
 //nodo->color.a = keyColor.color.a;
 nodo->color = keyColor.color;
}


Tambien podrias ahorrarnos el copiar los valores a x,y,z trabajando directamente con las llaves que que retorna la funcion IndicarTiempo:

struct Nodo
{
 ...
 LlavePruebas x, y, z;
 Secuencia sencuenciax,sencuenciay,sencuenciaz;
}

// Actualizar X
nodo->secuenciax->IndicarTiempo( tiempo, &x )



Pues esto es to... to... todo amigos ;)

Espero que les sirva de ayuda, suerte.


PD: Este documento no es mas que una ayuda a como hacer pista de tiempo, puede tener errores de código ya que se ha hecho todo de cabeza sin ningún tipo de comprobación.

Haddd

 Madre mía  :P

Gracias Zupervaca  (ole)  

zupervaca

 He implementado una idea algo mejorada de la preliminar que os puse, me imagino que con codigo me entendereis mejor :lol:
Ahora he hecho que sea compatible con el 3dsmax, pero al mismo tiempo las secuencias se pueden reutilizar, cosa que el 3dsmax no sucede, pero si en los juegos, esta probado y aparentemente no tienes fallos, su logica de funcionamiento es la siguiente:
- Si no hay llaves en la secuencia, no pasa nada (como el 3dsmax).
- Si tiene una llave en la secuencia pongas el tiempo que pongas se actualiza con ella (como el 3dsmax).
- Si indicas un tiempo fuera de rango se pone la llave mas cercana a este tiempo (en el 3dsmax es imposible poner un tiempo fuera de rango).
Bueno aqui esta el codigo:

// Anim.h
// Clases para controlar animaciones

#ifndef dib_Anim_Anim
#define dib_Anim_Anim

#include "../Collection/Strip.h"

namespace dib
{
namespace Anim
{
 // Predeclarar clases
 class Key;
 class Sequence;
 class Control;

 // Clase abstracta para almacenar las llaves
 class Key
 {
 // Eventos
 protected:
  // Funcion que sirve para saber como interpolar dos llaves
  virtual void Interpolate( Key *inter, Key *dest, int time ) = 0;
  // Igualar el destino a la actual (evitamos sobrecargar el operador)
  virtual void Equal( Key *dest ) = 0;

 // Propiedades
 public:
  // Obtener el tiempo de esta llave
  int GetTime()
  {
   return this->time;
  }

 // Amistades
 private:
  friend Sequence;
  friend Control;
  // Tiempo de la llave
  int time;
  // Puntero al item agregado
  dib::Collection::Strip<Key*>::Item<Key*> *item;
 };

 // Secuencia que almacena llaves
 class Sequence
 {
 // Metodos
 public:
  // Crear la secuencia
  Sequence( int totalTime )
  {
   this->totalTime = totalTime;
  }

  // Destructor
  ~Sequence()
  {
   // Eliminar todas las llaves
   dib::Collection::Strip<Key*>::Item<Key*> *item = this->keys.GetFirst();
   while( item != NULL )
   {
    delete item->element;
    item = item->GetNext();
   }
  }

  // Indicar una llave indicando su tiempo, si existe una en ese tiempo dara error
           bool SetKey( int time, Key *key )
  {
   // Mirar si esta dentro de los limites de esta secuencia
   if( time >= 0 && time < this->totalTime )
   {
    // Indicar tiempo a la llave
    key->time = time;
    // Buscar la llave desde el final, normalmente durante una edicion se agregan al final
    key->item = this->keys.GetLast();
    while( key->item != NULL )
    {
     int keyTime = key->item->element->time;
     if( time <= keyTime )
     {
      if( time == keyTime )
      {
       // Ya existe una llave en este tiempo, es un error
       return false;
      }
      // Encontramos la llave, despues de esta insertamos la nueva llave
      break;
     }
     // Procesar anterior
     key->item = key->item->GetPrevious();
    }
    // Agregar la llave desde su item (si es null la insertar al principio)
    key->item = this->keys.Add( key, key->item );
   }
   return key->item != NULL ? true : false;
  }

  // Obtener una llave indicando su tiempo
  Key *GetKey( int time )
  {
   // No miramos los limites ya que durante la edicion es raro que editemos alguna llave inexistente
   Key *key;
   int keyTime;
   // Comenzamos por el final ya que lo normal durante la edicion es modificar las ultimas
   dib::Collection::Strip<Key*>::Item<Key*> *item = this->keys.GetLast();
   while( item != NULL )
   {
    key = item->element;
    keyTime = key->time;
    // Mirar nos pasamos o la encontramos
    if( time <= keyTime )
    {
     if( time == keyTime )
     {
      // La encontramos
      return key;
     }
     // Nos pasamos con lo que no existe
     return NULL;
    }
    // Siguiente ya que el tiempo aun es menor
    item = item->GetPrevious();
   }
   return NULL;
  }

  // Eliminar una llave
  void DeleteKey( Key *key )
  {
   // Borrarla del strip
   this->keys.Delete( key->item );
   // Borrar la llave
   delete key;
  }

 // Propiedades
 public:
  // Indicar un nuevo tiempo a la secuencia
  void SetTotalTime( int totalTime )
  {
   this->totalTime = totalTime;
  }

  // Obtener el tiempo total de la secuencia
  int GetTotalTime()
  {
   return this->totalTime;
  }

 // Miembros privados
 private:
  // Tiempo total
  int totalTime;
  // Llaves de la secuencia
  dib::Collection::Strip<Key*> keys;
  // Hacer amigos
  friend Control;
 };

 // Clase para reproducir una secuencia
 class Control
 {
 // Metodos
 public:
  // Actualizar el control
  bool Update( Key *ret )
  {
   // Mirar si tiene una sencuencia
   if( this->sequence != NULL )
   {
    // Preparar valores
                   Key *currentKey = NULL, *keyTemp;
    dib::Collection::Strip<Key*>::Item<Key*> *item;
    while( this->lastItem != NULL )
    {
     // Obtener la llave actual
     currentKey = this->lastItem->element;
     if( this->time >= currentKey->time )
     {
      // Mirar el siguiente
      item = this->lastItem->GetNext();
      if( item != NULL )
      {
       keyTemp = item->element;
       // Si el tiempo es menor hemos encontrado las dos llaves a tener en cuenta
       if( this->time <= keyTemp->time )
       {
        // Interpolar
        currentKey->Interpolate( keyTemp, ret, this->time - currentKey->time );
        return true;
       }
      }
      this->lastItem = item;
     }
     else
     {
      // Mirar el anterior
      item = this->lastItem->GetPrevious();
      if( item != NULL )
      {
       keyTemp = item->element;
       // Si el tiempo es mayor hemos encontrado las dos llaves a tener en cuenta
       if( this->time >= keyTemp->time )
       {
        // Interpolar
        currentKey->Interpolate( keyTemp, ret, this->time - currentKey->time );
        return true;
       }
      }
      this->lastItem = item;
     }
    }
    // Solo hay una llave, o esta fuera del rango con lo que igualamos la llave de retorno a la actual si es que tiene
    if( currentKey != NULL )
    {
     this->lastItem = currentKey->item;
     currentKey->Equal( ret );
     return true;
    }
   }
   return false;
  }

  // Constructor
  Control()
  {
   this->sequence = NULL;
  }

 // Propiedades
 public:
  // Indicar el tiempo
  void SetTime( int time )
  {
   this->time = time;
  }

  // Obtener el tiempo
  int GetTime()
  {
   return this->time;
  }

  // Indicar la secuencia de este control
  void SetSequence( Sequence *sequence )
  {
   this->sequence = sequence;
   this->lastItem = this->sequence->keys.GetFirst();
   this->time = 0;
  }
 
  // Obtener la secuencia activa
  Sequence *GetSequence()
  {
   return this->sequence;
  }

 // Miembros privados
 private:
  // Secuencia activa en el control
  Sequence *sequence;
  // Tiempo actual
  int time;
  // Ultimo item procesado en Update
  dib::Collection::Strip<Key*>::Item<Key*> *lastItem;
 };


 /************************************************/
 /* Clase derivadas de Key para su funcionalidad */
 /************************************************/

 // Clase para realizar interpolaciones lineales con templates
 template <class TYPE>
 class Lerp : public Key
 {
 // Metodos
 public:
  // Constructor
  Lerp()
  {
  }

  // Constructor
  Lerp( TYPE value )
  {
   this->value = value;
  }

 // Eventos
 protected:
  // Funcion que sirve para saber como interpolar dos llaves
  void Interpolate( Key *inter, Key *dest, int time )
  {
   // Convertir a llaves legibles
   Lerp *keyInter = (Lerp*)inter;
   Lerp *keyDest = (Lerp*)dest;
   // Calcular interpolacion
   int period = keyInter->GetTime() - this->GetTime();
   // Los operadores se ponen a la derecha para que el template sea valido
   // destino = origen1 + tiempo * (origen2 - origen1) / (tiempo_final - tiempo_inicial)
   keyDest->value = this->value + ((keyInter->value - this->value) * time) / period;
  }

  // Igualar el destino a la actual
  void Equal( Key *dest )
  {
   Lerp *keyDest = (Lerp*)dest;
   keyDest->value = this->value;
  }

 // Valor
 public:
  TYPE value;
 };
}
}

#endif

/* Por David Inclán Blanco
* http://www.davidib.com
* Este codigo fuente puede ser utilizado para cualquier proposito, incluso para uso
* comercial, pero nunca podra decir que ha asi creado por usted, cualquier fallo
* derivado de este codigo no sera responsabilidad del autor
*/

Si alguien quiere el codigo en un archivo me avisa ya que el code de los foros quita tabulaciones, etc.
Como vemos la funcionalidad ahora es mayor, tenemos una clase Key que es la que hace de llave de 3dsmax y controlador para interpolar las llaves, asi la personalizacion de llaves es maxima, pudiendo crear llaves que interpolen mallas enteras, huesos, etc.
Las secuencias puede reutilizarse y nos permiten una edicion facil de las llaves, como se ve en las funciones de obtener e insertar una llave son las mas costosas de tiempo ya que solo se usan durante la edicion, no obstante las he optimizado un poco comenzando las busquedas de tiempo por el final ya que lo normal es editar las ultimas llaves.
La clase control es la encargada de reproducir una secuencia y es la que optimiza el tema de buscar las llaves, cada vez que llamamos a update tiene como objetivo encontrar las dos llaves que delimitan el tiempo, como memorizamos la ultima llave usada este proceso no es en nada costoso, solo añadir que siuna secuencia es modificada se debe de tener cuidado si se ha llamado al update de un control que usa esa secuencia, ya que puede que la llave que tenga memorizada como la ultima no sea correcta, para solucionar esto cada vez que se modifica una secuencia se debera de volver a indicar a todos sus controles, realmente esto no es problema ya que solo pasa en tiempo de edicion.
Como vemos he creado una clase derivada de key que usa templates, esta clase crear una interpolacion lineal muy basica, pero es funcional.
La clase dib::Collecion::Strip es una lista doblemente enlazada y la clase dib::Collecion::Strip::Item es el item que almacena el elemento, su siguiente y anterior item, en c-sharp ya existe una con lo que podreis adaptarlo facilmente.
Ahora os pongo un ejemplo de como funciona:

// seq = Sequence
// control = Control
// ColorInt = Clase que tiene int r,g,b,a para poder realizar operaciones con sus valores sin perdidas
// Crearmos una secuencia y actualizamos llave resultado this->color con el tiempo 10
this->seq = new dib::Anim::Sequence( 201 );
this->seq->SetKey( 0, new dib::Anim::Lerp<dib::Graphics::ColorInt>(dib::Graphics::ColorInt(0,81,174,0)) );
this->seq->SetKey( 50, new dib::Anim::Lerp<dib::Graphics::ColorInt>(dib::Graphics::ColorInt(0,0,255,0)) );
this->seq->SetKey( 100, new dib::Anim::Lerp<dib::Graphics::ColorInt>(dib::Graphics::ColorInt(0,255,0,0)) );
this->seq->SetKey( 150, new dib::Anim::Lerp<dib::Graphics::ColorInt>(dib::Graphics::ColorInt(200,0,0,0)) );
this->seq->SetKey( 200, new dib::Anim::Lerp<dib::Graphics::ColorInt>(dib::Graphics::ColorInt(0,0,255,0)) );
this->control.SetSequence( this->seq );
this->tiempo = 10;
this->control.SetTime( this->tiempo );
this->control.Update( &this->color );
// Realizamos la prueba de interpolacion
while( true )
{
this->tiempo++;
this->control.SetTime( this->tiempo );
this->control.Update( &this->color );
// Cambiar color o mirarlo o lo que sea
}

Como se ve su funcionamiento es la mar de sencillo, ahora explico como lleva esto a nodos de 3dsmax:

struct Nodo
{
 Nodo *padre, *siguiente, *anterior, .....;
 ...
 dib::Anim::Lerp<float> x, y, z;
 dib::Anim::Control controlX, controlY, controlZ;
};

Como se ve en esta estructura los valores X, Y y Z son llaves tipo float, tambien se ve que tenemos tres controles, uno para cada posicion, siempre que queramos actualizar la posicion X la Y o la Z mediante una secuencia de animacion deberemos de realizar esto:

nodo->controlX->SetTime( tiempo );
nodo->controlY->SetTime( tiempo );
nodo->controlZ->SetTime( tiempo );
nodo->controlX->Update( &nodo->x );
nodo->controlY->Update( &nodo->y );
nodo->controlZ->Update( &nodo->z );

Como se es sencillo ;), si no existiera una secuencia asignada al control la variable no cambiaria de valor.

Bueno pues esto es todo, espero que ahora si que os sirva :lol:

PD: Podiais copiar esto y pegarlo para el codigo del mes (ole)  

ethernet

 Oido cocina, lo pondré como COTW.






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.