refactor
This commit is contained in:
549
src/main.rs
549
src/main.rs
@@ -9,37 +9,24 @@ mod physics;
|
|||||||
mod picking;
|
mod picking;
|
||||||
mod postprocess;
|
mod postprocess;
|
||||||
mod render;
|
mod render;
|
||||||
mod snow;
|
|
||||||
mod snow_light;
|
|
||||||
mod state;
|
mod state;
|
||||||
mod systems;
|
mod systems;
|
||||||
mod texture;
|
mod texture;
|
||||||
mod utility;
|
mod utility;
|
||||||
mod world;
|
mod world;
|
||||||
|
|
||||||
use crate::debug::{collider_debug, DebugMode};
|
|
||||||
use crate::editor::{editor_loop, EditorState, FrameStats};
|
|
||||||
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
use glam::Vec3;
|
|
||||||
use render::Renderer;
|
|
||||||
use sdl3::event::Event;
|
|
||||||
use sdl3::keyboard::Keycode;
|
|
||||||
use sdl3::mouse::MouseButton;
|
|
||||||
use utility::input::InputState;
|
|
||||||
use world::World;
|
|
||||||
|
|
||||||
use crate::bundles::camera::CameraBundle;
|
use crate::bundles::camera::CameraBundle;
|
||||||
use crate::bundles::player::PlayerBundle;
|
use crate::bundles::player::PlayerBundle;
|
||||||
use crate::bundles::spotlight::spawn_spotlights;
|
use crate::bundles::spotlight::spawn_spotlights;
|
||||||
use crate::bundles::terrain::{TerrainBundle, TerrainConfig};
|
use crate::bundles::terrain::{TerrainBundle, TerrainConfig};
|
||||||
use crate::bundles::test_char::TestCharBundle;
|
use crate::bundles::test_char::TestCharBundle;
|
||||||
use crate::bundles::Bundle;
|
use crate::bundles::Bundle;
|
||||||
|
use crate::debug::{collider_debug, DebugMode};
|
||||||
|
use crate::editor::{editor_loop, EditorState, FrameStats};
|
||||||
|
use crate::entity::EntityHandle;
|
||||||
use crate::loaders::scene::Space;
|
use crate::loaders::scene::Space;
|
||||||
use crate::physics::PhysicsManager;
|
use crate::physics::PhysicsManager;
|
||||||
use crate::snow::{SnowConfig, SnowLayer};
|
use crate::render::snow::{SnowConfig, SnowLayer};
|
||||||
|
|
||||||
use crate::systems::camera::stop_camera_following;
|
use crate::systems::camera::stop_camera_following;
|
||||||
use crate::systems::{
|
use crate::systems::{
|
||||||
camera_follow_system, camera_input_system, camera_view_matrix, dialog_bubble_render_system,
|
camera_follow_system, camera_input_system, camera_view_matrix, dialog_bubble_render_system,
|
||||||
@@ -49,9 +36,34 @@ use crate::systems::{
|
|||||||
tree_dissolve_update_system, tree_instance_buffer_update_system, tree_occlusion_system,
|
tree_dissolve_update_system, tree_instance_buffer_update_system, tree_occlusion_system,
|
||||||
trigger_system,
|
trigger_system,
|
||||||
};
|
};
|
||||||
|
use crate::utility::input::InputState;
|
||||||
use crate::utility::time::Time;
|
use crate::utility::time::Time;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>>
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use glam::Vec3;
|
||||||
|
use render::Renderer;
|
||||||
|
use sdl3::event::Event;
|
||||||
|
use sdl3::keyboard::Keycode;
|
||||||
|
use sdl3::mouse::MouseButton;
|
||||||
|
use world::World;
|
||||||
|
|
||||||
|
struct Game
|
||||||
|
{
|
||||||
|
sdl_context: sdl3::Sdl,
|
||||||
|
window: sdl3::video::Window,
|
||||||
|
_event_pump: sdl3::EventPump,
|
||||||
|
world: World,
|
||||||
|
editor: EditorState,
|
||||||
|
input_state: InputState,
|
||||||
|
camera_entity: EntityHandle,
|
||||||
|
last_frame: Instant,
|
||||||
|
frame_duration: Duration,
|
||||||
|
physics_accumulator: f32,
|
||||||
|
stats: FrameStats,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init() -> Result<Game, Box<dyn std::error::Error>>
|
||||||
{
|
{
|
||||||
let sdl_context = sdl3::init()?;
|
let sdl_context = sdl3::init()?;
|
||||||
let video_subsystem = sdl_context.video()?;
|
let video_subsystem = sdl_context.video()?;
|
||||||
@@ -72,34 +84,69 @@ fn main() -> Result<(), Box<dyn std::error::Error>>
|
|||||||
});
|
});
|
||||||
editor.init_platform(&window);
|
editor.init_platform(&window);
|
||||||
|
|
||||||
|
let (mut world, camera_entity) = init_world()?;
|
||||||
|
start_camera_following(&mut world, camera_entity);
|
||||||
|
|
||||||
|
let _event_pump = sdl_context.event_pump()?;
|
||||||
|
let input_state = InputState::new();
|
||||||
|
|
||||||
|
sdl_context.mouse().set_relative_mouse_mode(&window, true);
|
||||||
|
|
||||||
|
Time::init();
|
||||||
|
|
||||||
|
Ok(Game {
|
||||||
|
sdl_context,
|
||||||
|
window,
|
||||||
|
_event_pump,
|
||||||
|
world,
|
||||||
|
editor,
|
||||||
|
input_state,
|
||||||
|
camera_entity,
|
||||||
|
last_frame: Instant::now(),
|
||||||
|
frame_duration: Duration::from_millis(1000 / 60),
|
||||||
|
physics_accumulator: 0.0,
|
||||||
|
stats: FrameStats {
|
||||||
|
fps: 0.0,
|
||||||
|
frame_ms: 0.0,
|
||||||
|
physics_budget_ms: 0.0,
|
||||||
|
draw_call_count: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_world() -> Result<(World, EntityHandle), Box<dyn std::error::Error>>
|
||||||
|
{
|
||||||
let space = Space::load_space(&crate::paths::meshes::terrain())?;
|
let space = Space::load_space(&crate::paths::meshes::terrain())?;
|
||||||
let terrain_config = TerrainConfig::default();
|
let terrain_config = TerrainConfig::default();
|
||||||
|
|
||||||
let player_spawn = space.player_spawn;
|
|
||||||
let camera_spawn = space.camera_spawn_position();
|
|
||||||
|
|
||||||
let tree_positions: Vec<Vec3> = space
|
let tree_positions: Vec<Vec3> = space
|
||||||
.mesh_data
|
.mesh_data
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|(_, instances)| instances.iter().map(|inst| inst.position))
|
.flat_map(|(_, instances)| instances.iter().map(|inst| inst.position))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let player_spawn = space.player_spawn;
|
||||||
|
let test_char_spawn = space.test_char_spawn;
|
||||||
|
let camera_spawn = space.camera_spawn_position();
|
||||||
|
let spotlights = space.spotlights;
|
||||||
|
let mesh_data = space.mesh_data;
|
||||||
|
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
|
||||||
let _player_entity = PlayerBundle {
|
PlayerBundle {
|
||||||
position: player_spawn,
|
position: player_spawn,
|
||||||
}
|
}
|
||||||
.spawn(&mut world)
|
.spawn(&mut world)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let _test_char_entity = TestCharBundle {
|
TestCharBundle {
|
||||||
position: space.test_char_spawn,
|
position: test_char_spawn,
|
||||||
}
|
}
|
||||||
.spawn(&mut world)
|
.spawn(&mut world)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let _terrain_entity = TerrainBundle::spawn(&mut world, space.mesh_data, &terrain_config)?;
|
TerrainBundle::spawn(&mut world, mesh_data, &terrain_config)?;
|
||||||
spawn_spotlights(&mut world, space.spotlights);
|
spawn_spotlights(&mut world, spotlights);
|
||||||
|
|
||||||
render::set_terrain_data();
|
render::set_terrain_data();
|
||||||
|
|
||||||
@@ -110,294 +157,320 @@ fn main() -> Result<(), Box<dyn std::error::Error>>
|
|||||||
);
|
);
|
||||||
|
|
||||||
let snow_config = SnowConfig::default();
|
let snow_config = SnowConfig::default();
|
||||||
let mut snow_layer = SnowLayer::load(&snow_config)?;
|
let snow_layer = SnowLayer::load(&snow_config)?;
|
||||||
for pos in &tree_positions
|
for pos in &tree_positions
|
||||||
{
|
{
|
||||||
snow_layer.deform_at_position(*pos, 5.0, 50.0);
|
snow_layer.deform_at_position(*pos, 5.0, 50.0);
|
||||||
}
|
}
|
||||||
println!("Snow layer loaded successfully");
|
println!("Snow layer loaded successfully");
|
||||||
|
|
||||||
render::set_snow_depth(&snow_layer.depth_texture_view);
|
render::set_snow_depth(&snow_layer.depth_texture_view);
|
||||||
|
world.snow_layer = Some(snow_layer);
|
||||||
let mut debug_mode = DebugMode::default();
|
|
||||||
|
|
||||||
let camera_entity = CameraBundle {
|
let camera_entity = CameraBundle {
|
||||||
position: camera_spawn,
|
position: camera_spawn,
|
||||||
}
|
}
|
||||||
.spawn(&mut world)
|
.spawn(&mut world)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
start_camera_following(&mut world, camera_entity);
|
|
||||||
|
|
||||||
let _event_pump = sdl_context.event_pump()?;
|
Ok((world, camera_entity))
|
||||||
let mut input_state = InputState::new();
|
}
|
||||||
|
|
||||||
sdl_context.mouse().set_relative_mouse_mode(&window, true);
|
fn process_events(game: &mut Game) -> bool
|
||||||
|
{
|
||||||
|
game.editor.begin_frame();
|
||||||
|
|
||||||
Time::init();
|
while let Some(raw_event) = dear_imgui_sdl3::sdl3_poll_event_ll()
|
||||||
let mut last_frame = Instant::now();
|
|
||||||
let target_fps = 60;
|
|
||||||
let frame_duration = Duration::from_millis(1000 / target_fps);
|
|
||||||
|
|
||||||
const FIXED_TIMESTEP: f32 = 1.0 / 60.0;
|
|
||||||
let mut physics_accumulator = 0.0;
|
|
||||||
|
|
||||||
let mut stats = FrameStats {
|
|
||||||
fps: 0.0,
|
|
||||||
frame_ms: 0.0,
|
|
||||||
physics_budget_ms: 0.0,
|
|
||||||
draw_call_count: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
'running: loop
|
|
||||||
{
|
{
|
||||||
let frame_start = Instant::now();
|
game.editor.process_event(&raw_event);
|
||||||
let time = Time::get_time_elapsed();
|
let event = Event::from_ll(raw_event);
|
||||||
let delta = (frame_start - last_frame).as_secs_f32();
|
|
||||||
last_frame = frame_start;
|
|
||||||
|
|
||||||
editor.begin_frame();
|
match &event
|
||||||
|
|
||||||
while let Some(raw_event) = dear_imgui_sdl3::sdl3_poll_event_ll()
|
|
||||||
{
|
{
|
||||||
editor.process_event(&raw_event);
|
Event::Quit { .. } =>
|
||||||
let event = Event::from_ll(raw_event);
|
|
||||||
|
|
||||||
match &event
|
|
||||||
{
|
{
|
||||||
Event::Quit { .. } =>
|
return true;
|
||||||
{
|
|
||||||
input_state.quit_requested = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::KeyDown {
|
|
||||||
keycode: Some(Keycode::Tab),
|
|
||||||
repeat: false,
|
|
||||||
..
|
|
||||||
} =>
|
|
||||||
{
|
|
||||||
editor.active = !editor.active;
|
|
||||||
if editor.active
|
|
||||||
{
|
|
||||||
stop_camera_following(&mut world, camera_entity);
|
|
||||||
sdl_context.mouse().set_relative_mouse_mode(&window, false);
|
|
||||||
editor.right_mouse_held = false;
|
|
||||||
input_state.mouse_captured = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
start_camera_following(&mut world, camera_entity);
|
|
||||||
input_state.mouse_captured = true;
|
|
||||||
sdl_context.mouse().set_relative_mouse_mode(&window, true);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::MouseButtonDown {
|
|
||||||
mouse_btn: MouseButton::Right,
|
|
||||||
..
|
|
||||||
} if editor.active =>
|
|
||||||
{
|
|
||||||
editor.right_mouse_held = true;
|
|
||||||
input_state.mouse_captured = true;
|
|
||||||
stop_camera_following(&mut world, camera_entity);
|
|
||||||
sdl_context.mouse().set_relative_mouse_mode(&window, true);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::MouseButtonUp {
|
|
||||||
mouse_btn: MouseButton::Right,
|
|
||||||
..
|
|
||||||
} if editor.active =>
|
|
||||||
{
|
|
||||||
editor.right_mouse_held = false;
|
|
||||||
input_state.mouse_captured = false;
|
|
||||||
sdl_context.mouse().set_relative_mouse_mode(&window, false);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Event::MouseButtonDown {
|
|
||||||
mouse_btn: MouseButton::Left,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
..
|
|
||||||
} if editor.active && !editor.wants_mouse() =>
|
|
||||||
{
|
|
||||||
if let Some(view) = crate::systems::camera_view_matrix(&world)
|
|
||||||
{
|
|
||||||
if let Some((_, cam)) = world.active_camera()
|
|
||||||
{
|
|
||||||
let projection = cam.projection_matrix();
|
|
||||||
let (win_w, win_h) = window.size();
|
|
||||||
let ray = crate::picking::Ray::from_screen_position(
|
|
||||||
*x,
|
|
||||||
*y,
|
|
||||||
win_w,
|
|
||||||
win_h,
|
|
||||||
&view,
|
|
||||||
&projection,
|
|
||||||
);
|
|
||||||
editor.selected_entity = crate::picking::pick_entity(&ray, &world);
|
|
||||||
render::set_selected_entity(editor.selected_entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ =>
|
|
||||||
{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if editor.active && (editor.wants_keyboard() || editor.wants_mouse())
|
Event::KeyDown {
|
||||||
|
keycode: Some(Keycode::Tab),
|
||||||
|
repeat: false,
|
||||||
|
..
|
||||||
|
} =>
|
||||||
{
|
{
|
||||||
|
toggle_editor(game);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let capture_changed = input_state.handle_event(&event);
|
Event::MouseButtonDown {
|
||||||
if capture_changed && !editor.active
|
mouse_btn: MouseButton::Right,
|
||||||
|
..
|
||||||
|
} if game.editor.active =>
|
||||||
{
|
{
|
||||||
sdl_context
|
game.editor.right_mouse_held = true;
|
||||||
|
game.input_state.mouse_captured = true;
|
||||||
|
stop_camera_following(&mut game.world, game.camera_entity);
|
||||||
|
game.sdl_context
|
||||||
.mouse()
|
.mouse()
|
||||||
.set_relative_mouse_mode(&window, input_state.mouse_captured);
|
.set_relative_mouse_mode(&game.window, true);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Event::MouseButtonUp {
|
||||||
|
mouse_btn: MouseButton::Right,
|
||||||
|
..
|
||||||
|
} if game.editor.active =>
|
||||||
|
{
|
||||||
|
game.editor.right_mouse_held = false;
|
||||||
|
game.input_state.mouse_captured = false;
|
||||||
|
game.sdl_context
|
||||||
|
.mouse()
|
||||||
|
.set_relative_mouse_mode(&game.window, false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Event::MouseButtonDown {
|
||||||
|
mouse_btn: MouseButton::Left,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
..
|
||||||
|
} if game.editor.active && !game.editor.wants_mouse() =>
|
||||||
|
{
|
||||||
|
handle_editor_pick(game, *x, *y);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ =>
|
||||||
|
{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if input_state.quit_requested
|
if game.editor.active && (game.editor.wants_keyboard() || game.editor.wants_mouse())
|
||||||
{
|
{
|
||||||
break 'running;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if input_state.debug_cycle_just_pressed
|
let capture_changed = game.input_state.handle_event(&event);
|
||||||
|
if capture_changed && !game.editor.active
|
||||||
{
|
{
|
||||||
debug_mode = debug_mode.cycle();
|
game.sdl_context
|
||||||
println!("Debug mode: {:?}", debug_mode);
|
.mouse()
|
||||||
|
.set_relative_mouse_mode(&game.window, game.input_state.mouse_captured);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_editor(game: &mut Game)
|
||||||
|
{
|
||||||
|
game.editor.active = !game.editor.active;
|
||||||
|
if game.editor.active
|
||||||
|
{
|
||||||
|
stop_camera_following(&mut game.world, game.camera_entity);
|
||||||
|
game.sdl_context
|
||||||
|
.mouse()
|
||||||
|
.set_relative_mouse_mode(&game.window, false);
|
||||||
|
game.editor.right_mouse_held = false;
|
||||||
|
game.input_state.mouse_captured = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
start_camera_following(&mut game.world, game.camera_entity);
|
||||||
|
game.input_state.mouse_captured = true;
|
||||||
|
game.sdl_context
|
||||||
|
.mouse()
|
||||||
|
.set_relative_mouse_mode(&game.window, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_editor_pick(game: &mut Game, x: f32, y: f32)
|
||||||
|
{
|
||||||
|
let view = match camera_view_matrix(&game.world)
|
||||||
|
{
|
||||||
|
Some(v) => v,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let (_, cam) = match game.world.active_camera()
|
||||||
|
{
|
||||||
|
Some(c) => c,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let projection = cam.projection_matrix();
|
||||||
|
let (win_w, win_h) = game.window.size();
|
||||||
|
let ray = picking::Ray::from_screen_position(x, y, win_w, win_h, &view, &projection);
|
||||||
|
game.editor.selected_entity = picking::pick_entity(&ray, &game.world);
|
||||||
|
render::set_selected_entity(game.editor.selected_entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn submit_frame(game: &mut Game, draw_calls: &[render::DrawCall], time: f32, delta: f32)
|
||||||
|
{
|
||||||
|
let (camera_entity, camera_component) = match game.world.active_camera()
|
||||||
|
{
|
||||||
|
Some(c) => c,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let camera_transform = match game.world.transforms.get(camera_entity)
|
||||||
|
{
|
||||||
|
Some(t) => t,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let view = match camera_view_matrix(&game.world)
|
||||||
|
{
|
||||||
|
Some(v) => v,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let projection = camera_component.projection_matrix();
|
||||||
|
let view_proj = projection * view;
|
||||||
|
let player_pos = game.world.player_position();
|
||||||
|
|
||||||
|
let billboard_calls =
|
||||||
|
dialog_bubble_render_system(&game.world, camera_transform.position, view_proj);
|
||||||
|
|
||||||
|
let frame = render::render(
|
||||||
|
&view,
|
||||||
|
&projection,
|
||||||
|
camera_transform.position,
|
||||||
|
player_pos,
|
||||||
|
draw_calls,
|
||||||
|
&billboard_calls,
|
||||||
|
time,
|
||||||
|
delta,
|
||||||
|
game.world.debug_mode,
|
||||||
|
);
|
||||||
|
|
||||||
|
if game.editor.active || game.editor.show_player_state
|
||||||
|
{
|
||||||
|
let screen_view = frame
|
||||||
|
.texture
|
||||||
|
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
let mut encoder = render::with_device(|d| {
|
||||||
|
d.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
|
label: Some("ImGui Encoder"),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
game.editor.render(&mut encoder, &screen_view);
|
||||||
|
render::with_queue(|q| q.submit(std::iter::once(encoder.finish())));
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
const FIXED_TIMESTEP: f32 = 1.0 / 60.0;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>>
|
||||||
|
{
|
||||||
|
let mut game = init()?;
|
||||||
|
|
||||||
|
loop
|
||||||
|
{
|
||||||
|
let frame_start = Instant::now();
|
||||||
|
let time = Time::get_time_elapsed();
|
||||||
|
let delta = (frame_start - game.last_frame).as_secs_f32();
|
||||||
|
game.last_frame = frame_start;
|
||||||
|
|
||||||
|
// --- events ---
|
||||||
|
if process_events(&mut game)
|
||||||
|
{
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if input_state.f2_just_pressed
|
if game.input_state.debug_cycle_just_pressed
|
||||||
{
|
{
|
||||||
editor.show_player_state = !editor.show_player_state;
|
game.world.debug_mode = game.world.debug_mode.cycle();
|
||||||
|
println!("Debug mode: {:?}", game.world.debug_mode);
|
||||||
|
}
|
||||||
|
if game.input_state.f2_just_pressed
|
||||||
|
{
|
||||||
|
game.editor.show_player_state = !game.editor.show_player_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
camera_input_system(&mut world, &input_state);
|
// --- camera + input ---
|
||||||
|
camera_input_system(&mut game.world, &game.input_state);
|
||||||
|
|
||||||
if editor.active
|
if game.editor.active
|
||||||
{
|
{
|
||||||
editor_loop(&mut editor, &mut world, &input_state, &stats, delta);
|
editor_loop(
|
||||||
|
&mut game.editor,
|
||||||
|
&mut game.world,
|
||||||
|
&game.input_state,
|
||||||
|
&game.stats,
|
||||||
|
delta,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
let dialog_active = !world.bubble_tags.all().is_empty();
|
let dialog_active = !game.world.bubble_tags.all().is_empty();
|
||||||
if dialog_active
|
if dialog_active
|
||||||
{
|
{
|
||||||
dialog_camera_system(&mut world, delta);
|
dialog_camera_system(&mut game.world, delta);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
camera_follow_system(&mut world);
|
camera_follow_system(&mut game.world);
|
||||||
}
|
}
|
||||||
player_input_system(&mut world, &input_state);
|
player_input_system(&mut game.world, &game.input_state);
|
||||||
if editor.show_player_state
|
if game.editor.show_player_state
|
||||||
{
|
{
|
||||||
editor.build_hud(&world);
|
game.editor.build_hud(&game.world);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- fixed-step physics ---
|
||||||
let physics_start = Instant::now();
|
let physics_start = Instant::now();
|
||||||
|
game.physics_accumulator += delta;
|
||||||
|
|
||||||
physics_accumulator += delta;
|
while game.physics_accumulator >= FIXED_TIMESTEP
|
||||||
|
|
||||||
while physics_accumulator >= FIXED_TIMESTEP
|
|
||||||
{
|
{
|
||||||
state_machine_physics_system(&mut world, FIXED_TIMESTEP);
|
state_machine_physics_system(&mut game.world, FIXED_TIMESTEP);
|
||||||
|
|
||||||
PhysicsManager::physics_step();
|
PhysicsManager::physics_step();
|
||||||
|
physics_sync_system(&mut game.world);
|
||||||
physics_sync_system(&mut world);
|
trigger_system(&mut game.world);
|
||||||
trigger_system(&mut world);
|
dialog_system(&mut game.world, FIXED_TIMESTEP);
|
||||||
dialog_system(&mut world, FIXED_TIMESTEP);
|
dialog_projectile_system(&mut game.world, &game.input_state);
|
||||||
dialog_projectile_system(&mut world, &input_state);
|
game.physics_accumulator -= FIXED_TIMESTEP;
|
||||||
|
|
||||||
physics_accumulator -= FIXED_TIMESTEP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.physics_budget_ms = physics_start.elapsed().as_secs_f32() * 1000.0;
|
game.stats.physics_budget_ms = physics_start.elapsed().as_secs_f32() * 1000.0;
|
||||||
|
|
||||||
state_machine_system(&mut world, delta);
|
// --- per-frame systems ---
|
||||||
|
state_machine_system(&mut game.world, delta);
|
||||||
|
rotate_system(&mut game.world, delta);
|
||||||
|
|
||||||
rotate_system(&mut world, delta);
|
tree_occlusion_system(&mut game.world);
|
||||||
|
tree_dissolve_update_system(&mut game.world, delta);
|
||||||
|
tree_instance_buffer_update_system(&mut game.world);
|
||||||
|
|
||||||
tree_occlusion_system(&mut world);
|
let spotlights = spotlight_sync_system(&game.world);
|
||||||
tree_dissolve_update_system(&mut world, delta);
|
|
||||||
tree_instance_buffer_update_system(&mut world);
|
|
||||||
|
|
||||||
let spotlights = spotlight_sync_system(&world);
|
|
||||||
render::update_spotlights(spotlights);
|
render::update_spotlights(spotlights);
|
||||||
|
|
||||||
snow_system(&world, &mut snow_layer, editor.active);
|
snow_system(&mut game.world, game.editor.active);
|
||||||
|
|
||||||
let mut draw_calls = render_system(&world);
|
// --- draw call collection ---
|
||||||
draw_calls.extend(snow_layer.get_draw_calls());
|
let mut draw_calls = render_system(&game.world);
|
||||||
|
if let Some(ref snow_layer) = game.world.snow_layer
|
||||||
|
{
|
||||||
|
draw_calls.extend(snow_layer.get_draw_calls());
|
||||||
|
}
|
||||||
|
|
||||||
if debug_mode == DebugMode::Colliders
|
if game.world.debug_mode == DebugMode::Colliders
|
||||||
{
|
{
|
||||||
draw_calls.extend(collider_debug::render_collider_debug());
|
draw_calls.extend(collider_debug::render_collider_debug());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((camera_entity, camera_component)) = world.active_camera()
|
game.stats.draw_call_count = draw_calls.len();
|
||||||
{
|
game.stats.fps = 1.0 / delta;
|
||||||
if let Some(camera_transform) = world.transforms.get(camera_entity)
|
game.stats.frame_ms = delta * 1000.0;
|
||||||
{
|
|
||||||
let player_pos = world.player_position();
|
|
||||||
|
|
||||||
if let Some(view) = camera_view_matrix(&world)
|
// --- render ---
|
||||||
{
|
submit_frame(&mut game, &draw_calls, time, delta);
|
||||||
let projection = camera_component.projection_matrix();
|
|
||||||
let view_proj = projection * view;
|
|
||||||
|
|
||||||
let billboard_calls =
|
// --- end frame ---
|
||||||
dialog_bubble_render_system(&world, camera_transform.position, view_proj);
|
game.input_state.clear_just_pressed();
|
||||||
|
|
||||||
stats.draw_call_count = draw_calls.len();
|
|
||||||
stats.fps = 1.0 / delta;
|
|
||||||
stats.frame_ms = delta * 1000.0;
|
|
||||||
|
|
||||||
let frame = render::render(
|
|
||||||
&view,
|
|
||||||
&projection,
|
|
||||||
camera_transform.position,
|
|
||||||
player_pos,
|
|
||||||
&draw_calls,
|
|
||||||
&billboard_calls,
|
|
||||||
time,
|
|
||||||
delta,
|
|
||||||
debug_mode,
|
|
||||||
);
|
|
||||||
|
|
||||||
if editor.active || editor.show_player_state
|
|
||||||
{
|
|
||||||
let screen_view = frame
|
|
||||||
.texture
|
|
||||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
let mut encoder = render::with_device(|d| {
|
|
||||||
d.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
||||||
label: Some("ImGui Encoder"),
|
|
||||||
})
|
|
||||||
});
|
|
||||||
editor.render(&mut encoder, &screen_view);
|
|
||||||
render::with_queue(|q| q.submit(std::iter::once(encoder.finish())));
|
|
||||||
}
|
|
||||||
|
|
||||||
frame.present();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input_state.clear_just_pressed();
|
|
||||||
|
|
||||||
let frame_time = frame_start.elapsed();
|
let frame_time = frame_start.elapsed();
|
||||||
if frame_time < frame_duration
|
if frame_time < game.frame_duration
|
||||||
{
|
{
|
||||||
std::thread::sleep(frame_duration - frame_time);
|
std::thread::sleep(game.frame_duration - frame_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
127
src/render/global.rs
Normal file
127
src/render/global.rs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/// Global renderer access via thread-local storage.
|
||||||
|
///
|
||||||
|
/// This module isolates the global singleton pattern used to give systems
|
||||||
|
/// and loaders access to the wgpu `Device` and `Queue` without threading
|
||||||
|
/// them through every call site. The long-term goal is to replace these
|
||||||
|
/// with explicit `RenderContext` parameters on systems that need GPU access.
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use crate::debug::DebugMode;
|
||||||
|
use crate::entity::EntityHandle;
|
||||||
|
|
||||||
|
use super::{BillboardDrawCall, DrawCall, Renderer, Spotlight};
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static GLOBAL_RENDERER: RefCell<Option<Renderer>> = RefCell::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_ref<F, R>(f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&Renderer) -> R,
|
||||||
|
{
|
||||||
|
GLOBAL_RENDERER.with(|r| {
|
||||||
|
let renderer = r.borrow();
|
||||||
|
let renderer = renderer.as_ref().expect("Renderer not initialized");
|
||||||
|
f(renderer)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_mut<F, R>(f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Renderer) -> R,
|
||||||
|
{
|
||||||
|
GLOBAL_RENDERER.with(|r| {
|
||||||
|
let mut renderer = r.borrow_mut();
|
||||||
|
let renderer = renderer.as_mut().expect("Renderer not initialized");
|
||||||
|
f(renderer)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(renderer: Renderer)
|
||||||
|
{
|
||||||
|
GLOBAL_RENDERER.with(|r| *r.borrow_mut() = Some(renderer));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_device<F, R>(f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&wgpu::Device) -> R,
|
||||||
|
{
|
||||||
|
with_ref(|r| f(&r.device))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_queue<F, R>(f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&wgpu::Queue) -> R,
|
||||||
|
{
|
||||||
|
with_ref(|r| f(&r.queue))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_surface_format<F, R>(f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(wgpu::TextureFormat) -> R,
|
||||||
|
{
|
||||||
|
with_ref(|r| f(r.config.format))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn aspect_ratio() -> f32
|
||||||
|
{
|
||||||
|
with_ref(|r| r.aspect_ratio())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_terrain_data()
|
||||||
|
{
|
||||||
|
with_mut(|r| r.set_terrain_data());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_snow_light_accumulation(terrain_min: glam::Vec2, terrain_max: glam::Vec2)
|
||||||
|
{
|
||||||
|
with_mut(|r| r.init_snow_light_accumulation(terrain_min, terrain_max));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_snow_depth(snow_depth_view: &wgpu::TextureView)
|
||||||
|
{
|
||||||
|
with_mut(|r| r.set_snow_depth(snow_depth_view));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn set_shadow_bias(bias: f32)
|
||||||
|
{
|
||||||
|
with_mut(|r| r.shadow_bias = bias);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_spotlights(spotlights: Vec<Spotlight>)
|
||||||
|
{
|
||||||
|
with_mut(|r| r.spotlights = spotlights);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_selected_entity(entity: Option<EntityHandle>)
|
||||||
|
{
|
||||||
|
with_mut(|r| r.selected_entity = entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(
|
||||||
|
view: &glam::Mat4,
|
||||||
|
projection: &glam::Mat4,
|
||||||
|
camera_position: glam::Vec3,
|
||||||
|
player_position: glam::Vec3,
|
||||||
|
draw_calls: &[DrawCall],
|
||||||
|
billboard_calls: &[BillboardDrawCall],
|
||||||
|
time: f32,
|
||||||
|
delta_time: f32,
|
||||||
|
debug_mode: DebugMode,
|
||||||
|
) -> wgpu::SurfaceTexture
|
||||||
|
{
|
||||||
|
with_mut(|r| {
|
||||||
|
r.render(
|
||||||
|
view,
|
||||||
|
projection,
|
||||||
|
camera_position,
|
||||||
|
player_position,
|
||||||
|
draw_calls,
|
||||||
|
billboard_calls,
|
||||||
|
time,
|
||||||
|
delta_time,
|
||||||
|
debug_mode,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,11 +1,19 @@
|
|||||||
pub mod billboard;
|
|
||||||
mod bind_group;
|
mod bind_group;
|
||||||
mod debug_overlay;
|
mod debug_overlay;
|
||||||
|
mod global;
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
mod shadow;
|
mod shadow;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
|
pub mod billboard;
|
||||||
|
pub mod snow;
|
||||||
|
pub mod snow_light;
|
||||||
|
|
||||||
pub use billboard::{BillboardDrawCall, BillboardPipeline};
|
pub use billboard::{BillboardDrawCall, BillboardPipeline};
|
||||||
|
pub use global::{
|
||||||
|
aspect_ratio, init, init_snow_light_accumulation, render, set_selected_entity, set_snow_depth,
|
||||||
|
set_terrain_data, update_spotlights, with_device, with_queue, with_surface_format,
|
||||||
|
};
|
||||||
pub use types::{DrawCall, Pipeline, Spotlight, SpotlightRaw, Uniforms, MAX_SPOTLIGHTS};
|
pub use types::{DrawCall, Pipeline, Spotlight, SpotlightRaw, Uniforms, MAX_SPOTLIGHTS};
|
||||||
|
|
||||||
use crate::entity::EntityHandle;
|
use crate::entity::EntityHandle;
|
||||||
@@ -18,7 +26,6 @@ use pipeline::{
|
|||||||
create_debug_lines_pipeline, create_main_pipeline, create_snow_clipmap_pipeline,
|
create_debug_lines_pipeline, create_main_pipeline, create_snow_clipmap_pipeline,
|
||||||
create_wireframe_pipeline,
|
create_wireframe_pipeline,
|
||||||
};
|
};
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
|
|
||||||
const MAX_DRAW_CALLS: usize = 64;
|
const MAX_DRAW_CALLS: usize = 64;
|
||||||
@@ -71,7 +78,7 @@ pub struct Renderer
|
|||||||
dummy_snow_light_view: wgpu::TextureView,
|
dummy_snow_light_view: wgpu::TextureView,
|
||||||
dummy_snow_light_sampler: wgpu::Sampler,
|
dummy_snow_light_sampler: wgpu::Sampler,
|
||||||
|
|
||||||
snow_light_accumulation: Option<crate::snow_light::SnowLightAccumulation>,
|
snow_light_accumulation: Option<snow_light::SnowLightAccumulation>,
|
||||||
snow_light_bound: bool,
|
snow_light_bound: bool,
|
||||||
|
|
||||||
pub selected_entity: Option<EntityHandle>,
|
pub selected_entity: Option<EntityHandle>,
|
||||||
@@ -1037,12 +1044,8 @@ impl Renderer
|
|||||||
|
|
||||||
pub fn init_snow_light_accumulation(&mut self, terrain_min: glam::Vec2, terrain_max: glam::Vec2)
|
pub fn init_snow_light_accumulation(&mut self, terrain_min: glam::Vec2, terrain_max: glam::Vec2)
|
||||||
{
|
{
|
||||||
let snow_light_accumulation = crate::snow_light::SnowLightAccumulation::new(
|
let snow_light_accumulation =
|
||||||
&self.device,
|
snow_light::SnowLightAccumulation::new(&self.device, terrain_min, terrain_max, 512);
|
||||||
terrain_min,
|
|
||||||
terrain_max,
|
|
||||||
512,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.snow_light_accumulation = Some(snow_light_accumulation);
|
self.snow_light_accumulation = Some(snow_light_accumulation);
|
||||||
}
|
}
|
||||||
@@ -1093,137 +1096,3 @@ impl Renderer
|
|||||||
self.config.width as f32 / self.config.height as f32
|
self.config.width as f32 / self.config.height as f32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local! {
|
|
||||||
static GLOBAL_RENDERER: RefCell<Option<Renderer>> = RefCell::new(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(renderer: Renderer)
|
|
||||||
{
|
|
||||||
GLOBAL_RENDERER.with(|r| *r.borrow_mut() = Some(renderer));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_device<F, R>(f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&wgpu::Device) -> R,
|
|
||||||
{
|
|
||||||
GLOBAL_RENDERER.with(|r| {
|
|
||||||
let renderer = r.borrow();
|
|
||||||
let renderer = renderer.as_ref().expect("Renderer not set");
|
|
||||||
f(&renderer.device)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_queue<F, R>(f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&wgpu::Queue) -> R,
|
|
||||||
{
|
|
||||||
GLOBAL_RENDERER.with(|r| {
|
|
||||||
let renderer = r.borrow();
|
|
||||||
let renderer = renderer.as_ref().expect("Renderer not set");
|
|
||||||
f(&renderer.queue)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_terrain_data()
|
|
||||||
{
|
|
||||||
GLOBAL_RENDERER.with(|r| {
|
|
||||||
let mut renderer = r.borrow_mut();
|
|
||||||
let renderer = renderer.as_mut().expect("Renderer not set");
|
|
||||||
renderer.set_terrain_data();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_snow_light_accumulation(terrain_min: glam::Vec2, terrain_max: glam::Vec2)
|
|
||||||
{
|
|
||||||
GLOBAL_RENDERER.with(|r| {
|
|
||||||
let mut renderer = r.borrow_mut();
|
|
||||||
let renderer = renderer.as_mut().expect("Renderer not set");
|
|
||||||
renderer.init_snow_light_accumulation(terrain_min, terrain_max);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_snow_depth(snow_depth_view: &wgpu::TextureView)
|
|
||||||
{
|
|
||||||
GLOBAL_RENDERER.with(|r| {
|
|
||||||
let mut renderer = r.borrow_mut();
|
|
||||||
let renderer = renderer.as_mut().expect("Renderer not set");
|
|
||||||
renderer.set_snow_depth(snow_depth_view);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn aspect_ratio() -> f32
|
|
||||||
{
|
|
||||||
GLOBAL_RENDERER.with(|r| {
|
|
||||||
let renderer = r.borrow();
|
|
||||||
let renderer = renderer.as_ref().expect("Renderer not set");
|
|
||||||
renderer.aspect_ratio()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render(
|
|
||||||
view: &glam::Mat4,
|
|
||||||
projection: &glam::Mat4,
|
|
||||||
camera_position: glam::Vec3,
|
|
||||||
player_position: glam::Vec3,
|
|
||||||
draw_calls: &[DrawCall],
|
|
||||||
billboard_calls: &[BillboardDrawCall],
|
|
||||||
time: f32,
|
|
||||||
delta_time: f32,
|
|
||||||
debug_mode: DebugMode,
|
|
||||||
) -> wgpu::SurfaceTexture
|
|
||||||
{
|
|
||||||
GLOBAL_RENDERER.with(|r| {
|
|
||||||
let mut renderer = r.borrow_mut();
|
|
||||||
let renderer = renderer.as_mut().expect("Renderer not set");
|
|
||||||
renderer.render(
|
|
||||||
view,
|
|
||||||
projection,
|
|
||||||
camera_position,
|
|
||||||
player_position,
|
|
||||||
draw_calls,
|
|
||||||
billboard_calls,
|
|
||||||
time,
|
|
||||||
delta_time,
|
|
||||||
debug_mode,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_surface_format<F, R>(f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(wgpu::TextureFormat) -> R,
|
|
||||||
{
|
|
||||||
GLOBAL_RENDERER.with(|r| {
|
|
||||||
let renderer = r.borrow();
|
|
||||||
let renderer = renderer.as_ref().expect("Renderer not set");
|
|
||||||
f(renderer.config.format)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_shadow_bias(bias: f32)
|
|
||||||
{
|
|
||||||
GLOBAL_RENDERER.with(|r| {
|
|
||||||
let mut renderer = r.borrow_mut();
|
|
||||||
let renderer = renderer.as_mut().expect("Renderer not set");
|
|
||||||
renderer.shadow_bias = bias;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_spotlights(spotlights: Vec<Spotlight>)
|
|
||||||
{
|
|
||||||
GLOBAL_RENDERER.with(|r| {
|
|
||||||
let mut renderer = r.borrow_mut();
|
|
||||||
let renderer = renderer.as_mut().expect("Renderer not set");
|
|
||||||
renderer.spotlights = spotlights;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_selected_entity(entity: Option<EntityHandle>)
|
|
||||||
{
|
|
||||||
GLOBAL_RENDERER.with(|r| {
|
|
||||||
let mut renderer = r.borrow_mut();
|
|
||||||
let renderer = renderer.as_mut().expect("Renderer not set");
|
|
||||||
renderer.selected_entity = entity;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ use exr::prelude::{ReadChannels, ReadLayers};
|
|||||||
use glam::{Vec2, Vec3};
|
use glam::{Vec2, Vec3};
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
|
use super::{with_device, with_queue, DrawCall, Pipeline};
|
||||||
use crate::{
|
use crate::{
|
||||||
loaders::mesh::{InstanceRaw, Mesh, Vertex},
|
loaders::mesh::{InstanceRaw, Mesh, Vertex},
|
||||||
paths,
|
paths,
|
||||||
render::{self, DrawCall, Pipeline},
|
|
||||||
texture::HeightmapTexture,
|
texture::HeightmapTexture,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ pub struct SnowLayer
|
|||||||
|
|
||||||
fn create_instance_buffer() -> wgpu::Buffer
|
fn create_instance_buffer() -> wgpu::Buffer
|
||||||
{
|
{
|
||||||
render::with_device(|device| {
|
with_device(|device| {
|
||||||
device.create_buffer(&wgpu::BufferDescriptor {
|
device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
label: Some("Snow Clipmap Instance Buffer"),
|
label: Some("Snow Clipmap Instance Buffer"),
|
||||||
size: std::mem::size_of::<InstanceRaw>() as u64,
|
size: std::mem::size_of::<InstanceRaw>() as u64,
|
||||||
@@ -107,13 +107,11 @@ impl SnowLayer
|
|||||||
let (deform_pipeline, deform_bind_group, deform_params_buffer) =
|
let (deform_pipeline, deform_bind_group, deform_params_buffer) =
|
||||||
Self::create_deform_pipeline(&depth_texture_view);
|
Self::create_deform_pipeline(&depth_texture_view);
|
||||||
|
|
||||||
let heightmap_texture = render::with_device(|device| {
|
let heightmap_texture = with_device(|device| {
|
||||||
render::with_queue(|queue| {
|
with_queue(|queue| HeightmapTexture::load(device, queue, &config.heightmap_path))
|
||||||
HeightmapTexture::load(device, queue, &config.heightmap_path)
|
|
||||||
})
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let depth_sampler = render::with_device(|device| {
|
let depth_sampler = with_device(|device| {
|
||||||
device.create_sampler(&wgpu::SamplerDescriptor {
|
device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
label: Some("Snow Depth Sampler"),
|
label: Some("Snow Depth Sampler"),
|
||||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||||
@@ -226,7 +224,7 @@ impl SnowLayer
|
|||||||
height: u32,
|
height: u32,
|
||||||
) -> (wgpu::Texture, wgpu::TextureView, wgpu::BindGroup)
|
) -> (wgpu::Texture, wgpu::TextureView, wgpu::BindGroup)
|
||||||
{
|
{
|
||||||
render::with_device(|device| {
|
with_device(|device| {
|
||||||
let size = wgpu::Extent3d {
|
let size = wgpu::Extent3d {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
@@ -248,7 +246,7 @@ impl SnowLayer
|
|||||||
|
|
||||||
let data_bytes: &[u8] = bytemuck::cast_slice(depth_data);
|
let data_bytes: &[u8] = bytemuck::cast_slice(depth_data);
|
||||||
|
|
||||||
render::with_queue(|queue| {
|
with_queue(|queue| {
|
||||||
queue.write_texture(
|
queue.write_texture(
|
||||||
wgpu::TexelCopyTextureInfo {
|
wgpu::TexelCopyTextureInfo {
|
||||||
texture: &texture,
|
texture: &texture,
|
||||||
@@ -300,7 +298,7 @@ impl SnowLayer
|
|||||||
depth_texture_view: &wgpu::TextureView,
|
depth_texture_view: &wgpu::TextureView,
|
||||||
) -> (wgpu::ComputePipeline, wgpu::BindGroup, wgpu::Buffer)
|
) -> (wgpu::ComputePipeline, wgpu::BindGroup, wgpu::Buffer)
|
||||||
{
|
{
|
||||||
render::with_device(|device| {
|
with_device(|device| {
|
||||||
let shader_source = std::fs::read_to_string(&paths::shaders::snow_deform())
|
let shader_source = std::fs::read_to_string(&paths::shaders::snow_deform())
|
||||||
.expect("Failed to load snow deform shader");
|
.expect("Failed to load snow deform shader");
|
||||||
|
|
||||||
@@ -383,7 +381,7 @@ impl SnowLayer
|
|||||||
depth_sampler: &wgpu::Sampler,
|
depth_sampler: &wgpu::Sampler,
|
||||||
) -> wgpu::BindGroup
|
) -> wgpu::BindGroup
|
||||||
{
|
{
|
||||||
render::with_device(|device| {
|
with_device(|device| {
|
||||||
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
label: Some("Snow Displacement Bind Group Layout"),
|
label: Some("Snow Displacement Bind Group Layout"),
|
||||||
entries: &[
|
entries: &[
|
||||||
@@ -490,7 +488,7 @@ impl SnowLayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let vertex_buffer = render::with_device(|device| {
|
let vertex_buffer = with_device(|device| {
|
||||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some(&format!("Snow Clipmap Level {} Vertex Buffer", level)),
|
label: Some(&format!("Snow Clipmap Level {} Vertex Buffer", level)),
|
||||||
contents: bytemuck::cast_slice(&vertices),
|
contents: bytemuck::cast_slice(&vertices),
|
||||||
@@ -498,7 +496,7 @@ impl SnowLayer
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let index_buffer = render::with_device(|device| {
|
let index_buffer = with_device(|device| {
|
||||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some(&format!("Snow Clipmap Level {} Index Buffer", level)),
|
label: Some(&format!("Snow Clipmap Level {} Index Buffer", level)),
|
||||||
contents: bytemuck::cast_slice(&indices),
|
contents: bytemuck::cast_slice(&indices),
|
||||||
@@ -542,7 +540,7 @@ impl SnowLayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render::with_queue(|queue| {
|
with_queue(|queue| {
|
||||||
for (level, clipmap_level) in self.levels.iter().enumerate()
|
for (level, clipmap_level) in self.levels.iter().enumerate()
|
||||||
{
|
{
|
||||||
let cell_size = self.clipmap_config.base_cell_size * (1u32 << level) as f32;
|
let cell_size = self.clipmap_config.base_cell_size * (1u32 << level) as f32;
|
||||||
@@ -589,7 +587,7 @@ impl SnowLayer
|
|||||||
|
|
||||||
pub fn deform_at_position(&self, position: Vec3, radius: f32, depth: f32)
|
pub fn deform_at_position(&self, position: Vec3, radius: f32, depth: f32)
|
||||||
{
|
{
|
||||||
render::with_queue(|queue| {
|
with_queue(|queue| {
|
||||||
let params_data = [
|
let params_data = [
|
||||||
position.x,
|
position.x,
|
||||||
position.z,
|
position.z,
|
||||||
@@ -605,7 +603,7 @@ impl SnowLayer
|
|||||||
queue.write_buffer(&self.deform_params_buffer, 0, params_bytes);
|
queue.write_buffer(&self.deform_params_buffer, 0, params_bytes);
|
||||||
});
|
});
|
||||||
|
|
||||||
render::with_device(|device| {
|
with_device(|device| {
|
||||||
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
label: Some("Snow Deform Encoder"),
|
label: Some("Snow Deform Encoder"),
|
||||||
});
|
});
|
||||||
@@ -625,7 +623,7 @@ impl SnowLayer
|
|||||||
compute_pass.dispatch_workgroups(dispatch_x, dispatch_y, 1);
|
compute_pass.dispatch_workgroups(dispatch_x, dispatch_y, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
render::with_queue(|queue| {
|
with_queue(|queue| {
|
||||||
queue.submit(Some(encoder.finish()));
|
queue.submit(Some(encoder.finish()));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use super::{Spotlight, SpotlightRaw, MAX_SPOTLIGHTS};
|
||||||
use crate::paths;
|
use crate::paths;
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
@@ -13,12 +14,12 @@ struct AccumulationUniforms
|
|||||||
delta_time: f32,
|
delta_time: f32,
|
||||||
spotlight_count: u32,
|
spotlight_count: u32,
|
||||||
_padding: u32,
|
_padding: u32,
|
||||||
light_view_projections: [[[f32; 4]; 4]; crate::render::MAX_SPOTLIGHTS],
|
light_view_projections: [[[f32; 4]; 4]; MAX_SPOTLIGHTS],
|
||||||
shadow_bias: f32,
|
shadow_bias: f32,
|
||||||
terrain_height_scale: f32,
|
terrain_height_scale: f32,
|
||||||
_padding3: f32,
|
_padding3: f32,
|
||||||
_padding4: f32,
|
_padding4: f32,
|
||||||
spotlights: [crate::render::SpotlightRaw; crate::render::MAX_SPOTLIGHTS],
|
spotlights: [SpotlightRaw; MAX_SPOTLIGHTS],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SnowLightAccumulation
|
pub struct SnowLightAccumulation
|
||||||
@@ -417,7 +418,7 @@ impl SnowLightAccumulation
|
|||||||
&mut self,
|
&mut self,
|
||||||
encoder: &mut wgpu::CommandEncoder,
|
encoder: &mut wgpu::CommandEncoder,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
spotlights: &[crate::render::Spotlight],
|
spotlights: &[Spotlight],
|
||||||
delta_time: f32,
|
delta_time: f32,
|
||||||
light_view_projections: &[glam::Mat4],
|
light_view_projections: &[glam::Mat4],
|
||||||
shadow_bias: f32,
|
shadow_bias: f32,
|
||||||
@@ -430,12 +431,8 @@ impl SnowLightAccumulation
|
|||||||
self.needs_clear = false;
|
self.needs_clear = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut spotlight_array =
|
let mut spotlight_array = [SpotlightRaw::default(); MAX_SPOTLIGHTS];
|
||||||
[crate::render::SpotlightRaw::default(); crate::render::MAX_SPOTLIGHTS];
|
for (i, spotlight) in spotlights.iter().take(MAX_SPOTLIGHTS).enumerate()
|
||||||
for (i, spotlight) in spotlights
|
|
||||||
.iter()
|
|
||||||
.take(crate::render::MAX_SPOTLIGHTS)
|
|
||||||
.enumerate()
|
|
||||||
{
|
{
|
||||||
spotlight_array[i] = spotlight.to_raw();
|
spotlight_array[i] = spotlight.to_raw();
|
||||||
}
|
}
|
||||||
@@ -445,13 +442,13 @@ impl SnowLightAccumulation
|
|||||||
terrain_max_xz: self.terrain_max.to_array(),
|
terrain_max_xz: self.terrain_max.to_array(),
|
||||||
decay_rate: self.decay_rate,
|
decay_rate: self.decay_rate,
|
||||||
delta_time,
|
delta_time,
|
||||||
spotlight_count: spotlights.len().min(crate::render::MAX_SPOTLIGHTS) as u32,
|
spotlight_count: spotlights.len().min(MAX_SPOTLIGHTS) as u32,
|
||||||
_padding: 0,
|
_padding: 0,
|
||||||
light_view_projections: {
|
light_view_projections: {
|
||||||
let mut arr = [[[0.0f32; 4]; 4]; crate::render::MAX_SPOTLIGHTS];
|
let mut arr = [[[0.0f32; 4]; 4]; MAX_SPOTLIGHTS];
|
||||||
for (i, mat) in light_view_projections
|
for (i, mat) in light_view_projections
|
||||||
.iter()
|
.iter()
|
||||||
.take(crate::render::MAX_SPOTLIGHTS)
|
.take(MAX_SPOTLIGHTS)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
arr[i] = mat.to_cols_array_2d();
|
arr[i] = mat.to_cols_array_2d();
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
use crate::snow::SnowLayer;
|
|
||||||
use crate::world::World;
|
use crate::world::World;
|
||||||
|
|
||||||
pub fn snow_system(world: &World, snow_layer: &mut SnowLayer, noclip: bool)
|
pub fn snow_system(world: &mut World, noclip: bool)
|
||||||
{
|
{
|
||||||
let camera_pos = world.active_camera_position();
|
let camera_pos = world.active_camera_position();
|
||||||
let player_pos = world.player_position();
|
let player_pos = world.player_position();
|
||||||
if !noclip
|
if let Some(ref mut snow_layer) = world.snow_layer
|
||||||
{
|
{
|
||||||
snow_layer.deform_at_position(player_pos, 1.5, 10.0);
|
if !noclip
|
||||||
|
{
|
||||||
|
snow_layer.deform_at_position(player_pos, 1.5, 10.0);
|
||||||
|
}
|
||||||
|
snow_layer.update(camera_pos);
|
||||||
}
|
}
|
||||||
snow_layer.update(camera_pos);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ use crate::components::{
|
|||||||
CameraComponent, InputComponent, JumpComponent, MeshComponent, MovementComponent,
|
CameraComponent, InputComponent, JumpComponent, MeshComponent, MovementComponent,
|
||||||
PhysicsComponent, RotateComponent,
|
PhysicsComponent, RotateComponent,
|
||||||
};
|
};
|
||||||
|
use crate::debug::DebugMode;
|
||||||
use crate::entity::{EntityHandle, EntityManager};
|
use crate::entity::{EntityHandle, EntityManager};
|
||||||
|
use crate::render::snow::SnowLayer;
|
||||||
use crate::state::StateMachine;
|
use crate::state::StateMachine;
|
||||||
|
|
||||||
pub use crate::utility::transform::Transform;
|
pub use crate::utility::transform::Transform;
|
||||||
@@ -107,6 +109,10 @@ pub struct World
|
|||||||
pub bubble_tags: Storage<()>,
|
pub bubble_tags: Storage<()>,
|
||||||
pub projectile_tags: Storage<()>,
|
pub projectile_tags: Storage<()>,
|
||||||
pub dialog_outcomes: Vec<DialogOutcomeEvent>,
|
pub dialog_outcomes: Vec<DialogOutcomeEvent>,
|
||||||
|
|
||||||
|
// --- singleton state (not per-entity) ---
|
||||||
|
pub snow_layer: Option<SnowLayer>,
|
||||||
|
pub debug_mode: DebugMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World
|
impl World
|
||||||
@@ -145,6 +151,8 @@ impl World
|
|||||||
bubble_tags: Storage::new(),
|
bubble_tags: Storage::new(),
|
||||||
projectile_tags: Storage::new(),
|
projectile_tags: Storage::new(),
|
||||||
dialog_outcomes: Vec::new(),
|
dialog_outcomes: Vec::new(),
|
||||||
|
snow_layer: None,
|
||||||
|
debug_mode: DebugMode::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user