feat: per-voice audio, outlines for what is not an actor, and a coast
Three things a game could not say, each of which had been worked around. Audio.play_at(id, gain:, pitch:, pan:) fires a one-shot with its own gain, pitch and stereo position. Audio.volume and Audio.pitch are global - they are the options screen - so a game placing a sound in the world was fighting them, and distance attenuation was simply not expressible. The backend already took volume and rate per call; this adds setPan: alongside them and stops routing through the master state. ludic.render3d gains outline_model(model, mat, width, r, g, b): a rim around something that is not an Actor. The outline pass walked the actor list and stopped, so instanced scatter - a forest - could not be highlighted at all. It is a queue flushed by the same pass, which is what gets the depth test right when the caller does not control pass order. And terrain_coast(cx, cz, margin, fall), the other way to make an island: the sea around the survey's own edge rather than cut out of the middle of it. terrain_island measures a radius from a centre, which drowns two thirds of a real survey to make an island of the rest; this measures inward from the boundary, so everything the data covers stays land and the coast is where the data runs out. Both modes gained a strand - the last few metres of height either side of the water line compressed, which stretches a cliff plunge out into beach and shallows. 132 regression tests and the self-host fixpoints pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
39ad8e85d9
commit
3c27606747
15 changed files with 30113 additions and 29690 deletions
|
|
@ -474,6 +474,10 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
|
|||
# Audio.play / play_music take a handle, or a name from the sound bank (Audio.define)
|
||||
if (meth == "play") { if first_arg_is_text(e) { bare = "audio_play_named"; push(labels, "name") } else { bare = "audio_play"; push(labels, "id") } }
|
||||
if (meth == "play_sound") { bare = "audio_play"; push(labels, "id") }
|
||||
# Audio.play_at(id, gain:, pitch:, pan:) — one shot with its own gain, pitch and
|
||||
# stereo position. Audio.volume / Audio.pitch stay global (they are the options
|
||||
# screen); this is the per-voice one, and it is what distance attenuation is built on.
|
||||
if (meth == "play_at") { bare = "audio_play_at"; push(labels, "id"); push(labels, "gain"); push(labels, "pitch"); push(labels, "pan") }
|
||||
if (meth == "play_music") { if first_arg_is_text(e) { bare = "audio_play_music_named"; push(labels, "name") } else { bare = "audio_play_music"; push(labels, "id") } }
|
||||
if (meth == "stop") { bare = "audio_stop"; push(labels, "id") }
|
||||
if (meth == "stop_music") { bare = "audio_stop_music" }
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ function emit_header() -> void {
|
|||
emith("declare i32 @snd_playing(ptr)\n")
|
||||
emith("declare void @snd_set_volume(ptr, i32)\n")
|
||||
emith("declare void @snd_set_rate(ptr, i32)\n")
|
||||
emith("declare void @snd_set_pan(ptr, i32)\n")
|
||||
emith("declare ptr @hs_req_new(ptr, ptr)\n") # #6 HTTP transport (http.ll)
|
||||
emith("declare void @hs_req_header(ptr, ptr, ptr)\n")
|
||||
emith("declare void @hs_req_body(ptr, ptr, i32)\n")
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ function is_intrinsic2(name: pointer) -> bool {
|
|||
if (name == "win_pad") or (name == "win_touch") { return true } # #51 gamepad / touch
|
||||
if (name == "win_cursor_mode") { return true } # #89 cursor capture
|
||||
if (name == "snd_load") or (name == "snd_play") or (name == "snd_stop") { return true } # #22 audio
|
||||
if (name == "snd_playing") or (name == "snd_set_volume") or (name == "snd_set_rate") { return true }
|
||||
if (name == "snd_playing") or (name == "snd_set_volume") or (name == "snd_set_rate") or (name == "snd_set_pan") { return true }
|
||||
if (name == "hs_req_new") or (name == "hs_req_header") or (name == "hs_req_body") { return true } # #6 HTTP
|
||||
if (name == "hs_send") or (name == "hs_done") or (name == "hs_status") or (name == "hs_body") { return true }
|
||||
if (name == "hs_blen") or (name == "hs_header") or (name == "hs_free") { return true }
|
||||
|
|
@ -78,6 +78,7 @@ function emit_intrinsic2(name: pointer, e: Node) -> Val {
|
|||
if (name == "snd_playing") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @snd_playing(ptr {a})`), "int") }
|
||||
if (name == "snd_set_volume") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @snd_set_volume(ptr "); emit(a); emit(", i32 "); emit(b); emit(")\n"); return val("0", "void") }
|
||||
if (name == "snd_set_rate") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @snd_set_rate(ptr "); emit(a); emit(", i32 "); emit(b); emit(")\n"); return val("0", "void") }
|
||||
if (name == "snd_set_pan") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @snd_set_pan(ptr "); emit(a); emit(", i32 "); emit(b); emit(")\n"); return val("0", "void") }
|
||||
# #6 HTTP transport — NSURLConnection on a background thread (http.ll).
|
||||
if (name == "hs_req_new") { let a = arg_code(e, 0); let b = arg_code(e, 1); return val(emit_bind(`call ptr @hs_req_new(ptr {a}, ptr {b})`), "pointer") }
|
||||
if (name == "hs_req_header") { let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); emit(" call void @hs_req_header(ptr "); emit(a); emit(", ptr "); emit(b); emit(", ptr "); emit(c); emit(")\n"); return val("0", "void") }
|
||||
|
|
|
|||
59522
selfhost/ludicc.seed.ll
59522
selfhost/ludicc.seed.ll
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue