mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 07:40:43 +00:00
Add steady glow effect beneath hydrogen bomb
Render a soft radial glow underneath the hydrogen bomb sprite in UnitPass. H-bomb instances draw an enlarged quad (hBombGlowScale) so there's room for the halo; a cell-space UV remap keeps the sprite at its normal size while the margin becomes glow area. The glow is a steady (non-pulsing) radial falloff in a warm amber, alpha-blended underneath the sprite and suppressed in alt/affiliation view. Detection uses a HYDROGEN_BOMB_COL shader define derived from UNIT_ORDER, so it tracks the atlas layout rather than hard-coding the column. All other units are unaffected (scale 1, same fillrate); this stays a single program / two instanced draw calls. Glow color, scale, strength, and falloff are exposed in render-settings.json for live tuning via the debug GUI.
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 553 B After Width: | Height: | Size: 578 B |
@@ -169,6 +169,13 @@ export interface RenderSettings {
|
||||
angryR: number;
|
||||
angryG: number;
|
||||
angryB: number;
|
||||
// Steady soft glow rendered underneath the hydrogen bomb
|
||||
hBombGlowScale: number; // quad enlargement factor (1 = no glow room)
|
||||
hBombGlowR: number;
|
||||
hBombGlowG: number;
|
||||
hBombGlowB: number;
|
||||
hBombGlowStrength: number; // peak opacity of the glow
|
||||
hBombGlowInner: number; // radial falloff start (0..1, quad-space)
|
||||
};
|
||||
name: {
|
||||
lerpSpeed: number;
|
||||
|
||||
@@ -82,6 +82,9 @@ const UNIT_ORDER = [
|
||||
|
||||
const ATLAS_COLS = UNIT_ORDER.length;
|
||||
|
||||
/** Atlas column of the hydrogen bomb — drives the GPU glow halo. */
|
||||
const HYDROGEN_BOMB_COL = UNIT_ORDER.indexOf(UT_HYDROGEN_BOMB);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Instance data layout
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -175,6 +178,10 @@ export class UnitPass {
|
||||
private uFlickerSpeed: WebGLUniformLocation;
|
||||
private uAngryColor: WebGLUniformLocation;
|
||||
private uAltView: WebGLUniformLocation;
|
||||
private uHBombGlowScale: WebGLUniformLocation;
|
||||
private uHBombGlowColor: WebGLUniformLocation;
|
||||
private uHBombGlowStrength: WebGLUniformLocation;
|
||||
private uHBombGlowInner: WebGLUniformLocation;
|
||||
|
||||
private affiliationTex: WebGLTexture | null = null;
|
||||
private altView = false;
|
||||
@@ -229,8 +236,8 @@ export class UnitPass {
|
||||
// Compile shaders
|
||||
this.program = createProgram(
|
||||
gl,
|
||||
shaderSrc(unitVertSrc, { ATLAS_COLS }),
|
||||
shaderSrc(unitFragSrc, { PALETTE_SIZE: getPaletteSize() }),
|
||||
shaderSrc(unitVertSrc, { ATLAS_COLS, HYDROGEN_BOMB_COL }),
|
||||
shaderSrc(unitFragSrc, { PALETTE_SIZE: getPaletteSize(), ATLAS_COLS }),
|
||||
);
|
||||
this.uCamera = gl.getUniformLocation(this.program, "uCamera")!;
|
||||
this.uTick = gl.getUniformLocation(this.program, "uTick")!;
|
||||
@@ -239,6 +246,22 @@ export class UnitPass {
|
||||
this.uAngryColor = gl.getUniformLocation(this.program, "uAngryColor")!;
|
||||
|
||||
this.uAltView = gl.getUniformLocation(this.program, "uAltView")!;
|
||||
this.uHBombGlowScale = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uHBombGlowScale",
|
||||
)!;
|
||||
this.uHBombGlowColor = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uHBombGlowColor",
|
||||
)!;
|
||||
this.uHBombGlowStrength = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uHBombGlowStrength",
|
||||
)!;
|
||||
this.uHBombGlowInner = gl.getUniformLocation(
|
||||
this.program,
|
||||
"uHBombGlowInner",
|
||||
)!;
|
||||
|
||||
// Texture unit bindings
|
||||
gl.useProgram(this.program);
|
||||
@@ -470,6 +493,15 @@ export class UnitPass {
|
||||
gl.uniform1f(this.uFlickerSpeed, us.flickerSpeed);
|
||||
gl.uniform3f(this.uAngryColor, us.angryR, us.angryG, us.angryB);
|
||||
gl.uniform1i(this.uAltView, this.altView ? 1 : 0);
|
||||
gl.uniform1f(this.uHBombGlowScale, us.hBombGlowScale);
|
||||
gl.uniform3f(
|
||||
this.uHBombGlowColor,
|
||||
us.hBombGlowR,
|
||||
us.hBombGlowG,
|
||||
us.hBombGlowB,
|
||||
);
|
||||
gl.uniform1f(this.uHBombGlowStrength, us.hBombGlowStrength);
|
||||
gl.uniform1f(this.uHBombGlowInner, us.hBombGlowInner);
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.paletteTex);
|
||||
|
||||
@@ -179,7 +179,13 @@
|
||||
"flickerSpeed": 0.3,
|
||||
"angryR": 0.784,
|
||||
"angryG": 0,
|
||||
"angryB": 0
|
||||
"angryB": 0,
|
||||
"hBombGlowScale": 2.2,
|
||||
"hBombGlowR": 1.0,
|
||||
"hBombGlowG": 0.72,
|
||||
"hBombGlowB": 0.15,
|
||||
"hBombGlowStrength": 0.5,
|
||||
"hBombGlowInner": 0.45
|
||||
},
|
||||
"name": {
|
||||
"lerpSpeed": 10,
|
||||
|
||||
@@ -8,12 +8,17 @@ uniform float uTick;
|
||||
uniform float uFlickerSpeed;
|
||||
uniform vec3 uAngryColor;
|
||||
uniform int uAltView;
|
||||
uniform vec3 uHBombGlowColor;
|
||||
uniform float uHBombGlowStrength;
|
||||
uniform float uHBombGlowInner;
|
||||
|
||||
in vec2 vLocalPos;
|
||||
in vec2 vAtlasUV;
|
||||
in vec2 vQuadPos;
|
||||
in vec2 vCellUV;
|
||||
flat in float vAtlasCol;
|
||||
flat in float vOwnerID;
|
||||
flat in float vFlags;
|
||||
flat in float vHash;
|
||||
flat in float vGlow;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
@@ -34,10 +39,29 @@ const vec3 FLICKER_COLORS[4] = vec3[4](
|
||||
);
|
||||
|
||||
void main() {
|
||||
vec4 texel = texture(uAtlas, vAtlasUV);
|
||||
// The sprite lives in the central cell-space region [0,1]; for the enlarged
|
||||
// hydrogen-bomb quad, anything outside that range is glow-only margin.
|
||||
vec4 texel = vec4(0.0);
|
||||
bool inSprite = vCellUV.x >= 0.0 && vCellUV.x <= 1.0 &&
|
||||
vCellUV.y >= 0.0 && vCellUV.y <= 1.0;
|
||||
if (inSprite) {
|
||||
vec2 atlasUV = vec2((vAtlasCol + vCellUV.x) / float(ATLAS_COLS), vCellUV.y);
|
||||
texel = texture(uAtlas, atlasUV);
|
||||
}
|
||||
|
||||
// Discard fully transparent pixels
|
||||
if (texel.a < 0.01) discard;
|
||||
// Outside the sprite: render the steady soft glow under the hydrogen bomb,
|
||||
// otherwise discard. Glow is suppressed in alt (affiliation) view.
|
||||
if (texel.a < 0.01) {
|
||||
if (vGlow > 0.5 && uAltView == 0) {
|
||||
float d = length(vQuadPos - 0.5) * 2.0; // 0 at center → ~1 at quad edge
|
||||
float g = (1.0 - smoothstep(uHBombGlowInner, 1.0, d)) * uHBombGlowStrength;
|
||||
if (g > 0.001) {
|
||||
fragColor = vec4(uHBombGlowColor, g);
|
||||
return;
|
||||
}
|
||||
}
|
||||
discard;
|
||||
}
|
||||
|
||||
float gray = texel.r;
|
||||
|
||||
|
||||
@@ -10,12 +10,15 @@ layout(location = 2) in vec2 aInstFlags; // atlasIdx (uint8→float), flags (uin
|
||||
uniform mat3 uCamera;
|
||||
|
||||
uniform float uUnitSize;
|
||||
uniform float uHBombGlowScale; // quad enlargement for the hydrogen bomb glow halo
|
||||
|
||||
out vec2 vLocalPos;
|
||||
out vec2 vAtlasUV;
|
||||
out vec2 vQuadPos; // quad coords [0,1] — drives the radial glow falloff
|
||||
out vec2 vCellUV; // sprite cell coords; the central 1/scale region is the sprite
|
||||
flat out float vAtlasCol;
|
||||
flat out float vOwnerID;
|
||||
flat out float vFlags; // 0.0 = normal, 1.0 = flicker, 2.0 = angry
|
||||
flat out float vHash; // per-instance hash for flicker phase offset
|
||||
flat out float vGlow; // 1.0 if this instance is a hydrogen bomb (draw glow), else 0.0
|
||||
|
||||
void main() {
|
||||
float worldX = aInstPos.x;
|
||||
@@ -24,13 +27,20 @@ void main() {
|
||||
|
||||
float atlasCol = aInstFlags.x;
|
||||
vFlags = aInstFlags.y;
|
||||
vAtlasCol = atlasCol;
|
||||
|
||||
// Position-based hash so each unit flickers independently
|
||||
vHash = fract(worldX * 0.1731 + worldY * 0.3179);
|
||||
|
||||
// Hydrogen bombs render an enlarged quad so there's room for a glow halo
|
||||
// around the sprite. All other units keep scale 1 (no behavior change).
|
||||
float isHBomb = step(abs(atlasCol - float(HYDROGEN_BOMB_COL)), 0.5);
|
||||
vGlow = isHBomb;
|
||||
float scale = mix(1.0, uHBombGlowScale, isHBomb);
|
||||
|
||||
// UNIT_SIZE is in world-space tiles — no zoom division needed.
|
||||
// Units scale with the map like territory tiles do.
|
||||
float halfSize = uUnitSize * 0.5;
|
||||
float halfSize = uUnitSize * 0.5 * scale;
|
||||
|
||||
vec2 center = vec2(worldX + 0.5, worldY + 0.5);
|
||||
vec2 worldPos = center + (aPos - 0.5) * halfSize * 2.0;
|
||||
@@ -38,9 +48,9 @@ void main() {
|
||||
vec3 clip = uCamera * vec3(worldPos, 1.0);
|
||||
gl_Position = vec4(clip.xy, 0.0, 1.0);
|
||||
|
||||
vLocalPos = aPos;
|
||||
vQuadPos = aPos;
|
||||
|
||||
// Atlas UV: map quad [0,1] to the correct column
|
||||
float colU = (atlasCol + aPos.x) / float(ATLAS_COLS);
|
||||
vAtlasUV = vec2(colU, aPos.y);
|
||||
// Map the enlarged quad back to sprite cell space: the central 1/scale
|
||||
// portion is the sprite, anything outside [0,1] is glow-only margin.
|
||||
vCellUV = (aPos - 0.5) * scale + 0.5;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user