use crate::{ mesh::{greedy::GreedyMesh, Meshable}, render::{ create_clouds_mesh, create_pp_mesh, create_skybox_mesh, BoneMeshes, CloudsLocals, CloudsPipeline, Consts, FigureModel, FigurePipeline, GlobalModel, Globals, Light, Mesh, Model, PostProcessLocals, PostProcessPipeline, Renderer, Shadow, ShadowLocals, SkyboxLocals, SkyboxPipeline, TerrainPipeline, }, scene::{ camera::{self, Camera, CameraMode}, figure::{load_mesh, FigureColLights, FigureModelCache, FigureModelEntry, FigureState}, LodData, }, window::{Event, PressState}, }; use anim::{ character::{CharacterSkeleton, IdleAnimation, SkeletonAttr}, fixture::FixtureSkeleton, Animation, }; use client::Client; use common::{ comp::{ humanoid, inventory::{slot::EquipSlot, Inventory}, item::ItemKind, }, figure::Segment, terrain::BlockKind, vol::{BaseVol, ReadVol}, }; use tracing::error; use vek::*; use winit::event::MouseButton; struct VoidVol; impl BaseVol for VoidVol { type Error = (); type Vox = (); } impl ReadVol for VoidVol { fn get(&self, _pos: Vec3) -> Result<&'_ Self::Vox, Self::Error> { Ok(&()) } } fn generate_mesh<'a>( greedy: &mut GreedyMesh<'a>, mesh: &mut Mesh, segment: Segment, offset: Vec3, bone_idx: u8, ) -> BoneMeshes { let (opaque, _, /* shadow */ _, bounds) = Meshable::::generate_mesh( segment, (greedy, mesh, offset, Vec3::one(), bone_idx), ); (opaque /* , shadow */, bounds) } struct Skybox { model: Model, locals: Consts, } struct PostProcess { model: Model, locals: Consts, } struct Clouds { model: Model, locals: Consts, } pub struct Scene { data: GlobalModel, camera: Camera, skybox: Skybox, clouds: Clouds, postprocess: PostProcess, lod: LodData, map_bounds: Vec2, col_lights: FigureColLights, backdrop: Option<(FigureModelEntry<1>, FigureState)>, figure_model_cache: FigureModelCache, figure_state: FigureState, //turning_camera: bool, turning_character: bool, char_ori: f32, } pub struct SceneData<'a> { pub time: f64, pub delta_time: f32, pub tick: u64, pub thread_pool: &'a uvth::ThreadPool, pub body: Option, pub gamma: f32, pub exposure: f32, pub ambiance: f32, pub figure_lod_render_distance: f32, pub mouse_smoothing: bool, } impl Scene { pub fn new(renderer: &mut Renderer, backdrop: Option<&str>, client: &Client) -> Self { let start_angle = 90.0f32.to_radians(); let resolution = renderer.get_resolution().map(|e| e as f32); let map_bounds = Vec2::new( client.world_data().min_chunk_alt(), client.world_data().max_chunk_alt(), ); let map_border = [0.0, 0.0, 0.0, 0.0]; let map_image = [0]; let alt_image = [0]; let horizon_image = [0x_00_01_00_01]; let mut camera = Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson); camera.set_focus_pos(Vec3::unit_z() * 1.5); camera.set_distance(3.4); camera.set_orientation(Vec3::new(start_angle, 0.0, 0.0)); let mut col_lights = FigureColLights::new(renderer); Self { data: GlobalModel { globals: renderer.create_consts(&[Globals::default()]).unwrap(), lights: renderer.create_consts(&[Light::default(); 32]).unwrap(), shadows: renderer.create_consts(&[Shadow::default(); 32]).unwrap(), shadow_mats: renderer .create_consts(&[ShadowLocals::default(); 6]) .unwrap(), }, skybox: Skybox { model: renderer.create_model(&create_skybox_mesh()).unwrap(), locals: renderer.create_consts(&[SkyboxLocals::default()]).unwrap(), }, clouds: Clouds { model: renderer.create_model(&create_clouds_mesh()).unwrap(), locals: renderer.create_consts(&[CloudsLocals::default()]).unwrap(), }, postprocess: PostProcess { model: renderer.create_model(&create_pp_mesh()).unwrap(), locals: renderer .create_consts(&[PostProcessLocals::default()]) .unwrap(), }, lod: LodData::new( renderer, Vec2::new(1, 1), &map_image, &alt_image, &horizon_image, 1, map_border.into(), ), map_bounds, figure_model_cache: FigureModelCache::new(), figure_state: FigureState::new(renderer, CharacterSkeleton::default()), backdrop: backdrop.map(|specifier| { let mut state = FigureState::new(renderer, FixtureSkeleton::default()); let mut greedy = FigureModel::make_greedy(); let mut opaque_mesh = Mesh::new(); let (segment, offset) = load_mesh(specifier, Vec3::new(-55.0, -49.5, -2.0)); let (_opaque_mesh, bounds) = generate_mesh(&mut greedy, &mut opaque_mesh, segment, offset, 0); // NOTE: Since MagicaVoxel sizes are limited to 256 × 256 × 256, and there are // at most 3 meshed vertices per unique vertex, we know the // total size is bounded by 2^24 * 3 * 1.5 which is bounded by // 2^27, which fits in a u32. let range = 0..opaque_mesh.vertices().len() as u32; let model = col_lights .create_figure(renderer, greedy.finalize(), (opaque_mesh, bounds), [range]) .unwrap(); let mut buf = [Default::default(); anim::MAX_BONE_COUNT]; state.update( renderer, anim::vek::Vec3::zero(), anim::vek::Quaternion::rotation_from_to_3d( anim::vek::Vec3::unit_y(), anim::vek::Vec3::new(start_angle.sin(), -start_angle.cos(), 0.0), ), 1.0, Rgba::broadcast(1.0), 15.0, // Want to get there immediately. 1.0, Some(&model), 0, true, false, &camera, &mut buf, None, ); (model, state) }), col_lights, camera, //turning_camera: false, turning_character: false, char_ori: -start_angle, } } pub fn globals(&self) -> &Consts { &self.data.globals } pub fn camera_mut(&mut self) -> &mut Camera { &mut self.camera } /// Handle an incoming user input event (e.g.: cursor moved, key pressed, /// window closed). /// /// If the event is handled, return true. pub fn handle_input_event(&mut self, event: Event) -> bool { match event { // When the window is resized, change the camera's aspect ratio Event::Resize(dims) => { self.camera.set_aspect_ratio(dims.x as f32 / dims.y as f32); true }, Event::MouseButton(button, state) => { if state == PressState::Pressed { //self.turning_camera = button == MouseButton::Right; self.turning_character = button == MouseButton::Left; } else { //self.turning_camera = false; self.turning_character = false; } true }, Event::CursorMove(delta) => { /*if self.turning_camera { self.camera.rotate_by(Vec3::new(delta.x * 0.01, 0.0, 0.0)) }*/ if self.turning_character { self.char_ori += delta.x * 0.01; } true }, // All other events are unhandled _ => false, } } pub fn maintain( &mut self, renderer: &mut Renderer, scene_data: SceneData, inventory: Option<&Inventory>, ) { self.camera.update( scene_data.time, /* 1.0 / 60.0 */ scene_data.delta_time, scene_data.mouse_smoothing, ); self.camera.compute_dependents_full(&VoidVol, |_| true); let camera::Dependents { view_mat, proj_mat, cam_pos, .. } = self.camera.dependents(); const VD: f32 = 115.0; // View Distance const TIME: f64 = 8.6 * 60.0 * 60.0; const SHADOW_NEAR: f32 = 1.0; const SHADOW_FAR: f32 = 25.0; if let Err(e) = renderer.update_consts(&mut self.data.globals, &[Globals::new( view_mat, proj_mat, cam_pos, self.camera.get_focus_pos(), VD, self.lod.tgt_detail as f32, self.map_bounds, TIME, scene_data.time, renderer.get_resolution(), Vec2::new(SHADOW_NEAR, SHADOW_FAR), 0, 0, 0, BlockKind::Air, None, scene_data.gamma, scene_data.exposure, scene_data.ambiance, self.camera.get_mode(), 250.0, )]) { error!(?e, "Renderer failed to update"); } self.figure_model_cache .clean(&mut self.col_lights, scene_data.tick); let active_item_kind = inventory .and_then(|inv| inv.equipped(EquipSlot::Mainhand)) .map(|i| i.kind()); let (active_tool_kind, active_tool_hand) = if let Some(ItemKind::Tool(tool)) = active_item_kind { (Some(tool.kind), Some(tool.hands)) } else { (None, None) }; let second_item_kind = inventory .and_then(|inv| inv.equipped(EquipSlot::Offhand)) .map(|i| i.kind()); let (second_tool_kind, second_tool_hand) = if let Some(ItemKind::Tool(tool)) = second_item_kind { (Some(tool.kind), Some(tool.hands)) } else { (None, None) }; let hands = (active_tool_hand, second_tool_hand); if let Some(body) = scene_data.body { let tgt_skeleton = IdleAnimation::update_skeleton( self.figure_state.skeleton_mut(), (active_tool_kind, second_tool_kind, hands, scene_data.time), scene_data.time, &mut 0.0, &SkeletonAttr::from(&body), ); let dt_lerp = (scene_data.delta_time * 15.0).min(1.0); *self.figure_state.skeleton_mut() = anim::vek::Lerp::lerp(&*self.figure_state.skeleton_mut(), &tgt_skeleton, dt_lerp); let model = self .figure_model_cache .get_or_create_model( renderer, &mut self.col_lights, body, inventory, scene_data.tick, CameraMode::default(), None, scene_data.thread_pool, ) .0; let mut buf = [Default::default(); anim::MAX_BONE_COUNT]; self.figure_state.update( renderer, anim::vek::Vec3::zero(), anim::vek::Quaternion::rotation_from_to_3d( anim::vek::Vec3::unit_y(), anim::vek::Vec3::new(self.char_ori.sin(), -self.char_ori.cos(), 0.0), ), 1.0, Rgba::broadcast(1.0), scene_data.delta_time, 1.0, model, 0, true, false, &self.camera, &mut buf, None, ); } } pub fn render( &mut self, renderer: &mut Renderer, tick: u64, body: Option, inventory: Option<&Inventory>, ) { renderer.render_skybox( &self.skybox.model, &self.data, &self.skybox.locals, &self.lod, ); if let Some(body) = body { let model = &self.figure_model_cache.get_model( &self.col_lights, body, inventory, tick, CameraMode::default(), None, ); if let Some(model) = model { renderer.render_figure( &model.models[0], &self.col_lights.texture(model), &self.data, self.figure_state.locals(), self.figure_state.bone_consts(), &self.lod, ); } } if let Some((model, state)) = &self.backdrop { renderer.render_figure( &model.models[0], &self.col_lights.texture(model), &self.data, state.locals(), state.bone_consts(), &self.lod, ); } renderer.render_clouds( &self.clouds.model, &self.data.globals, &self.clouds.locals, &self.lod, ); renderer.render_post_process( &self.postprocess.model, &self.data.globals, &self.postprocess.locals, &self.lod, ); } }