1use crate::{
2 Settings,
3 render::{
4 AltIndices, Consts, FirstPassDrawer, GlobalModel, Globals, GlobalsBindGroup, Light, Model,
5 PointLightMatrix, RainOcclusionLocals, Renderer, Shadow, ShadowLocals, SkyboxVertex,
6 SpriteGlobalsBindGroup, create_skybox_mesh,
7 pipelines::terrain::BoundLocals as BoundTerrainLocals,
8 },
9 scene::{
10 CloudsLocals, CullingMode, Lod, PostProcessLocals,
11 camera::{self, Camera, CameraMode},
12 figure::{FigureAtlas, FigureModelCache, FigureState, FigureUpdateCommonParameters},
13 terrain::{SpriteRenderContext, SpriteRenderState},
14 },
15 window::{Event, PressState},
16};
17use anim::{Animation, character::CharacterSkeleton, ship::ShipSkeleton};
18use client::Client;
19use common::{
20 comp::{
21 humanoid,
22 inventory::{Inventory, slot::EquipSlot},
23 item::ItemKind,
24 ship,
25 },
26 slowjob::SlowJobPool,
27 terrain::{BlockKind, CoordinateConversions},
28 vol::{BaseVol, ReadVol},
29};
30use std::sync::Arc;
31use vek::*;
32use winit::event::MouseButton;
33
34use super::figure::{ModelEntry, ModelEntryRef};
35
36struct VoidVol;
37impl BaseVol for VoidVol {
38 type Error = ();
39 type Vox = ();
40}
41impl ReadVol for VoidVol {
42 fn get(&self, _pos: Vec3<i32>) -> Result<&'_ Self::Vox, Self::Error> { Ok(&()) }
43}
44
45struct Skybox {
46 model: Model<SkyboxVertex>,
47}
48
49pub struct Scene {
50 data: GlobalModel,
51 globals_bind_group: GlobalsBindGroup,
52 camera: Camera,
53
54 skybox: Skybox,
55 lod: Lod,
56 map_bounds: Vec2<f32>,
57
58 figure_atlas: FigureAtlas,
59 sprite_render_state: Arc<SpriteRenderState>,
60 sprite_globals: SpriteGlobalsBindGroup,
61
62 turning_camera: bool,
63
64 char_pos: Vec3<f32>,
65 char_state: Option<FigureState<CharacterSkeleton>>,
66 char_model_cache: FigureModelCache<CharacterSkeleton>,
67
68 airship_pos: Vec3<f32>,
69 airship_state: Option<FigureState<ShipSkeleton, BoundTerrainLocals>>,
70 airship_model_cache: FigureModelCache<ShipSkeleton>,
71}
72
73pub struct SceneData<'a> {
74 pub time: f64,
75 pub delta_time: f32,
76 pub tick: u64,
77 pub slow_job_pool: &'a SlowJobPool,
78 pub body: Option<humanoid::Body>,
79 pub gamma: f32,
80 pub exposure: f32,
81 pub ambiance: f32,
82 pub figure_lod_render_distance: f32,
83 pub mouse_smoothing: bool,
84}
85
86impl Scene {
87 pub fn new(
88 renderer: &mut Renderer,
89 client: &mut Client,
90 settings: &Settings,
91 sprite_render_context: SpriteRenderContext,
92 ) -> Self {
93 let start_angle = -90.0f32.to_radians();
94 let resolution = renderer.resolution().map(|e| e as f32);
95
96 let map_bounds = Vec2::new(
97 client.world_data().min_chunk_alt(),
98 client.world_data().max_chunk_alt(),
99 );
100
101 let mut camera = Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson);
102 camera.set_distance(3.4);
103 camera.set_orientation(Vec3::new(start_angle, 0.1, 0.0));
104
105 let figure_atlas = FigureAtlas::new(renderer);
106
107 let data = GlobalModel {
108 globals: renderer.create_consts(&[Globals::default()]),
109 lights: renderer.create_consts(&[Light::default(); crate::scene::MAX_LIGHT_COUNT]),
110 shadows: renderer.create_consts(&[Shadow::default(); crate::scene::MAX_SHADOW_COUNT]),
111 shadow_mats: renderer.create_shadow_bound_locals(&[ShadowLocals::default()]),
112 rain_occlusion_mats: renderer
113 .create_rain_occlusion_bound_locals(&[RainOcclusionLocals::default()]),
114 point_light_matrices: Box::new(
115 [PointLightMatrix::default(); crate::scene::MAX_POINT_LIGHT_MATRICES_COUNT],
116 ),
117 };
118 let lod = Lod::new(renderer, client, settings);
119
120 let globals_bind_group = renderer.bind_globals(&data, lod.get_data());
121
122 let world = client.world_data();
123 let char_chunk = world.chunk_size().map(|e| e as i32 / 2);
124 let char_pos = char_chunk.cpos_to_wpos().map(|e| e as f32).with_z(
125 world
126 .lod_alt
127 .get(char_chunk)
128 .map_or(0.0, |z| *z as f32 + 48.0),
129 );
130 client.set_lod_pos_fallback(char_pos.xy());
131 client.set_lod_distance(settings.graphics.lod_distance);
132
133 Self {
134 globals_bind_group,
135 skybox: Skybox {
136 model: renderer.create_model(&create_skybox_mesh()).unwrap(),
137 },
138 map_bounds,
139
140 figure_atlas,
141 sprite_render_state: sprite_render_context.state,
142 sprite_globals: renderer.bind_sprite_globals(
143 &data,
144 lod.get_data(),
145 &sprite_render_context.sprite_verts_buffer,
146 ),
147 lod,
148 data,
149
150 camera,
151
152 turning_camera: false,
153 char_pos,
154 char_state: None,
155 char_model_cache: FigureModelCache::new(),
156
157 airship_pos: char_pos - Vec3::unit_z() * 10.0,
158 airship_state: None,
159 airship_model_cache: FigureModelCache::new(),
160 }
161 }
162
163 pub fn globals(&self) -> &Consts<Globals> { &self.data.globals }
164
165 pub fn camera_mut(&mut self) -> &mut Camera { &mut self.camera }
166
167 pub fn handle_input_event(&mut self, event: Event) -> bool {
172 match event {
173 Event::Resize(dims) => {
175 self.camera.set_aspect_ratio(dims.x as f32 / dims.y as f32);
176 true
177 },
178 Event::MouseButton(button, state) => {
179 if state == PressState::Pressed {
180 self.turning_camera = button == MouseButton::Left;
181 } else {
182 self.turning_camera = false;
183 }
184 true
185 },
186 Event::CursorMove(delta) => {
187 if self.turning_camera {
188 self.camera.rotate_by(delta.with_z(0.0) * 0.01);
189 }
190 true
191 },
192 _ => false,
194 }
195 }
196
197 pub fn maintain(
198 &mut self,
199 renderer: &mut Renderer,
200 scene_data: SceneData,
201 inventory: Option<&Inventory>,
202 client: &Client,
203 ) {
204 self.camera
205 .force_focus_pos(self.char_pos + Vec3::unit_z() * 1.5);
206 let ori = self.camera.get_tgt_orientation();
207 self.camera
208 .set_orientation(Vec3::new(ori.x, ori.y.max(-0.25), ori.z));
209 self.camera.update(
210 scene_data.time,
211 scene_data.delta_time,
212 scene_data.mouse_smoothing,
213 );
214
215 self.camera.compute_dependents_full(&VoidVol, |_| false);
216 let camera::Dependents {
217 view_mat,
218 proj_mat,
219 cam_pos,
220 proj_mat_inv,
221 view_mat_inv,
222 ..
223 } = self.camera.dependents();
224 const VD: f32 = 0.0; const TIME: f64 = 8.6 * 60.0 * 60.0;
227 const SHADOW_NEAR: f32 = 0.25;
228 const SHADOW_FAR: f32 = 1.0;
229
230 self.lod
231 .maintain(renderer, client, self.camera.get_focus_pos(), &self.camera);
232
233 renderer.update_consts(&mut self.data.globals, &[Globals::new(
234 view_mat,
235 proj_mat,
236 cam_pos,
237 self.camera.get_focus_pos(),
238 VD,
239 self.lod.get_data().tgt_detail as f32,
240 self.map_bounds,
241 TIME,
242 scene_data.time,
243 0.0,
244 renderer.resolution().as_(),
245 Vec2::new(SHADOW_NEAR, SHADOW_FAR),
246 0,
247 0,
248 0,
249 BlockKind::Air,
250 None,
251 scene_data.gamma,
252 scene_data.exposure,
253 (Vec3::zero(), -1000.0),
254 Vec2::zero(),
255 scene_data.ambiance,
256 self.camera.get_mode(),
257 250.0,
258 )]);
259 renderer.update_clouds_locals(CloudsLocals::new(proj_mat_inv, view_mat_inv));
260 renderer.update_postprocess_locals(PostProcessLocals::new(proj_mat_inv, view_mat_inv));
261
262 self.char_model_cache
263 .clean(&mut self.figure_atlas, scene_data.tick);
264 self.airship_model_cache
265 .clean(&mut self.figure_atlas, scene_data.tick);
266
267 let item_info = |equip_slot| {
268 inventory
269 .and_then(|inv| inv.equipped(equip_slot))
270 .and_then(|i| {
271 if let ItemKind::Tool(tool) = &*i.kind() {
272 Some((Some(tool.kind), Some(tool.hands)))
273 } else {
274 None
275 }
276 })
277 .unwrap_or((None, None))
278 };
279
280 let (active_tool_kind, active_tool_hand) = item_info(EquipSlot::ActiveMainhand);
281 let (second_tool_kind, second_tool_hand) = item_info(EquipSlot::ActiveOffhand);
282
283 let hands = (active_tool_hand, second_tool_hand);
284
285 fn figure_params(dt: f32, pos: Vec3<f32>) -> FigureUpdateCommonParameters<'static> {
286 FigureUpdateCommonParameters {
287 entity: None,
288 pos,
289 ori: anim::vek::Quaternion::identity().rotated_z(std::f32::consts::PI * -0.5),
290 scale: 1.0,
291 mount_transform_pos: None,
292 body: None,
293 tools: (None, None),
294 col: Rgba::broadcast(1.0),
295 dt,
296 is_player: false,
297 terrain: None,
298 ground_vel: Vec3::zero(),
299 }
300 }
301
302 if let Some(body) = scene_data.body {
303 let char_state = self.char_state.get_or_insert_with(|| {
304 FigureState::new(renderer, CharacterSkeleton::default(), body)
305 });
306 let params = figure_params(scene_data.delta_time, self.char_pos);
307 let tgt_skeleton = anim::character::IdleAnimation::update_skeleton(
308 char_state.skeleton_mut(),
309 (
310 active_tool_kind,
311 second_tool_kind,
312 hands,
313 scene_data.time as f32,
314 ),
315 scene_data.time as f32,
316 &mut 0.0,
317 &anim::character::SkeletonAttr::from(&body),
318 );
319 let dt_lerp = (scene_data.delta_time * 15.0).min(1.0);
320 *char_state.skeleton_mut() =
321 Lerp::lerp(&*char_state.skeleton_mut(), &tgt_skeleton, dt_lerp);
322 let (model, _) = self.char_model_cache.get_or_create_model(
323 renderer,
324 &mut self.figure_atlas,
325 body,
326 inventory,
327 (),
328 scene_data.tick,
329 CameraMode::default(),
330 None,
331 scene_data.slow_job_pool,
332 None,
333 );
334 char_state.update(
335 renderer,
336 None,
337 &mut [Default::default(); anim::MAX_BONE_COUNT],
338 ¶ms,
339 1.0,
340 model,
341 body,
342 );
343 }
344
345 let airship_body = ship::Body::DefaultAirship;
346 let airship_state = self.airship_state.get_or_insert_with(|| {
347 FigureState::new(renderer, ShipSkeleton::default(), airship_body)
348 });
349 let params = figure_params(scene_data.delta_time, self.airship_pos);
350 let tgt_skeleton = anim::ship::IdleAnimation::update_skeleton(
351 airship_state.skeleton_mut(),
352 (
353 None,
354 None,
355 scene_data.time as f32,
356 scene_data.time as f32,
357 (params.ori * Vec3::unit_y()),
358 (params.ori * Vec3::unit_y()),
359 ),
360 scene_data.time as f32,
361 &mut 0.0,
362 &anim::ship::SkeletonAttr::from(&airship_body),
363 );
364 let dt_lerp = (scene_data.delta_time * 15.0).min(1.0);
365 *airship_state.skeleton_mut() =
366 Lerp::lerp(&*airship_state.skeleton_mut(), &tgt_skeleton, dt_lerp);
367 let (model, _) = self.airship_model_cache.get_or_create_terrain_model(
368 renderer,
369 &mut self.figure_atlas,
370 airship_body,
371 (),
372 scene_data.tick,
373 scene_data.slow_job_pool,
374 &self.sprite_render_state,
375 );
376 airship_state.update(
377 renderer,
378 None,
379 &mut [Default::default(); anim::MAX_BONE_COUNT],
380 ¶ms,
381 1.0,
382 model,
383 airship_body,
384 );
385 }
386
387 pub fn global_bind_group(&self) -> &GlobalsBindGroup { &self.globals_bind_group }
388
389 pub fn render<'a>(
390 &'a self,
391 drawer: &mut FirstPassDrawer<'a>,
392 tick: u64,
393 body: Option<humanoid::Body>,
394 inventory: Option<&Inventory>,
395 ) {
396 let mut figure_drawer = drawer.draw_figures();
397 if let Some(body) = body {
398 let model = &self.char_model_cache.get_model(
399 &self.figure_atlas,
400 body,
401 inventory,
402 tick,
403 CameraMode::default(),
404 None,
405 None,
406 );
407
408 if let Some((model, char_state)) = model.zip(self.char_state.as_ref()) {
409 if let Some(lod) = model.lod_model(0) {
410 figure_drawer.draw(
411 lod,
412 char_state.bound(),
413 self.figure_atlas.texture(ModelEntryRef::Figure(model)),
414 );
415 }
416 }
417 }
418
419 let model = &self.airship_model_cache.get_model(
420 &self.figure_atlas,
421 ship::Body::DefaultAirship,
422 Default::default(),
423 tick,
424 CameraMode::default(),
425 None,
426 None,
427 );
428 if let Some((model, airship_state)) = model.zip(self.airship_state.as_ref()) {
429 if let Some(lod) = model.lod_model(0) {
430 figure_drawer.draw(
431 lod,
432 airship_state.bound(),
433 self.figure_atlas.texture(ModelEntryRef::Terrain(model)),
434 );
435 }
436 }
437
438 drop(figure_drawer);
439
440 let mut sprite_drawer = drawer.draw_sprites(
441 &self.sprite_globals,
442 &self.sprite_render_state.sprite_atlas_textures,
443 );
444 if let (Some(sprite_instances), Some(data)) = (
445 self.airship_model_cache
446 .get_sprites(ship::Body::DefaultAirship),
447 self.airship_state.as_ref().map(|s| &s.extra),
448 ) {
449 sprite_drawer.draw(
450 data,
451 &sprite_instances[0],
452 &AltIndices {
453 deep_end: 0,
454 underground_end: 0,
455 },
456 CullingMode::None,
457 );
458 }
459 drop(sprite_drawer);
460
461 self.lod.render(drawer, Default::default());
462
463 drawer.draw_skybox(&self.skybox.model);
464 }
465}