Merge branch 'tweak/pentagon-design': halo neon proporcional i pentàgon doble
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
# enemy_pentagon.shp - ORNI enemic (pentàgon regular, radi=20)
|
||||
# enemy_pentagon.shp - ORNI enemic (pentàgon doble concentric, radi exterior=20)
|
||||
|
||||
name: enemy_pentagon
|
||||
scale: 1.0
|
||||
center: 0, 0
|
||||
|
||||
# Pentàgon exterior (vèrtex apuntant amunt, radi 20)
|
||||
polyline: 0,-20 19.02,-6.18 11.76,16.18 -11.76,16.18 -19.02,-6.18 0,-20
|
||||
|
||||
# Pentàgon interior (radi 10, rotat 36° → vèrtex apuntant a les arestes exteriors)
|
||||
polyline: 5.88,-8.09 9.51,3.09 0,10 -9.51,3.09 -5.88,-8.09 5.88,-8.09
|
||||
|
||||
@@ -5,6 +5,32 @@
|
||||
|
||||
#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 {
|
||||
|
||||
// Color per defecte. La caller pot fer override (p.ex. heretar del pare),
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "core/graphics/shape.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
@@ -85,9 +86,23 @@ auto Shape::parseFile(const std::string& contingut) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
return std::sqrt(max_dist_sq);
|
||||
}
|
||||
|
||||
// Helper: trim whitespace
|
||||
auto Shape::trim(const std::string& str) -> std::string {
|
||||
const char* whitespace = " \t\n\r";
|
||||
|
||||
@@ -42,6 +42,9 @@ class Shape {
|
||||
}
|
||||
[[nodiscard]] auto getCenter() const -> const Vec2& { return center_; }
|
||||
[[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(); }
|
||||
|
||||
// Info de depuració
|
||||
@@ -53,6 +56,7 @@ class Shape {
|
||||
Vec2 center_; // Centro/origin de la shape
|
||||
float escala_defecte_{1.0F}; // Escala per defecte (normalment 1.0). Inicializada para
|
||||
// que el ctor por defecto no deje el campo indeterminado.
|
||||
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
|
||||
@@ -62,6 +66,9 @@ class Shape {
|
||||
[[nodiscard]] static auto extractValue(const std::string& line) -> std::string;
|
||||
void parseCenter(const std::string& value);
|
||||
[[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
|
||||
|
||||
@@ -221,7 +221,8 @@ namespace Graphics {
|
||||
// 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)
|
||||
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
|
||||
current_x += CHAR_WIDTH_SCALED + SPACING_SCALED;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user