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 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
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 [`safe_v0::destroy_texture`] that has already been destroyed.
/// * Dropping the [`TextureHandle`] before calling [`safe_v0::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::{safe_v0, TextureHandle};
///
/// pub struct Texture {
/// handle: std::rc::Rc<TextureHandle>,
/// }
///
/// impl Texture {
/// // Pass the handle retrieved from `safe_v0::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 `safe_v0::destroy_texture` yourself, let `Drop` call it.
/// fn drop(&mut self) {
/// if std::rc::Rc::strong_count(&self.handle) == 1 {
/// safe_v0::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::{safe_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,
/// };
///
/// let texture = safe_v0::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::safe as safe_v0;
#[cfg(not(target_arch = "wasm32"))]
pub use render::HostShim as HostShim_v0;