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;