Skip to main content

pfx_play/
present.rs

1use std::collections::BTreeMap;
2
3use pfx_live::frame::{
4    Instance, InstanceSurface, Matrix, Scene as FrameScene, SceneWind, multiply,
5};
6use pfx_live::glass;
7use pfx_live::renderer::{Effects, Renderer, Text, invert};
8use pfx_live::scene::{self as live_scene, Staged};
9use pfx_live::skin::InstancePose;
10use pfx_live::text::TextSpace;
11use pfx_load::scene::{Camera, Scene, surface as sprite_surface};
12use pfx_materials::{Family, Material};
13use pfx_post::{Bloom, Chain, Pass, Tone};
14
15use crate::PlayError;
16use crate::world::{Look, ObjectId, World};
17
18pub struct PlayFrame<'a> {
19    pub scene: FrameScene<'a>,
20    pub text: Text<'a>,
21    pub effects: Effects<'a>,
22}
23
24#[derive(Default)]
25pub(crate) struct Presenter {
26    instances: Vec<Instance>,
27    glass: Vec<glass::Surface>,
28    materials: Vec<Material>,
29    previous: Vec<Matrix>,
30    previous_palettes: BTreeMap<usize, Vec<Matrix>>,
31    surfaces: Vec<InstanceSurface>,
32    last_view_projection: Option<Matrix>,
33    lens_camera: Option<Camera>,
34    cut: bool,
35    lit: u64,
36    exposed: u64,
37    touched: Touched,
38}
39
40#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
41pub(crate) struct Touched {
42    pub lights: bool,
43    pub finish: bool,
44    pub posed: bool,
45    pub sprites: bool,
46}
47
48pub(crate) fn cut(renderer: &mut Renderer) -> Result<(), PlayError> {
49    let (width, height) = renderer.size();
50    renderer.resize(width, height).map_err(PlayError::Render)
51}
52
53pub(crate) fn exposed(scene: &Scene, exposure: f32) -> Chain {
54    let mut chain = match &scene.finish {
55        Some(finish) => finish.chain.clone(),
56        None => Chain {
57            passes: vec![Pass::Bloom(Bloom::neutral(0.0)), Pass::Tone(Tone::aces())],
58            seed: 0,
59            frame: 0,
60        },
61    };
62    match chain.passes.first_mut() {
63        Some(Pass::Exposure(value)) => *value = exposure,
64        _ => chain.passes.insert(0, Pass::Exposure(exposure)),
65    }
66    chain
67}
68
69pub(crate) fn untouch(
70    scene: &Scene,
71    touched: Touched,
72    renderer: &mut Renderer,
73) -> Result<(), PlayError> {
74    if touched.lights {
75        renderer
76            .set_lights(&live_scene::local_lights(scene))
77            .map_err(PlayError::Render)?;
78    }
79    if touched.finish {
80        match &scene.finish {
81            Some(finish) => renderer.set_finish(finish.chain.clone()),
82            None => renderer.clear_finish(),
83        }
84    }
85    Ok(())
86}
87
88fn looked(world: &World, base: Material, look: &Look) -> Material {
89    let mut material = look
90        .material
91        .as_deref()
92        .and_then(|name| world.material(name))
93        .unwrap_or(base);
94    if let Some(color) = look.color {
95        material.base = color;
96    }
97    if let Some(emission) = look.emission {
98        material.emission = emission;
99    }
100    material
101}
102
103impl Presenter {
104    pub(crate) fn touched(&self) -> Touched {
105        self.touched
106    }
107
108    fn sync(&mut self, world: &World, renderer: &mut Renderer) -> Result<(), PlayError> {
109        if self.lit != world.lights_revision() {
110            let lights: Vec<_> = world
111                .lights()
112                .values()
113                .map(pfx_live::lights::local_light)
114                .collect();
115            renderer.set_lights(&lights).map_err(PlayError::Render)?;
116            self.lit = world.lights_revision();
117            self.touched.lights = true;
118        }
119        if self.exposed != world.exposure_revision() {
120            renderer.set_finish(exposed(world.scene(), world.exposure()));
121            self.exposed = world.exposure_revision();
122            self.touched.finish = true;
123        }
124        Ok(())
125    }
126
127    pub(crate) fn present<'a>(
128        &'a mut self,
129        world: &World,
130        staged: &'a Staged,
131        renderer: &mut Renderer,
132        aspect: f32,
133        time: f32,
134        seed: u32,
135    ) -> Result<PlayFrame<'a>, PlayError> {
136        if !self.cut {
137            cut(renderer)?;
138            self.cut = true;
139        }
140        self.sync(world, renderer)?;
141        let camera = world.fx.camera(&world.camera);
142        if self.lens_camera != Some(camera) {
143            renderer.set_lens(live_scene::lens(&camera, aspect));
144            self.lens_camera = Some(camera);
145        }
146        let posed = world.faced_models(&camera);
147        let draws = staged.draws();
148        let palettes = world.palettes();
149        let frames = world.sprite_frames();
150        let models = draws.models_posed(&posed, &palettes);
151        let mut skinned: Vec<(usize, usize)> = Vec::new();
152        self.surfaces.clear();
153        self.surfaces.extend_from_slice(staged.surfaces());
154        self.instances.clear();
155        self.instances.extend_from_slice(staged.instances());
156        self.materials.clear();
157        self.materials.extend_from_slice(staged.materials());
158        let edited = world.edited_materials();
159        if !edited.is_empty() {
160            for (slot, name) in self.materials.iter_mut().zip(&draws.names) {
161                if let Some(material) = edited.get(name) {
162                    *slot = *material;
163                }
164            }
165        }
166        self.glass.clear();
167        let staged_glass = staged.glass();
168        let mut opaque = 0;
169        let mut glass_at = 0;
170        let mut made: BTreeMap<(usize, u32), u32> = BTreeMap::new();
171        for (index, draw) in draws.items.iter().enumerate() {
172            let model = models[index];
173            let previous = self.previous.get(index).copied().unwrap_or(model);
174            let object = draw
175                .owner
176                .filter(|&place| place < world.len())
177                .map(ObjectId::from_index);
178            let hidden = object.is_some_and(|id| world.hidden(id));
179            let look = object
180                .map(|id| world.look(id))
181                .filter(|look| !look.is_plain());
182            if draws.materials[draw.material as usize].family == Family::Glass {
183                if staged
184                    .overrides()
185                    .get(&draw.object)
186                    .is_some_and(|change| change.hidden)
187                {
188                    continue;
189                }
190                let Some(surface) = staged_glass.get(glass_at) else {
191                    continue;
192                };
193                glass_at += 1;
194                if hidden {
195                    continue;
196                }
197                let base = draws
198                    .names
199                    .get(draw.material as usize)
200                    .and_then(|name| edited.get(name))
201                    .copied()
202                    .unwrap_or(surface.material);
203                let material = match look {
204                    Some(look) => looked(world, base, look),
205                    None => base,
206                };
207                self.glass.push(glass::Surface {
208                    model,
209                    material,
210                    ..*surface
211                });
212                continue;
213            }
214            let Some(instance) = self.instances.get_mut(opaque) else {
215                continue;
216            };
217            if let Some(owner) = draw.owner {
218                if draw.geometry.skin.is_some() && palettes.contains_key(&owner) {
219                    skinned.push((opaque, owner));
220                }
221                if let (Some(frame), Some(surface)) =
222                    (frames.get(&owner), self.surfaces.get_mut(opaque))
223                {
224                    let (offset, scale, crop) = sprite_surface(*frame);
225                    surface.uv_offset = offset;
226                    surface.uv_scale = scale;
227                    surface.crop = crop;
228                }
229            }
230            opaque += 1;
231            instance.model = model;
232            instance.previous_model = previous;
233            if hidden {
234                instance.shadow_only = true;
235                instance.casts_shadow = false;
236            }
237            if let (Some(look), Some(id)) = (look, object) {
238                let key = (id.index(), instance.material);
239                instance.material = match made.get(&key) {
240                    Some(&material) => material,
241                    None => {
242                        let base = self.materials[instance.material as usize];
243                        let material = self.materials.len() as u32;
244                        self.materials.push(looked(world, base, look));
245                        made.insert(key, material);
246                        material
247                    }
248                };
249            }
250        }
251        self.previous = models;
252        let poses: Vec<InstancePose<'_>> = skinned
253            .iter()
254            .map(|(instance, owner)| {
255                let joints = &palettes[owner];
256                InstancePose {
257                    instance: *instance,
258                    joints,
259                    previous: self
260                        .previous_palettes
261                        .get(owner)
262                        .filter(|previous| previous.len() == joints.len())
263                        .unwrap_or(joints),
264                }
265            })
266            .collect();
267        if !poses.is_empty() || self.touched.posed {
268            renderer.set_poses(&poses).map_err(PlayError::Render)?;
269            self.touched.posed = true;
270        }
271        drop(poses);
272        self.previous_palettes = palettes;
273        if !frames.is_empty() {
274            renderer
275                .set_surfaces(&self.surfaces)
276                .map_err(PlayError::Render)?;
277            self.touched.sprites = true;
278        }
279        let mut frame_camera = live_scene::camera(&camera, aspect);
280        let view_projection = multiply(frame_camera.projection, frame_camera.view);
281        if let Some(last) = self.last_view_projection {
282            frame_camera.previous_view_projection = last;
283        }
284        self.last_view_projection = Some(view_projection);
285        let mut text = staged.text(aspect);
286        let rest = staged.camera();
287        if rest != camera {
288            let before = multiply(rest.projection(aspect), rest.view());
289            if let Some(undo) = invert(before) {
290                let fix = multiply(view_projection, undo);
291                for item in &mut text.surface_quads {
292                    match &mut item.space {
293                        TextSpace::Surface {
294                            model_view_projection,
295                        } => *model_view_projection = multiply(fix, *model_view_projection),
296                        TextSpace::LitSurface {
297                            model_view_projection,
298                            model,
299                        } => *model_view_projection = multiply(view_projection, *model),
300                        _ => {}
301                    }
302                }
303            }
304        }
305        Ok(PlayFrame {
306            scene: FrameScene {
307                camera: frame_camera,
308                time,
309                seed,
310                sun: live_scene::sun(&staged.sun()),
311                instances: &self.instances,
312                materials: &self.materials,
313                deformers: &[],
314                wind: SceneWind::default(),
315            },
316            text,
317            effects: Effects {
318                glass: &self.glass,
319                ..Effects::default()
320            },
321        })
322    }
323}