Skip to main content

veloren_voxygen/scene/
simple.rs

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    /// Handle an incoming user input event (e.g.: cursor moved, key pressed,
168    /// window closed).
169    ///
170    /// If the event is handled, return true.
171    pub fn handle_input_event(&mut self, event: Event) -> bool {
172        match event {
173            // When the window is resized, change the camera's aspect ratio
174            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            // All other events are unhandled
193            _ => 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            /* 1.0 / 60.0 */ 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; // View Distance
235        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            // Apply different animations to the character depending occasionally
358            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                // Don't perform meshing work on a job pool since we need the model to update
426                // immediately
427                None,
428                None,
429            );
430            char_state.update(
431                renderer,
432                None,
433                &mut [Default::default(); anim::MAX_BONE_COUNT],
434                &params,
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            &params,
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}