diff --git a/src/engine/particle.c b/src/engine/particle.c index c7838ac..c195502 100644 --- a/src/engine/particle.c +++ b/src/engine/particle.c @@ -556,38 +556,69 @@ void particle_emit_atmosphere_dust(Vec2 cam_pos, Vec2 vp) { * Two sub-layers for depth: large slow "far" motes and small quick * "near" specks. Wind carries them; gravity_scale controls how much * environmental forces (wind + gravity) affect each particle. - * Particles spawn along the upwind viewport edge and drift inward; - * occasional interior spawns prevent a visible edge seam. */ - float wind = physics_get_wind(); - float margin = 32.0f; - float dir = (wind >= 0.0f) ? 1.0f : -1.0f; /* velocity sign */ + * When wind is strong, particles spawn along the upwind viewport edge + * and drift inward. When wind is calm, particles spawn across the + * full viewport to avoid clustering on one side. */ + float wind = physics_get_wind(); + float margin = 32.0f; + float abs_wind = (wind >= 0.0f) ? wind : -wind; + int has_wind = abs_wind > 5.0f; /* threshold for edge-spawning */ - /* Upwind edge X for the two edge-spawned layers */ - float edge_far = (wind >= 0.0f) ? cam_pos.x - margin - : cam_pos.x + vp.x + margin; - float edge_near = (wind >= 0.0f) ? cam_pos.x - margin * 0.5f - : cam_pos.x + vp.x + margin * 0.5f; + if (has_wind) { + float dir = (wind >= 0.0f) ? 1.0f : -1.0f; - /* Far dust motes — large, slow, translucent (1/frame) */ - spawn_dust_mote( - vec2(edge_far, cam_pos.y + randf() * vp.y), - vec2(dir * randf_range(8.0f, 25.0f), randf_range(-6.0f, 6.0f)), - 4.0f, 7.0f, 1.5f, 3.0f, 0.3f, 0.08f, - 180, 140, 100, 25); + /* Upwind edge X for the two edge-spawned layers */ + float edge_far = (wind >= 0.0f) ? cam_pos.x - margin + : cam_pos.x + vp.x + margin; + float edge_near = (wind >= 0.0f) ? cam_pos.x - margin * 0.5f + : cam_pos.x + vp.x + margin * 0.5f; - /* Near dust specks — small, quicker, brighter (1/frame) */ - spawn_dust_mote( - vec2(edge_near, cam_pos.y + randf() * vp.y), - vec2(dir * randf_range(15.0f, 40.0f), randf_range(-10.0f, 10.0f)), - 2.5f, 5.0f, 0.8f, 1.5f, 0.2f, 0.12f, - 200, 160, 120, 20); + /* Far dust motes — large, slow, translucent (1/frame) */ + spawn_dust_mote( + vec2(edge_far, cam_pos.y + randf() * vp.y), + vec2(dir * randf_range(8.0f, 25.0f), randf_range(-6.0f, 6.0f)), + 4.0f, 7.0f, 1.5f, 3.0f, 0.3f, 0.08f, + 180, 140, 100, 25); - /* Occasional interior spawn — prevents edge seam on calm wind */ - if (rand() % 3 == 0) { + /* Near dust specks — small, quicker, brighter (1/frame) */ + spawn_dust_mote( + vec2(edge_near, cam_pos.y + randf() * vp.y), + vec2(dir * randf_range(15.0f, 40.0f), randf_range(-10.0f, 10.0f)), + 2.5f, 5.0f, 0.8f, 1.5f, 0.2f, 0.12f, + 200, 160, 120, 20); + + /* Occasional interior spawn — prevents edge seam */ + if (rand() % 3 == 0) { + spawn_dust_mote( + vec2(cam_pos.x + randf() * vp.x, cam_pos.y + randf() * vp.y), + vec2(randf_range(-5.0f, 5.0f), randf_range(-8.0f, 3.0f)), + 3.0f, 6.0f, 1.0f, 2.5f, 0.4f, 0.06f, + 160, 130, 95, 25); + } + } else { + /* Calm wind — spawn across the full viewport to distribute evenly */ + + /* Far dust motes — large, slow, translucent (1/frame) */ spawn_dust_mote( vec2(cam_pos.x + randf() * vp.x, cam_pos.y + randf() * vp.y), - vec2(randf_range(-5.0f, 5.0f), randf_range(-8.0f, 3.0f)), - 3.0f, 6.0f, 1.0f, 2.5f, 0.4f, 0.06f, - 160, 130, 95, 25); + vec2(randf_range(-10.0f, 10.0f), randf_range(-6.0f, 6.0f)), + 4.0f, 7.0f, 1.5f, 3.0f, 0.3f, 0.08f, + 180, 140, 100, 25); + + /* Near dust specks — small, quicker, brighter (1/frame) */ + spawn_dust_mote( + vec2(cam_pos.x + randf() * vp.x, cam_pos.y + randf() * vp.y), + vec2(randf_range(-15.0f, 15.0f), randf_range(-10.0f, 10.0f)), + 2.5f, 5.0f, 0.8f, 1.5f, 0.2f, 0.12f, + 200, 160, 120, 20); + + /* Extra interior mote for density parity with windy path */ + if (rand() % 3 == 0) { + spawn_dust_mote( + vec2(cam_pos.x + randf() * vp.x, cam_pos.y + randf() * vp.y), + vec2(randf_range(-5.0f, 5.0f), randf_range(-8.0f, 3.0f)), + 3.0f, 6.0f, 1.0f, 2.5f, 0.4f, 0.06f, + 160, 130, 95, 25); + } } }