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
pub use ark_api_ffi::entrypoints::render::RenderDrawFlags;
pub use ark_api_ffi::entrypoints::render::RenderDrawInfo;
pub use ark_api_ffi::entrypoints::render::RenderFrameInfo;

/// Render module creation & update trait
pub trait RenderModule {
    fn new() -> Self;

    /// Implement the rendering for an entity here.
    ///
    /// `cache_id` is unique per entity, and can be used internally to cache entity-specific data.
    /// It is unrelated to the `instance_id` you can specify in the render API, although you might
    /// find it useful to use the `cache_id` to come up with suitably unique and stable `instance_id`s.
    ///
    /// `static_data` is the data array you specified when creating the Shape.
    /// `dynamic_data` is empty by default but can be set at any time on the Render component of the Entity.
    fn draw(
        &mut self,
        common_info: &RenderFrameInfo,
        object_to_world: macaw::Mat4,
        static_data: &[u8],
        dynamic_data: &[u8],
        cache_id: u64,
    );

    /// Gets called when you can safely delete all cached information related to a specific `cache_id`.
    fn removed(&mut self, cache_id: u64);
}

/// Implement a render module
#[macro_export]
macro_rules! impl_render_module {
    ($module: ty) => {
        use $crate::render::RenderModule as _;

        static mut MODULE: Option<$module> = None;

        #[no_mangle]
        pub fn ark_initialize() {
            $crate::init();
            // SAFETY: Sound to access static as we do not use threads in Wasm and this is guaranteed to be called first in the module
            unsafe {
                MODULE = Some(<$module as $crate::render::RenderModule>::new());
            }
        }

        #[no_mangle]
        pub unsafe fn ark_render_frame(
            frame_info_ptr: *const $crate::render::RenderFrameInfo,
            draw_info_ptr: *const $crate::render::RenderDrawInfo,
            draw_info_len: u32,
        ) {
            // SAFETY: Sound to access static as we do not use threads in Wasm and this is guaranteed to be initialized on startup with `ark_initialize` first
            unsafe {
                let module = MODULE.as_mut().unwrap();
                let slice = std::slice::from_raw_parts::<$crate::render::RenderDrawInfo>(
                    draw_info_ptr,
                    draw_info_len as usize,
                );
                for entry in slice {
                    let static_data_slice = std::slice::from_raw_parts::<u8>(
                        entry.static_data_ptr as *const u8,
                        entry.static_data_len as usize,
                    );
                    let dynamic_data_slice = std::slice::from_raw_parts::<u8>(
                        entry.dynamic_data_ptr as *const u8,
                        entry.dynamic_data_len as usize,
                    );
                    if entry
                        .flags
                        .contains($crate::render::RenderDrawFlags::REMOVED)
                    {
                        // Instance was removed
                        <$module as $crate::render::RenderModule>::removed(module, entry.cache_id);
                    } else {
                        <$module as $crate::render::RenderModule>::draw(
                            module,
                            frame_info_ptr.as_ref().unwrap(),
                            macaw::Mat4::from_cols_array(&entry.object_to_world),
                            static_data_slice,
                            dynamic_data_slice,
                            entry.cache_id,
                        );
                    }
                }
            }
        }
    };
}