Compare commits
4 Commits
af3274e9bc
...
0d49a6e814
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d49a6e814 | |||
| d0b144dddc | |||
| 06aabc53c0 | |||
| 2ae515592d |
55
ROADMAP.md
55
ROADMAP.md
@@ -56,21 +56,47 @@
|
||||
- Estados: NORMAL, TRANSITION, ALTERNATIVE
|
||||
- Interpolación suave entre modos de rotación
|
||||
|
||||
### 2. ⏳ Modo DEMO (Auto-play)
|
||||
### 2. ✅ Modo DEMO (Auto-play)
|
||||
**Descripción:** Modo demostración automática con acciones aleatorias
|
||||
**Prioridad:** Alta
|
||||
**Estimación:** 1.5 horas
|
||||
**Estado:** ✅ COMPLETADO
|
||||
**Detalles:**
|
||||
- Toggle con tecla `D`
|
||||
- Timer que ejecuta acciones cada 3-8 segundos
|
||||
- Acciones: cambiar gravedad, activar figura, cambiar tema, impulso
|
||||
- Secuencia pseudo-aleatoria pero visualmente coherente
|
||||
- Pausable con cualquier tecla de usuario
|
||||
- Indicador visual "DEMO MODE" en pantalla
|
||||
- ✅ Toggle con tecla `D`
|
||||
- ✅ Timer que ejecuta acciones cada 3-8 segundos (configurable)
|
||||
- ✅ Acciones: gravedad, figuras, temas, escenarios, impulso, profundidad, escala, sprite
|
||||
- ✅ Secuencia pseudo-aleatoria con pesos configurables (defines.h)
|
||||
- ✅ Totalmente interactivo - usuario puede seguir usando controles
|
||||
- ✅ Indicador visual "DEMO MODE" centrado en pantalla (naranja)
|
||||
- ✅ Eliminado sistema auto-restart antiguo (ya no necesario)
|
||||
|
||||
### 3. ⏳ Sistema de Release
|
||||
**Descripción:** Empaquetado para distribución standalone
|
||||
### 3. ⏳ Resolución Lógica Configurable
|
||||
**Descripción:** Especificar resolución lógica por parámetros de línea de comandos
|
||||
**Prioridad:** Media
|
||||
**Estimación:** 1 hora
|
||||
**Detalles:**
|
||||
- Parámetros `--width <px> --height <px>` o `-w <px> -h <px>`
|
||||
- Parámetro `--fullscreen` o `-f` (opcional)
|
||||
- Defaults: 1280x720 en ventana
|
||||
- Ejemplo: `./vibe3_physics -w 1920 -h 1080 -f`
|
||||
- Validación de valores (mínimo 640x480, máximo según SDL_DisplayMode)
|
||||
- Help text con `-h` o `--help`
|
||||
|
||||
### 4. 🐛 Corregir Escalado de Pelotas en Reposo
|
||||
**Descripción:** Las pelotas cambian de tamaño cuando están quietas (bug visual)
|
||||
**Prioridad:** Alta (bug visible)
|
||||
**Estimación:** 30 minutos
|
||||
**Detalles:**
|
||||
- **Síntoma:** Pelotas en reposo (velocidad ≈ 0) se escalan incorrectamente
|
||||
- **Posible causa:**
|
||||
- Scale factor calculado desde velocidad o energía
|
||||
- División por cero o valor muy pequeño
|
||||
- Interpolación incorrecta en modo transición
|
||||
- **Investigar:** Ball::render(), scale calculations, depth brightness
|
||||
- **Solución esperada:** Tamaño constante independiente de velocidad
|
||||
|
||||
### 5. ⏳ Sistema de Release
|
||||
**Descripción:** Empaquetado para distribución standalone
|
||||
**Prioridad:** Baja
|
||||
**Estimación:** 30 minutos
|
||||
**Detalles:**
|
||||
- Crear carpeta `release/`
|
||||
@@ -110,6 +136,15 @@
|
||||
|
||||
## Historial de Cambios
|
||||
|
||||
### 2025-10-04 (Sesión 4) - Modo DEMO
|
||||
- ✅ **Implementado Modo DEMO (auto-play)** - Tecla D para toggle
|
||||
- ✅ Sistema de acciones aleatorias cada 3-8 segundos (configurable)
|
||||
- ✅ 8 tipos de acciones con pesos de probabilidad ajustables
|
||||
- ✅ Totalmente interactivo - usuario puede seguir controlando
|
||||
- ✅ Display visual "DEMO MODE" centrado en naranja
|
||||
- ✅ Mejoras animaciones 3D: tumbling en cilindro + pivoteo en wave grid
|
||||
- ❌ Eliminado sistema auto-restart antiguo (ya no necesario)
|
||||
|
||||
### 2025-10-04 (Sesión 3)
|
||||
- ✅ Implementado tema MONOCHROME (6º tema)
|
||||
- ✅ Sistema LERP para transiciones suaves de temas (0.5s)
|
||||
|
||||
@@ -39,7 +39,6 @@ Ball::Ball(float x, float vx, float vy, Color color, std::shared_ptr<Texture> te
|
||||
screen_width_ = screen_width; // Dimensiones del terreno de juego
|
||||
screen_height_ = screen_height;
|
||||
on_surface_ = false;
|
||||
stopped_ = false;
|
||||
// Coeficiente base IGUAL para todas las pelotas (solo variación por rebote individual)
|
||||
loss_ = BASE_BOUNCE_COEFFICIENT; // Coeficiente fijo para todas las pelotas
|
||||
|
||||
@@ -56,10 +55,6 @@ Ball::Ball(float x, float vx, float vy, Color color, std::shared_ptr<Texture> te
|
||||
|
||||
// Actualiza la lógica de la clase
|
||||
void Ball::update(float deltaTime) {
|
||||
if (stopped_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Aplica la gravedad según la dirección (píxeles/segundo²)
|
||||
if (!on_surface_) {
|
||||
// Aplicar gravedad multiplicada por factor de masa individual
|
||||
@@ -190,7 +185,6 @@ void Ball::update(float deltaTime) {
|
||||
vx_ = vx_ * friction_factor;
|
||||
if (std::fabs(vx_) < 6.0f) {
|
||||
vx_ = 0.0f;
|
||||
stopped_ = true;
|
||||
}
|
||||
break;
|
||||
case GravityDirection::LEFT:
|
||||
@@ -199,7 +193,6 @@ void Ball::update(float deltaTime) {
|
||||
vy_ = vy_ * friction_factor;
|
||||
if (std::fabs(vy_) < 6.0f) {
|
||||
vy_ = 0.0f;
|
||||
stopped_ = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -220,7 +213,6 @@ void Ball::modVel(float vx, float vy) {
|
||||
vx_ = vx_ + (vx * 60.0f); // Convertir a píxeles/segundo
|
||||
vy_ = vy_ + (vy * 60.0f); // Convertir a píxeles/segundo
|
||||
on_surface_ = false;
|
||||
stopped_ = false;
|
||||
}
|
||||
|
||||
// Cambia la gravedad (usa la versión convertida)
|
||||
@@ -249,7 +241,6 @@ void Ball::forceGravityOff() {
|
||||
void Ball::setGravityDirection(GravityDirection direction) {
|
||||
gravity_direction_ = direction;
|
||||
on_surface_ = false; // Ya no está en superficie al cambiar dirección
|
||||
stopped_ = false; // Reactivar movimiento
|
||||
}
|
||||
|
||||
// Aplica un pequeño empuje lateral aleatorio
|
||||
@@ -309,7 +300,6 @@ void Ball::enableRotoBallAttraction(bool enable) {
|
||||
// Al activar atracción, resetear flags de superficie para permitir física completa
|
||||
if (enable) {
|
||||
on_surface_ = false;
|
||||
stopped_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ class Ball {
|
||||
int screen_height_; // Alto del terreno de juego
|
||||
Color color_; // Color de la pelota
|
||||
bool on_surface_; // Indica si la pelota est\u00e1 en la superficie (suelo/techo/pared)
|
||||
bool stopped_; // Indica si la pelota ha terminado de moverse;
|
||||
float loss_; // Coeficiente de rebote. Pérdida de energía en cada rebote
|
||||
|
||||
// Datos para modo RotoBall (esfera 3D)
|
||||
@@ -71,7 +70,6 @@ class Ball {
|
||||
float getLossCoefficient() const { return loss_; }
|
||||
GravityDirection getGravityDirection() const { return gravity_direction_; }
|
||||
bool isOnSurface() const { return on_surface_; }
|
||||
bool isStopped() const { return stopped_; }
|
||||
|
||||
// Getters/Setters para batch rendering
|
||||
SDL_FRect getPosition() const { return pos_; }
|
||||
|
||||
@@ -153,4 +153,34 @@ constexpr float SHAPE_SCALE_MAX = 3.0f; // Escala máxima (300%)
|
||||
constexpr float SHAPE_SCALE_STEP = 0.1f; // Incremento por pulsación
|
||||
constexpr float SHAPE_SCALE_DEFAULT = 1.0f; // Escala por defecto (100%)
|
||||
|
||||
// Configuración de Modo DEMO (auto-play completo)
|
||||
constexpr float DEMO_ACTION_INTERVAL_MIN = 2.0f; // Tiempo mínimo entre acciones (segundos)
|
||||
constexpr float DEMO_ACTION_INTERVAL_MAX = 6.0f; // Tiempo máximo entre acciones (segundos)
|
||||
|
||||
// Pesos de probabilidad DEMO MODE (valores relativos, se normalizan)
|
||||
constexpr int DEMO_WEIGHT_GRAVITY_DIR = 10; // Cambiar dirección gravedad (10%)
|
||||
constexpr int DEMO_WEIGHT_GRAVITY_TOGGLE = 8; // Toggle gravedad ON/OFF (8%)
|
||||
constexpr int DEMO_WEIGHT_SHAPE = 20; // Activar figura 3D (20%)
|
||||
constexpr int DEMO_WEIGHT_TOGGLE_PHYSICS = 12; // Toggle física ↔ figura (12%)
|
||||
constexpr int DEMO_WEIGHT_REGENERATE_SHAPE = 8; // Re-generar misma figura (8%)
|
||||
constexpr int DEMO_WEIGHT_THEME = 15; // Cambiar tema de colores (15%)
|
||||
constexpr int DEMO_WEIGHT_SCENARIO = 10; // Cambiar número de pelotas (10%)
|
||||
constexpr int DEMO_WEIGHT_IMPULSE = 10; // Aplicar impulso (SPACE) (10%)
|
||||
constexpr int DEMO_WEIGHT_DEPTH_ZOOM = 3; // Toggle profundidad (3%)
|
||||
constexpr int DEMO_WEIGHT_SHAPE_SCALE = 2; // Cambiar escala figura (2%)
|
||||
constexpr int DEMO_WEIGHT_SPRITE = 2; // Cambiar sprite (2%)
|
||||
// TOTAL: 100
|
||||
|
||||
// Configuración de Modo DEMO LITE (solo física/figuras)
|
||||
constexpr float DEMO_LITE_ACTION_INTERVAL_MIN = 1.5f; // Más rápido que demo completo
|
||||
constexpr float DEMO_LITE_ACTION_INTERVAL_MAX = 4.0f;
|
||||
|
||||
// Pesos de probabilidad DEMO LITE (solo física/figuras, sin cambios de escenario/tema)
|
||||
constexpr int DEMO_LITE_WEIGHT_GRAVITY_DIR = 25; // Cambiar dirección gravedad (25%)
|
||||
constexpr int DEMO_LITE_WEIGHT_GRAVITY_TOGGLE = 20;// Toggle gravedad ON/OFF (20%)
|
||||
constexpr int DEMO_LITE_WEIGHT_SHAPE = 25; // Activar figura 3D (25%)
|
||||
constexpr int DEMO_LITE_WEIGHT_TOGGLE_PHYSICS = 20;// Toggle física ↔ figura (20%)
|
||||
constexpr int DEMO_LITE_WEIGHT_IMPULSE = 10; // Aplicar impulso (10%)
|
||||
// TOTAL: 100
|
||||
|
||||
constexpr float PI = 3.14159265358979323846f; // Constante PI
|
||||
@@ -166,9 +166,6 @@ void Engine::update() {
|
||||
for (auto &ball : balls_) {
|
||||
ball->update(delta_time_); // Pasar delta time a cada pelota
|
||||
}
|
||||
|
||||
// Verificar auto-reinicio cuando todas las pelotas están quietas (solo en modo física)
|
||||
checkAutoRestart();
|
||||
} else if (current_mode_ == SimulationMode::SHAPE) {
|
||||
// Modo Figura 3D: actualizar figura polimórfica
|
||||
updateShape();
|
||||
@@ -179,6 +176,9 @@ void Engine::update() {
|
||||
show_text_ = !(SDL_GetTicks() - text_init_time_ > TEXT_DURATION);
|
||||
}
|
||||
|
||||
// Actualizar Modo DEMO (auto-play)
|
||||
updateDemoMode();
|
||||
|
||||
// Actualizar transición de tema (LERP)
|
||||
if (transitioning_) {
|
||||
transition_progress_ += delta_time_ / transition_duration_;
|
||||
@@ -468,6 +468,60 @@ void Engine::handleEvents() {
|
||||
case SDLK_F5:
|
||||
toggleIntegerScaling();
|
||||
break;
|
||||
|
||||
// Toggle Modo DEMO COMPLETO (auto-play)
|
||||
case SDLK_D:
|
||||
demo_mode_enabled_ = !demo_mode_enabled_;
|
||||
if (demo_mode_enabled_) {
|
||||
// Desactivar demo lite si estaba activo (mutuamente excluyentes)
|
||||
demo_lite_enabled_ = false;
|
||||
|
||||
// Randomizar TODO al activar
|
||||
randomizeOnDemoStart(false);
|
||||
|
||||
// Inicializar timer con primer intervalo aleatorio
|
||||
demo_timer_ = 0.0f;
|
||||
float interval_range = DEMO_ACTION_INTERVAL_MAX - DEMO_ACTION_INTERVAL_MIN;
|
||||
demo_next_action_time_ = DEMO_ACTION_INTERVAL_MIN + (rand() % 1000) / 1000.0f * interval_range;
|
||||
|
||||
// Mostrar texto de activación
|
||||
text_ = "DEMO MODE ON";
|
||||
show_text_ = true;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
} else {
|
||||
// Al desactivar: mostrar texto
|
||||
text_ = "DEMO MODE OFF";
|
||||
show_text_ = true;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
}
|
||||
break;
|
||||
|
||||
// Toggle Modo DEMO LITE (solo física/figuras)
|
||||
case SDLK_L:
|
||||
demo_lite_enabled_ = !demo_lite_enabled_;
|
||||
if (demo_lite_enabled_) {
|
||||
// Desactivar demo completo si estaba activo (mutuamente excluyentes)
|
||||
demo_mode_enabled_ = false;
|
||||
|
||||
// Randomizar solo física/figura (mantiene escenario y tema)
|
||||
randomizeOnDemoStart(true);
|
||||
|
||||
// Inicializar timer con primer intervalo aleatorio (más rápido)
|
||||
demo_timer_ = 0.0f;
|
||||
float interval_range = DEMO_LITE_ACTION_INTERVAL_MAX - DEMO_LITE_ACTION_INTERVAL_MIN;
|
||||
demo_next_action_time_ = DEMO_LITE_ACTION_INTERVAL_MIN + (rand() % 1000) / 1000.0f * interval_range;
|
||||
|
||||
// Mostrar texto de activación
|
||||
text_ = "DEMO LITE ON";
|
||||
show_text_ = true;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
} else {
|
||||
// Al desactivar: mostrar texto
|
||||
text_ = "DEMO LITE OFF";
|
||||
show_text_ = true;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -608,6 +662,21 @@ void Engine::render() {
|
||||
dbg_print(8, 72, mode_text.c_str(), 0, 255, 128); // Verde claro para modo
|
||||
}
|
||||
|
||||
// Mostrar indicador "DEMO MODE" o "DEMO LITE" permanente (independiente de show_debug_)
|
||||
if (demo_mode_enabled_) {
|
||||
// Centrar texto horizontalmente
|
||||
const char* demo_text = "DEMO MODE";
|
||||
int demo_text_width = static_cast<int>(strlen(demo_text) * 8); // 8 píxeles por carácter
|
||||
int demo_x = (current_screen_width_ - demo_text_width) / 2;
|
||||
dbg_print(demo_x, 8, demo_text, 255, 128, 0); // Naranja brillante
|
||||
} else if (demo_lite_enabled_) {
|
||||
// Centrar texto horizontalmente
|
||||
const char* demo_lite_text = "DEMO LITE";
|
||||
int demo_lite_text_width = static_cast<int>(strlen(demo_lite_text) * 8);
|
||||
int demo_lite_x = (current_screen_width_ - demo_lite_text_width) / 2;
|
||||
dbg_print(demo_lite_x, 8, demo_lite_text, 128, 200, 255); // Azul claro
|
||||
}
|
||||
|
||||
SDL_RenderPresent(renderer_);
|
||||
}
|
||||
|
||||
@@ -645,6 +714,9 @@ void Engine::initBalls(int value) {
|
||||
}
|
||||
|
||||
void Engine::setText() {
|
||||
// Suprimir textos durante modos demo
|
||||
if (demo_mode_enabled_ || demo_lite_enabled_) return;
|
||||
|
||||
int num_balls = test_.at(scenario_);
|
||||
if (num_balls == 1) {
|
||||
text_ = "1 PELOTA";
|
||||
@@ -1137,12 +1209,14 @@ void Engine::startThemeTransition(ColorTheme new_theme) {
|
||||
transitioning_ = true;
|
||||
transition_progress_ = 0.0f;
|
||||
|
||||
// Mostrar nombre del tema (igual que selección directa con KP_1-6)
|
||||
ThemeColors& theme = themes_[static_cast<int>(new_theme)];
|
||||
text_ = theme.name_es;
|
||||
text_pos_ = (current_screen_width_ - static_cast<int>(text_.length() * 8)) / 2;
|
||||
show_text_ = true;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
// Mostrar nombre del tema (solo si NO estamos en modo demo)
|
||||
if (!demo_mode_enabled_ && !demo_lite_enabled_) {
|
||||
ThemeColors& theme = themes_[static_cast<int>(new_theme)];
|
||||
text_ = theme.name_es;
|
||||
text_pos_ = (current_screen_width_ - static_cast<int>(text_.length() * 8)) / 2;
|
||||
show_text_ = true;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
}
|
||||
}
|
||||
|
||||
Color Engine::getInterpolatedColor(size_t ball_index) const {
|
||||
@@ -1167,48 +1241,282 @@ Color Engine::getInterpolatedColor(size_t ball_index) const {
|
||||
};
|
||||
}
|
||||
|
||||
void Engine::checkAutoRestart() {
|
||||
// Verificar si TODAS las pelotas están paradas
|
||||
bool all_stopped = true;
|
||||
for (const auto &ball : balls_) {
|
||||
if (!ball->isStopped()) {
|
||||
all_stopped = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Sistema de Modo DEMO (auto-play)
|
||||
void Engine::updateDemoMode() {
|
||||
// Verificar si algún modo está activo
|
||||
bool is_demo_active = demo_mode_enabled_ || demo_lite_enabled_;
|
||||
if (!is_demo_active) return;
|
||||
|
||||
if (all_stopped) {
|
||||
if (!all_balls_were_stopped_) {
|
||||
// Primera vez que se detecta que todas están paradas
|
||||
all_balls_stopped_start_time_ = SDL_GetTicks();
|
||||
all_balls_were_stopped_ = true;
|
||||
} else {
|
||||
// Ya estaban paradas, verificar tiempo transcurrido
|
||||
Uint64 current_time = SDL_GetTicks();
|
||||
if (current_time - all_balls_stopped_start_time_ >= AUTO_RESTART_DELAY) {
|
||||
performRandomRestart();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Al menos una pelota se está moviendo - resetear temporizador
|
||||
all_balls_were_stopped_ = false;
|
||||
all_balls_stopped_start_time_ = 0;
|
||||
// Actualizar timer
|
||||
demo_timer_ += delta_time_;
|
||||
|
||||
// Si es hora de ejecutar acción
|
||||
if (demo_timer_ >= demo_next_action_time_) {
|
||||
// Ejecutar acción según modo activo
|
||||
bool is_lite = demo_lite_enabled_;
|
||||
performDemoAction(is_lite);
|
||||
|
||||
// Resetear timer y calcular próximo intervalo aleatorio
|
||||
demo_timer_ = 0.0f;
|
||||
|
||||
// Usar intervalos diferentes según modo
|
||||
float interval_min = is_lite ? DEMO_LITE_ACTION_INTERVAL_MIN : DEMO_ACTION_INTERVAL_MIN;
|
||||
float interval_max = is_lite ? DEMO_LITE_ACTION_INTERVAL_MAX : DEMO_ACTION_INTERVAL_MAX;
|
||||
float interval_range = interval_max - interval_min;
|
||||
demo_next_action_time_ = interval_min + (rand() % 1000) / 1000.0f * interval_range;
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::performRandomRestart() {
|
||||
// Escenario aleatorio usando tamaño del array
|
||||
scenario_ = rand() % test_.size();
|
||||
void Engine::performDemoAction(bool is_lite) {
|
||||
int TOTAL_WEIGHT;
|
||||
int random_value;
|
||||
int accumulated_weight = 0;
|
||||
|
||||
// Tema aleatorio usando tamaño del array de temas
|
||||
current_theme_ = static_cast<ColorTheme>(rand() % (sizeof(themes_) / sizeof(themes_[0])));
|
||||
if (is_lite) {
|
||||
// DEMO LITE: Solo física/figuras
|
||||
TOTAL_WEIGHT = DEMO_LITE_WEIGHT_GRAVITY_DIR + DEMO_LITE_WEIGHT_GRAVITY_TOGGLE
|
||||
+ DEMO_LITE_WEIGHT_SHAPE + DEMO_LITE_WEIGHT_TOGGLE_PHYSICS
|
||||
+ DEMO_LITE_WEIGHT_IMPULSE;
|
||||
random_value = rand() % TOTAL_WEIGHT;
|
||||
|
||||
// Reinicializar pelotas con nuevo escenario y tema
|
||||
initBalls(scenario_);
|
||||
// Cambiar dirección gravedad (25%)
|
||||
accumulated_weight += DEMO_LITE_WEIGHT_GRAVITY_DIR;
|
||||
if (random_value < accumulated_weight) {
|
||||
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
||||
changeGravityDirection(new_direction);
|
||||
return;
|
||||
}
|
||||
|
||||
// Resetear temporizador
|
||||
all_balls_were_stopped_ = false;
|
||||
all_balls_stopped_start_time_ = 0;
|
||||
// Toggle gravedad ON/OFF (20%)
|
||||
accumulated_weight += DEMO_LITE_WEIGHT_GRAVITY_TOGGLE;
|
||||
if (random_value < accumulated_weight) {
|
||||
toggleGravityOnOff();
|
||||
return;
|
||||
}
|
||||
|
||||
// Activar figura 3D (25%)
|
||||
accumulated_weight += DEMO_LITE_WEIGHT_SHAPE;
|
||||
if (random_value < accumulated_weight) {
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
int shape_index = rand() % 8;
|
||||
activateShape(shapes[shape_index]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle física ↔ figura (20%)
|
||||
accumulated_weight += DEMO_LITE_WEIGHT_TOGGLE_PHYSICS;
|
||||
if (random_value < accumulated_weight) {
|
||||
toggleShapeMode(false); // NO forzar gravedad al salir
|
||||
return;
|
||||
}
|
||||
|
||||
// Aplicar impulso (10%)
|
||||
accumulated_weight += DEMO_LITE_WEIGHT_IMPULSE;
|
||||
if (random_value < accumulated_weight) {
|
||||
pushBallsAwayFromGravity();
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
// DEMO COMPLETO: Todas las acciones
|
||||
TOTAL_WEIGHT = DEMO_WEIGHT_GRAVITY_DIR + DEMO_WEIGHT_GRAVITY_TOGGLE + DEMO_WEIGHT_SHAPE
|
||||
+ DEMO_WEIGHT_TOGGLE_PHYSICS + DEMO_WEIGHT_REGENERATE_SHAPE + DEMO_WEIGHT_THEME
|
||||
+ DEMO_WEIGHT_SCENARIO + DEMO_WEIGHT_IMPULSE + DEMO_WEIGHT_DEPTH_ZOOM
|
||||
+ DEMO_WEIGHT_SHAPE_SCALE + DEMO_WEIGHT_SPRITE;
|
||||
random_value = rand() % TOTAL_WEIGHT;
|
||||
|
||||
// Cambiar dirección gravedad (10%)
|
||||
accumulated_weight += DEMO_WEIGHT_GRAVITY_DIR;
|
||||
if (random_value < accumulated_weight) {
|
||||
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
||||
changeGravityDirection(new_direction);
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle gravedad ON/OFF (8%)
|
||||
accumulated_weight += DEMO_WEIGHT_GRAVITY_TOGGLE;
|
||||
if (random_value < accumulated_weight) {
|
||||
toggleGravityOnOff();
|
||||
return;
|
||||
}
|
||||
|
||||
// Activar figura 3D (20%)
|
||||
accumulated_weight += DEMO_WEIGHT_SHAPE;
|
||||
if (random_value < accumulated_weight) {
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
int shape_index = rand() % 8;
|
||||
activateShape(shapes[shape_index]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle física ↔ figura (12%)
|
||||
accumulated_weight += DEMO_WEIGHT_TOGGLE_PHYSICS;
|
||||
if (random_value < accumulated_weight) {
|
||||
toggleShapeMode(false); // NO forzar gravedad al salir
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-generar misma figura (8%)
|
||||
accumulated_weight += DEMO_WEIGHT_REGENERATE_SHAPE;
|
||||
if (random_value < accumulated_weight) {
|
||||
if (current_mode_ == SimulationMode::SHAPE && active_shape_) {
|
||||
generateShape(); // Re-generar sin cambiar tipo
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Cambiar tema (15%)
|
||||
accumulated_weight += DEMO_WEIGHT_THEME;
|
||||
if (random_value < accumulated_weight) {
|
||||
ColorTheme new_theme = static_cast<ColorTheme>(rand() % 6);
|
||||
startThemeTransition(new_theme);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cambiar escenario (10%) - EXCLUIR índices 0, 6, 7 (1, 50K, 100K pelotas)
|
||||
accumulated_weight += DEMO_WEIGHT_SCENARIO;
|
||||
if (random_value < accumulated_weight) {
|
||||
// Escenarios válidos: índices 1, 2, 3, 4, 5 (10, 100, 500, 1000, 10000 pelotas)
|
||||
int valid_scenarios[] = {1, 2, 3, 4, 5};
|
||||
scenario_ = valid_scenarios[rand() % 5];
|
||||
initBalls(scenario_);
|
||||
return;
|
||||
}
|
||||
|
||||
// Aplicar impulso (10%)
|
||||
accumulated_weight += DEMO_WEIGHT_IMPULSE;
|
||||
if (random_value < accumulated_weight) {
|
||||
pushBallsAwayFromGravity();
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle profundidad (3%)
|
||||
accumulated_weight += DEMO_WEIGHT_DEPTH_ZOOM;
|
||||
if (random_value < accumulated_weight) {
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
depth_zoom_enabled_ = !depth_zoom_enabled_;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Cambiar escala de figura (2%)
|
||||
accumulated_weight += DEMO_WEIGHT_SHAPE_SCALE;
|
||||
if (random_value < accumulated_weight) {
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
int scale_action = rand() % 3;
|
||||
if (scale_action == 0) {
|
||||
shape_scale_factor_ += SHAPE_SCALE_STEP;
|
||||
} else if (scale_action == 1) {
|
||||
shape_scale_factor_ -= SHAPE_SCALE_STEP;
|
||||
} else {
|
||||
shape_scale_factor_ = SHAPE_SCALE_DEFAULT;
|
||||
}
|
||||
clampShapeScale();
|
||||
generateShape();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Cambiar sprite (2%)
|
||||
accumulated_weight += DEMO_WEIGHT_SPRITE;
|
||||
if (random_value < accumulated_weight) {
|
||||
switchTexture();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Randomizar todo al iniciar modo DEMO
|
||||
void Engine::randomizeOnDemoStart(bool is_lite) {
|
||||
if (is_lite) {
|
||||
// DEMO LITE: Solo randomizar física/figura + gravedad
|
||||
// Elegir aleatoriamente entre modo física o figura
|
||||
if (rand() % 2 == 0) {
|
||||
// Modo física
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
toggleShapeMode(false); // Salir a física sin forzar gravedad
|
||||
}
|
||||
} else {
|
||||
// Modo figura: elegir figura aleatoria
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
activateShape(shapes[rand() % 8]);
|
||||
}
|
||||
|
||||
// Randomizar gravedad: dirección + ON/OFF
|
||||
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
||||
changeGravityDirection(new_direction);
|
||||
if (rand() % 2 == 0) {
|
||||
toggleGravityOnOff(); // 50% probabilidad de desactivar gravedad
|
||||
}
|
||||
|
||||
} else {
|
||||
// DEMO COMPLETO: Randomizar TODO
|
||||
|
||||
// 1. Escenario (excluir índices 0, 6, 7)
|
||||
int valid_scenarios[] = {1, 2, 3, 4, 5};
|
||||
scenario_ = valid_scenarios[rand() % 5];
|
||||
initBalls(scenario_);
|
||||
|
||||
// 2. Tema
|
||||
ColorTheme new_theme = static_cast<ColorTheme>(rand() % 6);
|
||||
startThemeTransition(new_theme);
|
||||
|
||||
// 3. Sprite
|
||||
if (rand() % 2 == 0) {
|
||||
switchTexture();
|
||||
}
|
||||
|
||||
// 4. Física o Figura
|
||||
if (rand() % 2 == 0) {
|
||||
// Modo física
|
||||
if (current_mode_ == SimulationMode::SHAPE) {
|
||||
toggleShapeMode(false);
|
||||
}
|
||||
} else {
|
||||
// Modo figura: elegir figura aleatoria
|
||||
ShapeType shapes[] = {ShapeType::SPHERE, ShapeType::WAVE_GRID, ShapeType::HELIX,
|
||||
ShapeType::TORUS, ShapeType::CUBE, ShapeType::CYLINDER,
|
||||
ShapeType::ICOSAHEDRON, ShapeType::ATOM};
|
||||
activateShape(shapes[rand() % 8]);
|
||||
|
||||
// 5. Profundidad (solo si estamos en figura)
|
||||
if (rand() % 2 == 0) {
|
||||
depth_zoom_enabled_ = !depth_zoom_enabled_;
|
||||
}
|
||||
|
||||
// 6. Escala de figura (aleatoria entre 0.5x y 2.0x)
|
||||
shape_scale_factor_ = 0.5f + (rand() % 1500) / 1000.0f;
|
||||
clampShapeScale();
|
||||
generateShape();
|
||||
}
|
||||
|
||||
// 7. Gravedad: dirección + ON/OFF
|
||||
GravityDirection new_direction = static_cast<GravityDirection>(rand() % 4);
|
||||
changeGravityDirection(new_direction);
|
||||
if (rand() % 3 == 0) { // 33% probabilidad de desactivar gravedad
|
||||
toggleGravityOnOff();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle gravedad ON/OFF para todas las pelotas
|
||||
void Engine::toggleGravityOnOff() {
|
||||
// Alternar entre activar/desactivar gravedad
|
||||
bool first_ball_gravity_enabled = (balls_.empty() || balls_[0]->getGravityForce() > 0.0f);
|
||||
|
||||
if (first_ball_gravity_enabled) {
|
||||
// Desactivar gravedad
|
||||
forceBallsGravityOff();
|
||||
} else {
|
||||
// Activar gravedad
|
||||
forceBallsGravityOn();
|
||||
}
|
||||
}
|
||||
|
||||
// Sistema de cambio de sprites dinámico
|
||||
@@ -1277,12 +1585,14 @@ void Engine::switchTexture() {
|
||||
// Ajustar posiciones según el cambio de tamaño
|
||||
updateBallSizes(old_size, new_size);
|
||||
|
||||
// Mostrar texto informativo
|
||||
std::string texture_name = (current_texture_index_ == 0) ? "NORMAL" : "SMALL";
|
||||
text_ = "SPRITE: " + texture_name;
|
||||
text_pos_ = (current_screen_width_ - static_cast<int>(text_.length() * 8)) / 2;
|
||||
show_text_ = true;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
// Mostrar texto informativo (solo si NO estamos en modo demo)
|
||||
if (!demo_mode_enabled_ && !demo_lite_enabled_) {
|
||||
std::string texture_name = (current_texture_index_ == 0) ? "NORMAL" : "SMALL";
|
||||
text_ = "SPRITE: " + texture_name;
|
||||
text_pos_ = (current_screen_width_ - static_cast<int>(text_.length() * 8)) / 2;
|
||||
show_text_ = true;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
}
|
||||
}
|
||||
|
||||
// Sistema de Figuras 3D - Alternar entre modo física y última figura (Toggle con tecla F)
|
||||
@@ -1305,12 +1615,14 @@ void Engine::toggleShapeMode(bool force_gravity_on_exit) {
|
||||
forceBallsGravityOn();
|
||||
}
|
||||
|
||||
// Mostrar texto informativo
|
||||
text_ = "MODO FISICA";
|
||||
int text_width = static_cast<int>(text_.length() * 8);
|
||||
text_pos_ = (current_screen_width_ - text_width) / 2;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
show_text_ = true;
|
||||
// Mostrar texto informativo (solo si NO estamos en modo demo)
|
||||
if (!demo_mode_enabled_ && !demo_lite_enabled_) {
|
||||
text_ = "MODO FISICA";
|
||||
int text_width = static_cast<int>(text_.length() * 8);
|
||||
text_pos_ = (current_screen_width_ - text_width) / 2;
|
||||
text_init_time_ = SDL_GetTicks();
|
||||
show_text_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1365,8 +1677,8 @@ void Engine::activateShape(ShapeType type) {
|
||||
ball->enableRotoBallAttraction(true);
|
||||
}
|
||||
|
||||
// Mostrar texto informativo con nombre de figura
|
||||
if (active_shape_) {
|
||||
// Mostrar texto informativo con nombre de figura (solo si NO estamos en modo demo)
|
||||
if (active_shape_ && !demo_mode_enabled_ && !demo_lite_enabled_) {
|
||||
text_ = std::string("MODO ") + active_shape_->getName();
|
||||
int text_width = static_cast<int>(text_.length() * 8);
|
||||
text_pos_ = (current_screen_width_ - text_width) / 2;
|
||||
|
||||
@@ -63,11 +63,6 @@ private:
|
||||
bool real_fullscreen_enabled_ = false;
|
||||
ScalingMode current_scaling_mode_ = ScalingMode::INTEGER; // Modo de escalado actual (F5)
|
||||
|
||||
// Auto-restart system
|
||||
Uint64 all_balls_stopped_start_time_ = 0; // Momento cuando todas se pararon
|
||||
bool all_balls_were_stopped_ = false; // Flag de estado anterior
|
||||
static constexpr Uint64 AUTO_RESTART_DELAY = 5000; // 5 segundos en ms
|
||||
|
||||
// Resolución dinámica para modo real fullscreen
|
||||
int current_screen_width_ = SCREEN_WIDTH;
|
||||
int current_screen_height_ = SCREEN_HEIGHT;
|
||||
@@ -100,6 +95,12 @@ private:
|
||||
float shape_scale_factor_ = 1.0f; // Factor de escala manual (Numpad +/-)
|
||||
bool depth_zoom_enabled_ = true; // Zoom por profundidad Z activado
|
||||
|
||||
// Sistema de Modo DEMO (auto-play)
|
||||
bool demo_mode_enabled_ = false; // ¿Está activo el modo demo completo?
|
||||
bool demo_lite_enabled_ = false; // ¿Está activo el modo demo lite?
|
||||
float demo_timer_ = 0.0f; // Contador de tiempo para próxima acción
|
||||
float demo_next_action_time_ = 0.0f; // Tiempo aleatorio hasta próxima acción (segundos)
|
||||
|
||||
// Batch rendering
|
||||
std::vector<SDL_Vertex> batch_vertices_;
|
||||
std::vector<int> batch_indices_;
|
||||
@@ -125,8 +126,12 @@ private:
|
||||
void toggleIntegerScaling();
|
||||
std::string gravityDirectionToString(GravityDirection direction) const;
|
||||
void initializeThemes();
|
||||
void checkAutoRestart();
|
||||
void performRandomRestart();
|
||||
|
||||
// Sistema de Modo DEMO
|
||||
void updateDemoMode();
|
||||
void performDemoAction(bool is_lite);
|
||||
void randomizeOnDemoStart(bool is_lite);
|
||||
void toggleGravityOnOff();
|
||||
|
||||
// Sistema de transiciones LERP
|
||||
float lerp(float a, float b, float t) const { return a + (b - a) * t; }
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
#include "cylinder_shape.h"
|
||||
#include "../defines.h"
|
||||
#include <cmath>
|
||||
#include <cstdlib> // Para rand()
|
||||
|
||||
void CylinderShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
num_points_ = num_points;
|
||||
radius_ = screen_height * CYLINDER_RADIUS_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
|
||||
}
|
||||
|
||||
@@ -14,8 +18,39 @@ void CylinderShape::update(float delta_time, float screen_width, float screen_he
|
||||
radius_ = screen_height * CYLINDER_RADIUS_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;
|
||||
|
||||
// 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 {
|
||||
@@ -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 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 sin_y = sinf(angle_y_);
|
||||
float x_rot = x_base * cos_y - z_base * sin_y;
|
||||
float z_rot = x_base * sin_y + z_base * cos_y;
|
||||
float x_rot_y = x_base * cos_y - z_base * sin_y;
|
||||
float z_rot_y = x_base * sin_y + z_base * cos_y;
|
||||
|
||||
// Retornar coordenadas finales rotadas
|
||||
x = x_rot;
|
||||
y = y_base;
|
||||
// Aplicar rotación en eje X (tumbling ocasional)
|
||||
float cos_x = cosf(angle_x_);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,22 @@
|
||||
#include "shape.h"
|
||||
|
||||
// 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)
|
||||
class CylinderShape : public Shape {
|
||||
private:
|
||||
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 height_ = 0.0f; // Altura del cilindro (píxeles)
|
||||
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:
|
||||
void generatePoints(int num_points, float screen_width, float screen_height) override;
|
||||
void update(float delta_time, float screen_width, float screen_height) override;
|
||||
|
||||
@@ -34,8 +34,10 @@ void WaveGridShape::update(float delta_time, float screen_width, float screen_he
|
||||
grid_size_ = screen_height * WAVE_GRID_SIZE_FACTOR;
|
||||
amplitude_ = screen_height * WAVE_GRID_AMPLITUDE;
|
||||
|
||||
// Actualizar rotación en eje Y (horizontal)
|
||||
angle_y_ += WAVE_GRID_ROTATION_SPEED_Y * delta_time;
|
||||
// Pivoteo sutil en ejes X e Y (esquinas adelante/atrás, izq/der)
|
||||
// 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)
|
||||
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)
|
||||
float kx = WAVE_GRID_FREQUENCY * PI; // Frecuencia en X
|
||||
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)
|
||||
float cos_y = cosf(angle_y_);
|
||||
float sin_y = sinf(angle_y_);
|
||||
float x_rot = x_base * cos_y - z_base * sin_y;
|
||||
float z_rot = x_base * sin_y + z_base * cos_y;
|
||||
// Añadir pivoteo sutil: esquinas se mueven adelante/atrás según posición
|
||||
// tilt_x oscila esquinas arriba/abajo, tilt_y oscila esquinas izq/der
|
||||
float tilt_amount_x = sinf(tilt_x_) * 0.15f; // Máximo 15% del grid_size
|
||||
float tilt_amount_y = sinf(tilt_y_) * 0.1f; // Máximo 10% del grid_size
|
||||
|
||||
// Retornar coordenadas finales
|
||||
x = x_rot;
|
||||
float z_tilt = (u * tilt_amount_y + v * tilt_amount_x) * grid_size_;
|
||||
|
||||
// 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;
|
||||
z = z_rot;
|
||||
z = z_final;
|
||||
}
|
||||
|
||||
float WaveGridShape::getScaleFactor(float screen_height) const {
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
#include "shape.h"
|
||||
|
||||
// Figura: Malla ondeante 3D (Wave Grid)
|
||||
// Comportamiento: Grid 2D con ondas sinusoidales en Z + rotación
|
||||
// Ecuaciones: z = A * sin(kx*x + phase) * cos(ky*y + phase)
|
||||
// Comportamiento: Grid 2D paralelo a pantalla con ondas + pivoteo sutil en esquinas
|
||||
// Ecuaciones: z = A * sin(kx*x + phase) * cos(ky*y + phase) + pivoteo
|
||||
class WaveGridShape : public Shape {
|
||||
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 grid_size_ = 0.0f; // Tamaño del grid (píxeles)
|
||||
float amplitude_ = 0.0f; // Amplitud de las ondas (píxeles)
|
||||
|
||||
Reference in New Issue
Block a user