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 resources::TimeOfDay,
27 slowjob::SlowJobPool,
28 terrain::{BlockKind, CoordinateConversions},
29 util::Dir,
30 vol::{BaseVol, ReadVol},
31};
32use specs::WorldExt;
33use std::sync::Arc;
34use vek::*;
35use winit::event::MouseButton;
36
37use super::figure::{ModelEntry, ModelEntryRef};
38
39struct VoidVol;
40impl BaseVol for VoidVol {
41 type Error = ();
42 type Vox = ();
43}
44impl ReadVol for VoidVol {
45 fn get(&self, _pos: Vec3<i32>) -> Result<&'_ Self::Vox, Self::Error> { Ok(&()) }
46}
47
48struct Skybox {
49 model: Model<SkyboxVertex>,
50}
51
52pub struct Scene {
53 data: GlobalModel,
54 globals_bind_group: GlobalsBindGroup,
55 camera: Camera,
56
57 skybox: Skybox,
58 lod: Lod,
59 map_bounds: Vec2<f32>,
60
61 figure_atlas: FigureAtlas,
62 sprite_render_state: Arc<SpriteRenderState>,
63 sprite_globals: SpriteGlobalsBindGroup,
64
65 turning_camera: bool,
66
67 char_pos: Vec3<f32>,
68 char_state: Option<FigureState<CharacterSkeleton>>,
69 char_model_cache: FigureModelCache<CharacterSkeleton>,
70
71 airship_pos: Vec3<f32>,
72 airship_state: Option<FigureState<ShipSkeleton, BoundTerrainLocals>>,
73 airship_model_cache: FigureModelCache<ShipSkeleton>,
74}
75
76pub struct SceneData<'a> {
77 pub time: f64,
78 pub delta_time: f32,
79 pub tick: u64,
80 pub slow_job_pool: &'a SlowJobPool,
81 pub body: Option<humanoid::Body>,
82 pub gamma: f32,
83 pub exposure: f32,
84 pub ambiance: f32,
85 pub figure_lod_render_distance: f32,
86 pub mouse_smoothing: bool,
87}
88
89impl Scene {
90 pub fn new(
91 renderer: &mut Renderer,
92 client: &mut Client,
93 settings: &Settings,
94 sprite_render_context: SpriteRenderContext,
95 ) -> Self {
96 let start_angle = -90.0f32.to_radians();
97 let resolution = renderer.resolution().map(|e| e as f32);
98
99 let map_bounds = Vec2::new(
100 client.world_data().min_chunk_alt(),
101 client.world_data().max_chunk_alt(),
102 );
103
104 let mut camera = Camera::new(resolution.x / resolution.y, CameraMode::ThirdPerson);
105 camera.set_orientation(Vec3::new(start_angle, 0.1, 0.0));
106
107 let figure_atlas = FigureAtlas::new(renderer);
108
109 let data = GlobalModel {
110 globals: renderer.create_consts(&[Globals::default()]),
111 lights: renderer.create_consts(&[Light::default(); crate::scene::MAX_LIGHT_COUNT]),
112 shadows: renderer.create_consts(&[Shadow::default(); crate::scene::MAX_SHADOW_COUNT]),
113 shadow_mats: renderer.create_shadow_bound_locals(&[ShadowLocals::default()]),
114 rain_occlusion_mats: renderer
115 .create_rain_occlusion_bound_locals(&[RainOcclusionLocals::default()]),
116 point_light_matrices: Box::new(
117 [PointLightMatrix::default(); crate::scene::MAX_POINT_LIGHT_MATRICES_COUNT],
118 ),
119 };
120 let lod = Lod::new(renderer, client, settings);
121
122 let globals_bind_group = renderer.bind_globals(&data, lod.get_data());
123
124 let world = client.world_data();
125 let char_chunk = world.chunk_size().map(|e| e as i32 / 2);
126 let char_pos = char_chunk
127 .cpos_to_wpos()
128 .map(|e| e as f32)
129 .with_z(world.alt_at(char_chunk).unwrap_or(0.0) + 200.0);
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 is_edit: bool,
204 ) {
205 self.camera.set_distance(if is_edit { 3.5 } else { 6.0 });
206 let cam_height = if let Some(b) = scene_data.body {
207 b.height() * 0.65
208 } else {
209 1.75
210 };
211 self.camera
212 .set_focus_pos(self.char_pos + Vec3::unit_z() * cam_height);
213 let ori = self.camera.get_tgt_orientation();
214 self.camera
215 .set_orientation(Vec3::new(ori.x, ori.y.max(-0.15), ori.z));
216 self.camera.update(
217 scene_data.time,
218 scene_data.delta_time,
219 scene_data.mouse_smoothing,
220 );
221
222 self.camera.compute_dependents_full(&VoidVol, |_| false);
223 let camera::Dependents {
224 view_mat,
225 proj_mat,
226 cam_pos,
227 proj_mat_inv,
228 view_mat_inv,
229 ..
230 } = self.camera.dependents();
231
232 let time_of_day = client.state().ecs().read_resource::<TimeOfDay>();
233
234 const VD: f32 = 0.0; const SHADOW_NEAR: f32 = 0.25;
236 const SHADOW_FAR: f32 = 1.0;
237
238 self.lod
239 .maintain(renderer, client, self.camera.get_focus_pos(), &self.camera);
240
241 let lantern_light = if !is_edit
242 && !time_of_day.day_period().is_light()
243 && let Some(char_state) = &self.char_state
244 && let Some(inv) = inventory
245 && let Some(item) = inv.equipped(EquipSlot::Lantern)
246 && let ItemKind::Lantern(lantern) = &*item.kind()
247 {
248 let pos = char_state.wpos_of(
249 char_state
250 .computed_skeleton
251 .lantern
252 .mul_point(Vec3::new(0.0, 0.0, -5.5)),
253 );
254 let mut light = Light::new(pos, lantern.color(), lantern.strength());
255 if let Some((dir, fov)) = lantern.dir {
256 light =
257 light.with_dir(char_state.computed_skeleton.lantern.mul_direction(dir), fov);
258 }
259 Some(light)
260 } else {
261 None
262 };
263 renderer.update_consts(&mut self.data.lights, lantern_light.as_slice());
264
265 renderer.update_consts(&mut self.data.globals, &[Globals::new(
266 view_mat,
267 proj_mat,
268 cam_pos,
269 self.camera.get_focus_pos(),
270 VD,
271 self.lod.get_data().tgt_detail as f32,
272 self.map_bounds,
273 time_of_day.0,
274 scene_data.time,
275 0.0,
276 renderer.resolution().as_(),
277 renderer.internal_resolution().as_(),
278 Vec2::new(SHADOW_NEAR, SHADOW_FAR),
279 lantern_light.is_some() as usize,
280 0,
281 0,
282 BlockKind::Air,
283 None,
284 scene_data.gamma,
285 scene_data.exposure,
286 (Vec3::zero(), -1000.0),
287 Vec2::zero(),
288 scene_data.ambiance,
289 self.camera.get_mode(),
290 250.0,
291 0.0,
292 1.0,
293 )]);
294 renderer.update_clouds_locals(CloudsLocals::new(proj_mat_inv, view_mat_inv));
295 renderer.update_postprocess_locals(PostProcessLocals::new(proj_mat_inv, view_mat_inv));
296
297 self.char_model_cache
298 .clean(&mut self.figure_atlas, scene_data.tick);
299 self.airship_model_cache
300 .clean(&mut self.figure_atlas, scene_data.tick);
301
302 let item_info = |equip_slot| {
303 inventory
304 .and_then(|inv| inv.equipped(equip_slot))
305 .and_then(|i| {
306 if let ItemKind::Tool(tool) = &*i.kind() {
307 Some((Some(tool.kind), Some(tool.hands)))
308 } else {
309 None
310 }
311 })
312 .unwrap_or((None, None))
313 };
314
315 let (active_tool_kind, active_tool_hand) = item_info(EquipSlot::ActiveMainhand);
316 let (second_tool_kind, second_tool_hand) = item_info(EquipSlot::ActiveOffhand);
317
318 let hands = (active_tool_hand, second_tool_hand);
319
320 fn figure_params(dt: f32, pos: Vec3<f32>) -> FigureUpdateCommonParameters<'static> {
321 FigureUpdateCommonParameters {
322 entity: None,
323 pos,
324 ori: anim::vek::Quaternion::identity().rotated_z(std::f32::consts::PI * -0.5),
325 scale: 1.0,
326 mount_transform_pos: None,
327 body: None,
328 col: Rgba::broadcast(1.0),
329 dt,
330 is_player: false,
331 terrain: None,
332 ground_vel: Vec3::zero(),
333 primary_trail_points: None,
334 secondary_trail_points: None,
335 squash: 1.0,
336 }
337 }
338
339 if let Some(body) = scene_data.body {
340 let char_state = self.char_state.get_or_insert_with(|| {
341 FigureState::new(renderer, CharacterSkeleton::new(false, 0.0, 1.0), body)
342 });
343 let params = figure_params(scene_data.delta_time, self.char_pos);
344 char_state.skeleton.holding_lantern = lantern_light.is_some();
345 let tgt_skeleton = anim::character::IdleAnimation::update_skeleton(
346 &char_state.skeleton,
347 (
348 active_tool_kind,
349 second_tool_kind,
350 hands,
351 scene_data.time as f32,
352 ),
353 scene_data.time as f32,
354 &mut 0.0,
355 &anim::character::SkeletonAttr::from(&body),
356 );
357 let tgt_skeleton = if !is_edit && (scene_data.time + 113.0) % 80.0 < 10.0 {
359 anim::character::SitAnimation::update_skeleton(
360 &tgt_skeleton,
361 (
362 active_tool_kind,
363 second_tool_kind,
364 Dir::forward().to_vec(),
365 Dir::forward(),
366 scene_data.time as f32,
367 ),
368 scene_data.time as f32,
369 &mut 0.0,
370 &anim::character::SkeletonAttr::from(&body),
371 )
372 } else if (scene_data.time + 133.0) % 70.0 < 3.0 {
373 use common::{
374 comp::item::ConsumableKind,
375 states::{use_item::ItemUseKind, utils::StageSection},
376 };
377 anim::character::ConsumeAnimation::update_skeleton(
378 &tgt_skeleton,
379 (
380 scene_data.time as f32,
381 Some(StageSection::Action),
382 Some(ItemUseKind::Consumable(ConsumableKind::Food)),
383 ),
384 scene_data.time as f32,
385 &mut 0.0,
386 &anim::character::SkeletonAttr::from(&body),
387 )
388 } else if (scene_data.time + 173.0) % 60.0 < 6.0 {
389 anim::character::DanceAnimation::update_skeleton(
390 &tgt_skeleton,
391 (active_tool_kind, second_tool_kind, scene_data.time as f32),
392 scene_data.time as f32,
393 &mut 0.0,
394 &anim::character::SkeletonAttr::from(&body),
395 )
396 } else {
397 anim::character::StandAnimation::update_skeleton(
398 &tgt_skeleton,
399 (
400 active_tool_kind,
401 second_tool_kind,
402 hands,
403 Dir::forward().to_vec(),
404 Dir::forward().to_vec(),
405 Dir::forward(),
406 scene_data.time as f32,
407 Vec3::zero(),
408 ),
409 scene_data.time as f32,
410 &mut 0.0,
411 &anim::character::SkeletonAttr::from(&body),
412 )
413 };
414 let dt_lerp = (scene_data.delta_time * 15.0).min(1.0);
415 char_state.skeleton = Lerp::lerp(&char_state.skeleton, &tgt_skeleton, dt_lerp);
416 let (model, _) = self.char_model_cache.get_or_create_model(
417 renderer,
418 &mut self.figure_atlas,
419 body,
420 inventory,
421 (),
422 scene_data.tick,
423 CameraMode::default(),
424 None,
425 None,
428 None,
429 );
430 char_state.update(
431 renderer,
432 None,
433 &mut [Default::default(); anim::MAX_BONE_COUNT],
434 ¶ms,
435 1.0,
436 model,
437 body,
438 );
439 }
440
441 let airship_body = ship::Body::DefaultAirship;
442 let airship_state = self.airship_state.get_or_insert_with(|| {
443 FigureState::new(renderer, ShipSkeleton::default(), airship_body)
444 });
445 let params = figure_params(scene_data.delta_time, self.airship_pos);
446 let tgt_skeleton = anim::ship::IdleAnimation::update_skeleton(
447 &airship_state.skeleton,
448 (
449 None,
450 None,
451 scene_data.time as f32,
452 scene_data.time as f32,
453 (params.ori * Vec3::unit_y()),
454 (params.ori * Vec3::unit_y()),
455 ),
456 scene_data.time as f32,
457 &mut 0.0,
458 &anim::ship::SkeletonAttr::from(&airship_body),
459 );
460 let dt_lerp = (scene_data.delta_time * 15.0).min(1.0);
461 airship_state.skeleton = Lerp::lerp(&airship_state.skeleton, &tgt_skeleton, dt_lerp);
462 let (model, _) = self.airship_model_cache.get_or_create_terrain_model(
463 renderer,
464 &mut self.figure_atlas,
465 airship_body,
466 (),
467 scene_data.tick,
468 scene_data.slow_job_pool,
469 &self.sprite_render_state,
470 );
471 airship_state.update(
472 renderer,
473 None,
474 &mut [Default::default(); anim::MAX_BONE_COUNT],
475 ¶ms,
476 1.0,
477 model,
478 airship_body,
479 );
480 }
481
482 pub fn global_bind_group(&self) -> &GlobalsBindGroup { &self.globals_bind_group }
483
484 pub fn render<'a>(
485 &'a self,
486 drawer: &mut FirstPassDrawer<'a>,
487 tick: u64,
488 body: Option<humanoid::Body>,
489 inventory: Option<&Inventory>,
490 ) {
491 let mut figure_drawer = drawer.draw_figures();
492 if let Some(body) = body {
493 let model = &self.char_model_cache.get_model(
494 &self.figure_atlas,
495 body,
496 inventory,
497 tick,
498 CameraMode::default(),
499 None,
500 None,
501 );
502
503 if let Some((model, char_state)) = model.zip(self.char_state.as_ref())
504 && let Some(lod) = model.lod_model(0)
505 {
506 figure_drawer.draw(
507 lod,
508 char_state.bound(),
509 self.figure_atlas.texture(ModelEntryRef::Figure(model)),
510 );
511 }
512 }
513
514 let model = &self.airship_model_cache.get_model(
515 &self.figure_atlas,
516 ship::Body::DefaultAirship,
517 Default::default(),
518 tick,
519 CameraMode::default(),
520 None,
521 None,
522 );
523 if let Some((model, airship_state)) = model.zip(self.airship_state.as_ref())
524 && let Some(lod) = model.lod_model(0)
525 {
526 figure_drawer.draw(
527 lod,
528 airship_state.bound(),
529 self.figure_atlas.texture(ModelEntryRef::Terrain(model)),
530 );
531 }
532
533 drop(figure_drawer);
534
535 let mut sprite_drawer = drawer.draw_sprites(
536 &self.sprite_globals,
537 &self.sprite_render_state.sprite_atlas_textures,
538 );
539 if let (Some(sprite_instances), Some(data)) = (
540 self.airship_model_cache
541 .get_sprites(ship::Body::DefaultAirship),
542 self.airship_state.as_ref().map(|s| &s.extra),
543 ) {
544 sprite_drawer.draw(
545 data,
546 &sprite_instances[0],
547 &AltIndices {
548 deep_end: 0,
549 underground_end: 0,
550 },
551 CullingMode::None,
552 );
553 }
554 drop(sprite_drawer);
555
556 self.lod.render(drawer, Default::default());
557
558 drawer.draw_skybox(&self.skybox.model);
559 }
560}