Sprint 3: Visualización de Audio



Objectivos

  • Realizar el picking de los elementos del secuenciador.

  • Finalización de la escena completa con todos los elementos visuales.

  • Realizar las animaciones de los elementos para que represente visualmente la musica.

Referencias


Desarrollo

Siendo este el sprint mas largo, fue en el que mas trabajo se realizo. Primero, se implemento el Picking que no se realizo en el Sprint pasado. Para este, tomando la referencia mostrada anteriormente, se creo una nueva clase Picking Entity, la cual maneja la detección de la colisión de un rayo al bounding box del objeto, tomando en cuenta su orientación. De esta forma, cuando se requiere que un objeto sea seleccionable, se crea un PickingEntity asociado.

export default class PickingEntity {
  constructor(code, entity, aabb) {
    this.code = code;
    this.entity = entity;
    this.aabb_max = aabb;
    this.aabb_min = vec3.create();
    vec3.scale(this.aabb_min, aabb, -1.0);
    this.distance = 100000.0;
  }

  TestForPicking(pos, dir) {
    var tMin = 0.0;
    var tMax = 100000.0;
    this.distance = tMax;

    var wm = this.entity.worldMatrix;
    var obbPos = vec3.fromValues(wm[12], wm[13], wm[14]);
    var delta = vec3.create();
    vec3.subtract(delta, obbPos, pos);

    var xAxis = vec3.fromValues(wm[0], wm[1], wm[2]);
    var ex = vec3.dot(xAxis, delta);
    var fx = vec3.dot(dir, xAxis);

    if(Math.abs(fx)>0.001) {
      var t1x = (ex+this.aabb_min[0])/fx;
      var t2x = (ex+this.aabb_max[0])/fx;
      if (t1x>t2x) {
        var temp=t1x;
        t1x=t2x;
        t2x=temp;
      }
      if ( t2x < tMax ) tMax = t2x;
      if ( t1x > tMin ) tMin = t1x;
      if ( tMax < tMin ) return false;
    }
    else if(-ex+this.aabb_min[0] > 0.0 || -ex+this.aabb_max[0] < 0.0) {
      return false;
    }

    var yAxis = vec3.fromValues(wm[4], wm[5], wm[6]);
    var ey = vec3.dot(yAxis, delta);
    var fy = vec3.dot(dir, yAxis);

    if(Math.abs(fy)>0.001) {
      var t1y = (ey+this.aabb_min[1])/fy;
      var t2y = (ey+this.aabb_max[1])/fy;
      if (t1y>t2y) {
        var temp=t1y;
        t1y=t2y;
        t2y=temp;
      }
      if ( t2y < tMax ) tMax = t2y;
      if ( t1y > tMin ) tMin = t1y;
      if ( tMax < tMin ) return false;
    }
    else if(-ey+this.aabb_min[1] > 0.0 || -ey+this.aabb_max[1] < 0.0) {
      return false;
    }

    var zAxis = vec3.fromValues(wm[8], wm[9], wm[10]);
    var ez = vec3.dot(zAxis, delta);
    var fz = vec3.dot(dir, zAxis);

    if(Math.abs(fz)>0.001) {
      var t1z = (ez+this.aabb_min[2])/fz;
      var t2z = (ez+this.aabb_max[2])/fz;
      if (t1z>t2z) {
        var temp=t1z;
        t1z=t2z;
        t2z=temp;
      }
      if ( t2z < tMax ) tMax = t2z;
      if ( t1z > tMin ) tMin = t1z;
      if ( tMax < tMin ) return false;
    }
    else if(-ez+this.aabb_min[2] > 0.0 || -ez+this.aabb_max[2] < 0.0) {
      return false;
    }

    this.distance = tMin;
    return true;
  }
}

Posteriormente, me di a la tarea de crear las interfaces de usuario de cada instrumento, lo cual no fue tan sencillo como creia. La organización de los botones en la interfaz era crucial para diferenciar cada instrumento entre si y que sea intuible de utilizar.

Sin embargo, considere que la interfaz no era muy intuitiva. Por lo que cree un menu con información pertinente al instrumento, de forma que el usuario pueda entender mejor como utilizarlo.

Con los interfaces creadas, se implemento su funcionalidad. Lo cual, utilizando la clase anteriormente presentada, fue un procedimiento trivial.

Posteriormente, me vi en la tarea de crear uno de los aspectos mas importantes del proyecto: las animaciones que se generan dada las secuencias creadas por los usuarios. Llegue a la decición que se realizarian utilizando una primitivas sencillas (cubos y esferas) con transformaciones basicas que se den segun las secuencias creadas.

Para facilitar esto, cree una clase que maneje las animaciones:

export default class Animation {
  constructor(name, entity, type, start, end, time, delay) {
    this.name = name;
    this.entity = entity;
    this.type = type;
    this.start = start;
    this.end = end;
    this.time = time;
    this.startTime = 0;
    this.needStart = true;
    this.delay = delay;
  }

  SetStart(now) {
    this.needStart = false;
    this.startTime = now;
    switch (this.type) {
      case 'color':
        this.entity.ChangeColor(this.start);
        break;
      case 'position':
        this.entity.ResetLocalMatrix();
        this.entity.Translate(this.start);
        break;
    }
  }

  CalculateFrame(now) {
    switch (this.type) {
      case 'color':
        return this.HandleColorAnimation(now);
        break;
      case 'position':
        return this.HandlePositionAnimation(now);
        break;
    }
  }

  HandleColorAnimation(now) {
    if(now < this.startTime + this.delay) {
      return;
    }
    var i = (now - this.startTime + this.delay) / this.time;
    if (i<1) {
      var color = vec3.create();
      vec3.lerp(color, this.start, this.end, i);
      this.entity.ChangeColor(color);
      return false;
    }
    else {
      this.entity.ChangeColor(this.end);
      return true;
    }
  }

  HandlePositionAnimation(now) {
    if(now < this.startTime + this.delay) {
      return;
    }
    var i = (now - this.startTime + this.delay) / this.time;
    if (i<1) {
      var pos = vec3.create();
      vec3.lerp(pos, this.start, this.end, i);
      this.entity.ResetLocalMatrix();
      this.entity.Translate(pos);
      return false;
    }
    else {
      this.entity.ResetLocalMatrix();
      this.entity.Translate(this.end);
      return true;
    }
  }
}

Como se puede ver, solo se implementaron animaciones de cambio de color y de translación. Creada esta clase, para generan una animación solo se debe crear una nueva instancia de esta. El GraphicEngine recorre todas las animaciones y calcula el fotograma actual, manejando las animaciones que ya finalizaron y los delays.

Por ultimo, modele en blender un personaje que funcionara como Avatar para que los usuarios vean las demas personas conectadas. Al igual que todos los modelos creados en blender, se utilizo esta herramienta online para convertir el archivo .obj en un json, el cual es leido e interpretado por el engine.

Y con eso, di por finalizado el proyecto del curso. Sin embargo, planeo en el futuro mejorar la aplicación. Algunos de los apsectos quq me gustaria mejorar son las animaciones de la musica, mejorar los modelos e interfaces de los instrumentos, y crear animaciones a los avatars para que bailen al ritmo de la musica.

results matching ""

    No results matching ""