Pulsar B en escenarios 6-8 (o custom >1K) no activa boids y muestra notificación. Cambiar de escenario estando en BOIDS queda bloqueado si el destino supera BOIDS_MAX_BALLS (= SCENE_BALLS_5). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
282 lines
12 KiB
C++
282 lines
12 KiB
C++
#pragma once
|
||
|
||
#include <SDL3/SDL_events.h> // for SDL_Event
|
||
#include <SDL3/SDL_render.h> // for SDL_Renderer (ui_renderer_ software renderer)
|
||
#include <SDL3/SDL_stdinc.h> // for Uint64
|
||
#include <SDL3/SDL_surface.h> // for SDL_Surface (ui_surface_)
|
||
#include <SDL3/SDL_video.h> // for SDL_Window
|
||
|
||
#include <array> // for array
|
||
#include <memory> // for unique_ptr, shared_ptr
|
||
#include <string> // for string
|
||
#include <vector> // for vector
|
||
|
||
#include "ui/app_logo.hpp" // for AppLogo
|
||
#include "ball.hpp" // for Ball
|
||
#include "boids_mgr/boid_manager.hpp" // for BoidManager
|
||
#include "defines.hpp" // for GravityDirection, ColorTheme, ShapeType
|
||
#include "external/texture.hpp" // for Texture
|
||
#include "gpu/gpu_ball_buffer.hpp" // for GpuBallBuffer, BallGPUData
|
||
#include "gpu/gpu_context.hpp" // for GpuContext
|
||
#include "gpu/gpu_pipeline.hpp" // for GpuPipeline
|
||
#include "gpu/gpu_sprite_batch.hpp" // for GpuSpriteBatch
|
||
#include "gpu/gpu_texture.hpp" // for GpuTexture
|
||
#include "input/input_handler.hpp" // for InputHandler
|
||
#include "scene/scene_manager.hpp" // for SceneManager
|
||
#include "shapes_mgr/shape_manager.hpp" // for ShapeManager
|
||
#include "state/state_manager.hpp" // for StateManager
|
||
#include "theme_manager.hpp" // for ThemeManager
|
||
#include "ui/ui_manager.hpp" // for UIManager
|
||
|
||
class Engine {
|
||
public:
|
||
// Interfaz pública principal
|
||
bool initialize(int width = 0, int height = 0, int zoom = 0, bool fullscreen = false, AppMode initial_mode = AppMode::SANDBOX);
|
||
void run();
|
||
void shutdown();
|
||
|
||
// === Métodos públicos para InputHandler ===
|
||
|
||
// Gravedad y física
|
||
void pushBallsAwayFromGravity();
|
||
void handleGravityToggle();
|
||
void handleGravityDirectionChange(GravityDirection direction, const char* notification_text);
|
||
|
||
// Display y depuración
|
||
void toggleVSync();
|
||
void toggleDebug();
|
||
void toggleHelp();
|
||
|
||
// Figuras 3D
|
||
void toggleShapeMode();
|
||
void activateShape(ShapeType type, const char* notification_text);
|
||
void handleShapeScaleChange(bool increase);
|
||
void resetShapeScale();
|
||
void toggleDepthZoom();
|
||
|
||
// Boids (comportamiento de enjambre)
|
||
void toggleBoidsMode(bool force_gravity_on = true);
|
||
|
||
// Temas de colores
|
||
void cycleTheme(bool forward);
|
||
void switchThemeByNumpad(int numpad_key);
|
||
void toggleThemePage();
|
||
void pauseDynamicTheme();
|
||
|
||
// Sprites/Texturas
|
||
void switchTexture();
|
||
|
||
// Escenarios (número de pelotas)
|
||
void changeScenario(int scenario_id, const char* notification_text);
|
||
|
||
// Zoom y fullscreen
|
||
void handleZoomIn();
|
||
void handleZoomOut();
|
||
void toggleFullscreen();
|
||
void toggleRealFullscreen();
|
||
void toggleIntegerScaling();
|
||
|
||
// Campo de juego (tamaño lógico + físico)
|
||
void fieldSizeUp();
|
||
void fieldSizeDown();
|
||
void setFieldScale(float new_scale);
|
||
|
||
// PostFX presets
|
||
void handlePostFXCycle();
|
||
void handlePostFXToggle();
|
||
void setInitialPostFX(int mode);
|
||
void setPostFXParamOverrides(float vignette, float chroma);
|
||
|
||
// Cicle PostFX nadiu (OFF → Vinyeta → Scanlines → Cromàtica → Complet)
|
||
void cycleShader();
|
||
|
||
// Modo kiosko
|
||
void setKioskMode(bool enabled) { kiosk_mode_ = enabled; }
|
||
bool isKioskMode() const { return kiosk_mode_; }
|
||
|
||
// Escenario custom (tecla 9, --custom-balls)
|
||
void setCustomScenario(int balls);
|
||
bool isCustomScenarioEnabled() const { return custom_scenario_enabled_; }
|
||
bool isCustomAutoAvailable() const { return custom_auto_available_; }
|
||
int getCustomScenarioBalls() const { return custom_scenario_balls_; }
|
||
|
||
// Control manual del benchmark (--skip-benchmark, --max-balls)
|
||
void setSkipBenchmark();
|
||
void setMaxBallsOverride(int n);
|
||
|
||
// Notificaciones (público para InputHandler)
|
||
void showNotificationForAction(const std::string& text);
|
||
|
||
// Modos de aplicación (DEMO/LOGO)
|
||
void toggleDemoMode();
|
||
void toggleDemoLiteMode();
|
||
void toggleLogoMode();
|
||
|
||
// === Métodos públicos para StateManager (automatización DEMO/LOGO sin notificación) ===
|
||
void enterShapeMode(ShapeType type); // Activar figura (sin notificación)
|
||
void exitShapeMode(bool force_gravity = true); // Volver a física (sin notificación)
|
||
void switchTextureSilent(); // Cambiar textura (sin notificación)
|
||
void setTextureByIndex(size_t index); // Restaurar textura específica
|
||
|
||
// === Getters públicos para UIManager (Debug HUD) ===
|
||
bool getVSyncEnabled() const { return vsync_enabled_; }
|
||
bool getFullscreenEnabled() const { return fullscreen_enabled_; }
|
||
bool getRealFullscreenEnabled() const { return real_fullscreen_enabled_; }
|
||
ScalingMode getCurrentScalingMode() const { return current_scaling_mode_; }
|
||
int getCurrentScreenWidth() const { return current_screen_width_; }
|
||
int getCurrentScreenHeight() const { return current_screen_height_; }
|
||
std::string getCurrentTextureName() const {
|
||
if (texture_names_.empty()) return "";
|
||
return texture_names_[current_texture_index_];
|
||
}
|
||
int getBaseScreenWidth() const { return base_screen_width_; }
|
||
int getBaseScreenHeight() const { return base_screen_height_; }
|
||
int getMaxAutoScenario() const { return max_auto_scenario_; }
|
||
size_t getCurrentTextureIndex() const { return current_texture_index_; }
|
||
bool isPostFXEnabled() const { return postfx_enabled_; }
|
||
int getPostFXMode() const { return postfx_effect_mode_; }
|
||
float getPostFXVignette() const { return postfx_uniforms_.vignette_strength; }
|
||
float getPostFXChroma() const { return postfx_uniforms_.chroma_strength; }
|
||
float getPostFXScanline() const { return postfx_uniforms_.scanline_strength; }
|
||
|
||
private:
|
||
// === Componentes del sistema (Composición) ===
|
||
std::unique_ptr<InputHandler> input_handler_; // Manejo de entradas SDL
|
||
std::unique_ptr<SceneManager> scene_manager_; // Gestión de bolas y física
|
||
std::unique_ptr<ShapeManager> shape_manager_; // Gestión de figuras 3D
|
||
std::unique_ptr<BoidManager> boid_manager_; // Gestión de comportamiento boids
|
||
std::unique_ptr<StateManager> state_manager_; // Gestión de estados (DEMO/LOGO)
|
||
std::unique_ptr<UIManager> ui_manager_; // Gestión de UI (HUD, FPS, notificaciones)
|
||
std::unique_ptr<AppLogo> app_logo_; // Gestión de logo periódico en pantalla
|
||
|
||
// === SDL window ===
|
||
SDL_Window* window_ = nullptr;
|
||
|
||
// === SDL_GPU rendering pipeline ===
|
||
std::unique_ptr<GpuContext> gpu_ctx_; // Device + swapchain
|
||
std::unique_ptr<GpuPipeline> gpu_pipeline_; // Sprite + ball + postfx pipelines
|
||
std::unique_ptr<GpuSpriteBatch> sprite_batch_; // Per-frame vertex/index batch (bg + shape + UI)
|
||
std::unique_ptr<GpuBallBuffer> gpu_ball_buffer_; // Instanced ball instance data (PHYSICS/BOIDS)
|
||
std::vector<BallGPUData> ball_gpu_data_; // CPU-side staging vector (reused each frame)
|
||
std::unique_ptr<GpuTexture> offscreen_tex_; // Offscreen render target (Pass 1)
|
||
std::unique_ptr<GpuTexture> white_tex_; // 1×1 white (background gradient)
|
||
std::unique_ptr<GpuTexture> ui_tex_; // UI text overlay texture
|
||
|
||
// GPU sprite textures (one per ball skin, parallel to textures_/texture_names_)
|
||
std::unique_ptr<GpuTexture> gpu_texture_; // Active GPU sprite texture
|
||
std::vector<std::unique_ptr<GpuTexture>> gpu_textures_; // All GPU sprite textures
|
||
|
||
// === SDL_Renderer (software, for UI text via SDL3_ttf) ===
|
||
// Renders to ui_surface_, then uploaded as gpu texture overlay.
|
||
SDL_Renderer* ui_renderer_ = nullptr;
|
||
SDL_Surface* ui_surface_ = nullptr;
|
||
|
||
// Legacy Texture objects — kept for ball physics sizing and AppLogo
|
||
std::shared_ptr<Texture> texture_ = nullptr; // Textura activa actual
|
||
std::vector<std::shared_ptr<Texture>> textures_; // Todas las texturas disponibles
|
||
std::vector<std::string> texture_names_; // Nombres de texturas (sin extensión)
|
||
size_t current_texture_index_ = 0; // Índice de textura activa
|
||
int current_ball_size_ = 10; // Tamaño actual de pelotas (dinámico)
|
||
|
||
// Estado del simulador
|
||
bool should_exit_ = false;
|
||
|
||
// Sistema de timing
|
||
Uint64 last_frame_time_ = 0;
|
||
float delta_time_ = 0.0f;
|
||
|
||
// PostFX uniforms (passed to GPU each frame)
|
||
PostFXUniforms postfx_uniforms_ = {0.0f, 0.0f, 0.0f, 0.0f};
|
||
int postfx_effect_mode_ = 3;
|
||
bool postfx_enabled_ = false;
|
||
float postfx_override_vignette_ = -1.f; // -1 = sin override
|
||
float postfx_override_chroma_ = -1.f;
|
||
|
||
// Sistema de escala de ventana
|
||
float current_window_scale_ = 1.0f;
|
||
|
||
// Escala del campo de juego lógico (F7/F8)
|
||
float current_field_scale_ = 1.0f;
|
||
|
||
// V-Sync y fullscreen
|
||
bool vsync_enabled_ = true;
|
||
bool fullscreen_enabled_ = false;
|
||
bool real_fullscreen_enabled_ = false;
|
||
bool kiosk_mode_ = false;
|
||
ScalingMode current_scaling_mode_ = ScalingMode::INTEGER;
|
||
|
||
// Resolución base (configurada por CLI o default)
|
||
int base_screen_width_ = DEFAULT_SCREEN_WIDTH;
|
||
int base_screen_height_ = DEFAULT_SCREEN_HEIGHT;
|
||
|
||
// Resolución dinámica actual (cambia en fullscreen real)
|
||
int current_screen_width_ = DEFAULT_SCREEN_WIDTH;
|
||
int current_screen_height_ = DEFAULT_SCREEN_HEIGHT;
|
||
|
||
// Resolución física real de ventana/pantalla (para texto absoluto)
|
||
int physical_window_width_ = DEFAULT_SCREEN_WIDTH;
|
||
int physical_window_height_ = DEFAULT_SCREEN_HEIGHT;
|
||
|
||
// Sistema de temas (delegado a ThemeManager)
|
||
std::unique_ptr<ThemeManager> theme_manager_;
|
||
int theme_page_ = 0;
|
||
|
||
// Modo de simulación actual (PHYSICS/SHAPE/BOIDS)
|
||
SimulationMode current_mode_ = SimulationMode::PHYSICS;
|
||
|
||
// Sistema de Modo DEMO (auto-play) y LOGO
|
||
int max_auto_scenario_ = 5;
|
||
|
||
// Escenario custom (--custom-balls)
|
||
int custom_scenario_balls_ = 0;
|
||
bool custom_scenario_enabled_ = false;
|
||
bool custom_auto_available_ = false;
|
||
bool skip_benchmark_ = false;
|
||
|
||
// Bucket sort per z-ordering (SHAPE mode)
|
||
static constexpr int DEPTH_SORT_BUCKETS = 256;
|
||
std::array<std::vector<size_t>, DEPTH_SORT_BUCKETS> depth_buckets_;
|
||
|
||
// Métodos principales del loop
|
||
void calculateDeltaTime();
|
||
void update();
|
||
void render();
|
||
|
||
// Benchmark de rendimiento (determina max_auto_scenario_ al inicio)
|
||
void runPerformanceBenchmark();
|
||
|
||
// Métodos auxiliares privados
|
||
|
||
// Sistema de cambio de sprites dinámico
|
||
void switchTextureInternal(bool show_notification);
|
||
|
||
// Sistema de escala de ventana
|
||
float calculateMaxWindowScale() const;
|
||
void setWindowScale(float new_scale);
|
||
void zoomIn();
|
||
void zoomOut();
|
||
void updatePhysicalWindowSize();
|
||
|
||
// Rendering (GPU path replaces addSpriteToBatch)
|
||
void addSpriteToBatch(float x, float y, float w, float h, int r, int g, int b, float scale = 1.0f);
|
||
|
||
// Sistema de Figuras 3D
|
||
void toggleShapeModeInternal(bool force_gravity_on_exit = true);
|
||
void activateShapeInternal(ShapeType type);
|
||
void updateShape();
|
||
void generateShape();
|
||
|
||
// PostFX helper
|
||
void applyPostFXPreset(int mode);
|
||
|
||
// Boids: comprueba si un escenario tiene ≤ BOIDS_MAX_BALLS bolas
|
||
bool isScenarioAllowedForBoids(int scenario_id) const;
|
||
|
||
// GPU helpers
|
||
bool loadGpuSpriteTexture(size_t index); // Upload one sprite texture to GPU
|
||
void recreateOffscreenTexture(); // Recreate when resolution changes
|
||
void renderUIToSurface(); // Render text/UI to ui_surface_
|
||
void uploadUISurface(SDL_GPUCommandBuffer* cmd_buf); // Upload ui_surface_ → ui_tex_
|
||
|
||
};
|