Implementar zoom por profundidad Z con perspectiva 3D + toggle

NUEVAS CARACTERÍSTICAS:
- Zoom por profundidad Z: Escala sprites según distancia (0.5x-1.5x)
- Toggle con Numpad / (KP_DIVIDE) para activar/desactivar perspectiva
- Fix transición figura→física: Reset automático de depth_scale a 1.0
- Texto informativo: "DEPTH ZOOM ON/OFF"

IMPLEMENTACIÓN TÉCNICA:
- Ball class: Nueva variable depth_scale_ (0.5-1.5)
- Ball class: Getters/setters getDepthScale() / setDepthScale()
- Engine::addSpriteToBatch(): Parámetro scale con valor defecto 1.0
- Engine::addSpriteToBatch(): Cálculo de vértices escalados centrados
- Engine::updateShape(): Cálculo depth_scale = 0.5 + z_normalized * 1.0
- Engine::render(): Pasa depth_scale al batch en modo SHAPE
- Engine::toggleShapeMode(): Reset depth_scale en salida de figura
- Engine: Variable depth_zoom_enabled_ (true por defecto)
- Batch rendering: Mantiene performance (sin llamadas individuales)

EFECTO VISUAL:
- Pelotas lejanas (Z-): Pequeñas (50%) y oscuras
- Pelotas medias (Z=0): Normales (100%) y brillo medio
- Pelotas cercanas (Z+): Grandes (150%) y brillantes
- Perspectiva 3D realista combinada con Z-sorting

CONTROLES:
- Numpad /: Toggle zoom por profundidad (solo en modo SHAPE)
- Por defecto: ACTIVADO para máximo realismo 3D

README ACTUALIZADO:
- Añadida tecla KP_/ a tabla de controles
- Actualizada sección "Características Técnicas"
- Añadida línea "Zoom por profundidad" en características
- Actualizada sección "Uso" con control de perspectiva

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-03 21:00:19 +02:00
parent a484ce69e8
commit 91f8bfdd30
5 changed files with 47 additions and 11 deletions

View File

@@ -71,6 +71,7 @@ El nombre refleja su proposito: **ViBe** (vibe-coding experimental) + **Physics*
| `KP_+` | **Aumentar escala de figura (+10%)** | | `KP_+` | **Aumentar escala de figura (+10%)** |
| `KP_-` | **Reducir escala de figura (-10%)** | | `KP_-` | **Reducir escala de figura (-10%)** |
| `KP_*` | **Reset escala de figura a 100%** | | `KP_*` | **Reset escala de figura a 100%** |
| `KP_/` | **Toggle zoom por profundidad Z (perspectiva ON/OFF)** |
## 📊 Informacion en Pantalla ## 📊 Informacion en Pantalla
@@ -147,10 +148,11 @@ Cuando se activa el debug display con la tecla `H`:
- **Física de atracción**: Sistema spring-damper (Hooke's Law) para transición suave - **Física de atracción**: Sistema spring-damper (Hooke's Law) para transición suave
- **Profundidad Z simulada**: Color modulado según distancia (oscuro=fondo, brillante=frente) - **Profundidad Z simulada**: Color modulado según distancia (oscuro=fondo, brillante=frente)
- **Zoom por profundidad**: Perspectiva 3D con escala variable (50%-150% según Z)
- **Z-sorting**: Painter's Algorithm para oclusión correcta - **Z-sorting**: Painter's Algorithm para oclusión correcta
- **Escala dinámica**: Control manual con Numpad +/- (30% - 300%) - **Escala dinámica**: Control manual con Numpad +/- (30% - 300%)
- **Protección de clipping**: Escala limitada automáticamente según resolución - **Protección de clipping**: Escala limitada automáticamente según resolución
- **Sin sprites adicionales**: Usa `SDL_SetTextureColorMod` para simular profundidad - **Sin sprites adicionales**: Usa `SDL_SetTextureColorMod` y vértices escalados para efectos 3D
### Parámetros Físicos (defines.h) ### Parámetros Físicos (defines.h)
@@ -191,6 +193,7 @@ SHAPE_SCALE_STEP = 0.1f; // Incremento 10%
- **Activación**: Presiona Q/W/E/R/T/Y/U/I para figura específica, o F para toggle - **Activación**: Presiona Q/W/E/R/T/Y/U/I para figura específica, o F para toggle
- **Escala**: Usa Numpad +/- para ajustar tamaño, * para reset - **Escala**: Usa Numpad +/- para ajustar tamaño, * para reset
- **Perspectiva**: Numpad / para activar/desactivar zoom por profundidad (ON por defecto)
- **Salir**: G (sin gravedad), cursores (con gravedad), F (toggle), o 1-8 (cambiar escenario) - **Salir**: G (sin gravedad), cursores (con gravedad), F (toggle), o 1-8 (cambiar escenario)
- **Compatible**: Funciona con 1-100,000 pelotas - **Compatible**: Funciona con 1-100,000 pelotas
- **Temas**: Mantiene paleta de colores activa - **Temas**: Mantiene paleta de colores activa

View File

@@ -50,6 +50,7 @@ Ball::Ball(float x, float vx, float vy, Color color, std::shared_ptr<Texture> te
target_x_ = pos_.x; target_x_ = pos_.x;
target_y_ = pos_.y; target_y_ = pos_.y;
depth_brightness_ = 1.0f; depth_brightness_ = 1.0f;
depth_scale_ = 1.0f;
rotoball_attraction_active_ = false; rotoball_attraction_active_ = false;
} }
@@ -297,6 +298,10 @@ void Ball::setDepthBrightness(float brightness) {
depth_brightness_ = brightness; depth_brightness_ = brightness;
} }
void Ball::setDepthScale(float scale) {
depth_scale_ = scale;
}
// Activar/desactivar atracción física hacia esfera RotoBall // Activar/desactivar atracción física hacia esfera RotoBall
void Ball::enableRotoBallAttraction(bool enable) { void Ball::enableRotoBallAttraction(bool enable) {
rotoball_attraction_active_ = enable; rotoball_attraction_active_ = enable;

View File

@@ -27,6 +27,7 @@ class Ball {
float pos_3d_x_, pos_3d_y_, pos_3d_z_; // Posición 3D en la esfera float pos_3d_x_, pos_3d_y_, pos_3d_z_; // Posición 3D en la esfera
float target_x_, target_y_; // Posición destino 2D (proyección) float target_x_, target_y_; // Posición destino 2D (proyección)
float depth_brightness_; // Brillo según profundidad Z (0.0-1.0) float depth_brightness_; // Brillo según profundidad Z (0.0-1.0)
float depth_scale_; // Escala según profundidad Z (0.5-1.5)
bool rotoball_attraction_active_; // ¿Está siendo atraída hacia la esfera? bool rotoball_attraction_active_; // ¿Está siendo atraída hacia la esfera?
public: public:
@@ -82,6 +83,8 @@ class Ball {
void setRotoBallScreenPosition(float x, float y); // Establecer posición directa en pantalla void setRotoBallScreenPosition(float x, float y); // Establecer posición directa en pantalla
void setDepthBrightness(float brightness); void setDepthBrightness(float brightness);
float getDepthBrightness() const { return depth_brightness_; } float getDepthBrightness() const { return depth_brightness_; }
void setDepthScale(float scale);
float getDepthScale() const { return depth_scale_; }
// Sistema de atracción física hacia esfera RotoBall // Sistema de atracción física hacia esfera RotoBall
void enableRotoBallAttraction(bool enable); void enableRotoBallAttraction(bool enable);

View File

@@ -346,6 +346,17 @@ void Engine::handleEvents() {
} }
break; break;
case SDLK_KP_DIVIDE:
if (current_mode_ == SimulationMode::SHAPE) {
depth_zoom_enabled_ = !depth_zoom_enabled_;
text_ = depth_zoom_enabled_ ? "DEPTH ZOOM ON" : "DEPTH ZOOM OFF";
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;
}
break;
case SDLK_1: case SDLK_1:
scenario_ = 0; scenario_ = 0;
initBalls(scenario_); initBalls(scenario_);
@@ -443,6 +454,7 @@ void Engine::render() {
SDL_FRect pos = balls_[idx]->getPosition(); SDL_FRect pos = balls_[idx]->getPosition();
Color color = balls_[idx]->getColor(); Color color = balls_[idx]->getColor();
float brightness = balls_[idx]->getDepthBrightness(); float brightness = balls_[idx]->getDepthBrightness();
float depth_scale = balls_[idx]->getDepthScale();
// Mapear brightness de 0-1 a rango MIN-MAX // Mapear brightness de 0-1 a rango MIN-MAX
float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness * float brightness_factor = (ROTOBALL_MIN_BRIGHTNESS + brightness *
@@ -453,14 +465,14 @@ void Engine::render() {
int g_mod = static_cast<int>(color.g * brightness_factor); int g_mod = static_cast<int>(color.g * brightness_factor);
int b_mod = static_cast<int>(color.b * brightness_factor); int b_mod = static_cast<int>(color.b * brightness_factor);
addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, r_mod, g_mod, b_mod); addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, r_mod, g_mod, b_mod, depth_scale);
} }
} else { } else {
// MODO PHYSICS: Renderizar en orden normal del vector // MODO PHYSICS: Renderizar en orden normal del vector (sin escala de profundidad)
for (auto &ball : balls_) { for (auto &ball : balls_) {
SDL_FRect pos = ball->getPosition(); SDL_FRect pos = ball->getPosition();
Color color = ball->getColor(); Color color = ball->getColor();
addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, color.r, color.g, color.b); addSpriteToBatch(pos.x, pos.y, pos.w, pos.h, color.r, color.g, color.b, 1.0f);
} }
} }
@@ -774,7 +786,7 @@ void Engine::renderGradientBackground() {
SDL_RenderGeometry(renderer_, nullptr, bg_vertices, 4, bg_indices, 6); SDL_RenderGeometry(renderer_, nullptr, bg_vertices, 4, bg_indices, 6);
} }
void Engine::addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b) { void Engine::addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b, float scale) {
int vertex_index = static_cast<int>(batch_vertices_.size()); int vertex_index = static_cast<int>(batch_vertices_.size());
// Crear 4 vértices para el quad (2 triángulos) // Crear 4 vértices para el quad (2 triángulos)
@@ -785,23 +797,29 @@ void Engine::addSpriteToBatch(float x, float y, float w, float h, int r, int g,
float gf = g / 255.0f; float gf = g / 255.0f;
float bf = b / 255.0f; float bf = b / 255.0f;
// Aplicar escala al tamaño (centrado en el punto x, y)
float scaled_w = w * scale;
float scaled_h = h * scale;
float offset_x = (w - scaled_w) / 2.0f; // Offset para centrar
float offset_y = (h - scaled_h) / 2.0f;
// Vértice superior izquierdo // Vértice superior izquierdo
vertices[0].position = {x, y}; vertices[0].position = {x + offset_x, y + offset_y};
vertices[0].tex_coord = {0.0f, 0.0f}; vertices[0].tex_coord = {0.0f, 0.0f};
vertices[0].color = {rf, gf, bf, 1.0f}; vertices[0].color = {rf, gf, bf, 1.0f};
// Vértice superior derecho // Vértice superior derecho
vertices[1].position = {x + w, y}; vertices[1].position = {x + offset_x + scaled_w, y + offset_y};
vertices[1].tex_coord = {1.0f, 0.0f}; vertices[1].tex_coord = {1.0f, 0.0f};
vertices[1].color = {rf, gf, bf, 1.0f}; vertices[1].color = {rf, gf, bf, 1.0f};
// Vértice inferior derecho // Vértice inferior derecho
vertices[2].position = {x + w, y + h}; vertices[2].position = {x + offset_x + scaled_w, y + offset_y + scaled_h};
vertices[2].tex_coord = {1.0f, 1.0f}; vertices[2].tex_coord = {1.0f, 1.0f};
vertices[2].color = {rf, gf, bf, 1.0f}; vertices[2].color = {rf, gf, bf, 1.0f};
// Vértice inferior izquierdo // Vértice inferior izquierdo
vertices[3].position = {x, y + h}; vertices[3].position = {x + offset_x, y + offset_y + scaled_h};
vertices[3].tex_coord = {0.0f, 1.0f}; vertices[3].tex_coord = {0.0f, 1.0f};
vertices[3].color = {rf, gf, bf, 1.0f}; vertices[3].color = {rf, gf, bf, 1.0f};
@@ -1010,9 +1028,10 @@ void Engine::toggleShapeMode(bool force_gravity_on_exit) {
// Volver a modo física normal // Volver a modo física normal
current_mode_ = SimulationMode::PHYSICS; current_mode_ = SimulationMode::PHYSICS;
// Desactivar atracción - las pelotas conservan su velocidad tangencial actual // Desactivar atracción y resetear escala de profundidad
for (auto& ball : balls_) { for (auto& ball : balls_) {
ball->enableRotoBallAttraction(false); ball->enableRotoBallAttraction(false);
ball->setDepthScale(1.0f); // Reset escala a 100% (evita "pop" visual)
} }
// Activar gravedad al salir (solo si se especifica) // Activar gravedad al salir (solo si se especifica)
@@ -1120,6 +1139,11 @@ void Engine::updateShape() {
float z_normalized = (z_3d + shape_size) / (2.0f * 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);
// Calcular escala según profundidad Z (perspectiva) - solo si está activado
// 0.0 (fondo) → 0.5x, 0.5 (medio) → 1.0x, 1.0 (frente) → 1.5x
float depth_scale = depth_zoom_enabled_ ? (0.5f + z_normalized * 1.0f) : 1.0f;
balls_[i]->setDepthScale(depth_scale);
} }
} }

View File

@@ -87,6 +87,7 @@ private:
ShapeType last_shape_type_ = ShapeType::SPHERE; // Última figura para toggle F ShapeType last_shape_type_ = ShapeType::SPHERE; // Última figura para toggle F
std::unique_ptr<Shape> active_shape_; // Puntero polimórfico a figura activa std::unique_ptr<Shape> active_shape_; // Puntero polimórfico a figura activa
float shape_scale_factor_ = 1.0f; // Factor de escala manual (Numpad +/-) float shape_scale_factor_ = 1.0f; // Factor de escala manual (Numpad +/-)
bool depth_zoom_enabled_ = true; // Zoom por profundidad Z activado
// Batch rendering // Batch rendering
std::vector<SDL_Vertex> batch_vertices_; std::vector<SDL_Vertex> batch_vertices_;
@@ -123,7 +124,7 @@ private:
// Rendering // Rendering
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, float scale = 1.0f);
// Sistema de Figuras 3D // Sistema de Figuras 3D
void toggleShapeMode(bool force_gravity_on_exit = true); // Toggle PHYSICS ↔ última figura (tecla F) void toggleShapeMode(bool force_gravity_on_exit = true); // Toggle PHYSICS ↔ última figura (tecla F)