mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 02:56:17 -10:00
feat(mods): adjust final world fog parameters
Co-Authored-By: Codex <[email protected]>
This commit is contained in:
1 parent
885e287a2e
commit
e0c64b5cc8
4 files changed
+121
No files matched your search
@@ -1331,6 +1331,17 @@ impl Game<'_> {
|
||||
pub(super) fn native_zoom(&self, zoom: &mut Option<NativeZoom>) {
|
||||
let _ = zoom;
|
||||
}
|
||||
/// Final world fog, after ordinary calculations and Horizon's fog option, before upload.
|
||||
/// Title panoramas skip it. Invalid color or distance values retain the original whole fog.
|
||||
pub(super) fn native_render_fog(
|
||||
&self,
|
||||
partial: f32,
|
||||
camera: [f64; 3],
|
||||
fog_type: horizon_client::fog::FogType,
|
||||
fog: &mut horizon_client::fog::FogData,
|
||||
) {
|
||||
let _ = (partial, camera, fog_type, fog);
|
||||
}
|
||||
/// The camera shake native mods ask for in this frame, `partial` ticks
|
||||
/// past the last tick (0 to 1): add your pitch, yaw and roll in degrees
|
||||
/// and your offset in blocks, along the camera's right, up and back, to
|
||||
|
||||
@@ -140,8 +140,43 @@ impl Game<'_> {
|
||||
if !self.settings.horizon.fog {
|
||||
horizon_client::fog::clear_distance_fog(&mut fog, &input);
|
||||
}
|
||||
if self.title.is_none() {
|
||||
fog = self.native_fog_now(self.clock.fraction() as f32, at, fog_type, fog);
|
||||
}
|
||||
fog
|
||||
}
|
||||
fn native_fog_now(
|
||||
&self,
|
||||
partial: f32,
|
||||
camera: [f64; 3],
|
||||
fog_type: FogType,
|
||||
original: FogData,
|
||||
) -> FogData {
|
||||
let mut fog = original;
|
||||
self.native_render_fog(partial, camera, fog_type, &mut fog);
|
||||
let distances = [
|
||||
fog.environmental_start,
|
||||
fog.environmental_end,
|
||||
fog.render_distance_start,
|
||||
fog.render_distance_end,
|
||||
fog.sky_end,
|
||||
fog.cloud_end,
|
||||
];
|
||||
if fog
|
||||
.color
|
||||
.iter()
|
||||
.all(|v| v.is_finite() && (0.0..=1.0).contains(v))
|
||||
&& distances.iter().all(|v| v.is_finite())
|
||||
&& fog.environmental_end > fog.environmental_start
|
||||
&& fog.render_distance_end > fog.render_distance_start
|
||||
&& fog.sky_end >= 0.0
|
||||
&& fog.cloud_end >= 0.0
|
||||
{
|
||||
fog
|
||||
} else {
|
||||
original
|
||||
}
|
||||
}
|
||||
/// `FogRenderer.computeFogColor`'s night vision brightenFactor:
|
||||
/// `GameRenderer.nightVisionScale`, but not while Darkness is on.
|
||||
fn fog_night_vision(&self) -> f32 {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"""Exercise the actual final-fog validation/dispatch with a fake mod."""
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from test_world_input_capture import method
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
class RenderFogHookTests(unittest.TestCase):
|
||||
@unittest.skipUnless(shutil.which("rustc"), "requires Rust")
|
||||
def test_valid_parameters_and_atomic_invalid_fallback(self):
|
||||
source = (ROOT / "crates/horizon-client/src/game/render_tick.rs").read_text(encoding="utf-8")
|
||||
actual = method(source, "native_fog_now")
|
||||
fixture = r"""
|
||||
#[derive(Clone, Copy)]
|
||||
enum FogType { Water }
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
struct FogData {
|
||||
color: [f32; 4], environmental_start: f32, environmental_end: f32,
|
||||
render_distance_start: f32, render_distance_end: f32, sky_end: f32, cloud_end: f32,
|
||||
}
|
||||
struct Game { replacement: Option<FogData> }
|
||||
impl Game {
|
||||
fn native_render_fog(&self, partial: f32, camera: [f64; 3], kind: FogType, fog: &mut FogData) {
|
||||
assert_eq!((partial, camera), (0.5, [1.0, 2.0, 3.0]));
|
||||
assert!(matches!(kind, FogType::Water));
|
||||
assert_eq!(fog.environmental_start, -8.0);
|
||||
if let Some(value) = self.replacement { *fog = value; }
|
||||
}
|
||||
METHOD
|
||||
}
|
||||
#[test]
|
||||
fn changed_unchanged_and_invalid() {
|
||||
let original = FogData { color: [0.2, 0.3, 0.4, 1.0], environmental_start: -8.0,
|
||||
environmental_end: 24.0, render_distance_start: 230.0, render_distance_end: 256.0,
|
||||
sky_end: 24.0, cloud_end: 24.0 };
|
||||
let run = |replacement| Game { replacement }.native_fog_now(0.5, [1.0, 2.0, 3.0], FogType::Water, original);
|
||||
assert_eq!(run(None), original);
|
||||
let changed = FogData { color: [1.0, 0.0, 0.5, 0.5], environmental_start: -16.0,
|
||||
environmental_end: 48.0, render_distance_start: 512.0, render_distance_end: 1024.0,
|
||||
sky_end: 0.0, cloud_end: 80.0 };
|
||||
assert_eq!(run(Some(changed)), changed);
|
||||
for invalid in [FogData { environmental_start: 48.0, ..changed },
|
||||
FogData { environmental_end: f32::NAN, ..changed },
|
||||
FogData { render_distance_end: 512.0, ..changed },
|
||||
FogData { render_distance_start: f32::NEG_INFINITY, ..changed },
|
||||
FogData { sky_end: -1.0, ..changed }, FogData { cloud_end: f32::INFINITY, ..changed },
|
||||
FogData { cloud_end: -1.0, ..changed },
|
||||
FogData { color: [f32::NAN, 0.0, 0.0, 1.0], ..changed },
|
||||
FogData { color: [0.0, 0.0, 0.0, 1.1], ..changed }] {
|
||||
assert_eq!(run(Some(invalid)), original);
|
||||
}
|
||||
}
|
||||
""".replace("METHOD", actual)
|
||||
with tempfile.TemporaryDirectory() as folder:
|
||||
src, exe = Path(folder) / "fog.rs", Path(folder) / "fog.exe"
|
||||
src.write_text(fixture, encoding="utf-8")
|
||||
subprocess.run(["rustc", "--edition=2024", "--test", str(src), "-o", str(exe)], check=True)
|
||||
subprocess.run([str(exe)], check=True)
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"name": "render_fog",
|
||||
"target": "horizon_client::game::native_hooks::Game<'_>::native_render_fog",
|
||||
"neoforge": "ViewportEvent.RenderFog",
|
||||
"side": "client"
|
||||
}
|
||||
],
|
||||
"rust_summary": "a final world fog hook (`native_render_fog`).",
|
||||
"doc_summary": "`native_render_fog(partial, camera, fog_type, fog)` adjusts final FogData after normal environment/effect/render-distance calculation and Horizon's fog option, before GPU upload. Title panoramas skip it. The whole request is accepted only when color components are finite in 0..=1, all distances are finite, environmental and render-distance ends exceed their starts, and sky/cloud ends are nonnegative. Negative near planes remain valid (water/lava). Invalid requests retain the original whole fog. Bundles need \"hooks\": @API:render_fog@."
|
||||
}
|
||||
Reference in new issue
Block a user