Skip to main content

pfx_live/
scene.rs

1use std::collections::{BTreeMap, HashMap};
2
3use pfx_geom::mesh::Mesh;
4use pfx_load::scene::{
5    Camera as SceneCamera, Content, Draws, Environment, Geometry, Haze, Scene, SceneDiff,
6    Sun as SceneSun,
7};
8use pfx_materials::{Family, Material};
9use pfx_post::dof;
10
11use crate::effects::RoomHaze;
12use crate::frame::{
13    Camera, Instance, InstanceSurface, Matrix, MeshData, MeshHandle, Scene as FrameScene,
14    SceneWind, Sun, multiply,
15};
16use crate::glass;
17use crate::lights::{LocalLight, local_light};
18use crate::maps::ContentFormat;
19use crate::renderer::{Effects, Finish, Renderer, Text, TextQuadItem};
20use crate::skin::SkinData;
21use crate::sky::SkySource;
22use crate::text::{GpuAtlas, RichQuad, TextSpace, rich_quads};
23
24#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
25pub struct Applied {
26    pub uploaded: usize,
27    pub replaced: usize,
28    pub released: usize,
29    pub contents: usize,
30    pub texts: usize,
31    pub draws: bool,
32    pub lights: bool,
33    pub sky: bool,
34    pub sky_request: Option<u64>,
35    pub haze: bool,
36    pub sun: bool,
37    pub lens: bool,
38    pub finish: bool,
39}
40
41impl Applied {
42    pub fn touched_device(&self) -> bool {
43        self.uploaded + self.replaced + self.released + self.contents + self.texts > 0
44            || self.draws
45            || self.lights
46            || self.sky
47            || self.haze
48            || self.sun
49            || self.lens
50            || self.finish
51    }
52}
53
54#[derive(Clone, Copy, Debug, Default, PartialEq)]
55pub struct Override {
56    pub hidden: bool,
57    pub color: Option<[f32; 3]>,
58    pub highlight: Option<[f32; 3]>,
59    pub transform: Option<Matrix>,
60}
61
62impl Override {
63    fn tints(&self) -> bool {
64        self.color.is_some() || self.highlight.is_some()
65    }
66
67    fn moved(&self, model: Matrix) -> Matrix {
68        match self.transform {
69            Some(transform) => multiply(transform, model),
70            None => model,
71        }
72    }
73
74    fn material(&self, material: Material) -> Material {
75        let mut out = material;
76        if let Some(color) = self.color {
77            out.base = color;
78        }
79        if let Some(highlight) = self.highlight {
80            for (channel, add) in out.emission.iter_mut().zip(highlight) {
81                *channel += add;
82            }
83        }
84        out
85    }
86}
87
88pub struct StagedFrame<'a> {
89    pub scene: FrameScene<'a>,
90    pub text: Text<'a>,
91    pub effects: Effects<'a>,
92}
93
94struct StagedText {
95    quads: Vec<RichQuad>,
96    atlas: GpuAtlas,
97    model: Matrix,
98    lit: bool,
99    dynamic: bool,
100}
101
102#[derive(Clone, Copy)]
103enum Slot {
104    Opaque(usize),
105    Glass(usize),
106    Plate,
107}
108
109pub struct Staged {
110    meshes: HashMap<[u8; 32], MeshHandle>,
111    glass_meshes: HashMap<[u8; 32], glass::MeshHandle>,
112    draws: Draws,
113    slots: Vec<Slot>,
114    base: Vec<Instance>,
115    base_glass: Vec<glass::Surface>,
116    models: Vec<Matrix>,
117    previous: Vec<Matrix>,
118    instances: Vec<Instance>,
119    surfaces: Vec<InstanceSurface>,
120    glass: Vec<glass::Surface>,
121    materials: Vec<Material>,
122    overrides: BTreeMap<String, Override>,
123    content_slots: BTreeMap<usize, [u8; 32]>,
124    texts: Vec<StagedText>,
125    posing: Option<Scene>,
126    camera: SceneCamera,
127    sun: SceneSun,
128    plate: crate::plates::PlateMode,
129}
130
131fn mesh_data(geometry: &Geometry) -> MeshData<'_> {
132    MeshData {
133        positions: &geometry.positions,
134        normals: &geometry.normals,
135        tangents: &geometry.tangents,
136        uvs: &geometry.uvs,
137        uvs1: geometry.uvs1.as_deref(),
138        alpha: None,
139        indices: &geometry.indices,
140    }
141}
142
143fn upload_geometry(renderer: &mut Renderer, geometry: &Geometry) -> Result<MeshHandle, String> {
144    match &geometry.skin {
145        Some(skin) => renderer.upload_skinned_mesh(
146            mesh_data(geometry),
147            SkinData {
148                joints: &skin.joints,
149                weights: &skin.weights,
150            },
151        ),
152        None => renderer.upload_mesh(mesh_data(geometry)),
153    }
154}
155
156fn sprite_surface(scene: &Scene, draw: &pfx_load::scene::Draw, surface: &mut InstanceSurface) {
157    let Some(frame) = draw
158        .owner
159        .and_then(|owner| scene.objects.get(owner))
160        .and_then(|object| scene.animations.get(&object.name))
161        .and_then(|animation| animation.still_frame())
162    else {
163        return;
164    };
165    let (offset, scale, crop) = pfx_load::scene::surface(frame);
166    surface.uv_offset = offset;
167    surface.uv_scale = scale;
168    surface.crop = crop;
169}
170
171fn glass_mesh(geometry: &Geometry) -> Mesh {
172    Mesh::new(
173        geometry.positions.clone(),
174        geometry.normals.clone(),
175        geometry.tangents.clone(),
176        geometry.uvs.clone(),
177        geometry.indices.clone(),
178    )
179}
180
181pub fn sky_source(environment: Environment) -> SkySource {
182    match environment {
183        Environment::Analytic(sky) => SkySource::Analytic(sky),
184        Environment::Hdr(sky) => SkySource::Hdr(std::sync::Arc::unwrap_or_clone(sky)),
185    }
186}
187
188pub fn local_lights(scene: &Scene) -> Vec<LocalLight> {
189    scene.lights.values().map(local_light).collect()
190}
191
192pub fn room_haze(haze: &Haze) -> RoomHaze {
193    RoomHaze {
194        fog: haze.fog,
195        smoke: haze.smoke,
196        mist: haze.mist,
197        floor: haze.floor,
198        phase: haze.phase,
199        back: haze.back,
200        gold: haze.gold,
201        ambient: haze.ambient,
202        reach: haze.reach,
203        unmapped: haze.unmapped,
204        seed: haze.seed,
205        ..RoomHaze::new(haze.lo, haze.hi)
206    }
207}
208
209pub fn lens(camera: &SceneCamera, aspect: f32) -> Option<dof::Lens> {
210    let depth = camera.depth_of_field?;
211    let tan_half_width = camera.tan_half_height()? * aspect;
212    Some(dof::Lens {
213        focal_m: depth.focal,
214        sensor_m: dof::Lens::sensor_for_view(depth.focal, depth.distance, tan_half_width),
215        fstop: depth.fstop,
216        aperture: 1.0,
217        focus_m: depth.distance,
218        near_m: 0.0,
219        near_blur: 0.0,
220        far_m: 0.0,
221        far_blur: 0.0,
222        ground_m: None,
223    })
224}
225
226pub fn camera(camera: &SceneCamera, aspect: f32) -> Camera {
227    let view = camera.view();
228    let projection = camera.projection(aspect);
229    Camera {
230        view,
231        projection,
232        previous_view_projection: multiply(projection, view),
233        position: camera.at,
234    }
235}
236
237pub fn sun(sun: &SceneSun) -> Sun {
238    Sun {
239        direction: sun.direction,
240        colour: sun.color,
241        intensity: sun.intensity,
242    }
243}
244
245fn aspect_of(renderer: &Renderer) -> f32 {
246    let (width, height) = renderer.size();
247    width as f32 / height.max(1) as f32
248}
249
250fn content_slots(scene: &Scene, draws: &Draws) -> Result<BTreeMap<usize, String>, String> {
251    let mut slots: BTreeMap<usize, String> = BTreeMap::new();
252    for draw in &draws.items {
253        let Some(content) = &draw.content else {
254            continue;
255        };
256        let material = &draws.materials[draw.material as usize];
257        if !material.content_layer.active() {
258            continue;
259        }
260        let slot = material.content_layer.slot as usize;
261        match slots.get(&slot) {
262            Some(other) if other != content => {
263                return Err(format!(
264                    "{}: objects show content {other} and {content} through slot {slot} of material {}",
265                    scene.path.display(),
266                    draws.names[draw.material as usize]
267                ));
268            }
269            _ => {
270                slots.insert(slot, content.clone());
271            }
272        }
273    }
274    Ok(slots)
275}
276
277fn content_bytes(content: &Content) -> (ContentFormat, Vec<u8>) {
278    if content.srgb {
279        (ContentFormat::Srgb8, content.rgba.to_vec())
280    } else {
281        (
282            ContentFormat::Linear16,
283            content
284                .rgba
285                .iter()
286                .flat_map(|&byte| (u16::from(byte) * 257).to_le_bytes())
287                .collect(),
288        )
289    }
290}
291
292fn is_glass(draws: &Draws, index: usize) -> bool {
293    draws.materials[draws.items[index].material as usize].family == Family::Glass
294}
295
296fn set_sun_radius(renderer: &mut Renderer, radius: f32) -> bool {
297    let quality = renderer.shadows().quality();
298    if quality.sun_radius_deg == radius {
299        return false;
300    }
301    renderer.set_shadow_quality(crate::shadow::Quality {
302        sun_radius_deg: radius,
303        ..quality
304    });
305    true
306}
307
308fn stage_texts(renderer: &Renderer, scene: &Scene) -> Result<Vec<StagedText>, String> {
309    let gpu = renderer.gpu();
310    let mut texts = Vec::new();
311    for text in scene.texts.values() {
312        let paragraph = text.paragraph()?;
313        let quads = rich_quads(&paragraph);
314        if quads.is_empty() {
315            continue;
316        }
317        let atlas = GpuAtlas::new(&gpu.device, &gpu.queue, &paragraph.atlas)
318            .map_err(|error| format!("{}: {error:?}", text.font.display()))?;
319        texts.push(StagedText {
320            quads,
321            atlas,
322            model: text.model(),
323            lit: text.lit,
324            dynamic: text.dynamic,
325        });
326    }
327    Ok(texts)
328}
329
330impl Renderer {
331    pub fn stage(&mut self, scene: &Scene) -> Result<Staged, String> {
332        let draws = scene.draws();
333        let wanted = content_slots(scene, &draws)?;
334        let mut meshes = HashMap::new();
335        let mut glass_meshes = HashMap::new();
336        for (index, draw) in draws.items.iter().enumerate() {
337            if is_glass(&draws, index) {
338                if let std::collections::hash_map::Entry::Vacant(entry) =
339                    glass_meshes.entry(draw.geometry.hash)
340                {
341                    entry.insert(self.upload_glass(&glass_mesh(&draw.geometry))?);
342                }
343            } else if let std::collections::hash_map::Entry::Vacant(entry) =
344                meshes.entry(draw.geometry.hash)
345            {
346                entry.insert(upload_geometry(self, &draw.geometry)?);
347            }
348        }
349        let mut staged = Staged {
350            meshes,
351            glass_meshes,
352            draws: Draws::default(),
353            slots: Vec::new(),
354            base: Vec::new(),
355            base_glass: Vec::new(),
356            models: Vec::new(),
357            previous: Vec::new(),
358            instances: Vec::new(),
359            surfaces: Vec::new(),
360            glass: Vec::new(),
361            materials: Vec::new(),
362            overrides: BTreeMap::new(),
363            content_slots: BTreeMap::new(),
364            texts: stage_texts(self, scene)?,
365            posing: None,
366            camera: scene.camera_or_default(),
367            sun: scene.sun_or_dark(),
368            plate: crate::plates::PlateMode::default(),
369        };
370        staged.plate.load(self, scene, true)?;
371        staged.place(self, scene, draws, wanted)?;
372        self.set_plate_hour(staged.sun.hour);
373        self.set_lights(&local_lights(scene))?;
374        self.set_sky(sky_source(scene.environment()), staged.sun.hour)?;
375        self.set_haze(scene.haze.as_ref().map(room_haze));
376        set_sun_radius(self, staged.sun.radius);
377        self.set_lens(lens(&staged.camera, aspect_of(self)));
378        if let Some(finish) = &scene.finish {
379            self.set_finish(finish.chain.clone());
380        }
381        Ok(staged)
382    }
383}
384
385impl Staged {
386    fn place(
387        &mut self,
388        renderer: &mut Renderer,
389        scene: &Scene,
390        draws: Draws,
391        wanted: BTreeMap<usize, String>,
392    ) -> Result<usize, String> {
393        let mut slots = Vec::with_capacity(draws.items.len());
394        let mut base = Vec::new();
395        let mut base_glass = Vec::new();
396        let mut surfaces = Vec::new();
397        let mut stand_ins = Vec::new();
398        for (index, draw) in draws.items.iter().enumerate() {
399            if self.plate.baked(draw) {
400                slots.push(Slot::Plate);
401                if !is_glass(&draws, index)
402                    && draw.shadow.casts()
403                    && let Some(&mesh) = self.meshes.get(&draw.geometry.hash)
404                {
405                    stand_ins.push(Instance {
406                        casts_shadow: true,
407                        two_sided: draw.two_sided,
408                        alpha_cutoff: draw.alpha_cutoff,
409                        ..Instance::new(mesh, draw.model, draw.material, draw.id)
410                    });
411                }
412                continue;
413            }
414            if is_glass(&draws, index) {
415                slots.push(Slot::Glass(base_glass.len()));
416                base_glass.push(glass::Surface {
417                    mesh: self.glass_meshes[&draw.geometry.hash],
418                    model: draw.model,
419                    material: draws.materials[draw.material as usize],
420                    liquid: false,
421                    fluid_height: 0.0,
422                    ripple_height: 0.0,
423                    caustic_strength: 0.0,
424                    tinted: None,
425                    id: draw.id,
426                    casts_shadow: draw.shadow.casts(),
427                    shadow_only: !draw.shadow.drawn(),
428                    clip: draw.clip,
429                });
430            } else {
431                slots.push(Slot::Opaque(base.len()));
432                base.push(Instance {
433                    shadow_only: !draw.shadow.drawn(),
434                    casts_shadow: draw.shadow.casts(),
435                    two_sided: draw.two_sided,
436                    alpha_cutoff: draw.alpha_cutoff,
437                    ..Instance::new(
438                        self.meshes[&draw.geometry.hash],
439                        draw.model,
440                        draw.material,
441                        draw.id,
442                    )
443                });
444                let mut surface = InstanceSurface {
445                    clip: draw.clip,
446                    ..InstanceSurface::default()
447                };
448                sprite_surface(scene, draw, &mut surface);
449                surfaces.push(surface);
450            }
451        }
452        renderer.frame().set_surfaces(&surfaces)?;
453        self.plate.cast(renderer, &stand_ins)?;
454        let mut written = 0;
455        for (slot, name) in &wanted {
456            let content = &scene.contents[name];
457            if self.content_slots.get(slot) == Some(&content.hash) {
458                continue;
459            }
460            let (format, bytes) = content_bytes(content);
461            renderer
462                .frame()
463                .set_content(*slot, content.width, content.height, format)?;
464            renderer
465                .frame()
466                .write_content(*slot, [0, 0, content.width, content.height], &bytes)?;
467            self.content_slots.insert(*slot, content.hash);
468            written += 1;
469        }
470        self.content_slots
471            .retain(|slot, _| wanted.contains_key(slot));
472        self.models = draws.items.iter().map(|draw| draw.model).collect();
473        self.previous = self.models.clone();
474        self.posing = scene.animated().then(|| scene.clone());
475        self.draws = draws;
476        self.slots = slots;
477        self.base = base;
478        self.base_glass = base_glass;
479        self.surfaces = surfaces;
480        if let Some(posing) = &self.posing {
481            self.models = self.draws.models(&posing.posed(&self.camera, 0.0));
482            self.previous = self.models.clone();
483        }
484        self.refresh();
485        Ok(written)
486    }
487
488    fn refresh(&mut self) {
489        self.materials = self.draws.materials.clone();
490        if self.materials.is_empty() {
491            self.materials.push(Material::default());
492        }
493        self.instances = self.base.clone();
494        self.glass.clear();
495        let mut tinted: BTreeMap<(String, u32), u32> = BTreeMap::new();
496        for (index, slot) in self.slots.iter().enumerate() {
497            let draw = &self.draws.items[index];
498            let shown = self.overrides.get(&draw.object).copied();
499            let mut material = draw.material;
500            if let Some(change) = shown.filter(Override::tints) {
501                let key = (draw.object.clone(), draw.material);
502                material = match tinted.get(&key) {
503                    Some(&made) => made,
504                    None => {
505                        let made = self.materials.len() as u32;
506                        self.materials
507                            .push(change.material(self.draws.materials[draw.material as usize]));
508                        tinted.insert(key, made);
509                        made
510                    }
511                };
512            }
513            let hidden = shown.is_some_and(|change| change.hidden);
514            let change = shown.unwrap_or_default();
515            match *slot {
516                Slot::Opaque(at) => {
517                    let instance = &mut self.instances[at];
518                    instance.model = change.moved(self.models[index]);
519                    instance.previous_model = change.moved(self.previous[index]);
520                    instance.material = material;
521                    if hidden {
522                        instance.shadow_only = true;
523                        instance.casts_shadow = false;
524                    }
525                }
526                Slot::Plate => {}
527                Slot::Glass(at) => {
528                    if hidden {
529                        continue;
530                    }
531                    self.glass.push(glass::Surface {
532                        model: change.moved(self.models[index]),
533                        material: self.materials[material as usize],
534                        ..self.base_glass[at]
535                    });
536                }
537            }
538        }
539    }
540
541    pub fn apply(
542        &mut self,
543        renderer: &mut Renderer,
544        scene: &Scene,
545        diff: &SceneDiff,
546    ) -> Result<Applied, String> {
547        let mut applied = Applied::default();
548        if diff.is_empty() {
549            return Ok(applied);
550        }
551        if diff.camera {
552            self.camera = scene.camera_or_default();
553            renderer.set_lens(lens(&self.camera, aspect_of(renderer)));
554            applied.lens = true;
555        }
556        if diff.plates || diff.draws() {
557            self.plate.load(renderer, scene, diff.plates)?;
558            let draws = scene.draws();
559            let wanted = content_slots(scene, &draws)?;
560            let mut needed: Vec<&Geometry> = Vec::new();
561            let mut needed_glass: Vec<&Geometry> = Vec::new();
562            for (index, draw) in draws.items.iter().enumerate() {
563                let list = if is_glass(&draws, index) {
564                    &mut needed_glass
565                } else {
566                    &mut needed
567                };
568                if !list
569                    .iter()
570                    .any(|geometry| geometry.hash == draw.geometry.hash)
571                {
572                    list.push(&draw.geometry);
573                }
574            }
575            let mut spare: Vec<[u8; 32]> = self
576                .meshes
577                .keys()
578                .filter(|hash| !needed.iter().any(|geometry| &geometry.hash == *hash))
579                .copied()
580                .collect();
581            spare.sort();
582            for geometry in needed {
583                if self.meshes.contains_key(&geometry.hash) {
584                    continue;
585                }
586                let reuse = if geometry.skin.is_none() {
587                    spare
588                        .iter()
589                        .rposition(|old| {
590                            self.meshes
591                                .get(old)
592                                .is_some_and(|handle| !renderer.frame().skinned(*handle))
593                        })
594                        .map(|at| spare.remove(at))
595                } else {
596                    None
597                };
598                match reuse {
599                    Some(old) => {
600                        let handle = self.meshes.remove(&old).ok_or("a staged mesh vanished")?;
601                        renderer.replace_mesh(handle, mesh_data(geometry))?;
602                        self.meshes.insert(geometry.hash, handle);
603                        applied.replaced += 1;
604                    }
605                    None => {
606                        let handle = upload_geometry(renderer, geometry)?;
607                        self.meshes.insert(geometry.hash, handle);
608                        applied.uploaded += 1;
609                    }
610                }
611            }
612            for old in spare {
613                if let Some(handle) = self.meshes.remove(&old) {
614                    renderer.release_mesh(handle)?;
615                    applied.released += 1;
616                }
617            }
618            let mut spare_glass: Vec<[u8; 32]> = self
619                .glass_meshes
620                .keys()
621                .filter(|hash| !needed_glass.iter().any(|geometry| &geometry.hash == *hash))
622                .copied()
623                .collect();
624            spare_glass.sort();
625            for geometry in needed_glass {
626                if self.glass_meshes.contains_key(&geometry.hash) {
627                    continue;
628                }
629                match spare_glass.pop() {
630                    Some(old) => {
631                        let handle = self
632                            .glass_meshes
633                            .remove(&old)
634                            .ok_or("a staged glass mesh vanished")?;
635                        renderer.replace_glass(handle, &glass_mesh(geometry))?;
636                        self.glass_meshes.insert(geometry.hash, handle);
637                        applied.replaced += 1;
638                    }
639                    None => {
640                        let handle = renderer.upload_glass(&glass_mesh(geometry))?;
641                        self.glass_meshes.insert(geometry.hash, handle);
642                        applied.uploaded += 1;
643                    }
644                }
645            }
646            for old in spare_glass {
647                if let Some(handle) = self.glass_meshes.remove(&old) {
648                    renderer.release_glass(handle)?;
649                    applied.released += 1;
650                }
651            }
652            applied.contents = self.place(renderer, scene, draws, wanted)?;
653            applied.draws = true;
654        } else if diff.camera && self.posing.is_some() {
655            if let Some(posing) = &self.posing {
656                self.models = self.draws.models(&posing.posed(&self.camera, 0.0));
657                self.previous = self.models.clone();
658            }
659            self.refresh();
660        }
661        if !diff.texts.is_empty() {
662            self.texts = stage_texts(renderer, scene)?;
663            applied.texts = self.texts.len().max(1);
664        }
665        if !diff.lights.is_empty() {
666            renderer.set_lights(&local_lights(scene))?;
667            applied.lights = true;
668        }
669        if diff.sun {
670            self.sun = scene.sun_or_dark();
671            applied.sun = set_sun_radius(renderer, self.sun.radius);
672            renderer.set_plate_hour(self.sun.hour);
673        }
674        if diff.sky {
675            applied.sky_request =
676                Some(renderer.request_sky(sky_source(scene.environment()), self.sun.hour)?);
677            applied.sky = true;
678        }
679        if diff.haze {
680            renderer.set_haze(scene.haze.as_ref().map(room_haze));
681            applied.haze = true;
682        }
683        if diff.finish {
684            match &scene.finish {
685                Some(finish) => renderer.set_finish(finish.chain.clone()),
686                None => renderer.clear_finish(),
687            }
688            applied.finish = true;
689        }
690        Ok(applied)
691    }
692
693    pub fn pose(&mut self, time: f32) {
694        if let Some(posing) = &self.posing {
695            let models = self.draws.models(&posing.posed(&self.camera, time));
696            self.previous = std::mem::replace(&mut self.models, models);
697            self.refresh();
698        }
699    }
700
701    pub fn scene(&self, aspect: f32, time: f32, seed: u32) -> FrameScene<'_> {
702        FrameScene {
703            camera: camera(&self.camera, aspect),
704            time,
705            seed,
706            sun: sun(&self.sun),
707            instances: &self.instances,
708            materials: &self.materials,
709            deformers: &[],
710            wind: SceneWind::default(),
711        }
712    }
713
714    pub fn frame(&mut self, aspect: f32, time: f32, seed: u32) -> StagedFrame<'_> {
715        self.pose(time);
716        StagedFrame {
717            scene: self.scene(aspect, time, seed),
718            text: self.text(aspect),
719            effects: self.effects(),
720        }
721    }
722
723    pub fn text(&self, aspect: f32) -> Text<'_> {
724        let view_projection = multiply(self.camera.projection(aspect), self.camera.view());
725        Text {
726            surface_quads: self
727                .texts
728                .iter()
729                .filter(|text| text.dynamic || !self.plate.plated())
730                .map(|text| TextQuadItem {
731                    atlas: &text.atlas,
732                    quads: &text.quads,
733                    space: if text.lit {
734                        TextSpace::LitSurface {
735                            model_view_projection: multiply(view_projection, text.model),
736                            model: text.model,
737                        }
738                    } else {
739                        TextSpace::Surface {
740                            model_view_projection: multiply(view_projection, text.model),
741                        }
742                    },
743                    icons: false,
744                    id: 0,
745                })
746                .collect(),
747            ..Text::default()
748        }
749    }
750
751    pub fn effects(&self) -> Effects<'_> {
752        Effects {
753            glass: &self.glass,
754            ..Effects::default()
755        }
756    }
757
758    pub fn lens(&self, aspect: f32) -> Option<dof::Lens> {
759        lens(&self.camera, aspect)
760    }
761
762    pub fn set_override(&mut self, object: &str, change: Override) -> bool {
763        if !self.draws.items.iter().any(|draw| draw.object == object) {
764            return false;
765        }
766        if change == Override::default() {
767            self.overrides.remove(object);
768        } else {
769            self.overrides.insert(object.to_string(), change);
770        }
771        self.refresh();
772        true
773    }
774
775    pub fn clear_override(&mut self, object: &str) {
776        if self.overrides.remove(object).is_some() {
777            self.refresh();
778        }
779    }
780
781    pub fn clear_overrides(&mut self) {
782        if !self.overrides.is_empty() {
783            self.overrides.clear();
784            self.refresh();
785        }
786    }
787
788    pub fn overrides(&self) -> &BTreeMap<String, Override> {
789        &self.overrides
790    }
791
792    pub fn finish(&self) -> Finish {
793        Finish::Standard
794    }
795
796    pub fn draws(&self) -> &Draws {
797        &self.draws
798    }
799
800    pub fn instances(&self) -> &[Instance] {
801        &self.instances
802    }
803
804    pub fn glass(&self) -> &[glass::Surface] {
805        &self.glass
806    }
807
808    pub fn materials(&self) -> &[Material] {
809        &self.materials
810    }
811
812    pub fn surfaces(&self) -> &[InstanceSurface] {
813        &self.surfaces
814    }
815
816    pub fn meshes(&self) -> usize {
817        self.meshes.len() + self.glass_meshes.len()
818    }
819
820    pub fn texts(&self) -> usize {
821        self.texts.len()
822    }
823
824    pub fn camera(&self) -> SceneCamera {
825        self.camera
826    }
827
828    pub fn sun(&self) -> SceneSun {
829        self.sun
830    }
831
832    pub fn plated(&self) -> bool {
833        self.plate.plated()
834    }
835
836    pub fn release(self, renderer: &mut Renderer) -> Result<(), String> {
837        self.plate.release(renderer)?;
838        for handle in self.meshes.into_values() {
839            renderer.release_mesh(handle)?;
840        }
841        for handle in self.glass_meshes.into_values() {
842            renderer.release_glass(handle)?;
843        }
844        Ok(())
845    }
846}