Implementar sistema polimórfico de figuras 3D (Sphere + Cube)
- Crear interfaz abstracta Shape con métodos virtuales - Refactorizar RotoBall → SphereShape (clase polimórfica) - Implementar CubeShape con triple rotación (X/Y/Z) - Distribución inteligente en cubo: vértices/centros/grid 3D - Cambiar controles: F=toggle, Q/W/E/R/T/Y/U/I=figuras, B=temas - Actualizar SimulationMode: ROTOBALL → SHAPE - Añadir enum ShapeType (8 figuras: Sphere/Cube/Helix/Torus/etc.) - Incluir source/shapes/*.cpp en CMakeLists.txt - Física compartida escalable entre todas las figuras - Roadmap: 6 figuras pendientes (Helix/Torus/Wave/Cylinder/Icosahedron/Atom) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ if (NOT SDL3_FOUND)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Archivos fuente (excluir main_old.cpp)
|
# Archivos fuente (excluir main_old.cpp)
|
||||||
file(GLOB SOURCE_FILES source/*.cpp source/external/*.cpp)
|
file(GLOB SOURCE_FILES source/*.cpp source/external/*.cpp source/shapes/*.cpp)
|
||||||
list(REMOVE_ITEM SOURCE_FILES "${CMAKE_SOURCE_DIR}/source/main_old.cpp")
|
list(REMOVE_ITEM SOURCE_FILES "${CMAKE_SOURCE_DIR}/source/main_old.cpp")
|
||||||
|
|
||||||
# Comprobar si se encontraron archivos fuente
|
# Comprobar si se encontraron archivos fuente
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL_stdinc.h> // for Uint64
|
||||||
|
|
||||||
// Configuración de ventana y pantalla
|
// Configuración de ventana y pantalla
|
||||||
constexpr char WINDOW_CAPTION[] = "vibe3_physics";
|
constexpr char WINDOW_CAPTION[] = "vibe3_physics";
|
||||||
|
|
||||||
@@ -58,10 +60,23 @@ enum class ColorTheme {
|
|||||||
RGB = 4 // RGB puros y subdivisiones matemáticas (fondo blanco)
|
RGB = 4 // RGB puros y subdivisiones matemáticas (fondo blanco)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Enum para tipo de figura 3D
|
||||||
|
enum class ShapeType {
|
||||||
|
NONE, // Sin figura (modo física pura)
|
||||||
|
SPHERE, // Esfera Fibonacci (antiguo RotoBall)
|
||||||
|
CUBE, // Cubo rotante
|
||||||
|
HELIX, // Espiral 3D (futuro)
|
||||||
|
TORUS, // Toroide/donut (futuro)
|
||||||
|
WAVE_GRID, // Malla ondeante (futuro)
|
||||||
|
CYLINDER, // Cilindro rotante (futuro)
|
||||||
|
ICOSAHEDRON, // Icosaedro D20 (futuro)
|
||||||
|
ATOM // Átomo con órbitas (futuro)
|
||||||
|
};
|
||||||
|
|
||||||
// Enum para modo de simulación
|
// Enum para modo de simulación
|
||||||
enum class SimulationMode {
|
enum class SimulationMode {
|
||||||
PHYSICS, // Modo física normal con gravedad
|
PHYSICS, // Modo física normal con gravedad
|
||||||
ROTOBALL // Modo esfera 3D rotante (demoscene effect)
|
SHAPE // Modo figura 3D (Shape polimórfico)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Configuración de RotoBall (esfera 3D rotante)
|
// Configuración de RotoBall (esfera 3D rotante)
|
||||||
@@ -72,11 +87,17 @@ constexpr float ROTOBALL_TRANSITION_TIME = 1.5f; // Tiempo de transición (seg
|
|||||||
constexpr int ROTOBALL_MIN_BRIGHTNESS = 50; // Brillo mínimo (fondo, 0-255)
|
constexpr int ROTOBALL_MIN_BRIGHTNESS = 50; // Brillo mínimo (fondo, 0-255)
|
||||||
constexpr int ROTOBALL_MAX_BRIGHTNESS = 255; // Brillo máximo (frente, 0-255)
|
constexpr int ROTOBALL_MAX_BRIGHTNESS = 255; // Brillo máximo (frente, 0-255)
|
||||||
|
|
||||||
// Física de atracción RotoBall (sistema de resorte)
|
// Física de atracción para figuras 3D (sistema de resorte compartido)
|
||||||
constexpr float ROTOBALL_SPRING_K = 300.0f; // Constante de rigidez del resorte (N/m)
|
constexpr float ROTOBALL_SPRING_K = 300.0f; // Constante de rigidez del resorte (N/m)
|
||||||
constexpr float ROTOBALL_DAMPING_BASE = 35.0f; // Amortiguación base (amortiguamiento crítico ≈ 2*√k*m)
|
constexpr float ROTOBALL_DAMPING_BASE = 35.0f; // Amortiguación base (amortiguamiento crítico ≈ 2*√k*m)
|
||||||
constexpr float ROTOBALL_DAMPING_NEAR = 80.0f; // Amortiguación cerca del punto (absorción rápida)
|
constexpr float ROTOBALL_DAMPING_NEAR = 80.0f; // Amortiguación cerca del punto (absorción rápida)
|
||||||
constexpr float ROTOBALL_NEAR_THRESHOLD = 5.0f; // Distancia "cerca" en píxeles
|
constexpr float ROTOBALL_NEAR_THRESHOLD = 5.0f; // Distancia "cerca" en píxeles
|
||||||
constexpr float ROTOBALL_MAX_FORCE = 1000.0f; // Fuerza máxima aplicable (evita explosiones)
|
constexpr float ROTOBALL_MAX_FORCE = 1000.0f; // Fuerza máxima aplicable (evita explosiones)
|
||||||
|
|
||||||
|
// Configuración del Cubo (cubo 3D rotante)
|
||||||
|
constexpr float CUBE_SIZE_FACTOR = 0.25f; // Tamaño como proporción de altura (60/240 = 0.25)
|
||||||
|
constexpr float CUBE_ROTATION_SPEED_X = 0.5f; // Velocidad rotación eje X (rad/s)
|
||||||
|
constexpr float CUBE_ROTATION_SPEED_Y = 0.7f; // Velocidad rotación eje Y (rad/s)
|
||||||
|
constexpr float CUBE_ROTATION_SPEED_Z = 0.3f; // Velocidad rotación eje Z (rad/s)
|
||||||
|
|
||||||
constexpr float PI = 3.14159265358979323846f; // Constante PI
|
constexpr float PI = 3.14159265358979323846f; // Constante PI
|
||||||
@@ -23,6 +23,8 @@
|
|||||||
#include "ball.h" // for Ball
|
#include "ball.h" // for Ball
|
||||||
#include "external/dbgtxt.h" // for dbg_init, dbg_print
|
#include "external/dbgtxt.h" // for dbg_init, dbg_print
|
||||||
#include "external/texture.h" // for Texture
|
#include "external/texture.h" // for Texture
|
||||||
|
#include "shapes/sphere_shape.h" // for SphereShape
|
||||||
|
#include "shapes/cube_shape.h" // for CubeShape
|
||||||
|
|
||||||
// Función auxiliar para obtener la ruta del directorio del ejecutable
|
// Función auxiliar para obtener la ruta del directorio del ejecutable
|
||||||
std::string getExecutableDirectory() {
|
std::string getExecutableDirectory() {
|
||||||
@@ -148,9 +150,9 @@ void Engine::update() {
|
|||||||
|
|
||||||
// Verificar auto-reinicio cuando todas las pelotas están quietas (solo en modo física)
|
// Verificar auto-reinicio cuando todas las pelotas están quietas (solo en modo física)
|
||||||
checkAutoRestart();
|
checkAutoRestart();
|
||||||
} else if (current_mode_ == SimulationMode::ROTOBALL) {
|
} else if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
// Modo RotoBall: actualizar esfera 3D rotante
|
// Modo Figura 3D: actualizar figura polimórfica
|
||||||
updateRotoBall();
|
updateShape();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar texto (sin cambios en la lógica)
|
// Actualizar texto (sin cambios en la lógica)
|
||||||
@@ -180,9 +182,9 @@ void Engine::handleEvents() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_G:
|
case SDLK_G:
|
||||||
// Si estamos en RotoBall, salir a modo física SIN GRAVEDAD
|
// Si estamos en modo figura, salir a modo física SIN GRAVEDAD
|
||||||
if (current_mode_ == SimulationMode::ROTOBALL) {
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
toggleRotoBallMode(false); // Desactivar RotoBall sin forzar gravedad ON
|
toggleShapeMode(false); // Desactivar figura sin forzar gravedad ON
|
||||||
} else {
|
} else {
|
||||||
switchBallsGravity(); // Toggle normal en modo física
|
switchBallsGravity(); // Toggle normal en modo física
|
||||||
}
|
}
|
||||||
@@ -190,9 +192,9 @@ void Engine::handleEvents() {
|
|||||||
|
|
||||||
// Controles de dirección de gravedad con teclas de cursor
|
// Controles de dirección de gravedad con teclas de cursor
|
||||||
case SDLK_UP:
|
case SDLK_UP:
|
||||||
// Si estamos en RotoBall, salir a modo física CON gravedad
|
// Si estamos en modo figura, salir a modo física CON gravedad
|
||||||
if (current_mode_ == SimulationMode::ROTOBALL) {
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
toggleRotoBallMode(); // Desactivar RotoBall (activa gravedad automáticamente)
|
toggleShapeMode(); // Desactivar figura (activa gravedad automáticamente)
|
||||||
} else {
|
} else {
|
||||||
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||||
}
|
}
|
||||||
@@ -200,9 +202,9 @@ void Engine::handleEvents() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_DOWN:
|
case SDLK_DOWN:
|
||||||
// Si estamos en RotoBall, salir a modo física CON gravedad
|
// Si estamos en modo figura, salir a modo física CON gravedad
|
||||||
if (current_mode_ == SimulationMode::ROTOBALL) {
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
toggleRotoBallMode(); // Desactivar RotoBall (activa gravedad automáticamente)
|
toggleShapeMode(); // Desactivar figura (activa gravedad automáticamente)
|
||||||
} else {
|
} else {
|
||||||
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||||
}
|
}
|
||||||
@@ -210,9 +212,9 @@ void Engine::handleEvents() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_LEFT:
|
case SDLK_LEFT:
|
||||||
// Si estamos en RotoBall, salir a modo física CON gravedad
|
// Si estamos en modo figura, salir a modo física CON gravedad
|
||||||
if (current_mode_ == SimulationMode::ROTOBALL) {
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
toggleRotoBallMode(); // Desactivar RotoBall (activa gravedad automáticamente)
|
toggleShapeMode(); // Desactivar figura (activa gravedad automáticamente)
|
||||||
} else {
|
} else {
|
||||||
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||||
}
|
}
|
||||||
@@ -220,9 +222,9 @@ void Engine::handleEvents() {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_RIGHT:
|
case SDLK_RIGHT:
|
||||||
// Si estamos en RotoBall, salir a modo física CON gravedad
|
// Si estamos en modo figura, salir a modo física CON gravedad
|
||||||
if (current_mode_ == SimulationMode::ROTOBALL) {
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
toggleRotoBallMode(); // Desactivar RotoBall (activa gravedad automáticamente)
|
toggleShapeMode(); // Desactivar figura (activa gravedad automáticamente)
|
||||||
} else {
|
} else {
|
||||||
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
enableBallsGravityIfDisabled(); // Reactivar gravedad si estaba OFF
|
||||||
}
|
}
|
||||||
@@ -237,11 +239,46 @@ void Engine::handleEvents() {
|
|||||||
show_debug_ = !show_debug_;
|
show_debug_ = !show_debug_;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_C:
|
// Toggle Física ↔ Última Figura (antes era C)
|
||||||
toggleRotoBallMode();
|
case SDLK_F:
|
||||||
|
toggleShapeMode();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Selección directa de figuras 3D
|
||||||
|
case SDLK_Q:
|
||||||
|
activateShape(ShapeType::SPHERE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDLK_W:
|
||||||
|
activateShape(ShapeType::WAVE_GRID);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDLK_E:
|
||||||
|
activateShape(ShapeType::HELIX);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDLK_R:
|
||||||
|
activateShape(ShapeType::TORUS);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_T:
|
case SDLK_T:
|
||||||
|
activateShape(ShapeType::CUBE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDLK_Y:
|
||||||
|
activateShape(ShapeType::CYLINDER);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDLK_U:
|
||||||
|
activateShape(ShapeType::ICOSAHEDRON);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDLK_I:
|
||||||
|
activateShape(ShapeType::ATOM);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Ciclar temas de color (movido de T a B)
|
||||||
|
case SDLK_B:
|
||||||
// Ciclar al siguiente tema
|
// Ciclar al siguiente tema
|
||||||
current_theme_ = static_cast<ColorTheme>((static_cast<int>(current_theme_) + 1) % (sizeof(themes_) / sizeof(themes_[0])));
|
current_theme_ = static_cast<ColorTheme>((static_cast<int>(current_theme_) + 1) % (sizeof(themes_) / sizeof(themes_[0])));
|
||||||
initBalls(scenario_); // Regenerar bolas con nueva paleta
|
initBalls(scenario_); // Regenerar bolas con nueva paleta
|
||||||
@@ -348,8 +385,8 @@ void Engine::render() {
|
|||||||
batch_vertices_.clear();
|
batch_vertices_.clear();
|
||||||
batch_indices_.clear();
|
batch_indices_.clear();
|
||||||
|
|
||||||
if (current_mode_ == SimulationMode::ROTOBALL) {
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
// MODO ROTOBALL: Ordenar por profundidad Z (Painter's Algorithm)
|
// MODO FIGURA 3D: Ordenar por profundidad Z (Painter's Algorithm)
|
||||||
// Las pelotas con menor depth_brightness (más lejos/oscuras) se renderizan primero
|
// Las pelotas con menor depth_brightness (más lejos/oscuras) se renderizan primero
|
||||||
|
|
||||||
// Crear vector de índices para ordenamiento
|
// Crear vector de índices para ordenamiento
|
||||||
@@ -462,7 +499,14 @@ void Engine::render() {
|
|||||||
dbg_print(8, 64, theme_text.c_str(), 255, 255, 128); // Amarillo claro para tema
|
dbg_print(8, 64, theme_text.c_str(), 255, 255, 128); // Amarillo claro para tema
|
||||||
|
|
||||||
// Debug: Mostrar modo de simulación actual
|
// Debug: Mostrar modo de simulación actual
|
||||||
std::string mode_text = current_mode_ == SimulationMode::PHYSICS ? "MODE PHYSICS" : "MODE ROTOBALL";
|
std::string mode_text;
|
||||||
|
if (current_mode_ == SimulationMode::PHYSICS) {
|
||||||
|
mode_text = "MODE PHYSICS";
|
||||||
|
} else if (active_shape_) {
|
||||||
|
mode_text = std::string("MODE ") + active_shape_->getName();
|
||||||
|
} else {
|
||||||
|
mode_text = "MODE SHAPE";
|
||||||
|
}
|
||||||
dbg_print(8, 72, mode_text.c_str(), 0, 255, 128); // Verde claro para modo
|
dbg_print(8, 72, mode_text.c_str(), 0, 255, 128); // Verde claro para modo
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,11 +514,10 @@ void Engine::render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Engine::initBalls(int value) {
|
void Engine::initBalls(int value) {
|
||||||
// Si estamos en modo RotoBall, desactivarlo antes de regenerar pelotas
|
// Si estamos en modo figura 3D, desactivarlo antes de regenerar pelotas
|
||||||
if (current_mode_ == SimulationMode::ROTOBALL) {
|
if (current_mode_ == SimulationMode::SHAPE) {
|
||||||
current_mode_ = SimulationMode::PHYSICS;
|
current_mode_ = SimulationMode::PHYSICS;
|
||||||
rotoball_.transitioning = false;
|
active_shape_.reset(); // Liberar figura actual
|
||||||
rotoball_.transition_progress = 0.0f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limpiar las bolas actuales
|
// Limpiar las bolas actuales
|
||||||
@@ -922,39 +965,14 @@ void Engine::performRandomRestart() {
|
|||||||
all_balls_stopped_start_time_ = 0;
|
all_balls_stopped_start_time_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sistema RotoBall - Alternar entre modo física y esfera 3D
|
// Sistema de Figuras 3D - Alternar entre modo física y última figura (Toggle con tecla F)
|
||||||
void Engine::toggleRotoBallMode(bool force_gravity_on_exit) {
|
void Engine::toggleShapeMode(bool force_gravity_on_exit) {
|
||||||
if (current_mode_ == SimulationMode::PHYSICS) {
|
if (current_mode_ == SimulationMode::PHYSICS) {
|
||||||
// Cambiar a modo RotoBall
|
// Cambiar a modo figura (usar última figura seleccionada)
|
||||||
current_mode_ = SimulationMode::ROTOBALL;
|
activateShape(last_shape_type_);
|
||||||
rotoball_.transitioning = true;
|
|
||||||
rotoball_.transition_progress = 0.0f;
|
|
||||||
rotoball_.angle_y = 0.0f;
|
|
||||||
rotoball_.angle_x = 0.0f;
|
|
||||||
|
|
||||||
// Desactivar gravedad al entrar en modo figura
|
|
||||||
forceBallsGravityOff();
|
|
||||||
|
|
||||||
// Generar esfera 3D
|
|
||||||
generateRotoBallSphere();
|
|
||||||
|
|
||||||
// Activar atracción física en todas las pelotas
|
|
||||||
// Las pelotas mantienen su velocidad actual y son atraídas hacia la esfera
|
|
||||||
for (auto& ball : balls_) {
|
|
||||||
ball->enableRotoBallAttraction(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mostrar texto informativo
|
|
||||||
text_ = "MODO ROTOBALL";
|
|
||||||
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;
|
|
||||||
} else {
|
} else {
|
||||||
// Volver a modo física normal
|
// Volver a modo física normal
|
||||||
current_mode_ = SimulationMode::PHYSICS;
|
current_mode_ = SimulationMode::PHYSICS;
|
||||||
rotoball_.transitioning = false;
|
|
||||||
rotoball_.transition_progress = 0.0f;
|
|
||||||
|
|
||||||
// Desactivar atracción - las pelotas conservan su velocidad tangencial actual
|
// Desactivar atracción - las pelotas conservan su velocidad tangencial actual
|
||||||
for (auto& ball : balls_) {
|
for (auto& ball : balls_) {
|
||||||
@@ -975,53 +993,67 @@ void Engine::toggleRotoBallMode(bool force_gravity_on_exit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generar esfera 3D usando algoritmo Fibonacci Sphere
|
// Activar figura específica (llamado por teclas Q/W/E/R/Y/U/I o por toggleShapeMode)
|
||||||
void Engine::generateRotoBallSphere() {
|
void Engine::activateShape(ShapeType type) {
|
||||||
int num_points = static_cast<int>(balls_.size());
|
// Guardar como última figura seleccionada
|
||||||
if (num_points == 0) return;
|
last_shape_type_ = type;
|
||||||
|
current_shape_type_ = type;
|
||||||
|
|
||||||
// Calcular radio dinámico proporcional a la altura de pantalla
|
// Cambiar a modo figura
|
||||||
float radius = current_screen_height_ * ROTOBALL_RADIUS_FACTOR;
|
current_mode_ = SimulationMode::SHAPE;
|
||||||
|
|
||||||
// Constante Golden Ratio para Fibonacci sphere
|
// Desactivar gravedad al entrar en modo figura
|
||||||
const float golden_ratio = (1.0f + sqrtf(5.0f)) / 2.0f;
|
forceBallsGravityOff();
|
||||||
const float angle_increment = PI * 2.0f * golden_ratio;
|
|
||||||
|
|
||||||
for (int i = 0; i < num_points; i++) {
|
// Crear instancia polimórfica de la figura correspondiente
|
||||||
// Distribución uniforme usando Fibonacci sphere
|
switch (type) {
|
||||||
float t = static_cast<float>(i) / static_cast<float>(num_points);
|
case ShapeType::SPHERE:
|
||||||
float phi = acosf(1.0f - 2.0f * t); // Latitud
|
active_shape_ = std::make_unique<SphereShape>();
|
||||||
float theta = angle_increment * static_cast<float>(i); // Longitud
|
break;
|
||||||
|
case ShapeType::CUBE:
|
||||||
|
active_shape_ = std::make_unique<CubeShape>();
|
||||||
|
break;
|
||||||
|
// Futuras figuras se añadirán aquí
|
||||||
|
default:
|
||||||
|
active_shape_ = std::make_unique<SphereShape>(); // Fallback
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Convertir coordenadas esféricas a cartesianas
|
// Generar puntos de la figura
|
||||||
float x = cosf(theta) * sinf(phi) * radius;
|
generateShape();
|
||||||
float y = sinf(theta) * sinf(phi) * radius;
|
|
||||||
float z = cosf(phi) * radius;
|
|
||||||
|
|
||||||
// Guardar posición 3D en la pelota
|
// Activar atracción física en todas las pelotas
|
||||||
balls_[i]->setRotoBallPosition3D(x, y, z);
|
for (auto& ball : balls_) {
|
||||||
|
ball->enableRotoBallAttraction(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Calcular posición 2D inicial (centro de pantalla)
|
// Mostrar texto informativo con nombre de figura
|
||||||
float center_x = current_screen_width_ / 2.0f;
|
if (active_shape_) {
|
||||||
float center_y = current_screen_height_ / 2.0f;
|
text_ = std::string("MODO ") + active_shape_->getName();
|
||||||
balls_[i]->setRotoBallTarget2D(center_x + x, center_y + y);
|
int text_width = static_cast<int>(text_.length() * 8);
|
||||||
|
text_pos_ = (current_screen_width_ - text_width) / 2;
|
||||||
// Calcular brillo inicial según profundidad Z
|
text_init_time_ = SDL_GetTicks();
|
||||||
float z_normalized = (z + radius) / (2.0f * radius);
|
show_text_ = true;
|
||||||
balls_[i]->setDepthBrightness(z_normalized);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar esfera RotoBall con física de atracción
|
// Generar puntos de la figura activa
|
||||||
void Engine::updateRotoBall() {
|
void Engine::generateShape() {
|
||||||
if (current_mode_ != SimulationMode::ROTOBALL) return;
|
if (!active_shape_) return;
|
||||||
|
|
||||||
// Calcular radio dinámico proporcional a la altura de pantalla
|
int num_points = static_cast<int>(balls_.size());
|
||||||
float radius = current_screen_height_ * ROTOBALL_RADIUS_FACTOR;
|
active_shape_->generatePoints(num_points, static_cast<float>(current_screen_width_), static_cast<float>(current_screen_height_));
|
||||||
|
}
|
||||||
|
|
||||||
// Actualizar ángulos de rotación de la esfera
|
// Actualizar figura activa (rotación, animación, etc.)
|
||||||
rotoball_.angle_y += ROTOBALL_ROTATION_SPEED_Y * delta_time_;
|
void Engine::updateShape() {
|
||||||
rotoball_.angle_x += ROTOBALL_ROTATION_SPEED_X * delta_time_;
|
if (!active_shape_ || current_mode_ != SimulationMode::SHAPE) return;
|
||||||
|
|
||||||
|
// Actualizar animación de la figura
|
||||||
|
active_shape_->update(delta_time_, static_cast<float>(current_screen_width_), static_cast<float>(current_screen_height_));
|
||||||
|
|
||||||
|
// Obtener factor de escala para física
|
||||||
|
float scale_factor = active_shape_->getScaleFactor(static_cast<float>(current_screen_height_));
|
||||||
|
|
||||||
// Centro de la pantalla
|
// Centro de la pantalla
|
||||||
float center_x = current_screen_width_ / 2.0f;
|
float center_x = current_screen_width_ / 2.0f;
|
||||||
@@ -1029,40 +1061,22 @@ void Engine::updateRotoBall() {
|
|||||||
|
|
||||||
// Actualizar cada pelota con física de atracción
|
// Actualizar cada pelota con física de atracción
|
||||||
for (size_t i = 0; i < balls_.size(); i++) {
|
for (size_t i = 0; i < balls_.size(); i++) {
|
||||||
// Recalcular posición 3D original usando Fibonacci sphere
|
// Obtener posición 3D rotada del punto i
|
||||||
int num_points = static_cast<int>(balls_.size());
|
float x_3d, y_3d, z_3d;
|
||||||
const float golden_ratio = (1.0f + sqrtf(5.0f)) / 2.0f;
|
active_shape_->getPoint3D(static_cast<int>(i), x_3d, y_3d, z_3d);
|
||||||
const float angle_increment = PI * 2.0f * golden_ratio;
|
|
||||||
|
|
||||||
float t = static_cast<float>(i) / static_cast<float>(num_points);
|
|
||||||
float phi = acosf(1.0f - 2.0f * t);
|
|
||||||
float theta = angle_increment * static_cast<float>(i);
|
|
||||||
|
|
||||||
float x = cosf(theta) * sinf(phi) * radius;
|
|
||||||
float y = sinf(theta) * sinf(phi) * radius;
|
|
||||||
float z = cosf(phi) * radius;
|
|
||||||
|
|
||||||
// Aplicar rotación en eje Y
|
|
||||||
float cos_y = cosf(rotoball_.angle_y);
|
|
||||||
float sin_y = sinf(rotoball_.angle_y);
|
|
||||||
float x_rot = x * cos_y - z * sin_y;
|
|
||||||
float z_rot = x * sin_y + z * cos_y;
|
|
||||||
|
|
||||||
// Aplicar rotación en eje X
|
|
||||||
float cos_x = cosf(rotoball_.angle_x);
|
|
||||||
float sin_x = sinf(rotoball_.angle_x);
|
|
||||||
float y_rot = y * cos_x - z_rot * sin_x;
|
|
||||||
float z_final = y * sin_x + z_rot * cos_x;
|
|
||||||
|
|
||||||
// Proyección 2D ortográfica (punto objetivo móvil)
|
// Proyección 2D ortográfica (punto objetivo móvil)
|
||||||
float target_x = center_x + x_rot;
|
float target_x = center_x + x_3d;
|
||||||
float target_y = center_y + y_rot;
|
float target_y = center_y + y_3d;
|
||||||
|
|
||||||
// Aplicar fuerza de atracción física hacia el punto rotado
|
// Aplicar fuerza de atracción física hacia el punto rotado
|
||||||
balls_[i]->applyRotoBallForce(target_x, target_y, radius, delta_time_);
|
// Pasar el tamaño de la figura para escalar fuerzas
|
||||||
|
float shape_size = scale_factor * 80.0f; // 80px = radio base
|
||||||
|
balls_[i]->applyRotoBallForce(target_x, target_y, shape_size, delta_time_);
|
||||||
|
|
||||||
// Calcular brillo según profundidad Z para renderizado
|
// Calcular brillo según profundidad Z para renderizado
|
||||||
float z_normalized = (z_final + radius) / (2.0f * radius);
|
// Normalizar Z al rango de la figura (asumiendo simetría ±shape_size)
|
||||||
|
float z_normalized = (z_3d + shape_size) / (2.0f * shape_size);
|
||||||
z_normalized = std::max(0.0f, std::min(1.0f, z_normalized));
|
z_normalized = std::max(0.0f, std::min(1.0f, z_normalized));
|
||||||
balls_[i]->setDepthBrightness(z_normalized);
|
balls_[i]->setDepthBrightness(z_normalized);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,10 @@
|
|||||||
#include <string> // for string
|
#include <string> // for string
|
||||||
#include <vector> // for vector
|
#include <vector> // for vector
|
||||||
|
|
||||||
#include "defines.h" // for GravityDirection, ColorTheme
|
#include "defines.h" // for GravityDirection, ColorTheme, ShapeType
|
||||||
#include "ball.h" // for Ball
|
#include "ball.h" // for Ball
|
||||||
#include "external/texture.h" // for Texture
|
#include "external/texture.h" // for Texture
|
||||||
|
#include "shapes/shape.h" // for Shape (interfaz polimórfica)
|
||||||
|
|
||||||
class Engine {
|
class Engine {
|
||||||
public:
|
public:
|
||||||
@@ -80,15 +81,11 @@ private:
|
|||||||
// Temas de colores definidos
|
// Temas de colores definidos
|
||||||
ThemeColors themes_[5];
|
ThemeColors themes_[5];
|
||||||
|
|
||||||
// Sistema RotoBall (esfera 3D rotante)
|
// Sistema de Figuras 3D (polimórfico)
|
||||||
SimulationMode current_mode_ = SimulationMode::PHYSICS;
|
SimulationMode current_mode_ = SimulationMode::PHYSICS;
|
||||||
struct RotoBallData {
|
ShapeType current_shape_type_ = ShapeType::SPHERE; // Tipo de figura actual
|
||||||
float angle_y = 0.0f; // Ángulo de rotación en eje Y
|
ShapeType last_shape_type_ = ShapeType::SPHERE; // Última figura para toggle F
|
||||||
float angle_x = 0.0f; // Ángulo de rotación en eje X
|
std::unique_ptr<Shape> active_shape_; // Puntero polimórfico a figura activa
|
||||||
float transition_progress = 0.0f; // Progreso de transición (0.0-1.0)
|
|
||||||
bool transitioning = false; // ¿Está en transición?
|
|
||||||
};
|
|
||||||
RotoBallData rotoball_;
|
|
||||||
|
|
||||||
// Batch rendering
|
// Batch rendering
|
||||||
std::vector<SDL_Vertex> batch_vertices_;
|
std::vector<SDL_Vertex> batch_vertices_;
|
||||||
@@ -127,8 +124,9 @@ private:
|
|||||||
void renderGradientBackground();
|
void renderGradientBackground();
|
||||||
void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b);
|
void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b);
|
||||||
|
|
||||||
// Sistema RotoBall
|
// Sistema de Figuras 3D
|
||||||
void toggleRotoBallMode(bool force_gravity_on_exit = true);
|
void toggleShapeMode(bool force_gravity_on_exit = true); // Toggle PHYSICS ↔ última figura (tecla F)
|
||||||
void generateRotoBallSphere();
|
void activateShape(ShapeType type); // Activar figura específica (teclas Q/W/E/R/Y/U/I)
|
||||||
void updateRotoBall();
|
void updateShape(); // Actualizar figura activa
|
||||||
};
|
void generateShape(); // Generar puntos de figura activa
|
||||||
|
};
|
||||||
|
|||||||
168
source/shapes/cube_shape.cpp
Normal file
168
source/shapes/cube_shape.cpp
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
#include "cube_shape.h"
|
||||||
|
#include "../defines.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
void CubeShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||||
|
num_points_ = num_points;
|
||||||
|
size_ = screen_height * CUBE_SIZE_FACTOR;
|
||||||
|
|
||||||
|
// Limpiar vectores anteriores
|
||||||
|
base_x_.clear();
|
||||||
|
base_y_.clear();
|
||||||
|
base_z_.clear();
|
||||||
|
|
||||||
|
// Seleccionar estrategia según cantidad de pelotas
|
||||||
|
if (num_points <= 8) {
|
||||||
|
generateVertices();
|
||||||
|
} else if (num_points <= 26) {
|
||||||
|
generateVerticesAndCenters();
|
||||||
|
} else {
|
||||||
|
generateVolumetricGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si sobran posiciones, repetir en espiral (distribución uniforme)
|
||||||
|
while (static_cast<int>(base_x_.size()) < num_points) {
|
||||||
|
base_x_.push_back(base_x_[base_x_.size() % base_x_.size()]);
|
||||||
|
base_y_.push_back(base_y_[base_y_.size() % base_y_.size()]);
|
||||||
|
base_z_.push_back(base_z_[base_z_.size() % base_z_.size()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CubeShape::update(float delta_time, float screen_width, float screen_height) {
|
||||||
|
// Recalcular tamaño por si cambió resolución (F4)
|
||||||
|
size_ = screen_height * CUBE_SIZE_FACTOR;
|
||||||
|
|
||||||
|
// Actualizar ángulos de rotación en los 3 ejes (efecto Rubik)
|
||||||
|
angle_x_ += CUBE_ROTATION_SPEED_X * delta_time;
|
||||||
|
angle_y_ += CUBE_ROTATION_SPEED_Y * delta_time;
|
||||||
|
angle_z_ += CUBE_ROTATION_SPEED_Z * delta_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CubeShape::getPoint3D(int index, float& x, float& y, float& z) const {
|
||||||
|
if (index >= static_cast<int>(base_x_.size())) {
|
||||||
|
x = y = z = 0.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener posición base
|
||||||
|
float x_base = base_x_[index];
|
||||||
|
float y_base = base_y_[index];
|
||||||
|
float z_base = base_z_[index];
|
||||||
|
|
||||||
|
// Aplicar rotación en eje Z
|
||||||
|
float cos_z = cosf(angle_z_);
|
||||||
|
float sin_z = sinf(angle_z_);
|
||||||
|
float x_rot_z = x_base * cos_z - y_base * sin_z;
|
||||||
|
float y_rot_z = x_base * sin_z + y_base * cos_z;
|
||||||
|
float z_rot_z = z_base;
|
||||||
|
|
||||||
|
// Aplicar rotación en eje Y
|
||||||
|
float cos_y = cosf(angle_y_);
|
||||||
|
float sin_y = sinf(angle_y_);
|
||||||
|
float x_rot_y = x_rot_z * cos_y + z_rot_z * sin_y;
|
||||||
|
float y_rot_y = y_rot_z;
|
||||||
|
float z_rot_y = -x_rot_z * sin_y + z_rot_z * cos_y;
|
||||||
|
|
||||||
|
// Aplicar rotación en eje X
|
||||||
|
float cos_x = cosf(angle_x_);
|
||||||
|
float sin_x = sinf(angle_x_);
|
||||||
|
float x_final = x_rot_y;
|
||||||
|
float y_final = y_rot_y * cos_x - z_rot_y * sin_x;
|
||||||
|
float z_final = y_rot_y * sin_x + z_rot_y * cos_x;
|
||||||
|
|
||||||
|
// Retornar coordenadas finales rotadas
|
||||||
|
x = x_final;
|
||||||
|
y = y_final;
|
||||||
|
z = z_final;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CubeShape::getScaleFactor(float screen_height) const {
|
||||||
|
// Factor de escala para física: proporcional al tamaño del cubo
|
||||||
|
// Tamaño base = 60px (resolución 320x240, factor 0.25)
|
||||||
|
const float BASE_SIZE = 60.0f;
|
||||||
|
float current_size = screen_height * CUBE_SIZE_FACTOR;
|
||||||
|
return current_size / BASE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Métodos auxiliares privados
|
||||||
|
|
||||||
|
void CubeShape::generateVertices() {
|
||||||
|
// 8 vértices del cubo: todas las combinaciones de (±size, ±size, ±size)
|
||||||
|
for (int x_sign : {-1, 1}) {
|
||||||
|
for (int y_sign : {-1, 1}) {
|
||||||
|
for (int z_sign : {-1, 1}) {
|
||||||
|
base_x_.push_back(x_sign * size_);
|
||||||
|
base_y_.push_back(y_sign * size_);
|
||||||
|
base_z_.push_back(z_sign * size_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CubeShape::generateVerticesAndCenters() {
|
||||||
|
// 1. Añadir 8 vértices
|
||||||
|
generateVertices();
|
||||||
|
|
||||||
|
// 2. Añadir 6 centros de caras
|
||||||
|
// Caras: X=±size (Y,Z varían), Y=±size (X,Z varían), Z=±size (X,Y varían)
|
||||||
|
base_x_.push_back(size_); base_y_.push_back(0); base_z_.push_back(0); // +X
|
||||||
|
base_x_.push_back(-size_); base_y_.push_back(0); base_z_.push_back(0); // -X
|
||||||
|
base_x_.push_back(0); base_y_.push_back(size_); base_z_.push_back(0); // +Y
|
||||||
|
base_x_.push_back(0); base_y_.push_back(-size_);base_z_.push_back(0); // -Y
|
||||||
|
base_x_.push_back(0); base_y_.push_back(0); base_z_.push_back(size_); // +Z
|
||||||
|
base_x_.push_back(0); base_y_.push_back(0); base_z_.push_back(-size_); // -Z
|
||||||
|
|
||||||
|
// 3. Añadir 12 centros de aristas
|
||||||
|
// Aristas paralelas a X (4), Y (4), Z (4)
|
||||||
|
// Paralelas a X (Y y Z en vértices, X=0)
|
||||||
|
for (int y_sign : {-1, 1}) {
|
||||||
|
for (int z_sign : {-1, 1}) {
|
||||||
|
base_x_.push_back(0);
|
||||||
|
base_y_.push_back(y_sign * size_);
|
||||||
|
base_z_.push_back(z_sign * size_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Paralelas a Y (X y Z en vértices, Y=0)
|
||||||
|
for (int x_sign : {-1, 1}) {
|
||||||
|
for (int z_sign : {-1, 1}) {
|
||||||
|
base_x_.push_back(x_sign * size_);
|
||||||
|
base_y_.push_back(0);
|
||||||
|
base_z_.push_back(z_sign * size_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Paralelas a Z (X y Y en vértices, Z=0)
|
||||||
|
for (int x_sign : {-1, 1}) {
|
||||||
|
for (int y_sign : {-1, 1}) {
|
||||||
|
base_x_.push_back(x_sign * size_);
|
||||||
|
base_y_.push_back(y_sign * size_);
|
||||||
|
base_z_.push_back(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CubeShape::generateVolumetricGrid() {
|
||||||
|
// Calcular dimensión del grid cúbico: N³ ≈ num_points
|
||||||
|
int grid_dim = static_cast<int>(ceilf(cbrtf(static_cast<float>(num_points_))));
|
||||||
|
if (grid_dim < 3) grid_dim = 3; // Mínimo grid 3x3x3
|
||||||
|
|
||||||
|
float step = (2.0f * size_) / (grid_dim - 1); // Espacio entre puntos
|
||||||
|
|
||||||
|
for (int ix = 0; ix < grid_dim; ix++) {
|
||||||
|
for (int iy = 0; iy < grid_dim; iy++) {
|
||||||
|
for (int iz = 0; iz < grid_dim; iz++) {
|
||||||
|
float x = -size_ + ix * step;
|
||||||
|
float y = -size_ + iy * step;
|
||||||
|
float z = -size_ + iz * step;
|
||||||
|
|
||||||
|
base_x_.push_back(x);
|
||||||
|
base_y_.push_back(y);
|
||||||
|
base_z_.push_back(z);
|
||||||
|
|
||||||
|
// Si ya tenemos suficientes puntos, salir
|
||||||
|
if (static_cast<int>(base_x_.size()) >= num_points_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
37
source/shapes/cube_shape.h
Normal file
37
source/shapes/cube_shape.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "shape.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Figura: Cubo 3D rotante
|
||||||
|
// Distribución:
|
||||||
|
// - 1-8 pelotas: Solo vértices (8 puntos)
|
||||||
|
// - 9-26 pelotas: Vértices + centros de caras + centros de aristas (26 puntos)
|
||||||
|
// - 27+ pelotas: Grid volumétrico 3D uniforme
|
||||||
|
// Comportamiento: Rotación simultánea en ejes X, Y, Z (efecto Rubik)
|
||||||
|
class CubeShape : public Shape {
|
||||||
|
private:
|
||||||
|
float angle_x_ = 0.0f; // Ángulo de rotación en eje X (rad)
|
||||||
|
float angle_y_ = 0.0f; // Ángulo de rotación en eje Y (rad)
|
||||||
|
float angle_z_ = 0.0f; // Ángulo de rotación en eje Z (rad)
|
||||||
|
float size_ = 0.0f; // Mitad del lado del cubo (píxeles)
|
||||||
|
int num_points_ = 0; // Cantidad de puntos generados
|
||||||
|
|
||||||
|
// Posiciones base 3D (sin rotar) - se calculan en generatePoints()
|
||||||
|
std::vector<float> base_x_;
|
||||||
|
std::vector<float> base_y_;
|
||||||
|
std::vector<float> base_z_;
|
||||||
|
|
||||||
|
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;
|
||||||
|
void getPoint3D(int index, float& x, float& y, float& z) const override;
|
||||||
|
const char* getName() const override { return "CUBE"; }
|
||||||
|
float getScaleFactor(float screen_height) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Métodos auxiliares para distribución de puntos
|
||||||
|
void generateVertices(); // 8 vértices
|
||||||
|
void generateVerticesAndCenters(); // 26 puntos (vértices + caras + aristas)
|
||||||
|
void generateVolumetricGrid(); // Grid 3D para muchas pelotas
|
||||||
|
};
|
||||||
30
source/shapes/shape.h
Normal file
30
source/shapes/shape.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Interfaz abstracta para todas las figuras 3D
|
||||||
|
class Shape {
|
||||||
|
public:
|
||||||
|
virtual ~Shape() = default;
|
||||||
|
|
||||||
|
// Generar distribución inicial de puntos en la figura
|
||||||
|
// num_points: cantidad de pelotas a distribuir
|
||||||
|
// screen_width/height: dimensiones del área de juego (para escalar)
|
||||||
|
virtual void generatePoints(int num_points, float screen_width, float screen_height) = 0;
|
||||||
|
|
||||||
|
// Actualizar animación de la figura (rotación, deformación, etc.)
|
||||||
|
// delta_time: tiempo transcurrido desde último frame
|
||||||
|
// screen_width/height: dimensiones actuales (puede cambiar con F4)
|
||||||
|
virtual void update(float delta_time, float screen_width, float screen_height) = 0;
|
||||||
|
|
||||||
|
// Obtener posición 3D del punto i después de transformaciones (rotación, etc.)
|
||||||
|
// index: índice del punto (0 a num_points-1)
|
||||||
|
// x, y, z: coordenadas 3D en espacio mundo (centradas en 0,0,0)
|
||||||
|
virtual void getPoint3D(int index, float& x, float& y, float& z) const = 0;
|
||||||
|
|
||||||
|
// Obtener nombre de la figura para debug display
|
||||||
|
virtual const char* getName() const = 0;
|
||||||
|
|
||||||
|
// Obtener factor de escala para ajustar física según tamaño de figura
|
||||||
|
// screen_height: altura actual de pantalla
|
||||||
|
// Retorna: factor multiplicador para constantes de física (spring_k, damping, etc.)
|
||||||
|
virtual float getScaleFactor(float screen_height) const = 0;
|
||||||
|
};
|
||||||
58
source/shapes/sphere_shape.cpp
Normal file
58
source/shapes/sphere_shape.cpp
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#include "sphere_shape.h"
|
||||||
|
#include "../defines.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
void SphereShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||||
|
num_points_ = num_points;
|
||||||
|
radius_ = screen_height * ROTOBALL_RADIUS_FACTOR;
|
||||||
|
// Las posiciones 3D se calculan en getPoint3D() usando Fibonacci Sphere
|
||||||
|
}
|
||||||
|
|
||||||
|
void SphereShape::update(float delta_time, float screen_width, float screen_height) {
|
||||||
|
// Recalcular radio por si cambió resolución (F4)
|
||||||
|
radius_ = screen_height * ROTOBALL_RADIUS_FACTOR;
|
||||||
|
|
||||||
|
// Actualizar ángulos de rotación
|
||||||
|
angle_y_ += ROTOBALL_ROTATION_SPEED_Y * delta_time;
|
||||||
|
angle_x_ += ROTOBALL_ROTATION_SPEED_X * delta_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SphereShape::getPoint3D(int index, float& x, float& y, float& z) const {
|
||||||
|
// Algoritmo Fibonacci Sphere para distribución uniforme
|
||||||
|
const float golden_ratio = (1.0f + sqrtf(5.0f)) / 2.0f;
|
||||||
|
const float angle_increment = PI * 2.0f * golden_ratio;
|
||||||
|
|
||||||
|
float t = static_cast<float>(index) / static_cast<float>(num_points_);
|
||||||
|
float phi = acosf(1.0f - 2.0f * t); // Latitud
|
||||||
|
float theta = angle_increment * static_cast<float>(index); // Longitud
|
||||||
|
|
||||||
|
// Convertir coordenadas esféricas a cartesianas
|
||||||
|
float x_base = cosf(theta) * sinf(phi) * radius_;
|
||||||
|
float y_base = sinf(theta) * sinf(phi) * radius_;
|
||||||
|
float z_base = cosf(phi) * radius_;
|
||||||
|
|
||||||
|
// Aplicar rotación en eje Y
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Aplicar rotación en eje X
|
||||||
|
float cos_x = cosf(angle_x_);
|
||||||
|
float sin_x = sinf(angle_x_);
|
||||||
|
float y_rot = y_base * cos_x - z_rot * sin_x;
|
||||||
|
float z_final = y_base * sin_x + z_rot * cos_x;
|
||||||
|
|
||||||
|
// Retornar coordenadas finales rotadas
|
||||||
|
x = x_rot;
|
||||||
|
y = y_rot;
|
||||||
|
z = z_final;
|
||||||
|
}
|
||||||
|
|
||||||
|
float SphereShape::getScaleFactor(float screen_height) const {
|
||||||
|
// Factor de escala para física: proporcional al radio
|
||||||
|
// Radio base = 80px (resolución 320x240)
|
||||||
|
const float BASE_RADIUS = 80.0f;
|
||||||
|
float current_radius = screen_height * ROTOBALL_RADIUS_FACTOR;
|
||||||
|
return current_radius / BASE_RADIUS;
|
||||||
|
}
|
||||||
21
source/shapes/sphere_shape.h
Normal file
21
source/shapes/sphere_shape.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "shape.h"
|
||||||
|
|
||||||
|
// Figura: Esfera 3D con distribución uniforme (Fibonacci Sphere Algorithm)
|
||||||
|
// Comportamiento: Rotación dual en ejes X e Y
|
||||||
|
// Uso anterior: RotoBall
|
||||||
|
class SphereShape : public Shape {
|
||||||
|
private:
|
||||||
|
float angle_x_ = 0.0f; // Ángulo de rotación en eje X (rad)
|
||||||
|
float angle_y_ = 0.0f; // Ángulo de rotación en eje Y (rad)
|
||||||
|
float radius_ = 0.0f; // Radio de la esfera (píxeles)
|
||||||
|
int num_points_ = 0; // Cantidad de puntos generados
|
||||||
|
|
||||||
|
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;
|
||||||
|
void getPoint3D(int index, float& x, float& y, float& z) const override;
|
||||||
|
const char* getName() const override { return "SPHERE"; }
|
||||||
|
float getScaleFactor(float screen_height) const override;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user