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
+26
View File
@@ -5,6 +5,32 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
namespace Defaults::FX::Glow {
// Neon glow per outline gruixut, aplicat automàticament per renderShape.
// Els gruixos d'halo són RÀTIOS del bounding_radius de la shape (escalat
// per scale), de manera que un pentàgon (radi 20) té halo gros i una bala
// (radi 3) té halo subtil. El core (últim pass) usa el gruix de línia
// global (1.5px) — no escala amb la shape.
//
// Cap superior: si la shape és molt gran (logos del títol, intro), el
// bounding_radius es satura a aquest valor — així cap shape té més
// glow que el pentàgon (referència de gameplay).
constexpr float MAX_REFERENCE_RADIUS = 20.0F;
struct Pass {
float thickness_ratio; // % del bounding_radius*scale. <0 → usa core (gruix global)
float alpha;
};
constexpr Pass PASSES[] = {
{.thickness_ratio = 0.55F, .alpha = 0.07F},
{.thickness_ratio = 0.35F, .alpha = 0.14F},
{.thickness_ratio = 0.20F, .alpha = 0.28F},
{.thickness_ratio = -1.0F, .alpha = 1.0F}, // core: línia "real"
};
} // namespace Defaults::FX::Glow
namespace Defaults::FX::Firework { namespace Defaults::FX::Firework {
// Color per defecte. La caller pot fer override (p.ex. heretar del pare), // Color per defecte. La caller pot fer override (p.ex. heretar del pare),
+138 -123
View File
@@ -4,156 +4,171 @@
#include "core/graphics/shape.hpp" #include "core/graphics/shape.hpp"
#include <algorithm> #include <algorithm>
#include <cmath>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
namespace Graphics { namespace Graphics {
Shape::Shape(const std::string& filepath) Shape::Shape(const std::string& filepath)
: center_({.x = 0.0F, .y = 0.0F}), : center_({.x = 0.0F, .y = 0.0F}),
nom_("unnamed") { nom_("unnamed") {
load(filepath); load(filepath);
}
auto Shape::load(const std::string& filepath) -> bool {
// Llegir file
std::ifstream file(filepath);
if (!file.is_open()) {
std::cerr << "[Shape] Error: no es pot obrir " << filepath << '\n';
return false;
} }
// Llegir todo el contingut auto Shape::load(const std::string& filepath) -> bool {
std::stringstream buffer; // Llegir file
buffer << file.rdbuf(); std::ifstream file(filepath);
std::string contingut = buffer.str(); if (!file.is_open()) {
file.close(); std::cerr << "[Shape] Error: no es pot obrir " << filepath << '\n';
return false;
// Parsejar
return parseFile(contingut);
}
auto Shape::parseFile(const std::string& contingut) -> bool {
std::istringstream iss(contingut);
std::string line;
while (std::getline(iss, line)) {
// Trim whitespace
line = trim(line);
// Skip comments and blanks
if (line.empty() || line[0] == '#') {
continue;
} }
// Parse command // Llegir todo el contingut
if (startsWith(line, "name:")) { std::stringstream buffer;
nom_ = trim(extractValue(line)); buffer << file.rdbuf();
} else if (startsWith(line, "scale:")) { std::string contingut = buffer.str();
try { file.close();
escala_defecte_ = std::stof(extractValue(line));
} catch (...) { // Parsejar
std::cerr << "[Shape] Warning: scale invàlida, usant 1.0" << '\n'; return parseFile(contingut);
escala_defecte_ = 1.0F; }
auto Shape::parseFile(const std::string& contingut) -> bool {
std::istringstream iss(contingut);
std::string line;
while (std::getline(iss, line)) {
// Trim whitespace
line = trim(line);
// Skip comments and blanks
if (line.empty() || line[0] == '#') {
continue;
} }
} else if (startsWith(line, "center:")) {
parseCenter(extractValue(line)); // Parse command
} else if (startsWith(line, "polyline:")) { if (startsWith(line, "name:")) {
auto points = parsePoints(extractValue(line)); nom_ = trim(extractValue(line));
if (points.size() >= 2) { } else if (startsWith(line, "scale:")) {
primitives_.push_back({PrimitiveType::POLYLINE, points}); try {
} else { escala_defecte_ = std::stof(extractValue(line));
std::cerr << "[Shape] Warning: polyline con menys de 2 points ignorada" } catch (...) {
<< '\n'; std::cerr << "[Shape] Warning: scale invàlida, usant 1.0" << '\n';
escala_defecte_ = 1.0F;
}
} else if (startsWith(line, "center:")) {
parseCenter(extractValue(line));
} else if (startsWith(line, "polyline:")) {
auto points = parsePoints(extractValue(line));
if (points.size() >= 2) {
primitives_.push_back({PrimitiveType::POLYLINE, points});
} else {
std::cerr << "[Shape] Warning: polyline con menys de 2 points ignorada"
<< '\n';
}
} else if (startsWith(line, "line:")) {
auto points = parsePoints(extractValue(line));
if (points.size() == 2) {
primitives_.push_back({PrimitiveType::LINE, points});
} else {
std::cerr << "[Shape] Warning: line ha de tenir exactament 2 points"
<< '\n';
}
} }
} else if (startsWith(line, "line:")) { // Comandes desconegudes ignorades silenciosament
auto points = parsePoints(extractValue(line)); }
if (points.size() == 2) {
primitives_.push_back({PrimitiveType::LINE, points}); if (primitives_.empty()) {
} else { std::cerr << "[Shape] Error: sin primitiva carregada" << '\n';
std::cerr << "[Shape] Warning: line ha de tenir exactament 2 points" return false;
<< '\n'; }
bounding_radius_ = computeBoundingRadius(primitives_, center_);
return true;
}
auto Shape::computeBoundingRadius(const std::vector<ShapePrimitive>& primitives,
const Vec2& center) -> float {
float max_dist_sq = 0.0F;
for (const auto& prim : primitives) {
for (const auto& p : prim.points) {
const float DX = p.x - center.x;
const float DY = p.y - center.y;
max_dist_sq = std::max(max_dist_sq, (DX * DX) + (DY * DY));
} }
} }
// Comandes desconegudes ignorades silenciosament return std::sqrt(max_dist_sq);
} }
if (primitives_.empty()) { // Helper: trim whitespace
std::cerr << "[Shape] Error: sin primitiva carregada" << '\n'; auto Shape::trim(const std::string& str) -> std::string {
return false; const char* whitespace = " \t\n\r";
} size_t start = str.find_first_not_of(whitespace);
if (start == std::string::npos) {
return true; return "";
}
// Helper: trim whitespace
auto Shape::trim(const std::string& str) -> std::string {
const char* whitespace = " \t\n\r";
size_t start = str.find_first_not_of(whitespace);
if (start == std::string::npos) {
return "";
}
size_t end = str.find_last_not_of(whitespace);
return str.substr(start, end - start + 1);
}
// Helper: startsWith
auto Shape::startsWith(const std::string& str,
const std::string& prefix) -> bool {
if (str.length() < prefix.length()) {
return false;
}
return str.starts_with(prefix);
}
// Helper: extract value after ':'
auto Shape::extractValue(const std::string& line) -> std::string {
size_t colon = line.find(':');
if (colon == std::string::npos) {
return "";
}
return line.substr(colon + 1);
}
// Helper: parse center "x, y"
void Shape::parseCenter(const std::string& value) {
std::string val = trim(value);
size_t comma = val.find(',');
if (comma != std::string::npos) {
try {
center_.x = std::stof(trim(val.substr(0, comma)));
center_.y = std::stof(trim(val.substr(comma + 1)));
} catch (...) {
std::cerr << "[Shape] Warning: centro invàlid, usant (0,0)" << '\n';
center_ = {.x = 0.0F, .y = 0.0F};
} }
size_t end = str.find_last_not_of(whitespace);
return str.substr(start, end - start + 1);
} }
}
// Helper: parse points "x1,y1 x2,y2 x3,y3" // Helper: startsWith
auto Shape::parsePoints(const std::string& str) -> std::vector<Vec2> { auto Shape::startsWith(const std::string& str,
std::vector<Vec2> points; const std::string& prefix) -> bool {
std::istringstream iss(trim(str)); if (str.length() < prefix.length()) {
std::string pair; return false;
}
return str.starts_with(prefix);
}
while (iss >> pair) { // Whitespace-separated // Helper: extract value after ':'
size_t comma = pair.find(','); auto Shape::extractValue(const std::string& line) -> std::string {
size_t colon = line.find(':');
if (colon == std::string::npos) {
return "";
}
return line.substr(colon + 1);
}
// Helper: parse center "x, y"
void Shape::parseCenter(const std::string& value) {
std::string val = trim(value);
size_t comma = val.find(',');
if (comma != std::string::npos) { if (comma != std::string::npos) {
try { try {
float x = std::stof(pair.substr(0, comma)); center_.x = std::stof(trim(val.substr(0, comma)));
float y = std::stof(pair.substr(comma + 1)); center_.y = std::stof(trim(val.substr(comma + 1)));
points.push_back({x, y});
} catch (...) { } catch (...) {
std::cerr << "[Shape] Warning: point invàlid ignorat: " << pair std::cerr << "[Shape] Warning: centro invàlid, usant (0,0)" << '\n';
<< '\n'; center_ = {.x = 0.0F, .y = 0.0F};
} }
} }
} }
return points; // Helper: parse points "x1,y1 x2,y2 x3,y3"
} auto Shape::parsePoints(const std::string& str) -> std::vector<Vec2> {
std::vector<Vec2> points;
std::istringstream iss(trim(str));
std::string pair;
while (iss >> pair) { // Whitespace-separated
size_t comma = pair.find(',');
if (comma != std::string::npos) {
try {
float x = std::stof(pair.substr(0, comma));
float y = std::stof(pair.substr(comma + 1));
points.push_back({x, y});
} catch (...) {
std::cerr << "[Shape] Warning: point invàlid ignorat: " << pair
<< '\n';
}
}
}
return points;
}
} // namespace Graphics } // namespace Graphics
+24 -17
View File
@@ -11,21 +11,21 @@
namespace Graphics { namespace Graphics {
// Tipo de primitiva dins de una shape // Tipo de primitiva dins de una shape
enum class PrimitiveType : std::uint8_t { enum class PrimitiveType : std::uint8_t {
POLYLINE, // Secuencia de points connectats POLYLINE, // Secuencia de points connectats
LINE // Línia individual (2 points) LINE // Línia individual (2 points)
}; };
// Primitiva individual (polyline o line) // Primitiva individual (polyline o line)
struct ShapePrimitive { struct ShapePrimitive {
PrimitiveType type; PrimitiveType type;
std::vector<Vec2> points; // 2+ points per polyline, exactament 2 per line std::vector<Vec2> points; // 2+ points per polyline, exactament 2 per line
}; };
// Clase Shape - representa una shape vectorial carregada desde .shp // Clase Shape - representa una shape vectorial carregada desde .shp
class Shape { class Shape {
public: public:
// Constructors // Constructors
Shape() = default; Shape() = default;
explicit Shape(const std::string& filepath); explicit Shape(const std::string& filepath);
@@ -42,18 +42,22 @@ class Shape {
} }
[[nodiscard]] auto getCenter() const -> const Vec2& { return center_; } [[nodiscard]] auto getCenter() const -> const Vec2& { return center_; }
[[nodiscard]] auto getDefaultScale() const -> float { return escala_defecte_; } [[nodiscard]] auto getDefaultScale() const -> float { return escala_defecte_; }
// Distància màx. del center_ al vèrtex més llunyà; ús: dimensionar
// efectes proporcionals a la mida de la shape (halos, glow).
[[nodiscard]] auto getBoundingRadius() const -> float { return bounding_radius_; }
[[nodiscard]] auto isValid() const -> bool { return !primitives_.empty(); } [[nodiscard]] auto isValid() const -> bool { return !primitives_.empty(); }
// Info de depuració // Info de depuració
[[nodiscard]] auto getName() const -> const std::string& { return nom_; } [[nodiscard]] auto getName() const -> const std::string& { return nom_; }
[[nodiscard]] auto getNumPrimitives() const -> size_t { return primitives_.size(); } [[nodiscard]] auto getNumPrimitives() const -> size_t { return primitives_.size(); }
private: private:
std::vector<ShapePrimitive> primitives_; std::vector<ShapePrimitive> primitives_;
Vec2 center_; // Centro/origin de la shape Vec2 center_; // Centro/origin de la shape
float escala_defecte_{1.0F}; // Escala per defecte (normalment 1.0). Inicializada para float escala_defecte_{1.0F}; // Escala per defecte (normalment 1.0). Inicializada para
// que el ctor por defecto no deje el campo indeterminado. // que el ctor por defecto no deje el campo indeterminado.
std::string nom_; // Nom de la shape (per depuració) float bounding_radius_{0.0F}; // Distància màx. del center_ al vèrtex més llunyà.
std::string nom_; // Nom de la shape (per depuració)
// Helpers privats per parsejar. Son estáticos: no necesitan estado // Helpers privats per parsejar. Son estáticos: no necesitan estado
// de instancia, trabajan sobre el string pasado por parámetro. // de instancia, trabajan sobre el string pasado por parámetro.
@@ -62,6 +66,9 @@ class Shape {
[[nodiscard]] static auto extractValue(const std::string& line) -> std::string; [[nodiscard]] static auto extractValue(const std::string& line) -> std::string;
void parseCenter(const std::string& value); void parseCenter(const std::string& value);
[[nodiscard]] static auto parsePoints(const std::string& str) -> std::vector<Vec2>; [[nodiscard]] static auto parsePoints(const std::string& str) -> std::vector<Vec2>;
}; [[nodiscard]] static auto computeBoundingRadius(
const std::vector<ShapePrimitive>& primitives,
const Vec2& center) -> float;
};
} // namespace Graphics } // namespace Graphics
+2 -1
View File
@@ -221,7 +221,8 @@ namespace Graphics {
// Ajustar X e Y para que position represente esquina superior izquierda // Ajustar X e Y para que position represente esquina superior izquierda
// (render_shape espera el centro, así que sumamos la mitad de ancho y altura) // (render_shape espera el centro, así que sumamos la mitad de ancho y altura)
Vec2 char_pos = {.x = current_x + (CHAR_WIDTH_SCALED / 2.0F), .y = position.y + (CHAR_HEIGHT_SCALED / 2.0F)}; Vec2 char_pos = {.x = current_x + (CHAR_WIDTH_SCALED / 2.0F), .y = position.y + (CHAR_HEIGHT_SCALED / 2.0F)};
Rendering::renderShape(renderer_, it->second, char_pos, 0.0F, scale, 1.0F, brightness, color); // Text opt-out del glow: HUD/marker s'ha de mantenir net.
Rendering::renderShape(renderer_, it->second, char_pos, 0.0F, scale, 1.0F, brightness, color, 0.0F, 1.0F, /*glow=*/false);
// Avanzar posición // Avanzar posición
current_x += CHAR_WIDTH_SCALED + SPACING_SCALED; current_x += CHAR_WIDTH_SCALED + SPACING_SCALED;
+3 -2
View File
@@ -22,7 +22,8 @@ namespace Rendering {
int y2, int y2,
float brightness, float brightness,
float thickness, float thickness,
SDL_Color color) { SDL_Color color,
float alpha) {
if (renderer == nullptr) { if (renderer == nullptr) {
return; return;
} }
@@ -42,7 +43,7 @@ namespace Rendering {
const float W = (thickness > 0.0F) ? thickness : g_current_line_thickness; 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; } 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). // 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). // 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. // 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 // color: si alpha==0, se usa el color global del oscilador; si alpha>0 se
// usa este color directo (paleta semántica por entidad). // 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, void linea(Renderer* renderer,
int x1, int x1,
int y1, int y1,
@@ -27,7 +31,8 @@ namespace Rendering {
int y2, int y2,
float brightness = 1.0F, float brightness = 1.0F,
float thickness = 0.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). // Color global de las líneas (lo actualiza ColorOscillator vía SDLManager).
void setLineColor(SDL_Color color); void setLineColor(SDL_Color color);
+81 -27
View File
@@ -3,31 +3,77 @@
#include "core/rendering/shape_renderer.hpp" #include "core/rendering/shape_renderer.hpp"
#include <algorithm>
#include <cmath> #include <cmath>
#include "core/defaults/effects.hpp"
#include "core/graphics/shape.hpp"
#include "core/rendering/line_renderer.hpp" #include "core/rendering/line_renderer.hpp"
namespace Rendering { namespace Rendering {
// Helper: transformar un point con rotación, scale i traslación // 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 { 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 const float CENTERED_X = point.x - shape_centre.x;
float centered_x = point.x - shape_centre.x; const float CENTERED_Y = point.y - shape_centre.y;
float centered_y = point.y - shape_centre.y;
// 2. Aplicar scale al point const float SCALED_X = CENTERED_X * scale;
float scaled_x = centered_x * scale; const float SCALED_Y = CENTERED_Y * scale;
float scaled_y = centered_y * scale;
// 3. Aplicar rotación 2D (Z-axis) const float COS_A = std::cos(angle);
float cos_a = std::cos(angle); const float SIN_A = std::sin(angle);
float sin_a = std::sin(angle);
float rotated_x = (scaled_x * cos_a) - (scaled_y * sin_a); const 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_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, void renderShape(Rendering::Renderer* renderer,
@@ -37,7 +83,10 @@ namespace Rendering {
float scale, float scale,
float progress, float progress,
float brightness, float brightness,
SDL_Color color) { SDL_Color color,
float thickness,
float alpha,
bool glow) {
if (!shape || !shape->isValid()) { if (!shape || !shape->isValid()) {
return; return;
} }
@@ -45,21 +94,26 @@ namespace Rendering {
return; 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()) { // Glow: multi-pass amb halos translúcids proporcionals al tamany de
if (primitive.type == Graphics::PrimitiveType::POLYLINE) { // la shape. Cada pass amb thickness_ratio<0 usa el thickness/alpha
// POLYLINE: conectar puntos consecutivos. // que ha passat el caller (és el "core" / línia real). Saturem la
for (size_t i = 0; i < primitive.points.size() - 1; i++) { // mida de referència a MAX_REFERENCE_RADIUS perquè shapes molt
const Vec2 P1 = transformPoint(primitive.points[i], shape_centre, position, angle, scale); // grans (logos) no tinguin halo desproporcionat.
const Vec2 P2 = transformPoint(primitive.points[i + 1], shape_centre, position, angle, scale); const float RAW_REF = shape->getBoundingRadius() * 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); const float REFERENCE_SIZE = std::min(RAW_REF, Defaults::FX::Glow::MAX_REFERENCE_RADIUS);
} for (const auto& pass : Defaults::FX::Glow::PASSES) {
} else if (primitive.points.size() >= 2) { // LINE float pass_thickness = thickness;
const Vec2 P1 = transformPoint(primitive.points[0], shape_centre, position, angle, scale); float pass_alpha = alpha;
const Vec2 P2 = transformPoint(primitive.points[1], shape_centre, position, angle, scale); if (pass.thickness_ratio > 0.0F) {
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); 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) // - scale: factor de scale (1.0 = mida original)
// - progress: progrés de l'animación (0.0-1.0, default 1.0 = tot visible) // - 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) // - 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, void renderShape(Rendering::Renderer* renderer,
const std::shared_ptr<Graphics::Shape>& shape, const std::shared_ptr<Graphics::Shape>& shape,
const Vec2& position, const Vec2& position,
@@ -28,6 +33,9 @@ namespace Rendering {
float scale = 1.0F, float scale = 1.0F,
float progress = 1.0F, float progress = 1.0F,
float brightness = 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 } // namespace Rendering