Mejoras animaciones 3D: tumbling en cilindro + pivoteo en wave grid

- **CylinderShape**: Añadido sistema de tumbling ocasional
  - Volteretas de 90° en eje X cada 3-5 segundos
  - Interpolación suave con ease-in-out (1.5s)
  - Rotación Y continua + tumbling X ocasional = más dinámico

- **WaveGridShape**: Reemplazada rotación por pivoteo sutil
  - Eliminada rotación completa en eje Y
  - Pivoteo en esquinas (oscilación lenta 0.3/0.5 rad/s)
  - Grid paralelo a pantalla con efecto "sábana ondeando"
  - Ondas sinusoidales + pivoteo = movimiento más orgánico

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-04 11:14:29 +02:00
parent af3274e9bc
commit 2ae515592d
4 changed files with 77 additions and 22 deletions

View File

@@ -1,11 +1,15 @@
#include "cylinder_shape.h" #include "cylinder_shape.h"
#include "../defines.h" #include "../defines.h"
#include <cmath> #include <cmath>
#include <cstdlib> // Para rand()
void CylinderShape::generatePoints(int num_points, float screen_width, float screen_height) { void CylinderShape::generatePoints(int num_points, float screen_width, float screen_height) {
num_points_ = num_points; num_points_ = num_points;
radius_ = screen_height * CYLINDER_RADIUS_FACTOR; radius_ = screen_height * CYLINDER_RADIUS_FACTOR;
height_ = screen_height * CYLINDER_HEIGHT_FACTOR; height_ = screen_height * CYLINDER_HEIGHT_FACTOR;
// Inicializar timer de tumbling con valor aleatorio (3-5 segundos)
tumble_timer_ = 3.0f + (rand() % 2000) / 1000.0f;
// Las posiciones 3D se calculan en getPoint3D() usando ecuaciones paramétricas del cilindro // Las posiciones 3D se calculan en getPoint3D() usando ecuaciones paramétricas del cilindro
} }
@@ -14,8 +18,39 @@ void CylinderShape::update(float delta_time, float screen_width, float screen_he
radius_ = screen_height * CYLINDER_RADIUS_FACTOR; radius_ = screen_height * CYLINDER_RADIUS_FACTOR;
height_ = screen_height * CYLINDER_HEIGHT_FACTOR; height_ = screen_height * CYLINDER_HEIGHT_FACTOR;
// Actualizar ángulo de rotación en eje Y // Actualizar ángulo de rotación en eje Y (siempre activo)
angle_y_ += CYLINDER_ROTATION_SPEED_Y * delta_time; angle_y_ += CYLINDER_ROTATION_SPEED_Y * delta_time;
// Sistema de tumbling ocasional
if (is_tumbling_) {
// Estamos en tumble: animar angle_x hacia el objetivo
tumble_duration_ += delta_time;
float tumble_progress = tumble_duration_ / 1.5f; // 1.5 segundos de duración
if (tumble_progress >= 1.0f) {
// Tumble completado
angle_x_ = tumble_target_;
is_tumbling_ = false;
tumble_timer_ = 3.0f + (rand() % 2000) / 1000.0f; // Nuevo timer (3-5s)
} else {
// Interpolación suave con ease-in-out
float t = tumble_progress;
float ease = t < 0.5f
? 2.0f * t * t
: 1.0f - (-2.0f * t + 2.0f) * (-2.0f * t + 2.0f) / 2.0f;
angle_x_ = ease * tumble_target_;
}
} else {
// No estamos en tumble: contar tiempo
tumble_timer_ -= delta_time;
if (tumble_timer_ <= 0.0f) {
// Iniciar nuevo tumble
is_tumbling_ = true;
tumble_duration_ = 0.0f;
// Objetivo: PI/2 radianes (90°) o -PI/2
tumble_target_ = angle_x_ + ((rand() % 2) == 0 ? PI * 0.5f : -PI * 0.5f);
}
}
} }
void CylinderShape::getPoint3D(int index, float& x, float& y, float& z) const { void CylinderShape::getPoint3D(int index, float& x, float& y, float& z) const {
@@ -56,15 +91,21 @@ void CylinderShape::getPoint3D(int index, float& x, float& y, float& z) const {
float y_base = (height_ * 0.5f) * v; // Centrar verticalmente float y_base = (height_ * 0.5f) * v; // Centrar verticalmente
float z_base = radius_ * sinf(u); float z_base = radius_ * sinf(u);
// Aplicar rotación en eje Y // Aplicar rotación en eje Y (principal, siempre activa)
float cos_y = cosf(angle_y_); float cos_y = cosf(angle_y_);
float sin_y = sinf(angle_y_); float sin_y = sinf(angle_y_);
float x_rot = x_base * cos_y - z_base * sin_y; float x_rot_y = x_base * cos_y - z_base * sin_y;
float z_rot = x_base * sin_y + z_base * cos_y; float z_rot_y = x_base * sin_y + z_base * cos_y;
// Retornar coordenadas finales rotadas // Aplicar rotación en eje X (tumbling ocasional)
x = x_rot; float cos_x = cosf(angle_x_);
y = y_base; float sin_x = sinf(angle_x_);
float y_rot = y_base * cos_x - z_rot_y * sin_x;
float z_rot = y_base * sin_x + z_rot_y * cos_x;
// Retornar coordenadas finales con ambas rotaciones
x = x_rot_y;
y = y_rot;
z = z_rot; z = z_rot;
} }

View File

@@ -3,15 +3,22 @@
#include "shape.h" #include "shape.h"
// Figura: Cilindro 3D rotante // Figura: Cilindro 3D rotante
// Comportamiento: Superficie cilíndrica con rotación en eje Y // Comportamiento: Superficie cilíndrica con rotación en eje Y + tumbling ocasional en X/Z
// Ecuaciones: x = r*cos(u), y = v, z = r*sin(u) // Ecuaciones: x = r*cos(u), y = v, z = r*sin(u)
class CylinderShape : public Shape { class CylinderShape : public Shape {
private: private:
float angle_y_ = 0.0f; // Ángulo de rotación en eje Y (rad) float angle_y_ = 0.0f; // Ángulo de rotación en eje Y (rad)
float angle_x_ = 0.0f; // Ángulo de rotación en eje X (tumbling ocasional)
float radius_ = 0.0f; // Radio del cilindro (píxeles) float radius_ = 0.0f; // Radio del cilindro (píxeles)
float height_ = 0.0f; // Altura del cilindro (píxeles) float height_ = 0.0f; // Altura del cilindro (píxeles)
int num_points_ = 0; // Cantidad de puntos generados int num_points_ = 0; // Cantidad de puntos generados
// Sistema de tumbling ocasional
float tumble_timer_ = 0.0f; // Temporizador para próximo tumble
float tumble_duration_ = 0.0f; // Duración del tumble actual
bool is_tumbling_ = false; // ¿Estamos en modo tumble?
float tumble_target_ = 0.0f; // Ángulo objetivo del tumble
public: public:
void generatePoints(int num_points, float screen_width, float screen_height) override; void generatePoints(int num_points, float screen_width, float screen_height) override;
void update(float delta_time, float screen_width, float screen_height) override; void update(float delta_time, float screen_width, float screen_height) override;

View File

@@ -34,8 +34,10 @@ void WaveGridShape::update(float delta_time, float screen_width, float screen_he
grid_size_ = screen_height * WAVE_GRID_SIZE_FACTOR; grid_size_ = screen_height * WAVE_GRID_SIZE_FACTOR;
amplitude_ = screen_height * WAVE_GRID_AMPLITUDE; amplitude_ = screen_height * WAVE_GRID_AMPLITUDE;
// Actualizar rotación en eje Y (horizontal) // Pivoteo sutil en ejes X e Y (esquinas adelante/atrás, izq/der)
angle_y_ += WAVE_GRID_ROTATION_SPEED_Y * delta_time; // Usamos velocidades lentas para movimiento sutil y orgánico
tilt_x_ += 0.3f * delta_time; // Pivoteo vertical (esquinas arriba/abajo)
tilt_y_ += 0.5f * delta_time; // Pivoteo horizontal (esquinas izq/der)
// Actualizar fase de las ondas (animación) // Actualizar fase de las ondas (animación)
phase_ += WAVE_GRID_PHASE_SPEED * delta_time; phase_ += WAVE_GRID_PHASE_SPEED * delta_time;
@@ -70,18 +72,22 @@ void WaveGridShape::getPoint3D(int index, float& x, float& y, float& z) const {
// z = amplitude * sin(frequency * x + phase) * cos(frequency * y + phase) // z = amplitude * sin(frequency * x + phase) * cos(frequency * y + phase)
float kx = WAVE_GRID_FREQUENCY * PI; // Frecuencia en X float kx = WAVE_GRID_FREQUENCY * PI; // Frecuencia en X
float ky = WAVE_GRID_FREQUENCY * PI; // Frecuencia en Y float ky = WAVE_GRID_FREQUENCY * PI; // Frecuencia en Y
float z_base = amplitude_ * sinf(kx * u + phase_) * cosf(ky * v + phase_); float z_wave = amplitude_ * sinf(kx * u + phase_) * cosf(ky * v + phase_);
// Aplicar rotación en eje Y (horizontal) // Añadir pivoteo sutil: esquinas se mueven adelante/atrás según posición
float cos_y = cosf(angle_y_); // tilt_x oscila esquinas arriba/abajo, tilt_y oscila esquinas izq/der
float sin_y = sinf(angle_y_); float tilt_amount_x = sinf(tilt_x_) * 0.15f; // Máximo 15% del grid_size
float x_rot = x_base * cos_y - z_base * sin_y; float tilt_amount_y = sinf(tilt_y_) * 0.1f; // Máximo 10% del grid_size
float z_rot = x_base * sin_y + z_base * cos_y;
// Retornar coordenadas finales float z_tilt = (u * tilt_amount_y + v * tilt_amount_x) * grid_size_;
x = x_rot;
// Z final = ondas + pivoteo
float z_final = z_wave + z_tilt;
// Retornar coordenadas (grid paralelo a pantalla, sin rotación global)
x = x_base;
y = y_base; y = y_base;
z = z_rot; z = z_final;
} }
float WaveGridShape::getScaleFactor(float screen_height) const { float WaveGridShape::getScaleFactor(float screen_height) const {

View File

@@ -3,11 +3,12 @@
#include "shape.h" #include "shape.h"
// Figura: Malla ondeante 3D (Wave Grid) // Figura: Malla ondeante 3D (Wave Grid)
// Comportamiento: Grid 2D con ondas sinusoidales en Z + rotación // Comportamiento: Grid 2D paralelo a pantalla con ondas + pivoteo sutil en esquinas
// Ecuaciones: z = A * sin(kx*x + phase) * cos(ky*y + phase) // Ecuaciones: z = A * sin(kx*x + phase) * cos(ky*y + phase) + pivoteo
class WaveGridShape : public Shape { class WaveGridShape : public Shape {
private: private:
float angle_y_ = 0.0f; // Ángulo de rotación en eje Y (rad) float tilt_x_ = 0.0f; // Ángulo de pivoteo en eje X (esquinas adelante/atrás)
float tilt_y_ = 0.0f; // Ángulo de pivoteo en eje Y (esquinas izq/der)
float phase_ = 0.0f; // Fase de animación de ondas (rad) float phase_ = 0.0f; // Fase de animación de ondas (rad)
float grid_size_ = 0.0f; // Tamaño del grid (píxeles) float grid_size_ = 0.0f; // Tamaño del grid (píxeles)
float amplitude_ = 0.0f; // Amplitud de las ondas (píxeles) float amplitude_ = 0.0f; // Amplitud de las ondas (píxeles)