Entrega 3 - Computacion-Visual-Interactiva/proyecto GitHub Wiki

1. Preparación y separación del modelo

1.1 Separación de sub-mallas en Blender

  • Cómo se hizo: Se accedió al Edit Mode, se seleccionaron las caras correspondientes a cada ala y al cuerpo, y se utilizó la opción P → Selection para separarlos como objetos independientes.

  • Por qué: La animación de aleteo requiere que cada ala tenga sus propios vértices para rotar de forma independiente, sin afectar el cuerpo.

1.2 Asignación de nombres significativos

  • Los objetos se renombraron como Body, LeftWing y RightWing.

  • Esto permite que al exportar el modelo en formato OBJ, los nombres se conserven en las cabeceras (o Body, o LeftWing, o RightWing) y el script Python los utilice para determinar a qué parte pertenece cada triángulo.

1.3 Exportación OBJ con grupos y objetos

  • Se activaron las opciones Objects as OBJ Objects y Objects as OBJ Groups al exportar.

  • Esto introduce líneas o ... y g ... en el archivo, facilitando que el conversor Python asigne correctamente un flag a cada vértice: -1 para el ala izquierda, +1 para la derecha, 0 para el cuerpo.


2. Conversor Python → butterfly_verts.hpp

2.1 Deduplicación de vértices con flag

python
CopiarEditar
key = (vi, ti, wingFlag) # vi = índice posición, ti = índice UV
  • Por qué: Si no se incluye el wingFlag como parte de la clave, vértices compartidos entre el cuerpo y un ala se fusionarían, lo que provoca que el cuerpo también se anime como ala.

2.2 Estructura del vértice en C++

cpp
CopiarEditar
struct Vertex { float3 Pos; float2 UV; float Wing; };
  • Se genera un array interleaved de 6 floats por vértice: (x, y, z), (u, v) y flag.

2.3 Índices únicos sin repetición

  • Se utiliza un OrderedDict para mapear por primera vez cada tupla (vi, ti, flag) a un índice. En llamadas siguientes, se reutiliza ese índice, evitando duplicados.


3. Cambios en la capa de renderizado (C++)

3.1 Input-layout

cpp
CopiarEditar
{0, 0, 3, VT_FLOAT32, false}, // Pos → ATTRIB0 {1, 0, 2, VT_FLOAT32, false}, // UV → ATTRIB1 {2, 0, 1, VT_FLOAT32, false}, // Flag → ATTRIB2
  • Por qué: Se configuran los tres atributos esperados por el shader. El layout usa un único slot con stride = sizeof(Vertex) (24 bytes).

3.2 Constant buffer del VS

cpp
CopiarEditar
struct VSConstants { float4x4 WorldViewProj; float WingAngle; };
  • Por qué: Al pasar WingAngle como constante al vertex shader, se evita recalcular senos y cosenos en cada vértice. La frecuencia del batido queda bajo control del CPU.

3.3 Animación por cuadro (frame)

cpp
CopiarEditar
float angularRevPS = kSpeed / (2*PI_F); float wingFreq = kWingFactor * angularRevPS; CB->WingAngle = sin(m_PathTime * 2*PI_F * wingFreq) * kWingAmp;
  • Por qué: La frecuencia del aleteo está ligada a la velocidad de vuelo. Reducir kSpeed ralentiza también el batido.


4. Lógica de vuelo (Update())

4.1 Posición circular

cpp
CopiarEditar
float theta = m_PathTime * kSpeed; pos = { kRadius*cosθ, y(t), kRadius*sinθ };

4.2 Bobbing suave (ascenso/descenso)

cpp
CopiarEditar
y(t) = kBaseHeight + kBobAmp * sin(2π kBobFreq t)
  • Se agrega un armónico suave para romper la simetría perfecta del movimiento.

4.3 Orientación tangencial sin cabeceo

cpp
CopiarEditar
forward = normalize({ -kRadius*sinθ, 0, kRadius*cosθ }); World = MakeWorld(pos, forward, {0, 1, 0});
  • Por qué: El cuerpo sólo rota en yaw (alrededor del eje Y), lo que evita movimientos bruscos y mantiene la estabilidad visual.


5. Vertex Shader (cube.vsh)

hlsl
CopiarEditar
if (abs(IN.WingFlg) > 0.5) { float pivot = PIVOT_X * IN.WingFlg; p.x -= pivot;
float a = g_WingAngle * IN.WingFlg;
float2 xy = mul(float2x2(cos(a), -sin(a), sin(a), cos(a)), p.xy);

p.x = xy.x;  p.y = xy.y;
p.x += pivot;

}

  • Traslación a la bisagra: evita que el ala se desprenda visualmente.

  • Rotación en XY (eje Z): produce un aleteo vertical puro.

  • Signo opuesto: hace que ambas alas suban y bajen en sincronía.

  • Exclusión del cuerpo: como WingFlg == 0, el cuerpo no entra al bloque.


6. Parámetros calibrados

Parámetro Valor Justificación
kRadius 6 u Circulación amplia y legible.
kSpeed 0.20 rad/s Vuelo pausado (31s por vuelta).
kBobAmp 0.25 u Ondulación perceptible.
kBobFreq 0.8 Hz 3 bobs por vuelta.
kWingFactor 12 ~3 batidos por bob.
kWingAmp 0.35 rad Aleteo de ~20°.
PIVOT_X 0.02 u Bisagra realista visualmente.

7. Resultados observables

  • Movimiento: La mariposa describe un vuelo circular fluido, sube y baja suavemente, y se orienta tangencialmente sin cabecear.

  • Animación: Ambas alas se abren y cierran de forma sincronizada, con una amplitud y frecuencia que se ajusta dinámicamente a la velocidad.

  • Modularidad: Cambiar radio, velocidad o amplitud solo requiere ajustar constantes; no es necesario modificar geometría ni shaders.

  • Rendimiento: Solo se realiza un draw-call, con un buffer y un vertex shader simples. La animación es O(1) por vértice.

8 Resultado

2025-05-19-14-16-49.mp4
⚠️ **GitHub.com Fallback** ⚠️