feat: Add Spear of Destiny demo support with dedicated asset registry and entity definitions
- Introduced SpearDemoAssetRegistry for managing assets specific to the Spear of Destiny demo. - Created SpearDemoEntityModule to define enemy animations with adjusted sprite ranges. - Implemented SpearDemoHudModule and SpearDemoMenuPicModule for HUD and menu assets. - Added SpearDemoSfxModule for sound effect mappings specific to the demo version. - Updated enemy classes (Guard, Mutant, Officer, SS) to support custom animation sets. - Modified entity registry to accept a custom AssetRegistry for spawning entities. - Enhanced rendering with CRT phosphor bloom effect in GLSL shaders. - Adjusted ASCII and software renderer layouts for improved UI spacing. - Added tests for SpearDemoAssetRegistry to ensure correct asset resolution and enemy spawning. Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -14,6 +14,9 @@ class WolfGlslRenderer extends BaseWolfRenderer {
|
||||
/// Whether CRT-like post effects are enabled in the shader pass.
|
||||
final bool effectsEnabled;
|
||||
|
||||
/// Whether CRT phosphor bloom is enabled in the shader pass.
|
||||
final bool bloomEnabled;
|
||||
|
||||
/// Callback when shader loading fails and software fallback should be used.
|
||||
final VoidCallback? onUnavailable;
|
||||
|
||||
@@ -21,6 +24,7 @@ class WolfGlslRenderer extends BaseWolfRenderer {
|
||||
const WolfGlslRenderer({
|
||||
required super.engine,
|
||||
this.effectsEnabled = false,
|
||||
this.bloomEnabled = false,
|
||||
super.onKeyEvent,
|
||||
this.onUnavailable,
|
||||
super.key,
|
||||
@@ -116,6 +120,7 @@ class _WolfGlslRendererState extends BaseWolfRendererState<WolfGlslRenderer> {
|
||||
frame: _renderedFrame!,
|
||||
shader: _shader!,
|
||||
effectsEnabled: widget.effectsEnabled,
|
||||
bloomEnabled: widget.bloomEnabled,
|
||||
elapsedSeconds: widget.engine.timeAliveMs / 1000.0,
|
||||
),
|
||||
child: const SizedBox.expand(),
|
||||
@@ -153,12 +158,14 @@ class _GlslFramePainter extends CustomPainter {
|
||||
final ui.Image frame;
|
||||
final ui.FragmentShader shader;
|
||||
final bool effectsEnabled;
|
||||
final bool bloomEnabled;
|
||||
final double elapsedSeconds;
|
||||
|
||||
_GlslFramePainter({
|
||||
required this.frame,
|
||||
required this.shader,
|
||||
required this.effectsEnabled,
|
||||
required this.bloomEnabled,
|
||||
required this.elapsedSeconds,
|
||||
});
|
||||
|
||||
@@ -173,6 +180,7 @@ class _GlslFramePainter extends CustomPainter {
|
||||
..setFloat(3, texelY)
|
||||
..setFloat(4, effectsEnabled ? 1.0 : 0.0)
|
||||
..setFloat(5, elapsedSeconds)
|
||||
..setFloat(6, bloomEnabled ? 1.0 : 0.0)
|
||||
..setImageSampler(0, frame);
|
||||
|
||||
final Paint paint = Paint()
|
||||
@@ -187,6 +195,7 @@ class _GlslFramePainter extends CustomPainter {
|
||||
return oldDelegate.frame != frame ||
|
||||
oldDelegate.shader != shader ||
|
||||
oldDelegate.effectsEnabled != effectsEnabled ||
|
||||
oldDelegate.bloomEnabled != bloomEnabled ||
|
||||
oldDelegate.elapsedSeconds != elapsedSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ uniform vec2 uTexel;
|
||||
uniform float uEffectsEnabled;
|
||||
// Engine time in seconds used to animate scanline travel.
|
||||
uniform float uTime;
|
||||
// 1.0 enables CRT phosphor bloom glow, 0.0 disables it.
|
||||
uniform float uBloomEnabled;
|
||||
// Source frame produced by the software renderer.
|
||||
uniform sampler2D uTexture;
|
||||
|
||||
@@ -46,6 +48,7 @@ void main() {
|
||||
texture(uTexture, bleedUv1).rgb * 0.28 +
|
||||
texture(uTexture, bleedUv2).rgb * 0.14 +
|
||||
texture(uTexture, bleedUv3).rgb * 0.06;
|
||||
float edgeBleedLuma = luma(edgeBleedColor);
|
||||
|
||||
// Approximate concave bezel depth by measuring how far this fragment is
|
||||
// from the emissive screen boundary in aspect-corrected UV space.
|
||||
@@ -57,20 +60,24 @@ void main() {
|
||||
vec2 clampedCentered = clampedUv * 2.0 - 1.0;
|
||||
float cornerFactor = smoothstep(0.60, 1.15, length(clampedCentered));
|
||||
|
||||
float verticalShade = 0.88 + 0.07 * (1.0 - (FlutterFragCoord().y / uResolution.y));
|
||||
float depthShade = 1.0 - smoothstep(0.0, 0.058, overflow) * 0.34;
|
||||
float grain = sin(FlutterFragCoord().x * 0.21 + FlutterFragCoord().y * 0.11) * 0.006;
|
||||
float moldedHighlight = smoothstep(0.072, 0.0, overflow) * 0.028;
|
||||
float verticalShade = 0.88 + 0.07 * (1.0 - (FlutterFragCoord().y / uResolution.y));
|
||||
float depthShade = 1.0 - smoothstep(0.0, 0.058, overflow) * 0.34;
|
||||
float grain = sin(FlutterFragCoord().x * 0.21 + FlutterFragCoord().y * 0.11) * 0.006;
|
||||
float moldedHighlight = smoothstep(0.072, 0.0, overflow) * 0.028;
|
||||
|
||||
// Deeper arcade-style profile: tighter, scene-tinted bleed rolloff.
|
||||
float bezelGlow = exp(-bezelDistance * 82.0) * mix(1.0, 0.56, cornerFactor);
|
||||
float innerLip = exp(-bezelDistance * 170.0) * 0.10;
|
||||
float bleedStrength = smoothstep(0.12, 0.0, overflow) * (0.78 - cornerFactor * 0.26);
|
||||
// Deeper arcade-style profile: tighter, scene-tinted bleed rolloff.
|
||||
float bezelGlow = exp(-bezelDistance * 82.0) * mix(1.0, 0.56, cornerFactor);
|
||||
float innerLip = exp(-bezelDistance * 170.0) * 0.10;
|
||||
float bleedStrength = smoothstep(0.12, 0.0, overflow) * (0.78 - cornerFactor * 0.26);
|
||||
float bloomBezelBoost = 1.0 +
|
||||
uBloomEnabled * smoothstep(0.16, 0.82, edgeBleedLuma) * 0.75;
|
||||
float bloomLipBoost = 1.0 +
|
||||
uBloomEnabled * smoothstep(0.10, 0.68, edgeBleedLuma) * 0.45;
|
||||
|
||||
vec3 bezelColor =
|
||||
vec3(0.225, 0.225, 0.215) * verticalShade * depthShade +
|
||||
edgeBleedColor * bezelGlow * bleedStrength * 1.12 +
|
||||
edgeBleedColor * innerLip * 0.36 +
|
||||
edgeBleedColor * bezelGlow * bleedStrength * 1.12 * bloomBezelBoost +
|
||||
edgeBleedColor * innerLip * 0.36 * bloomLipBoost +
|
||||
vec3(moldedHighlight) +
|
||||
vec3(grain);
|
||||
fragColor = vec4(bezelColor, 1.0);
|
||||
@@ -127,5 +134,42 @@ void main() {
|
||||
outColor *= mix(0.62, 1.0, vignette);
|
||||
}
|
||||
|
||||
if (uBloomEnabled > 0.5) {
|
||||
// CRT phosphor bloom: bright areas spread a soft luminance glow.
|
||||
// Sample a three-ring cross pattern directly from the source texture so
|
||||
// the spread is measured in source-texel space and stays resolution-stable.
|
||||
vec2 s1 = uTexel * 3.0;
|
||||
vec2 s2 = uTexel * 7.0;
|
||||
vec2 s3 = uTexel * 13.0;
|
||||
|
||||
vec3 glow = vec3(0.0);
|
||||
// Inner ring — weight 1.0 each
|
||||
glow += texture(uTexture, uv + vec2( s1.x, 0.0)).rgb;
|
||||
glow += texture(uTexture, uv + vec2(-s1.x, 0.0)).rgb;
|
||||
glow += texture(uTexture, uv + vec2( 0.0, s1.y)).rgb;
|
||||
glow += texture(uTexture, uv + vec2( 0.0, -s1.y)).rgb;
|
||||
// Mid ring — weight 0.5 each
|
||||
glow += texture(uTexture, uv + vec2( s2.x, 0.0)).rgb * 0.5;
|
||||
glow += texture(uTexture, uv + vec2(-s2.x, 0.0)).rgb * 0.5;
|
||||
glow += texture(uTexture, uv + vec2( 0.0, s2.y)).rgb * 0.5;
|
||||
glow += texture(uTexture, uv + vec2( 0.0, -s2.y)).rgb * 0.5;
|
||||
// Outer ring — weight 0.25 each
|
||||
glow += texture(uTexture, uv + vec2( s3.x, 0.0)).rgb * 0.25;
|
||||
glow += texture(uTexture, uv + vec2(-s3.x, 0.0)).rgb * 0.25;
|
||||
glow += texture(uTexture, uv + vec2( 0.0, s3.y)).rgb * 0.25;
|
||||
glow += texture(uTexture, uv + vec2( 0.0, -s3.y)).rgb * 0.25;
|
||||
// Normalize: 4*1.0 + 4*0.5 + 4*0.25 = 7.0
|
||||
glow /= 7.0;
|
||||
|
||||
// Only bright pixels contribute — gate the bloom contribution on luma.
|
||||
float glowLuma = luma(glow);
|
||||
float bloomStrength = smoothstep(0.18, 0.82, glowLuma);
|
||||
|
||||
// Add bloom additively then apply a gentle Reinhard-style tone-map to
|
||||
// prevent over-saturation while keeping dark areas clean.
|
||||
outColor = outColor + glow * bloomStrength * 0.42;
|
||||
outColor = outColor / (outColor + vec3(0.75)) * 1.75;
|
||||
}
|
||||
|
||||
fragColor = vec4(outColor, centerSample.a);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user