define_api_id!(0xc711_14ac_a9a4_ae3b, "render-v0");
use crate::FFIResult;
use crate::PodBool;
use bytemuck::CheckedBitPattern;
use bytemuck::NoUninit;
use bytemuck::Pod;
use bytemuck::Zeroable;
// TODO: figure out a safer way to go about texture handles. -- max
/// Handle to a texture in Ark.
///
/// # Safety
///
/// Care should be taken around the validity of a raw [`TextureHandle`]. Some validity issues are:
///
/// * Trying to destroy a texture with [`destroy_texture`] that has already been destroyed.
/// * Dropping the [`TextureHandle`] before calling [`destroy_texture`], causing a leak.
/// * Passing an invalid [`TextureHandle`], one that has already been destroyed, to a function that
/// operates on a texture.
///
/// To safely handle these cases we recommend wrapping your [`TextureHandle`] in a [`std::rc::Rc`]
/// or [`std::sync::Arc`]. See the examples section for a safe implementation.
///
/// # Examples
///
/// Safe implementation example around [`TextureHandle`].
///
/// ```no_run
/// use ark_api_ffi::render_v0::TextureHandle;
///
/// pub struct Texture {
/// handle: std::rc::Rc<TextureHandle>,
/// }
///
/// impl Texture {
/// // Pass the handle retrieved from `create_texture` here.
/// pub fn new(handle: TextureHandle) -> Self {
/// Self {
/// handle: std::rc::Rc::new(handle),
/// }
/// }
///
/// pub fn handle(&self) -> TextureHandle {
/// *self.handle
/// }
/// }
///
/// impl Drop for Texture {
/// // Never call `destroy_texture` yourself, let `Drop` call it.
/// fn drop(&mut self) {
/// if std::rc::Rc::strong_count(&self.handle) == 1 {
/// # #[cfg(target_arch = "wasm32")]
/// destroy_texture(*self.handle);
/// }
/// }
/// }
///
/// impl Clone for Texture {
/// fn clone(&self) -> Self {
/// Self {
/// handle: std::rc::Rc::clone(&self.handle),
/// }
/// }
/// }
/// ```
pub type TextureHandle = u64;
/// Used to restrict the drawing of 2d triangles to a specific region.
#[derive(Clone, Copy, Debug, Default, Pod, Zeroable)]
#[repr(C)]
pub struct Rectangle {
pub min_x: f32,
pub min_y: f32,
pub max_x: f32,
pub max_y: f32,
}
impl Rectangle {
#[inline(always)]
pub fn width(&self) -> f32 {
self.max_x - self.min_x
}
#[inline(always)]
pub fn height(&self) -> f32 {
self.max_y - self.min_y
}
pub fn from_resolution(resolution: [u32; 2]) -> Self {
Self {
min_x: 0.0,
min_y: 0.0,
max_x: resolution[0] as f32,
max_y: resolution[1] as f32,
}
}
}
/// Used for 3D culling and tessellation applications.
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct BoundingBox {
pub min: [f32; 3],
pub max: [f32; 3],
}
/// Describes how a texture stores its data.
#[derive(Copy, Clone, Debug, PartialEq, Eq, NoUninit, CheckedBitPattern)]
#[repr(u32)]
#[non_exhaustive]
#[allow(non_camel_case_types)]
pub enum TextureFormat {
/// 32-bit-per-pixel, fixed-point pixel format assuming premultiplied alpha. SRGB encoded.
R8G8B8A8_SRGB = 1,
/// 32-bit-per-pixel, fixed-point pixel format assuming premultiplied alpha. Linear.
R8G8B8A8_UNORM = 2,
// A single red channel in gamma space
//R8,
}
impl TextureFormat {
/// The amount of storage required for a single texture pixel.
#[inline]
pub fn bytes_per_pixel(&self) -> u64 {
match self {
TextureFormat::R8G8B8A8_SRGB | TextureFormat::R8G8B8A8_UNORM => 4,
//TextureFormat::R8 => 1,
}
}
}
/// Defines the type of texture.
#[derive(Copy, Clone, Debug, PartialEq, Eq, NoUninit, CheckedBitPattern)]
#[repr(u32)]
#[non_exhaustive]
pub enum TextureType {
/// 2 dimensional texture.
D2 = 1,
}
/// Describes a texture and is used for texture creation.
///
/// * `depth`, `mips`, `array_len` - Currently not supported by Ark but is made public for future-proofing the API.
#[derive(Copy, Clone, Debug, PartialEq, Eq, NoUninit, CheckedBitPattern)]
#[repr(C)]
pub struct TextureDescription {
pub texture_type: TextureType,
pub format: TextureFormat,
pub width: u64,
pub height: u64,
pub depth: u64,
pub mipmaps: u32,
pub array_len: u32,
}
/// Describes the transform of a bone. More compact and more defined than a Mat4.
///
/// Currently uses a pos + quat representation, although this may changed in the future.
/// An extra float is provided for padding.
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct BoneTransform {
pub pos: [f32; 3],
pub _padding: f32,
pub rot: [f32; 4],
}
impl PartialEq for BoneTransform {
fn eq(&self, other: &Self) -> bool {
self.pos == other.pos && self.rot == other.rot
}
}
impl BoneTransform {
pub fn zero() -> Self {
Self {
pos: [0.0; 3],
_padding: 0.0,
rot: [0.0; 4],
}
}
}
/// Describes an instance of an Sdf function to be rendered.
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
#[repr(C)]
pub struct SdfInstanceData {
/// Instance space to world transform for the draw call. Column major.
pub world_from_instance: [f32; 16], // TODO: [f32; 16]
/// Index into the bounding box array.
pub bounding_box_index: u32,
/// Dynamic data for the procedural instance
///
/// Set both to zero in order to render it without modification.
pub dynamic_data_offset: u32,
pub dynamic_data_length: u32,
/// Detail level. 0.0 means automatic detail.
///
/// TODO: Define what this means.
pub detail_bias: f32,
/// Range: `[0, 1]`. Set to 1 for fully opaque rendering.
/// Set to `< 1` for transparent rendering.
/// Transparent objects are rendered back-to-front, after opaque objects,
/// but before objects with `depth_test=false`.
pub opacity: f32,
/// Range: `[0, 1]`. Interpolated between unlit and lit colors.
///
/// Set to `0` to disable lighting. Set to `1` to enable lighting.
pub lighting: f32,
/// If `true`, depth testing is enabled, which means things closer to the camera
/// will rendered on top of things further away. This is normally what you want.
///
/// If `false`, this instance will be rendered on top of previous instances, even if this instance is further away.
/// In other words, settings `depth_test=false` will
/// make your instance visible through all other instances, even if they are not transparent.
///
/// Instances with `depth_test=false` are always rendered last.
pub depth_test: PodBool,
/// Control whether or not to write to the depth buffer.
///
/// NOTE: render order is respected, EXCEPT for instances which are transparent or has `depth_test=false`.
pub depth_write: PodBool,
pub _pad: [u8; 2],
}
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
#[repr(C)]
pub struct SkinnedSdfInstanceData {
pub detail_bias: f32,
pub opacity: f32,
pub lighting: f32,
}
pub type SdfHandle = u64;
#[ark_api_macros::ark_bindgen(imports = "ark-render-v0")]
mod render {
use super::*;
extern "C" {
/// Create a texture, returning a handle to it.
///
/// For valid texture creation, the description's `width`, `height`, `depth`, and [`TextureFormat`]
/// need to match up against the length of the `data` so that `width` * `height` * `format.bytes_per_pixel()` == `data.len()`.
///
/// # Errors
///
/// Returns an [`crate::ErrorCode::InvalidArguments`] if `data`'s length doesn't match
/// up against the description's dimensions and format or if description dimensions has
/// values equal to 0.
///
/// # Examples
///
/// Basic usage:
/// ```no_run
/// use ark_api_ffi::render_v0::{TextureDescription, TextureFormat, TextureType};
///
/// // Create a simple 1x1 opaque red texture. More sophisticated data can be
/// // acquired through the use of, for example, the `image` crate.
/// let data = [255, 0, 0, 255];
/// let description = TextureDescription {
/// width: 1,
/// height: 1,
/// depth: 1,
/// format: TextureFormat::R8G8B8A8_SRGB,
/// mipmaps: 1,
/// array_len: 1,
/// texture_type: TextureType::D2,
/// };
///
/// # #[cfg(target_arch = "wasm32")]
/// let texture = create_texture("my amazing texture", &description, &data)?;
/// # Ok::<(), ark_api_ffi::ErrorCode>(())
/// ```
pub fn create_texture(
name: &str,
description: &TextureDescription,
data: &[u8],
) -> FFIResult<TextureHandle>;
/// Updates a subrectangle of a texture with new data.
///
/// Format is implied to be the same as the texture already is in.
pub fn update_texture(
handle: TextureHandle,
pos_x: u32,
pos_y: u32,
width: u32,
height: u32,
data: &[u8],
);
/// Destroy a [`TextureHandle`]'s associated texture data.
///
/// # Errors
///
/// Returns an [`crate::ErrorCode::InvalidArguments`] if there is no texture associated
/// to the provided handle.
#[deprecated_infallible]
pub fn destroy_texture(handle: TextureHandle);
/// Draw colored 2D triangles in screen space (physical pixel coordinates).
///
/// * `colors` - Assumes premultiplied alpha.
///
/// # Errors
///
/// Returns an [`crate::ErrorCode::InvalidArguments`] for the following cases:
/// * `positions` length is not an even multiple of 2.
/// * `indices` length is not an even multiple of 3.
/// * `colors` length is not an even multiple of 4.
#[deprecated_infallible]
pub fn draw_triangles_2d(
clip_rect: &Rectangle,
indices: &[u32], // vertex index triplets
positions: &[f32], // x,y interleaved
colors: &[u8], // r,g,b,a interleaved
);
/// Draw colored 2D textured triangles in screen space (physical pixel coordinates).
///
/// * `colors` - Assumes premultiplied alpha.
///
/// # Errors
///
/// Returns an [`crate::ErrorCode::InvalidArguments`] for the following reasons:
/// * `positions`/`uvs` length is not an even multiple of 2.
/// * `indices` length is not an even multiple of 3.
/// * `colors` length is not an even multiple of 4.
/// * `handle` is invalid
#[deprecated_infallible]
pub fn draw_textured_triangles_2d(
clip_rect: &Rectangle,
handle: TextureHandle,
indices: &[u32], // vertex index triplets
positions: &[f32], // x,y interleaved
colors: &[u8], // r,g,b,a interleaved
uvs: &[f32], // u,v interleaved
);
/// Creates an SDF model from a Saft program.
///
/// These can be rendered directly or used to define bones for skinned SDF models.
/// A correct bounding box is required for now (later we will derive it from the program).
pub fn create_sdf_model(
opcodes: &[u32],
constants: &[f32],
bounding_box: &BoundingBox,
) -> FFIResult<u64>;
/// Destroys an SDF model.
#[deprecated_infallible]
pub fn destroy_sdf_model(sdf: u64);
/// Draws 1 or more instances of an SDF model.
///
/// Bounding boxes are only looked at if you pass in modified `constants`.
/// To keep the old constants, just pass in an empty slice.
#[deprecated_infallible]
pub fn draw_sdf_model(
sdf: SdfHandle,
instances: &[SdfInstanceData],
constants: &[f32],
bounding_boxes: &[BoundingBox],
);
}
}
pub use render::*;