1use std::sync::Arc;
13
14use cranpose_ui_graphics::{
15 Rect, Size,
16 space::{Camera, EyeWorld, LayerSpace, Light, Mat4, MeshSurface, Scene3d, Vec3},
17};
18
19use crate::graph::{LayerNode, ProjectiveTransform, RenderNode};
20
21const BACK_THROUGH_DOORWAY: f32 = 1.0e-4;
26
27const SMALLEST_OPENING: f32 = 0.25;
30
31pub struct Cell<'a> {
33 pub layer: &'a LayerNode,
35 pub scene: &'a Scene3d,
37 pub parent: Option<usize>,
39 pub hole: Option<(usize, Rect)>,
43 pub doorway: Mat4,
46 pub to_parent: Mat4,
48}
49
50pub struct Placed<'a> {
52 pub layer: &'a LayerNode,
54 pub surface: MeshSurface,
56 pub cell: usize,
58 pub size: Size,
60}
61
62type Host = (usize, ProjectiveTransform);
65
66pub struct SpaceTree<'a> {
69 pub cells: Vec<Cell<'a>>,
71 pub surfaces: Vec<Placed<'a>>,
73}
74
75pub struct EyeView<'a> {
79 pub camera: Camera,
81 pub size: Size,
83 pub cell: usize,
85 pub seen: Vec<SeenSpace>,
88 pub surfaces: Vec<SeenSurface<'a>>,
90}
91
92pub struct SeenSpace {
94 pub cell: usize,
96 pub through: Option<usize>,
99 pub depth: u8,
103 pub to_eye: Mat4,
105 pub light: Option<Light>,
107 pub door: Option<SeenDoor>,
109}
110
111#[derive(Clone, Copy, Debug, PartialEq)]
113pub struct SeenDoor {
114 pub quad: Mat4,
116 pub away: f32,
118 pub clip: [f32; 4],
121 pub opening: Rect,
124}
125
126pub struct SeenSurface<'a> {
128 pub layer: &'a LayerNode,
130 pub surface: MeshSurface,
132 pub size: Size,
134 pub seen: usize,
136}
137
138impl<'a> SpaceTree<'a> {
139 pub fn of(scene_layer: &'a LayerNode) -> Option<Self> {
142 let scene = scene_of(scene_layer)?;
143 let mut tree = Self {
144 cells: Vec::new(),
145 surfaces: Vec::new(),
146 };
147 tree.open(scene_layer, scene, None);
148 Some(tree)
149 }
150
151 pub fn holds_scene(&self, layer: &LayerNode) -> bool {
153 layer.node_id.is_some()
154 && self
155 .cells
156 .iter()
157 .any(|cell| cell.layer.node_id == layer.node_id)
158 }
159
160 pub fn cell_along(&self, path: &[usize]) -> usize {
163 let mut cell = 0;
164 for &node in path {
165 let next = self
166 .cells
167 .iter()
168 .position(|inner| inner.parent == Some(cell) && inner.layer.node_id == Some(node));
169 match next {
170 Some(next) => cell = next,
171 None => break,
172 }
173 }
174 cell
175 }
176
177 pub fn to_root(&self, cell: usize) -> Mat4 {
179 let mut transform = Mat4::IDENTITY;
180 let mut at = Some(cell);
181 while let Some(index) = at {
182 let cell = &self.cells[index];
183 transform = transform.then(&cell.to_parent);
184 at = cell.parent;
185 }
186 transform
187 }
188
189 fn open(
194 &mut self,
195 layer: &'a LayerNode,
196 scene: &'a Scene3d,
197 place: Option<(usize, Mat4, Option<Host>)>,
198 ) {
199 let index = self.cells.len();
200 let cell = match place {
201 None => Cell {
202 layer,
203 scene,
204 parent: None,
205 hole: None,
206 doorway: Mat4::IDENTITY,
207 to_parent: Mat4::IDENTITY,
208 },
209 Some((parent, frame, host)) => {
210 let rect = layer.node_rect();
211 Cell {
212 layer,
213 scene,
214 parent: Some(parent),
215 hole: host
216 .map(|(surface, to_content)| (surface, to_content.bounds_for_rect(rect))),
217 doorway: Mat4::scaling(Vec3::new(rect.width, rect.height, 1.0))
218 .then(&Mat4::translation(Vec3::new(rect.x, rect.y, 0.0)))
219 .then(&frame),
220 to_parent: scene.camera.doorway(rect).then(&frame),
221 }
222 }
223 };
224 self.cells.push(cell);
225 self.children(layer, &Mat4::IDENTITY, (index, None));
226 }
227
228 fn children(
232 &mut self,
233 layer: &'a LayerNode,
234 frame: &Mat4,
235 (cell, host): (usize, Option<Host>),
236 ) {
237 for child in &layer.children {
238 let RenderNode::Layer(child) = child else {
239 continue;
240 };
241 if !child.has_space() {
242 continue;
243 }
244 let child_frame = embed(child.transform_to_parent).then(frame);
245 let child_host = host
246 .map(|(surface, to_content)| (surface, child.transform_to_parent.then(to_content)));
247 match child.graphics_layer.space.as_deref() {
248 Some(LayerSpace::Scene(scene)) => {
249 self.open(child, scene, Some((cell, child_frame, child_host)));
250 }
251 Some(LayerSpace::Surface(surface)) => {
252 self.surface(child, surface, &child_frame, cell);
253 }
254 Some(LayerSpace::Transform(transform)) => {
255 self.children(child, &transform.then(&child_frame), (cell, None));
256 }
257 None => self.children(child, &child_frame, (cell, child_host)),
258 }
259 }
260 }
261
262 fn surface(&mut self, layer: &'a LayerNode, surface: &MeshSurface, frame: &Mat4, cell: usize) {
265 let size = node_size(layer);
266 let placed = surface.placed_by(frame);
267 let content = placed.content_to_scene(size);
268 let index = self.surfaces.len();
269 self.surfaces.push(Placed {
270 layer,
271 surface: placed,
272 cell,
273 size,
274 });
275 if let Some(content) = content {
276 self.children(
277 layer,
278 &content,
279 (cell, Some((index, ProjectiveTransform::identity()))),
280 );
281 }
282 }
283}
284
285#[derive(Clone, Copy, PartialEq, Eq)]
288enum Through {
289 Inward,
291 Outward,
293}
294
295impl<'a> EyeView<'a> {
296 pub fn of(tree: &SpaceTree<'a>, size: Size) -> Self {
300 let root = tree.cells[0].scene;
301 let cell = root
302 .eye
303 .as_ref()
304 .map_or(0, |eye| tree.cell_along(&eye.doorways));
305 let mut view = Self {
306 camera: root.camera,
307 size,
308 cell,
309 seen: vec![SeenSpace {
310 cell,
311 through: None,
312 depth: 0,
313 to_eye: Mat4::IDENTITY,
314 light: tree.cells[cell].scene.light,
315 door: None,
316 }],
317 surfaces: Vec::new(),
318 };
319 view.look_through(tree, (0, None), Rect::from_size(size));
320 let mut seen_of = vec![None; tree.cells.len()];
321 for (index, seen) in view.seen.iter().enumerate() {
322 seen_of[seen.cell] = Some(index);
323 }
324 for placed in &tree.surfaces {
325 let Some(seen) = seen_of[placed.cell] else {
326 continue;
327 };
328 view.surfaces.push(SeenSurface {
329 layer: placed.layer,
330 surface: placed.surface.placed_by(&view.seen[seen].to_eye),
331 size: placed.size,
332 seen,
333 });
334 }
335 view
336 }
337
338 fn look_through(
341 &mut self,
342 tree: &SpaceTree<'a>,
343 (seen, came_from): (usize, Option<usize>),
344 opening: Rect,
345 ) {
346 let (cell, to_eye) = (self.seen[seen].cell, self.seen[seen].to_eye);
347 for (inner, space) in tree.cells.iter().enumerate() {
348 if space.parent != Some(cell) || Some(inner) == came_from {
349 continue;
350 }
351 let inner_to_eye = space.to_parent.then(&to_eye);
352 let quad = space.doorway.then(&to_eye);
353 self.through(
354 tree,
355 (seen, inner, inner_to_eye),
356 (quad, Through::Inward),
357 opening,
358 );
359 }
360 let space = &tree.cells[cell];
361 if let Some(outer) = space.parent.filter(|outer| Some(*outer) != came_from)
362 && let Some(from_parent) = space.to_parent.inverse()
363 {
364 let outer_to_eye = from_parent.then(&to_eye);
365 let quad = space.doorway.then(&outer_to_eye);
366 self.through(
367 tree,
368 (seen, outer, outer_to_eye),
369 (quad, Through::Outward),
370 opening,
371 );
372 }
373 }
374
375 fn through(
379 &mut self,
380 tree: &SpaceTree<'a>,
381 (seen, cell, to_eye): (usize, usize, Mat4),
382 (quad, through): (Mat4, Through),
383 opening: Rect,
384 ) {
385 let Some(depth) = self.seen[seen].depth.checked_add(1) else {
386 return;
387 };
388 let corner = quad.transform_point(Vec3::ZERO);
389 let Some(normal) = quad.transform_vector(Vec3::new(0.0, 0.0, 1.0)).normalized() else {
390 return;
391 };
392 let side = normal.dot(self.camera.eye() - corner);
393 let (facing, away, clip) = match through {
396 Through::Inward => (side < 0.0, 1.0, plane(normal, corner, 0.0)),
397 Through::Outward => {
398 let margin = BACK_THROUGH_DOORWAY * normal.dot(corner).abs().max(1.0);
399 (side > 0.0, -1.0, plane(-normal, corner, margin))
400 }
401 };
402 let shown = facing
403 .then(|| self.opening(&quad))
404 .flatten()
405 .and_then(|shown| shown.intersect(opening))
406 .filter(|shown| shown.width * shown.height >= SMALLEST_OPENING);
407 let Some(shown) = shown else {
408 return;
409 };
410 let light = tree.cells[cell].scene.light.map(|light| Light {
411 direction: to_eye.transform_vector(light.direction),
412 ..light
413 });
414 self.seen.push(SeenSpace {
415 cell,
416 through: Some(seen),
417 depth,
418 to_eye,
419 light,
420 door: Some(SeenDoor {
421 quad,
422 away,
423 clip,
424 opening: shown,
425 }),
426 });
427 let index = self.seen.len() - 1;
428 self.look_through(tree, (index, Some(self.seen[seen].cell)), shown);
429 }
430
431 fn opening(&self, quad: &Mat4) -> Option<Rect> {
435 let to_camera = quad.then(&self.camera.view);
436 let corners = [(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)]
437 .map(|(u, v)| to_camera.transform_point(Vec3::new(u, v, 0.0)));
438 let near = self.camera.near();
439 let (mut low, mut high) = (
440 (f32::INFINITY, f32::INFINITY),
441 (f32::NEG_INFINITY, f32::NEG_INFINITY),
442 );
443 let mut any = false;
444 let mut show = |point: Vec3| {
445 let scale = self.camera.focal_length / point.z;
446 let (x, y) = (
447 self.size.width * 0.5 + point.x * scale,
448 self.size.height * 0.5 + point.y * scale,
449 );
450 low = (low.0.min(x), low.1.min(y));
451 high = (high.0.max(x), high.1.max(y));
452 any = true;
453 };
454 for (index, &from) in corners.iter().enumerate() {
455 let to = corners[(index + 1) % corners.len()];
456 if from.z >= near {
457 show(from);
458 }
459 if (from.z >= near) != (to.z >= near) {
460 show(from + (to - from) * ((near - from.z) / (to.z - from.z)));
461 }
462 }
463 any.then_some(Rect {
464 x: low.0,
465 y: low.1,
466 width: high.0 - low.0,
467 height: high.1 - low.1,
468 })
469 }
470
471 pub fn build_world(&self, tree: &SpaceTree<'_>, world: &mut EyeWorld) {
475 if let Some(window_scene) = tree.cells[0].layer.node_id {
476 world.set_window_scene(window_scene);
477 }
478 let walked = tree.walked_to(self.cell);
479 let base = world.add_space(Arc::clone(&walked), Mat4::IDENTITY);
480 let mut spaces = vec![(self.cell, base, Mat4::IDENTITY)];
481 let own = &tree.cells[self.cell];
482 if let Some(outer) = own.parent
483 && let Some(from_parent) = own.to_parent.inverse()
484 {
485 let path: Arc<[usize]> = Arc::from(&walked[..walked.len().saturating_sub(1)]);
486 let index = world.add_space(path, from_parent);
487 world.add_doorway((index, base), own.doorway.then(&from_parent));
488 spaces.push((outer, index, from_parent));
489 }
490 for (inner, space) in tree.cells.iter().enumerate() {
491 let (Some(cell), Some(node)) = (space.parent, space.layer.node_id) else {
492 continue;
493 };
494 if cell != self.cell {
495 continue;
496 }
497 let path: Arc<[usize]> = walked.iter().copied().chain([node]).collect();
498 let index = world.add_space(path, space.to_parent);
499 world.add_doorway((base, index), space.doorway);
500 spaces.push((inner, index, space.to_parent));
501 }
502 for (surface_index, placed) in tree.surfaces.iter().enumerate() {
503 let Some(&(_, space, to_base)) =
504 spaces.iter().find(|(cell, _, _)| *cell == placed.cell)
505 else {
506 continue;
507 };
508 if !placed.surface.solid {
509 continue;
510 }
511 let holes = tree.cells.iter().filter_map(|cell| {
512 cell.hole
513 .filter(|(host, _)| *host == surface_index)
514 .map(|(_, rect)| rect)
515 });
516 world.add_solid(
517 (space, Arc::clone(&placed.surface.mesh)),
518 placed.surface.transform.then(&to_base),
519 (placed.size, holes),
520 );
521 }
522 }
523}
524
525impl SpaceTree<'_> {
526 fn walked_to(&self, cell: usize) -> Arc<[usize]> {
529 let mut path = Vec::new();
530 let mut at = cell;
531 while let Some(parent) = self.cells[at].parent {
532 if let Some(node) = self.cells[at].layer.node_id {
533 path.push(node);
534 }
535 at = parent;
536 }
537 path.reverse();
538 Arc::from(path)
539 }
540}
541
542fn plane(normal: Vec3, point: Vec3, margin: f32) -> [f32; 4] {
545 [normal.x, normal.y, normal.z, -normal.dot(point) - margin]
546}
547
548pub fn embed(transform: ProjectiveTransform) -> Mat4 {
551 let [[a, b, c], [d, e, f], [g, h, i]] = transform.matrix();
552 Mat4::from_rows([
553 [a, b, 0.0, c],
554 [d, e, 0.0, f],
555 [0.0, 0.0, 1.0, 0.0],
556 [g, h, 0.0, i],
557 ])
558}
559
560pub fn scene_of(layer: &LayerNode) -> Option<&Scene3d> {
562 match layer.graphics_layer.space.as_deref()? {
563 LayerSpace::Scene(scene) => Some(scene),
564 LayerSpace::Surface(_) | LayerSpace::Transform(_) => None,
565 }
566}
567
568pub fn node_size(layer: &LayerNode) -> Size {
570 let rect = layer.node_rect();
571 Size {
572 width: rect.width,
573 height: rect.height,
574 }
575}
576
577pub fn for_each_window_eye(
582 layer: &LayerNode,
583 to_window: ProjectiveTransform,
584 visit: &mut impl FnMut(&SpaceTree<'_>, &EyeView<'_>, ProjectiveTransform),
585) {
586 for_each_window_scene(layer, to_window, &mut |scene_layer, to_window| {
587 let Some(tree) = SpaceTree::of(scene_layer) else {
588 return;
589 };
590 let view = EyeView::of(&tree, node_size(scene_layer));
591 let rect = scene_layer.node_rect();
592 visit(
593 &tree,
594 &view,
595 ProjectiveTransform::translation(rect.x, rect.y).then(to_window),
596 );
597 });
598}
599
600impl EyeView<'_> {
601 pub fn content_to_window(
605 &self,
606 seen: &SeenSurface<'_>,
607 to_window: ProjectiveTransform,
608 ) -> Option<ProjectiveTransform> {
609 seen.surface
610 .content_transform(&self.camera, self.size, seen.size)
611 .map(|content| content.then(to_window))
612 }
613}
614
615pub fn for_each_window_scene<'a>(
619 layer: &'a LayerNode,
620 to_window: ProjectiveTransform,
621 visit: &mut impl FnMut(&'a LayerNode, ProjectiveTransform),
622) {
623 if scene_of(layer).is_some() {
624 visit(layer, to_window);
625 return;
626 }
627 for child in &layer.children {
628 if let RenderNode::Layer(child) = child
629 && child.has_space()
630 {
631 for_each_window_scene(child, child.transform_to_parent.then(to_window), visit);
632 }
633 }
634}