mirror of
https://github.com/SK8-ENGINE/skate-3-rust-engine.git
synced 2026-10-26 19:47:44 +00:00
Light custom map scenes using authored environment and native provenance
This commit is contained in:
1 parent
2795121816
commit
e6047b7073
4 files changed
+81
-5
No files matched your search
@@ -37,7 +37,7 @@ pub(crate) fn build(
|
||||
physics: GamePhysics,
|
||||
skater: SkaterRuntime,
|
||||
) -> App {
|
||||
let retail_scene = config.map.as_ref().is_some_and(|map| map.materials.iter().any(|m| m.retail_definition.is_some()));
|
||||
let retail_scene = config.map.as_ref().is_some_and(|map| crate::retail_render::RetailScene::for_map(map));
|
||||
let mut app = App::new();
|
||||
crate::custom_models::register_source(&mut app);
|
||||
app.add_plugins(
|
||||
|
||||
@@ -96,9 +96,13 @@ impl PreparedScene {
|
||||
if let Some(map) = map {
|
||||
crate::skate_world::spawn(map, &mut self.commands, &mut self.meshes,
|
||||
&mut self.materials, &mut self.retail, &mut self.images, &crate::retail_render::MaterialTuning::load(root));
|
||||
crate::retail_render::spawn_backdrop(&map.name, root, &mut self.commands, &mut self.meshes, &mut self.materials, &mut self.retail, &mut self.images);
|
||||
crate::retail_render::spawn_sky(&map.name, root, &mut self.commands,
|
||||
&mut self.meshes, &mut self.images, &mut self.sky, &mut self.retail);
|
||||
if crate::retail_render::RetailScene::for_map(map) {
|
||||
crate::retail_render::spawn_backdrop(&map.name, root, &mut self.commands, &mut self.meshes, &mut self.materials, &mut self.retail, &mut self.images);
|
||||
crate::retail_render::spawn_sky(&map.name, root, &mut self.commands,
|
||||
&mut self.meshes, &mut self.images, &mut self.sky, &mut self.retail);
|
||||
} else {
|
||||
custom_lighting(map, &mut self.commands);
|
||||
}
|
||||
} else {
|
||||
crate::world::spawn_test_world(&mut self.commands, &mut self.meshes, &mut self.materials);
|
||||
}
|
||||
@@ -122,6 +126,41 @@ pub(crate) struct MapAssets {
|
||||
sky: Vec<AssetId<RetailSkyMaterial>>,
|
||||
images: Vec<AssetId<Image>>,
|
||||
}
|
||||
|
||||
/// Fixed authored start hour; dynamic refers to real-time surface lighting and
|
||||
/// shadows, not a new day/night simulation. All lights are scene-owned.
|
||||
fn custom_lighting(map: &skate_data::skate_map::SkateMap, commands: &mut SceneCommands) {
|
||||
let e = &map.environment;
|
||||
let hour = e[10].rem_euclid(24.);
|
||||
let elevation = ((hour - 6.) * std::f32::consts::PI / 12.).sin();
|
||||
let daylight = elevation.max(0.);
|
||||
let azimuth = e[11];
|
||||
let extended = e.len() == 45;
|
||||
let rgb = |i: usize| Color::linear_rgb(e[i].max(0.), e[i + 1].max(0.), e[i + 2].max(0.));
|
||||
let night = elevation < 0.;
|
||||
let color = if extended { rgb(if night { 35 } else { 32 }) } else { Color::WHITE };
|
||||
let strength = if extended { e[if night { 39 } else { 38 }].max(0.) } else { 1. };
|
||||
let ambient = if extended { e[41] + (e[40] - e[41]) * daylight } else { 0.32 };
|
||||
commands.insert_resource(GlobalAmbientLight {
|
||||
color: Color::WHITE, brightness: ambient.max(0.) * 1000., ..default()
|
||||
});
|
||||
let horizontal = (1. - elevation * elevation).max(0.).sqrt();
|
||||
let direction = Vec3::new(azimuth.cos() * horizontal, elevation.abs().max(0.001), azimuth.sin() * horizontal).normalize();
|
||||
commands.spawn((
|
||||
Name::new("Custom map sun/moon"),
|
||||
DirectionalLight {
|
||||
color, illuminance: 10_000. * strength * elevation.abs(),
|
||||
shadows_enabled: true,
|
||||
// Preserve authored indirect/baked diffuse on custom lightmapped meshes.
|
||||
affects_lightmapped_mesh_diffuse: false,
|
||||
..default()
|
||||
},
|
||||
Transform::default().looking_to(-direction, Vec3::Y),
|
||||
bevy::light::CascadeShadowConfigBuilder {
|
||||
maximum_distance: 100., first_cascade_far_bound: 10., ..default()
|
||||
}.build(),
|
||||
));
|
||||
}
|
||||
impl MapAssets {
|
||||
pub fn retire(world: &mut World) {
|
||||
let entities: Vec<_> = world.query_filtered::<Entity, With<MapEntity>>().iter(world).collect();
|
||||
@@ -143,6 +182,32 @@ impl MapAssets {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn custom_lighting_switches_without_accumulation_and_keeps_pbr() {
|
||||
let mut world = world();
|
||||
let mut map = skate_data::skate_map::SkateMap::parse(include_bytes!("../../../maps/format-demo.skate")).unwrap();
|
||||
map.name = "university".into(); // Names cannot select retail rendering.
|
||||
map.materials[0].retail_definition = None;
|
||||
map.extensions.clear();
|
||||
for retail in [false, true, false, true] {
|
||||
map.extensions.clear();
|
||||
if retail {
|
||||
// Legacy retail imports can retain provenance without definitions.
|
||||
map.extensions.push(skate_data::skate_map::Extension {
|
||||
tag: *b"WMET", schema: 1, payload: b"{}".to_vec(),
|
||||
});
|
||||
}
|
||||
assert_eq!(crate::retail_render::RetailScene::for_map(&map), retail);
|
||||
let mut scene = PreparedScene::new(&world);
|
||||
scene.prepare(Some(&map), std::path::Path::new("unused"));
|
||||
scene.publish(&mut world);
|
||||
assert_eq!(world.query::<&DirectionalLight>().iter(&world).count(), usize::from(!retail));
|
||||
assert!(world.resource::<Assets<StandardMaterial>>().iter().all(|(_, m)| !m.unlit));
|
||||
MapAssets::retire(&mut world);
|
||||
assert_eq!(world.query::<&DirectionalLight>().iter(&world).count(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn world() -> World {
|
||||
let mut world = World::new();
|
||||
world.init_resource::<Assets<Mesh>>();
|
||||
|
||||
@@ -110,7 +110,7 @@ fn start(world: &World, entry: Entry) -> Result<Phase, String> {
|
||||
if let Some(map) = &map { crate::skate_world::validate_runtime(map)?; }
|
||||
let validation_time = validation_started.elapsed();
|
||||
let metadata = CurrentMap::from_package(selected.path, map.as_ref());
|
||||
let retail = map.as_ref().is_some_and(|m| m.materials.iter().any(|m| m.retail_definition.is_some()));
|
||||
let retail = map.as_ref().is_some_and(|m| crate::retail_render::RetailScene::for_map(m));
|
||||
// Both builders only read the decoded package. Reserve render handles
|
||||
// on a second worker while the first constructs fresh simulation state;
|
||||
// neither publishes to the live world until both have succeeded.
|
||||
|
||||
@@ -76,6 +76,17 @@ pub(crate) use shadow::ShadowState;
|
||||
#[derive(Resource)]
|
||||
pub(crate) struct RetailScene(pub bool);
|
||||
|
||||
impl RetailScene {
|
||||
/// Package evidence, never the editable map name. Older Skate 2 exports
|
||||
/// can retain native provenance/sky/collision without material definitions.
|
||||
pub(crate) fn for_map(map: &skate_data::skate_map::SkateMap) -> bool {
|
||||
map.materials.iter().any(|m| m.retail_definition.is_some())
|
||||
|| map.extensions.iter().any(|e| matches!(&e.tag, b"WMET" | b"RWCM" | b"SKYB"))
|
||||
|| map.geometry.collision.iter().any(|c| c.native_edges.is_some())
|
||||
|| map.rails.iter().any(|r| r.native.is_some())
|
||||
}
|
||||
}
|
||||
|
||||
#[path = "retail_sky.rs"]
|
||||
mod sky;
|
||||
pub(crate) use sky::{spawn_sky, RetailSkyMaterial};
|
||||
|
||||
Reference in new issue
Block a user