// rigid_body.hpp - Cuerpo rígido 2D para el sistema de física // © 2025 Orni Attack // // Estructura POD-like que encapsula el estado físico de una entidad: // posición, velocidad lineal/angular, masa, restitución y damping. // El integrador es semi-implícito de Euler (estable para juegos arcade). // // Convenciones: // - position: coordenadas lógicas (px), donde la entidad está en el mundo // - angle: radianes; 0 apunta hacia arriba (eje Y negativo en SDL) // - velocity: px/s en cartesianas (NO polares — adiós a cos/sin por entidad) // - mass = 0 (inverse_mass = 0) representa un cuerpo estático (masa infinita) // - restitution 0 = inelástico, 1 = elástico perfecto // - linear_damping en s⁻¹ (fricción exponencial: v *= exp(-damping * dt)) #pragma once #include "core/types.hpp" namespace Physics { struct RigidBody { // --- Estado cinemático --- Vec2 position{}; // Posición del centro (px) Vec2 velocity{}; // Velocidad lineal (px/s) float angle{0.0F}; // Orientación (rad) float angular_velocity{0.0F}; // Velocidad angular (rad/s) // --- Propiedades físicas --- float mass{1.0F}; // Masa (kg, escala libre) float inverse_mass{1.0F}; // 1/mass cacheado (0 = estático) float restitution{0.5F}; // Elasticidad (0..1) float linear_damping{0.0F}; // Fricción lineal (s⁻¹) float angular_damping{0.0F}; // Fricción angular (s⁻¹) float radius{0.0F}; // Radio de colisión (círculo) // --- Fuerzas acumuladas (reseteadas tras cada integrate) --- Vec2 force_accumulator{}; float torque_accumulator{0.0F}; // Configura la masa y precalcula inverse_mass. // mass <= 0 marca el cuerpo como estático (inmovible por impulsos). void setMass(float new_mass) { mass = new_mass; inverse_mass = (new_mass > 0.0F) ? 1.0F / new_mass : 0.0F; } // Marca el cuerpo como estático (paredes, obstáculos fijos). void setStatic() { mass = 0.0F; inverse_mass = 0.0F; velocity = Vec2{}; angular_velocity = 0.0F; } [[nodiscard]] auto isStatic() const -> bool { return inverse_mass == 0.0F; } // Aplica una fuerza instantánea (acumulada para el siguiente integrate). void applyForce(const Vec2& force) { force_accumulator += force; } // Aplica un impulso (cambio inmediato de velocidad: Δv = J / m). void applyImpulse(const Vec2& impulse) { if (!isStatic()) { velocity += impulse * inverse_mass; } } // Resetea los acumuladores tras la integración. void clearAccumulators() { force_accumulator = Vec2{}; torque_accumulator = 0.0F; } }; } // namespace Physics