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
//! 🌈 Render module
//!
//! TODO: Add overviewdescription how they work and how to use it
//!
//! Also see the Developer Guide section about [render modules](https://ark.embark.dev/guide/api/render.html)

#[doc(hidden)]
pub use ark_api::Mat4;
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 {
    /// Creation of a new module
    ///
    /// This is called once on startup of the module
    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: ark_api::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);

    /// Gets called before `draw` or `removed` is called for all the instances.
    fn pre_frame(&mut self, _common_info: &RenderFrameInfo, _draw_info: &[RenderDrawInfo]) {}

    /// Gets called after `draw` or `removed` is called for all the instances.
    fn post_frame(&mut self, _common_info: &RenderFrameInfo, _draw_info: &[RenderDrawInfo]) {}
}

/// Implement a render module.
///
/// This enables profiling support by default: make sure to import the Profiler API by including
/// `ark::require_profiler_api!();` somewhere in your code.
#[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();

            ark::setup_profiler!();

            // 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,
        ) {
            ark::profiler::set_scopes_on(ark::profiler::is_active());

            assert!(!frame_info_ptr.is_null());
            assert!(!draw_info_ptr.is_null());

            // 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,
                );

                let common_info = frame_info_ptr.as_ref().unwrap();

                <$module as $crate::render::RenderModule>::pre_frame(module, common_info, slice);

                for entry in slice {
                    assert!(entry.static_data_ptr != 0);
                    assert!(entry.dynamic_data_ptr != 0);

                    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,
                            common_info,
                            $crate::render::Mat4::from_cols_array(&entry.object_to_world),
                            static_data_slice,
                            dynamic_data_slice,
                            entry.cache_id,
                        );
                    }
                }

                <$module as $crate::render::RenderModule>::post_frame(module, common_info, slice);
            }
        }
    };
}