feat(render): SAGE water/ocean shaders, per-user logs, camera & culling

Terrain pass now ports the SAGE water model (Ocean.fx / OpenSAGE Water.frag): a de-gridded procedural wave normal combined with the retail ra3_deepocean flow and ra3_deepocean_nrm bump maps (appended as the last two terrain-atlas layers, no new backend binding), Schlick fresnel, sky reflection + depth-graded refraction, SAGE diffuse/specular lighting, depth-based transparency, and an underwater tint (UnderwaterDeferred.fx). Mirrored across terrain.frag / dx_terrain.hlsl / webgl_terrain_frag.glsl / webgpu_terrain.wgsl.

Also: logs move to the per-user state dir (%LOCALAPPDATA%\\OpenRA3\\logs, else XDG) and archive as openra3.<stamp>.log; the FPS label shows the active backend; middle-drag camera reset; objects and roads below the water plane are culled.
This commit is contained in:
EnderTheCoder
2026-09-30 22:18:08 +08:00
parent 10a1963eec
commit 26e1934a5d
16 changed files with 969 additions and 249 deletions
+12 -5
View File
@@ -314,10 +314,11 @@ export namespace ra3::render {
}
/**
* A small translucent label for the top-left corner, e.g. `FPS: 155/160`.
* `cap == 0` means vertical sync, `cap < 0` means uncapped.
* A small translucent label for the top-left corner, e.g.
* `FPS: 155/160 [vulkan]`. `cap == 0` means vertical sync, `cap < 0` means
* uncapped. `backend` is the active renderer backend name (empty omits it).
*/
[[nodiscard]] inline auto compose_fps_label(uint32 fps, int cap) -> image {
[[nodiscard]] inline auto compose_fps_label(uint32 fps, int cap, std::string_view backend = {}) -> image {
char text[64];
if (cap == 0) {
std::snprintf(text, sizeof(text), "FPS: %u/vsync", fps);
@@ -326,10 +327,16 @@ export namespace ra3::render {
} else {
std::snprintf(text, sizeof(text), "FPS: %u/%d", fps, cap);
}
const auto w = text_width(text, 1U) + 8U;
std::string label{text};
if (!backend.empty()) {
label += " [";
label += backend;
label += ']';
}
const auto w = text_width(label, 1U) + 8U;
const auto h = detail::glyph_height + 6U;
image img(w, h, argb(30, 6, 6, 200)); // warm translucent backing
draw_text(img, 4, 3, text, argb(240, 200, 90), 1U);
draw_text(img, 4, 3, label, argb(240, 200, 90), 1U);
return img;
}