- an attached model's frame rides its wearer's rig: composed as bone-offset-plus-world-root every frame, a helm at Elwynn's ~9500-yard coordinates lands on a 0.98 mm f32 grid (exactly 2^-10 yd) and snaps whole steps against a body that moves exactly — the plumed helm, both pauldrons, the sword and the shield, 0.83 px of hop at a fully zoomed-in close-up; each attachment now holds one rigid palette frame composed in the rig's own space, the world position added back camera-relative in the vertex stage, so nothing on the path is ever a big-plus-small sum — the property the reference keeps by injecting its view matrix once, at the root of the attachment tree - a rider composes through the rig's own frame, not the unit's: `joints_root` re-points at the saddle's seat anchor the moment a character mounts, so reading the host entity's transform would have placed a mounted rider's helm and weapon by the gap between its feet and its saddle — pinned by a test whose seat is offset far enough that the wrong frame misses by 6 yards - the water-walk surface is GROUND, not a post-step clamp: stepping onto water with Levitate up froze the last land velocity in — the mover classified every over-water frame airborne (liquid is queried, never swept) while the clamp reported grounded, so the body ran on at full speed through every key release; the reference has no water-walking code path at all — the flag just ORs the liquid layers into the same swept query that finds terrain — so the surface now feeds the same grounded classification the colliders do, and the clamp stays as the resolve half - `WOW_JITTER`, a motion-jitter meter: the camera, the unit root and the pose sampled every frame as first AND second differences — Δ² tells a slow idle from per-frame noise at two orders' distance — with every reading also in pixels at the subject's own distance, because the same millimetre is 0.03 px across a courtyard and 0.8 px pressed against the camera Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
188 lines
6.3 KiB
Rust
188 lines
6.3 KiB
Rust
//! **benilla-world** — the world renderer, with no game attached.
|
|
//!
|
|
//! Decision 1160's crate. The line it draws: *this crate can put something into a world and answer
|
|
//! questions about that world; it knows nothing about a session, a wire, an avatar or a UI.* What
|
|
//! enforces the line is not this doc — it is the dependency edge. `benilla-world` does not depend
|
|
//! on `benilla-app`, so a name that crosses back is a compile error rather than a lint, and
|
|
//! `benilla-worldview` links only this crate, so an engine that quietly needs the game stops
|
|
//! booting rather than quietly working.
|
|
//!
|
|
//! Above it: `benilla-app` (the game — session, wire, units, UI) and the two binaries.
|
|
//! Below it: `benilla-assets` (the asset foundation both sides share) and `benilla-formats`.
|
|
//!
|
|
//! **One instrument travels with the engine and three do not**, and the rule that sorts them is
|
|
//! whether `WorldPlugins` registers it. `art_scope` does — within-map art residency is engine
|
|
//! behaviour, not a readout — so it is here. The debug panel, the perf journal and the pipeline
|
|
//! warmer are registered by the game, and every one of them reads the game (units, targets, names,
|
|
//! the UI): they are instruments *above* the stack, and the compiler said so the moment the move
|
|
//! was attempted with them inside. That is the whole value of the crate edge over a naming
|
|
//! convention — the earlier module-name-based wall had them excluded by an assumption, and the
|
|
//! assumption was wrong.
|
|
|
|
/// Re-exported so a shim binary needs no `bevy` dependency of its own — the same courtesy
|
|
/// `benilla-app` extends to the `benilla` launcher.
|
|
pub use bevy::app::AppExit;
|
|
|
|
/// **Fixtures for dependents' tests.**
|
|
///
|
|
/// `#[cfg(test)]` code is not compiled for a downstream crate, so a fixture the game's tests share
|
|
/// with the engine's has to be ordinary public code. Three of `benilla-app`'s tests build a
|
|
/// particle emitter def — thirty fields of authored defaults — and duplicating that literal three
|
|
/// times is how two of the copies quietly drift from the type.
|
|
pub mod testing {
|
|
use benilla_formats::{
|
|
CellRamp, EmitParams, EmitTiming, OverLife, ParamsNow, ParticleBlend, ParticleEmitterDef,
|
|
ParticleShape,
|
|
};
|
|
|
|
/// The sampled-parameter side of [`particle_def`], constant-baked so a sim test is
|
|
/// deterministic.
|
|
pub fn particle_params_now() -> ParamsNow {
|
|
ParamsNow {
|
|
emission_speed: 1.0,
|
|
speed_variation: 0.0,
|
|
vertical_range: 0.5,
|
|
horizontal_range: std::f32::consts::PI,
|
|
gravity: 0.0,
|
|
lifespan: 1.0,
|
|
area_length: 2.0,
|
|
area_width: 4.0,
|
|
z_source: 0.0,
|
|
}
|
|
}
|
|
|
|
/// A minimal emitter def for kernel tests — only the fields the emit kernel reads matter.
|
|
pub fn particle_def(shape: ParticleShape) -> ParticleEmitterDef {
|
|
ParticleEmitterDef {
|
|
flags: 0,
|
|
position: [1.0, 2.0, 3.0],
|
|
bone: 0,
|
|
shape,
|
|
blend: ParticleBlend::Add,
|
|
lit: false,
|
|
texture: None,
|
|
tile_rows: 1,
|
|
tile_cols: 1,
|
|
head_tail: 0,
|
|
timing: EmitTiming::constant(10.0),
|
|
params: EmitParams::constant(particle_params_now()),
|
|
drag: 0.0,
|
|
tail_time: 0.0,
|
|
spline: None,
|
|
geometry_model: None,
|
|
recursion_model: None,
|
|
angular_velocity_min: [0.0; 3],
|
|
angular_velocity_max: [0.0; 3],
|
|
inherit_scale: 0.0,
|
|
follow_speed1: 0.0,
|
|
follow_scale1: 0.0,
|
|
follow_speed2: 0.0,
|
|
follow_scale2: 0.0,
|
|
twinkle_speed: 0.0,
|
|
twinkle_percent: 1.0,
|
|
twinkle_min: 0.0,
|
|
twinkle_max: 0.0,
|
|
spin: 0.0,
|
|
over_life: OverLife {
|
|
mid: 0.5,
|
|
color: [[1.0; 4]; 3],
|
|
scale: [1.0; 3],
|
|
head_cells: [CellRamp::new(0, 0); 2],
|
|
tail_cells: [CellRamp::new(0, 0); 2],
|
|
repeat: [1.0; 2],
|
|
},
|
|
}
|
|
}
|
|
|
|
/// The plain planar def most emitter tests want.
|
|
pub fn plain_particle_def() -> ParticleEmitterDef {
|
|
particle_def(ParticleShape::Plane)
|
|
}
|
|
|
|
/// A bind-pose [`crate::rig_anim::RigPose`] for tests that resolve consumer anchors
|
|
/// (decision 1355: anchors spawn on first demand through `RigPose::anchor_for`, so a test
|
|
/// wearer/mount/caster needs a pose, not a hand-built joint map): one root-parented joint per
|
|
/// entry, seated at its local translation.
|
|
pub fn test_rig_pose(
|
|
root: bevy::ecs::entity::Entity,
|
|
joints: &[bevy::math::Vec3],
|
|
) -> crate::rig_anim::RigPose {
|
|
let skeleton = benilla_assets::ModelSkeleton {
|
|
joints: joints
|
|
.iter()
|
|
.map(|&t| benilla_assets::ModelJoint {
|
|
parent: -1,
|
|
local_translation: t,
|
|
billboard: None,
|
|
parent_arm: None,
|
|
})
|
|
.collect(),
|
|
spine_bone: None,
|
|
head_bone: None,
|
|
};
|
|
crate::rig_anim::RigPose::new(root, &skeleton)
|
|
}
|
|
}
|
|
|
|
pub mod art_scope;
|
|
pub mod assets;
|
|
pub mod bgwin;
|
|
pub mod billboard;
|
|
pub mod boot;
|
|
pub mod build_id;
|
|
pub mod clouds;
|
|
pub mod clutter;
|
|
pub mod collision;
|
|
pub mod decal;
|
|
pub mod dev_state;
|
|
pub mod doodad_anim;
|
|
pub mod entity_shade;
|
|
pub mod exterior_cull;
|
|
pub mod ffx_glow;
|
|
pub mod ground_fx;
|
|
pub mod instance_tint;
|
|
pub mod interact;
|
|
pub mod interior;
|
|
pub mod lighting;
|
|
pub mod liquid;
|
|
|
|
/// macOS `Cmd+Q`, re-pointed at the window close so the gesture goes through an exit the client
|
|
/// can actually observe (decision 1528).
|
|
pub mod mac_quit;
|
|
pub mod map_proj;
|
|
pub mod mat_anim_table;
|
|
pub mod mesh_tag;
|
|
pub mod model_fade;
|
|
pub mod model_forms;
|
|
pub mod model_render;
|
|
pub mod modkeys;
|
|
pub mod particles;
|
|
pub mod ribbons;
|
|
pub mod rig_anim;
|
|
pub mod rig_palette;
|
|
pub mod rig_rider;
|
|
pub mod schedule;
|
|
mod shaders;
|
|
pub mod sky;
|
|
pub mod sky_order;
|
|
pub mod static_gx;
|
|
pub mod static_merge;
|
|
pub mod sun;
|
|
pub mod surface;
|
|
pub mod terrain_stream;
|
|
pub mod thread_qos;
|
|
pub mod view;
|
|
pub mod vis_chain;
|
|
pub mod water_fx;
|
|
pub mod wdl;
|
|
pub mod weather;
|
|
pub mod wmo_portal;
|
|
pub mod wmo_sky;
|
|
pub mod world_census;
|
|
pub mod world_map;
|
|
pub mod world_plugins;
|
|
pub mod world_point;
|
|
pub mod world_unit;
|
|
pub mod worldview;
|
|
pub mod zfill;
|