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);
}
}
};
}