Created
December 11, 2024 22:01
-
-
Save Naxela/a0ce261405df3ecffbeead4681466741 to your computer and use it in GitHub Desktop.
Lightmap loading example code for Bevy 0.14 - Can't promise it works!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::fs; | |
use bevy::render::camera::TemporalJitter; | |
use bevy::{ | |
core_pipeline::*, | |
pbr::*, | |
gltf::*, | |
prelude::*, | |
}; | |
use bevy_panorbit_camera::*; | |
use bloom::BloomSettings; | |
use experimental::taa::{TemporalAntiAliasBundle, TemporalAntiAliasPlugin, TemporalAntiAliasSettings}; | |
use fxaa::Fxaa; | |
use tonemapping::Tonemapping; | |
use serde::Deserialize; | |
use std::collections::HashMap; | |
#[derive(Resource)] | |
struct SsaoState { | |
enabled: bool, | |
} | |
impl Default for SsaoState { | |
fn default() -> Self { | |
SsaoState { enabled: true } // Start with SSAO enabled | |
} | |
} | |
#[derive(Resource)] | |
pub struct SceneReady(pub bool); | |
#[derive(Resource, Debug)] | |
struct LightmapRegistry { | |
set: bool, | |
loaded: bool, | |
map: HashMap<String, String>, | |
} | |
use bevy_mod_mipmap_generator::{MipmapGeneratorPlugin, generate_mipmaps}; | |
//Read project manifest + lightmap manifest (merge into one function) | |
// Main entry call | |
fn main() { | |
App::new() | |
.insert_resource(Msaa::Off) // Keep MSAA off for TAA to work better | |
.insert_resource(SceneReady(false)) | |
// .insert_resource(AmbientLight { | |
// brightness: 100., | |
// ..default() | |
// }) | |
.insert_resource(LightmapRegistry { | |
set: false, | |
loaded: false, | |
map: HashMap::new(), | |
}) | |
.add_plugins(DefaultPlugins | |
.set(ImagePlugin::default_linear()) | |
.set(WindowPlugin { | |
primary_window: Some(Window { | |
..default() | |
}), | |
..default() | |
})) | |
.add_plugins(TemporalAntiAliasPlugin) | |
.add_plugins(MipmapGeneratorPlugin) | |
.add_plugins(PanOrbitCameraPlugin) | |
.add_systems(Startup, initialize_project) | |
.add_systems(Startup, setup) | |
.add_systems(Startup, spawn_scene.after(setup)) | |
.add_systems(Update, generate_mipmaps::<StandardMaterial>) | |
.add_systems(Update, create_lightmap_registry.after(spawn_scene)) | |
.add_systems(Update, load_lightmaps.after(create_lightmap_registry)) | |
.add_systems(Update, start_runtime.after(load_lightmaps)) | |
.add_systems(Update, toggle_ssao) | |
.run(); | |
} | |
/* ASSET LOADING */ | |
#[derive(Resource)] | |
pub struct SceneHandle(pub Handle<Gltf>); // Make the field public | |
#[derive(Deserialize, Debug, Clone)] | |
pub struct ProjectData { | |
pub name: String, | |
pub options: HashMap<String, String>, | |
} | |
#[derive(Deserialize, Debug, Clone)] | |
pub struct LightmapManifestData { | |
pub ext: String, | |
pub lightmaps: HashMap<String, String>, | |
} | |
#[derive(Resource, Debug)] | |
pub struct ProjectDataResource(pub ProjectData); | |
#[derive(Resource, Debug)] | |
pub struct LightmapManifestDataResource(pub LightmapManifestData); | |
fn initialize_project( | |
mut commands: Commands | |
){ | |
println!("Reading project file"); | |
let project_file: String = fs::read_to_string("package.json").expect("Failed to read file"); | |
let project_data: ProjectData = serde_json::from_str(&project_file).expect("Failed to parse JSON"); | |
println!("Reading lightmap manifest file"); | |
let lightmap_manifest_file: String = fs::read_to_string("assets/lightmaps/manifest.json").expect("Failed to read file"); | |
let lightmap_manifest_data: LightmapManifestData = serde_json::from_str(&lightmap_manifest_file).expect("Failed to parse JSON"); | |
println!("{:?}", lightmap_manifest_data); | |
commands.insert_resource(ProjectDataResource(project_data)); | |
commands.insert_resource(LightmapManifestDataResource(lightmap_manifest_data)); | |
println!("Done initializing"); | |
} | |
fn spawn_scene( | |
mut commands: Commands, | |
asset_server: Res<AssetServer>, | |
){ | |
commands.spawn(SceneBundle { | |
scene: asset_server.load("models/model.glb#Scene0"), | |
..default() | |
}); | |
} | |
fn start_runtime( | |
mut scene_ready : ResMut<SceneReady>, | |
registry: ResMut<LightmapRegistry> | |
){ | |
if registry.loaded { | |
if !scene_ready.0 { | |
println!("Everything ready"); | |
*scene_ready = SceneReady(true); | |
} | |
} | |
} | |
// Setup function (spawns the camera and scene) | |
fn setup( | |
mut commands: Commands, | |
asset_server: Res<AssetServer> | |
) { | |
commands.insert_resource(SsaoState::default()); | |
let scene_handle: Handle<Gltf>= asset_server.load("models/model.glb"); | |
commands | |
.spawn(( | |
Camera3dBundle { | |
transform: Transform::from_translation(Vec3::new(-5.0, 1.0, -5.0)), | |
camera: Camera { | |
hdr: true, | |
..default() | |
}, | |
tonemapping: Tonemapping::BlenderFilmic, | |
..default() | |
}, | |
// ScreenSpaceAmbientOcclusionSettings { | |
// quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Ultra, | |
// ..default() | |
// }, | |
// ScreenSpaceAmbientOcclusionBundle { | |
// settings: ScreenSpaceAmbientOcclusionSettings { | |
// quality_level:ScreenSpaceAmbientOcclusionQualityLevel::Ultra | |
// }, | |
// ..default() | |
// }, | |
//TemporalAntiAliasBundle::default(), | |
//TemporalAntiAliasSettings::default(), | |
//BloomSettings::NATURAL, | |
PanOrbitCamera::default(), | |
//Fxaa::default(), | |
EnvironmentMapLight { | |
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), | |
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), | |
intensity: 100.0, | |
}, | |
)) | |
.insert(ScreenSpaceAmbientOcclusionBundle::default()) | |
.insert(TemporalAntiAliasBundle::default()); | |
commands.insert_resource(SceneHandle(scene_handle)); | |
} | |
fn toggle_ssao( | |
mut commands: Commands, | |
mut query: Query<Entity, With<Camera3d>>, | |
mut ssao_state: ResMut<SsaoState>, | |
input: Res<ButtonInput<KeyCode>>, | |
) { | |
if input.just_pressed(KeyCode::KeyF) { | |
for entity in query.iter_mut() { | |
if ssao_state.enabled { | |
// Disable SSAO by removing the SSAO settings component | |
commands.entity(entity).remove::<ScreenSpaceAmbientOcclusionSettings>(); | |
println!("SSAO disabled"); | |
} else { | |
// Enable SSAO by adding the SSAO settings component back | |
commands.entity(entity).insert(ScreenSpaceAmbientOcclusionSettings { | |
..default() | |
}); | |
println!("SSAO enabled"); | |
} | |
} | |
// Toggle the state | |
ssao_state.enabled = !ssao_state.enabled; | |
} | |
} | |
#[derive(Deserialize, Debug)] | |
pub struct GltfExtrasValue { | |
#[serde(rename = "TLM_Lightmap")] | |
pub tlm_lightmap: Option<String>, | |
} | |
fn load_lightmaps( | |
mut commands: Commands, | |
asset_server: Res<AssetServer>, | |
mut materials: ResMut<Assets<StandardMaterial>>, | |
meshes: Query<(Entity, &Name, &Handle<StandardMaterial>), With<Handle<Mesh>>>, | |
mut registry: ResMut<LightmapRegistry> | |
){ | |
let exposure = 1000.0; | |
if(registry.loaded == false){ | |
if meshes.iter().count() > 0 { | |
println!("Applying lightmaps after scene is fully spawned!"); | |
for (entity, name, material) in meshes.iter() { | |
// Look up the mesh name in the lightmap manifest | |
if let Some(lightmap_name) = registry.map.get(name.as_str()) { | |
println!("Found lightmap for {}: {}", name, lightmap_name); | |
materials.get_mut(material).unwrap().lightmap_exposure = exposure; | |
commands.entity(entity).insert(Lightmap { | |
image: asset_server.load("lightmaps/".to_owned() + lightmap_name + ".ktx2"), | |
..default() | |
}); | |
} else { | |
println!("No lightmap found for mesh: {}", name); | |
} | |
} | |
registry.loaded = true; | |
} | |
} | |
} | |
fn create_lightmap_registry( | |
q: Query<(Entity, &Name, Option<&GltfExtras>)>, | |
children_query: Query<&Children>, | |
name_query: Query<&Name>, | |
mut registry: ResMut<LightmapRegistry> | |
){ | |
if registry.set == false { | |
//println!("Setting registry now"); | |
if q.iter().count() > 0 { | |
for (entity, name, extras) in q.iter() { | |
//println!("Checking registry for: {}", name); | |
let mut lightmap_found = false; | |
let mut object_name = ""; // Initialize with a default value | |
let mut lightmap_name = String::new(); // Initialize as an empty String | |
if let Some(extras) = extras { | |
//println!("Found extras for: {}", name); | |
// Deserialize the JSON value to extract TLM-Lightmap | |
match serde_json::from_str::<GltfExtrasValue>(&extras.value) { | |
Ok(parsed) => { | |
//println!("V: {:?}", parsed); | |
if let Some(lightmap) = parsed.tlm_lightmap { | |
object_name = name.as_str(); | |
lightmap_name = lightmap.to_string(); | |
lightmap_found = true; | |
//println!("A: {}, B: {}, C:{}",object_name, name, lightmap_name) | |
} | |
} | |
Err(err) => { | |
println!("Failed to parse extras for {}: {}", name.as_str(), err); | |
} | |
} | |
} | |
if lightmap_found { | |
if let Ok(children) = children_query.get(entity) { | |
for child in children.iter() { | |
// Access child entity names or data if necessary. | |
if let Ok(child_name) = name_query.get(*child) { | |
//println!("X: {}, Y: {}", child_name, lightmap_name); | |
registry.map.insert(child_name.to_string().clone(), lightmap_name.clone()); | |
} | |
} | |
} | |
} | |
} | |
registry.set = true; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment