feat(render): Phase G.1 — billboard particle renderer for weather + spells
Add ParticleRenderer that draws every live particle from the shared ParticleSystem as a billboarded quad. Unit quad VBO + per-instance (pos, size, color) VBO with glVertexAttribDivisor for one draw call per emitter. Billboards using the camera's basis vectors so quads always face the viewer. Fragment shader does a procedural radial falloff (no texture pipeline needed — raindrops / snowflakes read as soft dots). AttachLocal emitters get re-centred on the camera each frame so the rain volume follows the player per r12 §7. Two-pass render splits additive from alpha-blend emitters so blend state flips once per kind rather than per-emitter. Wired into GameWindow.OnRender after static-mesh draw with depth write off (particles occluded by walls but don't self-occlude). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9957070cab
commit
9618c66813
4 changed files with 283 additions and 0 deletions
|
|
@ -140,6 +140,7 @@ public sealed class GameWindow : IDisposable
|
|||
private readonly AcDream.Core.Vfx.EmitterDescRegistry _emitterRegistry = new();
|
||||
private AcDream.Core.Vfx.ParticleSystem? _particleSystem;
|
||||
private AcDream.Core.Vfx.ParticleHookSink? _particleSink;
|
||||
private AcDream.App.Rendering.ParticleRenderer? _particleRenderer;
|
||||
|
||||
// Remote-entity motion inference: tracks when each remote entity last
|
||||
// moved meaningfully. Used in TickAnimations to swap to Ready when
|
||||
|
|
@ -737,6 +738,12 @@ public sealed class GameWindow : IDisposable
|
|||
_skyRenderer = new AcDream.App.Rendering.Sky.SkyRenderer(
|
||||
_gl, _dats, skyShader, _textureCache);
|
||||
|
||||
// Phase G.1 particle renderer — renders rain / snow / spell auras
|
||||
// spawned into the shared ParticleSystem as billboard quads.
|
||||
// Weather uses AttachLocal emitters so the rain volume follows
|
||||
// the player.
|
||||
_particleRenderer = new ParticleRenderer(_gl, shadersDir);
|
||||
|
||||
// Phase A.1: replace the one-shot 3×3 preload with a streaming controller.
|
||||
// Parse runtime radius from environment (default 2 → 5×5 window).
|
||||
// Values outside [0, 8] fall back to the field default of 2.
|
||||
|
|
@ -3035,6 +3042,13 @@ public sealed class GameWindow : IDisposable
|
|||
neverCullLandblockId: playerLb,
|
||||
visibleCellIds: visibility?.VisibleCellIds);
|
||||
|
||||
// Phase G.1 / E.3: draw all live particles after opaque
|
||||
// scene geometry so alpha blending composites correctly.
|
||||
// Runs with depth test on (particles occluded by walls)
|
||||
// but depth write off (no self-occlusion sorting needed).
|
||||
if (_particleSystem is not null && _particleRenderer is not null)
|
||||
_particleRenderer.Draw(_particleSystem, camera, camPos);
|
||||
|
||||
// Debug: draw collision shapes as wireframe cylinders around the
|
||||
// player so we can visually verify alignment with scenery meshes.
|
||||
if (_debugCollisionVisible && _debugLines is not null)
|
||||
|
|
@ -3690,6 +3704,7 @@ public sealed class GameWindow : IDisposable
|
|||
_shader?.Dispose();
|
||||
_sceneLightingUbo?.Dispose();
|
||||
_skyRenderer?.Dispose();
|
||||
_particleRenderer?.Dispose();
|
||||
_debugLines?.Dispose();
|
||||
_textRenderer?.Dispose();
|
||||
_debugFont?.Dispose();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue