feat(render): halo neon proporcional al bounding_radius de la shape (opt-out a text)

This commit is contained in:
2026-05-22 21:35:01 +02:00
parent 869b4374ba
commit f0b3a1fbc4
8 changed files with 289 additions and 172 deletions
+3 -2
View File
@@ -22,7 +22,8 @@ namespace Rendering {
int y2,
float brightness,
float thickness,
SDL_Color color) {
SDL_Color color,
float alpha) {
if (renderer == nullptr) {
return;
}
@@ -42,7 +43,7 @@ namespace Rendering {
const float W = (thickness > 0.0F) ? thickness : g_current_line_thickness;
renderer->pushLine(FX1, FY1, FX2, FY2, W, R, G, B, 1.0F);
renderer->pushLine(FX1, FY1, FX2, FY2, W, R, G, B, alpha);
}
void setLineColor(SDL_Color color) { g_current_line_color = color; }
+6 -1
View File
@@ -17,9 +17,13 @@ namespace Rendering {
// Dibuja una línea entre dos puntos en coordenadas lógicas (1280×720).
// brightness: factor de brillo (0.0..1.0, default 1.0 = brillo máximo).
// Pre-multiplica el RGB del color (color dim sobre fons negre).
// thickness: grosor en píxeles lógicos. Si <= 0 usa g_current_line_thickness.
// color: si alpha==0, se usa el color global del oscilador; si alpha>0 se
// usa este color directo (paleta semántica por entidad).
// alpha: alpha que arriba al GPU (default 1.0 = opac, behavior original).
// Valors <1.0 fan que la línia es barregi de veritat sobre el dest
// en comptes de sobrepintar-lo (útil per halos translúcids).
void linea(Renderer* renderer,
int x1,
int y1,
@@ -27,7 +31,8 @@ namespace Rendering {
int y2,
float brightness = 1.0F,
float thickness = 0.0F,
SDL_Color color = {0, 0, 0, 0});
SDL_Color color = {0, 0, 0, 0},
float alpha = 1.0F);
// Color global de las líneas (lo actualiza ColorOscillator vía SDLManager).
void setLineColor(SDL_Color color);
+81 -27
View File
@@ -3,31 +3,77 @@
#include "core/rendering/shape_renderer.hpp"
#include <algorithm>
#include <cmath>
#include "core/defaults/effects.hpp"
#include "core/graphics/shape.hpp"
#include "core/rendering/line_renderer.hpp"
namespace Rendering {
// Helper: transformar un point con rotación, scale i traslación
static auto transformPoint(const Vec2& point, const Vec2& shape_centre, const Vec2& position, float angle, float scale) -> Vec2 {
// 1. Centrar el point respecte al centro de la shape
float centered_x = point.x - shape_centre.x;
float centered_y = point.y - shape_centre.y;
const float CENTERED_X = point.x - shape_centre.x;
const float CENTERED_Y = point.y - shape_centre.y;
// 2. Aplicar scale al point
float scaled_x = centered_x * scale;
float scaled_y = centered_y * scale;
const float SCALED_X = CENTERED_X * scale;
const float SCALED_Y = CENTERED_Y * scale;
// 3. Aplicar rotación 2D (Z-axis)
float cos_a = std::cos(angle);
float sin_a = std::sin(angle);
const float COS_A = std::cos(angle);
const float SIN_A = std::sin(angle);
float rotated_x = (scaled_x * cos_a) - (scaled_y * sin_a);
float rotated_y = (scaled_x * sin_a) + (scaled_y * cos_a);
const float ROTATED_X = (SCALED_X * COS_A) - (SCALED_Y * SIN_A);
const float ROTATED_Y = (SCALED_X * SIN_A) + (SCALED_Y * COS_A);
// 4. Aplicar traslación a posición mundial
return {.x = rotated_x + position.x, .y = rotated_y + position.y};
return {.x = ROTATED_X + position.x, .y = ROTATED_Y + position.y};
}
// Una passada de renderitzat: itera primitives de la shape i emet línies
// amb el thickness/alpha indicats. Es crida N vegades en glow mode (una
// per pass de halo + core), o 1 vegada quan glow=false.
static void renderSinglePass(Rendering::Renderer* renderer,
const std::shared_ptr<Graphics::Shape>& shape,
const Vec2& position,
float angle,
float scale,
float brightness,
SDL_Color color,
float thickness,
float alpha) {
const Vec2& shape_centre = shape->getCenter();
// Petita extensió a línies gruixudes per tapar forats entre segments.
// A vèrtex aguts (~108°) un valor alt produeix "espigues" — 15%.
const float EFFECTIVE_T = (thickness > 0.0F) ? thickness : getLineThickness();
const float EXTEND = (EFFECTIVE_T > 2.0F) ? (EFFECTIVE_T * 0.15F) : 0.0F;
for (const auto& primitive : shape->getPrimitives()) {
if (primitive.type == Graphics::PrimitiveType::POLYLINE) {
for (size_t i = 0; i < primitive.points.size() - 1; i++) {
Vec2 p1 = transformPoint(primitive.points[i], shape_centre, position, angle, scale);
Vec2 p2 = transformPoint(primitive.points[i + 1], shape_centre, position, angle, scale);
if (EXTEND > 0.0F) {
const float DX = p2.x - p1.x;
const float DY = p2.y - p1.y;
const float LEN = std::sqrt((DX * DX) + (DY * DY));
if (LEN > 1e-6F) {
const float UX = (DX / LEN) * EXTEND;
const float UY = (DY / LEN) * EXTEND;
p1.x -= UX;
p1.y -= UY;
p2.x += UX;
p2.y += UY;
}
}
linea(renderer, static_cast<int>(p1.x), static_cast<int>(p1.y), static_cast<int>(p2.x), static_cast<int>(p2.y), brightness, thickness, color, alpha);
}
} else if (primitive.points.size() >= 2) { // LINE
const Vec2 P1 = transformPoint(primitive.points[0], shape_centre, position, angle, scale);
const Vec2 P2 = transformPoint(primitive.points[1], shape_centre, position, angle, scale);
linea(renderer, static_cast<int>(P1.x), static_cast<int>(P1.y), static_cast<int>(P2.x), static_cast<int>(P2.y), brightness, thickness, color, alpha);
}
}
}
void renderShape(Rendering::Renderer* renderer,
@@ -37,7 +83,10 @@ namespace Rendering {
float scale,
float progress,
float brightness,
SDL_Color color) {
SDL_Color color,
float thickness,
float alpha,
bool glow) {
if (!shape || !shape->isValid()) {
return;
}
@@ -45,21 +94,26 @@ namespace Rendering {
return;
}
const Vec2& shape_centre = shape->getCenter();
if (!glow) {
renderSinglePass(renderer, shape, position, angle, scale, brightness, color, thickness, alpha);
return;
}
for (const auto& primitive : shape->getPrimitives()) {
if (primitive.type == Graphics::PrimitiveType::POLYLINE) {
// POLYLINE: conectar puntos consecutivos.
for (size_t i = 0; i < primitive.points.size() - 1; i++) {
const Vec2 P1 = transformPoint(primitive.points[i], shape_centre, position, angle, scale);
const Vec2 P2 = transformPoint(primitive.points[i + 1], shape_centre, position, angle, scale);
linea(renderer, static_cast<int>(P1.x), static_cast<int>(P1.y), static_cast<int>(P2.x), static_cast<int>(P2.y), brightness, 0.0F, color);
}
} else if (primitive.points.size() >= 2) { // LINE
const Vec2 P1 = transformPoint(primitive.points[0], shape_centre, position, angle, scale);
const Vec2 P2 = transformPoint(primitive.points[1], shape_centre, position, angle, scale);
linea(renderer, static_cast<int>(P1.x), static_cast<int>(P1.y), static_cast<int>(P2.x), static_cast<int>(P2.y), brightness, 0.0F, color);
// Glow: multi-pass amb halos translúcids proporcionals al tamany de
// la shape. Cada pass amb thickness_ratio<0 usa el thickness/alpha
// que ha passat el caller (és el "core" / línia real). Saturem la
// mida de referència a MAX_REFERENCE_RADIUS perquè shapes molt
// grans (logos) no tinguin halo desproporcionat.
const float RAW_REF = shape->getBoundingRadius() * scale;
const float REFERENCE_SIZE = std::min(RAW_REF, Defaults::FX::Glow::MAX_REFERENCE_RADIUS);
for (const auto& pass : Defaults::FX::Glow::PASSES) {
float pass_thickness = thickness;
float pass_alpha = alpha;
if (pass.thickness_ratio > 0.0F) {
pass_thickness = REFERENCE_SIZE * pass.thickness_ratio;
pass_alpha = pass.alpha * alpha; // respecta el master alpha del caller
}
renderSinglePass(renderer, shape, position, angle, scale, brightness, color, pass_thickness, pass_alpha);
}
}
+9 -1
View File
@@ -21,6 +21,11 @@ namespace Rendering {
// - scale: factor de scale (1.0 = mida original)
// - progress: progrés de l'animación (0.0-1.0, default 1.0 = tot visible)
// - brightness: factor de brightness (0.0-1.0, default 1.0 = màxima brightness)
// - color: si alpha==0, usa oscil·lador global
// - thickness: gruix de línia. <=0 → usa global (g_current_line_thickness)
// - alpha: alpha que arriba al GPU (default 1.0 = opac). <1.0 = halo real
// - glow: si true, redibuixa la shape amb halos translúcids proporcionals
// al bounding_radius*scale (efecte neon). Si false, single-pass.
void renderShape(Rendering::Renderer* renderer,
const std::shared_ptr<Graphics::Shape>& shape,
const Vec2& position,
@@ -28,6 +33,9 @@ namespace Rendering {
float scale = 1.0F,
float progress = 1.0F,
float brightness = 1.0F,
SDL_Color color = {0, 0, 0, 0}); // alpha==0 → usa global oscilador
SDL_Color color = {0, 0, 0, 0},
float thickness = 0.0F,
float alpha = 1.0F,
bool glow = true);
} // namespace Rendering