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(¶graph);
314 if quads.is_empty() {
315 continue;
316 }
317 let atlas = GpuAtlas::new(&gpu.device, &gpu.queue, ¶graph.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}