1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use crate::render_features::RenderObjectHandle;
use crate::visibility::view_frustum_arc::ViewFrustumArc;
use crate::visibility::visibility_object_allocator::{
    VisibilityObjectAllocator, VisibilityObjectId,
};
use crate::visibility::visibility_object_arc::{CullModel, VisibilityObjectArc};
use crate::visibility::ObjectId;
use crossbeam_channel::Sender;
use rafx_visibility::geometry::Transform;
use rafx_visibility::{AsyncCommand, ModelHandle, VisibilityObject, VisibilityWorld, ZoneHandle};

pub struct VisibilityObjectInfo<'a> {
    arc: VisibilityObjectArc,
    obj: &'a VisibilityObject,
}

impl<'a> VisibilityObjectInfo<'a> {
    pub fn object_id(&self) -> ObjectId {
        self.arc.object_id()
    }

    pub fn render_objects(&self) -> &[RenderObjectHandle] {
        self.arc.render_objects()
    }

    pub fn transform(&self) -> Transform {
        self.obj.transform.unwrap_or_default()
    }

    pub fn previous_frame_transform(&self) -> Option<Transform> {
        self.obj.previous_frame_transform
    }

    pub fn model_handle(&self) -> &Option<ModelHandle> {
        &self.obj.cull_model
    }
}

pub struct VisibilityResource {
    allocator: VisibilityObjectAllocator,
    commands: Sender<AsyncCommand>,
    dynamic_zone: ZoneHandle,
    static_zone: ZoneHandle,
}

impl VisibilityResource {
    pub fn new() -> Self {
        let mut visibility_world = VisibilityWorld::new();
        let static_zone = visibility_world.inner.new_zone();
        let dynamic_zone = visibility_world.inner.new_zone();
        let commands = visibility_world.new_async_command_sender();
        let allocator = VisibilityObjectAllocator::new(visibility_world);

        VisibilityResource {
            commands,
            static_zone,
            dynamic_zone,
            allocator,
        }
    }

    pub fn world(&self) -> &VisibilityWorld {
        self.allocator.world()
    }

    pub fn update(&mut self) {
        self.allocator.update();
    }

    pub fn register_view_frustum(&mut self) -> ViewFrustumArc {
        self.allocator
            .new_view_frustum(Some(self.static_zone), Some(self.dynamic_zone))
    }

    pub fn register_static_view_frustum(&mut self) -> ViewFrustumArc {
        self.allocator
            .new_view_frustum(Some(self.static_zone), None)
    }

    pub fn register_dynamic_view_frustum(&mut self) -> ViewFrustumArc {
        self.allocator
            .new_view_frustum(None, Some(self.dynamic_zone))
    }

    /// Returns a smart pointer to a handle representing a static object.
    /// A static object is a hint to the visibility world that the object's transform changes rarely.
    /// Most geometry in the world is static -- buildings, trees, rocks, grass, and so on.
    pub fn register_static_object(
        &mut self,
        object_id: ObjectId,
        cull_model: CullModel,
        render_objects: Vec<RenderObjectHandle>,
    ) -> VisibilityObjectArc {
        self.register_object(object_id, cull_model, render_objects, self.static_zone)
    }

    /// Returns a smart pointer to a handle representing a dynamic object.
    /// A dynamic object is a hint to the visibility world that the object's transform changes often.
    /// Characters, projectiles, vehicles, and moving platforms are examples of dynamic geometry.
    pub fn register_dynamic_object(
        &mut self,
        object_id: ObjectId,
        cull_model: CullModel,
        render_objects: Vec<RenderObjectHandle>,
    ) -> VisibilityObjectArc {
        self.register_object(object_id, cull_model, render_objects, self.dynamic_zone)
    }

    fn register_object(
        &mut self,
        object_id: ObjectId,
        cull_model: CullModel,
        render_objects: Vec<RenderObjectHandle>,
        zone: ZoneHandle,
    ) -> VisibilityObjectArc {
        self.allocator
            .new_object(object_id, cull_model, render_objects, Some(zone))
    }

    pub fn visibility_object_arc(
        &self,
        id: VisibilityObjectId,
    ) -> Option<VisibilityObjectArc> {
        self.allocator.object_ref(id)
    }

    pub fn visibility_object_info(
        &self,
        id: VisibilityObjectId,
    ) -> Option<VisibilityObjectInfo> {
        let arc = self.allocator.object_ref(id);
        if let Some(arc) = arc {
            let obj = self
                .world()
                .inner
                .visibility_object(arc.visibility_object_handle());
            if let Some(obj) = obj {
                return Some(VisibilityObjectInfo { arc, obj });
            }
        }

        None
    }
}

impl Drop for VisibilityResource {
    fn drop(&mut self) {
        let _ = self
            .commands
            .send(AsyncCommand::DestroyZone(self.static_zone));
        let _ = self
            .commands
            .send(AsyncCommand::DestroyZone(self.dynamic_zone));
    }
}