kitgit

tirbofish/dropbear · commit

31165a80c92ec191e1959bde608471d3b0355dcd

wip: eucalyptus-core/scene.rs is the only one not done, all bindings completed

Unverified

Thribhu K <4tkbytes@pm.me> · 2026-02-15 09:50

view full diff

diff --git a/crates/dropbear-engine/src/asset.rs b/crates/dropbear-engine/src/asset.rs
index acdc1f1..bd7821a 100644
--- a/crates/dropbear-engine/src/asset.rs
+++ b/crates/dropbear-engine/src/asset.rs
@@ -66,6 +66,7 @@ pub struct AssetRegistry {
     model_labels: HashMap<String, Handle<Model>>,
 }
 
+#[repr(C)]
 #[derive(Debug, Clone)]
 #[dropbear_macro::repr_c_enum]
 pub enum AssetKind {
@@ -203,6 +204,10 @@ impl AssetRegistry {
         
         self.add_texture_with_label(label, texture)
     }
+
+    pub fn get_label_from_texture_handle(&self, handle: Handle<Texture>) -> Option<String> {
+        self.texture_labels.iter().find_map(|(label, h)| if *h == handle { Some(label.clone()) } else { None })
+    }
 }
 
 /// Model stuff
diff --git a/crates/dropbear-engine/src/entity.rs b/crates/dropbear-engine/src/entity.rs
index 72f7486..61621c0 100644
--- a/crates/dropbear-engine/src/entity.rs
+++ b/crates/dropbear-engine/src/entity.rs
@@ -225,6 +225,10 @@ impl MeshRenderer {
         self.import_scale = scale;
     }
 
+    pub fn import_scale(&self) -> f32 {
+        self.import_scale
+    }
+
     pub fn set_model(&mut self, model: Handle<Model>) {
         self.handle = model;
     }
@@ -237,6 +241,10 @@ impl MeshRenderer {
         self.texture_override = Some(texture);
     }
 
+    pub fn texture_override(&self) -> Option<Handle<Texture>> {
+        self.texture_override
+    }
+
     pub fn is_texture_attached(&self, texture: Handle<Texture>) -> bool {
         let registry = ASSET_REGISTRY.read();
         
diff --git a/crates/dropbear-macro/src/lib.rs b/crates/dropbear-macro/src/lib.rs
index 3c3c5c6..13fa41b 100644
--- a/crates/dropbear-macro/src/lib.rs
+++ b/crates/dropbear-macro/src/lib.rs
@@ -7,6 +7,7 @@ use syn::{
     DeriveInput, FnArg, GenericArgument, Ident, Item, ItemFn, ItemMod, ItemEnum, LitStr, PathArguments,
     ReturnType, Token, Type,
 };
+use std::path::Path;
 
 /// A `derive` macro that converts a struct to a usable [SerializableComponent].
 ///
@@ -550,7 +551,10 @@ fn build_c_wrapper(
     option_inner: Option<&Type>,
     c_args: CArgs,
 ) -> proc_macro2::TokenStream {
-    let c_name = c_args.name.unwrap_or_else(|| default_c_name(original_name));
+    let module_path = module_path_from_callsite();
+    let c_name = c_args
+        .name
+        .unwrap_or_else(|| default_c_name(original_name, module_path.as_deref()));
     let c_ident = Ident::new(&c_name, original_name.span());
 
     let mut wrapper_inputs = Vec::new();
@@ -893,9 +897,38 @@ fn build_jni_return(
             let (sig, wrapper, jvalue_expr) = jni_boxing_info(inner, jni_path);
             let body = quote! {
                 match #inner_name(#(#call_args),*) {
-                    crate::scripting::result::DropbearNativeResult::Ok(val) => {
-                        crate::return_boxed!(&mut env, val.map(|v| #jvalue_expr), #sig, #wrapper)
-                    }
+                    crate::scripting::result::DropbearNativeResult::Ok(val) => match val {
+                        Some(v) => {
+                            let cls = match env.find_class(#wrapper) {
+                                Ok(cls) => cls,
+                                Err(e) => {
+                                    eprintln!("return_boxed failed for {}: {:?}", #wrapper, e);
+                                    let _ = env.throw_new("java/lang/RuntimeException", format!("Boxing failed: {:?}", e));
+                                    return std::ptr::null_mut();
+                                }
+                            };
+
+                            let param: #jni_path::objects::JValue = #jvalue_expr;
+                            let ret = match env.call_static_method(cls, "valueOf", #sig, &[param]) {
+                                Ok(ret) => ret,
+                                Err(e) => {
+                                    eprintln!("return_boxed failed for {}: {:?}", #wrapper, e);
+                                    let _ = env.throw_new("java/lang/RuntimeException", format!("Boxing failed: {:?}", e));
+                                    return std::ptr::null_mut();
+                                }
+                            };
+
+                            match ret.l() {
+                                Ok(obj) => obj.into_raw(),
+                                Err(e) => {
+                                    eprintln!("return_boxed failed for {}: {:?}", #wrapper, e);
+                                    let _ = env.throw_new("java/lang/RuntimeException", format!("Boxing failed: {:?}", e));
+                                    std::ptr::null_mut()
+                                }
+                            }
+                        }
+                        None => std::ptr::null_mut(),
+                    },
                     crate::scripting::result::DropbearNativeResult::Err(e) => {
                         let _ = env.throw_new("java/lang/RuntimeException", format!("JNI call failed: {:?}", e));
                         std::ptr::null_mut()
@@ -1157,8 +1190,50 @@ fn jni_boxing_info(
     )
 }
 
-fn default_c_name(original_name: &Ident) -> String {
-    format!("dropbear_{}", original_name)
+fn module_path_from_callsite() -> Option<String> {
+    let path = proc_macro::Span::call_site().local_file()?;
+    // proc_macro::Span::call_site().source_text()
+    module_path_from_file(&path)
+}
+
+fn module_path_from_file(path: &Path) -> Option<String> {
+    let components: Vec<String> = path
+        .components()
+        .map(|c| c.as_os_str().to_string_lossy().to_string())
+        .collect();
+
+    let src_index = components.iter().position(|c| c == "src")?;
+    if src_index + 1 >= components.len() {
+        return None;
+    }
+
+    let mut parts = components[src_index + 1..].to_vec();
+    let file = parts.pop()?;
+    let stem = file.strip_suffix(".rs").unwrap_or(&file);
+
+    if stem != "mod" && stem != "lib" && stem != "main" {
+        parts.push(stem.to_string());
+    }
+
+    if parts.is_empty() {
+        return None;
+    }
+
+    let joined = parts.join("_");
+    Some(joined.replace('-', "_"))
+}
+
+fn default_c_name(original_name: &Ident, module_path: Option<&str>) -> String {
+    let mut name = String::from("dropbear");
+    if let Some(path) = module_path {
+        if !path.is_empty() {
+            name.push('_');
+            name.push_str(path);
+        }
+    }
+    name.push('_');
+    name.push_str(&original_name.to_string());
+    name
 }
 
 fn is_string_type(ty: &Type) -> bool {
diff --git a/crates/eucalyptus-core/Cargo.toml b/crates/eucalyptus-core/Cargo.toml
index f66f554..c16be97 100644
--- a/crates/eucalyptus-core/Cargo.toml
+++ b/crates/eucalyptus-core/Cargo.toml
@@ -61,5 +61,4 @@ jvm_debug = ["jvm"]
 [build-dependencies]
 anyhow.workspace = true
 app_dirs2.workspace = true
-cbindgen.workspace = true
-syn = { workspace = true, features = ["full"] }
+goanna-gen = { path = "../goanna-gen" }
diff --git a/crates/eucalyptus-core/build.rs b/crates/eucalyptus-core/build.rs
index 6bf41b4..3cfe003 100644
--- a/crates/eucalyptus-core/build.rs
+++ b/crates/eucalyptus-core/build.rs
@@ -6,798 +6,10 @@ fn main() -> anyhow::Result<()> {
         println!("cargo:rustc-link-arg=/NODEFAULTLIB:libcmt.lib");
     }
 
-    generate_c_header()?;
+    goanna_gen::generate_c_header()?;
 
     println!("cargo:rerun-if-changed=build.rs");
     println!("cargo:rerun-if-changed=src");
     println!("cargo:rerun-if-changed=../../include/dropbear.h");
     Ok(())
-}
-
-fn generate_c_header() -> anyhow::Result<()> {
-    let manifest_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);
-    let workspace_root = manifest_dir
-        .parent()
-        .and_then(|p| p.parent())
-        .ok_or_else(|| anyhow::anyhow!("Failed to locate workspace root"))?;
-
-    let output_path = workspace_root.join("include").join("dropbear.h");
-    if let Some(parent) = output_path.parent() {
-        std::fs::create_dir_all(parent)?;
-    }
-
-    let src_dir = manifest_dir.join("src");
-    let mut functions = Vec::new();
-    let mut structs = std::collections::HashMap::new();
-    let mut enums = Vec::new();
-    collect_exported_functions(&src_dir, &mut functions, &mut structs, &mut enums)?;
-
-    let header = render_header(&functions, &structs, &enums);
-    std::fs::write(&output_path, header)?;
-
-    Ok(())
-}
-
-#[derive(Debug)]
-struct ExportedFunction {
-    name: String,
-    params: Vec<ExportParam>,
-    out_type: Option<String>,
-}
-
-#[derive(Debug, Clone)]
-struct ExportParam {
-    name: String,
-    ty: String,
-}
-
-fn collect_exported_functions(
-    dir: &std::path::Path,
-    out: &mut Vec<ExportedFunction>,
-    structs: &mut std::collections::HashMap<String, StructDef>,
-    enums: &mut Vec<EnumDef>,
-) -> anyhow::Result<()> {
-    if dir.is_dir() {
-        for entry in std::fs::read_dir(dir)? {
-            let entry = entry?;
-            let path = entry.path();
-            if path.is_dir() {
-                collect_exported_functions(&path, out, structs, enums)?;
-            } else if path.extension().and_then(|s| s.to_str()) == Some("rs") {
-                let content = std::fs::read_to_string(&path)?;
-                let file = syn::parse_file(&content)?;
-                extract_exports_from_file(&file, out, structs)?;
-                extract_structs_from_file(&file, structs)?;
-                extract_repr_c_enums_from_file(&file, enums)?;
-            }
-        }
-    }
-    Ok(())
-}
-
-fn extract_exports_from_file(
-    file: &syn::File,
-    out: &mut Vec<ExportedFunction>,
-    _structs: &mut std::collections::HashMap<String, StructDef>,
-) -> anyhow::Result<()> {
-    for item in &file.items {
-        if let syn::Item::Fn(func) = item {
-            if let Some(export) = parse_export_attr(&func.attrs)? {
-                if export.c.is_none() {
-                    continue;
-                }
-
-                let c_name = export.c.and_then(|c| c.name).unwrap_or_else(|| {
-                    format!("dropbear_{}", func.sig.ident)
-                });
-
-                let (out_type, out_is_optional) = extract_result_type(&func.sig.output)?;
-                let mut params = Vec::new();
-                for input in &func.sig.inputs {
-                    if let syn::FnArg::Typed(pat_ty) = input {
-                        let (define_ty, is_entity) = extract_arg_markers(&pat_ty.attrs);
-                        let name = match &*pat_ty.pat {
-                            syn::Pat::Ident(ident) => ident.ident.to_string(),
-                            _ => continue,
-                        };
-
-                        let ty = if is_entity {
-                            "uint64_t".to_string()
-                        } else if let Some(define_ty) = define_ty {
-                            type_to_c(&define_ty, true)
-                        } else if is_object_input(&pat_ty.ty) {
-                            object_input_to_c(&pat_ty.ty)
-                        } else {
-                            type_to_c(&pat_ty.ty, false)
-                        };
-
-                        params.push(ExportParam { name, ty });
-                    }
-                }
-
-                if out_type.is_some() {
-                    let out_ty = out_type.clone().unwrap();
-                    params.push(ExportParam { name: "out0".to_string(), ty: format!("{}*", out_ty) });
-                    if out_is_optional {
-                        params.push(ExportParam { name: "out0_present".to_string(), ty: "bool*".to_string() });
-                    }
-                }
-
-                out.push(ExportedFunction {
-                    name: c_name,
-                    params,
-                    out_type,
-                });
-            }
-        }
-    }
-    Ok(())
-}
-
-#[derive(Debug, Clone)]
-struct StructDef {
-    name: String,
-    fields: Vec<ExportParam>,
-    is_repr_c: bool,
-}
-
-#[derive(Debug)]
-struct EnumDef {
-    name: String,
-    variants: Vec<EnumVariantDef>,
-}
-
-#[derive(Debug)]
-struct EnumVariantDef {
-    name: String,
-    fields: Vec<ExportParam>,
-}
-
-fn extract_repr_c_enums_from_file(
-    file: &syn::File,
-    enums: &mut Vec<EnumDef>,
-) -> anyhow::Result<()> {
-    for item in &file.items {
-        if let syn::Item::Enum(enm) = item {
-            if !has_repr_c_enum_attr(&enm.attrs) {
-                continue;
-            }
-            let mut variants = Vec::new();
-            for variant in &enm.variants {
-                let mut fields = Vec::new();
-                match &variant.fields {
-                    syn::Fields::Named(named) => {
-                        for field in &named.named {
-                            let name = field
-                                .ident
-                                .as_ref()
-                                .map(|i| i.to_string())
-                                .unwrap_or_else(|| "field".to_string());
-                            let ty = type_to_c(&field.ty, false);
-                            fields.push(ExportParam { name, ty });
-                        }
-                    }
-                    syn::Fields::Unnamed(unnamed) => {
-                        for (idx, field) in unnamed.unnamed.iter().enumerate() {
-                            let name = format!("_{}", idx);
-                            let ty = type_to_c(&field.ty, false);
-                            fields.push(ExportParam { name, ty });
-                        }
-                    }
-                    syn::Fields::Unit => {}
-                }
-                variants.push(EnumVariantDef { name: variant.ident.to_string(), fields });
-            }
-            enums.push(EnumDef { name: enm.ident.to_string(), variants });
-        }
-    }
-    Ok(())
-}
-
-fn extract_structs_from_file(
-    file: &syn::File,
-    structs: &mut std::collections::HashMap<String, StructDef>,
-) -> anyhow::Result<()> {
-    for item in &file.items {
-        if let syn::Item::Struct(strct) = item {
-            let name = strct.ident.to_string();
-            let is_repr_c = has_repr_c(&strct.attrs);
-            let mut fields = Vec::new();
-
-            if let syn::Fields::Named(named) = &strct.fields {
-                for field in &named.named {
-                    let field_name = field.ident.as_ref().map(|i| i.to_string()).unwrap_or_else(|| "field".to_string());
-                    let ty = type_to_c(&field.ty, false);
-                    fields.push(ExportParam { name: field_name, ty });
-                }
-            }
-
-            structs.insert(name.clone(), StructDef { name, fields, is_repr_c });
-        }
-    }
-
-    Ok(())
-}
-
-#[derive(Default)]
-struct ExportAttr {
-    c: Option<CArgs>,
-}
-
-#[derive(Default)]
-struct CArgs {
-    name: Option<String>,
-}
-
-fn parse_export_attr(attrs: &[syn::Attribute]) -> anyhow::Result<Option<ExportAttr>> {
-    for attr in attrs {
-        let path = attr.path();
-        let is_export = path.segments.last().map(|s| s.ident == "export").unwrap_or(false)
-            || (path.segments.iter().any(|s| s.ident == "dropbear_macro")
-                && path.segments.last().map(|s| s.ident == "export").unwrap_or(false));
-        if !is_export {
-            continue;
-        }
-
-        let meta = &attr.meta;
-        if let syn::Meta::List(list) = meta {
-            let args = syn::parse2::<ExportArgs>(list.tokens.clone())?;
-            return Ok(Some(ExportAttr { c: args.c }));
-        }
-    }
-
-    Ok(None)
-}
-
-struct ExportArgs {
-    c: Option<CArgs>,
-}
-
-impl syn::parse::Parse for ExportArgs {
-    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
-        let items = syn::punctuated::Punctuated::<ExportItem, syn::Token![,]>::parse_terminated(input)?;
-        let mut args = ExportArgs { c: None };
-
-        for item in items {
-            if let ExportItem::C(c) = item {
-                args.c = Some(c);
-            }
-        }
-
-        Ok(args)
-    }
-}
-
-enum ExportItem {
-    C(CArgs),
-    Kotlin,
-}
-
-impl syn::parse::Parse for ExportItem {
-    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
-        let ident: syn::Ident = input.parse()?;
-        if ident == "c" {
-            let args = if input.peek(syn::token::Paren) {
-                let content;
-                syn::parenthesized!(content in input);
-                let mut c_args = CArgs::default();
-                while !content.is_empty() {
-                    let key: syn::Ident = content.parse()?;
-                    content.parse::<syn::Token![=]>()?;
-                    let value: syn::LitStr = content.parse()?;
-                    if key == "name" {
-                        c_args.name = Some(value.value());
-                    }
-                    if content.peek(syn::Token![,]) {
-                        content.parse::<syn::Token![,]>()?;
-                    }
-                }
-                c_args
-            } else {
-                CArgs::default()
-            };
-            return Ok(ExportItem::C(args));
-        }
-
-        if ident == "kotlin" {
-            if input.peek(syn::token::Paren) {
-                let content;
-                syn::parenthesized!(content in input);
-                while !content.is_empty() {
-                    let _key: syn::Ident = content.parse()?;
-                    content.parse::<syn::Token![=]>()?;
-                    let _value: syn::LitStr = content.parse()?;
-                    if content.peek(syn::Token![,]) {
-                        content.parse::<syn::Token![,]>()?;
-                    }
-                }
-            }
-            return Ok(ExportItem::Kotlin);
-        }
-
-        Err(syn::Error::new(ident.span(), "Expected c or kotlin"))
-    }
-}
-
-fn extract_arg_markers(attrs: &[syn::Attribute]) -> (Option<syn::Type>, bool) {
-    let mut define_ty: Option<syn::Type> = None;
-    let mut is_entity = false;
-    for attr in attrs {
-        let path = attr.path();
-        let ident = path.segments.last().map(|s| s.ident.to_string()).unwrap_or_default();
-        if ident == "define" {
-            if let Ok(ty) = attr.parse_args::<syn::Type>() {
-                define_ty = Some(ty);
-            }
-        }
-        if ident == "entity" {
-            is_entity = true;
-        }
-    }
-    (define_ty, is_entity)
-}
-
-fn extract_result_type(output: &syn::ReturnType) -> anyhow::Result<(Option<String>, bool)> {
-    let ty = match output {
-        syn::ReturnType::Type(_, ty) => ty,
-        syn::ReturnType::Default => return Ok((None, false)),
-    };
-
-    let inner = match &**ty {
-        syn::Type::Path(path) => {
-            let last = path.path.segments.last().ok_or_else(|| anyhow::anyhow!("Invalid return type"))?;
-            if last.ident != "DropbearNativeResult" {
-                return Ok((None, false));
-            }
-            match &last.arguments {
-                syn::PathArguments::AngleBracketed(args) => args.args.first().and_then(|a| match a {
-                    syn::GenericArgument::Type(t) => Some(t.clone()),
-                    _ => None,
-                }).ok_or_else(|| anyhow::anyhow!("DropbearNativeResult missing type"))?,
-                _ => return Ok((None, false)),
-            }
-        }
-        _ => return Ok((None, false)),
-    };
-
-    if is_unit_type(&inner) {
-        return Ok((None, false));
-    }
-
-    if let Some(opt_inner) = extract_option_inner(&inner) {
-        return Ok((Some(type_to_c(&opt_inner, true)), true));
-    }
-
-    Ok((Some(type_to_c(&inner, true)), false))
-}
-
-fn extract_option_inner(ty: &syn::Type) -> Option<syn::Type> {
-    if let syn::Type::Path(path) = ty {
-        let last = path.path.segments.last()?;
-        if last.ident != "Option" {
-            return None;
-        }
-        if let syn::PathArguments::AngleBracketed(args) = &last.arguments {
-            if let Some(syn::GenericArgument::Type(inner)) = args.args.first() {
-                return Some(inner.clone());
-            }
-        }
-    }
-    None
-}
-
-fn is_unit_type(ty: &syn::Type) -> bool {
-    matches!(ty, syn::Type::Tuple(tuple) if tuple.elems.is_empty())
-}
-
-fn type_to_c(ty: &syn::Type, for_output: bool) -> String {
-    if let syn::Type::Reference(reference) = ty {
-        let inner = type_to_c(&reference.elem, for_output);
-        let mutability = if reference.mutability.is_some() { "" } else { "const " };
-        return format!("{}{}*", mutability, inner);
-    }
-    if let Some(inner) = vec_inner_type(ty) {
-        return array_struct_name_from_type(&inner);
-    }
-
-    if let syn::Type::Path(path) = ty {
-        let ident = path.path.segments.last().map(|s| s.ident.to_string()).unwrap_or_else(|| "void".to_string());
-        return match ident.as_str() {
-            "i8" => "int8_t".to_string(),
-            "u8" => "uint8_t".to_string(),
-            "i16" => "int16_t".to_string(),
-            "u16" => "uint16_t".to_string(),
-            "i32" => "int32_t".to_string(),
-            "u32" => "uint32_t".to_string(),
-            "i64" => "int64_t".to_string(),
-            "u64" => "uint64_t".to_string(),
-            "isize" => "intptr_t".to_string(),
-            "usize" => "size_t".to_string(),
-            "f32" => "float".to_string(),
-            "f64" => "double".to_string(),
-            "bool" => "bool".to_string(),
-            "String" => {
-                if for_output {
-                    "char*".to_string()
-                } else {
-                    "const char*".to_string()
-                }
-            }
-            _ => ident,
-        };
-    }
-
-    "void".to_string()
-}
-
-fn vec_inner_type(ty: &syn::Type) -> Option<syn::Type> {
-    if let syn::Type::Path(path) = ty {
-        let last = path.path.segments.last()?;
-        if last.ident != "Vec" {
-            return None;
-        }
-        if let syn::PathArguments::AngleBracketed(args) = &last.arguments {
-            if let Some(syn::GenericArgument::Type(inner)) = args.args.first() {
-                return Some(inner.clone());
-            }
-        }
-    }
-    None
-}
-
-fn type_name_from_type(ty: &syn::Type) -> Option<String> {
-    if let syn::Type::Path(path) = ty {
-        return path.path.segments.last().map(|s| s.ident.to_string());
-    }
-    None
-}
-
-fn array_struct_name_from_type(ty: &syn::Type) -> String {
-    if let Some(inner) = vec_inner_type(ty) {
-        let inner_name = array_struct_name_from_type(&inner);
-        return format!("{}ArrayFfi", inner_name);
-    }
-
-    let name = type_name_from_type(ty).unwrap_or_else(|| "Unknown".to_string());
-    format!("{}ArrayFfi", name)
-}
-
-fn render_header(
-    funcs: &[ExportedFunction],
-    structs: &std::collections::HashMap<String, StructDef>,
-    enums: &[EnumDef],
-) -> String {
-    let mut out = String::new();
-    out.push_str("#ifndef DROPBEAR_H\n");
-    out.push_str("#define DROPBEAR_H\n\n");
-    out.push_str("#include <stdbool.h>\n");
-    out.push_str("#include <stdint.h>\n\n");
-    out.push_str("#include <stddef.h>\n\n");
-
-    let mut emitted_arrays = std::collections::HashSet::new();
-    for enm in enums {
-        emit_repr_c_enum(enm, &mut emitted_arrays, &mut out);
-    }
-
-    let mut needed = std::collections::HashSet::new();
-    for func in funcs {
-        if let Some(out_ty) = &func.out_type {
-            if is_custom_type(out_ty) {
-                needed.insert(out_ty.clone());
-            } else if is_opaque_ptr_name(out_ty) {
-                needed.insert(out_ty.clone());
-            }
-        }
-        for param in &func.params {
-            if is_opaque_ptr_name(&param.ty) {
-                needed.insert(param.ty.clone());
-            }
-            if let Some(base) = base_type_name(&param.ty) {
-                if is_custom_type(&base) {
-                    needed.insert(base);
-                } else if is_opaque_ptr_name(&base) {
-                    needed.insert(base);
-                }
-            }
-        }
-    }
-
-    let mut emitted = std::collections::HashSet::new();
-    for ty in needed {
-        emit_structs_recursive(&ty, structs, &mut emitted, &mut out);
-    }
-
-    for func in funcs {
-        let params = if func.params.is_empty() {
-            "void".to_string()
-        } else {
-            func.params.iter()
-                .map(|p| format!("{} {}", p.ty, p.name))
-                .collect::<Vec<_>>()
-                .join(", ")
-        };
-        out.push_str(&format!("int32_t {}({});\n", func.name, params));
-    }
-
-    out.push_str("\n#endif /* DROPBEAR_H */\n");
-    out
-}
-
-fn emit_repr_c_enum(
-    enm: &EnumDef,
-    emitted_arrays: &mut std::collections::HashSet<String>,
-    out: &mut String,
-) {
-    let tag_name = format!("{}Tag", enm.name);
-    let data_name = format!("{}Data", enm.name);
-    let ffi_name = format!("{}Ffi", enm.name);
-
-    for var in &enm.variants {
-        for field in &var.fields {
-            if is_array_type(&field.ty) {
-                emit_array_struct(&field.ty, &std::collections::HashMap::new(), emitted_arrays, out);
-            }
-        }
-    }
-
-    out.push_str(&format!("typedef enum {} {{\n", tag_name));
-    for (idx, var) in enm.variants.iter().enumerate() {
-        out.push_str(&format!("    {}_{} = {},\n", tag_name, var.name, idx));
-    }
-    out.push_str(&format!("}} {};\n\n", tag_name));
-
-    for var in &enm.variants {
-        let struct_name = format!("{}{}", enm.name, var.name);
-        out.push_str(&format!("typedef struct {} {{\n", struct_name));
-        for field in &var.fields {
-            out.push_str(&format!("    {} {};\n", field.ty, field.name));
-        }
-        out.push_str(&format!("}} {};\n\n", struct_name));
-    }
-
-    out.push_str(&format!("typedef union {} {{\n", data_name));
-    for var in &enm.variants {
-        let struct_name = format!("{}{}", enm.name, var.name);
-        out.push_str(&format!("    {} {};\n", struct_name, var.name));
-    }
-    out.push_str(&format!("}} {};\n\n", data_name));
-
-    out.push_str(&format!("typedef struct {} {{\n", ffi_name));
-    out.push_str(&format!("    {} tag;\n", tag_name));
-    out.push_str(&format!("    {} data;\n", data_name));
-    out.push_str(&format!("}} {};\n\n", ffi_name));
-}
-
-fn has_repr_c_enum_attr(attrs: &[syn::Attribute]) -> bool {
-    for attr in attrs {
-        let path = attr.path();
-        if path.segments.last().map(|s| s.ident == "repr_c_enum").unwrap_or(false) {
-            return true;
-        }
-        if path.segments.iter().any(|s| s.ident == "dropbear_macro")
-            && path.segments.last().map(|s| s.ident == "repr_c_enum").unwrap_or(false)
-        {
-            return true;
-        }
-    }
-    false
-}
-
-fn has_repr_c(attrs: &[syn::Attribute]) -> bool {
-    for attr in attrs {
-        if !attr.path().is_ident("repr") {
-            continue;
-        }
-        if let syn::Meta::List(list) = &attr.meta {
-            let tokens = list.tokens.to_string();
-            if tokens.contains("C") || tokens.contains("transparent") {
-                return true;
-            }
-        }
-    }
-    false
-}
-
-fn is_custom_type(ty: &str) -> bool {
-    if ty.ends_with('*') {
-        return false;
-    }
-    if is_opaque_ptr_name(ty) {
-        return false;
-    }
-    !is_builtin_c_type(ty)
-}
-
-fn base_type_name(ty: &str) -> Option<String> {
-    let mut t = ty.trim().to_string();
-    if t.starts_with("const ") {
-        t = t.trim_start_matches("const ").trim().to_string();
-    }
-    if t.ends_with('*') {
-        t = t.trim_end_matches('*').trim().to_string();
-        return Some(t);
-    }
-    None
-}
-
-fn is_object_input(ty: &syn::Type) -> bool {
-    let inner = peel_reference(ty);
-    if is_string_type(inner) || is_primitive_type(inner) {
-        return false;
-    }
-    !is_define_or_entity_like(inner)
-}
-
-fn object_input_to_c(ty: &syn::Type) -> String {
-    match ty {
-        syn::Type::Reference(reference) => {
-            let inner = type_to_c(&reference.elem, false);
-            let mutability = if reference.mutability.is_some() { "" } else { "const " };
-            format!("{}{}*", mutability, inner)
-        }
-        _ => type_to_c(ty, false),
-    }
-}
-
-fn is_define_or_entity_like(_ty: &syn::Type) -> bool {
-    false
-}
-
-fn is_string_type(ty: &syn::Type) -> bool {
-    matches!(ty, syn::Type::Path(path) if path.path.segments.last().map(|s| s.ident == "String").unwrap_or(false))
-}
-
-fn is_primitive_type(ty: &syn::Type) -> bool {
-    matches!(ty, syn::Type::Path(path) if {
-        let ident = path.path.segments.last().map(|s| s.ident.to_string()).unwrap_or_default();
-        matches!(
-            ident.as_str(),
-            "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" |
-            "isize" | "usize" | "f32" | "f64" | "bool"
-        )
-    })
-}
-
-fn peel_reference<'a>(ty: &'a syn::Type) -> &'a syn::Type {
-    if let syn::Type::Reference(reference) = ty {
-        &reference.elem
-    } else {
-        ty
-    }
-}
-
-fn is_builtin_c_type(ty: &str) -> bool {
-    matches!(
-        ty,
-        "int8_t" | "uint8_t" | "int16_t" | "uint16_t" | "int32_t" | "uint32_t" |
-        "int64_t" | "uint64_t" | "intptr_t" | "uintptr_t" | "bool" | "float" |
-        "double" | "char" | "char*" | "const char*" | "void*"
-    )
-}
-
-fn is_opaque_ptr_name(name: &str) -> bool {
-    name.ends_with("Ptr")
-}
-
-fn emit_structs_recursive(
-    name: &str,
-    structs: &std::collections::HashMap<String, StructDef>,
-    emitted: &mut std::collections::HashSet<String>,
-    out: &mut String,
-) {
-    if emitted.contains(name) {
-        return;
-    }
-
-    if is_array_type(name) {
-        emit_array_struct(name, structs, emitted, out);
-        return;
-    }
-
-    if is_opaque_ptr_name(name) {
-        out.push_str(&format!("typedef void* {};\n\n", name));
-        emitted.insert(name.to_string());
-        return;
-    }
-
-    if let Some(def) = structs.get(name) {
-        for field in &def.fields {
-            if is_array_type(&field.ty) {
-                emit_array_struct(&field.ty, structs, emitted, out);
-                continue;
-            }
-            if is_custom_type(&field.ty) {
-                emit_structs_recursive(&field.ty, structs, emitted, out);
-            }
-        }
-
-        if !def.is_repr_c {
-            out.push_str("// NOTE: type is not #[repr(C)] in Rust; ensure C ABI safety.\n");
-        }
-
-        out.push_str(&format!("typedef struct {} {{\n", def.name));
-        for field in &def.fields {
-            out.push_str(&format!("    {} {};\n", field.ty, field.name));
-        }
-        out.push_str(&format!("}} {};\n\n", def.name));
-        emitted.insert(def.name.clone());
-    } else {
-        out.push_str(&format!("typedef struct {} {};// opaque\n\n", name, name));
-        emitted.insert(name.to_string());
-    }
-}
-
-fn is_array_type(ty: &str) -> bool {
-    ty.ends_with("ArrayFfi")
-}
-
-fn emit_array_struct(
-    name: &str,
-    structs: &std::collections::HashMap<String, StructDef>,
-    emitted: &mut std::collections::HashSet<String>,
-    out: &mut String,
-) {
-    if emitted.contains(name) {
-        return;
-    }
-    let elem = name.trim_end_matches("ArrayFfi");
-
-    if is_array_type(elem) {
-        emit_array_struct(elem, structs, emitted, out);
-    }
-
-    if is_builtin_rust_primitive(elem) {
-        let c_elem = map_primitive_name(elem);
-        if !emitted.contains(elem) {
-            emitted.insert(elem.to_string());
-        }
-        out.push_str(&format!("typedef struct {} {{\n", name));
-        out.push_str(&format!("    const {}* ptr;\n", c_elem));
-        out.push_str("    size_t len;\n");
-        out.push_str(&format!("}} {};\n\n", name));
-        emitted.insert(name.to_string());
-        return;
-    }
-
-    if !emitted.contains(elem) {
-        if structs.contains_key(elem) {
-            emit_structs_recursive(elem, structs, emitted, out);
-        } else {
-            out.push_str(&format!("typedef struct {} {};// opaque\n\n", elem, elem));
-            emitted.insert(elem.to_string());
-        }
-    }
-
-    out.push_str(&format!("typedef struct {} {{\n", name));
-    out.push_str(&format!("    const {}* ptr;\n", elem));
-    out.push_str("    size_t len;\n");
-    out.push_str(&format!("}} {};\n\n", name));
-    emitted.insert(name.to_string());
-}
-
-fn is_builtin_rust_primitive(name: &str) -> bool {
-    matches!(
-        name,
-        "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" |
-        "isize" | "usize" | "f32" | "f64" | "bool"
-    )
-}
-
-fn map_primitive_name(name: &str) -> &'static str {
-    match name {
-        "i8" => "int8_t",
-        "u8" => "uint8_t",
-        "i16" => "int16_t",
-        "u16" => "uint16_t",
-        "i32" => "int32_t",
-        "u32" => "uint32_t",
-        "i64" => "int64_t",
-        "u64" => "uint64_t",
-        "isize" => "intptr_t",
-        "usize" => "size_t",
-        "f32" => "float",
-        "f64" => "double",
-        "bool" => "bool",
-        _ => "void",
-    }
-}
+}
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/asset/mod.rs b/crates/eucalyptus-core/src/asset/mod.rs
index f244e30..dabf4a4 100644
--- a/crates/eucalyptus-core/src/asset/mod.rs
+++ b/crates/eucalyptus-core/src/asset/mod.rs
@@ -1,8 +1,12 @@
 pub mod texture;
 pub mod model;
 
+use jni::JNIEnv;
+use jni::objects::JObject;
 use dropbear_engine::asset::AssetKind;
 use crate::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped};
+use crate::scripting::jni::utils::{FromJObject};
+use crate::scripting::native::DropbearNativeError;
 use crate::scripting::result::DropbearNativeResult;
 
 #[dropbear_macro::export(
@@ -34,4 +38,21 @@ fn dropbear_asset_get_asset(
             }
         }
     }
+}
+
+impl FromJObject for AssetKind {
+    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self>
+    where
+        Self: Sized
+    {
+        let ordinal = env
+            .call_method(obj, "ordinal", "()I", &[])?
+            .i()?;
+
+        match ordinal {
+            0 => Ok(AssetKind::Texture),
+            1 => Ok(AssetKind::Model),
+            _ => Err(DropbearNativeError::InvalidEnumOrdinal)
+        }
+    }
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/asset/model.rs b/crates/eucalyptus-core/src/asset/model.rs
index 0a1a527..aea4fba 100644
--- a/crates/eucalyptus-core/src/asset/model.rs
+++ b/crates/eucalyptus-core/src/asset/model.rs
@@ -1,3 +1,6 @@
+use jni::JNIEnv;
+use jni::objects::{JObject, JValue};
+use jni::sys::{jdouble, jint};
 use crate::scripting::native::DropbearNativeError;
 use crate::scripting::result::DropbearNativeResult;
 use crate::types::{NQuaternion, NVector2, NVector3, NVector4};
@@ -5,6 +8,7 @@ use dropbear_engine::asset::Handle;
 use dropbear_engine::model::{Animation, AnimationChannel, AnimationInterpolation, ChannelValues, Material, Mesh, ModelVertex, Node, NodeTransform, Skin};
 use dropbear_engine::texture::Texture;
 use crate::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped};
+use crate::scripting::jni::utils::ToJObject;
 
 #[repr(C)]
 #[derive(Clone, Debug)]
@@ -19,6 +23,43 @@ pub struct NModelVertex {
     pub weights0: NVector4,
 }
 
+impl ToJObject for NModelVertex {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env.find_class("com/dropbear/asset/model/ModelVertex")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        let position = self.position.to_jobject(env)?;
+        let normal = self.normal.to_jobject(env)?;
+        let tangent = self.tangent.to_jobject(env)?;
+        let tex_coords0 = self.tex_coords0.to_jobject(env)?;
+        let tex_coords1 = self.tex_coords1.to_jobject(env)?;
+        let colour0 = self.colour0.to_jobject(env)?;
+        let joints0 = self.joints0.as_slice().to_jobject(env)?;
+        let weights0 = self.weights0.to_jobject(env)?;
+
+        let args = [
+            JValue::Object(&position),
+            JValue::Object(&normal),
+            JValue::Object(&tangent),
+            JValue::Object(&tex_coords0),
+            JValue::Object(&tex_coords1),
+            JValue::Object(&colour0),
+            JValue::Object(&joints0),
+            JValue::Object(&weights0),
+        ];
+
+        let obj = env
+            .new_object(
+                &class,
+                "(Lcom/dropbear/math/Vector3f;Lcom/dropbear/math/Vector3f;Lcom/dropbear/math/Vector4f;Lcom/dropbear/math/Vector2f;Lcom/dropbear/math/Vector2f;Lcom/dropbear/math/Vector4f;[ILcom/dropbear/math/Vector4f;)V",
+                &args,
+            )
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+
+        Ok(obj)
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Debug)]
 pub struct NMesh {
@@ -28,6 +69,34 @@ pub struct NMesh {
     pub vertices: Vec<NModelVertex>,
 }
 
+impl ToJObject for NMesh {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env.find_class("com/dropbear/asset/model/Mesh")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        let name = env.new_string(&self.name)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+        let vertices = self.vertices.to_jobject(env)?;
+
+        let args = [
+            JValue::Object(&name),
+            JValue::Int(self.num_elements),
+            JValue::Int(self.material_index),
+            JValue::Object(&vertices),
+        ];
+
+        let obj = env
+            .new_object(
+                &class,
+                "(Ljava/lang/String;IILjava/util/List;)V",
+                &args,
+            )
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+
+        Ok(obj)
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Debug)]
 pub struct NMaterial {
@@ -48,6 +117,62 @@ pub struct NMaterial {
     pub occlusion_texture: Option<u64>,
 }
 
+impl ToJObject for NMaterial {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env.find_class("com/dropbear/asset/model/Material")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        let name = env.new_string(&self.name)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+        let diffuse_texture = new_texture(env, self.diffuse_texture)?;
+        let normal_texture = new_texture(env, self.normal_texture)?;
+        let tint = self.tint.to_jobject(env)?;
+        let emissive_factor = self.emissive_factor.to_jobject(env)?;
+        let uv_tiling = self.uv_tiling.to_jobject(env)?;
+        let alpha_cutoff = self.alpha_cutoff.to_jobject(env)?;
+        let emissive_texture = match self.emissive_texture {
+            Some(id) => new_texture(env, id)?,
+            None => JObject::null(),
+        };
+        let metallic_roughness_texture = match self.metallic_roughness_texture {
+            Some(id) => new_texture(env, id)?,
+            None => JObject::null(),
+        };
+        let occlusion_texture = match self.occlusion_texture {
+            Some(id) => new_texture(env, id)?,
+            None => JObject::null(),
+        };
+
+        let args = [
+            JValue::Object(&name),
+            JValue::Object(&diffuse_texture),
+            JValue::Object(&normal_texture),
+            JValue::Object(&tint),
+            JValue::Object(&emissive_factor),
+            JValue::Double(self.metallic_factor as f64),
+            JValue::Double(self.roughness_factor as f64),
+            JValue::Object(&alpha_cutoff),
+            JValue::Bool(if self.double_sided { 1 } else { 0 }),
+            JValue::Double(self.occlusion_strength as f64),
+            JValue::Double(self.normal_scale as f64),
+            JValue::Object(&uv_tiling),
+            JValue::Object(&emissive_texture),
+            JValue::Object(&metallic_roughness_texture),
+            JValue::Object(&occlusion_texture),
+        ];
+
+        let obj = env
+            .new_object(
+                &class,
+                "(Ljava/lang/String;Lcom/dropbear/asset/Texture;Lcom/dropbear/asset/Texture;Lcom/dropbear/math/Vector4d;Lcom/dropbear/math/Vector3d;DDLjava/lang/Double;ZDDLcom/dropbear/math/Vector2d;Lcom/dropbear/asset/Texture;Lcom/dropbear/asset/Texture;Lcom/dropbear/asset/Texture;)V",
+                &args,
+            )
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+
+        Ok(obj)
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Debug)]
 pub struct NNodeTransform {
@@ -56,6 +181,33 @@ pub struct NNodeTransform {
     pub scale: NVector3,
 }
 
+impl ToJObject for NNodeTransform {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env.find_class("com/dropbear/asset/model/NodeTransform")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        let translation = self.translation.to_jobject(env)?;
+        let rotation = self.rotation.to_jobject(env)?;
+        let scale = self.scale.to_jobject(env)?;
+
+        let args = [
+            JValue::Object(&translation),
+            JValue::Object(&rotation),
+            JValue::Object(&scale),
+        ];
+
+        let obj = env
+            .new_object(
+                &class,
+                "(Lcom/dropbear/math/Vector3d;Lcom/dropbear/math/Quaterniond;Lcom/dropbear/math/Vector3d;)V",
+                &args,
+            )
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+
+        Ok(obj)
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Debug)]
 pub struct NNode {
@@ -65,6 +217,36 @@ pub struct NNode {
     pub transform: NNodeTransform,
 }
 
+impl ToJObject for NNode {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env.find_class("com/dropbear/asset/model/Node")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        let name = env.new_string(&self.name)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+        let parent = self.parent.to_jobject(env)?;
+        let children = self.children.as_slice().to_jobject(env)?;
+        let transform = self.transform.to_jobject(env)?;
+
+        let args = [
+            JValue::Object(&name),
+            JValue::Object(&parent),
+            JValue::Object(&children),
+            JValue::Object(&transform),
+        ];
+
+        let obj = env
+            .new_object(
+                &class,
+                "(Ljava/lang/String;Ljava/lang/Integer;Ljava/util/List;Lcom/dropbear/asset/model/NodeTransform;)V",
+                &args,
+            )
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+
+        Ok(obj)
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Debug)]
 pub struct NSkin {
@@ -74,6 +256,36 @@ pub struct NSkin {
     pub skeleton_root: Option<i32>,
 }
 
+impl ToJObject for NSkin {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env.find_class("com/dropbear/asset/model/Skin")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        let name = env.new_string(&self.name)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+        let joints = self.joints.as_slice().to_jobject(env)?;
+        let inverse_bind_matrices = self.inverse_bind_matrices.as_slice().to_jobject(env)?;
+        let skeleton_root = self.skeleton_root.to_jobject(env)?;
+
+        let args = [
+            JValue::Object(&name),
+            JValue::Object(&joints),
+            JValue::Object(&inverse_bind_matrices),
+            JValue::Object(&skeleton_root),
+        ];
+
+        let obj = env
+            .new_object(
+                &class,
+                "(Ljava/lang/String;Ljava/util/List;Ljava/util/List;Ljava/lang/Integer;)V",
+                &args,
+            )
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+
+        Ok(obj)
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Debug)]
 pub struct NAnimation {
@@ -82,6 +294,33 @@ pub struct NAnimation {
     pub duration: f32,
 }
 
+impl ToJObject for NAnimation {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env.find_class("com/dropbear/asset/model/Animation")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        let name = env.new_string(&self.name)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+        let channels = self.channels.to_jobject(env)?;
+
+        let args = [
+            JValue::Object(&name),
+            JValue::Object(&channels),
+            JValue::Double(self.duration as f64),
+        ];
+
+        let obj = env
+            .new_object(
+                &class,
+                "(Ljava/lang/String;Ljava/util/List;D)V",
+                &args,
+            )
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+
+        Ok(obj)
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Debug)]
 pub struct NAnimationChannel {
@@ -91,22 +330,127 @@ pub struct NAnimationChannel {
     pub interpolation: NAnimationInterpolation,
 }
 
+impl ToJObject for NAnimationChannel {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env.find_class("com/dropbear/asset/model/AnimationChannel")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        let times = self.times.as_slice().to_jobject(env)?;
+        let values = self.values.to_jobject(env)?;
+        let interpolation = self.interpolation.to_jobject(env)?;
+
+        let args = [
+            JValue::Int(self.target_node),
+            JValue::Object(&times),
+            JValue::Object(&values),
+            JValue::Object(&interpolation),
+        ];
+
+        let obj = env
+            .new_object(
+                &class,
+                "(I[DLcom/dropbear/asset/model/ChannelValues;Lcom/dropbear/asset/model/AnimationInterpolation;)V",
+                &args,
+            )
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+
+        Ok(obj)
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Debug)]
+#[dropbear_macro::repr_c_enum]
 pub enum NAnimationInterpolation {
     Linear,
     Step,
     CubicSpline,
 }
 
+impl ToJObject for NAnimationInterpolation {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env
+            .find_class("com/dropbear/asset/model/AnimationInterpolation")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        let field_name = match self {
+            NAnimationInterpolation::Linear => "LINEAR",
+            NAnimationInterpolation::Step => "STEP",
+            NAnimationInterpolation::CubicSpline => "CUBIC_SPLINE",
+        };
+
+        let value = env
+            .get_static_field(
+                class,
+                field_name,
+                "Lcom/dropbear/asset/model/AnimationInterpolation;",
+            )
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .l()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        Ok(value)
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Debug)]
+#[dropbear_macro::repr_c_enum]
 pub enum NChannelValues {
     Translations { values: Vec<NVector3> },
     Rotations { values: Vec<NQuaternion> },
     Scales { values: Vec<NVector3> },
 }
 
+impl ToJObject for NChannelValues {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        match self {
+            NChannelValues::Translations { values } => {
+                let class = env
+                    .find_class("com/dropbear/asset/model/ChannelValues$Translations")
+                    .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+                let list = values.to_jobject(env)?;
+                let obj = env
+                    .new_object(&class, "(Ljava/util/List;)V", &[JValue::Object(&list)])
+                    .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+                Ok(obj)
+            }
+            NChannelValues::Rotations { values } => {
+                let class = env
+                    .find_class("com/dropbear/asset/model/ChannelValues$Rotations")
+                    .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+                let list = values.to_jobject(env)?;
+                let obj = env
+                    .new_object(&class, "(Ljava/util/List;)V", &[JValue::Object(&list)])
+                    .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+                Ok(obj)
+            }
+            NChannelValues::Scales { values } => {
+                let class = env
+                    .find_class("com/dropbear/asset/model/ChannelValues$Scales")
+                    .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+                let list = values.to_jobject(env)?;
+                let obj = env
+                    .new_object(&class, "(Ljava/util/List;)V", &[JValue::Object(&list)])
+                    .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+                Ok(obj)
+            }
+        }
+    }
+}
+
+fn new_texture<'a>(env: &mut JNIEnv<'a>, texture_id: u64) -> DropbearNativeResult<JObject<'a>> {
+    let class = env.find_class("com/dropbear/asset/Texture")
+        .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+    env.new_object(
+        &class,
+        "(J)V",
+        &[JValue::Long(texture_id as i64)],
+    )
+    .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)
+}
+
 fn texture_handle_id(
     registry: &dropbear_engine::asset::AssetRegistry,
     texture: &Texture,
diff --git a/crates/eucalyptus-core/src/asset/texture.rs b/crates/eucalyptus-core/src/asset/texture.rs
index 0acc2e9..d8a2899 100644
--- a/crates/eucalyptus-core/src/asset/texture.rs
+++ b/crates/eucalyptus-core/src/asset/texture.rs
@@ -1,83 +1,48 @@
-pub mod shared {
-    use dropbear_engine::asset::{AssetHandle, AssetRegistry};
-    use crate::scripting::native::DropbearNativeError;
-    use crate::scripting::result::DropbearNativeResult;
+use dropbear_engine::asset::Handle;
+use crate::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped};
+use crate::scripting::native::DropbearNativeError;
+use crate::scripting::result::DropbearNativeResult;
 
-    pub fn get_texture_name(
-        asset_registry: &AssetRegistry,
-        handle: u64,
-    ) -> DropbearNativeResult<String> {
-        let texture = asset_registry
-            .get_material(AssetHandle::new(handle))
-            .ok_or_else(|| DropbearNativeError::NoSuchHandle)?;
-
-        Ok(texture.name.clone())
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.asset.TextureNative", func = "getLabel"),
+    c(name = "dropbear_asset_texture_get_label")
+)]
+fn get_texture_label(
+    #[dropbear_macro::define(AssetRegistryPtr)]
+    asset_manager: &AssetRegistryUnwrapped,
+    texture_handle: u64,
+) -> DropbearNativeResult<Option<String>> {
+    Ok(asset_manager.read().get_label_from_texture_handle(Handle::new(texture_handle)))
 }
 
-pub mod jni {
-    #![allow(non_snake_case)]
-    use jni::JNIEnv;
-    use jni::sys::{jlong, jstring};
-    use dropbear_engine::asset::AssetRegistry;
-    use jni::objects::JClass;
-    use crate::asset::texture::shared::get_texture_name;
-    use crate::scripting::native::DropbearNativeError;
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_asset_TextureHandleNative_getTextureName(
-        env: JNIEnv,
-        _class: JClass,
-        asset_registry_ptr: jlong,
-        handle: jlong,
-    ) -> jstring {
-        let asset_registry = crate::convert_ptr!(asset_registry_ptr => AssetRegistry);
-        let result = get_texture_name(asset_registry, handle as u64);
-        match result {
-            Ok(name) => {
-                let output = env.new_string(name).map_err(|_| DropbearNativeError::JNIFailedToCreateObject);
-                match output {
-                    Ok(jstr) => jstr.into_raw(),
-                    Err(e) => {
-                        crate::ffi_error_return!("[ERROR] Failed to create Java string: {}", e)
-                    }
-                }
-            }
-            Err(e) => {
-                crate::ffi_error_return!("[ERROR] {}", e)
-            }
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.asset.TextureNative", func = "getWidth"),
+    c(name = "dropbear_asset_texture_get_width")
+)]
+fn get_texture_width(
+    #[dropbear_macro::define(AssetRegistryPtr)]
+    asset_manager: &AssetRegistryUnwrapped,
+    texture_handle: u64,
+) -> DropbearNativeResult<u32> {
+    asset_manager
+        .read()
+        .get_texture(Handle::new(texture_handle))
+        .map(|v| v.size.width)
+        .ok_or(DropbearNativeError::AssetNotFound)
 }
 
-#[dropbear_macro::impl_c_api]
-pub mod native {
-    use std::ffi::CString;
-    use crate::ptr::AssetRegistryPtr;
-    use crate::scripting::native::DropbearNativeError;
-    use crate::scripting::result::DropbearNativeResult;
-    use std::ffi::c_char;
-    use dropbear_engine::asset::AssetRegistry;
-    use crate::asset::texture::shared::get_texture_name;
-
-    pub fn dropbear_get_texture_name(
-        asset_registry_ptr: AssetRegistryPtr,
-        handle: u64,
-    ) -> DropbearNativeResult<*mut c_char> {
-        let asset_registry = crate::convert_ptr!(asset_registry_ptr => AssetRegistry);
-
-        let result = get_texture_name(asset_registry, handle)
-            .map(|name| {
-                match CString::new(name) {
-                    Ok(c_str) => {
-                        Ok(c_str.into_raw())
-                    },
-                    Err(_) => {
-                        Err(DropbearNativeError::CStringError)
-                    }
-                }
-            })?;
-        
-        result
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.asset.TextureNative", func = "getHeight"),
+    c(name = "dropbear_asset_texture_get_height")
+)]
+fn get_texture_height(
+    #[dropbear_macro::define(AssetRegistryPtr)]
+    asset_manager: &AssetRegistryUnwrapped,
+    texture_handle: u64,
+) -> DropbearNativeResult<u32> {
+    asset_manager
+        .read()
+        .get_texture(Handle::new(texture_handle))
+        .map(|v| v.size.height)
+        .ok_or(DropbearNativeError::AssetNotFound)
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/camera.rs b/crates/eucalyptus-core/src/camera.rs
index 6b9d280..4bc70aa 100644
--- a/crates/eucalyptus-core/src/camera.rs
+++ b/crates/eucalyptus-core/src/camera.rs
@@ -5,6 +5,9 @@ use dropbear_engine::camera::{Camera, CameraBuilder, CameraSettings};
 use dropbear_macro::SerializableComponent;
 use glam::DVec3;
 use serde::{Deserialize, Serialize};
+use crate::ptr::WorldPtr;
+use crate::scripting::result::DropbearNativeResult;
+use crate::types::NVector3;
 
 #[derive(Debug, Clone, Serialize, Deserialize, SerializableComponent)]
 pub struct CameraComponent {
@@ -118,737 +121,396 @@ pub enum CameraAction {
 }
 
 pub mod shared {
-    use dropbear_engine::camera::Camera;
-
     pub fn camera_exists_for_entity(world: &hecs::World, entity: hecs::Entity) -> bool {
-        world.get::<&Camera>(entity).is_ok()
+        world.get::<&dropbear_engine::camera::Camera>(entity).is_ok()
     }
 }
 
-pub mod jni {
-    #![allow(non_snake_case)]
-
-    use glam::DVec3;
-    use jni::JNIEnv;
-    use jni::objects::{JClass, JObject};
-    use jni::sys::{jboolean, jdouble, jlong, jobject};
-    use dropbear_engine::camera::Camera;
-    use crate::convert_jlong_to_entity;
-    use crate::scripting::jni::utils::{FromJObject, ToJObject};
-    use crate::types::NVector3;
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_cameraExistsForEntity(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jboolean {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if world.get::<&Camera>(entity).is_ok() {
-            true.into()
-        } else {
-            false.into()
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_getCameraEye(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jobject {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            let eye: NVector3 = NVector3::from(camera.eye);
-            return match eye.to_jobject(&mut env) {
-                Ok(val) => val.into_raw(),
-                Err(_) => std::ptr::null_mut()
-            };
-        } else {
-            let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Entity {} missing Camera", entity_id));
-            std::ptr::null_mut()
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_setCameraEye(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-        eye_obj: JObject,
-    ) {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        let new_eye = match NVector3::from_jobject(&mut env, &eye_obj) {
-            Ok(v) => v,
-            Err(e) => {
-                let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Invalid Vector3d: {:?}", e));
-                return;
-            }
-        };
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.eye = DVec3::from(new_eye);
-        } else {
-            let _ = env.throw_new("java/lang/IllegalArgumentException", "Entity missing Camera component");
-        }
-    }
-
-    // TARGET
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_getCameraTarget(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jobject {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            let target: NVector3 = NVector3::from(camera.target);
-            return match target.to_jobject(&mut env) {
-                Ok(val) => val.into_raw(),
-                Err(_) => std::ptr::null_mut()
-            };
-        } else {
-            let _ = env.throw_new("java/lang/IllegalArgumentException", "Entity missing Camera component");
-            std::ptr::null_mut()
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_setCameraTarget(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-        target_obj: JObject,
-    ) {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        let new_target = match NVector3::from_jobject(&mut env, &target_obj) {
-            Ok(v) => v,
-            Err(_) => return,
-        };
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.target = DVec3::from(new_target);
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_getCameraUp(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jobject {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            let up = NVector3::from(camera.up);
-            return match up.to_jobject(&mut env) {
-                Ok(val) => val.into_raw(),
-                Err(_) => std::ptr::null_mut()
-            };
-        } else {
-            let _ = env.throw_new("java/lang/IllegalArgumentException", "Entity missing Camera component");
-            std::ptr::null_mut()
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_setCameraUp(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-        up_obj: JObject,
-    ) {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        let new_up = match NVector3::from_jobject(&mut env, &up_obj) {
-            Ok(v) => v,
-            Err(_) => return,
-        };
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.up = DVec3::from(new_up);
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_getCameraAspect(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jdouble {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            camera.aspect
-        } else {
-            0.0
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_getCameraFovY(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jdouble {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            camera.settings.fov_y
-        } else {
-            0.0
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_setCameraFovY(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-        value: jdouble,
-    ) {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.settings.fov_y = value;
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_getCameraZNear(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jdouble {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            camera.znear
-        } else {
-            0.0
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_setCameraZNear(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-        value: jdouble,
-    ) {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.znear = value;
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_getCameraZFar(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jdouble {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            camera.zfar
-        } else {
-            0.0
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_setCameraZFar(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-        value: jdouble,
-    ) {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.zfar = value;
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_getCameraYaw(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jdouble {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            camera.yaw
-        } else {
-            0.0
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_setCameraYaw(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-        value: jdouble,
-    ) {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.yaw = value;
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_getCameraPitch(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jdouble {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            camera.pitch
-        } else {
-            0.0
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_setCameraPitch(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-        value: jdouble,
-    ) {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.pitch = value;
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_getCameraSpeed(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jdouble {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            camera.settings.speed
-        } else {
-            0.0
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_setCameraSpeed(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-        value: jdouble,
-    ) {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.settings.speed = value;
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_getCameraSensitivity(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jdouble {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            camera.settings.sensitivity
-        } else {
-            0.0
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CameraNative_setCameraSensitivity(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-        value: jdouble,
-    ) {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.settings.sensitivity = value;
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "cameraExistsForEntity"),
+    c
+)]
+fn exists_for_entity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<bool> {
+    Ok(shared::camera_exists_for_entity(world, entity))
 }
 
-#[dropbear_macro::impl_c_api]
-pub mod native {
-    use crate::ptr::WorldPtr;
-    use crate::convert_ptr;
-    use crate::scripting::native::{DropbearNativeError};
-    use hecs::Entity;
-    use glam::DVec3;
-    use dropbear_engine::camera::Camera;
-    use crate::scripting::result::DropbearNativeResult;
-    use crate::types::NVector3;
-
-    pub fn dropbear_camera_exists_for_entity(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<bool> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if world.get::<&Camera>(entity).is_ok() {
-            DropbearNativeResult::Ok(true)
-        } else {
-            DropbearNativeResult::Ok(false)
-        }
-    }
-
-    pub fn dropbear_get_camera_eye(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<NVector3> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            DropbearNativeResult::Ok(NVector3::from(camera.eye))
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraEye"),
+    c
+)]
+fn get_eye(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<NVector3> {
+    match world.get::<&Camera>(entity) {
+        Ok(camera) => Ok(camera.eye.into()),
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_set_camera_eye(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        value: NVector3,
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.eye = DVec3::from(value);
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraEye"),
+    c
+)]
+fn set_eye(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+    eye: &NVector3,
+) -> DropbearNativeResult<()> {
+    match world.get::<&mut Camera>(entity) {
+        Ok(mut camera) => {
+            camera.eye = (*eye).into();
+            Ok(())
+        },
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_get_camera_target(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<NVector3> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            DropbearNativeResult::Ok(NVector3::from(camera.target))
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraTarget"),
+    c
+)]
+fn get_target(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<NVector3> {
+    match world.get::<&Camera>(entity) {
+        Ok(camera) => Ok(camera.target.into()),
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_set_camera_target(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        value: NVector3,
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.target = DVec3::from(value);
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraTarget"),
+    c
+)]
+fn set_target(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+    target: &NVector3,
+) -> DropbearNativeResult<()> {
+    match world.get::<&mut Camera>(entity) {
+        Ok(mut camera) => {
+            camera.target = target.into();
+            Ok(())
+        },
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_get_camera_up(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<NVector3> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            DropbearNativeResult::Ok(NVector3::from(camera.up))
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraUp"),
+    c
+)]
+fn get_up(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<NVector3> {
+    match world.get::<&Camera>(entity) {
+        Ok(camera) => Ok(camera.up.into()),
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_set_camera_up(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        value: NVector3,
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.up = DVec3::from(value);
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraUp"),
+    c
+)]
+fn set_up(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+    up: &NVector3,
+) -> DropbearNativeResult<()> {
+    match world.get::<&mut Camera>(entity) {
+        Ok(mut camera) => {
+            camera.up = up.into();
             Ok(())
-        } else {
-            Err(DropbearNativeError::NoSuchComponent)
-        }
+        },
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_get_camera_aspect(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<f64> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            DropbearNativeResult::Ok(camera.aspect)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraAspect"),
+    c
+)]
+fn get_aspect(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<f64> {
+    match world.get::<&Camera>(entity) {
+        Ok(camera) => Ok(camera.aspect.into()),
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_get_camera_fov_y(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<f64> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            DropbearNativeResult::Ok(camera.settings.fov_y)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraFovY"),
+    c
+)]
+fn get_fovy(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<f64> {
+    match world.get::<&Camera>(entity) {
+        Ok(camera) => Ok(camera.settings.fov_y.into()),
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_set_camera_fov_y(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        value: f64,
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.settings.fov_y = value;
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraFovY"),
+    c
+)]
+fn set_fovy(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+    fovy: f64,
+) -> DropbearNativeResult<()> {
+    match world.get::<&mut Camera>(entity) {
+        Ok(mut camera) => {
+            camera.settings.fov_y = fovy.into();
+            Ok(())
+        },
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_get_camera_znear(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<f64> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            DropbearNativeResult::Ok(camera.znear)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraZNear"),
+    c
+)]
+fn get_znear(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<f64> {
+    match world.get::<&Camera>(entity) {
+        Ok(camera) => Ok(camera.znear.into()),
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_set_camera_znear(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        value: f64,
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.znear = value;
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraZNear"),
+    c
+)]
+fn set_znear(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+    znear: f64,
+) -> DropbearNativeResult<()> {
+    match world.get::<&mut Camera>(entity) {
+        Ok(mut camera) => {
+            camera.znear = znear.into();
+            Ok(())
+        },
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_get_camera_zfar(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<f64> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            DropbearNativeResult::Ok(camera.zfar)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraZFar"),
+    c
+)]
+fn get_zfar(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<f64> {
+    match world.get::<&Camera>(entity) {
+        Ok(camera) => Ok(camera.zfar.into()),
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_set_camera_zfar(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        value: f64,
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.zfar = value;
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraZFar"),
+    c
+)]
+fn set_zfar(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+    zfar: f64,
+) -> DropbearNativeResult<()> {
+    match world.get::<&mut Camera>(entity) {
+        Ok(mut camera) => {
+            camera.zfar = zfar.into();
+            Ok(())
+        },
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_get_camera_yaw(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<f64> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            DropbearNativeResult::Ok(camera.yaw)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraYaw"),
+    c
+)]
+fn get_yaw(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<f64> {
+    match world.get::<&Camera>(entity) {
+        Ok(camera) => Ok(camera.yaw.into()),
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_set_camera_yaw(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        value: f64,
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.yaw = value;
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraYaw"),
+    c
+)]
+fn set_yaw(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+    yaw: f64,
+) -> DropbearNativeResult<()> {
+    match world.get::<&mut Camera>(entity) {
+        Ok(mut camera) => {
+            camera.yaw = yaw.into();
+            Ok(())
+        },
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_get_camera_pitch(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<f64> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            DropbearNativeResult::Ok(camera.pitch)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraPitch"),
+    c
+)]
+fn get_pitch(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<f64> {
+    match world.get::<&Camera>(entity) {
+        Ok(camera) => Ok(camera.pitch.into()),
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_set_camera_pitch(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        value: f64,
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.pitch = value;
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraPitch"),
+    c
+)]
+fn set_pitch(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+    pitch: f64,
+) -> DropbearNativeResult<()> {
+    match world.get::<&mut Camera>(entity) {
+        Ok(mut camera) => {
+            camera.pitch = pitch.into();
+            Ok(())
+        },
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_get_camera_speed(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<f64> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            DropbearNativeResult::Ok(camera.settings.speed)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraSpeed"),
+    c
+)]
+fn get_speed(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<f64> {
+    match world.get::<&Camera>(entity) {
+        Ok(camera) => Ok(camera.settings.speed.into()),
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_set_camera_speed(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        value: f64,
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.settings.speed = value;
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraSpeed"),
+    c
+)]
+fn set_speed(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+    speed: f64,
+) -> DropbearNativeResult<()> {
+    match world.get::<&mut Camera>(entity) {
+        Ok(mut camera) => {
+            camera.settings.speed = speed.into();
+            Ok(())
+        },
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_get_camera_sensitivity(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<f64> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(camera) = world.get::<&Camera>(entity) {
-            DropbearNativeResult::Ok(camera.settings.sensitivity)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "getCameraSensitivity"),
+    c
+)]
+fn get_sensitivity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<f64> {
+    match world.get::<&Camera>(entity) {
+        Ok(camera) => Ok(camera.settings.sensitivity.into()),
+        Err(e) => Err(e.into()),
     }
+}
 
-    pub fn dropbear_set_camera_sensitivity(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        value: f64,
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if let Ok(mut camera) = world.get::<&mut Camera>(entity) {
-            camera.settings.sensitivity = value;
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CameraNative", func = "setCameraSensitivity"),
+    c
+)]
+fn set_sensitivity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropebear_macro::entity]
+    entity: hecs::Entity,
+    sensitivity: f64,
+) -> DropbearNativeResult<()> {
+    match world.get::<&mut Camera>(entity) {
+        Ok(mut camera) => {
+            camera.settings.sensitivity = sensitivity.into();
+            Ok(())
+        },
+        Err(e) => Err(e.into()),
     }
-}
+}
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/component.rs b/crates/eucalyptus-core/src/component.rs
deleted file mode 100644
index da5c573..0000000
--- a/crates/eucalyptus-core/src/component.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-//! Component helper macros. 
-
-/// Get a component and execute a closure if it exists
-///
-/// # Usage
-/// ```
-/// use eucalyptus_core::with_component;
-///
-/// use eucalyptus_core::scene::SceneEntity;
-///
-/// struct Transform {
-///     position: [f32; 3],
-/// }
-///
-/// let scene_entity = SceneEntity::default();
-///
-/// with_component!(scene_entity, Transform, /*mut*/ |transform| {
-///     transform.position.x += 1.0;
-/// });
-#[macro_export]
-macro_rules! with_component {
-    // immutable
-    ($entity:expr, $comp_type:ty, $closure:expr) => {
-        $crate::traits::SerializableComponent::
-        if let Some(comp) = $entity.as_any().downcast_ref::<$comp_type>() {
-            $closure(comp)
-        }
-    };
-
-    // mutable
-    ($entity:expr, $comp_type:ty, mut $closure:expr) => {
-        if let Some(comp) = $entity.as_any_mut().downcast_mut()::<$comp_type>() {
-            $closure(comp)
-        }
-    };
-}
-
-/// Try to get a component, or execute else block
-///
-/// # Usage
-/// ```
-/// use eucalyptus_core::if_component;
-///
-/// use eucalyptus_core::scene::SceneEntity;
-///
-/// struct Transform {
-///     position: [f32; 3],
-/// }
-///
-/// let scene_entity = SceneEntity::default();
-///
-/// if_component!(scene_entity, Transform, /*mut*/ |transform| {
-///     let position = transform.position.get(0);
-/// } else {
-///     println!("No transform found");
-/// });
-#[macro_export]
-macro_rules! if_component {
-    ($entity:expr, $comp_type:ty, |$comp:ident| $then:block else $else:block) => {
-        if let Some($comp) = $entity.as_any().downcast_ref::<$comp_type>() {
-            $then
-        } else {
-            $else
-        }
-    };
-
-    ($entity:expr, $comp_type:ty, |$comp:ident| $then:block) => {
-        if let Some($comp) = $entity.as_any().downcast_ref::<$comp_type>() {
-            $then
-        }
-    };
-
-    ($entity:expr, $comp_type:ty, |mut $comp:ident| $then:block else $else:block) => {
-        if let Some(mut $comp) =  $entity.as_any_mut().downcast_mut()::<$comp_type>() {
-            $then
-        } else {
-            $else
-        }
-    };
-
-    ($entity:expr, $comp_type:ty, |mut $comp:ident| $then:block) => {
-        if let Some(mut $comp) = $entity.as_any_mut().downcast_mut()::<$comp_type>() {
-            $then
-        }
-    };
-}
diff --git a/crates/eucalyptus-core/src/engine.rs b/crates/eucalyptus-core/src/engine.rs
index 8b90e35..440c724 100644
--- a/crates/eucalyptus-core/src/engine.rs
+++ b/crates/eucalyptus-core/src/engine.rs
@@ -1,17 +1,12 @@
-use crate::ptr::WorldPtr;
+use crate::ptr::{CommandBufferPtr, CommandBufferUnwrapped, WorldPtr};
 use crate::scripting::result::DropbearNativeResult;
-use hecs::World;
 
 pub mod shared {
     use crate::command::CommandBuffer;
     use crate::scripting::native::DropbearNativeError;
     use crate::scripting::result::DropbearNativeResult;
     use crate::states::Label;
-    use dropbear_engine::asset::AssetRegistry;
-    use dropbear_engine::utils::ResourceReference;
     use hecs::{Entity, World};
-    use std::ffi::CStr;
-    use std::os::raw::c_char;
 
     pub fn get_entity(world: &World, label: &str) -> DropbearNativeResult<u64> {
         for (id, entity_label) in world.query::<(Entity, &Label)>().iter() {
@@ -22,30 +17,12 @@ pub mod shared {
         Err(DropbearNativeError::EntityNotFound)
     }
 
-    pub fn get_asset(registry: &AssetRegistry, uri: &str) -> DropbearNativeResult<u64> {
-        let reference = ResourceReference::from_euca_uri(uri)
-            .map_err(|_| DropbearNativeError::InvalidURI)?;
-
-        match registry.get_handle_from_reference(&reference) {
-            Some(handle) => Ok(handle.raw()),
-            None => Err(DropbearNativeError::AssetNotFound),
-        }
-    }
-
     pub fn quit(command_buffer: &crossbeam_channel::Sender<CommandBuffer>) -> DropbearNativeResult<()> {
         command_buffer.send(CommandBuffer::Quit)
             .map_err(|_| DropbearNativeError::SendError)
     }
-
-    pub unsafe fn read_str(ptr: *const c_char) -> DropbearNativeResult<String> {
-        if ptr.is_null() { return Err(DropbearNativeError::NullPointer); }
-        unsafe { CStr::from_ptr(ptr) }.to_str()
-            .map(|s| s.to_string())
-            .map_err(|_| DropbearNativeError::InvalidUTF8)
-    }
 }
 
-// input func
 #[dropbear_macro::export(
     kotlin(
         class = "com.dropbear.DropbearEngineNative",
@@ -55,79 +32,22 @@ pub mod shared {
 )]
 fn get_entity(
     #[dropbear_macro::define(WorldPtr)]
-    world: &World,
+    world: &hecs::World,
     label: String,
 ) -> DropbearNativeResult<u64> {
     shared::get_entity(&world, &label)
 }
 
-pub mod jni {
-    #![allow(non_snake_case)]
-    use crate::command::CommandBuffer;
-    use crate::return_boxed;
-    use dropbear_engine::asset::AssetRegistry;
-    use hecs::World;
-    use jni::objects::{JClass, JString};
-    use jni::sys::{jlong, jobject};
-    use jni::JNIEnv;
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_DropbearEngineNative_getEntity(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        label: JString,
-    ) -> jobject {
-        let world = crate::convert_ptr!(world_handle => World);
-        let label_str = crate::convert_jstring!(env, label);
-
-        let value_opt = super::shared::get_entity(&world, &label_str)
-            .ok()
-            .map(|id| id as i64);
-
-        return_boxed!(
-            &mut env,
-            value_opt,
-            "(J)Ljava/lang/Long;",
-            "java/lang/Long"
-        )
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_DropbearEngineNative_getAsset(
-        mut env: JNIEnv,
-        _class: JClass,
-        asset_handle: jlong,
-        label: JString,
-    ) -> jobject {
-        let asset = crate::convert_ptr!(asset_handle => AssetRegistry);
-        let label_str = crate::convert_jstring!(env, label);
-
-        let value_opt = super::shared::get_asset(&asset, &label_str)
-            .ok()
-            .map(|id| id as i64);
-
-        return_boxed!(
-            &mut env,
-            value_opt,
-            "(J)Ljava/lang/Long;",
-            "java/lang/Long"
-        )
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_DropbearEngineNative_quit(
-        mut env: JNIEnv,
-        _class: JClass,
-        command_buffer_ptr: jlong,
-    ) {
-        let sender = crate::convert_ptr!(command_buffer_ptr => crossbeam_channel::Sender<CommandBuffer>);
-
-        if let Err(e) = super::shared::quit(sender) {
-            let _ = env.throw_new(
-                "java/lang/RuntimeException",
-                format!("Failed to send quit command: {:?}", e)
-            );
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(
+        class = "com.dropbear.DropbearEngineNative",
+        func = "quit",
+    ),
+    c
+)]
+fn quit(
+    #[dropbear_macro::define(CommandBufferPtr)]
+    command_buffer: &CommandBufferUnwrapped,
+) -> DropbearNativeResult<()> {
+    shared::quit(command_buffer)
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/entity.rs b/crates/eucalyptus-core/src/entity.rs
index 9779968..cd86971 100644
--- a/crates/eucalyptus-core/src/entity.rs
+++ b/crates/eucalyptus-core/src/entity.rs
@@ -1,132 +1,98 @@
-pub mod jni {
-    #![allow(non_snake_case)]
-    use crate::hierarchy::{Children, Parent};
-    use crate::states::Label;
-    use crate::{convert_jlong_to_entity, convert_jstring, convert_ptr};
-    use hecs::World;
-    use jni::objects::{JClass, JString, JValue};
-    use jni::sys::{jboolean, jlong, jlongArray, jobject, jsize, jstring};
-    use jni::JNIEnv;
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_LabelNative_labelExistsForEntity(
-        _env: JNIEnv,
-        _: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jboolean {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = convert_jlong_to_entity!(entity_id);
-
-        world.get::<&Label>(entity).is_ok().into()
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_EntityRefNative_getEntityLabel(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jstring {
-        let world = crate::convert_ptr!(world_ptr => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-
-        let Ok(label) = world.get::<&Label>(entity) else {
-            let _ = env.throw_new("java/lang/InvalidArgumentException", "Unable to locate label entity");
-            return std::ptr::null_mut();
-        };
-
-        let label = label.as_str();
-
-        let Ok(string) = env.new_string(label) else {
-            let _ = env.throw_new("java/lang/OutOfMemoryException", "Unable to create new string");
-            return std::ptr::null_mut();
-        };
-
-        string.into_raw()
-    }
-
-    pub extern "system" fn Java_com_dropbear_EntityRefNative_getChildren(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jlongArray {
-        let world = crate::convert_ptr!(world_ptr => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-
-        if let Ok(children) = world.query_one::<&Children>(entity).get()
-        {
-            let entity_bytes = children.children().iter().map(|c| c.to_bits().get() as i64).collect::<Vec<_>>();
-
-            let Ok(jarray) = env.new_long_array(entity_bytes.len() as jsize) else {
-                let _ = env.throw_new("java/lang/OutOfMemoryException", "Unable to create new long array");
-                return std::ptr::null_mut();
-            };
+use crate::hierarchy::{Children, Parent};
+use crate::ptr::WorldPtr;
+use crate::scripting::result::DropbearNativeResult;
+use crate::states::Label;
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.LabelNative", func = "labelExistsForEntity"),
+    c
+)]
+fn label_exists_for_entity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity
+) -> DropbearNativeResult<bool> {
+    Ok(world.get::<&Label>(entity).is_ok())
+}
 
-            if let Err(e) = env.set_long_array_region(&jarray, 0, entity_bytes.as_slice()) {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Unable to populate long array: {}", e));
-                return std::ptr::null_mut()
-            }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.EntityRefNative", func = "getEntityLabel"),
+    c
+)]
+fn get_label(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity
+) -> DropbearNativeResult<String> {
+    let label = world.get::<&Label>(entity)?.as_str().to_string();
+    Ok(label)
+}
 
-            jarray.into_raw()
-        } else {
-            // could be that the entity just doesn't have any children.
-            std::ptr::null_mut()
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.EntityRefNative", func = "getChildren"),
+    c
+)]
+fn get_children(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity
+) -> DropbearNativeResult<Vec<u64>> {
+    if let Ok(children) = world.query_one::<&Children>(entity).get()
+    {
+        let entity_bytes = children.children().iter().map(|c| c.to_bits().get()).collect::<Vec<_>>();
+        Ok(entity_bytes)
+    } else {
+        // could be that the entity just doesn't have any children, so no need to throw error
+        Ok(vec![])
     }
+}
 
-    pub extern "system" fn Java_com_dropbear_EntityRefNative_getChildByLabel(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-        label: JString,
-    ) -> jobject {
-        let world = crate::convert_ptr!(world_ptr => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let target = convert_jstring!(env, label);
-
-        if let Ok(children) = world.query_one::<&Children>(entity).get()
-        {
-            for child in children.children() {
-                if let Ok(label) = world.get::<&Label>(entity) {
-                    if label.as_str() == target {
-                        let found = child.clone();
-
-                        return crate::return_boxed!(&mut env, Some(JValue::Long(found.to_bits().get() as i64)), "(J)Ljava/lang/Long", "java/lang/Long");
-                    }
-                } else {
-                    // skip if error or no entity
-                    continue;
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.EntityRefNative", func = "getChildByLabel"),
+    c
+)]
+fn get_child_by_label(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    target: String,
+) -> DropbearNativeResult<Option<u64>> {
+    if let Ok(children) = world.query_one::<&Children>(entity).get()
+    {
+        for child in children.children() {
+            if let Ok(label) = world.get::<&Label>(entity) {
+                if label.as_str() == target {
+                    let found = child.clone();
+                    return Ok(Some(found.to_bits().get()));
                 }
+            } else {
+                // skip if error or no entity
+                continue;
             }
-            std::ptr::null_mut()
-        } else {
-            // could be that the entity just doesn't have any children.
-            std::ptr::null_mut()
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_EntityRefNative_getParent(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jobject {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = convert_jlong_to_entity!(entity_id);
-        
-        if let Ok(parent) = world.query_one::<&Parent>(entity).get() {
-            crate::return_boxed!(&mut env, Some(JValue::Long(parent.parent().to_bits().get() as jlong)), "(J)Ljava/lang/Long", "java/lang/Long")
-        } else {
-            std::ptr::null_mut()
         }
+        Ok(None)
+    } else {
+        Ok(None)
     }
 }
 
-#[dropbear_macro::impl_c_api]
-pub mod native {
-
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.EntityRefNative", func = "getParent"),
+    c
+)]
+fn get_parent(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<Option<u64>> {
+    if let Ok(parent) = world.query_one::<&Parent>(entity).get() {
+        Ok(Some(parent.parent().to_bits().get()))
+    } else {
+        Ok(None)
+    }
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/hierarchy.rs b/crates/eucalyptus-core/src/hierarchy.rs
index 8affa89..a03a35e 100644
--- a/crates/eucalyptus-core/src/hierarchy.rs
+++ b/crates/eucalyptus-core/src/hierarchy.rs
@@ -78,10 +78,8 @@ impl Hierarchy {
             }
         }
 
-        // Add Parent component to child
         let _ = world.insert_one(child, Parent::new(parent));
 
-        // Add child to parent's Children component
         if let Ok(mut children) = world.get::<&mut Children>(parent) {
             children.push(child);
         } else {
diff --git a/crates/eucalyptus-core/src/input.rs b/crates/eucalyptus-core/src/input.rs
index d765a32..c972f15 100644
--- a/crates/eucalyptus-core/src/input.rs
+++ b/crates/eucalyptus-core/src/input.rs
@@ -11,6 +11,12 @@ use std::{
 use glam::Vec2;
 use winit::window::Window;
 use winit::{event::MouseButton, keyboard::KeyCode};
+use crate::scripting::jni::utils::ToJObject;
+use crate::scripting::native::DropbearNativeError;
+use crate::scripting::result::DropbearNativeResult;
+use jni::objects::JObject;
+use jni::sys::jlong;
+use jni::JNIEnv;
 
 #[allow(dead_code)]
 #[derive(Clone, Debug)]
@@ -112,8 +118,6 @@ impl InputState {
 pub mod shared {
     use crossbeam_channel::Sender;
     use crate::command::{CommandBuffer, WindowCommand};
-    use crate::scripting::native::DropbearNativeError;
-    use crate::scripting::result::DropbearNativeResult;
     use crate::types::NVector2;
     use super::*;
 
@@ -191,266 +195,152 @@ pub mod shared {
     }
 }
 
-pub mod jni {
-    #![allow(non_snake_case)]
-    use jni::JNIEnv;
-    use jni::objects::{JClass, JObject};
-    use jni::sys::{jboolean, jint, jlong, jlongArray, jobject};
-    use crate::input::InputState;
-    use crate::command::CommandBuffer;
-    use crate::scripting::jni::utils::ToJObject;
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_InputStateNative_printInputState(
-        _env: JNIEnv,
-        _class: JClass,
-        input_ptr: jlong,
-    ) {
-        let input = crate::convert_ptr!(input_ptr => InputState);
-        println!("Input State: {:?}", input);
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_InputStateNative_isKeyPressed(
-        _env: JNIEnv,
-        _class: JClass,
-        input_ptr: jlong,
-        key_code: jint,
-    ) -> jboolean {
-        let input = crate::convert_ptr!(input_ptr => InputState);
-        if super::shared::is_key_pressed(&input, key_code) { 1 } else { 0 }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_InputStateNative_getMousePosition(
-        mut env: JNIEnv,
-        _class: JClass,
-        input_ptr: jlong,
-    ) -> jobject {
-        let input = crate::convert_ptr!(input_ptr => InputState);
-        let vec = super::shared::get_mouse_position(&input);
-        match vec.to_jobject(&mut env) {
-            Ok(obj) => obj.into_raw(),
-            Err(_) => std::ptr::null_mut(),
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_InputStateNative_isMouseButtonPressed(
-        mut env: JNIEnv,
-        _class: JClass,
-        input_ptr: jlong,
-        mouse_button_obj: JObject,
-    ) -> jboolean {
-        let input = crate::convert_ptr!(input_ptr => InputState);
-
-        let ordinal_res = env.call_method(&mouse_button_obj, "ordinal", "()I", &[]);
-
-        let ordinal = match ordinal_res.and_then(|v| v.i()) {
-            Ok(i) => i,
-            Err(e) => {
-                eprintln!("Failed to get MouseButton ordinal: {:?}", e);
-                return 0;
-            }
-        };
-
-        if super::shared::is_mouse_button_pressed(&input, ordinal) { 1 } else { 0 }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_InputStateNative_getMouseDelta(
-        mut env: JNIEnv,
-        _class: JClass,
-        input_ptr: jlong,
-    ) -> jobject {
-        let input = crate::convert_ptr!(input_ptr => InputState);
-        let vec = super::shared::get_mouse_delta(&input);
-        match vec.to_jobject(&mut env) {
-            Ok(obj) => obj.into_raw(),
-            Err(_) => std::ptr::null_mut(),
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_InputStateNative_isCursorLocked(
-        _env: JNIEnv,
-        _class: JClass,
-        input_ptr: jlong,
-    ) -> jboolean {
-        let input = crate::convert_ptr!(input_ptr => InputState);
-        if input.is_cursor_locked { 1 } else { 0 }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_InputStateNative_setCursorLocked(
-        mut env: JNIEnv,
-        _class: JClass,
-        cmd_ptr: jlong,
-        input_ptr: jlong,
-        locked: jboolean,
-    ) {
-        let input = crate::convert_ptr!(mut input_ptr => InputState);
-        let sender = crate::convert_ptr!(cmd_ptr => crossbeam_channel::Sender<CommandBuffer>);
-
-        if let Err(e) = super::shared::set_cursor_locked(input, sender, locked != 0) {
-            let _ = env.throw_new("java/lang/RuntimeException", format!("{:?}", e));
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_InputStateNative_getLastMousePos(
-        mut env: JNIEnv,
-        _class: JClass,
-        input_ptr: jlong,
-    ) -> jobject {
-        let input = crate::convert_ptr!(input_ptr => InputState);
-        let vec = super::shared::get_last_mouse_pos(&input);
-        match vec.to_jobject(&mut env) {
-            Ok(obj) => obj.into_raw(),
-            Err(_) => std::ptr::null_mut(),
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_InputStateNative_isCursorHidden(
-        _env: JNIEnv,
-        _class: JClass,
-        input_ptr: jlong,
-    ) -> jboolean {
-        let input = crate::convert_ptr!(input_ptr => InputState);
-        if input.is_cursor_hidden { 1 } else { 0 }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_InputStateNative_setCursorHidden(
-        mut env: JNIEnv,
-        _class: JClass,
-        cmd_ptr: jlong,
-        input_ptr: jlong,
-        hidden: jboolean,
-    ) {
-        let input = crate::convert_ptr!(mut input_ptr => InputState);
-        let sender = crate::convert_ptr!(cmd_ptr => crossbeam_channel::Sender<CommandBuffer>);
-
-        if let Err(e) = super::shared::set_cursor_hidden(input, sender, hidden != 0) {
-            let _ = env.throw_new("java/lang/RuntimeException", format!("{:?}", e));
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_InputStateNative_getConnectedGamepads(
-        env: JNIEnv,
-        _class: JClass,
-        input_ptr: jlong,
-    ) -> jlongArray {
-        let input = crate::convert_ptr!(input_ptr => InputState);
-        let ids = super::shared::get_connected_gamepads(&input);
-
-        let long_ids: Vec<i64> = ids.iter().map(|&id| id as i64).collect();
-
-        let output = match env.new_long_array(long_ids.len() as i32) {
-            Ok(arr) => arr,
-            Err(_) => return std::ptr::null_mut(),
-        };
-
-        if env.set_long_array_region(&output, 0, &long_ids).is_ok() {
-            output.into_raw()
-        } else {
-            std::ptr::null_mut()
-        }
-    }
+#[repr(C)]
+struct ConnectedGamepadIds {
+    ids: Vec<u64>,
 }
 
-#[dropbear_macro::impl_c_api]
-pub mod native {
-    use crate::ptr::{InputStatePtr, CommandBufferPtr};
-    use crate::input::{InputState};
-    use crate::command::CommandBuffer;
-    use crate::convert_ptr;
-    use crate::scripting::native::DropbearNativeError;
-    use crate::scripting::result::DropbearNativeResult;
-    use crate::types::NVector2;
+impl ToJObject for ConnectedGamepadIds {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let output = env
+            .new_long_array(self.ids.len() as i32)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
 
-    pub fn dropbear_print_input_state(input_ptr: InputStatePtr) -> DropbearNativeResult<()> {
-        let input = convert_ptr!(input_ptr => InputState);
-        println!("Input State: {:?}", input);
-        DropbearNativeResult::Ok(())
-    }
-
-    pub fn dropbear_is_key_pressed(input_ptr: InputStatePtr, key_ordinal: i32) -> DropbearNativeResult<bool> {
-        let input = convert_ptr!(input_ptr => InputState);
-        DropbearNativeResult::Ok(super::shared::is_key_pressed(input, key_ordinal))
-    }
-
-    pub fn dropbear_get_mouse_position(input_ptr: InputStatePtr) -> DropbearNativeResult<NVector2> {
-        let input = convert_ptr!(input_ptr => InputState);
-        DropbearNativeResult::Ok(super::shared::get_mouse_position(input))
-    }
-
-    pub fn dropbear_is_mouse_button_pressed(input_ptr: InputStatePtr, btn_ordinal: i32) -> DropbearNativeResult<bool> {
-        let input = convert_ptr!(input_ptr => InputState);
-        DropbearNativeResult::Ok(super::shared::is_mouse_button_pressed(input, btn_ordinal))
-    }
-
-    pub fn dropbear_get_mouse_delta(input_ptr: InputStatePtr) -> DropbearNativeResult<NVector2> {
-        let input = convert_ptr!(input_ptr => InputState);
-        DropbearNativeResult::Ok(super::shared::get_mouse_delta(input))
-    }
-
-    pub fn dropbear_is_cursor_locked(input_ptr: InputStatePtr) -> DropbearNativeResult<bool> {
-        let input = convert_ptr!(input_ptr => InputState);
-        DropbearNativeResult::Ok(input.is_cursor_locked)
+        let long_ids: Vec<jlong> = self.ids.iter().map(|&id| id as jlong).collect();
+        env.set_long_array_region(&output, 0, &long_ids)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+        Ok(JObject::from(output))
     }
+}
 
-    pub fn dropbear_set_cursor_locked(
-        cmd_ptr: CommandBufferPtr,
-        input_ptr: InputStatePtr,
-        locked: bool
-    ) -> DropbearNativeResult<()> {
-        let input = convert_ptr!(mut input_ptr => InputState);
-        let sender = convert_ptr!(cmd_ptr => crossbeam_channel::Sender<CommandBuffer>);
-        super::shared::set_cursor_locked(input, sender, locked)
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.InputStateNative", func = "printInputState"),
+    c
+)]
+fn print_input_state(
+    #[dropbear_macro::define(crate::ptr::InputStatePtr)]
+    input: &InputState,
+) -> DropbearNativeResult<()> {
+    println!("Input State: {:?}", input);
+    Ok(())
+}
 
-    pub fn dropbear_get_last_mouse_pos(input_ptr: InputStatePtr) -> DropbearNativeResult<NVector2> {
-        let input = convert_ptr!(input_ptr => InputState);
-        DropbearNativeResult::Ok(super::shared::get_last_mouse_pos(input))
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.InputStateNative", func = "isKeyPressed"),
+    c
+)]
+fn is_key_pressed(
+    #[dropbear_macro::define(crate::ptr::InputStatePtr)]
+    input: &InputState,
+    key_code: i32,
+) -> DropbearNativeResult<bool> {
+    Ok(shared::is_key_pressed(input, key_code))
+}
 
-    pub fn dropbear_is_cursor_hidden(input_ptr: InputStatePtr) -> DropbearNativeResult<bool> {
-        let input = convert_ptr!(input_ptr => InputState);
-        DropbearNativeResult::Ok(input.is_cursor_hidden)
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.InputStateNative", func = "getMousePosition"),
+    c
+)]
+fn get_mouse_position(
+    #[dropbear_macro::define(crate::ptr::InputStatePtr)]
+    input: &InputState,
+) -> DropbearNativeResult<crate::types::NVector2> {
+    Ok(shared::get_mouse_position(input))
+}
 
-    pub fn dropbear_set_cursor_hidden(
-        cmd_ptr: CommandBufferPtr,
-        input_ptr: InputStatePtr,
-        hidden: bool
-    ) -> DropbearNativeResult<()> {
-        let input = convert_ptr!(mut input_ptr, InputStatePtr => InputState);
-        let sender = convert_ptr!(cmd_ptr => crossbeam_channel::Sender<CommandBuffer>);
-        super::shared::set_cursor_hidden(input, sender, hidden)
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.InputStateNative", func = "isMouseButtonPressed"),
+    c
+)]
+fn is_mouse_button_pressed(
+    #[dropbear_macro::define(crate::ptr::InputStatePtr)]
+    input: &InputState,
+    button_ordinal: i32,
+) -> DropbearNativeResult<bool> {
+    Ok(shared::is_mouse_button_pressed(input, button_ordinal))
+}
 
-    pub fn dropbear_get_connected_gamepads(
-        input_ptr: InputStatePtr,
-        out_count: *mut usize,
-    ) -> DropbearNativeResult<*mut u64> {
-        let input = convert_ptr!(input_ptr => InputState);
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.InputStateNative", func = "getMouseDelta"),
+    c
+)]
+fn get_mouse_delta(
+    #[dropbear_macro::define(crate::ptr::InputStatePtr)]
+    input: &InputState,
+) -> DropbearNativeResult<crate::types::NVector2> {
+    Ok(shared::get_mouse_delta(input))
+}
 
-        let mut ids = super::shared::get_connected_gamepads(input);
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.InputStateNative", func = "isCursorLocked"),
+    c
+)]
+fn is_cursor_locked(
+    #[dropbear_macro::define(crate::ptr::InputStatePtr)]
+    input: &InputState,
+) -> DropbearNativeResult<bool> {
+    Ok(input.is_cursor_locked)
+}
 
-        ids.shrink_to_fit();
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.InputStateNative", func = "setCursorLocked"),
+    c
+)]
+fn set_cursor_locked(
+    #[dropbear_macro::define(crate::ptr::CommandBufferPtr)]
+    command_buffer: &crate::ptr::CommandBufferUnwrapped,
+    #[dropbear_macro::define(crate::ptr::InputStatePtr)]
+    input: &mut InputState,
+    locked: bool,
+) -> DropbearNativeResult<()> {
+    shared::set_cursor_locked(input, command_buffer, locked)
+}
 
-        if out_count.is_null() {
-            return DropbearNativeResult::Err(DropbearNativeError::NullPointer);
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.InputStateNative", func = "getLastMousePos"),
+    c
+)]
+fn get_last_mouse_pos(
+    #[dropbear_macro::define(crate::ptr::InputStatePtr)]
+    input: &InputState,
+) -> DropbearNativeResult<crate::types::NVector2> {
+    Ok(shared::get_last_mouse_pos(input))
+}
 
-        unsafe { *out_count = ids.len(); }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.InputStateNative", func = "isCursorHidden"),
+    c
+)]
+fn is_cursor_hidden(
+    #[dropbear_macro::define(crate::ptr::InputStatePtr)]
+    input: &InputState,
+) -> DropbearNativeResult<bool> {
+    Ok(input.is_cursor_hidden)
+}
 
-        let ptr = ids.as_mut_ptr();
-        std::mem::forget(ids);
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.InputStateNative", func = "setCursorHidden"),
+    c
+)]
+fn set_cursor_hidden(
+    #[dropbear_macro::define(crate::ptr::CommandBufferPtr)]
+    command_buffer: &crate::ptr::CommandBufferUnwrapped,
+    #[dropbear_macro::define(crate::ptr::InputStatePtr)]
+    input: &mut InputState,
+    hidden: bool,
+) -> DropbearNativeResult<()> {
+    shared::set_cursor_hidden(input, command_buffer, hidden)
+}
 
-        DropbearNativeResult::Ok(ptr)
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.InputStateNative", func = "getConnectedGamepads"),
+    c
+)]
+fn get_connected_gamepads(
+    #[dropbear_macro::define(crate::ptr::InputStatePtr)]
+    input: &InputState,
+) -> DropbearNativeResult<ConnectedGamepadIds> {
+    Ok(ConnectedGamepadIds {
+        ids: shared::get_connected_gamepads(input),
+    })
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/input/gamepad.rs b/crates/eucalyptus-core/src/input/gamepad.rs
index 024a932..1faae30 100644
--- a/crates/eucalyptus-core/src/input/gamepad.rs
+++ b/crates/eucalyptus-core/src/input/gamepad.rs
@@ -1,41 +1,44 @@
+use crate::input::InputState;
+use crate::ptr::InputStatePtr;
+use crate::scripting::result::DropbearNativeResult;
+use crate::types::NVector2;
+
 pub mod shared {
-    use dropbear_engine::gilrs::{Button, GamepadId};
     use jni::JNIEnv;
     use jni::objects::{JObject, JValue};
-    use dropbear_engine::gilrs;
     use crate::input::InputState;
     use crate::scripting::jni::utils::{FromJObject, ToJObject};
     use crate::scripting::native::DropbearNativeError;
     use crate::scripting::result::DropbearNativeResult;
     use crate::types::NVector2;
 
-    fn map_int_to_gamepad_button(ordinal: i32) -> Option<Button> {
+    fn map_int_to_gamepad_button(ordinal: i32) -> Option<dropbear_engine::gilrs::Button> {
         match ordinal {
-            0 => Some(gilrs::Button::Unknown),
-            1 => Some(gilrs::Button::South),
-            2 => Some(gilrs::Button::East),
-            3 => Some(gilrs::Button::North),
-            4 => Some(gilrs::Button::West),
-            5 => Some(gilrs::Button::C),
-            6 => Some(gilrs::Button::Z),
-            7 => Some(gilrs::Button::LeftTrigger),
-            8 => Some(gilrs::Button::RightTrigger),
-            9 => Some(gilrs::Button::LeftTrigger2),
-            10 => Some(gilrs::Button::RightTrigger2),
-            11 => Some(gilrs::Button::Select),
-            12 => Some(gilrs::Button::Start),
-            13 => Some(gilrs::Button::Mode),
-            14 => Some(gilrs::Button::LeftThumb),
-            15 => Some(gilrs::Button::RightThumb),
-            16 => Some(gilrs::Button::DPadUp),
-            17 => Some(gilrs::Button::DPadDown),
-            18 => Some(gilrs::Button::DPadLeft),
-            19 => Some(gilrs::Button::DPadRight),
+            0 => Some(dropbear_engine::gilrs::Button::Unknown),
+            1 => Some(dropbear_engine::gilrs::Button::South),
+            2 => Some(dropbear_engine::gilrs::Button::East),
+            3 => Some(dropbear_engine::gilrs::Button::North),
+            4 => Some(dropbear_engine::gilrs::Button::West),
+            5 => Some(dropbear_engine::gilrs::Button::C),
+            6 => Some(dropbear_engine::gilrs::Button::Z),
+            7 => Some(dropbear_engine::gilrs::Button::LeftTrigger),
+            8 => Some(dropbear_engine::gilrs::Button::RightTrigger),
+            9 => Some(dropbear_engine::gilrs::Button::LeftTrigger2),
+            10 => Some(dropbear_engine::gilrs::Button::RightTrigger2),
+            11 => Some(dropbear_engine::gilrs::Button::Select),
+            12 => Some(dropbear_engine::gilrs::Button::Start),
+            13 => Some(dropbear_engine::gilrs::Button::Mode),
+            14 => Some(dropbear_engine::gilrs::Button::LeftThumb),
+            15 => Some(dropbear_engine::gilrs::Button::RightThumb),
+            16 => Some(dropbear_engine::gilrs::Button::DPadUp),
+            17 => Some(dropbear_engine::gilrs::Button::DPadDown),
+            18 => Some(dropbear_engine::gilrs::Button::DPadLeft),
+            19 => Some(dropbear_engine::gilrs::Button::DPadRight),
             _ => None,
         }
     }
 
-    pub fn get_gamepad_id(input: &InputState, target: usize) -> Option<GamepadId> {
+    pub fn get_gamepad_id(input: &InputState, target: usize) -> Option<dropbear_engine::gilrs::GamepadId> {
         input.connected_gamepads.iter().find(|g| usize::from(**g) == target).copied()
     }
 
@@ -122,112 +125,39 @@ pub mod shared {
     }
 }
 
-pub mod jni {
-    #![allow(non_snake_case)]
-
-    use jni::JNIEnv;
-    use jni::objects::JClass;
-    use jni::sys::{jboolean, jint, jlong, jobject};
-    use crate::input::InputState;
-    use crate::scripting::jni::utils::ToJObject;
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_GamepadNative_isGamepadButtonPressed(
-        _env: JNIEnv,
-        _class: JClass,
-        input_ptr: jlong,
-        gamepad_id: jlong,
-        button_ordinal: jint,
-    ) -> jboolean {
-        let input = crate::convert_ptr!(input_ptr => InputState);
-        if super::shared::is_gamepad_button_pressed(&input, gamepad_id as u64, button_ordinal) {
-            1
-        } else {
-            0
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_GamepadNative_getLeftStickPosition(
-        mut env: JNIEnv,
-        _class: JClass,
-        input_ptr: jlong,
-        gamepad_id: jlong,
-    ) -> jobject {
-        let input = crate::convert_ptr!(input_ptr => InputState);
-        let vec = super::shared::get_left_stick(&input, gamepad_id as u64);
-
-        match vec.to_jobject(&mut env) {
-            Ok(obj) => obj.into_raw(),
-            Err(_) => std::ptr::null_mut(),
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_input_GamepadNative_getRightStickPosition(
-        mut env: JNIEnv,
-        _class: JClass,
-        input_ptr: jlong,
-        gamepad_id: jlong,
-    ) -> jobject {
-        let input = crate::convert_ptr!(input_ptr => InputState);
-        let vec = super::shared::get_right_stick(&input, gamepad_id as u64);
-
-        match vec.to_jobject(&mut env) {
-            Ok(obj) => obj.into_raw(),
-            Err(_) => std::ptr::null_mut(),
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.GamepadNative", func = "isGamepadButtonPressed"),
+    c
+)]
+fn is_button_pressed(
+    #[dropbear_macro::define(InputStatePtr)]
+    input: &InputState,
+    gamepad_id: u64,
+    button_ordinal: i32,
+) -> DropbearNativeResult<bool> {
+    Ok(shared::is_gamepad_button_pressed(&input, gamepad_id, button_ordinal))
 }
 
-#[dropbear_macro::impl_c_api]
-pub mod native {
-    use crate::ptr::InputStatePtr;
-    use crate::input::{InputState};
-    use crate::convert_ptr;
-    use crate::scripting::result::DropbearNativeResult;
-    use crate::types::NVector2;
-
-    pub fn dropbear_is_gamepad_button_pressed(
-        input_ptr: InputStatePtr,
-        gamepad_id: u64,
-        button_ordinal: i32
-    ) -> DropbearNativeResult<bool> {
-        let input = convert_ptr!(input_ptr => InputState);
-        let result = super::shared::is_gamepad_button_pressed(input, gamepad_id, button_ordinal);
-        DropbearNativeResult::Ok(result)
-    }
-
-    pub fn dropbear_get_left_stick_position(
-        input_ptr: InputStatePtr,
-        gamepad_id: u64
-    ) -> DropbearNativeResult<NVector2> {
-        let input = convert_ptr!(input_ptr => InputState);
-        let vec = super::shared::get_left_stick(input, gamepad_id);
-        DropbearNativeResult::Ok(vec)
-    }
-
-    pub fn dropbear_get_right_stick_position(
-        input_ptr: InputStatePtr,
-        gamepad_id: u64
-    ) -> DropbearNativeResult<NVector2> {
-        let input = convert_ptr!(input_ptr => InputState);
-        let vec = super::shared::get_right_stick(input, gamepad_id);
-        DropbearNativeResult::Ok(vec)
-    }
-
-    pub fn dropbear_free_gamepads_array(
-        ptr: *mut u64,
-        count: usize,
-    ) -> DropbearNativeResult<()> {
-        if ptr.is_null() {
-            return DropbearNativeResult::Ok(());
-        }
-
-        unsafe {
-            let _ = Vec::from_raw_parts(ptr, count, count);
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.GamepadNative", func = "getLeftStickPosition"),
+    c
+)]
+fn get_left_stick_position(
+    #[dropbear_macro::define(InputStatePtr)]
+    input: &InputState,
+    gamepad_id: u64,
+) -> DropbearNativeResult<NVector2> {
+    Ok(shared::get_left_stick(&input, gamepad_id))
+}
 
-        DropbearNativeResult::Ok(())
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.input.GamepadNative", func = "getRightStickPosition"),
+    c
+)]
+fn get_right_stick_position(
+    #[dropbear_macro::define(InputStatePtr)]
+    input: &InputState,
+    gamepad_id: u64,
+) -> DropbearNativeResult<NVector2> {
+    Ok(shared::get_right_stick(&input, gamepad_id))
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/lib.rs b/crates/eucalyptus-core/src/lib.rs
index dfa5110..3fa5c48 100644
--- a/crates/eucalyptus-core/src/lib.rs
+++ b/crates/eucalyptus-core/src/lib.rs
@@ -1,12 +1,10 @@
 pub mod lighting;
 pub mod camera;
-pub mod component;
 pub mod config;
 pub mod hierarchy;
 pub mod input;
 pub mod logging;
 pub mod ptr;
-pub mod result;
 pub mod runtime;
 pub mod scene;
 pub mod scripting;
@@ -21,7 +19,6 @@ pub mod mesh;
 pub mod entity;
 pub mod engine;
 pub mod transform;
-pub mod ui;
 pub mod asset;
 
 pub use dropbear_macro as macros;
@@ -39,7 +36,6 @@ use crate::physics::collider::ColliderGroup;
 use crate::physics::kcc::KCC;
 use crate::physics::rigidbody::RigidBody;
 use crate::states::{Camera3D, Light, Script, SerializedMeshRenderer};
-use crate::ui::UIComponent;
 
 /// The appdata directory for storing any information.
 ///
@@ -69,7 +65,6 @@ pub fn register_components(
     component_registry.register_with_default::<RigidBody>();
     component_registry.register_with_default::<ColliderGroup>();
     component_registry.register_with_default::<KCC>();
-    component_registry.register_with_default::<UIComponent>();
     component_registry.register_with_default::<AnimationComponent>();
 
     component_registry.register_converter::<MeshRenderer, SerializedMeshRenderer, _>(
diff --git a/crates/eucalyptus-core/src/lighting.rs b/crates/eucalyptus-core/src/lighting.rs
index 0aa1dd2..52dbc2a 100644
--- a/crates/eucalyptus-core/src/lighting.rs
+++ b/crates/eucalyptus-core/src/lighting.rs
@@ -1,19 +1,25 @@
+use crate::ptr::WorldPtr;
 use crate::scripting::jni::utils::{FromJObject, ToJObject};
 use crate::scripting::native::DropbearNativeError;
 use crate::scripting::result::DropbearNativeResult;
-use glam::DVec3;
-use ::jni::objects::{JObject, JValue};
-use ::jni::JNIEnv;
-
+use crate::types::NVector3;
+use dropbear_engine::entity::{EntityTransform, Transform};
+use dropbear_engine::lighting::{LightComponent, LightType};
+use glam::{DQuat, DVec3};
+use hecs::{Entity, World};
+use jni::objects::{JObject, JValue};
+use jni::JNIEnv;
+
+#[repr(C)]
 #[derive(Clone, Copy, Debug)]
-struct JvmColour {
+struct NColour {
 	r: u8,
 	g: u8,
 	b: u8,
 	a: u8,
 }
 
-impl JvmColour {
+impl NColour {
 	fn to_linear_rgb(self) -> DVec3 {
 		DVec3::new(
 			self.r as f64 / 255.0,
@@ -37,7 +43,7 @@ impl JvmColour {
 	}
 }
 
-impl FromJObject for JvmColour {
+impl FromJObject for NColour {
 	fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
 		let class = env
 			.find_class("com/dropbear/utils/Colour")
@@ -68,7 +74,7 @@ impl FromJObject for JvmColour {
 	}
 }
 
-impl ToJObject for JvmColour {
+impl ToJObject for NColour {
 	fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
 		let class = env
 			.find_class("com/dropbear/utils/Colour")
@@ -86,13 +92,14 @@ impl ToJObject for JvmColour {
 	}
 }
 
+#[repr(C)]
 #[derive(Clone, Copy, Debug)]
-struct JvmRange {
+struct NRange {
 	start: f32,
 	end: f32,
 }
 
-impl FromJObject for JvmRange {
+impl FromJObject for NRange {
 	fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
 		let class = env
 			.find_class("com/dropbear/utils/Range")
@@ -121,7 +128,7 @@ impl FromJObject for JvmRange {
 	}
 }
 
-impl ToJObject for JvmRange {
+impl ToJObject for NRange {
 	fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
 		let class = env
 			.find_class("com/dropbear/utils/Range")
@@ -137,14 +144,15 @@ impl ToJObject for JvmRange {
 	}
 }
 
+#[repr(C)]
 #[derive(Clone, Copy, Debug)]
-struct JvmAttenuation {
+struct NAttenuation {
 	constant: f32,
 	linear: f32,
 	quadratic: f32,
 }
 
-impl FromJObject for JvmAttenuation {
+impl FromJObject for NAttenuation {
 	fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
 		let class = env
 			.find_class("com/dropbear/lighting/Attenuation")
@@ -183,7 +191,7 @@ impl FromJObject for JvmAttenuation {
 	}
 }
 
-impl ToJObject for JvmAttenuation {
+impl ToJObject for NAttenuation {
 	fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
 		let class = env
 			.find_class("com/dropbear/lighting/Attenuation")
@@ -201,591 +209,462 @@ impl ToJObject for JvmAttenuation {
 }
 
 pub mod shared {
-	use dropbear_engine::lighting::LightComponent;
 	use hecs::{Entity, World};
 
 	pub fn light_exists_for_entity(world: &World, entity: Entity) -> bool {
-		world.get::<&LightComponent>(entity).is_ok()
+		world.get::<&dropbear_engine::lighting::LightComponent>(entity).is_ok()
 	}
 }
 
-pub mod jni {
-	#![allow(non_snake_case)]
-
-	use super::{JvmAttenuation, JvmColour, JvmRange};
-	use crate::scripting::jni::utils::{FromJObject, ToJObject};
-	use crate::types::NVector3;
-	use crate::{convert_jlong_to_entity, convert_ptr};
-	use dropbear_engine::entity::{EntityTransform, Transform};
-	use dropbear_engine::lighting::{LightComponent, LightType};
-	use glam::{DQuat, DVec3};
-	use hecs::World;
-	use jni::objects::{JClass, JObject};
-	use jni::sys::{jboolean, jdouble, jint, jlong, jobject};
-	use jni::JNIEnv;
-
-	fn get_transform(world: &World, entity: hecs::Entity) -> Option<Transform> {
-		if let Ok(et) = world.get::<&EntityTransform>(entity) {
-			Some(et.sync())
-		} else if let Ok(t) = world.get::<&Transform>(entity) {
-			Some(*t)
-		} else {
-			None
-		}
-	}
-
-	fn set_transform_position(world: &mut World, entity: hecs::Entity, position: DVec3) -> bool {
-		if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) {
-			et.local_mut().position = position;
-			true
-		} else if let Ok(mut t) = world.get::<&mut Transform>(entity) {
-			t.position = position;
-			true
-		} else {
-			false
-		}
-	}
-
-	fn set_transform_rotation(world: &mut World, entity: hecs::Entity, rotation: DQuat) -> bool {
-		if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) {
-			et.local_mut().rotation = rotation;
-			true
-		} else if let Ok(mut t) = world.get::<&mut Transform>(entity) {
-			t.rotation = rotation;
-			true
-		} else {
-			false
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_lightExistsForEntity(
-		_env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-	) -> jboolean {
-		let world = convert_ptr!(world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-		if world.get::<&LightComponent>(entity).is_ok() {
-			1
-		} else {
-			0
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_getPosition(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-	) -> jobject {
-		let world = convert_ptr!(world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Some(t) = get_transform(world, entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing Transform/EntityTransform");
-			return std::ptr::null_mut();
-		};
-
-		match NVector3::from(t.position).to_jobject(&mut env) {
-			Ok(obj) => obj.into_raw(),
-			Err(_) => std::ptr::null_mut(),
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_setPosition(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-		position: JObject,
-	) {
-		let world = convert_ptr!(mut world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let position: DVec3 = match NVector3::from_jobject(&mut env, &position) {
-			Ok(v) => v.into(),
-			Err(e) => {
-				let _ = env.throw_new(
-					"java/lang/IllegalArgumentException",
-					format!("Invalid Vector3d: {:?}", e),
-				);
-				return;
-			}
-		};
-
-		if !set_transform_position(world, entity, position) {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing Transform/EntityTransform");
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_getDirection(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-	) -> jobject {
-		let world = convert_ptr!(world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Some(t) = get_transform(world, entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing Transform/EntityTransform");
-			return std::ptr::null_mut();
-		};
-
-		let forward = DVec3::new(0.0, 0.0, -1.0);
-		let dir = (t.rotation * forward).normalize_or_zero();
-
-		match NVector3::from(dir).to_jobject(&mut env) {
-			Ok(obj) => obj.into_raw(),
-			Err(_) => std::ptr::null_mut(),
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_setDirection(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-		direction: JObject,
-	) {
-		let world = convert_ptr!(mut world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let dir: DVec3 = match NVector3::from_jobject(&mut env, &direction) {
-			Ok(v) => DVec3::from(v),
-			Err(e) => {
-				let _ = env.throw_new(
-					"java/lang/IllegalArgumentException",
-					format!("Invalid Vector3d: {:?}", e),
-				);
-				return;
-			}
-		};
-
-		let desired = dir.normalize_or_zero();
-		if desired.length_squared() < 1e-12 {
-			let _ = env.throw_new("java/lang/IllegalArgumentException", "Direction must be non-zero");
-			return;
-		}
-
-		let forward = DVec3::new(0.0, 0.0, -1.0);
-		let rotation = DQuat::from_rotation_arc(forward, desired);
-
-		if !set_transform_rotation(world, entity, rotation) {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing Transform/EntityTransform");
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_getColour(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-	) -> jobject {
-		let world = convert_ptr!(world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(light) = world.get::<&LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return std::ptr::null_mut();
-		};
-
-		let colour = JvmColour::from_linear_rgb(light.colour);
-		match colour.to_jobject(&mut env) {
-			Ok(obj) => obj.into_raw(),
-			Err(_) => std::ptr::null_mut(),
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_setColour(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-		colour: JObject,
-	) {
-		let world = convert_ptr!(mut world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(mut light) = world.get::<&mut LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return;
-		};
-
-		let colour = match JvmColour::from_jobject(&mut env, &colour) {
-			Ok(c) => c,
-			Err(e) => {
-				let _ = env.throw_new(
-					"java/lang/IllegalArgumentException",
-					format!("Invalid Colour: {:?}", e),
-				);
-				return;
-			}
-		};
-
-		light.colour = colour.to_linear_rgb();
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_getLightType(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-	) -> jint {
-		let world = convert_ptr!(world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(light) = world.get::<&LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return -1;
-		};
-
-		light.light_type as i32
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_setLightType(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-		light_type: jint,
-	) {
-		let world = convert_ptr!(mut world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(mut light) = world.get::<&mut LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return;
-		};
-
-		light.light_type = match light_type {
-			0 => LightType::Directional,
-			1 => LightType::Point,
-			2 => LightType::Spot,
-			_ => {
-				let _ = env.throw_new("java/lang/IllegalArgumentException", "Invalid lightType ordinal");
-				return;
-			}
-		};
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_getIntensity(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-	) -> jdouble {
-		let world = convert_ptr!(world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(light) = world.get::<&LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return f64::NAN;
-		};
-
-		light.intensity as f64
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_setIntensity(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-		intensity: jdouble,
-	) {
-		let world = convert_ptr!(mut world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(mut light) = world.get::<&mut LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return;
-		};
-
-		light.intensity = intensity as f32;
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_getAttenuation(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-	) -> jobject {
-		let world = convert_ptr!(world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(light) = world.get::<&LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return std::ptr::null_mut();
-		};
-
-		let att = JvmAttenuation {
-			constant: light.attenuation.constant,
-			linear: light.attenuation.linear,
-			quadratic: light.attenuation.quadratic,
-		};
-
-		match att.to_jobject(&mut env) {
-			Ok(obj) => obj.into_raw(),
-			Err(_) => std::ptr::null_mut(),
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_setAttenuation(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-		attenuation: JObject,
-	) {
-		let world = convert_ptr!(mut world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(mut light) = world.get::<&mut LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return;
-		};
+fn get_transform(world: &World, entity: Entity) -> DropbearNativeResult<Transform> {
+    if let Ok(et) = world.get::<&EntityTransform>(entity) {
+        Ok(et.sync())
+    } else if let Ok(t) = world.get::<&Transform>(entity) {
+        Ok(*t)
+    } else {
+        Err(DropbearNativeError::MissingComponent)
+    }
+}
 
-		let att = match JvmAttenuation::from_jobject(&mut env, &attenuation) {
-			Ok(a) => a,
-			Err(e) => {
-				let _ = env.throw_new(
-					"java/lang/IllegalArgumentException",
-					format!("Invalid Attenuation: {:?}", e),
-				);
-				return;
-			}
-		};
+fn set_transform_position(
+    world: &mut World,
+    entity: Entity,
+    position: DVec3,
+) -> DropbearNativeResult<()> {
+    if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) {
+        et.local_mut().position = position;
+        Ok(())
+    } else if let Ok(mut t) = world.get::<&mut Transform>(entity) {
+        t.position = position;
+        Ok(())
+    } else {
+        Err(DropbearNativeError::MissingComponent)
+    }
+}
 
-		// Kotlin exposes only coefficients; preserve existing `range`.
-		light.attenuation.constant = att.constant;
-		light.attenuation.linear = att.linear;
-		light.attenuation.quadratic = att.quadratic;
-	}
+fn set_transform_rotation(
+    world: &mut World,
+    entity: Entity,
+    rotation: DQuat,
+) -> DropbearNativeResult<()> {
+    if let Ok(mut et) = world.get::<&mut EntityTransform>(entity) {
+        et.local_mut().rotation = rotation;
+        Ok(())
+    } else if let Ok(mut t) = world.get::<&mut Transform>(entity) {
+        t.rotation = rotation;
+        Ok(())
+    } else {
+        Err(DropbearNativeError::MissingComponent)
+    }
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_getEnabled(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-	) -> jboolean {
-		let world = convert_ptr!(world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(light) = world.get::<&LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return 0;
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "lightExistsForEntity"),
+    c
+)]
+fn light_exists_for_entity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<bool> {
+    Ok(shared::light_exists_for_entity(world, entity))
+}
 
-		if light.enabled { 1 } else { 0 }
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "getPosition"),
+    c
+)]
+fn get_position(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<NVector3> {
+    let transform = get_transform(world, entity)?;
+    Ok(NVector3::from(transform.position))
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_setEnabled(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-		enabled: jboolean,
-	) {
-		let world = convert_ptr!(mut world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(mut light) = world.get::<&mut LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return;
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "setPosition"),
+    c
+)]
+fn set_position(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    position: &NVector3,
+) -> DropbearNativeResult<()> {
+    set_transform_position(world, entity, (*position).into())
+}
 
-		light.enabled = enabled != 0;
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "getDirection"),
+    c
+)]
+fn get_direction(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<NVector3> {
+    let transform = get_transform(world, entity)?;
+    let forward = DVec3::new(0.0, 0.0, -1.0);
+    let dir = (transform.rotation * forward).normalize_or_zero();
+    Ok(NVector3::from(dir))
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_getCutoffAngle(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-	) -> jdouble {
-		let world = convert_ptr!(world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(light) = world.get::<&LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return f64::NAN;
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "setDirection"),
+    c
+)]
+fn set_direction(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    direction: &NVector3,
+) -> DropbearNativeResult<()> {
+    let dir: DVec3 = (*direction).into();
+    let desired = dir.normalize_or_zero();
+    if desired.length_squared() < 1e-12 {
+        return Err(DropbearNativeError::InvalidArgument);
+    }
+
+    let forward = DVec3::new(0.0, 0.0, -1.0);
+    let rotation = DQuat::from_rotation_arc(forward, desired);
+    set_transform_rotation(world, entity, rotation)
+}
 
-		light.cutoff_angle as f64
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "getColour"),
+    c
+)]
+fn get_colour(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<NColour> {
+    let light = world
+        .get::<&LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    Ok(NColour::from_linear_rgb(light.colour))
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_setCutoffAngle(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-		cutoff_angle: jdouble,
-	) {
-		let world = convert_ptr!(mut world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(mut light) = world.get::<&mut LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return;
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "setColour"),
+    c
+)]
+fn set_colour(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    colour: &NColour,
+) -> DropbearNativeResult<()> {
+    let mut light = world
+        .get::<&mut LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    light.colour = (*colour).to_linear_rgb();
+    Ok(())
+}
 
-		light.cutoff_angle = cutoff_angle as f32;
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "getLightType"),
+    c
+)]
+fn get_light_type(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<i32> {
+    let light = world
+        .get::<&LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    Ok(light.light_type as i32)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_getOuterCutoffAngle(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-	) -> jdouble {
-		let world = convert_ptr!(world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(light) = world.get::<&LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return f64::NAN;
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "setLightType"),
+    c
+)]
+fn set_light_type(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    light_type: i32,
+) -> DropbearNativeResult<()> {
+    let mut light = world
+        .get::<&mut LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    light.light_type = match light_type {
+        0 => LightType::Directional,
+        1 => LightType::Point,
+        2 => LightType::Spot,
+        _ => return Err(DropbearNativeError::InvalidArgument),
+    };
+
+    Ok(())
+}
 
-		light.outer_cutoff_angle as f64
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "getIntensity"),
+    c
+)]
+fn get_intensity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<f64> {
+    let light = world
+        .get::<&LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    Ok(light.intensity as f64)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_setOuterCutoffAngle(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-		outer_cutoff_angle: jdouble,
-	) {
-		let world = convert_ptr!(mut world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(mut light) = world.get::<&mut LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return;
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "setIntensity"),
+    c
+)]
+fn set_intensity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    intensity: f64,
+) -> DropbearNativeResult<()> {
+    let mut light = world
+        .get::<&mut LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    light.intensity = intensity as f32;
+    Ok(())
+}
 
-		light.outer_cutoff_angle = outer_cutoff_angle as f32;
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "getAttenuation"),
+    c
+)]
+fn get_attenuation(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<NAttenuation> {
+    let light = world
+        .get::<&LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    Ok(NAttenuation {
+        constant: light.attenuation.constant,
+        linear: light.attenuation.linear,
+        quadratic: light.attenuation.quadratic,
+    })
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_getCastsShadows(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-	) -> jboolean {
-		let world = convert_ptr!(world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(light) = world.get::<&LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return 0;
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "setAttenuation"),
+    c
+)]
+fn set_attenuation(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    attenuation: &NAttenuation,
+) -> DropbearNativeResult<()> {
+    let mut light = world
+        .get::<&mut LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    light.attenuation.constant = attenuation.constant;
+    light.attenuation.linear = attenuation.linear;
+    light.attenuation.quadratic = attenuation.quadratic;
+    Ok(())
+}
 
-		if light.cast_shadows { 1 } else { 0 }
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "getEnabled"),
+    c
+)]
+fn get_enabled(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<bool> {
+    let light = world
+        .get::<&LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    Ok(light.enabled)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_setCastsShadows(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-		casts_shadows: jboolean,
-	) {
-		let world = convert_ptr!(mut world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(mut light) = world.get::<&mut LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return;
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "setEnabled"),
+    c
+)]
+fn set_enabled(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    enabled: bool,
+) -> DropbearNativeResult<()> {
+    let mut light = world
+        .get::<&mut LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    light.enabled = enabled;
+    Ok(())
+}
 
-		light.cast_shadows = casts_shadows != 0;
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "getCutoffAngle"),
+    c
+)]
+fn get_cutoff_angle(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<f64> {
+    let light = world
+        .get::<&LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    Ok(light.cutoff_angle as f64)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_getDepth(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-	) -> jobject {
-		let world = convert_ptr!(world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(light) = world.get::<&LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return std::ptr::null_mut();
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "setCutoffAngle"),
+    c
+)]
+fn set_cutoff_angle(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    cutoff_angle: f64,
+) -> DropbearNativeResult<()> {
+    let mut light = world
+        .get::<&mut LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    light.cutoff_angle = cutoff_angle as f32;
+    Ok(())
+}
 
-		let range = JvmRange {
-			start: light.depth.start,
-			end: light.depth.end,
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "getOuterCutoffAngle"),
+    c
+)]
+fn get_outer_cutoff_angle(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<f64> {
+    let light = world
+        .get::<&LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    Ok(light.outer_cutoff_angle as f64)
+}
 
-		match range.to_jobject(&mut env) {
-			Ok(obj) => obj.into_raw(),
-			Err(_) => std::ptr::null_mut(),
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "setOuterCutoffAngle"),
+    c
+)]
+fn set_outer_cutoff_angle(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    outer_cutoff_angle: f64,
+) -> DropbearNativeResult<()> {
+    let mut light = world
+        .get::<&mut LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    light.outer_cutoff_angle = outer_cutoff_angle as f32;
+    Ok(())
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_lighting_LightNative_setDepth(
-		mut env: JNIEnv,
-		_class: JClass,
-		world_ptr: jlong,
-		entity_id: jlong,
-		depth: JObject,
-	) {
-		let world = convert_ptr!(mut world_ptr => World);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		let Ok(mut light) = world.get::<&mut LightComponent>(entity) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Entity missing LightComponent");
-			return;
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "getCastsShadows"),
+    c
+)]
+fn get_casts_shadows(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<bool> {
+    let light = world
+        .get::<&LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    Ok(light.cast_shadows)
+}
 
-		let range = match JvmRange::from_jobject(&mut env, &depth) {
-			Ok(r) => r,
-			Err(e) => {
-				let _ = env.throw_new(
-					"java/lang/IllegalArgumentException",
-					format!("Invalid Range: {:?}", e),
-				);
-				return;
-			}
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "setCastsShadows"),
+    c
+)]
+fn set_casts_shadows(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    casts_shadows: bool,
+) -> DropbearNativeResult<()> {
+    let mut light = world
+        .get::<&mut LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    light.cast_shadows = casts_shadows;
+    Ok(())
+}
 
-		if !(range.start.is_finite() && range.end.is_finite()) {
-			let _ = env.throw_new("java/lang/IllegalArgumentException", "Depth range must be finite");
-			return;
-		}
-		if range.end <= range.start {
-			let _ = env.throw_new("java/lang/IllegalArgumentException", "Depth range must satisfy start < end");
-			return;
-		}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "getDepth"),
+    c
+)]
+fn get_depth(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<NRange> {
+    let light = world
+        .get::<&LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+
+    Ok(NRange {
+        start: light.depth.start,
+        end: light.depth.end,
+    })
+}
 
-		light.depth = range.start..range.end;
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.lighting.LightNative", func = "setDepth"),
+    c
+)]
+fn set_depth(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    depth: &NRange,
+) -> DropbearNativeResult<()> {
+    if !(depth.start.is_finite() && depth.end.is_finite()) {
+        return Err(DropbearNativeError::InvalidArgument);
+    }
+    if depth.end <= depth.start {
+        return Err(DropbearNativeError::InvalidArgument);
+    }
+
+    let mut light = world
+        .get::<&mut LightComponent>(entity)
+        .map_err(|_| DropbearNativeError::MissingComponent)?;
+    light.depth = depth.start..depth.end;
+    Ok(())
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/mesh.rs b/crates/eucalyptus-core/src/mesh.rs
index 554fb03..0649945 100644
--- a/crates/eucalyptus-core/src/mesh.rs
+++ b/crates/eucalyptus-core/src/mesh.rs
@@ -1,427 +1,263 @@
 pub mod shared {
+    use dropbear_engine::asset::AssetRegistry;
     use dropbear_engine::entity::MeshRenderer;
+    use dropbear_engine::model::Model;
     use hecs::{Entity, World};
 
     pub fn mesh_renderer_exists_for_entity(world: &World, entity: Entity) -> bool {
         world.get::<&MeshRenderer>(entity).is_ok()
     }
 
-    pub fn resolve_target_material_name(
-        renderer: &MeshRenderer,
-        target_identifier: &str
-    ) -> Option<String> {
-        if let Some(cached) = renderer.resolve_texture_identifier(target_identifier) {
-            return Some(cached.to_string());
-        }
-
-        let model = renderer.model();
-
-        model.materials.iter().find_map(|mat| {
-            if mat.name == target_identifier { return Some(mat.name.clone()); }
-            if let Some(tag) = &mat.texture_tag { if tag == target_identifier { return Some(mat.name.clone()); } }
-            None
-        })
+    fn matches_material_label(material: &dropbear_engine::model::Material, target: &str) -> bool {
+        material.texture_tag.as_deref() == Some(target) || material.name == target
     }
-}
-
-pub mod jni {
-    #![allow(non_snake_case)]
 
-    use crate::return_boxed;
-    use dropbear_engine::asset::PointerKind::Const;
-    use dropbear_engine::asset::{AssetHandle, AssetRegistry};
-    use dropbear_engine::entity::MeshRenderer;
-    use dropbear_engine::model::Model;
-    use hecs::World;
-    use jni::objects::{JClass, JString};
-    use jni::sys::{jboolean, jlong, jlongArray, jobject, jsize};
-    use jni::JNIEnv;
-    use parking_lot::Mutex;
-    use std::collections::HashMap;
-    use std::sync::Arc;
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_MeshRendererNative_meshRendererExistsForEntity(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jboolean {
-        let world = crate::convert_ptr!(world_ptr => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        super::shared::mesh_renderer_exists_for_entity(&world, entity) as jboolean
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_MeshRendererNative_getModel(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jlong {
-        let world = crate::convert_ptr!(world_ptr => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-
-        if let Ok(mesh) = world.get::<&MeshRenderer>(entity) {
-            mesh.model_id().raw() as jlong
-        } else {
-            let _ = env.throw_new("java/lang/RuntimeException", "Entity does not have a MeshRenderer component");
-            -1
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_MeshRendererNative_setModel(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        asset_ptr: jlong,
-        entity_id: jlong,
-        model_id: jlong,
-    ) {
-        let world = crate::convert_ptr!(world_ptr => World);
-        let asset = crate::convert_ptr!(asset_ptr => AssetRegistry);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-
-        if let Ok(mut mesh) = world.get::<&mut MeshRenderer>(entity) {
-            match mesh.set_asset_handle_raw(asset, AssetHandle::new(model_id as u32)) {
-                Ok(_) => {}
-                Err(e) => {
-                    let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to set model: {}", e));
-                }
-            }
-        } else {
-            let _ = env.throw_new("java/lang/RuntimeException", "Entity does not have a MeshRenderer component");
-        }
+    pub fn resolve_target_material_index(
+        model: &Model,
+        target_identifier: &str,
+    ) -> Option<usize> {
+        model
+            .materials
+            .iter()
+            .position(|mat| matches_material_label(mat, target_identifier))
     }
 
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_MeshRendererNative_getAllTextureIds(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        asset_ptr: jlong,
-        entity_id: jlong,
-    ) -> jlongArray {
-        let world = crate::convert_ptr!(world_ptr => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let asset = crate::convert_ptr!(asset_ptr => AssetRegistry);
-
-        if let Ok(renderer) = world.get::<&mut MeshRenderer>(entity) {
-            let handles = renderer.collect_all_material_handles_raw(&asset);
-            let jarray = match env.new_long_array(handles.len() as jsize) {
-                Ok(val) => val,
-                Err(_) => {
-                    let _ = env.throw_new("java/lang/OutOfMemoryError", "Could not allocate texture ID array");
-                    return std::ptr::null_mut();
-                }
-            };
-
-            if let Err(e) = env.set_long_array_region(&jarray, 0, handles.iter().map(|v| v.raw() as jlong).collect::<Vec<_>>().as_slice()) {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("{:?}", e));
-                return std::ptr::null_mut();
-            }
-
-            jarray.into_raw()
-        } else {
-            let _ = env.throw_new("java/lang/RuntimeException", "Entity does not have a MeshRenderer component");
-            std::ptr::null_mut()
-        }
+    pub fn resolve_target_material_name(
+        model: &Model,
+        target_identifier: &str,
+    ) -> Option<String> {
+        model
+            .materials
+            .iter()
+            .find(|mat| matches_material_label(mat, target_identifier))
+            .map(|mat| mat.name.clone())
     }
 
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_MeshRendererNative_getTexture(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        asset_ptr: jlong,
-        entity_id: jlong,
-        material_name: JString,
-    ) -> jobject {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let asset = crate::convert_ptr!(asset_ptr => dropbear_engine::asset::AssetRegistry);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let name = crate::convert_jstring!(env, material_name);
-
-        let handle_value: Option<i64> = if let Ok(renderer) = world.get::<&dropbear_engine::entity::MeshRenderer>(entity) {
-            renderer.material_handle_raw(asset, &name)
-                .map(|h| h.raw() as i64)
-        } else {
-            None
-        };
-
-        return_boxed!(&mut env, handle_value, "(J)Ljava/lang/Long;", "java/lang/Long")
+    pub fn model_for_renderer<'a>(
+        registry: &'a AssetRegistry,
+        renderer: &MeshRenderer,
+    ) -> Option<&'a Model> {
+        registry.get_model(renderer.model())
     }
+}
 
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_MeshRendererNative_setTextureOverride(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        asset_handle: jlong,
-        entity_id: jlong,
-        material_name: JString,
-        texture_handle: jlong,
-    ) {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let asset = crate::convert_ptr!(asset_handle => dropbear_engine::asset::AssetRegistry);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let target_identifier = crate::convert_jstring!(env, material_name);
-        let new_texture_handle = dropbear_engine::asset::AssetHandle::new(texture_handle as u64);
-
-        if let Ok(mut mesh) = world.get::<&mut dropbear_engine::entity::MeshRenderer>(entity) {
-            let resolved_target = super::shared::resolve_target_material_name(&mesh, &target_identifier);
-
-            let Some(target_material_name) = resolved_target else {
-                let _ = env.throw_new(
-                    "java/lang/IllegalArgumentException",
-                    format!(
-                        "Could not resolve material identifier '{}' on model '{}'",
-                        target_identifier,
-                        mesh.model().label
-                    ),
-                );
-                return;
-            };
-
-            if !asset.contains_handle(new_texture_handle) {
-                let _ = env.throw_new("java/lang/IllegalArgumentException", "Invalid texture handle provided");
-                return;
-            }
-
-            if !asset.is_material(new_texture_handle) {
-                let _ = env.throw_new("java/lang/IllegalArgumentException", "Handle provided is not a material/texture");
-                return;
-            }
-
-            let Some(source_material) = asset.get_material(new_texture_handle) else { return; };
-
-            let Some(owner_model_id) = asset.material_owner(new_texture_handle) else {
-                let _ = env.throw_new("java/lang/IllegalStateException", "Texture handle has no owner model in registry");
-                return;
-            };
-
-            let Some(owner_model_handle) = asset.model_handle_from_id(owner_model_id) else {
-                let _ = env.throw_new("java/lang/IllegalStateException", "Texture owner model not found in registry");
-                return;
-            };
-
-            let Some(source_reference) = asset.model_reference_for_handle(owner_model_handle) else {
-                let _ = env.throw_new("java/lang/IllegalStateException", "Texture owner model has no resource reference");
-                return;
-            };
-
-            let Some(model_cache) = asset.get_pointer(Const("model_cache")) else {
-                let _ = env.throw_new("java/lang/IllegalArgumentException", "Unable to locate model_cache pointer within the asset registry");
-                return;
-            };
-
-            let model_cache = model_cache as *const Mutex<HashMap<String, Arc<Model>>>;
-            if model_cache.is_null() {
-                let _ = env.throw_new("java/lang/IllegalStateException", "Model cache cannot be null");
-                return;
-            }
-
-            let model_cache = unsafe { &*model_cache };
-
-            if let Err(e) = mesh.apply_material_override_raw(
-                asset,
-                model_cache,
-                &target_material_name,
-                source_reference,
-                &source_material.name,
-            ) {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to apply texture override: {}", e));
-            }
+use crate::ptr::{AssetRegistryPtr, AssetRegistryUnwrapped, GraphicsContextPtr, WorldPtr};
+use crate::scripting::native::DropbearNativeError;
+use crate::scripting::result::DropbearNativeResult;
+use dropbear_engine::asset::Handle;
+use dropbear_engine::entity::MeshRenderer;
+use dropbear_engine::graphics::SharedGraphicsContext;
+use dropbear_engine::texture::Texture;
+use std::collections::HashSet;
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.MeshRendererNative", func = "meshRendererExistsForEntity")
+)]
+fn mesh_renderer_exists_for_entity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<bool> {
+    Ok(shared::mesh_renderer_exists_for_entity(world, entity))
+}
 
-        } else {
-            let _ = env.throw_new("java/lang/RuntimeException", "Entity does not have a MeshRenderer component");
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.MeshRendererNative", func = "getModel")
+)]
+fn get_model(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<u64> {
+    if let Ok(mesh) = world.get::<&MeshRenderer>(entity) {
+        Ok(mesh.model().id)
+    } else {
+        Err(DropbearNativeError::NoSuchComponent)
     }
 }
 
-#[dropbear_macro::impl_c_api]
-pub mod native {
-    use parking_lot::Mutex;
-    use std::collections::HashMap;
-    use std::os::raw::c_char;
-    use std::sync::Arc;
-
-    use hecs::{Entity, World};
-
-    use dropbear_engine::asset::{AssetHandle, AssetRegistry, PointerKind};
-    use dropbear_engine::entity::MeshRenderer;
-    use dropbear_engine::model::Model;
-
-    use crate::convert_ptr;
-    use crate::engine::shared::read_str;
-    use crate::ptr::{AssetRegistryPtr, WorldPtr};
-    use crate::scripting::native::DropbearNativeError;
-    use crate::scripting::result::DropbearNativeResult;
-
-    pub fn dropbear_mesh_renderer_exists_for_entity(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<bool> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        DropbearNativeResult::Ok(super::shared::mesh_renderer_exists_for_entity(world, entity))
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.MeshRendererNative", func = "setModel")
+)]
+fn set_model(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(AssetRegistryPtr)]
+    asset: &AssetRegistryUnwrapped,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    model_handle: u64,
+) -> DropbearNativeResult<()> {
+    let handle = Handle::new(model_handle);
+    if asset.read().get_model(handle).is_none() {
+        return Err(DropbearNativeError::InvalidHandle);
     }
-    
-    pub fn dropbear_get_model(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<u64> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
 
-        if let Ok(mesh) = world.get::<&MeshRenderer>(entity) {
-            DropbearNativeResult::Ok(mesh.model_id().raw())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+    if let Ok(mut mesh) = world.get::<&mut MeshRenderer>(entity) {
+        mesh.set_model(handle);
+        Ok(())
+    } else {
+        Err(DropbearNativeError::NoSuchComponent)
     }
-    
-    pub fn dropbear_set_model(
-        world_ptr: WorldPtr,
-        asset_ptr: AssetRegistryPtr,
-        entity_id: u64,
-        model_id: u64,
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => World);
-        let asset = convert_ptr!(asset_ptr => AssetRegistry);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
+}
 
-        if let Ok(mut mesh) = world.get::<&mut MeshRenderer>(entity) {
-            match mesh.set_asset_handle_raw(asset, AssetHandle::new(model_id)) {
-                Ok(_) => DropbearNativeResult::Ok(()),
-                Err(_) => DropbearNativeResult::Err(DropbearNativeError::UnknownError),
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.MeshRendererNative", func = "getAllTextureIds")
+)]
+fn get_all_texture_ids(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(AssetRegistryPtr)]
+    asset: &AssetRegistryUnwrapped,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<Vec<u64>> {
+    let reader = asset.read();
+    let renderer = world
+        .get::<&MeshRenderer>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+    let model = shared::model_for_renderer(&reader, &renderer)
+        .ok_or(DropbearNativeError::AssetNotFound)?;
+
+    let mut ids = HashSet::new();
+    let mut push_handle = |texture: &Texture| {
+        if let Some(hash) = texture.hash {
+            if let Some(handle) = reader.texture_handle_by_hash(hash) {
+                ids.insert(handle.id);
             }
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
         }
-    }
-    
-    pub fn dropbear_get_all_texture_ids(
-        world_ptr: WorldPtr,
-        asset_ptr: AssetRegistryPtr,
-        entity_id: u64,
-        out_count: *mut usize,
-    ) -> DropbearNativeResult<*mut u64> {
-        let world = convert_ptr!(world_ptr => World);
-        let asset = convert_ptr!(asset_ptr => AssetRegistry);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
+    };
 
-        if out_count.is_null() {
-            return DropbearNativeResult::Err(DropbearNativeError::NullPointer);
+    for material in &model.materials {
+        push_handle(&material.diffuse_texture);
+        push_handle(&material.normal_texture);
+        if let Some(tex) = &material.emissive_texture {
+            push_handle(tex);
         }
-
-        if let Ok(renderer) = world.get::<&MeshRenderer>(entity) {
-            let handles = renderer.collect_all_material_handles_raw(asset);
-
-            let mut raw_ids: Vec<u64> = handles.iter().map(|h| h.raw()).collect();
-
-            unsafe { *out_count = raw_ids.len(); }
-
-            let ptr = raw_ids.as_mut_ptr();
-            std::mem::forget(raw_ids);
-
-            DropbearNativeResult::Ok(ptr)
-        } else {
-            unsafe { *out_count = 0; }
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
+        if let Some(tex) = &material.metallic_roughness_texture {
+            push_handle(tex);
         }
-    }
-    
-    pub fn dropbear_get_texture(
-        world_ptr: WorldPtr,
-        asset_ptr: AssetRegistryPtr,
-        entity_id: u64,
-        material_name: *const c_char,
-    ) -> DropbearNativeResult<u64> {
-        let world = convert_ptr!(world_ptr => World);
-        let asset = convert_ptr!(asset_ptr => AssetRegistry);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let target = unsafe { read_str(material_name)? };
-
-        if let Ok(renderer) = world.get::<&MeshRenderer>(entity) {
-            match renderer.material_handle_raw(asset, &target) {
-                Some(handle) => DropbearNativeResult::Ok(handle.raw()),
-                None => DropbearNativeResult::Err(DropbearNativeError::UnknownError),
-            }
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
+        if let Some(tex) = &material.occlusion_texture {
+            push_handle(tex);
         }
     }
-    
-    pub fn dropbear_set_texture_override(
-        world_ptr: WorldPtr,
-        asset_ptr: AssetRegistryPtr,
-        entity_id: u64,
-        material_name: *const c_char,
-        texture_handle: u64,
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => World);
-        let asset = convert_ptr!(asset_ptr => AssetRegistry);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let target_identifier = unsafe { read_str(material_name)? };
-        let new_texture_handle = AssetHandle::new(texture_handle);
-
-        if let Ok(mut mesh) = world.get::<&mut MeshRenderer>(entity) {
-            let resolved_target = super::shared::resolve_target_material_name(&mesh, &target_identifier);
-            let target_material_name = match resolved_target {
-                Some(name) => name,
-                None => return DropbearNativeResult::Err(DropbearNativeError::InvalidArgument),
-            };
-
-            if !asset.contains_handle(new_texture_handle) || !asset.is_material(new_texture_handle) {
-                return DropbearNativeResult::Err(DropbearNativeError::InvalidArgument);
-            }
-
-            let source_material = match asset.get_material(new_texture_handle) {
-                Some(m) => m,
-                None => return DropbearNativeResult::Err(DropbearNativeError::UnknownError),
-            };
 
-            let owner_model_id = match asset.material_owner(new_texture_handle) {
-                Some(id) => id,
-                None => return DropbearNativeResult::Err(DropbearNativeError::QueryFailed),
-            };
-
-            let owner_model_handle = match asset.model_handle_from_id(owner_model_id) {
-                Some(h) => h,
-                None => return DropbearNativeResult::Err(DropbearNativeError::QueryFailed),
-            };
-
-            let source_reference = match asset.model_reference_for_handle(owner_model_handle) {
-                Some(r) => r,
-                None => return DropbearNativeResult::Err(DropbearNativeError::QueryFailed),
-            };
+    Ok(ids.into_iter().collect())
+}
 
-            let model_cache_ptr = match asset.get_pointer(PointerKind::Const("model_cache")) {
-                Some(ptr) => ptr as *const Mutex<HashMap<String, Arc<Model>>>,
-                None => return DropbearNativeResult::Err(DropbearNativeError::NullPointer), // Cache not found
-            };
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.MeshRendererNative", func = "getTexture"),
+    c
+)]
+fn get_texture(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(AssetRegistryPtr)]
+    asset: &AssetRegistryUnwrapped,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    material_name: String,
+) -> DropbearNativeResult<Option<u64>> {
+    let reader = asset.read();
+    let renderer = world
+        .get::<&MeshRenderer>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+    let model = shared::model_for_renderer(&reader, &renderer)
+        .ok_or(DropbearNativeError::AssetNotFound)?;
+    let idx = match shared::resolve_target_material_index(model, &material_name) {
+        Some(value) => value,
+        None => return Ok(None),
+    };
+    let material = model
+        .materials
+        .get(idx)
+        .ok_or(DropbearNativeError::InvalidArgument)?;
+
+    Ok(material
+        .diffuse_texture
+        .hash
+        .and_then(|hash| reader.texture_handle_by_hash(hash))
+        .map(|handle| handle.id))
+}
 
-            if model_cache_ptr.is_null() {
-                return DropbearNativeResult::Err(DropbearNativeError::NullPointer);
-            }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.MeshRendererNative", func = "setTextureOverride"),
+    c
+)]
+fn set_texture_override(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(AssetRegistryPtr)]
+    asset: &AssetRegistryUnwrapped,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    material_name: String,
+    texture_handle: u64,
+) -> DropbearNativeResult<()> {
+    let reader = asset.read();
+    let renderer = world
+        .get::<&MeshRenderer>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+    let model = shared::model_for_renderer(&reader, &renderer)
+        .ok_or(DropbearNativeError::AssetNotFound)?;
+    let _ = shared::resolve_target_material_name(model, &material_name)
+        .ok_or(DropbearNativeError::InvalidArgument)?;
+
+    let handle = Handle::<dropbear_engine::texture::Texture>::new(texture_handle);
+    if reader.get_texture(handle).is_none() {
+        return Err(DropbearNativeError::InvalidHandle);
+    }
 
-            let model_cache = unsafe { &*model_cache_ptr };
+    drop(reader);
 
-            match mesh.apply_material_override_raw(
-                asset,
-                model_cache,
-                &target_material_name,
-                source_reference,
-                &source_material.name,
-            ) {
-                Ok(_) => DropbearNativeResult::Ok(()),
-                Err(_) => DropbearNativeResult::Err(DropbearNativeError::WorldInsertError),
-            }
+    let mut renderer = world
+        .get::<&mut MeshRenderer>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+    renderer.set_texture_override(handle);
+    Ok(())
+}
 
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.MeshRendererNative", func = "setMaterialTint"),
+    c
+)]
+fn set_material_tint(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(AssetRegistryPtr)]
+    asset: &AssetRegistryUnwrapped,
+    #[dropbear_macro::define(GraphicsContextPtr)]
+    graphics: &SharedGraphicsContext,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    material_name: String,
+    r: f32,
+    g: f32,
+    b: f32,
+    a: f32,
+) -> DropbearNativeResult<()> {
+    let renderer = world
+        .get::<&MeshRenderer>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+    let handle = renderer.model();
+
+    let mut registry = asset.write();
+    let model = registry
+        .get_model(handle)
+        .cloned()
+        .ok_or(DropbearNativeError::AssetNotFound)?;
+    let mut model = model;
+
+    let index = shared::resolve_target_material_index(&model, &material_name)
+        .ok_or(DropbearNativeError::InvalidArgument)?;
+
+    if let Some(material) = model.materials.get_mut(index) {
+        material.tint = [r, g, b, a];
+        material.sync_uniform(graphics);
     }
+
+    registry.update_model(handle, model);
+    Ok(())
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/physics.rs b/crates/eucalyptus-core/src/physics.rs
index 3532097..e49d3e8 100644
--- a/crates/eucalyptus-core/src/physics.rs
+++ b/crates/eucalyptus-core/src/physics.rs
@@ -9,6 +9,10 @@ use rapier3d::prelude::*;
 use serde::{Deserialize, Serialize};
 use std::collections::HashMap;
 use rapier3d::control::CharacterCollision;
+use rapier3d::parry::query::{DefaultQueryDispatcher, ShapeCastOptions};
+use crate::ptr::PhysicsStatePtr;
+use crate::scripting::result::DropbearNativeResult;
+use crate::types::{IndexNative, NVector3, RayHit, NShapeCastHit, NCollider};
 
 pub mod rigidbody;
 pub mod collider;
@@ -328,383 +332,245 @@ pub mod shared {
     }
 }
 
-pub mod jni {
-    #![allow(non_snake_case)]
-
-    use crate::physics::nalgebra;
-    use crate::physics::PhysicsState;
-    use crate::scripting::jni::utils::{FromJObject, ToJObject};
-    use crate::types::{NCollider, IndexNative, RayHit, ShapeCastHitFFI, NVector3};
-    use hecs::Entity;
-    use jni::objects::{JClass, JObject};
-    use jni::sys::{jboolean, jdouble, jlong, jobject};
-    use jni::JNIEnv;
-    use rapier3d::parry::query::DefaultQueryDispatcher;
-    use rapier3d::pipeline::QueryFilter;
-    use rapier3d::prelude::{point, vector, Ray};
-    use rapier3d::parry::query::ShapeCastOptions;
-    use rapier3d::math::{Pose3, Vec3};
-    use crate::physics::collider::ColliderShape;
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_PhysicsNative_getGravity(
-        mut env: JNIEnv,
-        _: JClass,
-        physics_handle: jlong,
-    ) -> jobject {
-        let physics = crate::convert_ptr!(physics_handle => PhysicsState);
-
-        match super::shared::get_gravity(&physics).to_jobject(&mut env) {
-            Ok(v) => v.into_raw(),
-            Err(e) => {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Unable to create new Vector3d object for gravity: {}", e));
-                std::ptr::null_mut()
-            }
-        }
-
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_PhysicsNative_setGravity(
-        mut env: JNIEnv,
-        _: JClass,
-        physics_handle: jlong,
-        new_gravity: JObject,
-    ) {
-        let mut physics = crate::convert_ptr!(mut physics_handle => PhysicsState);
-        let vec3 = match NVector3::from_jobject(&mut env, &new_gravity) {
-            Ok(v) => v,
-            Err(e) => {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Unable to create new Vector3d object for gravity: {}", e));
-                return;
-            }
-        };
-
-        super::shared::set_gravity(&mut physics, vec3);
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_PhysicsNative_raycast(
-        mut env: JNIEnv,
-        _: JClass,
-        physics_handle: jlong,
-        origin: JObject,
-        direction: JObject,
-        time_of_impact: jdouble,
-        solid: jboolean,
-    ) -> jobject {
-        let physics = crate::convert_ptr!(mut physics_handle => PhysicsState);
-
-        let qp = physics.broad_phase.as_query_pipeline(&DefaultQueryDispatcher, &physics.bodies, &physics.colliders, QueryFilter::new());
-
-        let origin = match NVector3::from_jobject(&mut env, &origin) {
-            Ok(v) => v,
-            Err(e) => {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Unable to create a new rust Vector3 object: {}", e));
-                return std::ptr::null_mut();
-            }
-        };
-
-        let dir = match NVector3::from_jobject(&mut env, &direction) {
-            Ok(v) => v,
-            Err(e) => {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Unable to create a new rust Vector3 object: {}", e));
-                return std::ptr::null_mut();
-            }
-        };
-
-        let ray = Ray::new(
-            point![origin.x as f32, origin.y as f32, origin.z as f32].into(),
-            vector![dir.x as f32, dir.y as f32, dir.z as f32].into(),
-        );
-
-        if let Some((hit, distance)) = qp.cast_ray(&ray, time_of_impact as f32, solid != 0) {
-            let raw = hit.0;
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.PhysicsNative", func = "getGravity"),
+    c
+)]
+fn get_gravity(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+) -> DropbearNativeResult<NVector3> {
+    Ok(shared::get_gravity(physics))
+}
 
-            let mut found = None;
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.PhysicsNative", func = "setGravity"),
+    c
+)]
+fn set_gravity(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    gravity: &NVector3,
+) -> DropbearNativeResult<()> {
+    Ok(shared::set_gravity(physics, *gravity))
+}
 
-            for (l, colliders) in physics.colliders_entity_map.iter() {
-                for (_, c) in colliders {
-                    if c.0 == hit.0 {
-                        found = Some((l, c.0));
-                    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.PhysicsNative", func = "raycast"),
+    c
+)]
+fn raycast(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    origin: &NVector3,
+    dir: &NVector3,
+    time_of_impact: f64,
+    solid: bool,
+) -> DropbearNativeResult<Option<RayHit>> {
+    let qp = physics.broad_phase.as_query_pipeline(&DefaultQueryDispatcher, &physics.bodies, &physics.colliders, QueryFilter::new());
+
+    let ray = Ray::new(
+        point![origin.x as f32, origin.y as f32, origin.z as f32].into(),
+        vector![dir.x as f32, dir.y as f32, dir.z as f32].into(),
+    );
+
+    if let Some((hit, distance)) = qp.cast_ray(&ray, time_of_impact as f32, solid) {
+        let raw = hit.0;
+
+        let mut found = None;
+
+        for (l, colliders) in physics.colliders_entity_map.iter() {
+            for (_, c) in colliders {
+                if c.0 == hit.0 {
+                    found = Some((l, c.0));
                 }
             }
+        }
 
-            if let Some((label, _)) = found {
-                let entity = physics.entity_label_map.iter().find(|(_, l)| *l == label);
-                if let Some((e, _)) = entity {
-                    let rayhit = RayHit {
-                        collider: crate::types::NCollider {
-                            index: IndexNative::from(raw),
-                            entity_id: e.to_bits().get(),
-                            id: raw.into_raw_parts().0,
-                        },
-                        distance: distance as f64,
-                    };
-
-                    match rayhit.to_jobject(&mut env) {
-                        Ok(v) => v.into_raw(),
-                        Err(e) => {
-                            let _ = env.throw_new("java/lang/RuntimeException", format!("Unable to create a new rust RayHit object: {}", e));
-                            return std::ptr::null_mut();
-                        }
-                    }
-                } else {
-                    std::ptr::null_mut()
-                }
-            } else {
-                eprintln!("Unknown collider, still returning value without entity_id");
-
+        if let Some((label, _)) = found {
+            let entity = physics.entity_label_map.iter().find(|(_, l)| *l == label);
+            if let Some((e, _)) = entity {
                 let rayhit = RayHit {
                     collider: crate::types::NCollider {
                         index: IndexNative::from(raw),
-                        entity_id: Entity::DANGLING.to_bits().get(),
+                        entity_id: e.to_bits().get(),
                         id: raw.into_raw_parts().0,
                     },
                     distance: distance as f64,
                 };
 
-                match rayhit.to_jobject(&mut env) {
-                    Ok(v) => v.into_raw(),
-                    Err(e) => {
-                        let _ = env.throw_new("java/lang/RuntimeException", format!("Unable to create a new rust RayHit object: {}", e));
-                        return std::ptr::null_mut();
-                    }
-                }
+                Ok(Some(rayhit))
+            } else {
+                Ok(None)
             }
         } else {
-            std::ptr::null_mut()
+            eprintln!("Unknown collider, still returning value without entity_id");
+
+            let rayhit = RayHit {
+                collider: crate::types::NCollider {
+                    index: IndexNative::from(raw),
+                    entity_id: Entity::DANGLING.to_bits().get(),
+                    id: raw.into_raw_parts().0,
+                },
+                distance: distance as f64,
+            };
+            Ok(Some(rayhit))
+
         }
+    } else {
+        Ok(None)
     }
+}
 
-    fn collider_ffi_from_handle(physics: &PhysicsState, handle: rapier3d::prelude::ColliderHandle) -> NCollider {
-        let (idx, generation) = handle.into_raw_parts();
-
-        let mut found_label = None;
-        for (label, colliders) in physics.colliders_entity_map.iter() {
-            for (_, c) in colliders {
-                if c.0 == handle.0 {
-                    found_label = Some(label);
-                    break;
-                }
-            }
-            if found_label.is_some() {
-                break;
-            }
-        }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.PhysicsNative", func = "shapeCast"),
+    c
+)]
+fn shape_cast(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    origin: &NVector3,
+    direction: &NVector3,
+    shape: &collider::ColliderShape,
+    time_of_impact: f64,
+    solid: bool,
+) -> DropbearNativeResult<Option<NShapeCastHit>> {
+    let qp = physics.broad_phase.as_query_pipeline(
+        &DefaultQueryDispatcher,
+        &physics.bodies,
+        &physics.colliders,
+        QueryFilter::new(),
+    );
+
+    let dir_len = ((direction.x * direction.x) + (direction.y * direction.y) + (direction.z * direction.z)).sqrt();
+    if dir_len <= f64::EPSILON {
+        return Ok(None);
+    }
 
-        let entity_id = if let Some(label) = found_label {
-            physics
-                .entity_label_map
-                .iter()
-                .find(|(_, l)| *l == label)
-                .map(|(e, _)| e.to_bits().get())
-                .unwrap_or(Entity::DANGLING.to_bits().get())
-        } else {
-            Entity::DANGLING.to_bits().get()
-        };
+    let dir_unit = NVector3 {
+        x: direction.x / dir_len,
+        y: direction.y / dir_len,
+        z: direction.z / dir_len,
+    };
 
-        NCollider {
-            index: IndexNative { index: idx, generation },
-            entity_id,
-            id: idx,
-        }
-    }
 
-    fn shared_shape_from_collider_shape(shape: &ColliderShape) -> rapier3d::geometry::SharedShape {
+    let cast_shape = {
         match shape {
-            ColliderShape::Box { half_extents } => {
+            crate::physics::collider::ColliderShape::Box { half_extents } => {
                 rapier3d::geometry::SharedShape::cuboid(half_extents[0], half_extents[1], half_extents[2])
             }
-            ColliderShape::Sphere { radius } => rapier3d::geometry::SharedShape::ball(*radius),
-            ColliderShape::Capsule { half_height, radius } => {
+            crate::physics::collider::ColliderShape::Sphere { radius } => rapier3d::geometry::SharedShape::ball(*radius),
+            crate::physics::collider::ColliderShape::Capsule { half_height, radius } => {
                 rapier3d::geometry::SharedShape::capsule_y(*half_height, *radius)
             }
-            ColliderShape::Cylinder { half_height, radius } => {
+            crate::physics::collider::ColliderShape::Cylinder { half_height, radius } => {
                 rapier3d::geometry::SharedShape::cylinder(*half_height, *radius)
             }
-            ColliderShape::Cone { half_height, radius } => {
+            crate::physics::collider::ColliderShape::Cone { half_height, radius } => {
                 rapier3d::geometry::SharedShape::cone(*half_height, *radius)
             }
         }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_PhysicsNative_shapeCast(
-        mut env: JNIEnv,
-        _: JClass,
-        physics_handle: jlong,
-        origin: JObject,
-        direction: JObject,
-        shape: JObject,
-        time_of_impact: jdouble,
-        solid: jboolean,
-    ) -> jobject {
-        let physics = crate::convert_ptr!(mut physics_handle => PhysicsState);
-
-        let qp = physics.broad_phase.as_query_pipeline(
-            &DefaultQueryDispatcher,
-            &physics.bodies,
-            &physics.colliders,
-            QueryFilter::new(),
-        );
-
-        let origin = match NVector3::from_jobject(&mut env, &origin) {
-            Ok(v) => v,
-            Err(e) => {
-                let _ = env.throw_new(
-                    "java/lang/RuntimeException",
-                    format!("Unable to convert origin to Vector3: {e}"),
-                );
-                return std::ptr::null_mut();
-            }
-        };
-
-        let direction = match NVector3::from_jobject(&mut env, &direction) {
-            Ok(v) => v,
-            Err(e) => {
-                let _ = env.throw_new(
-                    "java/lang/RuntimeException",
-                    format!("Unable to convert direction to Vector3: {e}"),
-                );
-                return std::ptr::null_mut();
-            }
-        };
-
-        let shape = match ColliderShape::from_jobject(&mut env, &shape) {
-            Ok(v) => v,
-            Err(e) => {
-                let _ = env.throw_new(
-                    "java/lang/RuntimeException",
-                    format!("Unable to convert shape to ColliderShape: {e}"),
-                );
-                return std::ptr::null_mut();
-            }
-        };
-
-        let dir_len = ((direction.x * direction.x) + (direction.y * direction.y) + (direction.z * direction.z)).sqrt();
-        if dir_len <= f64::EPSILON {
-            return std::ptr::null_mut();
-        }
-
-        let dir_unit = NVector3 {
-            x: direction.x / dir_len,
-            y: direction.y / dir_len,
-            z: direction.z / dir_len,
-        };
-
-        let cast_shape = shared_shape_from_collider_shape(&shape);
-        let iso: Pose3 = nalgebra::Isometry3::translation(origin.x as f32, origin.y as f32, origin.z as f32).into();
-        let vel: Vec3 = vector![dir_unit.x as f32, dir_unit.y as f32, dir_unit.z as f32].into();
+    };
+    let iso: Pose3 = nalgebra::Isometry3::translation(origin.x as f32, origin.y as f32, origin.z as f32).into();
+    let vel: Vec3 = vector![dir_unit.x as f32, dir_unit.y as f32, dir_unit.z as f32].into();
+
+    let options = ShapeCastOptions {
+        max_time_of_impact: time_of_impact as f32,
+        target_distance: 0.0,
+        stop_at_penetration: solid,
+        compute_impact_geometry_on_penetration: true,
+    };
+
+    let Some((hit_handle, toi)) = qp.cast_shape(&iso, vel, cast_shape.as_ref(), options) else {
+        return Ok(None);
+    };
+
+    let collider = collider_ffi_from_handle(&physics, hit_handle);
+
+    let hit = NShapeCastHit {
+        collider,
+        distance: toi.time_of_impact as f64,
+        witness1: NVector3::from([toi.witness1.x, toi.witness1.y, toi.witness1.z]),
+        witness2: NVector3::from([toi.witness2.x, toi.witness2.y, toi.witness2.z]),
+        normal1: NVector3::from([toi.normal1.x, toi.normal1.y, toi.normal1.z]),
+        normal2: NVector3::from([toi.normal2.x, toi.normal2.y, toi.normal2.z]),
+        status: toi.status.into(),
+    };
+
+    Ok(Some(hit))
+}
 
-        let options = ShapeCastOptions {
-            max_time_of_impact: time_of_impact as f32,
-            target_distance: 0.0,
-            stop_at_penetration: solid != 0,
-            compute_impact_geometry_on_penetration: true,
-        };
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.PhysicsNative", func = "isOverlapping"),
+    c
+)]
+fn is_overlapping(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    collider1: &NCollider,
+    collider2: &NCollider,
+) -> DropbearNativeResult<bool> {
+    Ok(shared::overlapping(physics, collider1, collider2))
+}
 
-        let Some((hit_handle, toi)) = qp.cast_shape(&iso, vel, cast_shape.as_ref(), options) else {
-            return std::ptr::null_mut();
-        };
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.PhysicsNative", func = "isTriggering"),
+    c
+)]
+fn is_triggering(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    collider1: &NCollider,
+    collider2: &NCollider,
+) -> DropbearNativeResult<bool> {
+    Ok(shared::triggering(physics, collider1, collider2))
+}
 
-        let collider = collider_ffi_from_handle(&physics, hit_handle);
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.PhysicsNative", func = "isTouching"),
+    c
+)]
+fn is_touching(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    #[dropbear_macro::entity]
+    entity1: Entity,
+    #[dropbear_macro::entity]
+    entity2: Entity,
+) -> DropbearNativeResult<bool> {
+    Ok(shared::touching(physics, entity1, entity2))
+}
 
-        let hit = ShapeCastHitFFI {
-            collider,
-            distance: toi.time_of_impact as f64,
-            witness1: NVector3::from([toi.witness1.x, toi.witness1.y, toi.witness1.z]),
-            witness2: NVector3::from([toi.witness2.x, toi.witness2.y, toi.witness2.z]),
-            normal1: NVector3::from([toi.normal1.x, toi.normal1.y, toi.normal1.z]),
-            normal2: NVector3::from([toi.normal2.x, toi.normal2.y, toi.normal2.z]),
-            status: toi.status,
-        };
+fn collider_ffi_from_handle(physics: &PhysicsState, handle: rapier3d::prelude::ColliderHandle) -> NCollider {
+    let (idx, generation) = handle.into_raw_parts();
 
-        match hit.to_jobject(&mut env) {
-            Ok(v) => v.into_raw(),
-            Err(e) => {
-                let _ = env.throw_new(
-                    "java/lang/RuntimeException",
-                    format!("Unable to create ShapeCastHit object: {e}"),
-                );
-                std::ptr::null_mut()
+    let mut found_label = None;
+    for (label, colliders) in physics.colliders_entity_map.iter() {
+        for (_, c) in colliders {
+            if c.0 == handle.0 {
+                found_label = Some(label);
+                break;
             }
         }
+        if found_label.is_some() {
+            break;
+        }
     }
 
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_PhysicsNative_isOverlapping(
-        mut env: JNIEnv,
-        _: JClass,
-        physics_handle: jlong,
-        collider1: JObject,
-        collider2: JObject,
-    ) -> jboolean {
-        let physics = crate::convert_ptr!(physics_handle => PhysicsState);
-        let Ok(collider1) = NCollider::from_jobject(&mut env, &collider1) else {
-            let _ = env.throw_new(
-                "java/lang/RuntimeException",
-                "Unable to convert a Collider object [collider1] to a rust ColliderFFI"
-            );
-            return false.into();
-        };
-
-        let Ok(collider2) = NCollider::from_jobject(&mut env, &collider2) else {
-            let _ = env.throw_new(
-                "java/lang/RuntimeException",
-                "Unable to convert a Collider object [collider2] to a rust ColliderFFI"
-            );
-            return false.into();
-        };
-
-        super::shared::overlapping(&physics, &collider1, &collider2).into()
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_PhysicsNative_isTriggering(
-        mut env: JNIEnv,
-        _: JClass,
-        physics_handle: jlong,
-        collider1: JObject,
-        collider2: JObject,
-    ) -> jboolean {
-        let physics = crate::convert_ptr!(physics_handle => PhysicsState);
-        let Ok(collider1) = NCollider::from_jobject(&mut env, &collider1) else {
-            let _ = env.throw_new(
-                "java/lang/RuntimeException",
-                "Unable to convert a Collider object [collider1] to a rust ColliderFFI"
-            );
-            return false.into();
-        };
-
-        let Ok(collider2) = NCollider::from_jobject(&mut env, &collider2) else {
-            let _ = env.throw_new(
-                "java/lang/RuntimeException",
-                "Unable to convert a Collider object [collider2] to a rust ColliderFFI"
-            );
-            return false.into();
-        };
-
-        super::shared::triggering(&physics, &collider1, &collider2).into()
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_PhysicsNative_isTouching(
-        _env: JNIEnv,
-        _: JClass,
-        physics_handle: jlong,
-        entity1: jlong,
-        entity2: jlong,
-    ) -> jboolean {
-        let physics = crate::convert_ptr!(physics_handle => PhysicsState);
-        let entity1 = crate::convert_jlong_to_entity!(entity1);
-        let entity2 = crate::convert_jlong_to_entity!(entity2);
-
-        super::shared::touching(&physics, entity1, entity2).into()
+    let entity_id = if let Some(label) = found_label {
+        physics
+            .entity_label_map
+            .iter()
+            .find(|(_, l)| *l == label)
+            .map(|(e, _)| e.to_bits().get())
+            .unwrap_or(Entity::DANGLING.to_bits().get())
+    } else {
+        Entity::DANGLING.to_bits().get()
+    };
+
+    NCollider {
+        index: IndexNative { index: idx, generation },
+        entity_id,
+        id: idx,
     }
 }
-
-pub mod native {
-
-}
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/physics/collider.rs b/crates/eucalyptus-core/src/physics/collider.rs
index 3bf52bc..f5a7cc2 100644
--- a/crates/eucalyptus-core/src/physics/collider.rs
+++ b/crates/eucalyptus-core/src/physics/collider.rs
@@ -14,11 +14,9 @@
 //!     - `-colonly` (invisible collision mesh)
 
 pub mod shader;
-pub mod collidergroup;
+pub mod collider_group;
 
 use crate::scripting::jni::utils::{FromJObject, ToJObject};
-use crate::scripting::native::DropbearNativeError;
-use crate::scripting::result::DropbearNativeResult;
 use crate::states::Label;
 use dropbear_engine::graphics::SharedGraphicsContext;
 use dropbear_engine::wgpu::util::{BufferInitDescriptor, DeviceExt};
@@ -30,7 +28,14 @@ use ::jni::JNIEnv;
 use rapier3d::prelude::ColliderBuilder;
 use serde::{Deserialize, Serialize};
 use std::sync::Arc;
-use rapier3d::math::Vector;
+use crate::physics::collider::shared::{get_collider, get_collider_mut};
+use crate::scripting::native::DropbearNativeError;
+use crate::scripting::result::DropbearNativeResult;
+use crate::types::{NCollider, NVector3};
+use glam::DQuat;
+use rapier3d::prelude::{Rotation, SharedShape, TypedShape, Vector};
+use crate::physics::PhysicsState;
+use crate::ptr::PhysicsStatePtr;
 
 #[derive(Debug, Default, Serialize, Deserialize, Clone, SerializableComponent)]
 pub struct ColliderGroup {
@@ -136,6 +141,7 @@ impl From<&ColliderShape> for ColliderShapeKey {
 
 #[repr(C)]
 #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
+#[dropbear_macro::repr_c_enum]
 pub enum ColliderShape {
     /// Box shape with half-extents (half-width, half-height, half-depth).
     Box { half_extents: [f32; 3] },
@@ -632,650 +638,292 @@ pub mod shared {
     }
 }
 
-pub mod jni {
-    #![allow(non_snake_case)]
-    use crate::physics::collider::shared::{get_collider, get_collider_mut};
-    use crate::physics::collider::ColliderShape;
-    use crate::physics::PhysicsState;
-    use crate::scripting::jni::utils::{FromJObject, ToJObject};
-    use crate::types::NCollider;
-    use glam::DQuat;
-    use jni::objects::{JClass, JObject};
-    use jni::sys::{jboolean, jdouble, jlong, jobject};
-    use jni::JNIEnv;
-    
-    use rapier3d::prelude::{ColliderHandle, Rotation, SharedShape, TypedShape, Vector};
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderShape(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-    ) -> jobject {
-        let physics = crate::convert_ptr!(physics_ptr => PhysicsState);
-
-        let ffi = match NCollider::from_jobject(&mut env, &collider_obj) {
-            Ok(v) => v,
-            Err(_) => return std::ptr::null_mut(),
-        };
-
-        if let Some(collider) = get_collider(&physics, &ffi) {
-            let rapier_shape = collider.shape();
-
-            let my_shape = match rapier_shape.as_typed_shape() {
-                TypedShape::Cuboid(c) => {
-                    let he = c.half_extents;
-                    ColliderShape::Box {
-                        half_extents: [he.x, he.y, he.z]
-                    }
-                },
-                TypedShape::Ball(b) => {
-                     ColliderShape::Sphere {
-                        radius: b.radius
-                     }
-                },
-                TypedShape::Capsule(c) => {
-                    let height = c.segment.length();
-                    ColliderShape::Capsule {
-                        half_height: height * 0.5,
-                        radius: c.radius
-                    }
-                },
-                TypedShape::Cylinder(c) => {
-                    ColliderShape::Cylinder {
-                        half_height: c.half_height,
-                        radius: c.radius
-                    }
-                },
-                TypedShape::Cone(c) => {
-                    ColliderShape::Cone {
-                        half_height: c.half_height,
-                        radius: c.radius
-                    }
-                },
-                _ => {
-                    eprintln!("Unsupported collider shape type found.");
-                    return std::ptr::null_mut();
-                }
-            };
-
-            match my_shape.to_jobject(&mut env) {
-                Ok(obj) => obj.into_raw(),
-                Err(_) => std::ptr::null_mut(),
-            }
-
-        } else {
-            let _ = env.throw_new("java/lang/RuntimeException", "Collider handle invalid");
-            std::ptr::null_mut()
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderShape(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-        shape_obj: JObject,
-    ) {
-        let physics = crate::convert_ptr!(mut physics_ptr => PhysicsState);
-
-        let ffi = match NCollider::from_jobject(&mut env, &collider_obj) {
-            Ok(v) => v,
-            Err(_) => return,
-        };
-
-        let handle = ColliderHandle::from_raw_parts(ffi.index.index, ffi.index.generation);
-
-        let Some(collider) = physics.colliders.get_mut(handle) else {
-            let _ = env.throw_new("java/lang/IllegalArgumentException", "Collider handle invalid");
-            return;
-        };
-
-        let Ok(shape) = ColliderShape::from_jobject(&mut env, &shape_obj) else {
-            let _ = env.throw_new("java/lang/IllegalArgumentException", "Collider shape is invalid");
-            return;
-        };
-
-        let new_shape = match shape {
-            ColliderShape::Box { half_extents } => {
-                SharedShape::cuboid(half_extents[0], half_extents[1], half_extents[2])
-            }
-            ColliderShape::Sphere { radius } => {
-                SharedShape::ball(radius)
-            }
-            ColliderShape::Capsule { half_height, radius } => {
-                SharedShape::capsule_y(half_height, radius)
-            }
-            ColliderShape::Cylinder { half_height, radius } => {
-                SharedShape::cylinder(half_height, radius)
-            }
-            ColliderShape::Cone { half_height, radius } => {
-                SharedShape::cone(half_height, radius)
-            }
-        };
-
-        collider.set_shape(new_shape);
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderDensity(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-    ) -> jdouble {
-        let physics = crate::convert_ptr!(physics_ptr => PhysicsState);
-        let ffi = NCollider::from_jobject(&mut env, &collider_obj).ok().unwrap();
-
-        if let Some(col) = get_collider(&physics, &ffi) {
-            col.density() as jdouble
-        } else {
-            0.0
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderDensity(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-        density: jdouble,
-    ) {
-        let physics = crate::convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Ok(ffi) = NCollider::from_jobject(&mut env, &collider_obj) {
-            if let Some(col) = get_collider_mut(physics, &ffi) {
-                col.set_density(density as f32);
-            }
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderFriction(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-    ) -> jdouble {
-        let physics = crate::convert_ptr!(physics_ptr => PhysicsState);
-        let ffi = NCollider::from_jobject(&mut env, &collider_obj).ok().unwrap();
-        if let Some(col) = get_collider(&physics, &ffi) {
-            col.friction() as jdouble
-        } else { 0.0 }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderFriction(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-        friction: jdouble,
-    ) {
-        let physics = crate::convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Ok(ffi) = NCollider::from_jobject(&mut env, &collider_obj) {
-            if let Some(col) = get_collider_mut(physics, &ffi) {
-                col.set_friction(friction as f32);
-            }
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderRestitution(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-    ) -> jdouble {
-        let physics = crate::convert_ptr!(physics_ptr => PhysicsState);
-        let ffi = NCollider::from_jobject(&mut env, &collider_obj).ok().unwrap();
-        if let Some(col) = get_collider(&physics, &ffi) {
-            col.restitution() as jdouble
-        } else { 0.0 }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderRestitution(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-        restitution: jdouble,
-    ) {
-        let physics = crate::convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Ok(ffi) = NCollider::from_jobject(&mut env, &collider_obj) {
-            if let Some(col) = get_collider_mut(physics, &ffi) {
-                col.set_restitution(restitution as f32);
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "getColliderShape"),
+    c
+)]
+fn get_collider_shape(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    collider: &NCollider,
+) -> DropbearNativeResult<ColliderShape> {
+    let collider = get_collider(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+
+    let rapier_shape = collider.shape();
+    let my_shape = match rapier_shape.as_typed_shape() {
+        TypedShape::Cuboid(c) => {
+            let he = c.half_extents;
+            ColliderShape::Box {
+                half_extents: [he.x, he.y, he.z],
             }
         }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderMass(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-    ) -> jdouble {
-        let physics = crate::convert_ptr!(physics_ptr => PhysicsState);
-        let ffi = NCollider::from_jobject(&mut env, &collider_obj).ok().unwrap();
-        if let Some(col) = get_collider(&physics, &ffi) {
-            col.mass() as jdouble
-        } else { 0.0 }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderMass(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-        mass: jdouble,
-    ) {
-        let physics = crate::convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Ok(ffi) = NCollider::from_jobject(&mut env, &collider_obj) {
-            if let Some(col) = get_collider_mut(physics, &ffi) {
-                col.set_mass(mass as f32);
-            }
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderIsSensor(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-    ) -> jboolean {
-        let physics = crate::convert_ptr!(physics_ptr => PhysicsState);
-        let ffi = NCollider::from_jobject(&mut env, &collider_obj).ok().unwrap();
-        if let Some(col) = get_collider(&physics, &ffi) {
-            if col.is_sensor() { 1 } else { 0 }
-        } else { 0 }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderIsSensor(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-        is_sensor: jboolean,
-    ) {
-        let physics = crate::convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Ok(ffi) = NCollider::from_jobject(&mut env, &collider_obj) {
-            if let Some(col) = get_collider_mut(physics, &ffi) {
-                col.set_sensor(is_sensor != 0);
-            }
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderTranslation(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-    ) -> jobject {
-        let physics = crate::convert_ptr!(physics_ptr => PhysicsState);
-        let ffi = match NCollider::from_jobject(&mut env, &collider_obj) {
-            Ok(v) => v,
-            Err(_) => return std::ptr::null_mut(),
-        };
-
-        if let Some(col) = get_collider(&physics, &ffi) {
-            let t: Vector = col.translation();
-            let vec = crate::types::NVector3::new(t.x as f64, t.y as f64, t.z as f64);
-            match vec.to_jobject(&mut env) {
-                Ok(o) => o.into_raw(),
-                Err(_) => std::ptr::null_mut(),
+        TypedShape::Ball(b) => ColliderShape::Sphere { radius: b.radius },
+        TypedShape::Capsule(c) => {
+            let height = c.segment.length();
+            ColliderShape::Capsule {
+                half_height: height * 0.5,
+                radius: c.radius,
             }
-        } else {
-            std::ptr::null_mut()
         }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderTranslation(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-        vec_obj: JObject,
-    ) {
-        let physics = crate::convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Ok(ffi) = NCollider::from_jobject(&mut env, &collider_obj) {
-            if let Ok(vec) = crate::types::NVector3::from_jobject(&mut env, &vec_obj) {
-                if let Some(col) = get_collider_mut(physics, &ffi) {
-                    let t = Vector::new(vec.x as f32, vec.y as f32, vec.z as f32);
-                    col.set_translation(t);
-                }
-            }
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_getColliderRotation(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-    ) -> jobject {
-        let physics = crate::convert_ptr!(physics_ptr => PhysicsState);
-        let ffi = match NCollider::from_jobject(&mut env, &collider_obj) {
-            Ok(v) => v,
-            Err(_) => return std::ptr::null_mut(),
-        };
-
-        if let Some(col) = get_collider(&physics, &ffi) {
-            let r: Rotation = col.rotation();
-            let q = DQuat::from_xyzw(r.x as f64, r.y as f64, r.z as f64, r.w as f64);
-            let euler = q.to_euler(glam::EulerRot::XYZ);
-            let vec = crate::types::NVector3::new(euler.0, euler.1, euler.2);
-
-            match vec.to_jobject(&mut env) {
-                Ok(o) => o.into_raw(),
-                Err(_) => std::ptr::null_mut(),
-            }
-        } else {
-            std::ptr::null_mut()
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderNative_setColliderRotation(
-        mut env: JNIEnv,
-        _class: JClass,
-        physics_ptr: jlong,
-        collider_obj: JObject,
-        vec_obj: JObject,
-    ) {
-        let physics = crate::convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Ok(ffi) = NCollider::from_jobject(&mut env, &collider_obj) {
-            if let Ok(vec) = crate::types::NVector3::from_jobject(&mut env, &vec_obj) {
-                if let Some(col) = get_collider_mut(physics, &ffi) {
-                    let q = DQuat::from_euler(glam::EulerRot::XYZ, vec.x, vec.y, vec.z);
-                    let r = Rotation::from_array([q.w as f32, q.x as f32, q.y as f32, q.z as f32]);
-                    col.set_rotation(r);
-                }
-            }
-        }
-    }
+        TypedShape::Cylinder(c) => ColliderShape::Cylinder {
+            half_height: c.half_height,
+            radius: c.radius,
+        },
+        TypedShape::Cone(c) => ColliderShape::Cone {
+            half_height: c.half_height,
+            radius: c.radius,
+        },
+        _ => return Err(DropbearNativeError::InvalidArgument),
+    };
+
+    Ok(my_shape)
 }
 
-// #[dropbear_macro::impl_c_api]
-pub mod native {
-    use crate::physics::Rotation;
-    use crate::convert_ptr;
-    use crate::physics::collider::shared::{get_collider, get_collider_mut};
-    use crate::physics::PhysicsState;
-    use crate::ptr::PhysicsStatePtr;
-    use crate::types::NVector3;
-
-    use crate::scripting::native::DropbearNativeError;
-    use crate::scripting::result::DropbearNativeResult;
-    use crate::types::{NCollider, NColliderShape, ColliderShapeType};
-    use glam::DQuat;
-    use rapier3d::prelude::{SharedShape, TypedShape, Vector};
-
-    pub fn dropbear_get_collider_shape(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-    ) -> DropbearNativeResult<NColliderShape> {
-        let physics = convert_ptr!(physics_ptr => PhysicsState);
-
-        if let Some(collider) = get_collider(physics, &ffi) {
-            let rapier_shape = collider.shape();
-            let mut result = NColliderShape {
-                shape_type: ColliderShapeType::Box,
-                radius: 0.0, half_height: 0.0,
-                half_extents_x: 0.0, half_extents_y: 0.0, half_extents_z: 0.0,
-            };
-
-            match rapier_shape.as_typed_shape() {
-                TypedShape::Cuboid(c) => {
-                    result.shape_type = ColliderShapeType::Box;
-                    result.half_extents_x = c.half_extents.x;
-                    result.half_extents_y = c.half_extents.y;
-                    result.half_extents_z = c.half_extents.z;
-                },
-                TypedShape::Ball(b) => {
-                    result.shape_type = ColliderShapeType::Sphere;
-                    result.radius = b.radius;
-                },
-                TypedShape::Capsule(c) => {
-                    result.shape_type = ColliderShapeType::Capsule;
-                    result.radius = c.radius;
-                    result.half_height = c.segment.length() * 0.5;
-                },
-                TypedShape::Cylinder(c) => {
-                    result.shape_type = ColliderShapeType::Cylinder;
-                    result.radius = c.radius;
-                    result.half_height = c.half_height;
-                },
-                TypedShape::Cone(c) => {
-                    result.shape_type = ColliderShapeType::Cone;
-                    result.radius = c.radius;
-                    result.half_height = c.half_height;
-                },
-                _ => return DropbearNativeResult::Err(DropbearNativeError::GenericError),
-            }
-            DropbearNativeResult::Ok(result)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
-
-    pub fn dropbear_set_collider_shape(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-        shape: NColliderShape,
-    ) -> DropbearNativeResult<()> {
-        let physics = convert_ptr!(mut physics_ptr => PhysicsState);
-
-        if let Some(collider) = get_collider_mut(physics, &ffi) {
-            let new_shape = match shape.shape_type {
-                ColliderShapeType::Box => SharedShape::cuboid(shape.half_extents_x, shape.half_extents_y, shape.half_extents_z),
-                ColliderShapeType::Sphere => SharedShape::ball(shape.radius),
-                ColliderShapeType::Capsule => SharedShape::capsule_y(shape.half_height, shape.radius),
-                ColliderShapeType::Cylinder => SharedShape::cylinder(shape.half_height, shape.radius),
-                ColliderShapeType::Cone => SharedShape::cone(shape.half_height, shape.radius),
-            };
-            collider.set_shape(new_shape);
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "setColliderShape"),
+    c
+)]
+fn set_collider_shape(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    collider: &NCollider,
+    shape: &ColliderShape,
+) -> DropbearNativeResult<()> {
+    let collider = get_collider_mut(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+
+    let new_shape = match shape {
+        ColliderShape::Box { half_extents } => {
+            SharedShape::cuboid(half_extents[0], half_extents[1], half_extents[2])
+        }
+        ColliderShape::Sphere { radius } => SharedShape::ball(*radius),
+        ColliderShape::Capsule { half_height, radius } => {
+            SharedShape::capsule_y(*half_height, *radius)
+        }
+        ColliderShape::Cylinder { half_height, radius } => {
+            SharedShape::cylinder(*half_height, *radius)
+        }
+        ColliderShape::Cone { half_height, radius } => SharedShape::cone(*half_height, *radius),
+    };
+
+    collider.set_shape(new_shape);
+    Ok(())
+}
 
-    pub fn dropbear_get_collider_density(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-    ) -> DropbearNativeResult<f64> {
-        let physics = convert_ptr!(physics_ptr => PhysicsState);
-        if let Some(col) = get_collider(physics, &ffi) {
-            DropbearNativeResult::Ok(col.density() as f64)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "getColliderDensity"),
+    c
+)]
+fn get_collider_density(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    collider: &NCollider,
+) -> DropbearNativeResult<f64> {
+    let collider = get_collider(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    Ok(collider.density() as f64)
+}
 
-    pub fn dropbear_set_collider_density(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-        density: f64,
-    ) -> DropbearNativeResult<()> {
-        let physics = convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Some(col) = get_collider_mut(physics, &ffi) {
-            col.set_density(density as f32);
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "setColliderDensity"),
+    c
+)]
+fn set_collider_density(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    collider: &NCollider,
+    density: f64,
+) -> DropbearNativeResult<()> {
+    let collider = get_collider_mut(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    collider.set_density(density as f32);
+    Ok(())
+}
 
-    pub fn dropbear_get_collider_friction(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-    ) -> DropbearNativeResult<f64> {
-        let physics = convert_ptr!(physics_ptr => PhysicsState);
-        if let Some(col) = get_collider(physics, &ffi) {
-            DropbearNativeResult::Ok(col.friction() as f64)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "getColliderFriction"),
+    c
+)]
+fn get_collider_friction(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    collider: &NCollider,
+) -> DropbearNativeResult<f64> {
+    let collider = get_collider(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    Ok(collider.friction() as f64)
+}
 
-    pub fn dropbear_set_collider_friction(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-        friction: f64,
-    ) -> DropbearNativeResult<()> {
-        let physics = convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Some(col) = get_collider_mut(physics, &ffi) {
-            col.set_friction(friction as f32);
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "setColliderFriction"),
+    c
+)]
+fn set_collider_friction(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    collider: &NCollider,
+    friction: f64,
+) -> DropbearNativeResult<()> {
+    let collider = get_collider_mut(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    collider.set_friction(friction as f32);
+    Ok(())
+}
 
-    pub fn dropbear_get_collider_restitution(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-    ) -> DropbearNativeResult<f64> {
-        let physics = convert_ptr!(physics_ptr => PhysicsState);
-        if let Some(col) = get_collider(physics, &ffi) {
-            DropbearNativeResult::Ok(col.restitution() as f64)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "getColliderRestitution"),
+    c
+)]
+fn get_collider_restitution(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    collider: &NCollider,
+) -> DropbearNativeResult<f64> {
+    let collider = get_collider(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    Ok(collider.restitution() as f64)
+}
 
-    pub fn dropbear_set_collider_restitution(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-        restitution: f64,
-    ) -> DropbearNativeResult<()> {
-        let physics = convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Some(col) = get_collider_mut(physics, &ffi) {
-            col.set_restitution(restitution as f32);
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "setColliderRestitution"),
+    c
+)]
+fn set_collider_restitution(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    collider: &NCollider,
+    restitution: f64,
+) -> DropbearNativeResult<()> {
+    let collider = get_collider_mut(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    collider.set_restitution(restitution as f32);
+    Ok(())
+}
 
-    pub fn dropbear_get_collider_mass(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-    ) -> DropbearNativeResult<f64> {
-        let physics = convert_ptr!(physics_ptr => PhysicsState);
-        if let Some(col) = get_collider(physics, &ffi) {
-            DropbearNativeResult::Ok(col.mass() as f64)
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "getColliderMass"),
+    c
+)]
+fn get_collider_mass(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    collider: &NCollider,
+) -> DropbearNativeResult<f64> {
+    let collider = get_collider(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    Ok(collider.mass() as f64)
+}
 
-    pub fn dropbear_set_collider_mass(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-        mass: f64,
-    ) -> DropbearNativeResult<()> {
-        let physics = convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Some(col) = get_collider_mut(physics, &ffi) {
-            col.set_mass(mass as f32);
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "setColliderMass"),
+    c
+)]
+fn set_collider_mass(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    collider: &NCollider,
+    mass: f64,
+) -> DropbearNativeResult<()> {
+    let collider = get_collider_mut(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    collider.set_mass(mass as f32);
+    Ok(())
+}
 
-    pub fn dropbear_get_collider_is_sensor(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-    ) -> DropbearNativeResult<bool> {
-        let physics = convert_ptr!(physics_ptr => PhysicsState);
-        if let Some(col) = get_collider(physics, &ffi) {
-            DropbearNativeResult::Ok(col.is_sensor())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "getColliderIsSensor"),
+    c
+)]
+fn get_collider_is_sensor(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    collider: &NCollider,
+) -> DropbearNativeResult<bool> {
+    let collider = get_collider(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    Ok(collider.is_sensor())
+}
 
-    pub fn dropbear_set_collider_is_sensor(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-        is_sensor: bool,
-    ) -> DropbearNativeResult<()> {
-        let physics = convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Some(col) = get_collider_mut(physics, &ffi) {
-            col.set_sensor(is_sensor);
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "setColliderIsSensor"),
+    c
+)]
+fn set_collider_is_sensor(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    collider: &NCollider,
+    is_sensor: bool,
+) -> DropbearNativeResult<()> {
+    let collider = get_collider_mut(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    collider.set_sensor(is_sensor);
+    Ok(())
+}
 
-    pub fn dropbear_get_collider_translation(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-    ) -> DropbearNativeResult<NVector3> {
-        let physics = convert_ptr!(physics_ptr => PhysicsState);
-        if let Some(col) = get_collider(physics, &ffi) {
-            let t = col.translation();
-            DropbearNativeResult::Ok(NVector3 { x: t.x as f64, y: t.y as f64, z: t.z as f64 })
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "getColliderTranslation"),
+    c
+)]
+fn get_collider_translation(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    collider: &NCollider,
+) -> DropbearNativeResult<NVector3> {
+    let collider = get_collider(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    let t: Vector = collider.translation();
+    Ok(NVector3::new(t.x as f64, t.y as f64, t.z as f64))
+}
 
-    pub fn dropbear_set_collider_translation(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-        translation: NVector3,
-    ) -> DropbearNativeResult<()> {
-        let physics = convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Some(col) = get_collider_mut(physics, &ffi) {
-            let t = Vector::new(translation.x as f32, translation.y as f32, translation.z as f32);
-            col.set_translation(t);
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "setColliderTranslation"),
+    c
+)]
+fn set_collider_translation(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    collider: &NCollider,
+    translation: &NVector3,
+) -> DropbearNativeResult<()> {
+    let collider = get_collider_mut(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    let t = Vector::new(translation.x as f32, translation.y as f32, translation.z as f32);
+    collider.set_translation(t);
+    Ok(())
+}
 
-    pub fn dropbear_get_collider_rotation(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-    ) -> DropbearNativeResult<NVector3> {
-        let physics = convert_ptr!(physics_ptr => PhysicsState);
-        if let Some(col) = get_collider(physics, &ffi) {
-            let r = col.rotation();
-            let q: DQuat = DQuat::from_xyzw(r.x as f64, r.y as f64, r.z as f64, r.w as f64);
-            let (x, y, z) = q.to_euler(glam::EulerRot::XYZ);
-            DropbearNativeResult::Ok(NVector3 { x, y, z })
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "getColliderRotation"),
+    c
+)]
+fn get_collider_rotation(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    collider: &NCollider,
+) -> DropbearNativeResult<NVector3> {
+    let collider = get_collider(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    let r: Rotation = collider.rotation();
+    let q = DQuat::from_xyzw(r.x as f64, r.y as f64, r.z as f64, r.w as f64);
+    let euler = q.to_euler(glam::EulerRot::XYZ);
+    Ok(NVector3::new(euler.0, euler.1, euler.2))
+}
 
-    pub fn dropbear_set_collider_rotation(
-        physics_ptr: PhysicsStatePtr,
-        ffi: NCollider,
-        rotation: NVector3,
-    ) -> DropbearNativeResult<()> {
-        let physics = convert_ptr!(mut physics_ptr => PhysicsState);
-        if let Some(col) = get_collider_mut(physics, &ffi) {
-            let q = DQuat::from_euler(glam::EulerRot::XYZ, rotation.x, rotation.y, rotation.z);
-            let r = Rotation::from(q.as_quat());
-            col.set_rotation(r);
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::InvalidHandle)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderNative", func = "setColliderRotation"),
+    c
+)]
+fn set_collider_rotation(
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    collider: &NCollider,
+    rotation: &NVector3,
+) -> DropbearNativeResult<()> {
+    let collider = get_collider_mut(physics, &collider)
+        .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+    let q = DQuat::from_euler(glam::EulerRot::XYZ, rotation.x, rotation.y, rotation.z);
+    let r = Rotation::from_array([q.w as f32, q.x as f32, q.y as f32, q.z as f32]);
+    collider.set_rotation(r);
+    Ok(())
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/physics/collider/collider_group.rs b/crates/eucalyptus-core/src/physics/collider/collider_group.rs
new file mode 100644
index 0000000..230bab2
--- /dev/null
+++ b/crates/eucalyptus-core/src/physics/collider/collider_group.rs
@@ -0,0 +1,62 @@
+//! Scripting module for collider groups. 
+
+use crate::physics::collider::ColliderGroup;
+use crate::physics::PhysicsState;
+use crate::ptr::WorldPtr;
+use crate::scripting::native::DropbearNativeError;
+use crate::scripting::result::DropbearNativeResult;
+use crate::types::{IndexNative, NCollider};
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderGroupNative", func = "colliderGroupExistsForEntity"),
+    c
+)]
+fn exists_for_entity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity
+) -> DropbearNativeResult<bool> {
+    Ok(world.get::<&ColliderGroup>(entity).is_ok())
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.ColliderGroupNative", func = "getColliderGroupColliders"),
+    c
+)]
+fn get_colliders(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(crate::ptr::PhysicsStatePtr)]
+    physics: &PhysicsState,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity
+) -> DropbearNativeResult<Vec<NCollider>> {
+    if world.get::<&ColliderGroup>(entity).is_ok() {
+        let handles_opt = physics.entity_label_map
+            .get(&entity)
+            .and_then(|label| physics.colliders_entity_map.get(label));
+
+        let mut colliders: Vec<NCollider> = Vec::new();
+
+        if let Some(handles) = handles_opt {
+            for (_, handle) in handles {
+                let (idx, generation) = handle.into_raw_parts();
+
+                let col = NCollider {
+                    index: IndexNative {
+                        index: idx,
+                        generation,
+                    },
+                    entity_id: entity.to_bits().get(),
+                    id: idx,
+                };
+                colliders.push(col);
+            }
+        }
+
+        Ok(colliders)
+    } else {
+        Err(DropbearNativeError::MissingComponent)?
+    }
+}
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/physics/collider/collidergroup.rs b/crates/eucalyptus-core/src/physics/collider/collidergroup.rs
deleted file mode 100644
index 5251de7..0000000
--- a/crates/eucalyptus-core/src/physics/collider/collidergroup.rs
+++ /dev/null
@@ -1,252 +0,0 @@
-//! Scripting module for collider groups. 
-
-use crate::scripting::jni::utils::{FromJObject, ToJObject};
-use crate::scripting::native::DropbearNativeError;
-use crate::scripting::result::DropbearNativeResult;
-use crate::types::IndexNative;
-use ::jni::objects::{JObject, JValue};
-use ::jni::JNIEnv;
-
-impl ToJObject for IndexNative {
-    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
-        let cls = env.find_class("com/dropbear/physics/Index")
-            .map_err(|e| {
-                eprintln!("[JNI Error] Could not find Index class: {:?}", e);
-                DropbearNativeError::GenericError
-            })?;
-
-        let obj = env.new_object(
-            cls,
-            "(II)V",
-            &[
-                JValue::Int(self.index as i32),
-                JValue::Int(self.generation as i32)
-            ]
-        ).map_err(|e| {
-            eprintln!("[JNI Error] Failed to create Index object: {:?}", e);
-            DropbearNativeError::GenericError
-        })?;
-
-        Ok(obj)
-    }
-}
-
-impl FromJObject for IndexNative {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self>
-    where
-        Self: Sized
-    {
-        let idx_val = env.get_field(obj, "index", "I")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .i()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
-
-        let gen_val = env.get_field(obj, "generation", "I")
-            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
-            .i()
-            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
-
-        Ok(IndexNative {
-            index: idx_val as u32,
-            generation: gen_val as u32,
-        })
-    }
-}
-
-pub mod shared {
-    use crate::physics::collider::ColliderGroup;
-    use hecs::{Entity, World};
-
-    pub fn collider_group_exists_for_entity(world: &World, entity: Entity) -> bool {
-        world.get::<&ColliderGroup>(entity).is_ok()
-    }
-}
-
-pub mod jni {
-    #![allow(non_snake_case)]
-    use crate::physics::collider::ColliderGroup;
-    use crate::types::{NCollider, IndexNative};
-    use hecs::World;
-    use jni::objects::{JClass, JObject};
-    use jni::sys::{jboolean, jlong, jobjectArray};
-    use jni::JNIEnv;
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderGroupNative_colliderGroupExistsForEntity(
-        _env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        entity_id: jlong,
-    ) -> jboolean {
-        let world = crate::convert_ptr!(world_ptr => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-
-        if super::shared::collider_group_exists_for_entity(&world, entity) {
-            true.into()
-        } else {
-            false.into()
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_ColliderGroupNative_getColliderGroupColliders(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_ptr: jlong,
-        physics_ptr: jlong,
-        entity_id: jlong,
-    ) -> jobjectArray {
-        let world = crate::convert_ptr!(world_ptr => hecs::World);
-        let physics = crate::convert_ptr!(physics_ptr => crate::physics::PhysicsState);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-
-        // We check if the ColliderGroup component exists first
-        if world.get::<&ColliderGroup>(entity).is_ok() {
-            let handles_opt = physics.entity_label_map
-                .get(&entity)
-                .and_then(|label| physics.colliders_entity_map.get(label));
-
-            let mut colliders: Vec<NCollider> = Vec::new();
-
-            if let Some(handles) = handles_opt {
-                for (_, handle) in handles {
-                    let (idx, generation) = handle.into_raw_parts();
-
-                    let col = NCollider {
-                        index: IndexNative {
-                            index: idx,
-                            generation,
-                        },
-                        entity_id: entity_id as u64,
-                        id: idx,
-                    };
-                    colliders.push(col);
-                }
-            }
-
-            let collider_cls = match env.find_class("com/dropbear/physics/Collider") {
-                Ok(cls) => cls,
-                Err(e) => {
-                    eprintln!("[JNI Error] Could not find Collider class: {:?}", e);
-                    return std::ptr::null_mut();
-                }
-            };
-
-            let output_array = match env.new_object_array(colliders.len() as i32, &collider_cls, JObject::null()) {
-                Ok(arr) => arr,
-                Err(e) => {
-                    eprintln!("[JNI Error] Failed to allocate Collider array: {:?}", e);
-                    let _ = env.throw_new("java/lang/OutOfMemoryError", "Could not allocate collider array");
-                    return std::ptr::null_mut();
-                }
-            };
-
-            use crate::scripting::jni::utils::ToJObject;
-
-            for (i, ffi) in colliders.iter().enumerate() {
-                let java_obj = match ffi.to_jobject(&mut env) {
-                    Ok(obj) => obj,
-                    Err(_) => return std::ptr::null_mut(),
-                };
-
-                if let Err(e) = env.set_object_array_element(&output_array, i as i32, java_obj) {
-                    eprintln!("[JNI Error] Failed to set array element: {:?}", e);
-                    return std::ptr::null_mut();
-                }
-            }
-
-            output_array.into_raw()
-
-        } else {
-            let _ = env.throw_new("java/lang/RuntimeException", "Entity does not have a ColliderGroup component");
-            std::ptr::null_mut()
-        }
-    }
-}
-
-#[dropbear_macro::impl_c_api]
-pub mod native {
-    use crate::convert_ptr;
-    use crate::physics::collider::ColliderGroup;
-    use crate::physics::PhysicsState;
-    use crate::ptr::{PhysicsStatePtr, WorldPtr};
-    use crate::scripting::native::DropbearNativeError;
-    use crate::scripting::result::DropbearNativeResult;
-    use crate::types::{NCollider, IndexNative};
-    use hecs::Entity;
-
-    pub fn dropbear_collider_group_exists_for_entity(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<bool> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        DropbearNativeResult::Ok(super::shared::collider_group_exists_for_entity(world, entity))
-    }
-
-    pub fn dropbear_get_collider_group_colliders(
-        world_ptr: WorldPtr,
-        physics_ptr: PhysicsStatePtr,
-        entity_id: u64,
-        out_count: *mut usize,
-    ) -> DropbearNativeResult<*mut NCollider> {
-        let world = convert_ptr!(world_ptr => hecs::World);
-        let physics = convert_ptr!(physics_ptr => PhysicsState);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        if out_count.is_null() {
-            return DropbearNativeResult::Err(DropbearNativeError::NullPointer);
-        }
-
-        if world.get::<&ColliderGroup>(entity).is_err() {
-            unsafe { *out_count = 0; }
-            return DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent);
-        }
-
-        let handles_opt = physics.entity_label_map
-            .get(&entity)
-            .and_then(|label| physics.colliders_entity_map.get(label));
-
-        let mut colliders: Vec<NCollider> = Vec::new();
-
-        if let Some(handles) = handles_opt {
-            for (_, handle) in handles {
-                let (idx, generation) = handle.into_raw_parts();
-
-                let col = NCollider {
-                    index: IndexNative {
-                        index: idx,
-                        generation,
-                    },
-                    entity_id,
-                    id: idx,
-                };
-                colliders.push(col);
-            }
-        }
-
-        unsafe { *out_count = colliders.len(); }
-
-        colliders.shrink_to_fit();
-        let ptr = colliders.as_mut_ptr();
-        std::mem::forget(colliders);
-
-        DropbearNativeResult::Ok(ptr)
-    }
-
-    pub fn dropbear_free_collider_array(
-        ptr: *mut NCollider,
-        count: usize,
-    ) -> DropbearNativeResult<()> {
-        if ptr.is_null() {
-            return DropbearNativeResult::Ok(());
-        }
-
-        unsafe {
-            let _ = Vec::from_raw_parts(ptr, count, count);
-        }
-
-        DropbearNativeResult::Ok(())
-    }
-}
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/physics/kcc.rs b/crates/eucalyptus-core/src/physics/kcc.rs
index 60ec4c4..540b212 100644
--- a/crates/eucalyptus-core/src/physics/kcc.rs
+++ b/crates/eucalyptus-core/src/physics/kcc.rs
@@ -8,6 +8,17 @@ use rapier3d::control::{CharacterCollision, KinematicCharacterController};
 use serde::{Deserialize, Serialize};
 use dropbear_macro::SerializableComponent;
 use crate::states::Label;
+use crate::physics::PhysicsState;
+use crate::scripting::jni::utils::ToJObject;
+use crate::scripting::native::DropbearNativeError;
+use crate::scripting::result::DropbearNativeResult;
+use crate::types::{IndexNative, NQuaternion, NVector3};
+use jni::objects::{JObject, JValue};
+use jni::JNIEnv;
+use rapier3d::dynamics::RigidBodyType;
+use rapier3d::math::Rotation;
+use rapier3d::prelude::QueryFilter;
+use crate::ptr::WorldPtr;
 
 /// The kinematic character controller (kcc) component.
 #[derive(Debug, Default, Serialize, Deserialize, Clone, SerializableComponent)]
@@ -28,280 +39,225 @@ impl KCC {
     }
 }
 
-pub mod shared {
-
+#[repr(C)]
+struct CharacterCollisionArray {
+    entity_id: u64,
+    collisions: Vec<IndexNative>,
 }
 
-pub mod jni {
-    #![allow(non_snake_case)]
-
-    use hecs::World;
-    use jni::JNIEnv;
-    use jni::objects::{JClass, JObject};
-    use jni::sys::{jboolean, jdouble, jlong};
-    use rapier3d::dynamics::RigidBodyType;
-    use rapier3d::math::Rotation;
-    use rapier3d::prelude::QueryFilter;
-    use crate::{convert_jlong_to_entity, convert_ptr};
-    use crate::physics::kcc::KCC;
-    use crate::physics::PhysicsState;
-    use crate::scripting::jni::utils::FromJObject;
-    use crate::states::Label;
-    use crate::types::NVector3;
-    use jni::objects::JValue;
-    use jni::sys::{jint, jobjectArray};
-    use crate::types::IndexNative;
-    use crate::scripting::jni::utils::ToJObject;
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_KinematicCharacterControllerNative_existsForEntity(
-        _env: JNIEnv,
-        _: JClass,
-        world_handle: jlong,
-        entity: jlong,
-    ) -> jboolean {
-        let world = convert_ptr!(world_handle => World);
-        let entity = convert_jlong_to_entity!(entity);
-
-        let result = world.get::<&KCC>(entity).is_ok();
-        if result { 1 } else { 0 }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_KinematicCharacterControllerNative_moveCharacter(
-        mut env: JNIEnv,
-        _: JClass,
-        world_handle: jlong,
-        physics_handle: jlong,
-        entity: jlong,
-        translation: JObject,
-        delta_time: jdouble,
-    ) {
-        let world = convert_ptr!(world_handle => World);
-        let physics_state = convert_ptr!(mut physics_handle => PhysicsState);
-        let entity = convert_jlong_to_entity!(entity);
-
-        let movement = match NVector3::from_jobject(&mut env, &translation) {
-            Ok(v) => v,
-            Err(e) => {
-                let _ = env.throw_new("java/lang/RuntimeException", format!("Unable to convert JObject to Vector3: {}", e));
-                return;
-            }
-        };
-
-        if let Ok((label, kcc)) = world.query_one::<(&Label, &KCC)>(entity).get()
-        {
-            let Some(rigid_body_handle) = physics_state.bodies_entity_map.get(label) else {
-                return;
-            };
-
-            let (body_type, body_pos) = {
-                let Some(body) = physics_state.bodies.get(*rigid_body_handle) else {
-                    return;
-                };
-
-                (body.body_type(), *body.position())
-            };
-
-            match body_type {
-                RigidBodyType::KinematicPositionBased => {}
-                _ => return,
-            }
+impl ToJObject for CharacterCollisionArray {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let collision_cls = env.find_class("com/dropbear/physics/CharacterCollision")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
 
-            let Some(collider_handles) = physics_state.colliders_entity_map.get(label) else {
-                return;
-            };
-            let Some((_, collider_handle)) = collider_handles.first() else {
-                return;
-            };
-            let Some(collider) = physics_state.colliders.get(*collider_handle) else {
-                return;
-            };
+        let entity_cls = env.find_class("com/dropbear/EntityId")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
 
-            let character_pos = if let Some(pos_wrt_parent) = collider.position_wrt_parent() {
-                body_pos * (*pos_wrt_parent)
-            } else {
-                *collider.position()
-            };
+        let entity_obj = env.new_object(&entity_cls, "(J)V", &[JValue::Long(self.entity_id as i64)])
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
 
-            let filter = QueryFilter::default().exclude_rigid_body(*rigid_body_handle);
-            let query_pipeline = physics_state.broad_phase.as_query_pipeline(
-                physics_state.narrow_phase.query_dispatcher(),
-                &physics_state.bodies,
-                &physics_state.colliders,
-                filter,
-            );
+        let out = env.new_object_array(self.collisions.len() as i32, &collision_cls, JObject::null())
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
 
-            let movement = kcc.controller.move_shape(
-                delta_time as f32,
-                &query_pipeline,
-                collider.shape(),
-                &character_pos,
-                rapier3d::prelude::Vector::new(movement.x as f32, movement.y as f32, movement.z as f32),
-                |collision| {
-                    if let Some(collisions) = physics_state.collision_events_to_deal_with.get_mut(&entity) {
-                        collisions.push(collision)
-                    } else {
-                        physics_state.collision_events_to_deal_with.insert(entity, vec![collision]);
-                    }
-                },
-            );
+        for (i, handle) in self.collisions.iter().enumerate() {
+            let index_obj = handle.to_jobject(env)?;
+            let collision_obj = env.new_object(
+                &collision_cls,
+                "(Lcom/dropbear/EntityId;Lcom/dropbear/physics/Index;)V",
+                &[JValue::Object(&entity_obj), JValue::Object(&index_obj)],
+            )
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
 
-            if let Some(body) = physics_state.bodies.get_mut(*rigid_body_handle) {
-                let current_pos = body.translation();
-                let new_pos = current_pos + movement.translation;
-                body.set_next_kinematic_translation(new_pos);
-            }
+            env.set_object_array_element(&out, i as i32, collision_obj)
+                .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
         }
+
+        Ok(JObject::from(out))
     }
+}
 
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_KinematicCharacterControllerNative_setRotation(
-        mut env: JNIEnv,
-        _: JClass,
-        world_handle: jlong,
-        physics_handle: jlong,
-        entity: jlong,
-        rotation: JObject,
-    ) {
-        let world = convert_ptr!(world_handle => World);
-        let physics_state = convert_ptr!(mut physics_handle => PhysicsState);
-        let entity = convert_jlong_to_entity!(entity);
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.KinematicCharacterControllerNative", func = "existsForEntity"),
+    c
+)]
+fn kcc_exists_for_entity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<bool> {
+    Ok(world.get::<&KCC>(entity).is_ok())
+}
 
-        let class = match env.find_class("com/dropbear/math/Quaterniond") {
-            Ok(cls) => cls,
-            Err(_) => {
-                let _ = env.throw_new("java/lang/RuntimeException", "Unable to find Quaterniond class");
-                return;
-            }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.KinematicCharacterControllerNative", func = "moveCharacter"),
+    c
+)]
+fn move_character(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(crate::ptr::PhysicsStatePtr)]
+    physics_state: &mut PhysicsState,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    translation: &NVector3,
+    delta_time: f64,
+) -> DropbearNativeResult<()> {
+    if let Ok((label, kcc)) = world.query_one::<(&Label, &KCC)>(entity).get() {
+        let rigid_body_handle = physics_state
+            .bodies_entity_map
+            .get(label)
+            .ok_or(DropbearNativeError::NoSuchHandle)?;
+
+        let (body_type, body_pos) = {
+            let body = physics_state
+                .bodies
+                .get(*rigid_body_handle)
+                .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+            (body.body_type(), *body.position())
         };
 
-        if let Ok(false) = env.is_instance_of(&rotation, &class) {
-            let _ = env.throw_new("java/lang/IllegalArgumentException", "rotation must be Quaterniond");
-            return;
+        if body_type != RigidBodyType::KinematicPositionBased {
+            return Err(DropbearNativeError::InvalidArgument);
         }
 
-        let mut get_double = |field: &str| -> Option<f64> {
-            env.get_field(&rotation, field, "D").ok()?.d().ok()
-        };
-
-        let Some(rx) = get_double("x") else { return; };
-        let Some(ry) = get_double("y") else { return; };
-        let Some(rz) = get_double("z") else { return; };
-        let Some(rw) = get_double("w") else { return; };
-
-        let len = (rx * rx + ry * ry + rz * rz + rw * rw).sqrt();
-        let (x, y, z, w) = if len > 0.0 {
-            (rx / len, ry / len, rz / len, rw / len)
+        let collider_handles = physics_state
+            .colliders_entity_map
+            .get(label)
+            .ok_or(DropbearNativeError::NoSuchHandle)?;
+        let (_, collider_handle) = collider_handles
+            .first()
+            .ok_or(DropbearNativeError::NoSuchHandle)?;
+        let collider = physics_state
+            .colliders
+            .get(*collider_handle)
+            .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+
+        let character_pos = if let Some(pos_wrt_parent) = collider.position_wrt_parent() {
+            body_pos * (*pos_wrt_parent)
         } else {
-            (0.0, 0.0, 0.0, 1.0)
+            *collider.position()
         };
 
-        if let Ok((label, _kcc)) = world.query_one::<(&Label, &KCC)>(entity).get() {
-            let Some(rigid_body_handle) = physics_state.bodies_entity_map.get(label) else {
-                return;
-            };
-
-            let body_type = {
-                let Some(body) = physics_state.bodies.get(*rigid_body_handle) else {
-                    return;
-                };
-                body.body_type()
-            };
-
-            match body_type {
-                RigidBodyType::KinematicPositionBased => {}
-                _ => return,
-            }
+        let filter = QueryFilter::default().exclude_rigid_body(*rigid_body_handle);
+        let query_pipeline = physics_state.broad_phase.as_query_pipeline(
+            physics_state.narrow_phase.query_dispatcher(),
+            &physics_state.bodies,
+            &physics_state.colliders,
+            filter,
+        );
+
+        let movement = kcc.controller.move_shape(
+            delta_time as f32,
+            &query_pipeline,
+            collider.shape(),
+            &character_pos,
+            rapier3d::prelude::Vector::new(
+                translation.x as f32,
+                translation.y as f32,
+                translation.z as f32,
+            ),
+            |collision| {
+                if let Some(collisions) = physics_state.collision_events_to_deal_with.get_mut(&entity) {
+                    collisions.push(collision)
+                } else {
+                    physics_state.collision_events_to_deal_with.insert(entity, vec![collision]);
+                }
+            },
+        );
 
-            if let Some(body) = physics_state.bodies.get_mut(*rigid_body_handle) {
-                let rot = Rotation::from_xyzw(x as f32, y as f32, z as f32, w as f32);
-                body.set_next_kinematic_rotation(rot);
-            }
+        if let Some(body) = physics_state.bodies.get_mut(*rigid_body_handle) {
+            let current_pos = body.translation();
+            let new_pos = current_pos + movement.translation;
+            body.set_next_kinematic_translation(new_pos);
         }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_physics_KinematicCharacterControllerNative_getHitNative(
-        mut env: JNIEnv,
-        _: JClass,
-        world_handle: jlong,
-        entity: jlong,
-    ) -> jobjectArray {
-        let world = convert_ptr!(world_handle => World);
-        let entity = convert_jlong_to_entity!(entity);
 
-        let Ok(kcc) = world.get::<&KCC>(entity) else {
-            return std::ptr::null_mut();
-        };
-
-        let collision_cls = match env.find_class("com/dropbear/physics/CharacterCollision") {
-            Ok(cls) => cls,
-            Err(e) => {
-                eprintln!("[JNI Error] Could not find CharacterCollision class: {:?}", e);
-                return std::ptr::null_mut();
-            }
-        };
+        Ok(())
+    } else {
+        Err(DropbearNativeError::MissingComponent)
+    }
+}
 
-        let entity_cls = match env.find_class("com/dropbear/EntityId") {
-            Ok(cls) => cls,
-            Err(e) => {
-                eprintln!("[JNI Error] Could not find EntityId class: {:?}", e);
-                return std::ptr::null_mut();
-            }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.KinematicCharacterControllerNative", func = "setRotation"),
+    c
+)]
+fn set_rotation(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(crate::ptr::PhysicsStatePtr)]
+    physics_state: &mut PhysicsState,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    rotation: &NQuaternion,
+) -> DropbearNativeResult<()> {
+    if let Ok((label, _)) = world.query_one::<(&Label, &KCC)>(entity).get() {
+        let rigid_body_handle = physics_state
+            .bodies_entity_map
+            .get(label)
+            .ok_or(DropbearNativeError::NoSuchHandle)?;
+
+        let body_type = {
+            let body = physics_state
+                .bodies
+                .get(*rigid_body_handle)
+                .ok_or(DropbearNativeError::PhysicsObjectNotFound)?;
+            body.body_type()
         };
 
-        let out = match env.new_object_array(kcc.collisions.len() as jint, &collision_cls, JObject::null()) {
-            Ok(arr) => arr,
-            Err(e) => {
-                eprintln!("[JNI Error] Failed to allocate CharacterCollision array: {:?}", e);
-                let _ = env.throw_new("java/lang/OutOfMemoryError", "Could not allocate CharacterCollision array");
-                return std::ptr::null_mut();
-            }
-        };
+        if body_type != RigidBodyType::KinematicPositionBased {
+            return Err(DropbearNativeError::InvalidArgument);
+        }
 
-        let entity_id_obj = match env.new_object(&entity_cls, "(J)V", &[JValue::Long(entity.to_bits().get() as i64)]) {
-            Ok(obj) => obj,
-            Err(e) => {
-                eprintln!("[JNI Error] Failed to create EntityId object: {:?}", e);
-                return std::ptr::null_mut();
-            }
+        let len = (rotation.x * rotation.x
+            + rotation.y * rotation.y
+            + rotation.z * rotation.z
+            + rotation.w * rotation.w)
+            .sqrt();
+        let (x, y, z, w) = if len > 0.0 {
+            (
+                rotation.x / len,
+                rotation.y / len,
+                rotation.z / len,
+                rotation.w / len,
+            )
+        } else {
+            (0.0, 0.0, 0.0, 1.0)
         };
 
-        for (i, _) in kcc.collisions.iter().enumerate() {
-            let collision = &kcc.collisions[i];
-            let (idx, generation) = collision.handle.into_raw_parts();
-            let index_obj = match (IndexNative {
-                index: idx,
-                generation,
-            })
-            .to_jobject(&mut env)
-            {
-                Ok(obj) => obj,
-                Err(e) => {
-                    eprintln!("[JNI Error] Failed to create Index object: {e}");
-                    return std::ptr::null_mut();
-                }
-            };
-
-            let collision_obj = match env.new_object(
-                &collision_cls,
-                "(Lcom/dropbear/EntityId;Lcom/dropbear/physics/Index;)V",
-                &[JValue::Object(&entity_id_obj), JValue::Object(&index_obj)],
-            ) {
-                Ok(obj) => obj,
-                Err(e) => {
-                    eprintln!("[JNI Error] Failed to create CharacterCollision object: {:?}", e);
-                    return std::ptr::null_mut();
-                }
-            };
-
-            if let Err(e) = env.set_object_array_element(&out, i as jint, collision_obj) {
-                eprintln!("[JNI Error] Failed to set array element: {:?}", e);
-                return std::ptr::null_mut();
-            }
+        if let Some(body) = physics_state.bodies.get_mut(*rigid_body_handle) {
+            let rot = Rotation::from_xyzw(x as f32, y as f32, z as f32, w as f32);
+            body.set_next_kinematic_rotation(rot);
         }
 
-        out.into_raw()
+        Ok(())
+    } else {
+        Err(DropbearNativeError::MissingComponent)
     }
+}
+
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.KinematicCharacterControllerNative", func = "getHit"),
+    c
+)]
+fn get_hit(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<CharacterCollisionArray> {
+    let kcc = world
+        .get::<&KCC>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+
+    let mut collisions = Vec::with_capacity(kcc.collisions.len());
+    for collision in &kcc.collisions {
+        let (idx, generation) = collision.handle.into_raw_parts();
+        collisions.push(IndexNative { index: idx, generation });
+    }
+
+    Ok(CharacterCollisionArray {
+        entity_id: entity.to_bits().get(),
+        collisions,
+    })
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/physics/kcc/character_collision.rs b/crates/eucalyptus-core/src/physics/kcc/character_collision.rs
index 93f2910..b93ec25 100644
--- a/crates/eucalyptus-core/src/physics/kcc/character_collision.rs
+++ b/crates/eucalyptus-core/src/physics/kcc/character_collision.rs
@@ -3,16 +3,16 @@ use ::jni::objects::{JObject, JValue};
 use crate::scripting::jni::utils::ToJObject;
 use crate::scripting::native::DropbearNativeError;
 use crate::scripting::result::DropbearNativeResult;
-use crate::types::{NCollider, IndexNative, NVector3};
-use dropbear_engine::entity::Transform as DbTransform;
+use crate::types::{IndexNative, NCollider, NShapeCastStatus, NTransform, NVector3};
+use dropbear_engine::entity::Transform;
 use hecs::{Entity, World};
 use rapier3d::control::CharacterCollision;
-use rapier3d::parry::query::ShapeCastStatus;
 
 use crate::physics::collider::ColliderGroup;
 use crate::physics::kcc::KCC;
+use crate::ptr::WorldPtr;
 
-fn get_collision_from_world(world: &World, entity: Entity, collision_handle: IndexNative) -> DropbearNativeResult<CharacterCollision> {
+fn get_collision_from_world(world: &World, entity: Entity, collision_handle: &IndexNative) -> DropbearNativeResult<CharacterCollision> {
 	let kcc = world
 		.get::<&KCC>(entity)
 		.map_err(|_| DropbearNativeError::NoSuchComponent)?;
@@ -43,17 +43,17 @@ fn collider_ffi_from_handle(world: &World, handle: rapier3d::prelude::ColliderHa
 	None
 }
 
-impl ToJObject for ShapeCastStatus {
+impl ToJObject for NShapeCastStatus {
 	fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
 		let class = env
 			.find_class("com/dropbear/physics/ShapeCastStatus")
 			.map_err(|_| DropbearNativeError::JNIClassNotFound)?;
 
 		let name = match self {
-			ShapeCastStatus::OutOfIterations => "OutOfIterations",
-			ShapeCastStatus::Converged => "Converged",
-			ShapeCastStatus::Failed => "Failed",
-			ShapeCastStatus::PenetratingOrWithinTargetDist => "PenetratingOrWithinTargetDist",
+			NShapeCastStatus::OutOfIterations => "OutOfIterations",
+			NShapeCastStatus::Converged => "Converged",
+			NShapeCastStatus::Failed => "Failed",
+			NShapeCastStatus::PenetratingOrWithinTargetDist => "PenetratingOrWithinTargetDist",
 		};
 
 		let name_jstring = env
@@ -79,389 +79,212 @@ pub mod shared {
 	use super::*;
 	use glam::{DQuat, DVec3};
 	use rapier3d::na::Quaternion;
+	use crate::types::NVector3;
 
-	pub fn get_collider(world: &World, entity: Entity, collision_handle: IndexNative) -> DropbearNativeResult<NCollider> {
-		let collision = super::get_collision_from_world(world, entity, collision_handle)?;
+	pub fn get_collider(world: &World, entity: Entity, collision_handle: &IndexNative) -> DropbearNativeResult<NCollider> {
+		let collision = get_collision_from_world(world, entity, collision_handle)?;
 		collider_ffi_from_handle(world, collision.handle)
 			.ok_or(DropbearNativeError::PhysicsObjectNotFound)
 	}
 
-	pub fn get_character_position(world: &World, entity: Entity, collision_handle: IndexNative) -> DropbearNativeResult<DbTransform> {
-		let collision = super::get_collision_from_world(world, entity, collision_handle)?;
+	pub fn get_character_position(world: &World, entity: Entity, collision_handle: &IndexNative) -> DropbearNativeResult<NTransform> {
+		let collision = get_collision_from_world(world, entity, collision_handle)?;
 
 		let iso = collision.character_pos;
 		let t = iso.translation;
 		let rot = iso.rotation;
 		let q: Quaternion<f32> = Quaternion::from(rot);
 
-		Ok(DbTransform {
-			position: DVec3::new(t.x as f64, t.y as f64, t.z as f64),
-			rotation: DQuat::from_xyzw(q.i as f64, q.j as f64, q.k as f64, q.w as f64),
-			scale: DVec3::ONE,
+		Ok(NTransform {
+			position: DVec3::new(t.x as f64, t.y as f64, t.z as f64).into(),
+			rotation: DQuat::from_xyzw(q.i as f64, q.j as f64, q.k as f64, q.w as f64).into(),
+			scale: DVec3::ONE.into(),
 		})
 	}
 
-	pub fn get_translation_applied(world: &World, entity: Entity, collision_handle: IndexNative) -> DropbearNativeResult<NVector3> {
-		let collision = super::get_collision_from_world(world, entity, collision_handle)?;
+	pub fn get_translation_applied(world: &World, entity: Entity, collision_handle: &IndexNative) -> DropbearNativeResult<NVector3> {
+		let collision = get_collision_from_world(world, entity, collision_handle)?;
 		let v = collision.translation_applied;
 		Ok(NVector3 { x: v.x as f64, y: v.y as f64, z: v.z as f64 })
 	}
 
-	pub fn get_translation_remaining(world: &World, entity: Entity, collision_handle: IndexNative) -> DropbearNativeResult<NVector3> {
-		let collision = super::get_collision_from_world(world, entity, collision_handle)?;
+	pub fn get_translation_remaining(world: &World, entity: Entity, collision_handle: &IndexNative) -> DropbearNativeResult<NVector3> {
+		let collision = get_collision_from_world(world, entity, collision_handle)?;
 		let v = collision.translation_remaining;
 		Ok(NVector3 { x: v.x as f64, y: v.y as f64, z: v.z as f64 })
 	}
 
-	pub fn get_time_of_impact(world: &World, entity: Entity, collision_handle: IndexNative) -> DropbearNativeResult<f64> {
-		let collision = super::get_collision_from_world(world, entity, collision_handle)?;
+	pub fn get_time_of_impact(world: &World, entity: Entity, collision_handle: &IndexNative) -> DropbearNativeResult<f64> {
+		let collision = get_collision_from_world(world, entity, collision_handle)?;
 		Ok(collision.hit.time_of_impact as f64)
 	}
 
-	pub fn get_witness1(world: &World, entity: Entity, collision_handle: IndexNative) -> DropbearNativeResult<NVector3> {
-		let collision = super::get_collision_from_world(world, entity, collision_handle)?;
+	pub fn get_witness1(world: &World, entity: Entity, collision_handle: &IndexNative) -> DropbearNativeResult<NVector3> {
+		let collision = get_collision_from_world(world, entity, collision_handle)?;
 		let p = collision.hit.witness1;
 		Ok(NVector3 { x: p.x as f64, y: p.y as f64, z: p.z as f64 })
 	}
 
-	pub fn get_witness2(world: &World, entity: Entity, collision_handle: IndexNative) -> DropbearNativeResult<NVector3> {
-		let collision = super::get_collision_from_world(world, entity, collision_handle)?;
+	pub fn get_witness2(world: &World, entity: Entity, collision_handle: &IndexNative) -> DropbearNativeResult<NVector3> {
+		let collision = get_collision_from_world(world, entity, collision_handle)?;
 		let p = collision.hit.witness2;
 		Ok(NVector3 { x: p.x as f64, y: p.y as f64, z: p.z as f64 })
 	}
 
-	pub fn get_normal1(world: &World, entity: Entity, collision_handle: IndexNative) -> DropbearNativeResult<NVector3> {
-		let collision = super::get_collision_from_world(world, entity, collision_handle)?;
+	pub fn get_normal1(world: &World, entity: Entity, collision_handle: &IndexNative) -> DropbearNativeResult<NVector3> {
+		let collision = get_collision_from_world(world, entity, collision_handle)?;
 		let n = collision.hit.normal1;
 		Ok(NVector3 { x: n.x as f64, y: n.y as f64, z: n.z as f64 })
 	}
 
-	pub fn get_normal2(world: &World, entity: Entity, collision_handle: IndexNative) -> DropbearNativeResult<NVector3> {
-		let collision = super::get_collision_from_world(world, entity, collision_handle)?;
+	pub fn get_normal2(world: &World, entity: Entity, collision_handle: &IndexNative) -> DropbearNativeResult<NVector3> {
+		let collision = get_collision_from_world(world, entity, collision_handle)?;
 		let n = collision.hit.normal2;
 		Ok(NVector3 { x: n.x as f64, y: n.y as f64, z: n.z as f64 })
 	}
 
-	pub fn get_status(world: &World, entity: Entity, collision_handle: IndexNative) -> DropbearNativeResult<ShapeCastStatus> {
-		let collision = super::get_collision_from_world(world, entity, collision_handle)?;
-		Ok(collision.hit.status)
+	pub fn get_status(world: &World, entity: Entity, collision_handle: &IndexNative) -> DropbearNativeResult<NShapeCastStatus> {
+		let collision = get_collision_from_world(world, entity, collision_handle)?;
+		Ok(collision.hit.status.into())
 	}
 }
 
-pub mod jni {
-	#![allow(non_snake_case)]
-
-	use hecs::World;
-	use jni::objects::JClass;
-	use jni::sys::{jdouble, jlong, jobject};
-	use jni::JNIEnv;
-	use crate::convert_jlong_to_entity;
-	use crate::convert_ptr;
-	use crate::scripting::jni::utils::{FromJObject, ToJObject};
-	use crate::types::IndexNative;
-	use jni::objects::JObject;
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_CharacterCollisionNative_getCollider(
-		mut env: JNIEnv,
-		_: JClass,
-		world_handle: jlong,
-		entity: jlong,
-		collision_handle: JObject,
-	) -> jobject {
-		let world = convert_ptr!(world_handle => World);
-		let entity = convert_jlong_to_entity!(entity);
-
-		let Ok(index_native) = IndexNative::from_jobject(&mut env, &collision_handle) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Failed to read Index for collision handle");
-			return std::ptr::null_mut();
-		};
-
-		match super::shared::get_collider(&world, entity, index_native) {
-			Ok(ffi) => match ffi.to_jobject(&mut env) {
-				Ok(obj) => obj.into_raw(),
-				Err(e) => {
-					let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create Collider object: {e}"));
-					std::ptr::null_mut()
-				}
-			},
-			Err(e) => {
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to resolve collider: {e:?}"));
-				std::ptr::null_mut()
-			}
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_CharacterCollisionNative_getCharacterPosition(
-		mut env: JNIEnv,
-		_: JClass,
-		world_handle: jlong,
-		entity: jlong,
-		collision_handle: JObject,
-	) -> jobject {
-		let world = convert_ptr!(world_handle => World);
-		let entity = convert_jlong_to_entity!(entity);
-
-		let Ok(index_native) = IndexNative::from_jobject(&mut env, &collision_handle) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Failed to read Index for collision handle");
-			return std::ptr::null_mut();
-		};
-
-		match super::shared::get_character_position(&world, entity, index_native) {
-			Ok(transform) => match transform.to_jobject(&mut env) {
-				Ok(obj) => obj.into_raw(),
-				Err(e) => {
-					let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create Transform object: {e}"));
-					std::ptr::null_mut()
-				}
-			},
-			Err(e) => {
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to resolve character position: {e:?}"));
-				std::ptr::null_mut()
-			}
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_CharacterCollisionNative_getTranslationApplied(
-		mut env: JNIEnv,
-		_: JClass,
-		world_handle: jlong,
-		entity: jlong,
-		collision_handle: JObject,
-	) -> jobject {
-		let world = convert_ptr!(world_handle => World);
-		let entity = convert_jlong_to_entity!(entity);
-
-		let Ok(index_native) = IndexNative::from_jobject(&mut env, &collision_handle) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Failed to read Index for collision handle");
-			return std::ptr::null_mut();
-		};
-
-		match super::shared::get_translation_applied(&world, entity, index_native) {
-			Ok(v) => match v.to_jobject(&mut env) {
-				Ok(obj) => obj.into_raw(),
-				Err(e) => {
-					let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create Vector3d: {e}"));
-					std::ptr::null_mut()
-				}
-			},
-			Err(e) => {
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to resolve translation applied: {e:?}"));
-				std::ptr::null_mut()
-			}
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_CharacterCollisionNative_getTranslationRemaining(
-		mut env: JNIEnv,
-		_: JClass,
-		world_handle: jlong,
-		entity: jlong,
-		collision_handle: JObject,
-	) -> jobject {
-		let world = convert_ptr!(world_handle => World);
-		let entity = convert_jlong_to_entity!(entity);
-
-		let Ok(index_native) = IndexNative::from_jobject(&mut env, &collision_handle) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Failed to read Index for collision handle");
-			return std::ptr::null_mut();
-		};
-
-		match super::shared::get_translation_remaining(&world, entity, index_native) {
-			Ok(v) => match v.to_jobject(&mut env) {
-				Ok(obj) => obj.into_raw(),
-				Err(e) => {
-					let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create Vector3d: {e}"));
-					std::ptr::null_mut()
-				}
-			},
-			Err(e) => {
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to resolve translation remaining: {e:?}"));
-				std::ptr::null_mut()
-			}
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_CharacterCollisionNative_getTimeOfImpact(
-		mut env: JNIEnv,
-		_: JClass,
-		world_handle: jlong,
-		entity: jlong,
-		collision_handle: JObject,
-	) -> jdouble {
-		let world = convert_ptr!(world_handle => World);
-		let entity = convert_jlong_to_entity!(entity);
-
-		let Ok(index_native) = IndexNative::from_jobject(&mut env, &collision_handle) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Failed to read Index for collision handle");
-			return -1.0 as jdouble;
-		};
-
-		match super::shared::get_time_of_impact(&world, entity, index_native) {
-			Ok(v) => v as jdouble,
-			Err(e) => {
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to resolve time of impact: {e:?}"));
-				-1.0 as jdouble
-			}
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_CharacterCollisionNative_getWitness1(
-		mut env: JNIEnv,
-		_: JClass,
-		world_handle: jlong,
-		entity: jlong,
-		collision_handle: JObject,
-	) -> jobject {
-		let world = convert_ptr!(world_handle => World);
-		let entity = convert_jlong_to_entity!(entity);
-
-		let Ok(index_native) = IndexNative::from_jobject(&mut env, &collision_handle) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Failed to read Index for collision handle");
-			return std::ptr::null_mut();
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.CharacterCollisionNative", func = "getCollider"),
+	c
+)]
+fn get_character_collision_collider(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    collision_handle: &IndexNative,
+) -> DropbearNativeResult<crate::types::NCollider> {
+    shared::get_collider(world, entity, collision_handle)
+}
 
-		match super::shared::get_witness1(&world, entity, index_native) {
-			Ok(v) => match v.to_jobject(&mut env) {
-				Ok(obj) => obj.into_raw(),
-				Err(e) => {
-					let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create Vector3d: {e}"));
-					std::ptr::null_mut()
-				}
-			},
-			Err(e) => {
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to resolve witness1: {e:?}"));
-				std::ptr::null_mut()
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.CharacterCollisionNative", func = "getCharacterPosition"),
+	c
+)]
+fn get_character_collision_position(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    collision_handle: &IndexNative,
+) -> DropbearNativeResult<NTransform> {
+    shared::get_character_position(world, entity, collision_handle)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_CharacterCollisionNative_getWitness2(
-		mut env: JNIEnv,
-		_: JClass,
-		world_handle: jlong,
-		entity: jlong,
-		collision_handle: JObject,
-	) -> jobject {
-		let world = convert_ptr!(world_handle => World);
-		let entity = convert_jlong_to_entity!(entity);
-
-		let Ok(index_native) = IndexNative::from_jobject(&mut env, &collision_handle) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Failed to read Index for collision handle");
-			return std::ptr::null_mut();
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.CharacterCollisionNative", func = "getTranslationApplied"),
+	c
+)]
+fn get_character_collision_translation_applied(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    collision_handle: &IndexNative,
+) -> DropbearNativeResult<NVector3> {
+    shared::get_translation_applied(world, entity, collision_handle)
+}
 
-		match super::shared::get_witness2(&world, entity, index_native) {
-			Ok(v) => match v.to_jobject(&mut env) {
-				Ok(obj) => obj.into_raw(),
-				Err(e) => {
-					let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create Vector3d: {e}"));
-					std::ptr::null_mut()
-				}
-			},
-			Err(e) => {
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to resolve witness2: {e:?}"));
-				std::ptr::null_mut()
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.CharacterCollisionNative", func = "getTranslationRemaining"),
+	c
+)]
+fn get_character_collision_translation_remaining(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    collision_handle: &IndexNative,
+) -> DropbearNativeResult<NVector3> {
+    shared::get_translation_remaining(world, entity, collision_handle)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_CharacterCollisionNative_getNormal1(
-		mut env: JNIEnv,
-		_: JClass,
-		world_handle: jlong,
-		entity: jlong,
-		collision_handle: JObject,
-	) -> jobject {
-		let world = convert_ptr!(world_handle => World);
-		let entity = convert_jlong_to_entity!(entity);
-
-		let Ok(index_native) = IndexNative::from_jobject(&mut env, &collision_handle) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Failed to read Index for collision handle");
-			return std::ptr::null_mut();
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.CharacterCollisionNative", func = "getTimeOfImpact"),
+	c
+)]
+fn get_character_collision_time_of_impact(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    collision_handle: &IndexNative,
+) -> DropbearNativeResult<f64> {
+    shared::get_time_of_impact(world, entity, collision_handle)
+}
 
-		match super::shared::get_normal1(&world, entity, index_native) {
-			Ok(v) => match v.to_jobject(&mut env) {
-				Ok(obj) => obj.into_raw(),
-				Err(e) => {
-					let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create Vector3d: {e}"));
-					std::ptr::null_mut()
-				}
-			},
-			Err(e) => {
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to resolve normal1: {e:?}"));
-				std::ptr::null_mut()
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.CharacterCollisionNative", func = "getWitness1"),
+	c
+)]
+fn get_character_collision_witness1(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    collision_handle: &IndexNative,
+) -> DropbearNativeResult<NVector3> {
+    shared::get_witness1(world, entity, collision_handle)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_CharacterCollisionNative_getNormal2(
-		mut env: JNIEnv,
-		_: JClass,
-		world_handle: jlong,
-		entity: jlong,
-		collision_handle: JObject,
-	) -> jobject {
-		let world = convert_ptr!(world_handle => World);
-		let entity = convert_jlong_to_entity!(entity);
-
-		let Ok(index_native) = IndexNative::from_jobject(&mut env, &collision_handle) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Failed to read Index for collision handle");
-			return std::ptr::null_mut();
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.CharacterCollisionNative", func = "getWitness2"),
+	c
+)]
+fn get_character_collision_witness2(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    collision_handle: &IndexNative,
+) -> DropbearNativeResult<NVector3> {
+    shared::get_witness2(world, entity, collision_handle)
+}
 
-		match super::shared::get_normal2(&world, entity, index_native) {
-			Ok(v) => match v.to_jobject(&mut env) {
-				Ok(obj) => obj.into_raw(),
-				Err(e) => {
-					let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create Vector3d: {e}"));
-					std::ptr::null_mut()
-				}
-			},
-			Err(e) => {
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to resolve normal2: {e:?}"));
-				std::ptr::null_mut()
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.CharacterCollisionNative", func = "getNormal1"),
+	c
+)]
+fn get_character_collision_normal1(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    collision_handle: &IndexNative,
+) -> DropbearNativeResult<NVector3> {
+    shared::get_normal1(world, entity, collision_handle)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_CharacterCollisionNative_getStatus(
-		mut env: JNIEnv,
-		_: JClass,
-		world_handle: jlong,
-		entity: jlong,
-		collision_handle: JObject,
-	) -> jobject {
-		let world = convert_ptr!(world_handle => World);
-		let entity = convert_jlong_to_entity!(entity);
-
-		let Ok(index_native) = IndexNative::from_jobject(&mut env, &collision_handle) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Failed to read Index for collision handle");
-			return std::ptr::null_mut();
-		};
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.CharacterCollisionNative", func = "getNormal2"),
+	c
+)]
+fn get_character_collision_normal2(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    collision_handle: &IndexNative,
+) -> DropbearNativeResult<NVector3> {
+    shared::get_normal2(world, entity, collision_handle)
+}
 
-		match super::shared::get_status(&world, entity, index_native) {
-			Ok(status) => match status.to_jobject(&mut env) {
-				Ok(obj) => obj.into_raw(),
-				Err(e) => {
-					let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create ShapeCastStatus: {e}"));
-					std::ptr::null_mut()
-				}
-			},
-			Err(e) => {
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to resolve status: {e:?}"));
-				std::ptr::null_mut()
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.CharacterCollisionNative", func = "getStatus"),
+	c
+)]
+fn get_character_collision_status(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+    collision_handle: &IndexNative,
+) -> DropbearNativeResult<NShapeCastStatus> {
+    shared::get_status(world, entity, collision_handle)
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/physics/rigidbody.rs b/crates/eucalyptus-core/src/physics/rigidbody.rs
index 0e43106..306a031 100644
--- a/crates/eucalyptus-core/src/physics/rigidbody.rs
+++ b/crates/eucalyptus-core/src/physics/rigidbody.rs
@@ -10,6 +10,9 @@ use ::jni::objects::{JObject, JValue};
 use ::jni::JNIEnv;
 use rapier3d::prelude::RigidBodyType;
 use serde::{Deserialize, Serialize};
+use crate::types::{IndexNative, NCollider, RigidBodyContext, NVector3};
+use crate::physics::PhysicsState;
+use crate::ptr::{PhysicsStatePtr, WorldPtr};
 
 /// How this entity behaves in the physics simulation.
 ///
@@ -226,7 +229,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn get_rigidbody_type(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<RigidBodyType> {
+	pub fn get_rigidbody_type(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<RigidBodyType> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get(handle) {
 			Ok(rb.body_type())
@@ -235,7 +238,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn set_rigidbody_type(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, mode: i64) -> DropbearNativeResult<()> {
+	pub fn set_rigidbody_type(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, mode: i64) -> DropbearNativeResult<()> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get_mut(handle) {
 			let mode = match mode {
@@ -260,7 +263,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn get_rigidbody_gravity_scale(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<f64> {
+	pub fn get_rigidbody_gravity_scale(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<f64> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get(handle) {
 			Ok(rb.gravity_scale().into())
@@ -269,7 +272,7 @@ pub mod shared {
 		}
 	}
 	
-	pub fn set_rigidbody_gravity_scale(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new_scale: f64) -> DropbearNativeResult<()> {
+	pub fn set_rigidbody_gravity_scale(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new_scale: f64) -> DropbearNativeResult<()> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get_mut(handle) {
 			rb.set_gravity_scale(new_scale as f32, true);
@@ -286,7 +289,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn get_rigidbody_linear_damping(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<f64> {
+	pub fn get_rigidbody_linear_damping(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<f64> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get(handle) {
 			Ok(rb.linear_damping().into())
@@ -295,7 +298,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn set_rigidbody_linear_damping(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: f64) -> DropbearNativeResult<()> {
+	pub fn set_rigidbody_linear_damping(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: f64) -> DropbearNativeResult<()> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get_mut(handle) {
 			rb.set_linear_damping(new as f32);
@@ -312,7 +315,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn get_rigidbody_angular_damping(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<f64> {
+	pub fn get_rigidbody_angular_damping(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<f64> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get(handle) {
 			Ok(rb.angular_damping().into())
@@ -321,7 +324,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn set_rigidbody_angular_damping(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: f64) -> DropbearNativeResult<()> {
+	pub fn set_rigidbody_angular_damping(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: f64) -> DropbearNativeResult<()> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get_mut(handle) {
 			rb.set_angular_damping(new as f32);
@@ -338,7 +341,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn get_rigidbody_sleep(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<bool> {
+	pub fn get_rigidbody_sleep(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<bool> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get(handle) {
 			Ok(rb.is_sleeping().into())
@@ -347,7 +350,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn set_rigidbody_sleep(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: bool) -> DropbearNativeResult<()> {
+	pub fn set_rigidbody_sleep(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: bool) -> DropbearNativeResult<()> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get_mut(handle) {
 			if new {
@@ -368,7 +371,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn get_rigidbody_ccd(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<bool> {
+	pub fn get_rigidbody_ccd(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<bool> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get(handle) {
 			Ok(rb.is_ccd_enabled().into())
@@ -377,7 +380,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn set_rigidbody_ccd(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: bool) -> DropbearNativeResult<()> {
+	pub fn set_rigidbody_ccd(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: bool) -> DropbearNativeResult<()> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get_mut(handle) {
 			rb.enable_ccd(new);
@@ -394,7 +397,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn get_rigidbody_linvel(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<NVector3> {
+	pub fn get_rigidbody_linvel(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<NVector3> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get(handle) {
 			let linvel = rb.linvel().clone();
@@ -404,7 +407,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn set_rigidbody_linvel(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: NVector3) -> DropbearNativeResult<()> {
+	pub fn set_rigidbody_linvel(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: NVector3) -> DropbearNativeResult<()> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get_mut(handle) {
 			rb.set_linvel(Vector::new(new.x as f32, new.y as f32, new.z as f32), true);
@@ -421,7 +424,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn get_rigidbody_angvel(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<NVector3> {
+	pub fn get_rigidbody_angvel(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<NVector3> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get(handle) {
 			let angvel = rb.angvel().clone();
@@ -431,7 +434,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn set_rigidbody_angvel(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: NVector3) -> DropbearNativeResult<()> {
+	pub fn set_rigidbody_angvel(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: NVector3) -> DropbearNativeResult<()> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get_mut(handle) {
 			rb.set_angvel(Vector::new(new.x as f32, new.y as f32, new.z as f32), true);
@@ -448,7 +451,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn get_rigidbody_lock_translation(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<AxisLock> {
+	pub fn get_rigidbody_lock_translation(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<AxisLock> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get(handle) {
 			let lock = rb.locked_axes();
@@ -463,7 +466,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn set_rigidbody_lock_translation(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: AxisLock) -> DropbearNativeResult<()> {
+	pub fn set_rigidbody_lock_translation(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: AxisLock) -> DropbearNativeResult<()> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get_mut(handle) {
 			let mut bits = rb.locked_axes().clone();
@@ -496,7 +499,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn get_rigidbody_lock_rotation(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<AxisLock> {
+	pub fn get_rigidbody_lock_rotation(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<AxisLock> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get(handle) {
 			let lock = rb.locked_axes();
@@ -511,7 +514,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn set_rigidbody_lock_rotation(physics: &mut PhysicsState, world: &World, rb_context: RigidBodyContext, new: AxisLock) -> DropbearNativeResult<()> {
+	pub fn set_rigidbody_lock_rotation(physics: &mut PhysicsState, world: &World, rb_context: &RigidBodyContext, new: AxisLock) -> DropbearNativeResult<()> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get_mut(handle) {
 			let mut bits = rb.locked_axes().clone();
@@ -544,7 +547,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn get_rigidbody_children(physics: &PhysicsState, rb_context: RigidBodyContext) -> DropbearNativeResult<Vec<ColliderHandle>> {
+	pub fn get_rigidbody_children(physics: &PhysicsState, rb_context: &RigidBodyContext) -> DropbearNativeResult<Vec<ColliderHandle>> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get(handle) {
 			let children = rb.colliders().to_vec();
@@ -554,7 +557,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn apply_impulse(physics: &mut PhysicsState, _world: &World, rb_context: RigidBodyContext, new: NVector3) -> DropbearNativeResult<()> {
+	pub fn apply_impulse(physics: &mut PhysicsState, _world: &World, rb_context: &RigidBodyContext, new: NVector3) -> DropbearNativeResult<()> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get_mut(handle) {
 			rb.apply_impulse(Vector::new(new.x as f32, new.y as f32, new.z as f32), true);
@@ -565,7 +568,7 @@ pub mod shared {
 		}
 	}
 
-	pub fn apply_torque_impulse(physics: &mut PhysicsState, _world: &World, rb_context: RigidBodyContext, new: NVector3) -> DropbearNativeResult<()> {
+	pub fn apply_torque_impulse(physics: &mut PhysicsState, _world: &World, rb_context: &RigidBodyContext, new: NVector3) -> DropbearNativeResult<()> {
 		let handle = RigidBodyHandle::from_raw_parts(rb_context.index.index, rb_context.index.generation);
 		if let Some(rb) = physics.bodies.get_mut(handle) {
 			rb.apply_torque_impulse(Vector::new(new.x as f32, new.y as f32, new.z as f32), true);
@@ -577,710 +580,376 @@ pub mod shared {
 	}
 }
 
-pub mod jni {
-	#![allow(non_snake_case)]
-	use crate::physics::rigidbody::AxisLock;
-	use crate::physics::PhysicsState;
-	use crate::scripting::jni::utils::{FromJObject, ToJObject};
-	use crate::types::{IndexNative, RigidBodyContext, NVector3};
-	use crate::{convert_jlong_to_entity, convert_ptr};
-	use hecs::World;
-	use jni::objects::{JClass, JObject};
-	use jni::sys::{jboolean, jdouble, jint, jlong, jobject, jobjectArray, jsize};
-	use jni::JNIEnv;
-	use rapier3d::dynamics::RigidBodyType;
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_rigidBodyExistsForEntity(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		entity_id: jlong,
-	) -> jobject {
-		let world = convert_ptr!(world_ptr => World);
-		let physics = convert_ptr!(physics_ptr => PhysicsState);
-		let entity = convert_jlong_to_entity!(entity_id);
-
-		match super::shared::rigid_body_exists_for_entity(world, physics, entity) {
-			Some(v) => {
-				match v.to_jobject(&mut env) {
-					Ok(val) => val.into_raw(),
-					Err(e) => {
-						eprintln!("Failed to create new Index jobject: {}", e);
-						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create new Index jobject: {}", e));
-						std::ptr::null_mut()
-					}
-				}
-			}
-			None => std::ptr::null_mut()
-		}
-	}
-
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyMode(
-		mut env: JNIEnv,
-		_: JClass,
-		_world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-	) -> jint {
-		let physics = convert_ptr!(physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException", "Unable to convert com/dropbear/physics/RigidBody into a Rust rigidbody");
-			return -1 as jint;
-		};
-
-		match super::shared::get_rigidbody_type(physics, rb_context) {
-			Ok(v) => {
-				match v {
-					RigidBodyType::Dynamic => 0 as jint,
-					RigidBodyType::Fixed => 1 as jint,
-					RigidBodyType::KinematicPositionBased => 2 as jint,
-					RigidBodyType::KinematicVelocityBased => 3 as jint,
-				}
-			}
-			Err(e) => {
-				eprintln!("Failed to get RigidBody type: {}", e);
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody type: {}", e));
-				-1 as jint
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "rigidBodyExistsForEntity"),
+	c
+)]
+fn rigid_body_exists_for_entity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    #[dropbear_macro::entity]
+    entity: hecs::Entity,
+) -> DropbearNativeResult<Option<IndexNative>> {
+    Ok(shared::rigid_body_exists_for_entity(world, physics, entity))
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyMode(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-		mode: jint,
-	) {
-		let world = convert_ptr!(world_ptr => World);
-		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return;
-		};
-
-		if let Err(e) = super::shared::set_rigidbody_type(&mut physics, world, rb_context, mode as i64) {
-			eprintln!("Failed to set RigidBody Type: {}", e);
-			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody type: {}", e));
-			return;
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyMode"),
+	c
+)]
+fn get_rigidbody_mode(
+	#[dropbear_macro::define(WorldPtr)]
+	_world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    rigidbody: &RigidBodyContext,
+) -> DropbearNativeResult<i32> {
+    let body_type = shared::get_rigidbody_type(physics, rigidbody)?;
+    Ok(match body_type {
+        RigidBodyType::Dynamic => 0,
+        RigidBodyType::Fixed => 1,
+        RigidBodyType::KinematicPositionBased => 2,
+        RigidBodyType::KinematicVelocityBased => 3,
+    })
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyGravityScale(
-		mut env: JNIEnv,
-		_: JClass,
-		_world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-	) -> jdouble {
-		let physics = convert_ptr!(physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return -1 as jdouble;
-		};
-
-		match super::shared::get_rigidbody_gravity_scale(physics, rb_context) {
-			Ok(v) => v as jdouble,
-			Err(e) => {
-				eprintln!("Failed to get RigidBody component: {}", e);
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody component: {}", e));
-				-1 as jdouble
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "setRigidBodyMode"),
+	c
+)]
+fn set_rigidbody_mode(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    rigidbody: &RigidBodyContext,
+    mode: i32,
+) -> DropbearNativeResult<()> {
+    shared::set_rigidbody_type(physics, world, rigidbody, mode as i64)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyGravityScale(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-		gravity_scale: jdouble,
-	) {
-		let world = convert_ptr!(world_ptr => World);
-		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return;
-		};
-
-		if let Err(e) = super::shared::set_rigidbody_gravity_scale(&mut physics, world, rb_context, gravity_scale as f64) {
-			eprintln!("Failed to set RigidBody gravity scale: {}", e);
-			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody gravity scale: {}", e));
-			return;
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyGravityScale"),
+	c
+)]
+fn get_rigidbody_gravity_scale(
+	#[dropbear_macro::define(WorldPtr)]
+	_world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    rigidbody: &RigidBodyContext,
+) -> DropbearNativeResult<f64> {
+    shared::get_rigidbody_gravity_scale(physics, rigidbody)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyLinearDamping(
-		mut env: JNIEnv,
-		_: JClass,
-		_world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-	) -> jdouble {
-		let physics = convert_ptr!(physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return -1 as jdouble;
-		};
-
-		match super::shared::get_rigidbody_linear_damping(physics, rb_context) {
-			Ok(v) => v as jdouble,
-			Err(e) => {
-				eprintln!("Failed to get RigidBody linear damping: {}", e);
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody linear damping: {}", e));
-				-1 as jdouble
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "setRigidBodyGravityScale"),
+	c
+)]
+fn set_rigidbody_gravity_scale(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    rigidbody: &RigidBodyContext,
+    gravity_scale: f64,
+) -> DropbearNativeResult<()> {
+    shared::set_rigidbody_gravity_scale(physics, world, rigidbody, gravity_scale)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyLinearDamping(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-		linear_damping: jdouble,
-	) {
-		let world = convert_ptr!(world_ptr => World);
-		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return;
-		};
-
-		if let Err(e) = super::shared::set_rigidbody_linear_damping(&mut physics, world, rb_context, linear_damping as f64) {
-			eprintln!("Failed to set RigidBody linear damping: {}", e);
-			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody linear damping: {}", e));
-			return;
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyLinearDamping"),
+	c
+)]
+fn get_rigidbody_linear_damping(
+	#[dropbear_macro::define(WorldPtr)]
+	_world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    rigidbody: &RigidBodyContext,
+) -> DropbearNativeResult<f64> {
+    shared::get_rigidbody_linear_damping(physics, rigidbody)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyAngularDamping(
-		mut env: JNIEnv,
-		_: JClass,
-		_world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-	) -> jdouble {
-		let physics = convert_ptr!(physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return -1 as jdouble;
-		};
-
-		match super::shared::get_rigidbody_angular_damping(physics, rb_context) {
-			Ok(v) => v as jdouble,
-			Err(e) => {
-				eprintln!("Failed to get RigidBody angular damping: {}", e);
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody angular damping: {}", e));
-				-1 as jdouble
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "setRigidBodyLinearDamping"),
+	c
+)]
+fn set_rigidbody_linear_damping(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    rigidbody: &RigidBodyContext,
+    linear_damping: f64,
+) -> DropbearNativeResult<()> {
+    shared::set_rigidbody_linear_damping(physics, world, rigidbody, linear_damping)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyAngularDamping(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-		angular_damping: jdouble,
-	) {
-		let world = convert_ptr!(world_ptr => World);
-		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return;
-		};
-
-		if let Err(e) = super::shared::set_rigidbody_angular_damping(&mut physics, world, rb_context, angular_damping as f64) {
-			eprintln!("Failed to set RigidBody angular damping: {}", e);
-			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody angular damping: {}", e));
-			return;
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyAngularDamping"),
+	c
+)]
+fn get_rigidbody_angular_damping(
+	#[dropbear_macro::define(WorldPtr)]
+	_world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    rigidbody: &RigidBodyContext,
+) -> DropbearNativeResult<f64> {
+    shared::get_rigidbody_angular_damping(physics, rigidbody)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodySleep(
-		mut env: JNIEnv,
-		_: JClass,
-		_world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-	) -> jboolean {
-		let physics = convert_ptr!(physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return 0 as jboolean;
-		};
-
-		match super::shared::get_rigidbody_sleep(physics, rb_context) {
-			Ok(v) => v as jboolean,
-			Err(e) => {
-				eprintln!("Failed to get RigidBody sleep state: {}", e);
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody sleep state: {}", e));
-				0 as jboolean
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "setRigidBodyAngularDamping"),
+	c
+)]
+fn set_rigidbody_angular_damping(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    rigidbody: &RigidBodyContext,
+    angular_damping: f64,
+) -> DropbearNativeResult<()> {
+    shared::set_rigidbody_angular_damping(physics, world, rigidbody, angular_damping)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodySleep(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-		sleep: jboolean,
-	) {
-		let world = convert_ptr!(world_ptr => World);
-		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return;
-		};
-
-		if let Err(e) = super::shared::set_rigidbody_sleep(&mut physics, world, rb_context, sleep != 0) {
-			eprintln!("Failed to set RigidBody sleep state: {}", e);
-			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody sleep state: {}", e));
-			return;
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodySleep"),
+	c
+)]
+fn get_rigidbody_sleep(
+	#[dropbear_macro::define(WorldPtr)]
+	_world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    rigidbody: &RigidBodyContext,
+) -> DropbearNativeResult<bool> {
+    shared::get_rigidbody_sleep(physics, rigidbody)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyCcdEnabled(
-		mut env: JNIEnv,
-		_: JClass,
-		_world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-	) -> jboolean {
-		let physics = convert_ptr!(physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return 0 as jboolean;
-		};
-
-		match super::shared::get_rigidbody_ccd(physics, rb_context) {
-			Ok(v) => v as jboolean,
-			Err(e) => {
-				eprintln!("Failed to get RigidBody CCD state: {}", e);
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody CCD state: {}", e));
-				0 as jboolean
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "setRigidBodySleep"),
+	c
+)]
+fn set_rigidbody_sleep(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    rigidbody: &RigidBodyContext,
+    sleep: bool,
+) -> DropbearNativeResult<()> {
+    shared::set_rigidbody_sleep(physics, world, rigidbody, sleep)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyCcdEnabled(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-		ccd_enabled: jboolean,
-	) {
-		let world = convert_ptr!(world_ptr => World);
-		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return;
-		};
-
-		if let Err(e) = super::shared::set_rigidbody_ccd(&mut physics, world, rb_context, ccd_enabled != 0) {
-			eprintln!("Failed to set RigidBody CCD state: {}", e);
-			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody CCD state: {}", e));
-			return;
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyCcdEnabled"),
+	c
+)]
+fn get_rigidbody_ccd_enabled(
+	#[dropbear_macro::define(WorldPtr)]
+	_world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    rigidbody: &RigidBodyContext,
+) -> DropbearNativeResult<bool> {
+    shared::get_rigidbody_ccd(physics, rigidbody)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyLinearVelocity(
-		mut env: JNIEnv,
-		_: JClass,
-		_world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-	) -> jobject {
-		let physics = convert_ptr!(physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return std::ptr::null_mut();
-		};
-
-		match super::shared::get_rigidbody_linvel(physics, rb_context) {
-			Ok(v) => {
-				match v.to_jobject(&mut env) {
-					Ok(val) => val.into_raw(),
-					Err(e) => {
-						eprintln!("Failed to create Vector3d jobject: {}", e);
-						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create Vector3d jobject: {}", e));
-						std::ptr::null_mut()
-					}
-				}
-			}
-			Err(e) => {
-				eprintln!("Failed to get RigidBody linear velocity: {}", e);
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody linear velocity: {}", e));
-				std::ptr::null_mut()
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "setRigidBodyCcdEnabled"),
+	c
+)]
+fn set_rigidbody_ccd_enabled(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    rigidbody: &RigidBodyContext,
+    ccd_enabled: bool,
+) -> DropbearNativeResult<()> {
+    shared::set_rigidbody_ccd(physics, world, rigidbody, ccd_enabled)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyLinearVelocity(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-		linear_velocity: JObject,
-	) {
-		let world = convert_ptr!(world_ptr => World);
-		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return;
-		};
-
-		let Ok(velocity) = NVector3::from_jobject(&mut env, &linear_velocity) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/math/Vector3d to rust Vector3");
-			return;
-		};
-
-		if let Err(e) = super::shared::set_rigidbody_linvel(&mut physics, world, rb_context, velocity) {
-			eprintln!("Failed to set RigidBody linear velocity: {}", e);
-			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody linear velocity: {}", e));
-			return;
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyLinearVelocity"),
+	c
+)]
+fn get_rigidbody_linear_velocity(
+	#[dropbear_macro::define(WorldPtr)]
+	_world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    rigidbody: &RigidBodyContext,
+) -> DropbearNativeResult<NVector3> {
+    shared::get_rigidbody_linvel(physics, rigidbody)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyAngularVelocity(
-		mut env: JNIEnv,
-		_: JClass,
-		_world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-	) -> jobject {
-		let physics = convert_ptr!(physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return std::ptr::null_mut();
-		};
-
-		match super::shared::get_rigidbody_angvel(physics, rb_context) {
-			Ok(v) => {
-				match v.to_jobject(&mut env) {
-					Ok(val) => val.into_raw(),
-					Err(e) => {
-						eprintln!("Failed to create Vector3d jobject: {}", e);
-						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create Vector3d jobject: {}", e));
-						std::ptr::null_mut()
-					}
-				}
-			}
-			Err(e) => {
-				eprintln!("Failed to get RigidBody angular velocity: {}", e);
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody angular velocity: {}", e));
-				std::ptr::null_mut()
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "setRigidBodyLinearVelocity"),
+	c
+)]
+fn set_rigidbody_linear_velocity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    rigidbody: &RigidBodyContext,
+    linear_velocity: &NVector3,
+) -> DropbearNativeResult<()> {
+    shared::set_rigidbody_linvel(physics, world, rigidbody, *linear_velocity)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyAngularVelocity(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-		angular_velocity: JObject,
-	) {
-		let world = convert_ptr!(world_ptr => World);
-		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return;
-		};
-
-		let Ok(velocity) = NVector3::from_jobject(&mut env, &angular_velocity) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/math/Vector3d to rust Vector3");
-			return;
-		};
-
-		if let Err(e) = super::shared::set_rigidbody_angvel(&mut physics, world, rb_context, velocity) {
-			eprintln!("Failed to set RigidBody angular velocity: {}", e);
-			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody angular velocity: {}", e));
-			return;
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyAngularVelocity"),
+	c
+)]
+fn get_rigidbody_angular_velocity(
+	#[dropbear_macro::define(WorldPtr)]
+	_world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    rigidbody: &RigidBodyContext,
+) -> DropbearNativeResult<NVector3> {
+    shared::get_rigidbody_angvel(physics, rigidbody)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyLockTranslation(
-		mut env: JNIEnv,
-		_: JClass,
-		_world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-	) -> jobject {
-		let physics = convert_ptr!(physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return std::ptr::null_mut();
-		};
-
-		match super::shared::get_rigidbody_lock_translation(physics, rb_context) {
-			Ok(v) => {
-				match v.to_jobject(&mut env) {
-					Ok(val) => val.into_raw(),
-					Err(e) => {
-						eprintln!("Failed to create AxisLock jobject: {}", e);
-						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create AxisLock jobject: {}", e));
-						std::ptr::null_mut()
-					}
-				}
-			}
-			Err(e) => {
-				eprintln!("Failed to get RigidBody translation lock: {}", e);
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody translation lock: {}", e));
-				std::ptr::null_mut()
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "setRigidBodyAngularVelocity"),
+	c
+)]
+fn set_rigidbody_angular_velocity(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    rigidbody: &RigidBodyContext,
+    angular_velocity: &NVector3,
+) -> DropbearNativeResult<()> {
+    shared::set_rigidbody_angvel(physics, world, rigidbody, *angular_velocity)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyLockTranslation(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-		lock: JObject,
-	) {
-		let world = convert_ptr!(world_ptr => World);
-		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return;
-		};
-
-		let Ok(axis_lock) = AxisLock::from_jobject(&mut env, &lock) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/AxisLock to rust AxisLock");
-			return;
-		};
-
-		if let Err(e) = super::shared::set_rigidbody_lock_translation(&mut physics, world, rb_context, axis_lock) {
-			eprintln!("Failed to set RigidBody translation lock: {}", e);
-			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody translation lock: {}", e));
-			return;
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyLockTranslation"),
+	c
+)]
+fn get_rigidbody_lock_translation(
+	#[dropbear_macro::define(WorldPtr)]
+	_world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    rigidbody: &RigidBodyContext,
+) -> DropbearNativeResult<AxisLock> {
+    shared::get_rigidbody_lock_translation(physics, rigidbody)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyLockRotation(
-		mut env: JNIEnv,
-		_: JClass,
-		_world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-	) -> jobject {
-		let physics = convert_ptr!(physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return std::ptr::null_mut();
-		};
-
-		match super::shared::get_rigidbody_lock_rotation(physics, rb_context) {
-			Ok(v) => {
-				match v.to_jobject(&mut env) {
-					Ok(val) => val.into_raw(),
-					Err(e) => {
-						eprintln!("Failed to create AxisLock jobject: {}", e);
-						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create AxisLock jobject: {}", e));
-						std::ptr::null_mut()
-					}
-				}
-			}
-			Err(e) => {
-				eprintln!("Failed to get RigidBody rotation lock: {}", e);
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody rotation lock: {}", e));
-				std::ptr::null_mut()
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "setRigidBodyLockTranslation"),
+	c
+)]
+fn set_rigidbody_lock_translation(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    rigidbody: &RigidBodyContext,
+    lock_translation: &AxisLock,
+) -> DropbearNativeResult<()> {
+    shared::set_rigidbody_lock_translation(physics, world, rigidbody, *lock_translation)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_setRigidBodyLockRotation(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-		lock: JObject,
-	) {
-		let world = convert_ptr!(world_ptr => World);
-		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return;
-		};
-
-		let Ok(axis_lock) = AxisLock::from_jobject(&mut env, &lock) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/AxisLock to rust AxisLock");
-			return;
-		};
-
-		if let Err(e) = super::shared::set_rigidbody_lock_rotation(&mut physics, world, rb_context, axis_lock) {
-			eprintln!("Failed to set RigidBody rotation lock: {}", e);
-			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to set RigidBody rotation lock: {}", e));
-			return;
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyLockRotation"),
+	c
+)]
+fn get_rigidbody_lock_rotation(
+	#[dropbear_macro::define(WorldPtr)]
+	_world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    rigidbody: &RigidBodyContext,
+) -> DropbearNativeResult<AxisLock> {
+    shared::get_rigidbody_lock_rotation(physics, rigidbody)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_getRigidBodyChildren(
-		mut env: JNIEnv,
-		_: JClass,
-		_world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-	) -> jobjectArray {
-		let physics = convert_ptr!(physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return std::ptr::null_mut();
-		};
-
-		match super::shared::get_rigidbody_children(physics, rb_context) {
-			Ok(children) => {
-				let mut handles: Vec<JObject> = Vec::new();
-				for c in children {
-					match IndexNative::from(c.0).to_jobject(&mut env) {
-						Ok(val) => { handles.push(val); }
-						Err(_) => { continue; }
-					}
-				}
-
-				let array = match env.new_object_array(handles.len() as i32, "com/dropbear/physics/Collider", JObject::null()) {
-					Ok(array) => array,
-					Err(e) => {
-						eprintln!("Failed to create jlong array: {}", e);
-						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to create jlong array: {}", e));
-						return std::ptr::null_mut();
-					}
-				};
-
-				for (i, h) in handles.iter().enumerate() {
-					if let Err(e) = env.set_object_array_element(&array, i as jsize, h) {
-						eprintln!("Failed to set jlong array region: {}", e);
-						let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to set jobject array region at index {}: {}", i, e));
-						return std::ptr::null_mut();
-					}
-				}
-
-				array.into_raw()
-			}
-			Err(e) => {
-				eprintln!("Failed to get RigidBody children: {}", e);
-				let _ = env.throw_new("java/lang/RuntimeException", format!("Failed to get RigidBody children: {}", e));
-				std::ptr::null_mut()
-			}
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "setRigidBodyLockRotation"),
+	c
+)]
+fn set_rigidbody_lock_rotation(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    rigidbody: &RigidBodyContext,
+    lock_rotation: &AxisLock,
+) -> DropbearNativeResult<()> {
+    shared::set_rigidbody_lock_rotation(physics, world, rigidbody, *lock_rotation)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_applyImpulse(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-		x: jdouble,
-		y: jdouble,
-		z: jdouble,
-	) {
-		let world = convert_ptr!(world_ptr => World);
-		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return;
-		};
-
-		let impulse = NVector3::new(x as f64, y as f64, z as f64);
-		if let Err(e) = super::shared::apply_impulse(&mut physics, world, rb_context, impulse) {
-			eprintln!("Failed to apply impulse: {}", e);
-			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to apply impulse: {}", e));
-			return;
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "getRigidBodyChildren"),
+	c
+)]
+fn get_rigidbody_children(
+	#[dropbear_macro::define(WorldPtr)]
+	_world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &PhysicsState,
+    rigidbody: &RigidBodyContext,
+) -> DropbearNativeResult<Vec<NCollider>> {
+    let children = shared::get_rigidbody_children(physics, rigidbody)?;
+    let colliders = children
+        .into_iter()
+        .map(|handle| {
+            let (idx, generation) = handle.into_raw_parts();
+            NCollider {
+                index: IndexNative { index: idx, generation },
+                entity_id: rigidbody.entity_id,
+                id: idx,
+            }
+        })
+        .collect();
+
+    Ok(colliders)
+}
 
-	#[unsafe(no_mangle)]
-	pub extern "system" fn Java_com_dropbear_physics_RigidBodyNative_applyTorqueImpulse(
-		mut env: JNIEnv,
-		_: JClass,
-		world_ptr: jlong,
-		physics_ptr: jlong,
-		rigidbody: JObject,
-		x: jdouble,
-		y: jdouble,
-		z: jdouble,
-	) {
-		let world = convert_ptr!(world_ptr => World);
-		let mut physics = convert_ptr!(mut physics_ptr => PhysicsState);
-		let Ok(rb_context) = RigidBodyContext::from_jobject(&mut env, &rigidbody) else {
-			let _ = env.throw_new("java/lang/RuntimeException",
-								  "Unable to convert com/dropbear/physics/RigidBody to rust eucalyptus_core::physics::RigidBodyContext");
-			return;
-		};
-
-		let torque = NVector3::new(x as f64, y as f64, z as f64);
-		if let Err(e) = super::shared::apply_torque_impulse(&mut physics, world, rb_context, torque) {
-			eprintln!("Failed to apply torque impulse: {}", e);
-			let _ = env.throw_new("java/lang/IllegalArgumentException", format!("Failed to apply torque impulse: {}", e));
-			return;
-		}
-	}
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "applyImpulse"),
+	c
+)]
+fn apply_impulse(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    rigidbody: &RigidBodyContext,
+    x: f64,
+    y: f64,
+    z: f64,
+) -> DropbearNativeResult<()> {
+    let impulse = NVector3::new(x, y, z);
+    shared::apply_impulse(physics, world, rigidbody, impulse)
 }
 
-#[dropbear_macro::impl_c_api]
-pub mod native {
-	
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.physics.RigidBodyNative", func = "applyTorqueImpulse"),
+	c
+)]
+fn apply_torque_impulse(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &hecs::World,
+    #[dropbear_macro::define(PhysicsStatePtr)]
+    physics: &mut PhysicsState,
+    rigidbody: &RigidBodyContext,
+    x: f64,
+    y: f64,
+    z: f64,
+) -> DropbearNativeResult<()> {
+    let torque = NVector3::new(x, y, z);
+    shared::apply_torque_impulse(physics, world, rigidbody, torque)
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/properties.rs b/crates/eucalyptus-core/src/properties.rs
index 12c75f1..a850c24 100644
--- a/crates/eucalyptus-core/src/properties.rs
+++ b/crates/eucalyptus-core/src/properties.rs
@@ -1,17 +1,15 @@
 use std::fmt;
 use std::fmt::{Display, Formatter};
-use ::jni::JNIEnv;
-use ::jni::objects::{JClass, JValue};
-use ::jni::sys::{jint, jobject};
 use serde::{Deserialize, Serialize};
 use dropbear_macro::SerializableComponent;
 use dropbear_traits::SerializableComponent;
 use egui::Ui;
-use hecs::World;
-use crate::physics::PhysicsState;
-use crate::ptr::{PhysicsStatePtr, WorldPtr};
+use hecs::{Entity, World};
+use crate::ptr::WorldPtr;
+use crate::scripting::native::DropbearNativeError;
 use crate::scripting::result::DropbearNativeResult;
 use crate::states::Property;
+use crate::types::NVector3;
 
 /// Properties for an entity, typically queries with `entity.getProperty<Float>` and `entity.setProperty(21)`
 #[derive(Debug, Serialize, Deserialize, PartialEq, Clone, SerializableComponent)]
@@ -159,671 +157,303 @@ impl Display for Value {
 }
 
 pub mod shared {
-    use std::ffi::CStr;
-    use std::os::raw::c_char;
     use hecs::World;
     use crate::properties::CustomProperties;
-    use crate::scripting::native::DropbearNativeError;
-    use crate::scripting::result::DropbearNativeResult;
 
     pub fn custom_properties_exists_for_entity(world: &World, entity: hecs::Entity) -> bool {
         world.get::<&CustomProperties>(entity).is_ok()
     }
-
-    pub(crate) unsafe fn read_key(ptr: *const c_char) -> DropbearNativeResult<String> {
-        if ptr.is_null() {
-            return DropbearNativeResult::Err(DropbearNativeError::NullPointer);
-        }
-        match unsafe { CStr::from_ptr(ptr) }.to_str() {
-            Ok(s) => DropbearNativeResult::Ok(s.to_string()),
-            Err(_) => DropbearNativeResult::Err(DropbearNativeError::InvalidUTF8),
-        }
-    }
 }
 
-// input:
-// #[dropbear_macro::export(
-//     kotlin(
-//         class = "com.dropbear.components.CustomPropertiesNative",
-//         func = "getIntProperty",
-//     ),
-//     c
-// )]
-fn get_int_property(
-    #[dropbear_macro::define(crate::ptr::WorldPtr)]
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "customPropertiesExistsForEntity"),
+    c
+)]
+fn custom_properties_exists_for_entity(
+    #[dropbear_macro::define(WorldPtr)]
     world: &World,
-    // #[dropbear_macro::define(crate::ptr::PhysicsStatePtr)]
-    // physics: &PhysicsState,
-    #[dropbear_macro::entity] // this proc macro defines this argument to be an entity.
-    entity0: hecs::Entity,
-    // multiple entities can also be defined
-    // #[dropbear_macro::entity]
-    // entity1: hecs::Entity,
-    // key: &str, // this is not valid: &str is not supported, only rust String.
-    key: String, // allowed
-) -> DropbearNativeResult<Option<i32>> { // a nullable type is defined by an Option<T>, primitive and non-primitive included
-    if let Ok(props) = world.get::<&CustomProperties>(entity0) {
-        if let Some(Value::Int(v)) = props.get_property(key.as_str()) {
-            Ok(Some(*v as i32))
-        } else { Ok(None) }
-    } else { Ok(None) }
+    #[dropbear_macro::entity]
+    entity: Entity,
+) -> DropbearNativeResult<bool> {
+    Ok(shared::custom_properties_exists_for_entity(world, entity))
 }
 
-pub mod jni {
-    #![allow(non_snake_case)]
-    use hecs::World;
-    use jni::JNIEnv;
-    use jni::objects::{JClass, JObject, JString, JValue};
-    use jni::sys::{jboolean, jdouble, jfloat, jint, jlong, jobject, jstring};
-
-    use crate::properties::{CustomProperties, Value};
-    use crate::scripting::jni::utils::{FromJObject, ToJObject};
-    use crate::types::NVector3;
-
-    /// Returns a primitive that is boxed (long => java.lang.Long)
-    ///
-    /// ```
-    /// return_boxed!(&mut env, Some(JValue::Int(21 as jint)), "(I)Ljava/lang/Integer;", "java/lang/Integer")
-    /// ```
-    #[macro_export]
-    macro_rules! return_boxed {
-        ($env:expr, $val:expr, $sig:expr, $wrapper:expr) => {
-            match $val {
-                Some(v) => {
-                    let result = |env: &mut jni::JNIEnv| -> jni::errors::Result<jni::sys::jobject> {
-                        let cls = env.find_class($wrapper)?;
-
-                        let param: jni::objects::JValue = v.into();
-                        let ret = env.call_static_method(cls, "valueOf", $sig, &[param])?;
-
-                        Ok(ret.l()?.into_raw())
-                    }($env);
-
-                    match result {
-                        Ok(ptr) => ptr,
-                        Err(e) => {
-                            eprintln!("return_boxed failed for {}: {:?}", $wrapper, e);
-
-                            let _ = $env.throw_new("java/lang/RuntimeException", format!("Boxing failed: {:?}", e));
-
-                            std::ptr::null_mut()
-                        }
-                    }
-                }
-                None => std::ptr::null_mut(),
-            }
-        };
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_customPropertiesExistsForEntity(
-        _env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-    ) -> jboolean {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-
-        if super::shared::custom_properties_exists_for_entity(&world, entity) {
-            1
-        } else {
-            0
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_getStringProperty(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-    ) -> jstring {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::String(s)) = props.get_property(&key_str) {
-                return env.new_string(s).map(|s| s.into_raw()).unwrap_or(std::ptr::null_mut());
-            }
-        }
-        std::ptr::null_mut()
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_getIntProperty(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-    ) -> jobject {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        let val = if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::Int(v)) = props.get_property(&key_str) {
-                Some(JValue::Int(*v as jint))
-            } else { None }
-        } else { None };
-
-        return_boxed!(&mut env, val, "(I)Ljava/lang/Integer;", "java/lang/Integer")
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_getLongProperty(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-    ) -> jobject {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        let val = if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::Int(v)) = props.get_property(&key_str) {
-                Some(JValue::Long(*v))
-            } else { None }
-        } else { None };
-
-        return_boxed!(&mut env, val, "(J)Ljava/lang/Long;", "java/lang/Long")
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_getDoubleProperty(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-    ) -> jobject {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        let val = if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::Double(v)) = props.get_property(&key_str) {
-                Some(JValue::Double(*v))
-            } else { None }
-        } else { None };
-
-        return_boxed!(&mut env, val, "(D)Ljava/lang/Double;", "java/lang/Double")
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_getFloatProperty(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-    ) -> jobject {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        let val = if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::Double(v)) = props.get_property(&key_str) {
-                Some(JValue::Float(*v as jfloat))
-            } else { None }
-        } else { None };
-
-        return_boxed!(&mut env, val, "(F)Ljava/lang/Float;", "java/lang/Float")
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_getBoolProperty(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-    ) -> jobject {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        let val = if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::Bool(v)) = props.get_property(&key_str) {
-                Some(JValue::Bool(if *v { 1 } else { 0 }))
-            } else { None }
-        } else { None };
-
-        return_boxed!(&mut env, val, "(Z)Ljava/lang/Boolean;", "java/lang/Boolean")
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_getVec3Property(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-    ) -> jobject {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::Vec3(v)) = props.get_property(&key_str) {
-                return match NVector3::from(*v).to_jobject(&mut env) {
-                    Ok(obj) => obj.into_raw(),
-                    Err(_) => std::ptr::null_mut()
-                };
-            }
-        }
-        std::ptr::null_mut()
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_setStringProperty(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-        value: JString,
-    ) {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-        let val_str = crate::convert_jstring!(env, value);
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::String(val_str));
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_setIntProperty(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-        value: jint,
-    ) {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::Int(value as i64));
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_setLongProperty(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-        value: jlong,
-    ) {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::Int(value));
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_setFloatProperty(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-        value: jfloat,
-    ) {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::Double(value as f64));
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_setDoubleProperty(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-        value: jdouble,
-    ) {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::Double(value));
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_setBoolProperty(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-        value: jboolean,
-    ) {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::Bool(value != 0));
-        }
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_components_CustomPropertiesNative_setVec3Property(
-        mut env: JNIEnv,
-        _class: JClass,
-        world_handle: jlong,
-        entity_id: jlong,
-        key: JString,
-        value: JObject,
-    ) {
-        let world = crate::convert_ptr!(world_handle => World);
-        let entity = crate::convert_jlong_to_entity!(entity_id);
-        let key_str = crate::convert_jstring!(env, key);
-
-        let vec_val = match NVector3::from_jobject(&mut env, &value) {
-            Ok(v) => v,
-            Err(_) => return,
-        };
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::Vec3(vec_val.to_array()));
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getStringProperty"),
+    c
+)]
+fn get_string_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+) -> DropbearNativeResult<Option<String>> {
+    let props = world
+        .get::<&CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+
+    Ok(props.get_property(&key).and_then(|value| match value {
+        Value::String(s) => Some(s.clone()),
+        _ => None,
+    }))
 }
 
-#[dropbear_macro::impl_c_api]
-pub mod native {
-    use hecs::{Entity, World};
-    use std::ffi::CString;
-    use std::os::raw::c_char;
-
-    use crate::convert_ptr;
-    use crate::properties::shared::read_key;
-    use crate::properties::{CustomProperties, Value};
-    use crate::ptr::WorldPtr;
-    use crate::scripting::native::DropbearNativeError;
-    use crate::scripting::result::DropbearNativeResult;
-    use crate::types::NVector3;
-    
-    pub fn dropbear_custom_properties_exists_for_entity(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-    ) -> DropbearNativeResult<bool> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-
-        DropbearNativeResult::Ok(super::shared::custom_properties_exists_for_entity(world, entity))
-    }
-
-
-    pub fn dropbear_get_string_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char
-    ) -> DropbearNativeResult<*mut c_char> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::String(s)) = props.get_property(&key_str) {
-                return match CString::new(s.clone()) {
-                    Ok(c) => DropbearNativeResult::Ok(c.into_raw()),
-                    Err(_) => DropbearNativeResult::Err(DropbearNativeError::CStringError),
-                };
-            }
-        }
-        DropbearNativeResult::Err(DropbearNativeError::InvalidArgument)
-    }
-
-    pub fn dropbear_get_int_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char
-    ) -> DropbearNativeResult<i32> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::Int(v)) = props.get_property(&key_str) {
-                return DropbearNativeResult::Ok(*v as i32);
-            }
-        }
-        DropbearNativeResult::Err(DropbearNativeError::InvalidArgument)
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getIntProperty"),
+    c
+)]
+fn get_int_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+) -> DropbearNativeResult<Option<i32>> {
+    let props = world
+        .get::<&CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+
+    Ok(props.get_property(&key).and_then(|value| match value {
+        Value::Int(v) => Some(*v as i32),
+        _ => None,
+    }))
+}
 
-    pub fn dropbear_get_long_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char
-    ) -> DropbearNativeResult<i64> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::Int(v)) = props.get_property(&key_str) {
-                return DropbearNativeResult::Ok(*v);
-            }
-        }
-        DropbearNativeResult::Err(DropbearNativeError::InvalidArgument)
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getLongProperty"),
+    c
+)]
+fn get_long_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+) -> DropbearNativeResult<Option<i64>> {
+    let props = world
+        .get::<&CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+
+    Ok(props.get_property(&key).and_then(|value| match value {
+        Value::Int(v) => Some(*v),
+        _ => None,
+    }))
+}
 
-    pub fn dropbear_get_double_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char
-    ) -> DropbearNativeResult<f64> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::Double(v)) = props.get_property(&key_str) {
-                return DropbearNativeResult::Ok(*v);
-            }
-        }
-        DropbearNativeResult::Err(DropbearNativeError::InvalidArgument)
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getDoubleProperty"),
+    c
+)]
+fn get_double_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+) -> DropbearNativeResult<Option<f64>> {
+    let props = world
+        .get::<&CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+
+    Ok(props.get_property(&key).and_then(|value| match value {
+        Value::Double(v) => Some(*v),
+        _ => None,
+    }))
+}
 
-    pub fn dropbear_get_float_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char
-    ) -> DropbearNativeResult<f32> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::Double(v)) = props.get_property(&key_str) {
-                return DropbearNativeResult::Ok(*v as f32);
-            }
-        }
-        DropbearNativeResult::Err(DropbearNativeError::InvalidArgument)
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getFloatProperty"),
+    c
+)]
+fn get_float_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+) -> DropbearNativeResult<Option<f32>> {
+    let props = world
+        .get::<&CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+
+    Ok(props.get_property(&key).and_then(|value| match value {
+        Value::Double(v) => Some(*v as f32),
+        _ => None,
+    }))
+}
 
-    pub fn dropbear_get_bool_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char
-    ) -> DropbearNativeResult<bool> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::Bool(v)) = props.get_property(&key_str) {
-                return DropbearNativeResult::Ok(*v);
-            }
-        }
-        DropbearNativeResult::Err(DropbearNativeError::InvalidArgument)
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getBoolProperty"),
+    c
+)]
+fn get_bool_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+) -> DropbearNativeResult<Option<bool>> {
+    let props = world
+        .get::<&CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+
+    Ok(props.get_property(&key).and_then(|value| match value {
+        Value::Bool(v) => Some(*v),
+        _ => None,
+    }))
+}
 
-    pub fn dropbear_get_vec3_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char
-    ) -> DropbearNativeResult<NVector3> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(props) = world.get::<&CustomProperties>(entity) {
-            if let Some(Value::Vec3(v)) = props.get_property(&key_str) {
-                return DropbearNativeResult::Ok(NVector3::from(*v));
-            }
-        }
-        DropbearNativeResult::Err(DropbearNativeError::InvalidArgument)
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "getVec3Property"),
+    c
+)]
+fn get_vec3_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+) -> DropbearNativeResult<Option<NVector3>> {
+    let props = world
+        .get::<&CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+
+    Ok(props.get_property(&key).and_then(|value| match value {
+        Value::Vec3(v) => Some(NVector3::from(*v)),
+        _ => None,
+    }))
+}
 
-    pub fn dropbear_set_string_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char,
-        value: *const c_char
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-        let val_str = unsafe { read_key(value)? };
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::String(val_str));
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setStringProperty"),
+    c
+)]
+fn set_string_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+    value: String,
+) -> DropbearNativeResult<()> {
+    let mut props = world
+        .get::<&mut CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+    props.set_property(key, Value::String(value));
+    Ok(())
+}
 
-    pub fn dropbear_set_int_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char,
-        value: i32
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::Int(value as i64));
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setIntProperty"),
+    c
+)]
+fn set_int_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+    value: i32,
+) -> DropbearNativeResult<()> {
+    let mut props = world
+        .get::<&mut CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+    props.set_property(key, Value::Int(value as i64));
+    Ok(())
+}
 
-    pub fn dropbear_set_long_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char,
-        value: i64
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::Int(value));
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setLongProperty"),
+    c
+)]
+fn set_long_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+    value: i64,
+) -> DropbearNativeResult<()> {
+    let mut props = world
+        .get::<&mut CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+    props.set_property(key, Value::Int(value));
+    Ok(())
+}
 
-    pub fn dropbear_set_double_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char,
-        value: f64
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::Double(value));
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setDoubleProperty"),
+    c
+)]
+fn set_double_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+    value: f64,
+) -> DropbearNativeResult<()> {
+    let mut props = world
+        .get::<&mut CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+    props.set_property(key, Value::Double(value));
+    Ok(())
+}
 
-    pub fn dropbear_set_float_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char,
-        value: f32
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::Double(value as f64));
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setFloatProperty"),
+    c
+)]
+fn set_float_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+    value: f64,
+) -> DropbearNativeResult<()> {
+    let mut props = world
+        .get::<&mut CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+    props.set_property(key, Value::Double(value));
+    Ok(())
+}
 
-    pub fn dropbear_set_bool_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char,
-        value: bool
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::Bool(value));
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setBoolProperty"),
+    c
+)]
+fn set_bool_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+    value: bool,
+) -> DropbearNativeResult<()> {
+    let mut props = world
+        .get::<&mut CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+    props.set_property(key, Value::Bool(value));
+    Ok(())
+}
 
-    pub fn dropbear_set_vec3_property(
-        world_ptr: WorldPtr,
-        entity_id: u64,
-        key: *const c_char,
-        value: NVector3
-    ) -> DropbearNativeResult<()> {
-        let world = convert_ptr!(world_ptr => World);
-        let entity = Entity::from_bits(entity_id).ok_or(DropbearNativeError::InvalidEntity)?;
-        let key_str = unsafe { read_key(key)? };
-
-        if let Ok(mut props) = world.get::<&mut CustomProperties>(entity) {
-            props.set_property(key_str, Value::Vec3(value.to_array()));
-            DropbearNativeResult::Ok(())
-        } else {
-            DropbearNativeResult::Err(DropbearNativeError::NoSuchComponent)
-        }
-    }
+#[dropbear_macro::export(
+    kotlin(class = "com.dropbear.components.CustomPropertiesNative", func = "setVec3Property"),
+    c
+)]
+fn set_vec3_property(
+    #[dropbear_macro::define(WorldPtr)]
+    world: &mut World,
+    #[dropbear_macro::entity]
+    entity: Entity,
+    key: String,
+    value: &NVector3,
+) -> DropbearNativeResult<()> {
+    let mut props = world
+        .get::<&mut CustomProperties>(entity)
+        .map_err(|_| DropbearNativeError::NoSuchComponent)?;
+    props.set_property(key, Value::Vec3(value.to_array()));
+    Ok(())
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/ptr.rs b/crates/eucalyptus-core/src/ptr.rs
index 9c79279..1bf3a60 100644
--- a/crates/eucalyptus-core/src/ptr.rs
+++ b/crates/eucalyptus-core/src/ptr.rs
@@ -5,11 +5,11 @@ use crate::input::InputState;
 use crate::command::CommandBuffer;
 use crossbeam_channel::Sender;
 use dropbear_engine::asset::AssetRegistry;
+use dropbear_engine::graphics::SharedGraphicsContext;
 use hecs::World;
 use parking_lot::{Mutex, RwLock};
 use crate::physics::PhysicsState;
 use crate::scene::loading::SceneLoader;
-use crate::ui::{UiContext};
 
 /// A mutable pointer to a [`World`].
 ///
@@ -26,6 +26,12 @@ pub type InputStatePtr = *mut InputState;
 ///
 /// Defined in `dropbear_common.h` as `CommandBuffer`
 pub type CommandBufferPtr = *const Sender<CommandBuffer>;
+pub type CommandBufferUnwrapped = Sender<CommandBuffer>;
+
+/// A non-mutable pointer to the shared graphics context.
+///
+/// Defined in `dropbear_common.h` as `GraphicsContext`.
+pub type GraphicsContextPtr = *const SharedGraphicsContext;
 
 /// A non-mutable pointer to the [`AssetRegistry`].
 ///
@@ -46,10 +52,4 @@ pub type SceneLoaderUnwrapped = Mutex<SceneLoader>;
 /// A mutable pointer to a [`PhysicsState`].
 ///
 /// Defined in `dropbear_common.h` as `PhysicsEngine`
-pub type PhysicsStatePtr = *mut PhysicsState;
-
-/// A mutable pointer to a [`UiContext`], used for queueing UI components
-/// in the scripting module. 
-/// 
-/// Defined in `dropbear_common.h` as `UiBufferPtr`. 
-pub type UiBufferPtr = *const UiContext;
\ No newline at end of file
+pub type PhysicsStatePtr = *mut PhysicsState;
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/result.rs b/crates/eucalyptus-core/src/result.rs
deleted file mode 100644
index dcd2a1b..0000000
--- a/crates/eucalyptus-core/src/result.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-//! Deprecated and dead. I don't know why it even exists :shrug:
-
-use jni::objects::{JClass, JObject, JString};
-use std::ptr;
-
-/// Trait used by the `jni` crate for easier error matching.
-#[allow(dead_code)]
-pub trait ResultToNull {
-    type Output;
-
-    /// If the output is of a type [`jni::Error`](jni::errors::Error),
-    /// it will return a null pointer. Pretty useful for when you don't want to have
-    /// to deal with error matching.
-    ///
-    /// Specifically: converts result to the inner value on [`Ok`], or a null pointer on [`Err`]
-    fn or_null(self) -> Self::Output;
-}
-
-impl ResultToNull for Result<JObject<'_>, jni::errors::Error> {
-    type Output = JObject<'static>;
-
-    fn or_null(self) -> Self::Output {
-        match self {
-            Ok(val) => unsafe { JObject::from_raw(val.into_raw()) },
-            Err(_) => JObject::null(),
-        }
-    }
-}
-
-impl ResultToNull for anyhow::Result<JObject<'_>> {
-    type Output = JObject<'static>;
-
-    fn or_null(self) -> Self::Output {
-        match self {
-            Ok(val) => unsafe { JObject::from_raw(val.into_raw()) },
-            Err(_) => JObject::null(),
-        }
-    }
-}
-
-impl ResultToNull for Result<JClass<'_>, jni::errors::Error> {
-    type Output = JClass<'static>;
-
-    fn or_null(self) -> Self::Output {
-        match self {
-            Ok(val) => unsafe { JClass::from_raw(val.into_raw()) },
-            Err(_) => unsafe { JClass::from_raw(ptr::null_mut()) },
-        }
-    }
-}
-
-impl ResultToNull for anyhow::Result<JClass<'_>> {
-    type Output = JClass<'static>;
-
-    fn or_null(self) -> Self::Output {
-        match self {
-            Ok(val) => unsafe { JClass::from_raw(val.into_raw()) },
-            Err(_) => unsafe { JClass::from_raw(ptr::null_mut()) },
-        }
-    }
-}
-
-impl ResultToNull for Result<JString<'_>, jni::errors::Error> {
-    type Output = JString<'static>;
-
-    fn or_null(self) -> Self::Output {
-        match self {
-            Ok(val) => unsafe { JString::from_raw(val.into_raw()) },
-            Err(_) => unsafe { JString::from_raw(ptr::null_mut()) },
-        }
-    }
-}
-
-impl ResultToNull for anyhow::Result<JString<'_>> {
-    type Output = JString<'static>;
-
-    fn or_null(self) -> Self::Output {
-        match self {
-            Ok(val) => unsafe { JString::from_raw(val.into_raw()) },
-            Err(_) => unsafe { JString::from_raw(ptr::null_mut()) },
-        }
-    }
-}
diff --git a/crates/eucalyptus-core/src/scene.rs b/crates/eucalyptus-core/src/scene.rs
index 11cbe5a..a0b787f 100644
--- a/crates/eucalyptus-core/src/scene.rs
+++ b/crates/eucalyptus-core/src/scene.rs
@@ -13,8 +13,7 @@ use dropbear_engine::entity::{EntityTransform, MeshRenderer, Transform};
 use dropbear_engine::texture::Texture;
 use dropbear_engine::graphics::SharedGraphicsContext;
 use dropbear_engine::lighting::{Light as EngineLight, LightComponent};
-use dropbear_engine::model::{LoadedModel, Material, Model, ModelId};
-use dropbear_engine::model::MODEL_CACHE;
+use dropbear_engine::model::{Material, Model};
 use dropbear_engine::utils::{ResourceReference, ResourceReferenceType};
 use dropbear_traits::SerializableComponent;
 use dropbear_traits::registry::ComponentRegistry;
diff --git a/crates/eucalyptus-core/src/scripting.rs b/crates/eucalyptus-core/src/scripting.rs
index f4e89c9..5c9590f 100644
--- a/crates/eucalyptus-core/src/scripting.rs
+++ b/crates/eucalyptus-core/src/scripting.rs
@@ -12,7 +12,7 @@ pub static JVM_ARGS: OnceLock<String> = OnceLock::new();
 pub static AWAIT_JDB: OnceLock<bool> = OnceLock::new();
 
 use std::sync::OnceLock;
-use crate::ptr::{AssetRegistryPtr, CommandBufferPtr, InputStatePtr, PhysicsStatePtr, SceneLoaderPtr, UiBufferPtr, WorldPtr};
+use crate::ptr::{AssetRegistryPtr, CommandBufferPtr, GraphicsContextPtr, InputStatePtr, PhysicsStatePtr, SceneLoaderPtr, WorldPtr};
 use crate::scripting::jni::JavaContext;
 use crate::scripting::native::NativeLibrary;
 use crate::states::{Script};
@@ -24,12 +24,9 @@ use std::collections::{HashMap, HashSet};
 use std::path::{Path, PathBuf};
 use tokio::io::{AsyncBufReadExt, BufReader};
 use tokio::process::Command;
-use dropbear_engine::asset::PointerKind::Const;
-use dropbear_engine::model::MODEL_CACHE;
 use magna_carta::Target;
 use crate::scene::loading::SCENE_LOADER;
 use crate::types::{CollisionEvent, ContactForceEvent};
-use crate::ui::UI_CONTEXT;
 
 /// The target of the script. This can be either a JVM or a native library.
 #[derive(Default, Clone, Debug)]
@@ -222,31 +219,26 @@ impl ScriptManager {
         world: WorldPtr,
         input: InputStatePtr,
         graphics: CommandBufferPtr,
+        graphics_context: GraphicsContextPtr,
         physics_state: PhysicsStatePtr,
     ) -> anyhow::Result<()> {
         let assets = &raw const *ASSET_REGISTRY;
         let scene_loader = &raw const *SCENE_LOADER;
-        
-        let model_cache_ptr = &raw const *MODEL_CACHE;
-        ASSET_REGISTRY.add_pointer(Const("model_cache"), model_cache_ptr as usize);
-        
-        let ui_buf = UI_CONTEXT.with(|v| {
-            v.as_ptr()
-        });
 
         let context = DropbearContext {
             world,
             input,
-            graphics,
+            command_buffer: graphics,
+            graphics_context,
             assets,
             scene_loader,
             physics_state,
-            ui_buf,
         };
 
         if world.is_null() { log::error!("World pointer is null"); }
         if input.is_null() { log::error!("InputState pointer is null"); }
         if graphics.is_null() { log::error!("CommandBuffer pointer is null"); }
+        if graphics_context.is_null() { log::error!("SharedGraphicsContext pointer is null"); }
         if assets.is_null() { log::error!("AssetRegistry pointer is null"); }
         if scene_loader.is_null() { log::error!("SceneLoader pointer is null"); }
         if physics_state.is_null() { log::error!("PhysicsState pointer is null"); }
@@ -971,9 +963,9 @@ pub async fn build_native(
 pub struct DropbearContext {
     pub world: WorldPtr,
     pub input: InputStatePtr,
-    pub graphics: CommandBufferPtr,
+    pub command_buffer: CommandBufferPtr,
+    pub graphics_context: GraphicsContextPtr,
     pub assets: AssetRegistryPtr,
     pub scene_loader: SceneLoaderPtr,
     pub physics_state: PhysicsStatePtr,
-    pub ui_buf: UiBufferPtr,
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/scripting/jni.rs b/crates/eucalyptus-core/src/scripting/jni.rs
index 81cd304..319aea5 100644
--- a/crates/eucalyptus-core/src/scripting/jni.rs
+++ b/crates/eucalyptus-core/src/scripting/jni.rs
@@ -2,6 +2,7 @@
 //! Deals with the Java Native Interface (JNI) with the help of the [`jni`] crate
 
 pub mod utils;
+pub mod primitives;
 
 use crate::APP_INFO;
 use crate::logging::LOG_LEVEL;
@@ -281,20 +282,20 @@ impl JavaContext {
         let result = (|| -> anyhow::Result<()> {
             let world_handle = context.world as jlong;
             let input_handle = context.input as jlong;
-            let graphics_handle = context.graphics as jlong;
+            let graphics_handle = context.command_buffer as jlong;
+            let graphics_context_handle = context.graphics_context as jlong;
             let asset_handle = context.assets as jlong;
             let scene_loader_handle = context.scene_loader as jlong;
             let physics_handle = context.physics_state as jlong;
-            let ui_handle = context.ui_buf as jlong;
 
             let args = [
                 JValue::Long(world_handle),
                 JValue::Long(input_handle),
                 JValue::Long(graphics_handle),
+                JValue::Long(graphics_context_handle),
                 JValue::Long(asset_handle),
                 JValue::Long(scene_loader_handle),
                 JValue::Long(physics_handle),
-                JValue::Long(ui_handle),
             ];
 
             let mut sig = String::from("(");
diff --git a/crates/eucalyptus-core/src/scripting/jni/primitives.rs b/crates/eucalyptus-core/src/scripting/jni/primitives.rs
new file mode 100644
index 0000000..45b327b
--- /dev/null
+++ b/crates/eucalyptus-core/src/scripting/jni/primitives.rs
@@ -0,0 +1,138 @@
+use jni::JNIEnv;
+use jni::objects::{JObject, JValue};
+use jni::sys::{jdouble, jint, jlong};
+use crate::scripting::jni::utils::ToJObject;
+use crate::scripting::native::DropbearNativeError;
+use crate::scripting::result::DropbearNativeResult;
+
+impl ToJObject for Option<i32> {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        match self {
+            Some(value) => {
+                let class = env.find_class("java/lang/Integer")
+                    .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+                env.new_object(&class, "(I)V", &[JValue::Int(*value)])
+                    .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)
+            },
+            None => Ok(JObject::null()),
+        }
+    }
+}
+
+impl ToJObject for Vec<i32> {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        self.as_slice().to_jobject(env)
+    }
+}
+
+impl ToJObject for &[i32] {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let array = env
+            .new_int_array(self.len() as i32)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+        let buf: Vec<jint> = self.iter().map(|v| *v as jint).collect();
+        env.set_int_array_region(&array, 0, &buf)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+        Ok(JObject::from(array))
+    }
+}
+
+impl ToJObject for &[Vec<i32>] {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let list = new_array_list(env)?;
+        for value in self.iter() {
+            let boxed = value.as_slice().to_jobject(env)?;
+            array_list_add(env, &list, &boxed)?;
+        }
+        Ok(list)
+    }
+}
+
+impl ToJObject for Option<f32> {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        match self {
+            Some(value) => {
+                let class = env.find_class("java/lang/Float")
+                    .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+                env.new_object(&class, "(F)V", &[JValue::Float(*value)])
+                    .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)
+            }
+            None => Ok(JObject::null()),
+        }
+    }
+}
+
+impl ToJObject for Option<f64> {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        match self {
+            Some(value) => {
+                let class = env.find_class("java/lang/Double")
+                    .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+                env.new_object(&class, "(D)V", &[JValue::Double(*value)])
+                    .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)
+            }
+            None => Ok(JObject::null()),
+        }
+    }
+}
+
+impl ToJObject for Vec<f64> {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        self.as_slice().to_jobject(env)
+    }
+}
+
+impl ToJObject for &[f64] {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let array = env
+            .new_double_array(self.len() as i32)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+        let buf: Vec<jdouble> = self.iter().map(|v| *v as jdouble).collect();
+        env.set_double_array_region(&array, 0, &buf)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)?;
+        Ok(JObject::from(array))
+    }
+}
+
+impl ToJObject for &[Vec<f64>] {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let list = new_array_list(env)?;
+        for value in self.iter() {
+            let array = value.as_slice().to_jobject(env)?;
+            array_list_add(env, &list, &array)?;
+        }
+        Ok(list)
+    }
+}
+
+fn new_array_list<'a>(env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+    let class = env.find_class("java/util/ArrayList")
+        .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+    env.new_object(&class, "()V", &[])
+        .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)
+}
+
+fn array_list_add(env: &mut JNIEnv, list: &JObject, item: &JObject) -> DropbearNativeResult<()> {
+    env.call_method(list, "add", "(Ljava/lang/Object;)Z", &[JValue::Object(item)])
+        .map_err(|_| DropbearNativeError::JNIMethodNotFound)?;
+    Ok(())
+}
+
+impl ToJObject for Vec<u64> {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        self.as_slice().to_jobject(env)
+    }
+}
+
+impl ToJObject for &[u64] {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let array = env
+            .new_long_array(self.len() as i32)?;
+        let buf: Vec<jlong> = self.iter().map(|v| *v as jlong).collect();
+        env.set_long_array_region(&array, 0, &buf)?;
+        Ok(JObject::from(array))
+    }
+}
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/scripting/native.rs b/crates/eucalyptus-core/src/scripting/native.rs
index 46b30ae..6f67305 100644
--- a/crates/eucalyptus-core/src/scripting/native.rs
+++ b/crates/eucalyptus-core/src/scripting/native.rs
@@ -12,13 +12,13 @@ use libloading::{Library, Symbol};
 use std::ffi::CString;
 // use std::fmt::{Display, Formatter}; // Display derived by thiserror
 use std::path::Path;
+use hecs::ComponentError;
 use crate::scripting::DropbearContext;
 use crate::types::{CollisionEvent as CollisionEventFFI, ContactForceEvent as ContactForceEventFFI};
 use thiserror::Error;
 use jni::signature::TypeSignature;
 use jni::errors::JniError;
 
-
 pub struct NativeLibrary {
     #[allow(dead_code)]
     /// The libloading library that is currently loaded
@@ -526,6 +526,12 @@ pub enum DropbearNativeError {
     /// When a physics object is not found
     #[error("Physics object not found")]
     PhysicsObjectNotFound,
+    /// When parsing through the JObject, the enum ordinal provided was invalid.
+    #[error("Invalid enum ordinal")]
+    InvalidEnumOrdinal,
+    /// The entity did not have a requested component
+    #[error("Missing component")]
+    MissingComponent,
     /// The entity provided was invalid.
     #[error("Invalid entity")]
     InvalidEntity,
@@ -598,6 +604,8 @@ impl DropbearNativeError {
             DropbearNativeError::AssetNotFound => -21,
             DropbearNativeError::InvalidHandle => -22,
             DropbearNativeError::PhysicsObjectNotFound => -23,
+            DropbearNativeError::InvalidEnumOrdinal => -24,
+            DropbearNativeError::MissingComponent => -25,
             DropbearNativeError::InvalidEntity => -100,
             DropbearNativeError::InvalidUTF8 => -108,
             DropbearNativeError::UnknownError => -1274,
@@ -642,3 +650,12 @@ impl From<jni::errors::Error> for DropbearNativeError {
         }
     }
 }
+
+impl From<hecs::ComponentError> for DropbearNativeError {
+    fn from(e: hecs::ComponentError) -> Self {
+        match e {
+            ComponentError::NoSuchEntity => DropbearNativeError::NoSuchEntity,
+            ComponentError::MissingComponent(_) => DropbearNativeError::MissingComponent
+        }
+    }
+}
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/states.rs b/crates/eucalyptus-core/src/states.rs
index 83200aa..4adc248 100644
--- a/crates/eucalyptus-core/src/states.rs
+++ b/crates/eucalyptus-core/src/states.rs
@@ -7,10 +7,10 @@ use crate::config::{ProjectConfig, ResourceConfig, SourceConfig};
 use crate::scene::SceneConfig;
 use crate::traits::SerializableComponent;
 use dropbear_engine::camera::Camera;
-use dropbear_engine::entity::{MaterialOverride, MeshRenderer, Transform};
+use dropbear_engine::entity::{MeshRenderer, Transform};
 use dropbear_engine::lighting::LightComponent;
-use dropbear_engine::texture::TextureWrapMode;
-use dropbear_engine::utils::{ResourceReference};
+use dropbear_engine::asset::ASSET_REGISTRY;
+use dropbear_engine::utils::{ResourceReference, ResourceReferenceType, EUCA_SCHEME};
 use dropbear_macro::SerializableComponent;
 use once_cell::sync::Lazy;
 use parking_lot::RwLock;
@@ -370,106 +370,49 @@ impl DerefMut for Label {
 }
 
 /// A [MeshRenderer] that is serialized into a file to be stored as a value for config.
-#[derive(Default, Debug, Clone, Serialize, Deserialize)]
+#[derive(Default, Debug, Clone, Serialize, Deserialize, SerializableComponent)]
 pub struct SerializedMeshRenderer {
     pub handle: ResourceReference,
-    pub material_override: Vec<MaterialOverride>,
-
-    #[serde(default)]
-    pub material_customisation: Vec<SerializedMaterialcustomisation>,
-
-    #[serde(default)]
-    #[serde(alias = "custom_import_scale")]
-    #[serde(alias = "editor_import_scale")]
-    #[serde(alias = "baked_import_scale")]
     pub import_scale: Option<f32>,
-}
-
-#[derive(Default, Debug, Clone, Serialize, Deserialize)]
-pub struct SerializedMaterialcustomisation {
-    #[serde(default)]
-    pub material_index: Option<usize>,
-    pub target_material: String,
-    pub tint: [f32; 4],
-
-    #[serde(default)]
-    pub diffuse_texture: Option<ResourceReference>,
-
-    #[serde(default)]
-    pub wrap_mode: TextureWrapMode,
-
-    #[serde(default = "default_uv_tiling")]
-    pub uv_tiling: [f32; 2],
-}
-
-fn default_uv_tiling() -> [f32; 2] {
-    [1.0, 1.0]
-}
-
-#[typetag::serde]
-impl SerializableComponent for SerializedMeshRenderer {
-    fn as_any(&self) -> &dyn std::any::Any {
-        self
-    }
-
-    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
-        self
-    }
-
-    fn type_name(&self) -> &'static str {
-        "SerializedMeshRenderer"
-    }
-
-    fn clone_boxed(&self) -> Box<dyn SerializableComponent> {
-        Box::new(self.clone())
-    }
-
-    fn display_name(&self) -> String {
-        "MeshRenderer".to_string()
-    }
+    pub texture_override: Option<ResourceReference>,
 }
 
 impl SerializedMeshRenderer {
     /// Creates a new [SerializedMeshRenderer] from an existing [MeshRenderer] by cloning data.
     pub fn from_renderer(renderer: &MeshRenderer) -> Self {
-        fn is_probably_texture_uri(uri: &str) -> bool {
-            let uri = uri.to_ascii_lowercase();
-            uri.ends_with(".png")
-                || uri.ends_with(".jpg")
-                || uri.ends_with(".jpeg")
-                || uri.ends_with(".tga")
-                || uri.ends_with(".bmp")
-        }
+        let handle = renderer.model();
+        let handle_ref = if handle.is_null() {
+            ResourceReference::from_reference(ResourceReferenceType::Unassigned { id: handle.id })
+        } else {
+            let registry = ASSET_REGISTRY.read();
+            registry
+                .get_model(handle)
+                .map(|model| model.path.clone())
+                .unwrap_or_else(|| {
+                    ResourceReference::from_reference(ResourceReferenceType::Unassigned { id: handle.id })
+                })
+        };
 
-        let handle = renderer.handle();
-        let model = renderer.model();
-        let material_customisation = model
-            .materials
-            .iter()
-            .enumerate()
-            .map(|(index, mat)| {
-                let diffuse_texture = mat
-                    .texture_tag
-                    .as_deref()
-                    .filter(|tag| is_probably_texture_uri(tag))
-                    .and_then(|tag| ResourceReference::from_euca_uri(tag).ok());
-
-                SerializedMaterialcustomisation {
-                    material_index: Some(index),
-                    target_material: mat.name.clone(),
-                    tint: mat.tint,
-                    diffuse_texture,
-                    wrap_mode: mat.wrap_mode,
-                    uv_tiling: mat.uv_tiling,
+        let texture_override = renderer.texture_override().map(|handle| {
+            let registry = ASSET_REGISTRY.read();
+            let label = registry.get_label_from_texture_handle(handle);
+            let reference = label.and_then(|value| {
+                if value.starts_with(EUCA_SCHEME) {
+                    Some(ResourceReference::from_reference(ResourceReferenceType::File(value)))
+                } else {
+                    None
                 }
+            });
+
+            reference.unwrap_or_else(|| {
+                ResourceReference::from_reference(ResourceReferenceType::Unassigned { id: handle.id })
             })
-            .collect();
+        });
 
         Self {
-            handle: handle.path.clone(),
-            material_override: renderer.material_overrides.clone(),
-            material_customisation,
+            handle: handle_ref,
             import_scale: Some(renderer.import_scale()),
+            texture_override,
         }
     }
 }
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/types.rs b/crates/eucalyptus-core/src/types.rs
index 6bedf01..0ce4d72 100644
--- a/crates/eucalyptus-core/src/types.rs
+++ b/crates/eucalyptus-core/src/types.rs
@@ -5,6 +5,7 @@ use jni::JNIEnv;
 use jni::objects::{JObject, JValue};
 use jni::sys::jdouble;
 use rapier3d::data::Index;
+use rapier3d::parry::query::{ShapeCastOptions, ShapeCastStatus};
 use rapier3d::prelude::ColliderHandle;
 use dropbear_engine::entity::Transform;
 use crate::physics::PhysicsState;
@@ -93,6 +94,18 @@ impl From<glam::DVec3> for NVector3 {
     }
 }
 
+impl From<&glam::DVec3> for NVector3 {
+    fn from(v: &glam::DVec3) -> Self {
+        Self { x: v.x, y: v.y, z: v.z }
+    }
+}
+
+impl From<&NVector3> for glam::DVec3 {
+    fn from(v: &NVector3) -> Self {
+        Self { x: v.x, y: v.y, z: v.z }
+    }
+}
+
 impl From<[f64; 3]> for NVector3 {
     fn from(value: [f64; 3]) -> Self {
         NVector3 {
@@ -376,12 +389,67 @@ impl From<NVector4> for NQuaternion {
     }
 }
 
+impl ToJObject for NQuaternion {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env.find_class("com/dropbear/math/Quaterniond")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        let args = [
+            JValue::Double(self.x),
+            JValue::Double(self.y),
+            JValue::Double(self.z),
+            JValue::Double(self.w),
+        ];
+
+        env.new_object(&class, "(DDDD)V", &args)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)
+    }
+}
+
+impl FromJObject for NQuaternion {
+    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self>
+    where
+        Self: Sized
+    {
+        let class = env.find_class("com/dropbear/math/Quaterniond")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        if !env.is_instance_of(obj, &class)
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?
+        {
+            return Err(DropbearNativeError::InvalidArgument);
+        }
+
+        let x = env.get_field(obj, "x", "D")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .d()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        let y = env.get_field(obj, "y", "D")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .d()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        let z = env.get_field(obj, "z", "D")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .d()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        let w = env.get_field(obj, "w", "D")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .d()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        Ok(NQuaternion { x, y, z, w })
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Copy)]
 pub struct NTransform {
-    position: NVector3,
-    rotation: NQuaternion,
-    scale: NVector3,
+    pub position: NVector3,
+    pub rotation: NQuaternion,
+    pub scale: NVector3,
 }
 
 impl From<Transform> for NTransform {
@@ -404,6 +472,30 @@ impl From<NTransform> for Transform {
     }
 }
 
+impl ToJObject for NTransform {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let class = env
+            .find_class("com/dropbear/math/Transform")
+            .map_err(|_| DropbearNativeError::JNIClassNotFound)?;
+
+        let args = [
+            JValue::Double(self.position.x),
+            JValue::Double(self.position.y),
+            JValue::Double(self.position.z),
+            JValue::Double(self.rotation.x),
+            JValue::Double(self.rotation.y),
+            JValue::Double(self.rotation.z),
+            JValue::Double(self.rotation.w),
+            JValue::Double(self.scale.x),
+            JValue::Double(self.scale.y),
+            JValue::Double(self.scale.z),
+        ];
+
+        env.new_object(&class, "(DDDDDDDDDD)V", &args)
+            .map_err(|_| DropbearNativeError::JNIFailedToCreateObject)
+    }
+}
+
 #[repr(C)]
 #[derive(Clone, Copy)]
 pub struct NCollider {
@@ -521,25 +613,59 @@ impl From<IndexNative> for Index {
     }
 }
 
-#[repr(C)]
-#[derive(Clone, Copy)]
-pub enum ColliderShapeType {
-    Box = 0,
-    Sphere = 1,
-    Capsule = 2,
-    Cylinder = 3,
-    Cone = 4,
+impl ToJObject for IndexNative {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        let cls = env.find_class("com/dropbear/physics/Index")
+            .map_err(|e| {
+                eprintln!("[JNI Error] Could not find Index class: {:?}", e);
+                DropbearNativeError::GenericError
+            })?;
+
+        let obj = env.new_object(
+            cls,
+            "(II)V",
+            &[
+                JValue::Int(self.index as i32),
+                JValue::Int(self.generation as i32)
+            ]
+        ).map_err(|e| {
+            eprintln!("[JNI Error] Failed to create Index object: {:?}", e);
+            DropbearNativeError::GenericError
+        })?;
+
+        Ok(obj)
+    }
 }
 
-#[repr(C)]
-#[derive(Clone, Copy)]
-pub struct NColliderShape {
-    pub shape_type: ColliderShapeType,
-    pub radius: f32,
-    pub half_height: f32,
-    pub half_extents_x: f32,
-    pub half_extents_y: f32,
-    pub half_extents_z: f32,
+impl ToJObject for Option<IndexNative> {
+    fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
+        match self {
+            Some(value) => value.to_jobject(env),
+            None => Ok(JObject::null()),
+        }
+    }
+}
+
+impl FromJObject for IndexNative {
+    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self>
+    where
+        Self: Sized
+    {
+        let idx_val = env.get_field(obj, "index", "I")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .i()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        let gen_val = env.get_field(obj, "generation", "I")
+            .map_err(|_| DropbearNativeError::JNIFailedToGetField)?
+            .i()
+            .map_err(|_| DropbearNativeError::JNIUnwrapFailed)?;
+
+        Ok(IndexNative {
+            index: idx_val as u32,
+            generation: gen_val as u32,
+        })
+    }
 }
 
 #[repr(C)]
@@ -680,18 +806,50 @@ impl ToJObject for RayHit {
 }
 
 #[repr(C)]
+#[dropbear_macro::repr_c_enum]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum NShapeCastStatus {
+    OutOfIterations,
+    Converged,
+    Failed,
+    PenetratingOrWithinTargetDist,
+}
+
+impl Into<ShapeCastStatus> for NShapeCastStatus {
+    fn into(self) -> ShapeCastStatus {
+        match self {
+            NShapeCastStatus::OutOfIterations => ShapeCastStatus::OutOfIterations,
+            NShapeCastStatus::Converged => ShapeCastStatus::Converged,
+            NShapeCastStatus::Failed => ShapeCastStatus::Failed,
+            NShapeCastStatus::PenetratingOrWithinTargetDist => ShapeCastStatus::PenetratingOrWithinTargetDist,
+        }
+    }
+}
+
+impl Into<NShapeCastStatus> for ShapeCastStatus {
+    fn into(self) -> NShapeCastStatus {
+        match self {
+            ShapeCastStatus::OutOfIterations => NShapeCastStatus::OutOfIterations,
+            ShapeCastStatus::Converged => NShapeCastStatus::Converged,
+            ShapeCastStatus::Failed => NShapeCastStatus::Failed,
+            ShapeCastStatus::PenetratingOrWithinTargetDist => NShapeCastStatus::PenetratingOrWithinTargetDist,
+        }
+    }
+}
+
+#[repr(C)]
 #[derive(Clone, Copy)]
-pub struct ShapeCastHitFFI {
+pub struct NShapeCastHit {
     pub collider: NCollider,
     pub distance: f64,
     pub witness1: NVector3,
     pub witness2: NVector3,
     pub normal1: NVector3,
     pub normal2: NVector3,
-    pub status: rapier3d::parry::query::ShapeCastStatus,
+    pub status: NShapeCastStatus,
 }
 
-impl ToJObject for ShapeCastHitFFI {
+impl ToJObject for NShapeCastHit {
     fn to_jobject<'a>(&self, env: &mut JNIEnv<'a>) -> DropbearNativeResult<JObject<'a>> {
         use jni::sys::jdouble;
 
diff --git a/crates/eucalyptus-core/src/ui.rs b/crates/eucalyptus-core/src/ui.rs
deleted file mode 100644
index 608ee11..0000000
--- a/crates/eucalyptus-core/src/ui.rs
+++ /dev/null
@@ -1,192 +0,0 @@
-// mod button;
-// mod utils;
-// mod text;
-// mod align;
-// mod checkbox;
-
-use std::any::Any;
-use std::cell::RefCell;
-use std::collections::HashMap;
-use std::fmt::Debug;
-use ::jni::JNIEnv;
-use ::jni::objects::JObject;
-use parking_lot::Mutex;
-use serde::{Deserialize, Serialize};
-// use yakui::{Alignment, MainAxisSize, Yakui};
-use dropbear_engine::utils::ResourceReference;
-use dropbear_macro::SerializableComponent;
-use dropbear_traits::SerializableComponent;
-use crate::scripting::jni::utils::{FromJObject};
-use crate::scripting::result::DropbearNativeResult;
-// use crate::ui::align::{AlignParser};
-// use crate::ui::button::ButtonParser;
-// use crate::ui::checkbox::CheckboxParser;
-// use crate::ui::text::TextParser;
-
-thread_local! {
-    pub static UI_CONTEXT: RefCell<UiContext> = RefCell::new(UiContext::new());
-}
-
-/// A component that can be attached to an entity that renders UI for the entire scene.
-///
-/// This UI is used in tandem with a `.kts` (Kotlin Script file) with the dropbear-engine scripting
-/// ui DSL.
-#[derive(Debug, Serialize, Deserialize, Clone, SerializableComponent, Default)]
-pub struct UIComponent {
-    /// Does not render the UI file.
-    pub disabled: bool,
-    /// The reference to the script file.
-    pub ui_file: ResourceReference,
-}
-
-#[derive(Clone, Copy, Debug, Default)]
-pub struct WidgetState {
-    pub clicked: bool,
-    pub hovering: bool,
-    pub checked: bool,
-}
-
-pub trait WidgetParser: Send + Sync {
-    fn parse(&self, env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Option<UiInstructionType>>;
-    fn name(&self) -> String;
-}
-
-pub struct UiContext {
-    // pub yakui_state: Mutex<Yakui>,
-    pub instruction_set: Mutex<Vec<UiInstructionType>>,
-    pub widget_states: Mutex<HashMap<i64, WidgetState>>,
-    pub parsers: Mutex<Vec<Box<dyn WidgetParser>>>,
-}
-
-pub fn poll() {
-    UI_CONTEXT.with(|v| {
-        let ctx = v.borrow();
-        // let mut instructions = ctx.instruction_set.lock();
-        let mut widget_states = ctx.widget_states.lock();
-
-        widget_states.clear();
-
-        // let current_instructions = instructions.drain(..).collect::<Vec<UiInstructionType>>();
-
-        // let tree = build_tree(current_instructions);
-
-        // yakui::widgets::Align::new(Alignment::TOP_LEFT).show(|| {
-        //     yakui::widgets::List::column()
-        //         .main_axis_size(MainAxisSize::Max)
-        //         .show(|| {
-        //             render_tree(tree, &mut widget_states);
-        //         });
-        // });
-    });
-}
-
-#[derive(Debug)]
-pub enum UiInstructionType {
-    Containered(ContaineredWidgetType),
-    Widget(Box<dyn NativeWidget>),
-}
-
-#[derive(Debug)]
-pub enum ContaineredWidgetType {
-    Start {
-        id: i64,
-        widget: Box<dyn ContaineredWidget>,
-    },
-    End {
-        id: i64,
-    }
-}
-
-pub trait NativeWidget: Send + Sync + Debug {
-    fn render(self: Box<Self>, state: &mut HashMap<i64, WidgetState>);
-    fn id(&self) -> i64;
-    fn as_any(&self) -> &dyn Any;
-}
-
-pub trait ContaineredWidget: Send + Sync + Debug {
-    fn render(self: Box<Self>, children: Vec<UiNode>, state: &mut HashMap<i64, WidgetState>);
-    fn as_any(&self) -> &dyn Any;
-}
-
-pub struct UiNode {
-    pub instruction: UiInstructionType,
-    pub children: Vec<UiNode>,
-}
-
-impl UiContext {
-    pub fn new() -> Self {
-        let mut parsers: Vec<Box<dyn WidgetParser>> = Vec::new();
-
-        // parsers.push(Box::new(ButtonParser));
-        // parsers.push(Box::new(TextParser));
-        // parsers.push(Box::new(AlignParser));
-        // parsers.push(Box::new(CheckboxParser));
-
-        // let yakui = Yakui::new();
-
-        Self {
-            // yakui_state: Mutex::new(yakui),
-            instruction_set: Default::default(),
-            widget_states: Default::default(),
-            parsers: Mutex::new(parsers),
-        }
-    }
-}
-
-pub trait UiWidgetType: FromJObject {
-    type UIWidgetType;
-
-    fn as_id(&self) -> u32;
-    fn from_id(id: u32) -> Self::UIWidgetType;
-}
-
-pub mod jni {
-    #![allow(non_snake_case)]
-
-    use jni::sys::{jlong};
-    use jni::objects::{JClass, JObjectArray};
-    use jni::JNIEnv;
-    use crate::convert_ptr;
-    use crate::ui::{UiContext};
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_ui_UINative_renderUI(
-        mut env: JNIEnv,
-        _class: JClass,
-        ui_buf_ptr: jlong,
-        instructions: JObjectArray,
-    ) {
-        // println!("[Java_com_dropbear_ui_UINative_renderUI] received new renderUI request");
-        let ui = convert_ptr!(ui_buf_ptr => UiContext);
-        let mut rust_instructions = Vec::new();
-
-        let count = env.get_array_length(&instructions).unwrap_or(0);
-        let parsers_guard = ui.parsers.lock();
-
-        for i in 0..count {
-            let obj = match env.get_object_array_element(&instructions, i) {
-                Ok(o) => o,
-                Err(_) => continue,
-            };
-            if obj.is_null() { println!("[Java_com_dropbear_ui_UINative_renderUI] obj is null at index {}", i); continue; }
-
-            for parser in parsers_guard.iter() {
-                match parser.parse(&mut env, &obj) {
-                    Ok(Some(widget)) => {
-                        // println!("Received widget: {:?}", widget);
-                        rust_instructions.push(widget);
-                        break;
-                    },
-                    Ok(None) => continue,
-                    Err(e) => {
-                        eprintln!("[Java_com_dropbear_ui_UINative_renderUI] Error converting UI instruction: {:?}", e);
-                    }
-                }
-            }
-        }
-
-        let mut instruction_set = ui.instruction_set.lock();
-        instruction_set.clear();
-        instruction_set.extend(rust_instructions);
-    }
-}
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/ui/align.rs b/crates/eucalyptus-core/src/ui/align.rs
deleted file mode 100644
index 51fc973..0000000
--- a/crates/eucalyptus-core/src/ui/align.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-use std::any::Any;
-use std::collections::HashMap;
-use jni::JNIEnv;
-use jni::objects::JObject;
-use yakui::Alignment;
-use crate::scripting::jni::utils::FromJObject;
-use crate::scripting::result::DropbearNativeResult;
-use crate::ui::{ContaineredWidget, ContaineredWidgetType, UiInstructionType, UiNode, WidgetParser, WidgetState};
-
-pub(crate) struct AlignParser;
-
-#[derive(Debug, Clone)]
-pub(crate) struct AlignWidget {
-    pub alignment: Alignment,
-}
-
-impl WidgetParser for AlignParser {
-    fn parse(&self, env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Option<UiInstructionType>> {
-        let class = env.get_object_class(obj)?;
-        let name_str_obj = env.call_method(class, "getName", "()Ljava/lang/String;", &[])?.l()?;
-        let name_string: String = env.get_string(&name_str_obj.into())?.into();
-
-        if name_string.contains("AlignmentInstruction$StartAlignmentBlock") {
-            let align_obj = env.get_field(obj, "align", "Lcom/dropbear/ui/widgets/Align;")?.l()?;
-            let align = yakui::widgets::Align::from_jobject(env, &align_obj)?;
-
-            let id_obj = env.get_field(obj, "id", "Lcom/dropbear/ui/WidgetId;")?.l()?;
-            let id = env.get_field(id_obj, "id", "J")?.j()?;
-
-            return Ok(Some(UiInstructionType::Containered(
-                ContaineredWidgetType::Start {
-                    id,
-                    widget: Box::new(AlignWidget {
-                        alignment: align.alignment,
-                    }),
-                }
-            )));
-        }
-
-        if name_string.contains("AlignmentInstruction$EndAlignmentBlock") {
-            let id_obj = env.get_field(obj, "id", "Lcom/dropbear/ui/WidgetId;")?.l()?;
-            let id = env.get_field(id_obj, "id", "J")?.j()?;
-
-            return Ok(Some(UiInstructionType::Containered(
-                ContaineredWidgetType::End { id }
-            )));
-        }
-
-        Ok(None)
-    }
-
-    fn name(&self) -> String {
-        String::from("AlignParser")
-    }
-}
-
-impl ContaineredWidget for AlignWidget {
-    fn render(self: Box<Self>, children: Vec<UiNode>, state: &mut HashMap<i64, WidgetState>) {
-        yakui::widgets::Align::new(self.alignment).show(|| {
-            super::render_tree(children, state);
-        });
-    }
-
-    fn as_any(&self) -> &dyn Any {
-        self
-    }
-}
-
-impl FromJObject for yakui::widgets::Align {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self>
-    where
-        Self: Sized
-    {
-        let align_obj = env.get_field(obj, "align", "Lcom/dropbear/ui/styling/Alignment;")?.l()?;
-        let alignment = Alignment::from_jobject(env, &align_obj)?;
-
-        Ok(Self {
-            alignment,
-        })
-    }
-}
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/ui/button.rs b/crates/eucalyptus-core/src/ui/button.rs
deleted file mode 100644
index f4bb622..0000000
--- a/crates/eucalyptus-core/src/ui/button.rs
+++ /dev/null
@@ -1,134 +0,0 @@
-use std::any::Any;
-use std::collections::HashMap;
-use yakui::{Alignment, BorderRadius};
-use yakui::widgets::{Button, Pad, DynamicButtonStyle};
-use crate::scripting::jni::utils::FromJObject;
-use crate::scripting::result::DropbearNativeResult;
-use std::borrow::Cow;
-use ::jni::JNIEnv;
-use ::jni::objects::JObject;
-use crate::ui::{NativeWidget, UiInstructionType, WidgetParser, WidgetState};
-
-pub(crate) struct ButtonParser;
-
-#[derive(Debug)]
-pub(crate) struct ButtonWidget {
-    pub id: i64,
-    pub button: yakui::widgets::Button,
-}
-
-impl WidgetParser for ButtonParser {
-    fn parse(&self, env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Option<UiInstructionType>> {
-        let class = env.get_object_class(obj)?;
-        let name_str_obj = env.call_method(class, "getName", "()Ljava/lang/String;", &[])?.l()?;
-        let name_string: String = env.get_string(&name_str_obj.into())?.into();
-
-        if name_string.contains("ButtonInstruction$Button") {
-            let button_obj = env.get_field(obj, "button", "Lcom/dropbear/ui/widgets/Button;")?.l()?;
-            let button = yakui::widgets::Button::from_jobject(env, &button_obj)?;
-
-            let id_obj = env.get_field(obj, "id", "Lcom/dropbear/ui/WidgetId;")?.l()?;
-            let id = env.get_field(id_obj, "id", "J")?.j()?;
-
-            return Ok(Some(UiInstructionType::Widget(Box::new(ButtonWidget {
-                id,
-                button,
-            }))));
-        }
-
-        Ok(None)
-    }
-
-    fn name(&self) -> String {
-        String::from("ButtonParser")
-    }
-}
-
-impl NativeWidget for ButtonWidget {
-    fn render(self: Box<Self>, states: &mut HashMap<i64, WidgetState>) {
-        let res = self.button.show();
-        states.insert(self.id, WidgetState {
-            clicked: res.clicked,
-            hovering: res.hovering,
-            checked: false, // always be false because it is not a checkbox, obv
-        });
-    }
-
-    fn id(&self) -> i64 {
-        self.id
-    }
-
-    fn as_any(&self) -> &dyn Any {
-        self
-    }
-}
-
-impl FromJObject for Button {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let text_field = env.get_field(obj, "text", "Ljava/lang/String;")?;
-        let text_jstring = text_field.l()?.into();
-        let text: String = env.get_string(&text_jstring)?.into();
-
-        let f = env.get_field(obj, "alignment", "Lcom/dropbear/ui/styling/Alignment;")?.l()?;
-        let alignment = Alignment::from_jobject(env, &f)?;
-
-        let f = env.get_field(obj, "padding", "Lcom/dropbear/ui/styling/Padding;")?.l()?;
-        let padding = Pad::from_jobject(env, &f)?;
-
-        let f = env.get_field(obj, "borderRadius", "Lcom/dropbear/ui/styling/BorderRadius;")?.l()?;
-        let border_radius = BorderRadius::from_jobject(env, &f)?;
-
-        let f = env.get_field(obj, "style", "Lcom/dropbear/ui/styling/DynamicButtonStyle;")?.l()?;
-        let style = DynamicButtonStyle::from_jobject(env, &f)?;
-
-        let f = env.get_field(obj, "hoverStyle", "Lcom/dropbear/ui/styling/DynamicButtonStyle;")?.l()?;
-        let hover_style = DynamicButtonStyle::from_jobject(env, &f)?;
-
-        let f = env.get_field(obj, "downStyle", "Lcom/dropbear/ui/styling/DynamicButtonStyle;")?.l()?;
-        let down_style = DynamicButtonStyle::from_jobject(env, &f)?;
-
-        Ok(Self {
-            text: Cow::Owned(text),
-            alignment,
-            padding,
-            border_radius,
-            style,
-            hover_style,
-            down_style,
-        })
-    }
-}
-
-pub mod jni {
-    #![allow(non_snake_case)]
-    
-    use jni::JNIEnv;
-    use jni::objects::JClass;
-    use jni::sys::{jboolean, jlong};
-    use crate::convert_ptr;
-    use crate::ui::UiContext;
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_ui_widgets_ButtonNative_getClicked(
-        _env: JNIEnv,
-        _class: JClass,
-        ui_buf_ptr: jlong,
-        id: jlong,
-    ) -> jboolean {
-        let ui = convert_ptr!(ui_buf_ptr => UiContext);
-        let states = ui.widget_states.lock();
-        states.get(&id).map(|s| s.clicked).unwrap_or(false).into()
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_ui_widgets_ButtonNative_getHovering(
-        _env: JNIEnv,
-        _class: JClass,
-        ui_buf_ptr: jlong,
-        id: jlong,
-    ) -> jboolean {
-        let ui = convert_ptr!(ui_buf_ptr => UiContext);
-        let states = ui.widget_states.lock();
-        states.get(&id).map(|s| s.hovering).unwrap_or(false).into()
-    }
-}
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/ui/checkbox.rs b/crates/eucalyptus-core/src/ui/checkbox.rs
deleted file mode 100644
index 17ff8ec..0000000
--- a/crates/eucalyptus-core/src/ui/checkbox.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-use std::any::Any;
-use std::collections::HashMap;
-use ::jni::JNIEnv;
-use ::jni::objects::JObject;
-use yakui::widgets::Checkbox;
-use crate::scripting::result::DropbearNativeResult;
-use crate::ui::{NativeWidget, UiInstructionType, WidgetParser, WidgetState};
-
-pub(crate) struct CheckboxParser;
-
-#[derive(Debug)]
-pub(crate) struct CheckboxWidget {
-    pub id: i64,
-    pub checkbox: Checkbox,
-}
-
-impl WidgetParser for CheckboxParser {
-    fn parse(&self, env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Option<UiInstructionType>> {
-        let class = env.get_object_class(obj)?;
-        let name_str_obj = env.call_method(class, "getName", "()Ljava/lang/String;", &[])?.l()?;
-        let name_string: String = env.get_string(&name_str_obj.into())?.into();
-
-        if name_string.contains("CheckboxInstruction$Checkbox") {
-            let checked = env.get_field(obj, "checked", "Z")?.z()?;
-            let checkbox = Checkbox::new(checked);
-
-            let id_obj = env.get_field(obj, "id", "Lcom/dropbear/ui/WidgetId;")?.l()?;
-            let id = env.get_field(id_obj, "id", "J")?.j()?;
-
-            return Ok(Some(UiInstructionType::Widget(Box::new(CheckboxWidget {
-                id,
-                checkbox,
-            }))));
-        }
-
-        Ok(None)
-    }
-
-    fn name(&self) -> String {
-        String::from("CheckboxParser")
-    }
-}
-
-impl NativeWidget for CheckboxWidget {
-    fn render(self: Box<Self>, state: &mut HashMap<i64, WidgetState>) {
-        let resp = self.checkbox.show();
-        state.insert(self.id, WidgetState {
-            clicked: false,
-            hovering: false,
-            checked: resp.checked,
-        });
-
-    }
-
-    fn id(&self) -> i64 {
-        self.id
-    }
-
-    fn as_any(&self) -> &dyn Any {
-        self
-    }
-}
-
-pub mod jni {
-    #![allow(non_snake_case)]
-
-    use jni::JNIEnv;
-    use jni::objects::JClass;
-    use jni::sys::{jboolean, jlong};
-    use crate::convert_ptr;
-    use crate::ui::UiContext;
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_ui_widgets_CheckboxNative_getChecked(
-        _env: JNIEnv,
-        _: JClass,
-        ui_buf_ptr: jlong,
-        id: jlong,
-    ) -> jboolean {
-        let ui = convert_ptr!(ui_buf_ptr => UiContext);
-
-        if let Some(v) = ui.widget_states.lock().get(&(id as i64)) {
-            return v.checked.into();
-        }
-        false.into()
-    }
-
-    #[unsafe(no_mangle)]
-    pub extern "system" fn Java_com_dropbear_ui_widgets_CheckboxNative_hasCheckedState(
-        _env: JNIEnv,
-        _: JClass,
-        ui_buf_ptr: jlong,
-        id: jlong,
-    ) -> jboolean {
-        let ui = convert_ptr!(ui_buf_ptr => UiContext);
-
-        ui.widget_states.lock().contains_key(&(id as i64)).into()
-    }
-}
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/ui/text.rs b/crates/eucalyptus-core/src/ui/text.rs
deleted file mode 100644
index 1c5fce3..0000000
--- a/crates/eucalyptus-core/src/ui/text.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-use std::any::Any;
-use std::borrow::Cow;
-use std::collections::HashMap;
-use jni::JNIEnv;
-use jni::objects::JObject;
-use yakui::style::TextStyle;
-use yakui::widgets::Pad;
-use crate::scripting::jni::utils::FromJObject;
-use crate::scripting::result::DropbearNativeResult;
-use crate::ui::{NativeWidget, UiInstructionType, WidgetParser, WidgetState};
-
-pub(crate) struct TextParser;
-
-#[derive(Debug)]
-pub(crate) struct TextWidget {
-    pub id: i64,
-    pub text: yakui::widgets::Text,
-}
-
-impl WidgetParser for TextParser {
-    fn parse(&self, env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Option<UiInstructionType>> {
-        let class = env.get_object_class(obj)?;
-        let name_str_obj = env.call_method(class, "getName", "()Ljava/lang/String;", &[])?.l()?;
-        let name_string: String = env.get_string(&name_str_obj.into())?.into();
-        // println!("TextParser obj get_string result: {}", name_string);
-
-        if name_string.contains("TextInstruction$Text") {
-            let text_obj = env.get_field(obj, "text", "Lcom/dropbear/ui/widgets/Text;")?.l()?;
-            let text = yakui::widgets::Text::from_jobject(env, &text_obj)?;
-
-            let id_obj = env.get_field(obj, "id", "Lcom/dropbear/ui/WidgetId;")?.l()?;
-            let id = env.get_field(id_obj, "id", "J")?.j()?;
-
-            return Ok(Some(UiInstructionType::Widget(Box::new(TextWidget {
-                id,
-                text,
-            }))))
-        }
-
-        Ok(None)
-    }
-
-    fn name(&self) -> String {
-        String::from("TextParser")
-    }
-}
-
-impl NativeWidget for TextWidget {
-    fn render(self: Box<Self>, _state: &mut HashMap<i64, WidgetState>) {
-        let _ = self.text.show(); // no response
-    }
-
-    fn id(&self) -> i64 {
-        self.id
-    }
-
-    fn as_any(&self) -> &dyn Any {
-        self
-    }
-}
-
-impl FromJObject for yakui::widgets::Text {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self>
-    where
-        Self: Sized
-    {
-        let text_field = env.get_field(obj, "text", "Ljava/lang/String;")?;
-        let text_jstring = text_field.l()?.into();
-        let text: String = env.get_string(&text_jstring)?.into();
-
-        let style_obj = env.get_field(obj, "style", "Lcom/dropbear/ui/styling/TextStyle;")?.l()?;
-        let style = TextStyle::from_jobject(env, &style_obj)?;
-
-        let f = env.get_field(obj, "padding", "Lcom/dropbear/ui/styling/Padding;")?.l()?;
-        let padding = Pad::from_jobject(
-            env,
-            &f
-        )?;
-        
-        Ok(Self {
-            text: Cow::Owned(text),
-            style,
-            padding,
-        })
-    }
-}
\ No newline at end of file
diff --git a/crates/eucalyptus-core/src/ui/utils.rs b/crates/eucalyptus-core/src/ui/utils.rs
deleted file mode 100644
index 7067d9d..0000000
--- a/crates/eucalyptus-core/src/ui/utils.rs
+++ /dev/null
@@ -1,323 +0,0 @@
-use jni::JNIEnv;
-use jni::objects::{JObject, JByteArray};
-use crate::scripting::jni::utils::FromJObject;
-use crate::scripting::result::DropbearNativeResult;
-use yakui::{Border, Color};
-use yakui::widgets::{Pad, DynamicButtonStyle};
-use yakui::{Alignment, BorderRadius};
-use yakui::style::{TextStyle, TextAlignment};
-use yakui::cosmic_text::{Attrs, AttrsOwned, FamilyOwned, Weight, Style, Stretch, CacheKeyFlags, FontFeatures, Feature, FeatureTag, Metrics, CacheMetrics};
-
-impl FromJObject for FeatureTag {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let tag_obj = env.call_method(obj, "asBytes", "()[B", &[])?.l()?;
-        let tag_bytes = env.convert_byte_array(JByteArray::from(tag_obj))?;
-        
-        let mut fixed_tag = [0u8; 4];
-        let len = tag_bytes.len().min(4);
-        fixed_tag[..len].copy_from_slice(&tag_bytes[..len]);
-        
-        Ok(FeatureTag::new(&fixed_tag))
-    }
-}
-
-impl FromJObject for Feature {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let tag_obj = env.get_field(obj, "tag", "Lcom/dropbear/ui/styling/fonts/FeatureTag;")?.l()?;
-        let tag = FeatureTag::from_jobject(env, &tag_obj)?;
-        
-        let value_obj = env.get_field(obj, "value", "Lcom/dropbear/ui/styling/fonts/UInt;")?.l()?;
-        let value = env.get_field(&value_obj, "value", "I")?.i()? as u32;
-
-        Ok(Feature {
-            tag,
-            value,
-        })
-    }
-}
-
-impl FromJObject for FontFeatures {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let features_list_obj = env.get_field(obj, "features", "Ljava/util/List;")?.l()?;
-        
-        let size = env.call_method(&features_list_obj, "size", "()I", &[])?.i()?;
-        let mut features = Vec::with_capacity(size as usize);
-        
-        for i in 0..size {
-            let item = env.call_method(&features_list_obj, "get", "(I)Ljava/lang/Object;", &[i.into()])?.l()?;
-            if !item.is_null() {
-                features.push(Feature::from_jobject(env, &item)?);
-            }
-        }
-        
-        Ok(FontFeatures { features })
-    }
-}
-
-impl FromJObject for CacheMetrics {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let font_size = env.get_field(obj, "fontSize", "D")?.d()? as f32;
-        let line_height = env.get_field(obj, "lineHeight", "D")?.d()? as f32;
-
-        Ok(CacheMetrics::from(Metrics {
-            font_size,
-            line_height,
-        }))
-    }
-}
-
-impl FromJObject for Border {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let color_obj = env.get_field(obj, "colour", "Lcom/dropbear/utils/Colour;")?.l()?;
-        let color = Color::from_jobject(env, &color_obj)?;
-        let width = env.get_field(obj, "width", "D")?.d()? as f32;
-        Ok(Border {
-            color,
-            width,
-        })
-    }
-}
-
-impl FromJObject for Color {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let r = env.get_field(obj, "r", "B")?.b()? as u8;
-        let g = env.get_field(obj, "g", "B")?.b()? as u8;
-        let b = env.get_field(obj, "b", "B")?.b()? as u8;
-        let a = env.get_field(obj, "a", "B")?.b()? as u8;
-        Ok(Color::rgba(r, g, b, a))
-    }
-}
-
-impl FromJObject for Pad {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let left = env.get_field(obj, "left", "D")?.d()? as f32;
-        let right = env.get_field(obj, "right", "D")?.d()? as f32;
-        let top = env.get_field(obj, "top", "D")?.d()? as f32;
-        let bottom = env.get_field(obj, "bottom", "D")?.d()? as f32;
-        Ok(Pad {
-            left,
-            right,
-            top,
-            bottom,
-        })
-    }
-}
-
-impl FromJObject for BorderRadius {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let top_left = env.get_field(obj, "topLeft", "D")?.d()? as f32;
-        let top_right = env.get_field(obj, "topRight", "D")?.d()? as f32;
-        let bottom_left = env.get_field(obj, "bottomLeft", "D")?.d()? as f32;
-        let bottom_right = env.get_field(obj, "bottomRight", "D")?.d()? as f32;
-        Ok(BorderRadius {
-            top_left,
-            top_right,
-            bottom_left,
-            bottom_right,
-        })
-    }
-}
-
-impl FromJObject for Alignment {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let x = env.get_field(obj, "x", "D")?.d()? as f32;
-        let y = env.get_field(obj, "y", "D")?.d()? as f32;
-        Ok(Alignment::new(x, y))
-    }
-}
-
-impl FromJObject for TextAlignment {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let name_obj = env.call_method(obj, "name", "()Ljava/lang/String;", &[])?.l()?;
-        let name: String = env.get_string(&name_obj.into())?.into();
-        match name.as_str() {
-            "Start" => Ok(TextAlignment::Start),
-            "Center" => Ok(TextAlignment::Center),
-            "End" => Ok(TextAlignment::End),
-            _ => Ok(TextAlignment::Start),
-        }
-    }
-}
-
-impl FromJObject for Weight {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let val = env.get_field(obj, "value", "I")?.i()? as u16;
-        Ok(Weight(val))
-    }
-}
-
-impl FromJObject for Style {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let name_obj = env.call_method(obj, "name", "()Ljava/lang/String;", &[])?.l()?;
-        let name: String = env.get_string(&name_obj.into())?.into();
-        match name.as_str() {
-            "Normal" => Ok(Style::Normal),
-            "Italic" => Ok(Style::Italic),
-            "Oblique" => Ok(Style::Oblique),
-            _ => Ok(Style::Normal),
-        }
-    }
-}
-
-impl FromJObject for Stretch {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let name_obj = env.call_method(obj, "name", "()Ljava/lang/String;", &[])?.l()?;
-        let name: String = env.get_string(&name_obj.into())?.into();
-        match name.as_str() {
-            "UltraCondensed" => Ok(Stretch::UltraCondensed),
-            "ExtraCondensed" => Ok(Stretch::ExtraCondensed),
-            "Condensed" => Ok(Stretch::Condensed),
-            "SemiCondensed" => Ok(Stretch::SemiCondensed),
-            "Normal" => Ok(Stretch::Normal),
-            "SemiExpanded" => Ok(Stretch::SemiExpanded),
-            "Expanded" => Ok(Stretch::Expanded),
-            "ExtraExpanded" => Ok(Stretch::ExtraExpanded),
-            "UltraExpanded" => Ok(Stretch::UltraExpanded),
-            _ => Ok(Stretch::Normal),
-        }
-    }
-}
-
-impl FromJObject for FamilyOwned {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        if env.is_instance_of(obj, "com/dropbear/ui/styling/fonts/Family$Name")? {
-            let value_obj = env.call_method(obj, "getValue", "()Ljava/lang/String;", &[])?.l()?;
-            let val: String = env.get_string(&value_obj.into())?.into();
-            return Ok(FamilyOwned::Name(val.parse().unwrap()));
-        }
-        if env.is_instance_of(obj, "com/dropbear/ui/styling/fonts/Family$Serif")? { return Ok(FamilyOwned::Serif); }
-        if env.is_instance_of(obj, "com/dropbear/ui/styling/fonts/Family$SansSerif")? { return Ok(FamilyOwned::SansSerif); }
-        if env.is_instance_of(obj, "com/dropbear/ui/styling/fonts/Family$Cursive")? { return Ok(FamilyOwned::Cursive); }
-        if env.is_instance_of(obj, "com/dropbear/ui/styling/fonts/Family$Fantasy")? { return Ok(FamilyOwned::Fantasy); }
-        if env.is_instance_of(obj, "com/dropbear/ui/styling/fonts/Family$Monospace")? { return Ok(FamilyOwned::Monospace); }
-        
-        Ok(FamilyOwned::SansSerif)
-    }
-}
-
-impl FromJObject for AttrsOwned {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let family_obj = env.get_field(obj, "family", "Lcom/dropbear/ui/styling/fonts/Family;")?.l()?;
-        let family_owned = FamilyOwned::from_jobject(env, &family_obj)?;
-        let family = family_owned.as_family();
-        
-        let color_obj = env.get_field(obj, "colourOptions", "Lcom/dropbear/utils/Colour;")?.l()?;
-        let color_opt = if !color_obj.is_null() {
-            let colour = Color::from_jobject(env, &color_obj)?;
-             Some(yakui::cosmic_text::Color::rgba(colour.r, colour.g, colour.b, colour.a))
-        } else {
-             None
-        };
-        
-        let stretch_obj = env.get_field(obj, "stretch", "Lcom/dropbear/ui/styling/fonts/Stretch;")?.l()?;
-        let stretch = Stretch::from_jobject(env, &stretch_obj)?;
-        
-        let style_obj = env.get_field(obj, "style", "Lcom/dropbear/ui/styling/fonts/FontStyle;")?.l()?;
-        let style = Style::from_jobject(env, &style_obj)?;
-        
-        let weight_obj = env.get_field(obj, "weight", "Lcom/dropbear/ui/styling/fonts/FontWeight;")?.l()?;
-        let weight = Weight::from_jobject(env, &weight_obj)?;
-        
-        let metadata = match env.get_field(obj, "metadata", "I") {
-             Ok(val) => val.i()? as usize,
-             Err(_) => 0, 
-        };
-
-        let letter_spacing_obj = env.get_field(obj, "letterSpacingOptions", "Ljava/lang/Double;")?.l()?;
-        let letter_spacing_opt = if !letter_spacing_obj.is_null() {
-             let val = env.call_method(&letter_spacing_obj, "doubleValue", "()D", &[])?.d()?;
-             Some(yakui::cosmic_text::LetterSpacing(val as f32))
-        } else {
-             None
-        };
-
-        let font_features_obj = env.get_field(obj, "fontFeatures", "Lcom/dropbear/ui/styling/fonts/FontFeatures;")?.l()?;
-        let font_features = if !font_features_obj.is_null() {
-            FontFeatures::from_jobject(env, &font_features_obj)?
-        } else {
-            FontFeatures::default()
-        };
-
-        let cache_key_flags_val = env.get_field(obj, "cacheKeyFlags", "I")?.i()? as u32;
-        let cache_key_flags = CacheKeyFlags::from_bits_truncate(cache_key_flags_val);
-        
-        let metrics_obj = env.get_field(obj, "metricsOptions", "Lcom/dropbear/ui/styling/fonts/CacheMetrics;")?.l()?;
-        let metrics_opt = if !metrics_obj.is_null() {
-            Some(CacheMetrics::from_jobject(env, &metrics_obj)?)
-        } else {
-            None
-        };
-
-        Ok(AttrsOwned::new(&Attrs {
-            family,
-            stretch,
-            style,
-            weight,
-            metadata,
-            color_opt,
-            cache_key_flags,
-            metrics_opt,
-            letter_spacing_opt,
-            font_features,
-        }))
-    }
-}
-
-impl FromJObject for TextStyle {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let align_obj = env.get_field(obj, "align", "Lcom/dropbear/ui/styling/fonts/TextAlignment;")?.l()?;
-        let align = TextAlignment::from_jobject(env, &align_obj)?;
-
-        let font_size = env.get_field(obj, "fontSize", "D")?.d()? as f32;
-        
-        let color_obj = env.get_field(obj, "colour", "Lcom/dropbear/utils/Colour;")?.l()?;
-        let color = if !color_obj.is_null() {
-            Color::from_jobject(env, &color_obj)?
-        } else {
-            Color::WHITE
-        };
-
-        let attrs_obj = env.get_field(obj, "attrs", "Lcom/dropbear/ui/styling/fonts/FontAttributes;")?.l()?;
-        let attrs = AttrsOwned::from_jobject(env, &attrs_obj)?;
-
-        Ok(TextStyle {
-            align,
-            font_size,
-            line_height_override: None,
-            color,
-            attrs,
-        })
-    }
-}
-
-impl FromJObject for DynamicButtonStyle {
-    fn from_jobject(env: &mut JNIEnv, obj: &JObject) -> DropbearNativeResult<Self> {
-        let text_obj = env.get_field(obj, "text", "Lcom/dropbear/ui/styling/TextStyle;")?.l()?;
-        let text = if !text_obj.is_null() {
-            TextStyle::from_jobject(env, &text_obj)?
-        } else {
-            println!("Text is null, setting to default");
-            TextStyle::default()
-        };
-
-        let fill_obj = env.get_field(obj, "fill", "Lcom/dropbear/utils/Colour;")?.l()?;
-        let fill = if !fill_obj.is_null() {
-            Color::from_jobject(env, &fill_obj)?
-        } else {
-            Color::GRAY
-        };
-
-        let border_obj = env.get_field(obj, "border", "Lcom/dropbear/ui/styling/Border;")?.l()?;
-        let border = if !border_obj.is_null() {
-            Some(Border::from_jobject(env, &border_obj)?)
-        } else {
-            None
-        };
-
-        Ok(DynamicButtonStyle {
-            text,
-            fill,
-            border,
-        })
-    }
-}
diff --git a/crates/goanna-gen/Cargo.toml b/crates/goanna-gen/Cargo.toml
new file mode 100644
index 0000000..27fe719
--- /dev/null
+++ b/crates/goanna-gen/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "goanna-gen"
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+authors.workspace = true
+
+[dependencies]
+cbindgen.workspace = true
+syn = { workspace = true, features = ["full"] }
+anyhow.workspace = true
\ No newline at end of file
diff --git a/crates/goanna-gen/README.md b/crates/goanna-gen/README.md
new file mode 100644
index 0000000..9a57538
--- /dev/null
+++ b/crates/goanna-gen/README.md
@@ -0,0 +1,5 @@
+# goanna-gen
+
+A c header compile-time generator that sits in build.rs. 
+
+When paired with `dropbear_macro`, you can generate c headers and JNI headers easily for interop. 
\ No newline at end of file
diff --git a/crates/goanna-gen/src/lib.rs b/crates/goanna-gen/src/lib.rs
new file mode 100644
index 0000000..05dbaa2
--- /dev/null
+++ b/crates/goanna-gen/src/lib.rs
@@ -0,0 +1,844 @@
+pub fn generate_c_header() -> anyhow::Result<()> {
+    let manifest_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);
+    let workspace_root = manifest_dir
+        .parent()
+        .and_then(|p| p.parent())
+        .ok_or_else(|| anyhow::anyhow!("Failed to locate workspace root"))?;
+
+    let output_path = workspace_root.join("include").join("dropbear.h");
+    if let Some(parent) = output_path.parent() {
+        std::fs::create_dir_all(parent)?;
+    }
+
+    let src_dir = manifest_dir.join("src");
+    let mut functions = Vec::new();
+    let mut structs = std::collections::HashMap::new();
+    let mut enums = Vec::new();
+    collect_exported_functions(&src_dir, &mut functions, &mut structs, &mut enums)?;
+
+    let header = render_header(&functions, &structs, &enums);
+    std::fs::write(&output_path, header)?;
+
+    Ok(())
+}
+
+#[derive(Debug)]
+struct ExportedFunction {
+    name: String,
+    params: Vec<ExportParam>,
+    out_type: Option<String>,
+}
+
+#[derive(Debug, Clone)]
+struct ExportParam {
+    name: String,
+    ty: String,
+}
+
+fn collect_exported_functions(
+    dir: &std::path::Path,
+    out: &mut Vec<ExportedFunction>,
+    structs: &mut std::collections::HashMap<String, StructDef>,
+    enums: &mut Vec<EnumDef>,
+) -> anyhow::Result<()> {
+    if dir.is_dir() {
+        for entry in std::fs::read_dir(dir)? {
+            let entry = entry?;
+            let path = entry.path();
+            if path.is_dir() {
+                collect_exported_functions(&path, out, structs, enums)?;
+            } else if path.extension().and_then(|s| s.to_str()) == Some("rs") {
+                let content = std::fs::read_to_string(&path)?;
+                let file = syn::parse_file(&content)?;
+                extract_exports_from_file(&file, &path, dir, out, structs)?;
+                extract_structs_from_file(&file, structs)?;
+                extract_repr_c_enums_from_file(&file, enums)?;
+            }
+        }
+    }
+    Ok(())
+}
+
+fn extract_exports_from_file(
+    file: &syn::File,
+    file_path: &std::path::Path,
+    src_root: &std::path::Path,
+    out: &mut Vec<ExportedFunction>,
+    _structs: &mut std::collections::HashMap<String, StructDef>,
+) -> anyhow::Result<()> {
+    let module_path = module_path_from_file(file_path, src_root);
+    for item in &file.items {
+        if let syn::Item::Fn(func) = item {
+            if let Some(export) = parse_export_attr(&func.attrs)? {
+                if export.c.is_none() {
+                    continue;
+                }
+
+                let c_name = export.c.and_then(|c| c.name).unwrap_or_else(|| {
+                    if let Some(path) = module_path.as_deref() {
+                        if !path.is_empty() {
+                            return format!("dropbear_{}_{}", path, func.sig.ident);
+                        }
+                    }
+                    format!("dropbear_{}", func.sig.ident)
+                });
+
+                let (out_type, out_is_optional) = extract_result_type(&func.sig.output)?;
+                let mut params = Vec::new();
+                for input in &func.sig.inputs {
+                    if let syn::FnArg::Typed(pat_ty) = input {
+                        let (define_ty, is_entity) = extract_arg_markers(&pat_ty.attrs);
+                        let name = match &*pat_ty.pat {
+                            syn::Pat::Ident(ident) => ident.ident.to_string(),
+                            _ => continue,
+                        };
+
+                        let ty = if is_entity {
+                            "uint64_t".to_string()
+                        } else if let Some(define_ty) = define_ty {
+                            type_to_c(&define_ty, true)
+                        } else if is_object_input(&pat_ty.ty) {
+                            object_input_to_c(&pat_ty.ty)
+                        } else {
+                            type_to_c(&pat_ty.ty, false)
+                        };
+
+                        params.push(ExportParam { name, ty });
+                    }
+                }
+
+                if out_type.is_some() {
+                    let out_ty = out_type.clone().unwrap();
+                    params.push(ExportParam { name: "out0".to_string(), ty: format!("{}*", out_ty) });
+                    if out_is_optional {
+                        params.push(ExportParam { name: "out0_present".to_string(), ty: "bool*".to_string() });
+                    }
+                }
+
+                out.push(ExportedFunction {
+                    name: c_name,
+                    params,
+                    out_type,
+                });
+            }
+        }
+    }
+    Ok(())
+}
+
+fn module_path_from_file(
+    file_path: &std::path::Path,
+    src_root: &std::path::Path,
+) -> Option<String> {
+    let rel = file_path.strip_prefix(src_root).ok()?;
+    let mut parts: Vec<String> = rel
+        .components()
+        .map(|c| c.as_os_str().to_string_lossy().to_string())
+        .collect();
+
+    let file = parts.pop()?;
+    let stem = file.strip_suffix(".rs").unwrap_or(&file);
+
+    if stem != "mod" && stem != "lib" && stem != "main" {
+        parts.push(stem.to_string());
+    }
+
+    if parts.is_empty() {
+        return None;
+    }
+
+    let joined = parts.join("_");
+    Some(joined.replace('-', "_"))
+}
+
+#[derive(Debug, Clone)]
+struct StructDef {
+    name: String,
+    fields: Vec<ExportParam>,
+    is_repr_c: bool,
+}
+
+#[derive(Debug)]
+struct EnumDef {
+    name: String,
+    variants: Vec<EnumVariantDef>,
+}
+
+#[derive(Debug)]
+struct EnumVariantDef {
+    name: String,
+    fields: Vec<ExportParam>,
+}
+
+fn extract_repr_c_enums_from_file(
+    file: &syn::File,
+    enums: &mut Vec<EnumDef>,
+) -> anyhow::Result<()> {
+    for item in &file.items {
+        if let syn::Item::Enum(enm) = item {
+            if !has_repr_c_enum_attr(&enm.attrs) {
+                continue;
+            }
+            let mut variants = Vec::new();
+            for variant in &enm.variants {
+                let mut fields = Vec::new();
+                match &variant.fields {
+                    syn::Fields::Named(named) => {
+                        for field in &named.named {
+                            let name = field
+                                .ident
+                                .as_ref()
+                                .map(|i| i.to_string())
+                                .unwrap_or_else(|| "field".to_string());
+                            let ty = type_to_c(&field.ty, false);
+                            fields.push(ExportParam { name, ty });
+                        }
+                    }
+                    syn::Fields::Unnamed(unnamed) => {
+                        for (idx, field) in unnamed.unnamed.iter().enumerate() {
+                            let name = format!("_{}", idx);
+                            let ty = type_to_c(&field.ty, false);
+                            fields.push(ExportParam { name, ty });
+                        }
+                    }
+                    syn::Fields::Unit => {}
+                }
+                variants.push(EnumVariantDef { name: variant.ident.to_string(), fields });
+            }
+            enums.push(EnumDef { name: enm.ident.to_string(), variants });
+        }
+    }
+    Ok(())
+}
+
+fn extract_structs_from_file(
+    file: &syn::File,
+    structs: &mut std::collections::HashMap<String, StructDef>,
+) -> anyhow::Result<()> {
+    for item in &file.items {
+        if let syn::Item::Struct(strct) = item {
+            let name = strct.ident.to_string();
+            let is_repr_c = has_repr_c(&strct.attrs);
+            let mut fields = Vec::new();
+
+            if let syn::Fields::Named(named) = &strct.fields {
+                for field in &named.named {
+                    let field_name = field.ident.as_ref().map(|i| i.to_string()).unwrap_or_else(|| "field".to_string());
+                    let ty = type_to_c(&field.ty, false);
+                    fields.push(ExportParam { name: field_name, ty });
+                }
+            }
+
+            structs.insert(name.clone(), StructDef { name, fields, is_repr_c });
+        }
+    }
+
+    Ok(())
+}
+
+#[derive(Default)]
+struct ExportAttr {
+    c: Option<CArgs>,
+}
+
+#[derive(Default)]
+struct CArgs {
+    name: Option<String>,
+}
+
+fn parse_export_attr(attrs: &[syn::Attribute]) -> anyhow::Result<Option<ExportAttr>> {
+    for attr in attrs {
+        let path = attr.path();
+        let is_export = path.segments.last().map(|s| s.ident == "export").unwrap_or(false)
+            || (path.segments.iter().any(|s| s.ident == "dropbear_macro")
+                && path.segments.last().map(|s| s.ident == "export").unwrap_or(false));
+        if !is_export {
+            continue;
+        }
+
+        let meta = &attr.meta;
+        if let syn::Meta::List(list) = meta {
+            let args = syn::parse2::<ExportArgs>(list.tokens.clone())?;
+            return Ok(Some(ExportAttr { c: args.c }));
+        }
+    }
+
+    Ok(None)
+}
+
+struct ExportArgs {
+    c: Option<CArgs>,
+}
+
+impl syn::parse::Parse for ExportArgs {
+    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+        let items = syn::punctuated::Punctuated::<ExportItem, syn::Token![,]>::parse_terminated(input)?;
+        let mut args = ExportArgs { c: None };
+
+        for item in items {
+            if let ExportItem::C(c) = item {
+                args.c = Some(c);
+            }
+        }
+
+        Ok(args)
+    }
+}
+
+enum ExportItem {
+    C(CArgs),
+    Kotlin,
+}
+
+impl syn::parse::Parse for ExportItem {
+    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+        let ident: syn::Ident = input.parse()?;
+        if ident == "c" {
+            let args = if input.peek(syn::token::Paren) {
+                let content;
+                syn::parenthesized!(content in input);
+                let mut c_args = CArgs::default();
+                while !content.is_empty() {
+                    let key: syn::Ident = content.parse()?;
+                    content.parse::<syn::Token![=]>()?;
+                    let value: syn::LitStr = content.parse()?;
+                    if key == "name" {
+                        c_args.name = Some(value.value());
+                    }
+                    if content.peek(syn::Token![,]) {
+                        content.parse::<syn::Token![,]>()?;
+                    }
+                }
+                c_args
+            } else {
+                CArgs::default()
+            };
+            return Ok(ExportItem::C(args));
+        }
+
+        if ident == "kotlin" {
+            if input.peek(syn::token::Paren) {
+                let content;
+                syn::parenthesized!(content in input);
+                while !content.is_empty() {
+                    let _key: syn::Ident = content.parse()?;
+                    content.parse::<syn::Token![=]>()?;
+                    let _value: syn::LitStr = content.parse()?;
+                    if content.peek(syn::Token![,]) {
+                        content.parse::<syn::Token![,]>()?;
+                    }
+                }
+            }
+            return Ok(ExportItem::Kotlin);
+        }
+
+        Err(syn::Error::new(ident.span(), "Expected c or kotlin"))
+    }
+}
+
+fn extract_arg_markers(attrs: &[syn::Attribute]) -> (Option<syn::Type>, bool) {
+    let mut define_ty: Option<syn::Type> = None;
+    let mut is_entity = false;
+    for attr in attrs {
+        let path = attr.path();
+        let ident = path.segments.last().map(|s| s.ident.to_string()).unwrap_or_default();
+        if ident == "define" {
+            if let Ok(ty) = attr.parse_args::<syn::Type>() {
+                define_ty = Some(ty);
+            }
+        }
+        if ident == "entity" {
+            is_entity = true;
+        }
+    }
+    (define_ty, is_entity)
+}
+
+fn extract_result_type(output: &syn::ReturnType) -> anyhow::Result<(Option<String>, bool)> {
+    let ty = match output {
+        syn::ReturnType::Type(_, ty) => ty,
+        syn::ReturnType::Default => return Ok((None, false)),
+    };
+
+    let inner = match &**ty {
+        syn::Type::Path(path) => {
+            let last = path.path.segments.last().ok_or_else(|| anyhow::anyhow!("Invalid return type"))?;
+            if last.ident != "DropbearNativeResult" {
+                return Ok((None, false));
+            }
+            match &last.arguments {
+                syn::PathArguments::AngleBracketed(args) => args.args.first().and_then(|a| match a {
+                    syn::GenericArgument::Type(t) => Some(t.clone()),
+                    _ => None,
+                }).ok_or_else(|| anyhow::anyhow!("DropbearNativeResult missing type"))?,
+                _ => return Ok((None, false)),
+            }
+        }
+        _ => return Ok((None, false)),
+    };
+
+    if is_unit_type(&inner) {
+        return Ok((None, false));
+    }
+
+    if let Some(opt_inner) = extract_option_inner(&inner) {
+        return Ok((Some(type_to_c(&opt_inner, true)), true));
+    }
+
+    Ok((Some(type_to_c(&inner, true)), false))
+}
+
+fn extract_option_inner(ty: &syn::Type) -> Option<syn::Type> {
+    if let syn::Type::Path(path) = ty {
+        let last = path.path.segments.last()?;
+        if last.ident != "Option" {
+            return None;
+        }
+        if let syn::PathArguments::AngleBracketed(args) = &last.arguments {
+            if let Some(syn::GenericArgument::Type(inner)) = args.args.first() {
+                return Some(inner.clone());
+            }
+        }
+    }
+    None
+}
+
+fn is_unit_type(ty: &syn::Type) -> bool {
+    matches!(ty, syn::Type::Tuple(tuple) if tuple.elems.is_empty())
+}
+
+fn type_to_c(ty: &syn::Type, for_output: bool) -> String {
+    if let syn::Type::Reference(reference) = ty {
+        let inner = type_to_c(&reference.elem, for_output);
+        let mutability = if reference.mutability.is_some() { "" } else { "const " };
+        return format!("{}{}*", mutability, inner);
+    }
+    if let Some(inner) = extract_option_inner(ty) {
+        let inner_c = type_to_c(&inner, true);
+        let mutability = if for_output { "" } else { "const " };
+        return format!("{}{}*", mutability, inner_c);
+    }
+    if let Some(inner) = vec_inner_type(ty) {
+        return array_struct_name_from_type(&inner);
+    }
+
+    if let syn::Type::Path(path) = ty {
+        let ident = path.path.segments.last().map(|s| s.ident.to_string()).unwrap_or_else(|| "void".to_string());
+        return match ident.as_str() {
+            "i8" => "int8_t".to_string(),
+            "u8" => "uint8_t".to_string(),
+            "i16" => "int16_t".to_string(),
+            "u16" => "uint16_t".to_string(),
+            "i32" => "int32_t".to_string(),
+            "u32" => "uint32_t".to_string(),
+            "i64" => "int64_t".to_string(),
+            "u64" => "uint64_t".to_string(),
+            "isize" => "intptr_t".to_string(),
+            "usize" => "size_t".to_string(),
+            "f32" => "float".to_string(),
+            "f64" => "double".to_string(),
+            "bool" => "bool".to_string(),
+            "String" => {
+                if for_output {
+                    "char*".to_string()
+                } else {
+                    "const char*".to_string()
+                }
+            }
+            _ => ident,
+        };
+    }
+
+    "void".to_string()
+}
+
+fn vec_inner_type(ty: &syn::Type) -> Option<syn::Type> {
+    if let syn::Type::Path(path) = ty {
+        let last = path.path.segments.last()?;
+        if last.ident != "Vec" {
+            return None;
+        }
+        if let syn::PathArguments::AngleBracketed(args) = &last.arguments {
+            if let Some(syn::GenericArgument::Type(inner)) = args.args.first() {
+                return Some(inner.clone());
+            }
+        }
+    }
+    None
+}
+
+fn type_name_from_type(ty: &syn::Type) -> Option<String> {
+    if let syn::Type::Path(path) = ty {
+        return path.path.segments.last().map(|s| s.ident.to_string());
+    }
+    None
+}
+
+fn array_struct_name_from_type(ty: &syn::Type) -> String {
+    if let Some(inner) = vec_inner_type(ty) {
+        let inner_name = array_struct_name_from_type(&inner);
+        return format!("{}Array", inner_name);
+    }
+
+    let name = type_name_from_type(ty).unwrap_or_else(|| "Unknown".to_string());
+    format!("{}Array", name)
+}
+
+fn render_header(
+    funcs: &[ExportedFunction],
+    structs: &std::collections::HashMap<String, StructDef>,
+    enums: &[EnumDef],
+) -> String {
+    let mut out = String::new();
+    out.push_str("// Machine generated header bindings by goanna-gen.\n");
+    out.push_str("// DO NOT EDIT UNLESS YOU KNOW WHAT YOU ARE DOING (it will get regenerated anyways with a modification to eucalyptus-core/src).\n");
+    out.push_str("// Licensed under MIT or Apache 2.0 depending on your mood.\n");
+    out.push_str("// part of the dropbear project, by tirbofish\n\n");
+    out.push_str("#ifndef DROPBEAR_H\n");
+    out.push_str("#define DROPBEAR_H\n\n");
+    out.push_str("#include <stdbool.h>\n");
+    out.push_str("#include <stdint.h>\n\n");
+    out.push_str("#include <stddef.h>\n\n");
+
+    let mut needed = std::collections::HashSet::new();
+    for func in funcs {
+        if let Some(out_ty) = &func.out_type {
+            if is_custom_type(out_ty) {
+                needed.insert(out_ty.clone());
+            } else if is_opaque_ptr_name(out_ty) {
+                needed.insert(out_ty.clone());
+            }
+        }
+        for param in &func.params {
+            if is_opaque_ptr_name(&param.ty) {
+                needed.insert(param.ty.clone());
+            }
+            if let Some(base) = base_type_name(&param.ty) {
+                if is_custom_type(&base) {
+                    needed.insert(base);
+                } else if is_opaque_ptr_name(&base) {
+                    needed.insert(base);
+                }
+            }
+        }
+    }
+
+    let mut emitted = std::collections::HashSet::new();
+
+    if needed.contains("AssetKind") {
+        out.push_str("typedef enum AssetKind {\n");
+        out.push_str("    AssetKind_Texture = 0,\n");
+        out.push_str("    AssetKind_Model = 1,\n");
+        out.push_str("} AssetKind;\n\n");
+        emitted.insert("AssetKind".to_string());
+    }
+
+    for enm in enums {
+        emit_repr_c_enum(enm, structs, &mut emitted, &mut out);
+    }
+    for ty in needed {
+        emit_structs_recursive(&ty, structs, &mut emitted, &mut out);
+    }
+
+    for func in funcs {
+        let params = if func.params.is_empty() {
+            "void".to_string()
+        } else {
+            func.params.iter()
+                .map(|p| format!("{} {}", p.ty, p.name))
+                .collect::<Vec<_>>()
+                .join(", ")
+        };
+        out.push_str(&format!("int32_t {}({});\n", func.name, params));
+    }
+
+    out.push_str("\n#endif /* DROPBEAR_H */\n");
+    out
+}
+
+fn emit_repr_c_enum(
+    enm: &EnumDef,
+    structs: &std::collections::HashMap<String, StructDef>,
+    emitted: &mut std::collections::HashSet<String>,
+    out: &mut String,
+) {
+    if emitted.contains(&enm.name) {
+        return;
+    }
+
+    let tag_name = format!("{}Tag", enm.name);
+    let data_name = format!("{}Data", enm.name);
+    let ffi_name = format!("{}Ffi", enm.name);
+
+    for var in &enm.variants {
+        for field in &var.fields {
+            if is_array_type(&field.ty) {
+                emit_array_struct(&field.ty, structs, emitted, out);
+            }
+        }
+    }
+
+    out.push_str(&format!("typedef enum {} {{\n", tag_name));
+    for (idx, var) in enm.variants.iter().enumerate() {
+        out.push_str(&format!("    {}_{} = {},\n", tag_name, var.name, idx));
+    }
+    out.push_str(&format!("}} {};\n\n", tag_name));
+
+    for var in &enm.variants {
+        let struct_name = format!("{}{}", enm.name, var.name);
+        out.push_str(&format!("typedef struct {} {{\n", struct_name));
+        for field in &var.fields {
+            out.push_str(&format!("    {} {};\n", field.ty, field.name));
+        }
+        out.push_str(&format!("}} {};\n\n", struct_name));
+    }
+
+    out.push_str(&format!("typedef union {} {{\n", data_name));
+    for var in &enm.variants {
+        let struct_name = format!("{}{}", enm.name, var.name);
+        out.push_str(&format!("    {} {};\n", struct_name, var.name));
+    }
+    out.push_str(&format!("}} {};\n\n", data_name));
+
+    out.push_str(&format!("typedef struct {} {{\n", ffi_name));
+    out.push_str(&format!("    {} tag;\n", tag_name));
+    out.push_str(&format!("    {} data;\n", data_name));
+    out.push_str(&format!("}} {};\n\n", ffi_name));
+
+    out.push_str(&format!("typedef {} {};\n\n", ffi_name, enm.name));
+    emitted.insert(ffi_name);
+    emitted.insert(enm.name.clone());
+}
+
+fn has_repr_c_enum_attr(attrs: &[syn::Attribute]) -> bool {
+    for attr in attrs {
+        let path = attr.path();
+        if path.segments.last().map(|s| s.ident == "repr_c_enum").unwrap_or(false) {
+            return true;
+        }
+        if path.segments.iter().any(|s| s.ident == "dropbear_macro")
+            && path.segments.last().map(|s| s.ident == "repr_c_enum").unwrap_or(false)
+        {
+            return true;
+        }
+    }
+    false
+}
+
+fn has_repr_c(attrs: &[syn::Attribute]) -> bool {
+    for attr in attrs {
+        if !attr.path().is_ident("repr") {
+            continue;
+        }
+        if let syn::Meta::List(list) = &attr.meta {
+            let tokens = list.tokens.to_string();
+            if tokens.contains("C") || tokens.contains("transparent") {
+                return true;
+            }
+        }
+    }
+    false
+}
+
+fn is_custom_type(ty: &str) -> bool {
+    if ty.ends_with('*') {
+        return false;
+    }
+    if is_opaque_ptr_name(ty) {
+        return false;
+    }
+    !is_builtin_c_type(ty)
+}
+
+fn base_type_name(ty: &str) -> Option<String> {
+    let mut t = ty.trim().to_string();
+    if t.starts_with("const ") {
+        t = t.trim_start_matches("const ").trim().to_string();
+    }
+    if t.ends_with('*') {
+        t = t.trim_end_matches('*').trim().to_string();
+        return Some(t);
+    }
+    None
+}
+
+fn is_object_input(ty: &syn::Type) -> bool {
+    let inner = peel_reference(ty);
+    if is_string_type(inner) || is_primitive_type(inner) {
+        return false;
+    }
+    !is_define_or_entity_like(inner)
+}
+
+fn object_input_to_c(ty: &syn::Type) -> String {
+    match ty {
+        syn::Type::Reference(reference) => {
+            let inner = type_to_c(&reference.elem, false);
+            let mutability = if reference.mutability.is_some() { "" } else { "const " };
+            format!("{}{}*", mutability, inner)
+        }
+        _ => type_to_c(ty, false),
+    }
+}
+
+fn is_define_or_entity_like(_ty: &syn::Type) -> bool {
+    false
+}
+
+fn is_string_type(ty: &syn::Type) -> bool {
+    matches!(ty, syn::Type::Path(path) if path.path.segments.last().map(|s| s.ident == "String").unwrap_or(false))
+}
+
+fn is_primitive_type(ty: &syn::Type) -> bool {
+    matches!(ty, syn::Type::Path(path) if {
+        let ident = path.path.segments.last().map(|s| s.ident.to_string()).unwrap_or_default();
+        matches!(
+            ident.as_str(),
+            "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" |
+            "isize" | "usize" | "f32" | "f64" | "bool"
+        )
+    })
+}
+
+fn peel_reference<'a>(ty: &'a syn::Type) -> &'a syn::Type {
+    if let syn::Type::Reference(reference) = ty {
+        &reference.elem
+    } else {
+        ty
+    }
+}
+
+fn is_builtin_c_type(ty: &str) -> bool {
+    matches!(
+        ty,
+        "int8_t" | "uint8_t" | "int16_t" | "uint16_t" | "int32_t" | "uint32_t" |
+        "int64_t" | "uint64_t" | "intptr_t" | "uintptr_t" | "bool" | "float" |
+        "double" | "char" | "char*" | "const char*" | "void*" | "size_t"
+    )
+}
+
+fn is_opaque_ptr_name(name: &str) -> bool {
+    name.ends_with("Ptr")
+}
+
+fn emit_structs_recursive(
+    name: &str,
+    structs: &std::collections::HashMap<String, StructDef>,
+    emitted: &mut std::collections::HashSet<String>,
+    out: &mut String,
+) {
+    if emitted.contains(name) {
+        return;
+    }
+
+    if is_opaque_ptr_name(name) {
+        out.push_str(&format!("typedef void* {};\n\n", name));
+        emitted.insert(name.to_string());
+        return;
+    }
+
+    if let Some(def) = structs.get(name) {
+        for field in &def.fields {
+            if is_array_type(&field.ty) {
+                emit_array_struct(&field.ty, structs, emitted, out);
+                continue;
+            }
+            if is_custom_type(&field.ty) {
+                emit_structs_recursive(&field.ty, structs, emitted, out);
+            }
+        }
+
+        if !def.is_repr_c {
+            out.push_str("// NOTE: type is not #[repr(C)] in Rust; ensure C ABI safety.\n");
+        }
+
+        out.push_str(&format!("typedef struct {} {{\n", def.name));
+        for field in &def.fields {
+            out.push_str(&format!("    {} {};\n", field.ty, field.name));
+        }
+        out.push_str(&format!("}} {};\n\n", def.name));
+        emitted.insert(def.name.clone());
+    } else if is_array_type(name) {
+        emit_array_struct(name, structs, emitted, out);
+    } else {
+        out.push_str(&format!("typedef struct {} {};// opaque\n\n", name, name));
+        emitted.insert(name.to_string());
+    }
+}
+
+fn is_array_type(ty: &str) -> bool {
+    ty.ends_with("Array")
+}
+
+fn emit_array_struct(
+    name: &str,
+    structs: &std::collections::HashMap<String, StructDef>,
+    emitted: &mut std::collections::HashSet<String>,
+    out: &mut String,
+) {
+    if emitted.contains(name) {
+        return;
+    }
+    let elem = name.trim_end_matches("Array");
+
+    if is_array_type(elem) {
+        emit_array_struct(elem, structs, emitted, out);
+    }
+
+    if is_builtin_rust_primitive(elem) {
+        let c_elem = map_primitive_name(elem);
+        if !emitted.contains(elem) {
+            emitted.insert(elem.to_string());
+        }
+        out.push_str(&format!("typedef struct {} {{\n", name));
+        out.push_str(&format!("    {}* values;\n", c_elem));
+        out.push_str("    size_t length;\n");
+        out.push_str("    size_t capacity;\n");
+        out.push_str(&format!("}} {};\n\n", name));
+        emitted.insert(name.to_string());
+        return;
+    }
+
+    if !emitted.contains(elem) {
+        if structs.contains_key(elem) {
+            emit_structs_recursive(elem, structs, emitted, out);
+        } else {
+            out.push_str(&format!("typedef struct {} {};// opaque\n\n", elem, elem));
+            emitted.insert(elem.to_string());
+        }
+    }
+
+    out.push_str(&format!("typedef struct {} {{\n", name));
+    out.push_str(&format!("    {}* values;\n", elem));
+    out.push_str("    size_t length;\n");
+    out.push_str("    size_t capacity;\n");
+    out.push_str(&format!("}} {};\n\n", name));
+    emitted.insert(name.to_string());
+}
+
+fn is_builtin_rust_primitive(name: &str) -> bool {
+    matches!(
+        name,
+        "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" |
+        "isize" | "usize" | "f32" | "f64" | "bool"
+    )
+}
+
+fn map_primitive_name(name: &str) -> &'static str {
+    match name {
+        "i8" => "int8_t",
+        "u8" => "uint8_t",
+        "i16" => "int16_t",
+        "u16" => "uint16_t",
+        "i32" => "int32_t",
+        "u32" => "uint32_t",
+        "i64" => "int64_t",
+        "u64" => "uint64_t",
+        "isize" => "intptr_t",
+        "usize" => "size_t",
+        "f32" => "float",
+        "f64" => "double",
+        "bool" => "bool",
+        _ => "void",
+    }
+}
diff --git a/crates/redback-runtime/src/lib.rs b/crates/redback-runtime/src/lib.rs
index 389d6d4..a878420 100644
--- a/crates/redback-runtime/src/lib.rs
+++ b/crates/redback-runtime/src/lib.rs
@@ -20,7 +20,7 @@ use eucalyptus_core::scripting::{ScriptManager, ScriptTarget};
 use eucalyptus_core::states::{WorldLoadingStatus, SCENES, Script};
 use eucalyptus_core::scene::loading::{SceneLoadResult, SCENE_LOADER};
 use eucalyptus_core::traits::registry::ComponentRegistry;
-use eucalyptus_core::ptr::{CommandBufferPtr, InputStatePtr, PhysicsStatePtr, WorldPtr};
+use eucalyptus_core::ptr::{CommandBufferPtr, GraphicsContextPtr, InputStatePtr, PhysicsStatePtr, WorldPtr};
 use eucalyptus_core::command::COMMAND_BUFFER;
 use eucalyptus_core::scene::loading::IsSceneLoaded;
 use std::collections::HashMap;
@@ -255,7 +255,7 @@ impl PlayMode {
         }
     }
 
-    fn reload_scripts_for_current_world(&mut self) {
+    fn reload_scripts_for_current_world(&mut self, graphics: Arc<SharedGraphicsContext>) {
         let mut entity_tag_map: HashMap<String, Vec<Entity>> = HashMap::new();
         for (entity_id, script) in self.world.query::<(Entity, &Script)>().iter() {
             for tag in &script.tags {
@@ -281,11 +281,12 @@ impl PlayMode {
         let world_ptr = self.world.as_mut() as WorldPtr;
         let input_ptr = &mut self.input_state as InputStatePtr;
         let graphics_ptr = COMMAND_BUFFER.0.as_ref() as CommandBufferPtr;
+        let graphics_context_ptr = Arc::as_ptr(&graphics) as GraphicsContextPtr;
         let physics_ptr = self.physics_state.as_mut() as PhysicsStatePtr;
         
         if let Err(e) = self
             .script_manager
-            .load_script(world_ptr, input_ptr, graphics_ptr, physics_ptr)
+            .load_script(world_ptr, input_ptr, graphics_ptr, graphics_context_ptr, physics_ptr)
         {
             panic!("Failed to load scripts: {}", e);
         } else {
@@ -439,7 +440,7 @@ impl PlayMode {
 
         self.load_wgpu_nerdy_stuff(graphics);
 
-        self.reload_scripts_for_current_world();
+        self.reload_scripts_for_current_world(graphics.clone());
 
         log::debug!("Scene '{}' loaded", scene_name);
     }
@@ -461,7 +462,7 @@ impl PlayMode {
             }
 
             self.load_wgpu_nerdy_stuff(graphics);
-            self.reload_scripts_for_current_world();
+            self.reload_scripts_for_current_world(graphics.clone());
 
             self.current_scene = Some(scene_progress.requested_scene.clone());
         }
diff --git a/expanded_model.rs b/expanded_model.rs
deleted file mode 100644
index d80ed3c..0000000
--- a/expanded_model.rs
+++ /dev/null
@@ -1,2613 +0,0 @@
-pub mod model {
-    use crate::types::{NQuaternion, NVector2, NVector3, NVector4};
-    use dropbear_engine::model::{
-        Animation, AnimationChannel, AnimationInterpolation, ChannelValues, Material,
-        Mesh, ModelVertex, Node, NodeTransform, Skin,
-    };
-    use dropbear_engine::texture::Texture;
-    pub use ffi::*;
-    use ffi_impl::{
-        NMaterialInner, NMeshInner, NModelVertexInner, NNodeInner, NNodeTransformInner,
-        NSkinInner,
-    };
-    #[allow(dead_code)]
-    #[allow(unused_imports)]
-    mod ffi_impl {
-        use super::*;
-        use super::ffi::*;
-        use dropbear_engine::asset::Handle;
-        use crate::asset::model::{
-            map_animation, map_material, map_mesh, map_node, map_skin,
-        };
-        use crate::ptr::AssetRegistryUnwrapped;
-        pub struct NModelVertexInner {
-            pub position: crate::types::NVector3,
-            pub normal: crate::types::NVector3,
-            pub tangent: crate::types::NVector4,
-            pub tex_coords0: crate::types::NVector2,
-            pub tex_coords1: crate::types::NVector2,
-            pub colour0: crate::types::NVector4,
-            pub joints0: Vec<i32>,
-            pub weights0: crate::types::NVector4,
-        }
-        #[automatically_derived]
-        impl ::core::clone::Clone for NModelVertexInner {
-            #[inline]
-            fn clone(&self) -> NModelVertexInner {
-                NModelVertexInner {
-                    position: ::core::clone::Clone::clone(&self.position),
-                    normal: ::core::clone::Clone::clone(&self.normal),
-                    tangent: ::core::clone::Clone::clone(&self.tangent),
-                    tex_coords0: ::core::clone::Clone::clone(&self.tex_coords0),
-                    tex_coords1: ::core::clone::Clone::clone(&self.tex_coords1),
-                    colour0: ::core::clone::Clone::clone(&self.colour0),
-                    joints0: ::core::clone::Clone::clone(&self.joints0),
-                    weights0: ::core::clone::Clone::clone(&self.weights0),
-                }
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NModelVertexInner {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                let names: &'static _ = &[
-                    "position",
-                    "normal",
-                    "tangent",
-                    "tex_coords0",
-                    "tex_coords1",
-                    "colour0",
-                    "joints0",
-                    "weights0",
-                ];
-                let values: &[&dyn ::core::fmt::Debug] = &[
-                    &self.position,
-                    &self.normal,
-                    &self.tangent,
-                    &self.tex_coords0,
-                    &self.tex_coords1,
-                    &self.colour0,
-                    &self.joints0,
-                    &&self.weights0,
-                ];
-                ::core::fmt::Formatter::debug_struct_fields_finish(
-                    f,
-                    "NModelVertexInner",
-                    names,
-                    values,
-                )
-            }
-        }
-        pub struct NMeshInner {
-            pub name: String,
-            pub num_elements: i32,
-            pub material_index: i32,
-            pub vertices: Vec<NModelVertex>,
-        }
-        #[automatically_derived]
-        impl ::core::clone::Clone for NMeshInner {
-            #[inline]
-            fn clone(&self) -> NMeshInner {
-                NMeshInner {
-                    name: ::core::clone::Clone::clone(&self.name),
-                    num_elements: ::core::clone::Clone::clone(&self.num_elements),
-                    material_index: ::core::clone::Clone::clone(&self.material_index),
-                    vertices: ::core::clone::Clone::clone(&self.vertices),
-                }
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NMeshInner {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_struct_field4_finish(
-                    f,
-                    "NMeshInner",
-                    "name",
-                    &self.name,
-                    "num_elements",
-                    &self.num_elements,
-                    "material_index",
-                    &self.material_index,
-                    "vertices",
-                    &&self.vertices,
-                )
-            }
-        }
-        pub struct NMaterialInner {
-            pub name: String,
-            pub diffuse_texture: u64,
-            pub normal_texture: u64,
-            pub tint: crate::types::NVector4,
-            pub emissive_factor: crate::types::NVector3,
-            pub metallic_factor: f32,
-            pub roughness_factor: f32,
-            pub alpha_cutoff: Option<f32>,
-            pub double_sided: bool,
-            pub occlusion_strength: f32,
-            pub normal_scale: f32,
-            pub uv_tiling: crate::types::NVector2,
-            pub emissive_texture: Option<u64>,
-            pub metallic_roughness_texture: Option<u64>,
-            pub occlusion_texture: Option<u64>,
-        }
-        #[automatically_derived]
-        impl ::core::clone::Clone for NMaterialInner {
-            #[inline]
-            fn clone(&self) -> NMaterialInner {
-                NMaterialInner {
-                    name: ::core::clone::Clone::clone(&self.name),
-                    diffuse_texture: ::core::clone::Clone::clone(&self.diffuse_texture),
-                    normal_texture: ::core::clone::Clone::clone(&self.normal_texture),
-                    tint: ::core::clone::Clone::clone(&self.tint),
-                    emissive_factor: ::core::clone::Clone::clone(&self.emissive_factor),
-                    metallic_factor: ::core::clone::Clone::clone(&self.metallic_factor),
-                    roughness_factor: ::core::clone::Clone::clone(
-                        &self.roughness_factor,
-                    ),
-                    alpha_cutoff: ::core::clone::Clone::clone(&self.alpha_cutoff),
-                    double_sided: ::core::clone::Clone::clone(&self.double_sided),
-                    occlusion_strength: ::core::clone::Clone::clone(
-                        &self.occlusion_strength,
-                    ),
-                    normal_scale: ::core::clone::Clone::clone(&self.normal_scale),
-                    uv_tiling: ::core::clone::Clone::clone(&self.uv_tiling),
-                    emissive_texture: ::core::clone::Clone::clone(
-                        &self.emissive_texture,
-                    ),
-                    metallic_roughness_texture: ::core::clone::Clone::clone(
-                        &self.metallic_roughness_texture,
-                    ),
-                    occlusion_texture: ::core::clone::Clone::clone(
-                        &self.occlusion_texture,
-                    ),
-                }
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NMaterialInner {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                let names: &'static _ = &[
-                    "name",
-                    "diffuse_texture",
-                    "normal_texture",
-                    "tint",
-                    "emissive_factor",
-                    "metallic_factor",
-                    "roughness_factor",
-                    "alpha_cutoff",
-                    "double_sided",
-                    "occlusion_strength",
-                    "normal_scale",
-                    "uv_tiling",
-                    "emissive_texture",
-                    "metallic_roughness_texture",
-                    "occlusion_texture",
-                ];
-                let values: &[&dyn ::core::fmt::Debug] = &[
-                    &self.name,
-                    &self.diffuse_texture,
-                    &self.normal_texture,
-                    &self.tint,
-                    &self.emissive_factor,
-                    &self.metallic_factor,
-                    &self.roughness_factor,
-                    &self.alpha_cutoff,
-                    &self.double_sided,
-                    &self.occlusion_strength,
-                    &self.normal_scale,
-                    &self.uv_tiling,
-                    &self.emissive_texture,
-                    &self.metallic_roughness_texture,
-                    &&self.occlusion_texture,
-                ];
-                ::core::fmt::Formatter::debug_struct_fields_finish(
-                    f,
-                    "NMaterialInner",
-                    names,
-                    values,
-                )
-            }
-        }
-        pub struct NNodeTransformInner {
-            pub translation: crate::types::NVector3,
-            pub rotation: crate::types::NQuaternion,
-            pub scale: crate::types::NVector3,
-        }
-        #[automatically_derived]
-        impl ::core::clone::Clone for NNodeTransformInner {
-            #[inline]
-            fn clone(&self) -> NNodeTransformInner {
-                NNodeTransformInner {
-                    translation: ::core::clone::Clone::clone(&self.translation),
-                    rotation: ::core::clone::Clone::clone(&self.rotation),
-                    scale: ::core::clone::Clone::clone(&self.scale),
-                }
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NNodeTransformInner {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_struct_field3_finish(
-                    f,
-                    "NNodeTransformInner",
-                    "translation",
-                    &self.translation,
-                    "rotation",
-                    &self.rotation,
-                    "scale",
-                    &&self.scale,
-                )
-            }
-        }
-        pub struct NNodeInner {
-            pub name: String,
-            pub parent: Option<i32>,
-            pub children: Vec<i32>,
-            pub transform: NNodeTransform,
-        }
-        #[automatically_derived]
-        impl ::core::clone::Clone for NNodeInner {
-            #[inline]
-            fn clone(&self) -> NNodeInner {
-                NNodeInner {
-                    name: ::core::clone::Clone::clone(&self.name),
-                    parent: ::core::clone::Clone::clone(&self.parent),
-                    children: ::core::clone::Clone::clone(&self.children),
-                    transform: ::core::clone::Clone::clone(&self.transform),
-                }
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NNodeInner {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_struct_field4_finish(
-                    f,
-                    "NNodeInner",
-                    "name",
-                    &self.name,
-                    "parent",
-                    &self.parent,
-                    "children",
-                    &self.children,
-                    "transform",
-                    &&self.transform,
-                )
-            }
-        }
-        pub struct NSkinInner {
-            pub name: String,
-            pub joints: Vec<i32>,
-            pub inverse_bind_matrices: Vec<Vec<f64>>,
-            pub skeleton_root: Option<i32>,
-        }
-        #[automatically_derived]
-        impl ::core::clone::Clone for NSkinInner {
-            #[inline]
-            fn clone(&self) -> NSkinInner {
-                NSkinInner {
-                    name: ::core::clone::Clone::clone(&self.name),
-                    joints: ::core::clone::Clone::clone(&self.joints),
-                    inverse_bind_matrices: ::core::clone::Clone::clone(
-                        &self.inverse_bind_matrices,
-                    ),
-                    skeleton_root: ::core::clone::Clone::clone(&self.skeleton_root),
-                }
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NSkinInner {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_struct_field4_finish(
-                    f,
-                    "NSkinInner",
-                    "name",
-                    &self.name,
-                    "joints",
-                    &self.joints,
-                    "inverse_bind_matrices",
-                    &self.inverse_bind_matrices,
-                    "skeleton_root",
-                    &&self.skeleton_root,
-                )
-            }
-        }
-        pub struct NAnimationInner {
-            pub name: String,
-            pub channels: Vec<Box<NAnimationChannel>>,
-            pub duration: f32,
-        }
-        #[automatically_derived]
-        impl ::core::clone::Clone for NAnimationInner {
-            #[inline]
-            fn clone(&self) -> NAnimationInner {
-                NAnimationInner {
-                    name: ::core::clone::Clone::clone(&self.name),
-                    channels: ::core::clone::Clone::clone(&self.channels),
-                    duration: ::core::clone::Clone::clone(&self.duration),
-                }
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NAnimationInner {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_struct_field3_finish(
-                    f,
-                    "NAnimationInner",
-                    "name",
-                    &self.name,
-                    "channels",
-                    &self.channels,
-                    "duration",
-                    &&self.duration,
-                )
-            }
-        }
-        pub struct NAnimationChannelInner {
-            pub target_node: i32,
-            pub times: Vec<f64>,
-            pub values: Box<NChannelValues>,
-            pub interpolation: Box<NAnimationInterpolation>,
-        }
-        #[automatically_derived]
-        impl ::core::clone::Clone for NAnimationChannelInner {
-            #[inline]
-            fn clone(&self) -> NAnimationChannelInner {
-                NAnimationChannelInner {
-                    target_node: ::core::clone::Clone::clone(&self.target_node),
-                    times: ::core::clone::Clone::clone(&self.times),
-                    values: ::core::clone::Clone::clone(&self.values),
-                    interpolation: ::core::clone::Clone::clone(&self.interpolation),
-                }
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NAnimationChannelInner {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_struct_field4_finish(
-                    f,
-                    "NAnimationChannelInner",
-                    "target_node",
-                    &self.target_node,
-                    "times",
-                    &self.times,
-                    "values",
-                    &self.values,
-                    "interpolation",
-                    &&self.interpolation,
-                )
-            }
-        }
-        #[allow(non_camel_case_types)]
-        pub enum NAnimationInterpolationInner {
-            Linear,
-            Step,
-            CubicSpline,
-        }
-        #[automatically_derived]
-        #[allow(non_camel_case_types)]
-        impl ::core::clone::Clone for NAnimationInterpolationInner {
-            #[inline]
-            fn clone(&self) -> NAnimationInterpolationInner {
-                match self {
-                    NAnimationInterpolationInner::Linear => {
-                        NAnimationInterpolationInner::Linear
-                    }
-                    NAnimationInterpolationInner::Step => {
-                        NAnimationInterpolationInner::Step
-                    }
-                    NAnimationInterpolationInner::CubicSpline => {
-                        NAnimationInterpolationInner::CubicSpline
-                    }
-                }
-            }
-        }
-        #[automatically_derived]
-        #[allow(non_camel_case_types)]
-        impl ::core::fmt::Debug for NAnimationInterpolationInner {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::write_str(
-                    f,
-                    match self {
-                        NAnimationInterpolationInner::Linear => "Linear",
-                        NAnimationInterpolationInner::Step => "Step",
-                        NAnimationInterpolationInner::CubicSpline => "CubicSpline",
-                    },
-                )
-            }
-        }
-        #[allow(non_camel_case_types)]
-        pub enum NChannelValuesInner {
-            Translations { values: Vec<crate::types::NVector3> },
-            Rotations { values: Vec<crate::types::NQuaternion> },
-            Scales { values: Vec<crate::types::NVector3> },
-        }
-        #[automatically_derived]
-        #[allow(non_camel_case_types)]
-        impl ::core::clone::Clone for NChannelValuesInner {
-            #[inline]
-            fn clone(&self) -> NChannelValuesInner {
-                match self {
-                    NChannelValuesInner::Translations { values: __self_0 } => {
-                        NChannelValuesInner::Translations {
-                            values: ::core::clone::Clone::clone(__self_0),
-                        }
-                    }
-                    NChannelValuesInner::Rotations { values: __self_0 } => {
-                        NChannelValuesInner::Rotations {
-                            values: ::core::clone::Clone::clone(__self_0),
-                        }
-                    }
-                    NChannelValuesInner::Scales { values: __self_0 } => {
-                        NChannelValuesInner::Scales {
-                            values: ::core::clone::Clone::clone(__self_0),
-                        }
-                    }
-                }
-            }
-        }
-        #[automatically_derived]
-        #[allow(non_camel_case_types)]
-        impl ::core::fmt::Debug for NChannelValuesInner {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                match self {
-                    NChannelValuesInner::Translations { values: __self_0 } => {
-                        ::core::fmt::Formatter::debug_struct_field1_finish(
-                            f,
-                            "Translations",
-                            "values",
-                            &__self_0,
-                        )
-                    }
-                    NChannelValuesInner::Rotations { values: __self_0 } => {
-                        ::core::fmt::Formatter::debug_struct_field1_finish(
-                            f,
-                            "Rotations",
-                            "values",
-                            &__self_0,
-                        )
-                    }
-                    NChannelValuesInner::Scales { values: __self_0 } => {
-                        ::core::fmt::Formatter::debug_struct_field1_finish(
-                            f,
-                            "Scales",
-                            "values",
-                            &__self_0,
-                        )
-                    }
-                }
-            }
-        }
-    }
-    pub mod ffi {
-        use super::ffi_impl::{
-            NModelVertexInner, NMeshInner, NMaterialInner, NNodeTransformInner,
-            NNodeInner, NSkinInner, NAnimationInner, NAnimationChannelInner,
-            NAnimationInterpolationInner, NChannelValuesInner,
-        };
-        use crate::scripting::native::DropbearNativeError;
-        use crate::pointer_convert;
-        use dropbear_engine::asset::Handle;
-        use crate::asset::model::{
-            map_animation, map_material, map_mesh, map_node, map_skin,
-        };
-        use crate::ptr::AssetRegistryUnwrapped;
-        pub struct NModelVertex(pub NModelVertexInner);
-        #[automatically_derived]
-        impl ::core::clone::Clone for NModelVertex {
-            #[inline]
-            fn clone(&self) -> NModelVertex {
-                NModelVertex(::core::clone::Clone::clone(&self.0))
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NModelVertex {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_tuple_field1_finish(
-                    f,
-                    "NModelVertex",
-                    &&self.0,
-                )
-            }
-        }
-        impl NModelVertex {
-            pub fn new(
-                position: crate::types::NVector3,
-                normal: crate::types::NVector3,
-                tangent: crate::types::NVector4,
-                tex_coords0: crate::types::NVector2,
-                tex_coords1: crate::types::NVector2,
-                colour0: crate::types::NVector4,
-                joints0: &[i32],
-                weights0: crate::types::NVector4,
-            ) -> Box<Self> {
-                Box::new(
-                    Self(NModelVertexInner {
-                        position,
-                        normal,
-                        tangent,
-                        tex_coords0,
-                        tex_coords1,
-                        colour0,
-                        joints0: joints0.to_vec(),
-                        weights0,
-                    }),
-                )
-            }
-            pub fn get_position(&self) -> crate::types::NVector3 {
-                self.0.position.clone()
-            }
-            pub fn set_position(&mut self, value: crate::types::NVector3) {
-                self.0.position = value;
-            }
-            pub fn get_normal(&self) -> crate::types::NVector3 {
-                self.0.normal.clone()
-            }
-            pub fn set_normal(&mut self, value: crate::types::NVector3) {
-                self.0.normal = value;
-            }
-            pub fn get_tangent(&self) -> crate::types::NVector4 {
-                self.0.tangent.clone()
-            }
-            pub fn set_tangent(&mut self, value: crate::types::NVector4) {
-                self.0.tangent = value;
-            }
-            pub fn get_tex_coords0(&self) -> crate::types::NVector2 {
-                self.0.tex_coords0.clone()
-            }
-            pub fn set_tex_coords0(&mut self, value: crate::types::NVector2) {
-                self.0.tex_coords0 = value;
-            }
-            pub fn get_tex_coords1(&self) -> crate::types::NVector2 {
-                self.0.tex_coords1.clone()
-            }
-            pub fn set_tex_coords1(&mut self, value: crate::types::NVector2) {
-                self.0.tex_coords1 = value;
-            }
-            pub fn get_colour0(&self) -> crate::types::NVector4 {
-                self.0.colour0.clone()
-            }
-            pub fn set_colour0(&mut self, value: crate::types::NVector4) {
-                self.0.colour0 = value;
-            }
-            pub fn joints0_len(&self) -> usize {
-                self.0.joints0.len()
-            }
-            pub fn joints0_get(&self, index: usize) -> Option<i32> {
-                self.0.joints0.get(index).cloned()
-            }
-            pub fn joints0_push(&mut self, value: i32) {
-                self.0.joints0.push(value);
-            }
-            pub fn get_weights0(&self) -> crate::types::NVector4 {
-                self.0.weights0.clone()
-            }
-            pub fn set_weights0(&mut self, value: crate::types::NVector4) {
-                self.0.weights0 = value;
-            }
-        }
-        pub struct NMesh(pub NMeshInner);
-        #[automatically_derived]
-        impl ::core::clone::Clone for NMesh {
-            #[inline]
-            fn clone(&self) -> NMesh {
-                NMesh(::core::clone::Clone::clone(&self.0))
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NMesh {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "NMesh", &&self.0)
-            }
-        }
-        impl NMesh {
-            pub fn new(
-                name: String,
-                num_elements: i32,
-                material_index: i32,
-                vertices: &[NModelVertex],
-            ) -> Box<Self> {
-                Box::new(
-                    Self(NMeshInner {
-                        name,
-                        num_elements,
-                        material_index,
-                        vertices: vertices.to_vec(),
-                    }),
-                )
-            }
-            pub fn get_name(&self) -> String {
-                self.0.name.clone()
-            }
-            pub fn set_name(&mut self, value: String) {
-                self.0.name = value;
-            }
-            pub fn get_num_elements(&self) -> i32 {
-                self.0.num_elements.clone()
-            }
-            pub fn set_num_elements(&mut self, value: i32) {
-                self.0.num_elements = value;
-            }
-            pub fn get_material_index(&self) -> i32 {
-                self.0.material_index.clone()
-            }
-            pub fn set_material_index(&mut self, value: i32) {
-                self.0.material_index = value;
-            }
-            pub fn vertices_len(&self) -> usize {
-                self.0.vertices.len()
-            }
-            pub fn vertices_get(&self, index: usize) -> Option<NModelVertex> {
-                self.0.vertices.get(index).cloned()
-            }
-            pub fn vertices_push(&mut self, value: NModelVertex) {
-                self.0.vertices.push(value);
-            }
-        }
-        pub struct NMaterial(pub NMaterialInner);
-        #[automatically_derived]
-        impl ::core::clone::Clone for NMaterial {
-            #[inline]
-            fn clone(&self) -> NMaterial {
-                NMaterial(::core::clone::Clone::clone(&self.0))
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NMaterial {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_tuple_field1_finish(
-                    f,
-                    "NMaterial",
-                    &&self.0,
-                )
-            }
-        }
-        impl NMaterial {
-            pub fn new(
-                name: String,
-                diffuse_texture: u64,
-                normal_texture: u64,
-                tint: crate::types::NVector4,
-                emissive_factor: crate::types::NVector3,
-                metallic_factor: f32,
-                roughness_factor: f32,
-                alpha_cutoff: Option<f32>,
-                double_sided: bool,
-                occlusion_strength: f32,
-                normal_scale: f32,
-                uv_tiling: crate::types::NVector2,
-                emissive_texture: Option<u64>,
-                metallic_roughness_texture: Option<u64>,
-                occlusion_texture: Option<u64>,
-            ) -> Box<Self> {
-                Box::new(
-                    Self(NMaterialInner {
-                        name,
-                        diffuse_texture,
-                        normal_texture,
-                        tint,
-                        emissive_factor,
-                        metallic_factor,
-                        roughness_factor,
-                        alpha_cutoff,
-                        double_sided,
-                        occlusion_strength,
-                        normal_scale,
-                        uv_tiling,
-                        emissive_texture,
-                        metallic_roughness_texture,
-                        occlusion_texture,
-                    }),
-                )
-            }
-            pub fn get_name(&self) -> String {
-                self.0.name.clone()
-            }
-            pub fn set_name(&mut self, value: String) {
-                self.0.name = value;
-            }
-            pub fn get_diffuse_texture(&self) -> u64 {
-                self.0.diffuse_texture.clone()
-            }
-            pub fn set_diffuse_texture(&mut self, value: u64) {
-                self.0.diffuse_texture = value;
-            }
-            pub fn get_normal_texture(&self) -> u64 {
-                self.0.normal_texture.clone()
-            }
-            pub fn set_normal_texture(&mut self, value: u64) {
-                self.0.normal_texture = value;
-            }
-            pub fn get_tint(&self) -> crate::types::NVector4 {
-                self.0.tint.clone()
-            }
-            pub fn set_tint(&mut self, value: crate::types::NVector4) {
-                self.0.tint = value;
-            }
-            pub fn get_emissive_factor(&self) -> crate::types::NVector3 {
-                self.0.emissive_factor.clone()
-            }
-            pub fn set_emissive_factor(&mut self, value: crate::types::NVector3) {
-                self.0.emissive_factor = value;
-            }
-            pub fn get_metallic_factor(&self) -> f32 {
-                self.0.metallic_factor.clone()
-            }
-            pub fn set_metallic_factor(&mut self, value: f32) {
-                self.0.metallic_factor = value;
-            }
-            pub fn get_roughness_factor(&self) -> f32 {
-                self.0.roughness_factor.clone()
-            }
-            pub fn set_roughness_factor(&mut self, value: f32) {
-                self.0.roughness_factor = value;
-            }
-            pub fn get_alpha_cutoff(&self) -> Option<f32> {
-                self.0.alpha_cutoff.clone()
-            }
-            pub fn set_alpha_cutoff(&mut self, value: Option<f32>) {
-                self.0.alpha_cutoff = value;
-            }
-            pub fn get_double_sided(&self) -> bool {
-                self.0.double_sided.clone()
-            }
-            pub fn set_double_sided(&mut self, value: bool) {
-                self.0.double_sided = value;
-            }
-            pub fn get_occlusion_strength(&self) -> f32 {
-                self.0.occlusion_strength.clone()
-            }
-            pub fn set_occlusion_strength(&mut self, value: f32) {
-                self.0.occlusion_strength = value;
-            }
-            pub fn get_normal_scale(&self) -> f32 {
-                self.0.normal_scale.clone()
-            }
-            pub fn set_normal_scale(&mut self, value: f32) {
-                self.0.normal_scale = value;
-            }
-            pub fn get_uv_tiling(&self) -> crate::types::NVector2 {
-                self.0.uv_tiling.clone()
-            }
-            pub fn set_uv_tiling(&mut self, value: crate::types::NVector2) {
-                self.0.uv_tiling = value;
-            }
-            pub fn get_emissive_texture(&self) -> Option<u64> {
-                self.0.emissive_texture.clone()
-            }
-            pub fn set_emissive_texture(&mut self, value: Option<u64>) {
-                self.0.emissive_texture = value;
-            }
-            pub fn get_metallic_roughness_texture(&self) -> Option<u64> {
-                self.0.metallic_roughness_texture.clone()
-            }
-            pub fn set_metallic_roughness_texture(&mut self, value: Option<u64>) {
-                self.0.metallic_roughness_texture = value;
-            }
-            pub fn get_occlusion_texture(&self) -> Option<u64> {
-                self.0.occlusion_texture.clone()
-            }
-            pub fn set_occlusion_texture(&mut self, value: Option<u64>) {
-                self.0.occlusion_texture = value;
-            }
-        }
-        pub struct NNodeTransform(pub NNodeTransformInner);
-        #[automatically_derived]
-        impl ::core::clone::Clone for NNodeTransform {
-            #[inline]
-            fn clone(&self) -> NNodeTransform {
-                NNodeTransform(::core::clone::Clone::clone(&self.0))
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NNodeTransform {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_tuple_field1_finish(
-                    f,
-                    "NNodeTransform",
-                    &&self.0,
-                )
-            }
-        }
-        impl NNodeTransform {
-            pub fn new(
-                translation: crate::types::NVector3,
-                rotation: crate::types::NQuaternion,
-                scale: crate::types::NVector3,
-            ) -> Box<Self> {
-                Box::new(
-                    Self(NNodeTransformInner {
-                        translation,
-                        rotation,
-                        scale,
-                    }),
-                )
-            }
-            pub fn get_translation(&self) -> crate::types::NVector3 {
-                self.0.translation.clone()
-            }
-            pub fn set_translation(&mut self, value: crate::types::NVector3) {
-                self.0.translation = value;
-            }
-            pub fn get_rotation(&self) -> crate::types::NQuaternion {
-                self.0.rotation.clone()
-            }
-            pub fn set_rotation(&mut self, value: crate::types::NQuaternion) {
-                self.0.rotation = value;
-            }
-            pub fn get_scale(&self) -> crate::types::NVector3 {
-                self.0.scale.clone()
-            }
-            pub fn set_scale(&mut self, value: crate::types::NVector3) {
-                self.0.scale = value;
-            }
-        }
-        pub struct NNode(pub NNodeInner);
-        #[automatically_derived]
-        impl ::core::clone::Clone for NNode {
-            #[inline]
-            fn clone(&self) -> NNode {
-                NNode(::core::clone::Clone::clone(&self.0))
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NNode {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "NNode", &&self.0)
-            }
-        }
-        impl NNode {
-            pub fn new(
-                name: String,
-                parent: Option<i32>,
-                children: &[i32],
-                transform: NNodeTransform,
-            ) -> Box<Self> {
-                Box::new(
-                    Self(NNodeInner {
-                        name,
-                        parent,
-                        children: children.to_vec(),
-                        transform,
-                    }),
-                )
-            }
-            pub fn get_name(&self) -> String {
-                self.0.name.clone()
-            }
-            pub fn set_name(&mut self, value: String) {
-                self.0.name = value;
-            }
-            pub fn get_parent(&self) -> Option<i32> {
-                self.0.parent.clone()
-            }
-            pub fn set_parent(&mut self, value: Option<i32>) {
-                self.0.parent = value;
-            }
-            pub fn children_len(&self) -> usize {
-                self.0.children.len()
-            }
-            pub fn children_get(&self, index: usize) -> Option<i32> {
-                self.0.children.get(index).cloned()
-            }
-            pub fn children_push(&mut self, value: i32) {
-                self.0.children.push(value);
-            }
-            pub fn get_transform(&self) -> NNodeTransform {
-                self.0.transform.clone()
-            }
-            pub fn set_transform(&mut self, value: NNodeTransform) {
-                self.0.transform = value;
-            }
-        }
-        pub struct NSkin(pub NSkinInner);
-        #[automatically_derived]
-        impl ::core::clone::Clone for NSkin {
-            #[inline]
-            fn clone(&self) -> NSkin {
-                NSkin(::core::clone::Clone::clone(&self.0))
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NSkin {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "NSkin", &&self.0)
-            }
-        }
-        impl NSkin {
-            pub fn get_name(&self) -> String {
-                self.0.name.clone()
-            }
-            pub fn set_name(&mut self, value: String) {
-                self.0.name = value;
-            }
-            pub fn joints_len(&self) -> usize {
-                self.0.joints.len()
-            }
-            pub fn joints_get(&self, index: usize) -> Option<i32> {
-                self.0.joints.get(index).cloned()
-            }
-            pub fn joints_push(&mut self, value: i32) {
-                self.0.joints.push(value);
-            }
-            pub fn get_skeleton_root(&self) -> Option<i32> {
-                self.0.skeleton_root.clone()
-            }
-            pub fn set_skeleton_root(&mut self, value: Option<i32>) {
-                self.0.skeleton_root = value;
-            }
-        }
-        pub struct NAnimation(pub NAnimationInner);
-        #[automatically_derived]
-        impl ::core::clone::Clone for NAnimation {
-            #[inline]
-            fn clone(&self) -> NAnimation {
-                NAnimation(::core::clone::Clone::clone(&self.0))
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NAnimation {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_tuple_field1_finish(
-                    f,
-                    "NAnimation",
-                    &&self.0,
-                )
-            }
-        }
-        impl NAnimation {
-            pub fn new(
-                name: String,
-                channels: &[Box<NAnimationChannel>],
-                duration: f32,
-            ) -> Box<Self> {
-                Box::new(
-                    Self(NAnimationInner {
-                        name,
-                        channels: channels.to_vec(),
-                        duration,
-                    }),
-                )
-            }
-            pub fn get_name(&self) -> String {
-                self.0.name.clone()
-            }
-            pub fn set_name(&mut self, value: String) {
-                self.0.name = value;
-            }
-            pub fn channels_len(&self) -> usize {
-                self.0.channels.len()
-            }
-            pub fn channels_get(&self, index: usize) -> Option<Box<NAnimationChannel>> {
-                self.0.channels.get(index).cloned()
-            }
-            pub fn channels_push(&mut self, value: Box<NAnimationChannel>) {
-                self.0.channels.push(value);
-            }
-            pub fn get_duration(&self) -> f32 {
-                self.0.duration.clone()
-            }
-            pub fn set_duration(&mut self, value: f32) {
-                self.0.duration = value;
-            }
-        }
-        pub struct NAnimationChannel(pub NAnimationChannelInner);
-        #[automatically_derived]
-        impl ::core::clone::Clone for NAnimationChannel {
-            #[inline]
-            fn clone(&self) -> NAnimationChannel {
-                NAnimationChannel(::core::clone::Clone::clone(&self.0))
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NAnimationChannel {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_tuple_field1_finish(
-                    f,
-                    "NAnimationChannel",
-                    &&self.0,
-                )
-            }
-        }
-        impl NAnimationChannel {
-            pub fn new(
-                target_node: i32,
-                times: &[f64],
-                values: Box<NChannelValues>,
-                interpolation: Box<NAnimationInterpolation>,
-            ) -> Box<Self> {
-                Box::new(
-                    Self(NAnimationChannelInner {
-                        target_node,
-                        times: times.to_vec(),
-                        values,
-                        interpolation,
-                    }),
-                )
-            }
-            pub fn get_target_node(&self) -> i32 {
-                self.0.target_node.clone()
-            }
-            pub fn set_target_node(&mut self, value: i32) {
-                self.0.target_node = value;
-            }
-            pub fn times_len(&self) -> usize {
-                self.0.times.len()
-            }
-            pub fn times_get(&self, index: usize) -> Option<f64> {
-                self.0.times.get(index).cloned()
-            }
-            pub fn times_push(&mut self, value: f64) {
-                self.0.times.push(value);
-            }
-            pub fn get_values(&self) -> Box<NChannelValues> {
-                self.0.values.clone()
-            }
-            pub fn set_values(&mut self, value: Box<NChannelValues>) {
-                self.0.values = value;
-            }
-            pub fn get_interpolation(&self) -> Box<NAnimationInterpolation> {
-                self.0.interpolation.clone()
-            }
-            pub fn set_interpolation(&mut self, value: Box<NAnimationInterpolation>) {
-                self.0.interpolation = value;
-            }
-        }
-        pub struct NAnimationInterpolation(NAnimationInterpolationInner);
-        #[automatically_derived]
-        impl ::core::clone::Clone for NAnimationInterpolation {
-            #[inline]
-            fn clone(&self) -> NAnimationInterpolation {
-                NAnimationInterpolation(::core::clone::Clone::clone(&self.0))
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NAnimationInterpolation {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_tuple_field1_finish(
-                    f,
-                    "NAnimationInterpolation",
-                    &&self.0,
-                )
-            }
-        }
-        impl NAnimationInterpolation {
-            pub fn new_linear() -> Box<NAnimationInterpolation> {
-                Box::new(NAnimationInterpolation(NAnimationInterpolationInner::Linear))
-            }
-            pub fn new_step() -> Box<NAnimationInterpolation> {
-                Box::new(NAnimationInterpolation(NAnimationInterpolationInner::Step))
-            }
-            pub fn new_cubicspline() -> Box<NAnimationInterpolation> {
-                Box::new(
-                    NAnimationInterpolation(NAnimationInterpolationInner::CubicSpline),
-                )
-            }
-        }
-        pub struct NChannelValues(NChannelValuesInner);
-        #[automatically_derived]
-        impl ::core::clone::Clone for NChannelValues {
-            #[inline]
-            fn clone(&self) -> NChannelValues {
-                NChannelValues(::core::clone::Clone::clone(&self.0))
-            }
-        }
-        #[automatically_derived]
-        impl ::core::fmt::Debug for NChannelValues {
-            #[inline]
-            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
-                ::core::fmt::Formatter::debug_tuple_field1_finish(
-                    f,
-                    "NChannelValues",
-                    &&self.0,
-                )
-            }
-        }
-        impl NChannelValues {
-            pub fn new_translations(
-                values: &[crate::types::NVector3],
-            ) -> Box<NChannelValues> {
-                Box::new(
-                    NChannelValues(NChannelValuesInner::Translations {
-                        values: values.to_vec(),
-                    }),
-                )
-            }
-            pub fn new_rotations(
-                values: &[crate::types::NQuaternion],
-            ) -> Box<NChannelValues> {
-                Box::new(
-                    NChannelValues(NChannelValuesInner::Rotations {
-                        values: values.to_vec(),
-                    }),
-                )
-            }
-            pub fn new_scales(values: &[crate::types::NVector3]) -> Box<NChannelValues> {
-                Box::new(
-                    NChannelValues(NChannelValuesInner::Scales {
-                        values: values.to_vec(),
-                    }),
-                )
-            }
-            pub fn translations_values_len(&self) -> usize {
-                if let NChannelValuesInner::Translations { values, .. } = &self.0 {
-                    values.len()
-                } else {
-                    0
-                }
-            }
-            pub fn translations_values_get(
-                &self,
-                index: usize,
-            ) -> Option<crate::types::NVector3> {
-                if let NChannelValuesInner::Translations { values, .. } = &self.0 {
-                    values.get(index).cloned()
-                } else {
-                    None
-                }
-            }
-            pub fn translations_values_push(&mut self, value: crate::types::NVector3) {
-                if let NChannelValuesInner::Translations { values, .. } = &mut self.0 {
-                    values.push(value);
-                }
-            }
-            pub fn rotations_values_len(&self) -> usize {
-                if let NChannelValuesInner::Rotations { values, .. } = &self.0 {
-                    values.len()
-                } else {
-                    0
-                }
-            }
-            pub fn rotations_values_get(
-                &self,
-                index: usize,
-            ) -> Option<crate::types::NQuaternion> {
-                if let NChannelValuesInner::Rotations { values, .. } = &self.0 {
-                    values.get(index).cloned()
-                } else {
-                    None
-                }
-            }
-            pub fn rotations_values_push(&mut self, value: crate::types::NQuaternion) {
-                if let NChannelValuesInner::Rotations { values, .. } = &mut self.0 {
-                    values.push(value);
-                }
-            }
-            pub fn scales_values_len(&self) -> usize {
-                if let NChannelValuesInner::Scales { values, .. } = &self.0 {
-                    values.len()
-                } else {
-                    0
-                }
-            }
-            pub fn scales_values_get(
-                &self,
-                index: usize,
-            ) -> Option<crate::types::NVector3> {
-                if let NChannelValuesInner::Scales { values, .. } = &self.0 {
-                    values.get(index).cloned()
-                } else {
-                    None
-                }
-            }
-            pub fn scales_values_push(&mut self, value: crate::types::NVector3) {
-                if let NChannelValuesInner::Scales { values, .. } = &mut self.0 {
-                    values.push(value);
-                }
-            }
-        }
-        pub fn dropbear_asset_model_get_label(
-            asset_registry: u64,
-            model_handle: u64,
-        ) -> Result<String, crate::scripting::native::DropbearNativeError> {
-            let asset = {
-                let ptr = asset_registry as *const AssetRegistryUnwrapped;
-                if ptr.is_null() {
-                    return Err(
-                        crate::scripting::native::DropbearNativeError::NullPointer,
-                    );
-                }
-                let addr = ptr as usize;
-                let align = std::mem::align_of::<AssetRegistryUnwrapped>();
-                if addr % align != 0 {
-                    return Err(
-                        crate::scripting::native::DropbearNativeError::InvalidArgument,
-                    );
-                }
-                unsafe { &*ptr }
-            };
-            let label = asset
-                .read()
-                .get_label_from_model_handle(Handle::new(model_handle))
-                .ok_or_else(|| DropbearNativeError::InvalidHandle)?;
-            Ok(label)
-        }
-        pub struct NMeshList(pub Vec<NMesh>);
-        impl NMeshList {
-            pub fn len(&self) -> usize {
-                self.0.len()
-            }
-            pub fn get(&self, i: usize) -> Option<&NMesh> {
-                self.0.get(i)
-            }
-        }
-        pub fn dropbear_asset_model_get_meshes(
-            asset_registry: u64,
-            model_handle: u64,
-        ) -> Result<Box<NMeshList>, crate::scripting::native::DropbearNativeError> {
-            let logic = || -> Result<Vec<NMesh>, _> {
-                {
-                    let asset = {
-                        let ptr = asset_registry as *const AssetRegistryUnwrapped;
-                        if ptr.is_null() {
-                            return Err(
-                                crate::scripting::native::DropbearNativeError::NullPointer,
-                            );
-                        }
-                        let addr = ptr as usize;
-                        let align = std::mem::align_of::<AssetRegistryUnwrapped>();
-                        if addr % align != 0 {
-                            return Err(
-                                crate::scripting::native::DropbearNativeError::InvalidArgument,
-                            );
-                        }
-                        unsafe { &*ptr }
-                    };
-                    let reader = asset.read();
-                    let model = reader
-                        .get_model(Handle::new(model_handle))
-                        .ok_or(DropbearNativeError::InvalidHandle)?;
-                    Ok(model.meshes.iter().map(map_mesh).collect())
-                }
-            };
-            logic().map(|v| Box::new(NMeshList(v)))
-        }
-        pub struct NMaterialList(pub Vec<NMaterial>);
-        impl NMaterialList {
-            pub fn len(&self) -> usize {
-                self.0.len()
-            }
-            pub fn get(&self, i: usize) -> Option<&NMaterial> {
-                self.0.get(i)
-            }
-        }
-        pub fn dropbear_asset_model_get_materials(
-            asset_registry: u64,
-            model_handle: u64,
-        ) -> Result<Box<NMaterialList>, crate::scripting::native::DropbearNativeError> {
-            let logic = || -> Result<Vec<NMaterial>, _> {
-                {
-                    let asset = {
-                        let ptr = asset_registry as *const AssetRegistryUnwrapped;
-                        if ptr.is_null() {
-                            return Err(
-                                crate::scripting::native::DropbearNativeError::NullPointer,
-                            );
-                        }
-                        let addr = ptr as usize;
-                        let align = std::mem::align_of::<AssetRegistryUnwrapped>();
-                        if addr % align != 0 {
-                            return Err(
-                                crate::scripting::native::DropbearNativeError::InvalidArgument,
-                            );
-                        }
-                        unsafe { &*ptr }
-                    };
-                    let reader = asset.read();
-                    let model = reader
-                        .get_model(Handle::new(model_handle))
-                        .ok_or(DropbearNativeError::InvalidHandle)?;
-                    Ok(
-                        model
-                            .materials
-                            .iter()
-                            .map(|mat| map_material(&reader, mat))
-                            .collect(),
-                    )
-                }
-            };
-            logic().map(|v| Box::new(NMaterialList(v)))
-        }
-        pub struct NSkinList(pub Vec<NSkin>);
-        impl NSkinList {
-            pub fn len(&self) -> usize {
-                self.0.len()
-            }
-            pub fn get(&self, i: usize) -> Option<&NSkin> {
-                self.0.get(i)
-            }
-        }
-        pub fn dropbear_asset_model_get_skins(
-            asset_registry: u64,
-            model_handle: u64,
-        ) -> Result<Box<NSkinList>, crate::scripting::native::DropbearNativeError> {
-            let logic = || -> Result<Vec<NSkin>, _> {
-                {
-                    let asset = {
-                        let ptr = asset_registry as *const AssetRegistryUnwrapped;
-                        if ptr.is_null() {
-                            return Err(
-                                crate::scripting::native::DropbearNativeError::NullPointer,
-                            );
-                        }
-                        let addr = ptr as usize;
-                        let align = std::mem::align_of::<AssetRegistryUnwrapped>();
-                        if addr % align != 0 {
-                            return Err(
-                                crate::scripting::native::DropbearNativeError::InvalidArgument,
-                            );
-                        }
-                        unsafe { &*ptr }
-                    };
-                    let reader = asset.read();
-                    let model = reader
-                        .get_model(Handle::new(model_handle))
-                        .ok_or(DropbearNativeError::InvalidHandle)?;
-                    Ok(model.skins.iter().map(map_skin).collect())
-                }
-            };
-            logic().map(|v| Box::new(NSkinList(v)))
-        }
-        pub struct NAnimationList(pub Vec<Box<NAnimation>>);
-        impl NAnimationList {
-            pub fn len(&self) -> usize {
-                self.0.len()
-            }
-            pub fn get(&self, i: usize) -> Option<&Box<NAnimation>> {
-                self.0.get(i)
-            }
-        }
-        pub fn dropbear_asset_model_get_animations(
-            asset_registry: u64,
-            model_handle: u64,
-        ) -> Result<Box<NAnimationList>, crate::scripting::native::DropbearNativeError> {
-            let logic = || -> Result<Vec<Box<NAnimation>>, _> {
-                {
-                    let asset = {
-                        let ptr = asset_registry as *const AssetRegistryUnwrapped;
-                        if ptr.is_null() {
-                            return Err(
-                                crate::scripting::native::DropbearNativeError::NullPointer,
-                            );
-                        }
-                        let addr = ptr as usize;
-                        let align = std::mem::align_of::<AssetRegistryUnwrapped>();
-                        if addr % align != 0 {
-                            return Err(
-                                crate::scripting::native::DropbearNativeError::InvalidArgument,
-                            );
-                        }
-                        unsafe { &*ptr }
-                    };
-                    let reader = asset.read();
-                    let model = reader
-                        .get_model(Handle::new(model_handle))
-                        .ok_or(DropbearNativeError::InvalidHandle)?;
-                    Ok(model.animations.iter().map(map_animation).collect())
-                }
-            };
-            logic().map(|v| Box::new(NAnimationList(v)))
-        }
-        pub struct NNodeList(pub Vec<NNode>);
-        impl NNodeList {
-            pub fn len(&self) -> usize {
-                self.0.len()
-            }
-            pub fn get(&self, i: usize) -> Option<&NNode> {
-                self.0.get(i)
-            }
-        }
-        pub fn dropbear_asset_model_get_nodes(
-            asset_registry: u64,
-            model_handle: u64,
-        ) -> Result<Box<NNodeList>, crate::scripting::native::DropbearNativeError> {
-            let logic = || -> Result<Vec<NNode>, _> {
-                {
-                    let asset = {
-                        let ptr = asset_registry as *const AssetRegistryUnwrapped;
-                        if ptr.is_null() {
-                            return Err(
-                                crate::scripting::native::DropbearNativeError::NullPointer,
-                            );
-                        }
-                        let addr = ptr as usize;
-                        let align = std::mem::align_of::<AssetRegistryUnwrapped>();
-                        if addr % align != 0 {
-                            return Err(
-                                crate::scripting::native::DropbearNativeError::InvalidArgument,
-                            );
-                        }
-                        unsafe { &*ptr }
-                    };
-                    let reader = asset.read();
-                    let model = reader
-                        .get_model(Handle::new(model_handle))
-                        .ok_or(DropbearNativeError::InvalidHandle)?;
-                    Ok(model.nodes.iter().map(map_node).collect())
-                }
-            };
-            logic().map(|v| Box::new(NNodeList(v)))
-        }
-        use diplomat_runtime::*;
-        use core::ffi::c_void;
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimation_new(
-            name: String,
-            channels: &[Box<NAnimationChannel>],
-            duration: f32,
-        ) -> Box<NAnimation> {
-            NAnimation::new(name, channels, duration)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimation_get_name(this: &NAnimation) -> String {
-            this.get_name()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimation_set_name(this: &mut NAnimation, value: String) {
-            this.set_name(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimation_channels_len(this: &NAnimation) -> usize {
-            this.channels_len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimation_channels_get(
-            this: &NAnimation,
-            index: usize,
-        ) -> Option<Box<NAnimationChannel>> {
-            this.channels_get(index)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimation_channels_push(
-            this: &mut NAnimation,
-            value: Box<NAnimationChannel>,
-        ) {
-            this.channels_push(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimation_get_duration(this: &NAnimation) -> f32 {
-            this.get_duration()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimation_set_duration(this: &mut NAnimation, value: f32) {
-            this.set_duration(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimation_destroy(this: Box<NAnimation>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationChannel_new(
-            target_node: i32,
-            times: diplomat_runtime::DiplomatSlice<f64>,
-            values: Box<NChannelValues>,
-            interpolation: Box<NAnimationInterpolation>,
-        ) -> Box<NAnimationChannel> {
-            let times = times.into();
-            NAnimationChannel::new(target_node, times, values, interpolation)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationChannel_get_target_node(
-            this: &NAnimationChannel,
-        ) -> i32 {
-            this.get_target_node()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationChannel_set_target_node(
-            this: &mut NAnimationChannel,
-            value: i32,
-        ) {
-            this.set_target_node(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationChannel_times_len(this: &NAnimationChannel) -> usize {
-            this.times_len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationChannel_times_get(
-            this: &NAnimationChannel,
-            index: usize,
-        ) -> diplomat_runtime::DiplomatResult<f64, ()> {
-            this.times_get(index).ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationChannel_times_push(
-            this: &mut NAnimationChannel,
-            value: f64,
-        ) {
-            this.times_push(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationChannel_get_values(
-            this: &NAnimationChannel,
-        ) -> Box<NChannelValues> {
-            this.get_values()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationChannel_set_values(
-            this: &mut NAnimationChannel,
-            value: Box<NChannelValues>,
-        ) {
-            this.set_values(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationChannel_get_interpolation(
-            this: &NAnimationChannel,
-        ) -> Box<NAnimationInterpolation> {
-            this.get_interpolation()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationChannel_set_interpolation(
-            this: &mut NAnimationChannel,
-            value: Box<NAnimationInterpolation>,
-        ) {
-            this.set_interpolation(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationChannel_destroy(this: Box<NAnimationChannel>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationInterpolation_new_linear() -> Box<
-            NAnimationInterpolation,
-        > {
-            NAnimationInterpolation::new_linear()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationInterpolation_new_step() -> Box<
-            NAnimationInterpolation,
-        > {
-            NAnimationInterpolation::new_step()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationInterpolation_new_cubicspline() -> Box<
-            NAnimationInterpolation,
-        > {
-            NAnimationInterpolation::new_cubicspline()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationInterpolation_destroy(
-            this: Box<NAnimationInterpolation>,
-        ) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationList_len(this: &NAnimationList) -> usize {
-            this.len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationList_get(
-            this: &NAnimationList,
-            i: usize,
-        ) -> Option<&Box<NAnimation>> {
-            this.get(i)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NAnimationList_destroy(this: Box<NAnimationList>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_new_translations(
-            values: &[crate::types::NVector3],
-        ) -> Box<NChannelValues> {
-            NChannelValues::new_translations(values)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_new_rotations(
-            values: &[crate::types::NQuaternion],
-        ) -> Box<NChannelValues> {
-            NChannelValues::new_rotations(values)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_new_scales(
-            values: &[crate::types::NVector3],
-        ) -> Box<NChannelValues> {
-            NChannelValues::new_scales(values)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_translations_values_len(
-            this: &NChannelValues,
-        ) -> usize {
-            this.translations_values_len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_translations_values_get(
-            this: &NChannelValues,
-            index: usize,
-        ) -> diplomat_runtime::DiplomatResult<crate::types::NVector3, ()> {
-            this.translations_values_get(index).ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_translations_values_push(
-            this: &mut NChannelValues,
-            value: crate::types::NVector3,
-        ) {
-            this.translations_values_push(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_rotations_values_len(
-            this: &NChannelValues,
-        ) -> usize {
-            this.rotations_values_len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_rotations_values_get(
-            this: &NChannelValues,
-            index: usize,
-        ) -> diplomat_runtime::DiplomatResult<crate::types::NQuaternion, ()> {
-            this.rotations_values_get(index).ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_rotations_values_push(
-            this: &mut NChannelValues,
-            value: crate::types::NQuaternion,
-        ) {
-            this.rotations_values_push(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_scales_values_len(this: &NChannelValues) -> usize {
-            this.scales_values_len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_scales_values_get(
-            this: &NChannelValues,
-            index: usize,
-        ) -> diplomat_runtime::DiplomatResult<crate::types::NVector3, ()> {
-            this.scales_values_get(index).ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_scales_values_push(
-            this: &mut NChannelValues,
-            value: crate::types::NVector3,
-        ) {
-            this.scales_values_push(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NChannelValues_destroy(this: Box<NChannelValues>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_new(
-            name: String,
-            diffuse_texture: u64,
-            normal_texture: u64,
-            tint: crate::types::NVector4,
-            emissive_factor: crate::types::NVector3,
-            metallic_factor: f32,
-            roughness_factor: f32,
-            alpha_cutoff: diplomat_runtime::DiplomatOption<f32>,
-            double_sided: bool,
-            occlusion_strength: f32,
-            normal_scale: f32,
-            uv_tiling: crate::types::NVector2,
-            emissive_texture: diplomat_runtime::DiplomatOption<u64>,
-            metallic_roughness_texture: diplomat_runtime::DiplomatOption<u64>,
-            occlusion_texture: diplomat_runtime::DiplomatOption<u64>,
-        ) -> Box<NMaterial> {
-            let alpha_cutoff: Option<f32> = alpha_cutoff.into();
-            let emissive_texture: Option<u64> = emissive_texture.into();
-            let metallic_roughness_texture: Option<u64> = metallic_roughness_texture
-                .into();
-            let occlusion_texture: Option<u64> = occlusion_texture.into();
-            NMaterial::new(
-                name,
-                diffuse_texture,
-                normal_texture,
-                tint,
-                emissive_factor,
-                metallic_factor,
-                roughness_factor,
-                alpha_cutoff,
-                double_sided,
-                occlusion_strength,
-                normal_scale,
-                uv_tiling,
-                emissive_texture,
-                metallic_roughness_texture,
-                occlusion_texture,
-            )
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_name(this: &NMaterial) -> String {
-            this.get_name()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_name(this: &mut NMaterial, value: String) {
-            this.set_name(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_diffuse_texture(this: &NMaterial) -> u64 {
-            this.get_diffuse_texture()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_diffuse_texture(this: &mut NMaterial, value: u64) {
-            this.set_diffuse_texture(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_normal_texture(this: &NMaterial) -> u64 {
-            this.get_normal_texture()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_normal_texture(this: &mut NMaterial, value: u64) {
-            this.set_normal_texture(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_tint(this: &NMaterial) -> crate::types::NVector4 {
-            this.get_tint()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_tint(
-            this: &mut NMaterial,
-            value: crate::types::NVector4,
-        ) {
-            this.set_tint(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_emissive_factor(
-            this: &NMaterial,
-        ) -> crate::types::NVector3 {
-            this.get_emissive_factor()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_emissive_factor(
-            this: &mut NMaterial,
-            value: crate::types::NVector3,
-        ) {
-            this.set_emissive_factor(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_metallic_factor(this: &NMaterial) -> f32 {
-            this.get_metallic_factor()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_metallic_factor(this: &mut NMaterial, value: f32) {
-            this.set_metallic_factor(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_roughness_factor(this: &NMaterial) -> f32 {
-            this.get_roughness_factor()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_roughness_factor(this: &mut NMaterial, value: f32) {
-            this.set_roughness_factor(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_alpha_cutoff(
-            this: &NMaterial,
-        ) -> diplomat_runtime::DiplomatResult<f32, ()> {
-            this.get_alpha_cutoff().ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_alpha_cutoff(
-            this: &mut NMaterial,
-            value: diplomat_runtime::DiplomatOption<f32>,
-        ) {
-            let value: Option<f32> = value.into();
-            this.set_alpha_cutoff(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_double_sided(this: &NMaterial) -> bool {
-            this.get_double_sided()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_double_sided(this: &mut NMaterial, value: bool) {
-            this.set_double_sided(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_occlusion_strength(this: &NMaterial) -> f32 {
-            this.get_occlusion_strength()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_occlusion_strength(
-            this: &mut NMaterial,
-            value: f32,
-        ) {
-            this.set_occlusion_strength(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_normal_scale(this: &NMaterial) -> f32 {
-            this.get_normal_scale()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_normal_scale(this: &mut NMaterial, value: f32) {
-            this.set_normal_scale(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_uv_tiling(
-            this: &NMaterial,
-        ) -> crate::types::NVector2 {
-            this.get_uv_tiling()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_uv_tiling(
-            this: &mut NMaterial,
-            value: crate::types::NVector2,
-        ) {
-            this.set_uv_tiling(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_emissive_texture(
-            this: &NMaterial,
-        ) -> diplomat_runtime::DiplomatResult<u64, ()> {
-            this.get_emissive_texture().ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_emissive_texture(
-            this: &mut NMaterial,
-            value: diplomat_runtime::DiplomatOption<u64>,
-        ) {
-            let value: Option<u64> = value.into();
-            this.set_emissive_texture(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_metallic_roughness_texture(
-            this: &NMaterial,
-        ) -> diplomat_runtime::DiplomatResult<u64, ()> {
-            this.get_metallic_roughness_texture().ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_metallic_roughness_texture(
-            this: &mut NMaterial,
-            value: diplomat_runtime::DiplomatOption<u64>,
-        ) {
-            let value: Option<u64> = value.into();
-            this.set_metallic_roughness_texture(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_get_occlusion_texture(
-            this: &NMaterial,
-        ) -> diplomat_runtime::DiplomatResult<u64, ()> {
-            this.get_occlusion_texture().ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_set_occlusion_texture(
-            this: &mut NMaterial,
-            value: diplomat_runtime::DiplomatOption<u64>,
-        ) {
-            let value: Option<u64> = value.into();
-            this.set_occlusion_texture(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterial_destroy(this: Box<NMaterial>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterialList_len(this: &NMaterialList) -> usize {
-            this.len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterialList_get(
-            this: &NMaterialList,
-            i: usize,
-        ) -> Option<&NMaterial> {
-            this.get(i)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMaterialList_destroy(this: Box<NMaterialList>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMesh_new(
-            name: String,
-            num_elements: i32,
-            material_index: i32,
-            vertices: &[NModelVertex],
-        ) -> Box<NMesh> {
-            NMesh::new(name, num_elements, material_index, vertices)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMesh_get_name(this: &NMesh) -> String {
-            this.get_name()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMesh_set_name(this: &mut NMesh, value: String) {
-            this.set_name(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMesh_get_num_elements(this: &NMesh) -> i32 {
-            this.get_num_elements()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMesh_set_num_elements(this: &mut NMesh, value: i32) {
-            this.set_num_elements(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMesh_get_material_index(this: &NMesh) -> i32 {
-            this.get_material_index()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMesh_set_material_index(this: &mut NMesh, value: i32) {
-            this.set_material_index(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMesh_vertices_len(this: &NMesh) -> usize {
-            this.vertices_len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMesh_vertices_get(
-            this: &NMesh,
-            index: usize,
-        ) -> diplomat_runtime::DiplomatResult<NModelVertex, ()> {
-            this.vertices_get(index).ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMesh_vertices_push(this: &mut NMesh, value: NModelVertex) {
-            this.vertices_push(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMesh_destroy(this: Box<NMesh>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMeshList_len(this: &NMeshList) -> usize {
-            this.len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMeshList_get(this: &NMeshList, i: usize) -> Option<&NMesh> {
-            this.get(i)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NMeshList_destroy(this: Box<NMeshList>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_new(
-            position: crate::types::NVector3,
-            normal: crate::types::NVector3,
-            tangent: crate::types::NVector4,
-            tex_coords0: crate::types::NVector2,
-            tex_coords1: crate::types::NVector2,
-            colour0: crate::types::NVector4,
-            joints0: diplomat_runtime::DiplomatSlice<i32>,
-            weights0: crate::types::NVector4,
-        ) -> Box<NModelVertex> {
-            let joints0 = joints0.into();
-            NModelVertex::new(
-                position,
-                normal,
-                tangent,
-                tex_coords0,
-                tex_coords1,
-                colour0,
-                joints0,
-                weights0,
-            )
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_get_position(
-            this: &NModelVertex,
-        ) -> crate::types::NVector3 {
-            this.get_position()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_set_position(
-            this: &mut NModelVertex,
-            value: crate::types::NVector3,
-        ) {
-            this.set_position(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_get_normal(
-            this: &NModelVertex,
-        ) -> crate::types::NVector3 {
-            this.get_normal()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_set_normal(
-            this: &mut NModelVertex,
-            value: crate::types::NVector3,
-        ) {
-            this.set_normal(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_get_tangent(
-            this: &NModelVertex,
-        ) -> crate::types::NVector4 {
-            this.get_tangent()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_set_tangent(
-            this: &mut NModelVertex,
-            value: crate::types::NVector4,
-        ) {
-            this.set_tangent(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_get_tex_coords0(
-            this: &NModelVertex,
-        ) -> crate::types::NVector2 {
-            this.get_tex_coords0()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_set_tex_coords0(
-            this: &mut NModelVertex,
-            value: crate::types::NVector2,
-        ) {
-            this.set_tex_coords0(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_get_tex_coords1(
-            this: &NModelVertex,
-        ) -> crate::types::NVector2 {
-            this.get_tex_coords1()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_set_tex_coords1(
-            this: &mut NModelVertex,
-            value: crate::types::NVector2,
-        ) {
-            this.set_tex_coords1(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_get_colour0(
-            this: &NModelVertex,
-        ) -> crate::types::NVector4 {
-            this.get_colour0()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_set_colour0(
-            this: &mut NModelVertex,
-            value: crate::types::NVector4,
-        ) {
-            this.set_colour0(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_joints0_len(this: &NModelVertex) -> usize {
-            this.joints0_len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_joints0_get(
-            this: &NModelVertex,
-            index: usize,
-        ) -> diplomat_runtime::DiplomatResult<i32, ()> {
-            this.joints0_get(index).ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_joints0_push(this: &mut NModelVertex, value: i32) {
-            this.joints0_push(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_get_weights0(
-            this: &NModelVertex,
-        ) -> crate::types::NVector4 {
-            this.get_weights0()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_set_weights0(
-            this: &mut NModelVertex,
-            value: crate::types::NVector4,
-        ) {
-            this.set_weights0(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NModelVertex_destroy(this: Box<NModelVertex>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNode_new(
-            name: String,
-            parent: diplomat_runtime::DiplomatOption<i32>,
-            children: diplomat_runtime::DiplomatSlice<i32>,
-            transform: NNodeTransform,
-        ) -> Box<NNode> {
-            let parent: Option<i32> = parent.into();
-            let children = children.into();
-            NNode::new(name, parent, children, transform)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNode_get_name(this: &NNode) -> String {
-            this.get_name()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNode_set_name(this: &mut NNode, value: String) {
-            this.set_name(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNode_get_parent(
-            this: &NNode,
-        ) -> diplomat_runtime::DiplomatResult<i32, ()> {
-            this.get_parent().ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNode_set_parent(
-            this: &mut NNode,
-            value: diplomat_runtime::DiplomatOption<i32>,
-        ) {
-            let value: Option<i32> = value.into();
-            this.set_parent(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNode_children_len(this: &NNode) -> usize {
-            this.children_len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNode_children_get(
-            this: &NNode,
-            index: usize,
-        ) -> diplomat_runtime::DiplomatResult<i32, ()> {
-            this.children_get(index).ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNode_children_push(this: &mut NNode, value: i32) {
-            this.children_push(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNode_get_transform(this: &NNode) -> NNodeTransform {
-            this.get_transform()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNode_set_transform(this: &mut NNode, value: NNodeTransform) {
-            this.set_transform(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNode_destroy(this: Box<NNode>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNodeList_len(this: &NNodeList) -> usize {
-            this.len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNodeList_get(this: &NNodeList, i: usize) -> Option<&NNode> {
-            this.get(i)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNodeList_destroy(this: Box<NNodeList>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNodeTransform_new(
-            translation: crate::types::NVector3,
-            rotation: crate::types::NQuaternion,
-            scale: crate::types::NVector3,
-        ) -> Box<NNodeTransform> {
-            NNodeTransform::new(translation, rotation, scale)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNodeTransform_get_translation(
-            this: &NNodeTransform,
-        ) -> crate::types::NVector3 {
-            this.get_translation()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNodeTransform_set_translation(
-            this: &mut NNodeTransform,
-            value: crate::types::NVector3,
-        ) {
-            this.set_translation(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNodeTransform_get_rotation(
-            this: &NNodeTransform,
-        ) -> crate::types::NQuaternion {
-            this.get_rotation()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNodeTransform_set_rotation(
-            this: &mut NNodeTransform,
-            value: crate::types::NQuaternion,
-        ) {
-            this.set_rotation(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNodeTransform_get_scale(
-            this: &NNodeTransform,
-        ) -> crate::types::NVector3 {
-            this.get_scale()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNodeTransform_set_scale(
-            this: &mut NNodeTransform,
-            value: crate::types::NVector3,
-        ) {
-            this.set_scale(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NNodeTransform_destroy(this: Box<NNodeTransform>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NSkin_get_name(this: &NSkin) -> String {
-            this.get_name()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NSkin_set_name(this: &mut NSkin, value: String) {
-            this.set_name(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NSkin_joints_len(this: &NSkin) -> usize {
-            this.joints_len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NSkin_joints_get(
-            this: &NSkin,
-            index: usize,
-        ) -> diplomat_runtime::DiplomatResult<i32, ()> {
-            this.joints_get(index).ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NSkin_joints_push(this: &mut NSkin, value: i32) {
-            this.joints_push(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NSkin_get_skeleton_root(
-            this: &NSkin,
-        ) -> diplomat_runtime::DiplomatResult<i32, ()> {
-            this.get_skeleton_root().ok_or(()).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NSkin_set_skeleton_root(
-            this: &mut NSkin,
-            value: diplomat_runtime::DiplomatOption<i32>,
-        ) {
-            let value: Option<i32> = value.into();
-            this.set_skeleton_root(value)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NSkin_destroy(this: Box<NSkin>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NSkinList_len(this: &NSkinList) -> usize {
-            this.len()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NSkinList_get(this: &NSkinList, i: usize) -> Option<&NSkin> {
-            this.get(i)
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn NSkinList_destroy(this: Box<NSkinList>) {}
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn diplomat_external_dropbear_asset_model_get_animations(
-            asset_registry: u64,
-            model_handle: u64,
-        ) -> diplomat_runtime::DiplomatResult<
-            Box<NAnimationList>,
-            crate::scripting::native::DropbearNativeError,
-        > {
-            dropbear_asset_model_get_animations(asset_registry, model_handle).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn diplomat_external_dropbear_asset_model_get_label(
-            asset_registry: u64,
-            model_handle: u64,
-        ) -> diplomat_runtime::DiplomatResult<
-            String,
-            crate::scripting::native::DropbearNativeError,
-        > {
-            dropbear_asset_model_get_label(asset_registry, model_handle).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn diplomat_external_dropbear_asset_model_get_materials(
-            asset_registry: u64,
-            model_handle: u64,
-        ) -> diplomat_runtime::DiplomatResult<
-            Box<NMaterialList>,
-            crate::scripting::native::DropbearNativeError,
-        > {
-            dropbear_asset_model_get_materials(asset_registry, model_handle).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn diplomat_external_dropbear_asset_model_get_meshes(
-            asset_registry: u64,
-            model_handle: u64,
-        ) -> diplomat_runtime::DiplomatResult<
-            Box<NMeshList>,
-            crate::scripting::native::DropbearNativeError,
-        > {
-            dropbear_asset_model_get_meshes(asset_registry, model_handle).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn diplomat_external_dropbear_asset_model_get_nodes(
-            asset_registry: u64,
-            model_handle: u64,
-        ) -> diplomat_runtime::DiplomatResult<
-            Box<NNodeList>,
-            crate::scripting::native::DropbearNativeError,
-        > {
-            dropbear_asset_model_get_nodes(asset_registry, model_handle).into()
-        }
-        #[no_mangle]
-        #[allow(deprecated)]
-        extern "C" fn diplomat_external_dropbear_asset_model_get_skins(
-            asset_registry: u64,
-            model_handle: u64,
-        ) -> diplomat_runtime::DiplomatResult<
-            Box<NSkinList>,
-            crate::scripting::native::DropbearNativeError,
-        > {
-            dropbear_asset_model_get_skins(asset_registry, model_handle).into()
-        }
-    }
-    fn texture_handle_id(
-        registry: &dropbear_engine::asset::AssetRegistry,
-        texture: &Texture,
-    ) -> u64 {
-        texture
-            .hash()
-            .and_then(|hash| registry.texture_handle_by_hash(hash).map(|h| h.id))
-            .unwrap_or(0)
-    }
-    fn map_vertex(vertex: &ModelVertex) -> NModelVertex {
-        NModelVertex {
-            0: NModelVertexInner {
-                position: NVector3::from(vertex.position),
-                normal: NVector3::from(vertex.normal),
-                tangent: NVector4::from(vertex.tangent),
-                tex_coords0: NVector2::from(vertex.tex_coords0),
-                tex_coords1: NVector2::from(vertex.tex_coords1),
-                colour0: NVector4::from(vertex.colour0),
-                joints0: vertex.joints0.iter().map(|v| *v as i32).collect(),
-                weights0: NVector4::from(vertex.weights0),
-            },
-        }
-    }
-    fn map_mesh(mesh: &Mesh) -> NMesh {
-        NMesh {
-            0: NMeshInner {
-                name: mesh.name.clone(),
-                num_elements: mesh.num_elements as i32,
-                material_index: mesh.material as i32,
-                vertices: mesh.vertices.iter().map(map_vertex).collect(),
-            },
-        }
-    }
-    fn map_material(
-        registry: &dropbear_engine::asset::AssetRegistry,
-        material: &Material,
-    ) -> NMaterial {
-        NMaterial {
-            0: NMaterialInner {
-                name: material.name.clone(),
-                diffuse_texture: texture_handle_id(registry, &material.diffuse_texture),
-                normal_texture: texture_handle_id(registry, &material.normal_texture),
-                tint: NVector4::from(material.tint),
-                emissive_factor: NVector3::from(material.emissive_factor),
-                metallic_factor: material.metallic_factor,
-                roughness_factor: material.roughness_factor,
-                alpha_cutoff: material.alpha_cutoff,
-                double_sided: material.double_sided,
-                occlusion_strength: material.occlusion_strength,
-                normal_scale: material.normal_scale,
-                uv_tiling: NVector2::from(material.uv_tiling),
-                emissive_texture: material
-                    .emissive_texture
-                    .as_ref()
-                    .map(|tex| texture_handle_id(registry, tex))
-                    .filter(|id| *id != 0),
-                metallic_roughness_texture: material
-                    .metallic_roughness_texture
-                    .as_ref()
-                    .map(|tex| texture_handle_id(registry, tex))
-                    .filter(|id| *id != 0),
-                occlusion_texture: material
-                    .occlusion_texture
-                    .as_ref()
-                    .map(|tex| texture_handle_id(registry, tex))
-                    .filter(|id| *id != 0),
-            },
-        }
-    }
-    fn map_node_transform(transform: &NodeTransform) -> NNodeTransform {
-        NNodeTransform {
-            0: NNodeTransformInner {
-                translation: NVector3::from(transform.translation),
-                rotation: NQuaternion::from(transform.rotation),
-                scale: NVector3::from(transform.scale),
-            },
-        }
-    }
-    fn map_node(node: &Node) -> NNode {
-        NNode {
-            0: NNodeInner {
-                name: node.name.clone(),
-                parent: node.parent.map(|v| v as i32),
-                children: node.children.iter().map(|v| *v as i32).collect(),
-                transform: map_node_transform(&node.transform),
-            },
-        }
-    }
-    fn map_skin(skin: &Skin) -> NSkin {
-        let inverse_bind_matrices = skin
-            .inverse_bind_matrices
-            .iter()
-            .map(|matrix| matrix.to_cols_array().iter().map(|v| *v as f64).collect())
-            .collect();
-        NSkin {
-            0: NSkinInner {
-                name: skin.name.clone(),
-                joints: skin.joints.iter().map(|v| *v as i32).collect(),
-                inverse_bind_matrices,
-                skeleton_root: skin.skeleton_root.map(|v| v as i32),
-            },
-        }
-    }
-    fn map_interpolation(
-        value: &AnimationInterpolation,
-    ) -> Box<NAnimationInterpolation> {
-        match value {
-            AnimationInterpolation::Linear => NAnimationInterpolation::new_linear(),
-            AnimationInterpolation::Step => NAnimationInterpolation::new_step(),
-            AnimationInterpolation::CubicSpline => {
-                NAnimationInterpolation::new_cubicspline()
-            }
-        }
-    }
-    fn map_channel_values(values: &ChannelValues) -> Box<NChannelValues> {
-        match values {
-            ChannelValues::Translations(list) => {
-                let values: Vec<NVector3> = list
-                    .iter()
-                    .map(|v| NVector3::from(*v))
-                    .collect();
-                NChannelValues::new_translations(&values)
-            }
-            ChannelValues::Rotations(list) => {
-                let values: Vec<NQuaternion> = list
-                    .iter()
-                    .map(|v| NQuaternion::from(*v))
-                    .collect();
-                NChannelValues::new_rotations(&values)
-            }
-            ChannelValues::Scales(list) => {
-                let values: Vec<NVector3> = list
-                    .iter()
-                    .map(|v| NVector3::from(*v))
-                    .collect();
-                NChannelValues::new_scales(&values)
-            }
-        }
-    }
-    fn map_animation_channel(channel: &AnimationChannel) -> Box<NAnimationChannel> {
-        let times: Vec<f64> = channel.times.iter().map(|v| *v as f64).collect();
-        NAnimationChannel::new(
-            channel.target_node as i32,
-            &times,
-            map_channel_values(&channel.values),
-            map_interpolation(&channel.interpolation),
-        )
-    }
-    fn map_animation(animation: &Animation) -> Box<NAnimation> {
-        let channels: Vec<Box<NAnimationChannel>> = animation
-            .channels
-            .iter()
-            .map(map_animation_channel)
-            .collect();
-        NAnimation::new(animation.name.clone(), &channels, animation.duration)
-    }
-}
diff --git a/include/dropbear.h b/include/dropbear.h
index 8df3735..ddd1ee3 100644
--- a/include/dropbear.h
+++ b/include/dropbear.h
@@ -1,3 +1,8 @@
+// Machine generated header bindings by goanna-gen.
+// DO NOT EDIT UNLESS YOU KNOW WHAT YOU ARE DOING (it will get regenerated anyways with a modification to eucalyptus-core/src).
+// Licensed under MIT or Apache 2.0 depending on your mood.
+// part of the dropbear project, by tirbofish
+
 #ifndef DROPBEAR_H
 #define DROPBEAR_H
 
@@ -6,28 +11,271 @@
 
 #include <stddef.h>
 
-typedef void* WorldPtr;
+typedef enum AssetKind {
+    AssetKind_Texture = 0,
+    AssetKind_Model = 1,
+} AssetKind;
+
+typedef enum ColliderShapeTag {
+    ColliderShapeTag_Box = 0,
+    ColliderShapeTag_Sphere = 1,
+    ColliderShapeTag_Capsule = 2,
+    ColliderShapeTag_Cylinder = 3,
+    ColliderShapeTag_Cone = 4,
+} ColliderShapeTag;
+
+typedef struct ColliderShapeBox {
+    void half_extents;
+} ColliderShapeBox;
+
+typedef struct ColliderShapeSphere {
+    float radius;
+} ColliderShapeSphere;
+
+typedef struct ColliderShapeCapsule {
+    float half_height;
+    float radius;
+} ColliderShapeCapsule;
+
+typedef struct ColliderShapeCylinder {
+    float half_height;
+    float radius;
+} ColliderShapeCylinder;
+
+typedef struct ColliderShapeCone {
+    float half_height;
+    float radius;
+} ColliderShapeCone;
+
+typedef union ColliderShapeData {
+    ColliderShapeBox Box;
+    ColliderShapeSphere Sphere;
+    ColliderShapeCapsule Capsule;
+    ColliderShapeCylinder Cylinder;
+    ColliderShapeCone Cone;
+} ColliderShapeData;
+
+typedef struct ColliderShapeFfi {
+    ColliderShapeTag tag;
+    ColliderShapeData data;
+} ColliderShapeFfi;
+
+typedef ColliderShapeFfi ColliderShape;
+
+typedef enum NShapeCastStatusTag {
+    NShapeCastStatusTag_OutOfIterations = 0,
+    NShapeCastStatusTag_Converged = 1,
+    NShapeCastStatusTag_Failed = 2,
+    NShapeCastStatusTag_PenetratingOrWithinTargetDist = 3,
+} NShapeCastStatusTag;
+
+typedef struct NShapeCastStatusOutOfIterations {
+} NShapeCastStatusOutOfIterations;
+
+typedef struct NShapeCastStatusConverged {
+} NShapeCastStatusConverged;
+
+typedef struct NShapeCastStatusFailed {
+} NShapeCastStatusFailed;
+
+typedef struct NShapeCastStatusPenetratingOrWithinTargetDist {
+} NShapeCastStatusPenetratingOrWithinTargetDist;
+
+typedef union NShapeCastStatusData {
+    NShapeCastStatusOutOfIterations OutOfIterations;
+    NShapeCastStatusConverged Converged;
+    NShapeCastStatusFailed Failed;
+    NShapeCastStatusPenetratingOrWithinTargetDist PenetratingOrWithinTargetDist;
+} NShapeCastStatusData;
+
+typedef struct NShapeCastStatusFfi {
+    NShapeCastStatusTag tag;
+    NShapeCastStatusData data;
+} NShapeCastStatusFfi;
+
+typedef NShapeCastStatusFfi NShapeCastStatus;
+
+typedef enum NAnimationInterpolationTag {
+    NAnimationInterpolationTag_Linear = 0,
+    NAnimationInterpolationTag_Step = 1,
+    NAnimationInterpolationTag_CubicSpline = 2,
+} NAnimationInterpolationTag;
+
+typedef struct NAnimationInterpolationLinear {
+} NAnimationInterpolationLinear;
+
+typedef struct NAnimationInterpolationStep {
+} NAnimationInterpolationStep;
+
+typedef struct NAnimationInterpolationCubicSpline {
+} NAnimationInterpolationCubicSpline;
+
+typedef union NAnimationInterpolationData {
+    NAnimationInterpolationLinear Linear;
+    NAnimationInterpolationStep Step;
+    NAnimationInterpolationCubicSpline CubicSpline;
+} NAnimationInterpolationData;
+
+typedef struct NAnimationInterpolationFfi {
+    NAnimationInterpolationTag tag;
+    NAnimationInterpolationData data;
+} NAnimationInterpolationFfi;
+
+typedef NAnimationInterpolationFfi NAnimationInterpolation;
 
-typedef struct NVector4 {
+typedef struct NVector3 {
     double x;
     double y;
     double z;
-    double w;
-} NVector4;
+} NVector3;
 
-typedef struct NVector3 {
+typedef struct NVector3Array {
+    NVector3* values;
+    size_t length;
+    size_t capacity;
+} NVector3Array;
+
+typedef struct NQuaternion {
     double x;
     double y;
     double z;
-} NVector3;
+    double w;
+} NQuaternion;
+
+typedef struct NQuaternionArray {
+    NQuaternion* values;
+    size_t length;
+    size_t capacity;
+} NQuaternionArray;
+
+typedef enum NChannelValuesTag {
+    NChannelValuesTag_Translations = 0,
+    NChannelValuesTag_Rotations = 1,
+    NChannelValuesTag_Scales = 2,
+} NChannelValuesTag;
+
+typedef struct NChannelValuesTranslations {
+    NVector3Array values;
+} NChannelValuesTranslations;
 
-typedef struct Option Option;// opaque
+typedef struct NChannelValuesRotations {
+    NQuaternionArray values;
+} NChannelValuesRotations;
+
+typedef struct NChannelValuesScales {
+    NVector3Array values;
+} NChannelValuesScales;
+
+typedef union NChannelValuesData {
+    NChannelValuesTranslations Translations;
+    NChannelValuesRotations Rotations;
+    NChannelValuesScales Scales;
+} NChannelValuesData;
+
+typedef struct NChannelValuesFfi {
+    NChannelValuesTag tag;
+    NChannelValuesData data;
+} NChannelValuesFfi;
+
+typedef NChannelValuesFfi NChannelValues;
+
+typedef void* SceneLoaderPtr;
+
+typedef void* AssetRegistryPtr;
 
 typedef struct NVector2 {
     double x;
     double y;
 } NVector2;
 
+typedef struct Progress {
+    size_t current;
+    size_t total;
+    const char* message;
+} Progress;
+
+typedef struct u64Array {
+    uint64_t* values;
+    size_t length;
+    size_t capacity;
+} u64Array;
+
+typedef struct ConnectedGamepadIds {
+    u64Array ids;
+} ConnectedGamepadIds;
+
+typedef struct NAttenuation {
+    float constant;
+    float linear;
+    float quadratic;
+} NAttenuation;
+
+typedef struct NRange {
+    float start;
+    float end;
+} NRange;
+
+typedef struct IndexNative {
+    uint32_t index;
+    uint32_t generation;
+} IndexNative;
+
+typedef struct NCollider {
+    IndexNative index;
+    uint64_t entity_id;
+    uint32_t id;
+} NCollider;
+
+typedef struct NColliderArray {
+    NCollider* values;
+    size_t length;
+    size_t capacity;
+} NColliderArray;
+
+typedef struct NTransform {
+    NVector3 position;
+    NQuaternion rotation;
+    NVector3 scale;
+} NTransform;
+
+typedef void* InputStatePtr;
+
+typedef struct IndexNativeArray {
+    IndexNative* values;
+    size_t length;
+    size_t capacity;
+} IndexNativeArray;
+
+typedef struct CharacterCollisionArray {
+    uint64_t entity_id;
+    IndexNativeArray collisions;
+} CharacterCollisionArray;
+
+typedef struct NShapeCastHit {
+    NCollider collider;
+    double distance;
+    NVector3 witness1;
+    NVector3 witness2;
+    NVector3 normal1;
+    NVector3 normal2;
+    NShapeCastStatus status;
+} NShapeCastHit;
+
+typedef struct AxisLock {
+    bool x;
+    bool y;
+    bool z;
+} AxisLock;
+
+typedef void* CommandBufferPtr;
+
+typedef struct NVector4 {
+    double x;
+    double y;
+    double z;
+    double w;
+} NVector4;
+
 typedef struct NMaterial {
     const char* name;
     uint64_t diffuse_texture;
@@ -36,95 +284,86 @@ typedef struct NMaterial {
     NVector3 emissive_factor;
     float metallic_factor;
     float roughness_factor;
-    Option alpha_cutoff;
+    const float* alpha_cutoff;
     bool double_sided;
     float occlusion_strength;
     float normal_scale;
     NVector2 uv_tiling;
-    Option emissive_texture;
-    Option metallic_roughness_texture;
-    Option occlusion_texture;
+    const uint64_t* emissive_texture;
+    const uint64_t* metallic_roughness_texture;
+    const uint64_t* occlusion_texture;
 } NMaterial;
 
-typedef struct NMaterialArrayFfi {
-    const NMaterial* ptr;
-    size_t len;
-} NMaterialArrayFfi;
-
-typedef void* AssetRegistryPtr;
+typedef struct NMaterialArray {
+    NMaterial* values;
+    size_t length;
+    size_t capacity;
+} NMaterialArray;
 
-typedef void* SceneLoaderPtr;
+typedef struct i32Array {
+    int32_t* values;
+    size_t length;
+    size_t capacity;
+} i32Array;
 
-typedef struct size_t size_t;// opaque
-
-typedef struct Progress {
-    size_t current;
-    size_t total;
-    const char* message;
-} Progress;
-
-typedef struct i32ArrayFfi {
-    const int32_t* ptr;
-    size_t len;
-} i32ArrayFfi;
-
-typedef struct f64ArrayFfiArrayFfi {
-    const double* ptr;
-    size_t len;
-} f64ArrayFfiArrayFfi;
+typedef struct f64ArrayArray {
+    double* values;
+    size_t length;
+    size_t capacity;
+} f64ArrayArray;
 
 typedef struct NSkin {
     const char* name;
-    i32ArrayFfi joints;
-    f64ArrayFfiArrayFfi inverse_bind_matrices;
-    Option skeleton_root;
+    i32Array joints;
+    f64ArrayArray inverse_bind_matrices;
+    const int32_t* skeleton_root;
 } NSkin;
 
-typedef struct NSkinArrayFfi {
-    const NSkin* ptr;
-    size_t len;
-} NSkinArrayFfi;
+typedef struct NSkinArray {
+    NSkin* values;
+    size_t length;
+    size_t capacity;
+} NSkinArray;
 
-typedef struct f64ArrayFfi {
-    const double* ptr;
-    size_t len;
-} f64ArrayFfi;
-
-typedef struct NChannelValues NChannelValues;// opaque
-
-typedef struct NAnimationInterpolation NAnimationInterpolation;// opaque
+typedef struct f64Array {
+    double* values;
+    size_t length;
+    size_t capacity;
+} f64Array;
 
 typedef struct NAnimationChannel {
     int32_t target_node;
-    f64ArrayFfi times;
+    f64Array times;
     NChannelValues values;
     NAnimationInterpolation interpolation;
 } NAnimationChannel;
 
-typedef struct NAnimationChannelArrayFfi {
-    const NAnimationChannel* ptr;
-    size_t len;
-} NAnimationChannelArrayFfi;
+typedef struct NAnimationChannelArray {
+    NAnimationChannel* values;
+    size_t length;
+    size_t capacity;
+} NAnimationChannelArray;
 
 typedef struct NAnimation {
     const char* name;
-    NAnimationChannelArrayFfi channels;
+    NAnimationChannelArray channels;
     float duration;
 } NAnimation;
 
-typedef struct NAnimationArrayFfi {
-    const NAnimation* ptr;
-    size_t len;
-} NAnimationArrayFfi;
+typedef struct NAnimationArray {
+    NAnimation* values;
+    size_t length;
+    size_t capacity;
+} NAnimationArray;
 
-typedef struct AssetKind AssetKind;// opaque
+typedef struct NColour {
+    uint8_t r;
+    uint8_t g;
+    uint8_t b;
+    uint8_t a;
+} NColour;
 
-typedef struct NQuaternion {
-    double x;
-    double y;
-    double z;
-    double w;
-} NQuaternion;
+typedef void* PhysicsStatePtr;
 
 typedef struct NNodeTransform {
     NVector3 translation;
@@ -134,15 +373,18 @@ typedef struct NNodeTransform {
 
 typedef struct NNode {
     const char* name;
-    Option parent;
-    i32ArrayFfi children;
+    const int32_t* parent;
+    i32Array children;
     NNodeTransform transform;
 } NNode;
 
-typedef struct NNodeArrayFfi {
-    const NNode* ptr;
-    size_t len;
-} NNodeArrayFfi;
+typedef struct NNodeArray {
+    NNode* values;
+    size_t length;
+    size_t capacity;
+} NNodeArray;
+
+typedef void* GraphicsContextPtr;
 
 typedef struct NModelVertex {
     NVector3 position;
@@ -151,35 +393,198 @@ typedef struct NModelVertex {
     NVector2 tex_coords0;
     NVector2 tex_coords1;
     NVector4 colour0;
-    i32ArrayFfi joints0;
+    i32Array joints0;
     NVector4 weights0;
 } NModelVertex;
 
-typedef struct NModelVertexArrayFfi {
-    const NModelVertex* ptr;
-    size_t len;
-} NModelVertexArrayFfi;
+typedef struct NModelVertexArray {
+    NModelVertex* values;
+    size_t length;
+    size_t capacity;
+} NModelVertexArray;
 
 typedef struct NMesh {
     const char* name;
     int32_t num_elements;
     int32_t material_index;
-    NModelVertexArrayFfi vertices;
+    NModelVertexArray vertices;
 } NMesh;
 
-typedef struct NMeshArrayFfi {
-    const NMesh* ptr;
-    size_t len;
-} NMeshArrayFfi;
+typedef struct NMeshArray {
+    NMesh* values;
+    size_t length;
+    size_t capacity;
+} NMeshArray;
+
+typedef void* WorldPtr;
 
+typedef struct RayHit {
+    NCollider collider;
+    double distance;
+} RayHit;
+
+typedef struct RigidBodyContext {
+    IndexNative index;
+    uint64_t entity_id;
+} RigidBodyContext;
+
+int32_t dropbear_gamepad_is_button_pressed(InputStatePtr input, uint64_t gamepad_id, int32_t button_ordinal, bool* out0);
+int32_t dropbear_gamepad_get_left_stick_position(InputStatePtr input, uint64_t gamepad_id, NVector2* out0);
+int32_t dropbear_gamepad_get_right_stick_position(InputStatePtr input, uint64_t gamepad_id, NVector2* out0);
+int32_t dropbear_collider_group_exists_for_entity(WorldPtr world, uint64_t entity, bool* out0);
+int32_t dropbear_collider_group_get_colliders(WorldPtr world, PhysicsStatePtr physics, uint64_t entity, NColliderArray* out0);
+int32_t dropbear_character_collision_get_character_collision_collider(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NCollider* out0);
+int32_t dropbear_character_collision_get_character_collision_position(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NTransform* out0);
+int32_t dropbear_character_collision_get_character_collision_translation_applied(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0);
+int32_t dropbear_character_collision_get_character_collision_translation_remaining(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0);
+int32_t dropbear_character_collision_get_character_collision_time_of_impact(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, double* out0);
+int32_t dropbear_character_collision_get_character_collision_witness1(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0);
+int32_t dropbear_character_collision_get_character_collision_witness2(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0);
+int32_t dropbear_character_collision_get_character_collision_normal1(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0);
+int32_t dropbear_character_collision_get_character_collision_normal2(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NVector3* out0);
+int32_t dropbear_character_collision_get_character_collision_status(WorldPtr world, uint64_t entity, const IndexNative* collision_handle, NShapeCastStatus* out0);
+int32_t dropbear_collider_get_collider_shape(PhysicsStatePtr physics, const NCollider* collider, ColliderShape* out0);
+int32_t dropbear_collider_set_collider_shape(PhysicsStatePtr physics, const NCollider* collider, const ColliderShape* shape);
+int32_t dropbear_collider_get_collider_density(PhysicsStatePtr physics, const NCollider* collider, double* out0);
+int32_t dropbear_collider_set_collider_density(PhysicsStatePtr physics, const NCollider* collider, double density);
+int32_t dropbear_collider_get_collider_friction(PhysicsStatePtr physics, const NCollider* collider, double* out0);
+int32_t dropbear_collider_set_collider_friction(PhysicsStatePtr physics, const NCollider* collider, double friction);
+int32_t dropbear_collider_get_collider_restitution(PhysicsStatePtr physics, const NCollider* collider, double* out0);
+int32_t dropbear_collider_set_collider_restitution(PhysicsStatePtr physics, const NCollider* collider, double restitution);
+int32_t dropbear_collider_get_collider_mass(PhysicsStatePtr physics, const NCollider* collider, double* out0);
+int32_t dropbear_collider_set_collider_mass(PhysicsStatePtr physics, const NCollider* collider, double mass);
+int32_t dropbear_collider_get_collider_is_sensor(PhysicsStatePtr physics, const NCollider* collider, bool* out0);
+int32_t dropbear_collider_set_collider_is_sensor(PhysicsStatePtr physics, const NCollider* collider, bool is_sensor);
+int32_t dropbear_collider_get_collider_translation(PhysicsStatePtr physics, const NCollider* collider, NVector3* out0);
+int32_t dropbear_collider_set_collider_translation(PhysicsStatePtr physics, const NCollider* collider, const NVector3* translation);
+int32_t dropbear_collider_get_collider_rotation(PhysicsStatePtr physics, const NCollider* collider, NVector3* out0);
+int32_t dropbear_collider_set_collider_rotation(PhysicsStatePtr physics, const NCollider* collider, const NVector3* rotation);
+int32_t dropbear_rigidbody_rigid_body_exists_for_entity(WorldPtr world, PhysicsStatePtr physics, uint64_t entity, IndexNative* out0, bool* out0_present);
+int32_t dropbear_rigidbody_get_rigidbody_mode(WorldPtr _world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, int32_t* out0);
+int32_t dropbear_rigidbody_set_rigidbody_mode(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, int32_t mode);
+int32_t dropbear_rigidbody_get_rigidbody_gravity_scale(WorldPtr _world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, double* out0);
+int32_t dropbear_rigidbody_set_rigidbody_gravity_scale(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, double gravity_scale);
+int32_t dropbear_rigidbody_get_rigidbody_linear_damping(WorldPtr _world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, double* out0);
+int32_t dropbear_rigidbody_set_rigidbody_linear_damping(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, double linear_damping);
+int32_t dropbear_rigidbody_get_rigidbody_angular_damping(WorldPtr _world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, double* out0);
+int32_t dropbear_rigidbody_set_rigidbody_angular_damping(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, double angular_damping);
+int32_t dropbear_rigidbody_get_rigidbody_sleep(WorldPtr _world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, bool* out0);
+int32_t dropbear_rigidbody_set_rigidbody_sleep(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, bool sleep);
+int32_t dropbear_rigidbody_get_rigidbody_ccd_enabled(WorldPtr _world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, bool* out0);
+int32_t dropbear_rigidbody_set_rigidbody_ccd_enabled(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, bool ccd_enabled);
+int32_t dropbear_rigidbody_get_rigidbody_linear_velocity(WorldPtr _world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, NVector3* out0);
+int32_t dropbear_rigidbody_set_rigidbody_linear_velocity(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, const NVector3* linear_velocity);
+int32_t dropbear_rigidbody_get_rigidbody_angular_velocity(WorldPtr _world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, NVector3* out0);
+int32_t dropbear_rigidbody_set_rigidbody_angular_velocity(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, const NVector3* angular_velocity);
+int32_t dropbear_rigidbody_get_rigidbody_lock_translation(WorldPtr _world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, AxisLock* out0);
+int32_t dropbear_rigidbody_set_rigidbody_lock_translation(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, const AxisLock* lock_translation);
+int32_t dropbear_rigidbody_get_rigidbody_lock_rotation(WorldPtr _world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, AxisLock* out0);
+int32_t dropbear_rigidbody_set_rigidbody_lock_rotation(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, const AxisLock* lock_rotation);
+int32_t dropbear_rigidbody_get_rigidbody_children(WorldPtr _world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, NColliderArray* out0);
+int32_t dropbear_rigidbody_apply_impulse(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, double x, double y, double z);
+int32_t dropbear_rigidbody_apply_torque_impulse(WorldPtr world, PhysicsStatePtr physics, const RigidBodyContext* rigidbody, double x, double y, double z);
+int32_t dropbear_kcc_kcc_exists_for_entity(WorldPtr world, uint64_t entity, bool* out0);
+int32_t dropbear_kcc_move_character(WorldPtr world, PhysicsStatePtr physics_state, uint64_t entity, const NVector3* translation, double delta_time);
+int32_t dropbear_kcc_set_rotation(WorldPtr world, PhysicsStatePtr physics_state, uint64_t entity, const NQuaternion* rotation);
+int32_t dropbear_kcc_get_hit(WorldPtr world, uint64_t entity, CharacterCollisionArray* out0);
 int32_t dropbear_scene_get_scene_load_progress(SceneLoaderPtr scene_loader, uint64_t scene_id, Progress* out0);
-int32_t dropbear_get_entity(WorldPtr world, const char* label, uint64_t* out0);
+int32_t dropbear_camera_exists_for_entity(WorldPtr world, uint64_t entity, bool* out0);
+int32_t dropbear_camera_get_eye(WorldPtr world, uint64_t entity, NVector3* out0);
+int32_t dropbear_camera_set_eye(WorldPtr world, uint64_t entity, const NVector3* eye);
+int32_t dropbear_camera_get_target(WorldPtr world, uint64_t entity, NVector3* out0);
+int32_t dropbear_camera_set_target(WorldPtr world, uint64_t entity, const NVector3* target);
+int32_t dropbear_camera_get_up(WorldPtr world, uint64_t entity, NVector3* out0);
+int32_t dropbear_camera_set_up(WorldPtr world, uint64_t entity, const NVector3* up);
+int32_t dropbear_camera_get_aspect(WorldPtr world, uint64_t entity, double* out0);
+int32_t dropbear_camera_get_fovy(WorldPtr world, uint64_t entity, double* out0);
+int32_t dropbear_camera_set_fovy(WorldPtr world, uint64_t entity, double fovy);
+int32_t dropbear_camera_get_znear(WorldPtr world, uint64_t entity, double* out0);
+int32_t dropbear_camera_set_znear(WorldPtr world, uint64_t entity, double znear);
+int32_t dropbear_camera_get_zfar(WorldPtr world, uint64_t entity, double* out0);
+int32_t dropbear_camera_set_zfar(WorldPtr world, uint64_t entity, double zfar);
+int32_t dropbear_camera_get_yaw(WorldPtr world, uint64_t entity, double* out0);
+int32_t dropbear_camera_set_yaw(WorldPtr world, uint64_t entity, double yaw);
+int32_t dropbear_camera_get_pitch(WorldPtr world, uint64_t entity, double* out0);
+int32_t dropbear_camera_set_pitch(WorldPtr world, uint64_t entity, double pitch);
+int32_t dropbear_camera_get_speed(WorldPtr world, uint64_t entity, double* out0);
+int32_t dropbear_camera_set_speed(WorldPtr world, uint64_t entity, double speed);
+int32_t dropbear_camera_get_sensitivity(WorldPtr world, uint64_t entity, double* out0);
+int32_t dropbear_camera_set_sensitivity(WorldPtr world, uint64_t entity, double sensitivity);
+int32_t dropbear_entity_label_exists_for_entity(WorldPtr world, uint64_t entity, bool* out0);
+int32_t dropbear_entity_get_label(WorldPtr world, uint64_t entity, char** out0);
+int32_t dropbear_entity_get_children(WorldPtr world, uint64_t entity, u64Array* out0);
+int32_t dropbear_entity_get_child_by_label(WorldPtr world, uint64_t entity, const char* target, uint64_t* out0, bool* out0_present);
+int32_t dropbear_entity_get_parent(WorldPtr world, uint64_t entity, uint64_t* out0, bool* out0_present);
+int32_t dropbear_input_print_input_state(InputStatePtr input);
+int32_t dropbear_input_is_key_pressed(InputStatePtr input, int32_t key_code, bool* out0);
+int32_t dropbear_input_get_mouse_position(InputStatePtr input, NVector2* out0);
+int32_t dropbear_input_is_mouse_button_pressed(InputStatePtr input, int32_t button_ordinal, bool* out0);
+int32_t dropbear_input_get_mouse_delta(InputStatePtr input, NVector2* out0);
+int32_t dropbear_input_is_cursor_locked(InputStatePtr input, bool* out0);
+int32_t dropbear_input_set_cursor_locked(CommandBufferPtr command_buffer, InputStatePtr input, bool locked);
+int32_t dropbear_input_get_last_mouse_pos(InputStatePtr input, NVector2* out0);
+int32_t dropbear_input_is_cursor_hidden(InputStatePtr input, bool* out0);
+int32_t dropbear_input_set_cursor_hidden(CommandBufferPtr command_buffer, InputStatePtr input, bool hidden);
+int32_t dropbear_input_get_connected_gamepads(InputStatePtr input, ConnectedGamepadIds* out0);
+int32_t dropbear_lighting_light_exists_for_entity(WorldPtr world, uint64_t entity, bool* out0);
+int32_t dropbear_lighting_get_position(WorldPtr world, uint64_t entity, NVector3* out0);
+int32_t dropbear_lighting_set_position(WorldPtr world, uint64_t entity, const NVector3* position);
+int32_t dropbear_lighting_get_direction(WorldPtr world, uint64_t entity, NVector3* out0);
+int32_t dropbear_lighting_set_direction(WorldPtr world, uint64_t entity, const NVector3* direction);
+int32_t dropbear_lighting_get_colour(WorldPtr world, uint64_t entity, NColour* out0);
+int32_t dropbear_lighting_set_colour(WorldPtr world, uint64_t entity, const NColour* colour);
+int32_t dropbear_lighting_get_light_type(WorldPtr world, uint64_t entity, int32_t* out0);
+int32_t dropbear_lighting_set_light_type(WorldPtr world, uint64_t entity, int32_t light_type);
+int32_t dropbear_lighting_get_intensity(WorldPtr world, uint64_t entity, double* out0);
+int32_t dropbear_lighting_set_intensity(WorldPtr world, uint64_t entity, double intensity);
+int32_t dropbear_lighting_get_attenuation(WorldPtr world, uint64_t entity, NAttenuation* out0);
+int32_t dropbear_lighting_set_attenuation(WorldPtr world, uint64_t entity, const NAttenuation* attenuation);
+int32_t dropbear_lighting_get_enabled(WorldPtr world, uint64_t entity, bool* out0);
+int32_t dropbear_lighting_set_enabled(WorldPtr world, uint64_t entity, bool enabled);
+int32_t dropbear_lighting_get_cutoff_angle(WorldPtr world, uint64_t entity, double* out0);
+int32_t dropbear_lighting_set_cutoff_angle(WorldPtr world, uint64_t entity, double cutoff_angle);
+int32_t dropbear_lighting_get_outer_cutoff_angle(WorldPtr world, uint64_t entity, double* out0);
+int32_t dropbear_lighting_set_outer_cutoff_angle(WorldPtr world, uint64_t entity, double outer_cutoff_angle);
+int32_t dropbear_lighting_get_casts_shadows(WorldPtr world, uint64_t entity, bool* out0);
+int32_t dropbear_lighting_set_casts_shadows(WorldPtr world, uint64_t entity, bool casts_shadows);
+int32_t dropbear_lighting_get_depth(WorldPtr world, uint64_t entity, NRange* out0);
+int32_t dropbear_lighting_set_depth(WorldPtr world, uint64_t entity, const NRange* depth);
+int32_t dropbear_physics_get_gravity(PhysicsStatePtr physics, NVector3* out0);
+int32_t dropbear_physics_set_gravity(PhysicsStatePtr physics, const NVector3* gravity);
+int32_t dropbear_physics_raycast(PhysicsStatePtr physics, const NVector3* origin, const NVector3* dir, double time_of_impact, bool solid, RayHit* out0, bool* out0_present);
+int32_t dropbear_physics_shape_cast(PhysicsStatePtr physics, const NVector3* origin, const NVector3* direction, const ColliderShape* shape, double time_of_impact, bool solid, NShapeCastHit* out0, bool* out0_present);
+int32_t dropbear_physics_is_overlapping(PhysicsStatePtr physics, const NCollider* collider1, const NCollider* collider2, bool* out0);
+int32_t dropbear_physics_is_triggering(PhysicsStatePtr physics, const NCollider* collider1, const NCollider* collider2, bool* out0);
+int32_t dropbear_physics_is_touching(PhysicsStatePtr physics, uint64_t entity1, uint64_t entity2, bool* out0);
+int32_t dropbear_properties_custom_properties_exists_for_entity(WorldPtr world, uint64_t entity, bool* out0);
+int32_t dropbear_properties_get_string_property(WorldPtr world, uint64_t entity, const char* key, char** out0, bool* out0_present);
+int32_t dropbear_properties_get_int_property(WorldPtr world, uint64_t entity, const char* key, int32_t* out0, bool* out0_present);
+int32_t dropbear_properties_get_long_property(WorldPtr world, uint64_t entity, const char* key, int64_t* out0, bool* out0_present);
+int32_t dropbear_properties_get_double_property(WorldPtr world, uint64_t entity, const char* key, double* out0, bool* out0_present);
+int32_t dropbear_properties_get_float_property(WorldPtr world, uint64_t entity, const char* key, float* out0, bool* out0_present);
+int32_t dropbear_properties_get_bool_property(WorldPtr world, uint64_t entity, const char* key, bool* out0, bool* out0_present);
+int32_t dropbear_properties_get_vec3_property(WorldPtr world, uint64_t entity, const char* key, NVector3* out0, bool* out0_present);
+int32_t dropbear_properties_set_string_property(WorldPtr world, uint64_t entity, const char* key, const char* value);
+int32_t dropbear_properties_set_int_property(WorldPtr world, uint64_t entity, const char* key, int32_t value);
+int32_t dropbear_properties_set_long_property(WorldPtr world, uint64_t entity, const char* key, int64_t value);
+int32_t dropbear_properties_set_double_property(WorldPtr world, uint64_t entity, const char* key, double value);
+int32_t dropbear_properties_set_float_property(WorldPtr world, uint64_t entity, const char* key, double value);
+int32_t dropbear_properties_set_bool_property(WorldPtr world, uint64_t entity, const char* key, bool value);
+int32_t dropbear_properties_set_vec3_property(WorldPtr world, uint64_t entity, const char* key, const NVector3* value);
+int32_t dropbear_engine_get_entity(WorldPtr world, const char* label, uint64_t* out0);
+int32_t dropbear_engine_quit(CommandBufferPtr command_buffer);
 int32_t dropbear_engine_get_asset(AssetRegistryPtr asset, const char* label, const AssetKind* kind, uint64_t* out0, bool* out0_present);
 int32_t dropbear_asset_model_get_label(AssetRegistryPtr asset, uint64_t model_handle, char** out0);
-int32_t dropbear_asset_model_get_meshes(AssetRegistryPtr asset, uint64_t model_handle, NMeshArrayFfi* out0);
-int32_t dropbear_asset_model_get_materials(AssetRegistryPtr asset, uint64_t model_handle, NMaterialArrayFfi* out0);
-int32_t dropbear_asset_model_get_skins(AssetRegistryPtr asset, uint64_t model_handle, NSkinArrayFfi* out0);
-int32_t dropbear_asset_model_get_animations(AssetRegistryPtr asset, uint64_t model_handle, NAnimationArrayFfi* out0);
-int32_t dropbear_asset_model_get_nodes(AssetRegistryPtr asset, uint64_t model_handle, NNodeArrayFfi* out0);
+int32_t dropbear_asset_model_get_meshes(AssetRegistryPtr asset, uint64_t model_handle, NMeshArray* out0);
+int32_t dropbear_asset_model_get_materials(AssetRegistryPtr asset, uint64_t model_handle, NMaterialArray* out0);
+int32_t dropbear_asset_model_get_skins(AssetRegistryPtr asset, uint64_t model_handle, NSkinArray* out0);
+int32_t dropbear_asset_model_get_animations(AssetRegistryPtr asset, uint64_t model_handle, NAnimationArray* out0);
+int32_t dropbear_asset_model_get_nodes(AssetRegistryPtr asset, uint64_t model_handle, NNodeArray* out0);
+int32_t dropbear_asset_texture_get_label(AssetRegistryPtr asset_manager, uint64_t texture_handle, char** out0, bool* out0_present);
+int32_t dropbear_asset_texture_get_width(AssetRegistryPtr asset_manager, uint64_t texture_handle, uint32_t* out0);
+int32_t dropbear_asset_texture_get_height(AssetRegistryPtr asset_manager, uint64_t texture_handle, uint32_t* out0);
+int32_t dropbear_mesh_get_texture(WorldPtr world, AssetRegistryPtr asset, uint64_t entity, const char* material_name, uint64_t* out0, bool* out0_present);
+int32_t dropbear_mesh_set_texture_override(WorldPtr world, AssetRegistryPtr asset, uint64_t entity, const char* material_name, uint64_t texture_handle);
+int32_t dropbear_mesh_set_material_tint(WorldPtr world, AssetRegistryPtr asset, GraphicsContextPtr graphics, uint64_t entity, const char* material_name, float r, float g, float b, float a);
 
 #endif /* DROPBEAR_H */
diff --git a/scripting/commonMain/kotlin/com/dropbear/asset/Texture.kt b/scripting/commonMain/kotlin/com/dropbear/asset/Texture.kt
index a6b7f70..f148ddf 100644
--- a/scripting/commonMain/kotlin/com/dropbear/asset/Texture.kt
+++ b/scripting/commonMain/kotlin/com/dropbear/asset/Texture.kt
@@ -1,22 +1,16 @@
 package com.dropbear.asset
 
 class Texture(override val id: Long): AssetType(id) {
-    var label: String?
+    val label: String?
         get() = getLabel()
-        set(value) = setLabel(value)
 
     val width: Int
         get() = getWidth()
 
     val height: Int
         get() = getHeight()
-
-    val depth: Int
-        get() = getDepth()
 }
 
 expect fun Texture.getLabel(): String?
-expect fun Texture.setLabel(value: String?)
 expect fun Texture.getWidth(): Int
-expect fun Texture.getHeight(): Int
-expect fun Texture.getDepth(): Int
\ No newline at end of file
+expect fun Texture.getHeight(): Int
\ No newline at end of file
diff --git a/scripting/commonMain/kotlin/com/dropbear/asset/model/Animation.kt b/scripting/commonMain/kotlin/com/dropbear/asset/model/Animation.kt
index fe9649a..30914ba 100644
--- a/scripting/commonMain/kotlin/com/dropbear/asset/model/Animation.kt
+++ b/scripting/commonMain/kotlin/com/dropbear/asset/model/Animation.kt
@@ -6,7 +6,7 @@ import com.dropbear.math.Quaternionf
 data class Animation(
     val name: String,
     val channels: List<AnimationChannel>,
-    val duration: Float
+    val duration: Double
 )
 
 data class AnimationChannel(
diff --git a/scripting/commonMain/kotlin/com/dropbear/asset/model/Material.kt b/scripting/commonMain/kotlin/com/dropbear/asset/model/Material.kt
index 9e1c9ce..e7faf17 100644
--- a/scripting/commonMain/kotlin/com/dropbear/asset/model/Material.kt
+++ b/scripting/commonMain/kotlin/com/dropbear/asset/model/Material.kt
@@ -1,23 +1,23 @@
 package com.dropbear.asset.model
 
 import com.dropbear.asset.Texture
-import com.dropbear.math.Vector2f
-import com.dropbear.math.Vector3f
-import com.dropbear.math.Vector4f
+import com.dropbear.math.Vector2d
+import com.dropbear.math.Vector3d
+import com.dropbear.math.Vector4d
 
 data class Material(
     val name: String,
     val diffuseTexture: Texture,
     val normalTexture: Texture,
-    val tint: Vector4f,
-    val emissiveFactor: Vector3f,
-    val metallicFactor: Float,
-    val roughnessFactor: Float,
-    val alphaCutoff: Float?,
+    val tint: Vector4d,
+    val emissiveFactor: Vector3d,
+    val metallicFactor: Double,
+    val roughnessFactor: Double,
+    val alphaCutoff: Double?,
     val doubleSided: Boolean,
-    val occlusionStrength: Float,
-    val normalScale: Float,
-    val uvTiling: Vector2f,
+    val occlusionStrength: Double,
+    val normalScale: Double,
+    val uvTiling: Vector2d,
     val emissiveTexture: Texture?,
     val metallicRoughnessTexture: Texture?,
     val occlusionTexture: Texture?
diff --git a/scripting/commonMain/kotlin/com/dropbear/input/Gamepad.kt b/scripting/commonMain/kotlin/com/dropbear/input/Gamepad.kt
index e41eabc..e9ffe1d 100644
--- a/scripting/commonMain/kotlin/com/dropbear/input/Gamepad.kt
+++ b/scripting/commonMain/kotlin/com/dropbear/input/Gamepad.kt
@@ -13,20 +13,20 @@ class Gamepad(
      * The position of the left stick.
      */
     val leftStickPosition: Vector2d
-        get() = getLeftStickPosition(id)
+        get() = getLeftStickPosition()
 
     /**
      * The position of the right stick.
      */
     val rightStickPosition: Vector2d
-        get() = getRightStickPosition(id)
+        get() = getRightStickPosition()
 
     /**
      * Queries if a button is pressed, and returns either `true` if pressed
      * or `false` if not.
      */
     fun isButtonPressed(button: GamepadButton): Boolean {
-        return isGamepadButtonPressed(id, button)
+        return isGamepadButtonPressed(button)
     }
 
     override fun toString(): String {
@@ -34,6 +34,6 @@ class Gamepad(
     }
 }
 
-internal expect fun Gamepad.isGamepadButtonPressed(id: Long, button: GamepadButton): Boolean
-internal expect fun Gamepad.getLeftStickPosition(id: Long): Vector2d
-internal expect fun Gamepad.getRightStickPosition(id: Long): Vector2d
\ No newline at end of file
+internal expect fun Gamepad.isGamepadButtonPressed(button: GamepadButton): Boolean
+internal expect fun Gamepad.getLeftStickPosition(): Vector2d
+internal expect fun Gamepad.getRightStickPosition(): Vector2d
\ No newline at end of file
diff --git a/scripting/jvmMain/java/com/dropbear/asset/TextureNative.java b/scripting/jvmMain/java/com/dropbear/asset/TextureNative.java
new file mode 100644
index 0000000..00f4dc6
--- /dev/null
+++ b/scripting/jvmMain/java/com/dropbear/asset/TextureNative.java
@@ -0,0 +1,13 @@
+package com.dropbear.asset;
+
+import com.dropbear.EucalyptusCoreLoader;
+
+public class TextureNative {
+    static {
+        new EucalyptusCoreLoader().ensureLoaded();
+    }
+    
+    public static native String getLabel(long assetManagerHandle, long textureHandle);
+    public static native int getWidth(long assetManagerHandle, long textureHandle);
+    public static native int getHeight(long assetManagerHandle, long textureHandle);
+}
diff --git a/scripting/jvmMain/java/com/dropbear/components/MeshRendererNative.java b/scripting/jvmMain/java/com/dropbear/components/MeshRendererNative.java
index 4bc8052..3b438a4 100644
--- a/scripting/jvmMain/java/com/dropbear/components/MeshRendererNative.java
+++ b/scripting/jvmMain/java/com/dropbear/components/MeshRendererNative.java
@@ -14,4 +14,5 @@ public class MeshRendererNative {
     public static native long[] getAllTextureIds(long worldHandle, long assetRegistryHandle, long entityId);
     public static native Long getTexture(long worldHandle, long assetRegistryHandle, long entityId, String materialName);
     public static native void setTextureOverride(long worldHandle, long assetRegistryHandle, long entityId, String meshName, long textureHandle);
+    public static native void setMaterialTint(long worldHandle, long assetRegistryHandle, long graphicsContextHandle, long entityId, String materialName, float r, float g, float b, float a);
 }
\ No newline at end of file
diff --git a/scripting/jvmMain/java/com/dropbear/input/InputStateNative.java b/scripting/jvmMain/java/com/dropbear/input/InputStateNative.java
index 1f47bc3..bce8689 100644
--- a/scripting/jvmMain/java/com/dropbear/input/InputStateNative.java
+++ b/scripting/jvmMain/java/com/dropbear/input/InputStateNative.java
@@ -11,7 +11,7 @@ public class InputStateNative {
     public static native void printInputState(long inputStateHandle);
     public static native boolean isKeyPressed(long inputStateHandle, int keyCode);
     public static native Vector2d getMousePosition(long inputStateHandle);
-    public static native boolean isMouseButtonPressed(long inputStateHandle, MouseButton mouseButton);
+    public static native boolean isMouseButtonPressed(long inputStateHandle, int mouseButtonOrdinal);
     public static native Vector2d getMouseDelta(long inputStateHandle);
     public static native boolean isCursorLocked(long inputStateHandle);
     public static native void setCursorLocked(long commandBufferPtr, long inputStateHandle, boolean locked);
diff --git a/scripting/jvmMain/java/com/dropbear/physics/ColliderGroupNative.java b/scripting/jvmMain/java/com/dropbear/physics/ColliderGroupNative.java
index faa5998..59b05be 100644
--- a/scripting/jvmMain/java/com/dropbear/physics/ColliderGroupNative.java
+++ b/scripting/jvmMain/java/com/dropbear/physics/ColliderGroupNative.java
@@ -7,6 +7,6 @@ public class ColliderGroupNative {
         new EucalyptusCoreLoader().ensureLoaded();
     }
 
-    public static native Collider[] getColliderGroupColliders(long worldPtr, long physicsPtr, long entityId);
     public static native boolean colliderGroupExistsForEntity(long worldPtr, long entityId);
+    public static native Collider[] getColliderGroupColliders(long worldPtr, long physicsPtr, long entityId);
 }
\ No newline at end of file
diff --git a/scripting/jvmMain/java/com/dropbear/physics/KinematicCharacterControllerNative.java b/scripting/jvmMain/java/com/dropbear/physics/KinematicCharacterControllerNative.java
index cccdaa1..9d2ea7f 100644
--- a/scripting/jvmMain/java/com/dropbear/physics/KinematicCharacterControllerNative.java
+++ b/scripting/jvmMain/java/com/dropbear/physics/KinematicCharacterControllerNative.java
@@ -14,5 +14,5 @@ public class KinematicCharacterControllerNative {
 
     public static native void moveCharacter(long worldHandle, long physicsHandle, long entityHandle, Vector3d translation, double deltaTime);
     public static native void setRotation(long worldHandle, long physicsHandle, long entityHandle, Quaterniond rotation);
-    public static native CharacterCollision[] getHitNative(long worldHandle, long entity);
+    public static native CharacterCollision[] getHit(long worldHandle, long entity);
 }
diff --git a/scripting/jvmMain/kotlin/com/dropbear/asset/Texture.jvm.kt b/scripting/jvmMain/kotlin/com/dropbear/asset/Texture.jvm.kt
index 49c60b9..44ccc23 100644
--- a/scripting/jvmMain/kotlin/com/dropbear/asset/Texture.jvm.kt
+++ b/scripting/jvmMain/kotlin/com/dropbear/asset/Texture.jvm.kt
@@ -1,21 +1,15 @@
 package com.dropbear.asset
 
-actual fun Texture.getLabel(): String? {
-    TODO("Not yet implemented")
-}
+import com.dropbear.DropbearEngine
 
-actual fun Texture.setLabel(value: String?) {
-    TODO("Not yet implemented")
+actual fun Texture.getLabel(): String? {
+    return TextureNative.getLabel(DropbearEngine.native.assetHandle, id)
 }
 
 actual fun Texture.getWidth(): Int {
-    TODO("Not yet implemented")
+    return TextureNative.getWidth(DropbearEngine.native.assetHandle, id)
 }
 
 actual fun Texture.getHeight(): Int {
-    TODO("Not yet implemented")
-}
-
-actual fun Texture.getDepth(): Int {
-    TODO("Not yet implemented")
+    return TextureNative.getHeight(DropbearEngine.native.assetHandle, id)
 }
diff --git a/scripting/jvmMain/kotlin/com/dropbear/ffi/DropbearContext.kt b/scripting/jvmMain/kotlin/com/dropbear/ffi/DropbearContext.kt
index 4ed40fd..7ad1a9b 100644
--- a/scripting/jvmMain/kotlin/com/dropbear/ffi/DropbearContext.kt
+++ b/scripting/jvmMain/kotlin/com/dropbear/ffi/DropbearContext.kt
@@ -7,6 +7,7 @@ class DropbearContext(
     val worldHandle: Long,
     val inputHandle: Long,
     val commandBufferHandle: Long,
+    val graphicsContextHandle: Long,
     val assetHandle: Long,
     val sceneLoaderHandle: Long,
     val physicsEngineHandle: Long,
diff --git a/scripting/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt b/scripting/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
index 5acd8f6..ced1bc4 100644
--- a/scripting/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
+++ b/scripting/jvmMain/kotlin/com/dropbear/ffi/NativeEngine.jvm.kt
@@ -6,6 +6,7 @@ actual class NativeEngine {
     internal var worldHandle: Long = 0L
     internal var inputHandle: Long = 0L
     internal var commandBufferHandle: Long = 0L
+    internal var graphicsContextHandle: Long = 0L
     internal var assetHandle: Long = 0L
     internal var sceneLoaderHandle: Long = 0L
     internal var physicsEngineHandle: Long = 0L
@@ -16,6 +17,7 @@ actual class NativeEngine {
         this.worldHandle = ctx.worldHandle
         this.inputHandle = ctx.inputHandle
         this.commandBufferHandle = ctx.commandBufferHandle
+        this.graphicsContextHandle = ctx.graphicsContextHandle
         this.assetHandle = ctx.assetHandle
         this.sceneLoaderHandle = ctx.sceneLoaderHandle
         this.physicsEngineHandle = ctx.physicsEngineHandle
@@ -33,6 +35,10 @@ actual class NativeEngine {
             Logger.error("NativeEngine: Error - Invalid graphics handle received!")
             return
         }
+        if (this.graphicsContextHandle <= 0L) {
+            Logger.error("NativeEngine: Error - Invalid graphics context handle received!")
+            return
+        }
         if (this.assetHandle <= 0L) {
             Logger.error("NativeEngine: Error - Invalid asset handle received!")
             return
diff --git a/scripting/jvmMain/kotlin/com/dropbear/input/Gamepad.jvm.kt b/scripting/jvmMain/kotlin/com/dropbear/input/Gamepad.jvm.kt
index 9d23822..782257a 100644
--- a/scripting/jvmMain/kotlin/com/dropbear/input/Gamepad.jvm.kt
+++ b/scripting/jvmMain/kotlin/com/dropbear/input/Gamepad.jvm.kt
@@ -4,7 +4,6 @@ import com.dropbear.DropbearEngine
 import com.dropbear.math.Vector2d
 
 internal actual fun Gamepad.isGamepadButtonPressed(
-    id: Long,
     button: GamepadButton
 ): Boolean {
     return GamepadNative.isGamepadButtonPressed(
@@ -15,7 +14,7 @@ internal actual fun Gamepad.isGamepadButtonPressed(
 }
 
 @Suppress("UNCHECKED_CAST")
-internal actual fun Gamepad.getLeftStickPosition(id: Long): Vector2d {
+internal actual fun Gamepad.getLeftStickPosition(): Vector2d {
     val result = GamepadNative.getLeftStickPosition(
         DropbearEngine.native.inputHandle,
         id
@@ -25,7 +24,7 @@ internal actual fun Gamepad.getLeftStickPosition(id: Long): Vector2d {
 }
 
 @Suppress("UNCHECKED_CAST")
-internal actual fun Gamepad.getRightStickPosition(id: Long): Vector2d {
+internal actual fun Gamepad.getRightStickPosition(): Vector2d {
     val result = GamepadNative.getRightStickPosition(
         DropbearEngine.native.inputHandle,
         id
diff --git a/scripting/jvmMain/kotlin/com/dropbear/input/InputState.jvm.kt b/scripting/jvmMain/kotlin/com/dropbear/input/InputState.jvm.kt
index 30dffa8..bc348aa 100644
--- a/scripting/jvmMain/kotlin/com/dropbear/input/InputState.jvm.kt
+++ b/scripting/jvmMain/kotlin/com/dropbear/input/InputState.jvm.kt
@@ -17,7 +17,7 @@ actual class InputState actual constructor() {
     }
 
     actual fun isMouseButtonPressed(button: MouseButton): Boolean {
-        return InputStateNative.isMouseButtonPressed(DropbearEngine.native.inputHandle, button)
+        return InputStateNative.isMouseButtonPressed(DropbearEngine.native.inputHandle, button.ordinal)
     }
 
     actual fun getMouseDelta(): Vector2d {
diff --git a/scripting/nativeMain/kotlin/com/dropbear/asset/Texture.native.kt b/scripting/nativeMain/kotlin/com/dropbear/asset/Texture.native.kt
index 49c60b9..3a9d161 100644
--- a/scripting/nativeMain/kotlin/com/dropbear/asset/Texture.native.kt
+++ b/scripting/nativeMain/kotlin/com/dropbear/asset/Texture.native.kt
@@ -4,10 +4,6 @@ actual fun Texture.getLabel(): String? {
     TODO("Not yet implemented")
 }
 
-actual fun Texture.setLabel(value: String?) {
-    TODO("Not yet implemented")
-}
-
 actual fun Texture.getWidth(): Int {
     TODO("Not yet implemented")
 }
@@ -15,7 +11,3 @@ actual fun Texture.getWidth(): Int {
 actual fun Texture.getHeight(): Int {
     TODO("Not yet implemented")
 }
-
-actual fun Texture.getDepth(): Int {
-    TODO("Not yet implemented")
-}
diff --git a/scripting/nativeMain/kotlin/com/dropbear/ffi/generated/DropbearContext.kt b/scripting/nativeMain/kotlin/com/dropbear/ffi/generated/DropbearContext.kt
index 6875c38..3904dab 100644
--- a/scripting/nativeMain/kotlin/com/dropbear/ffi/generated/DropbearContext.kt
+++ b/scripting/nativeMain/kotlin/com/dropbear/ffi/generated/DropbearContext.kt
@@ -13,7 +13,9 @@ data class DropbearContext(
     val world: NativeHandle?,
     val input: NativeHandle?,
     val graphics: NativeHandle?,
+    val graphics_context: NativeHandle?,
     val assets: NativeHandle?,
     val scene_loader: NativeHandle?,
-    val physics_engine: NativeHandle?
+    val physics_engine: NativeHandle?,
+    val ui_buf: NativeHandle?
 )
diff --git a/scripting/nativeMain/kotlin/com/dropbear/input/Gamepad.native.kt b/scripting/nativeMain/kotlin/com/dropbear/input/Gamepad.native.kt
index d9d6677..6e21850 100644
--- a/scripting/nativeMain/kotlin/com/dropbear/input/Gamepad.native.kt
+++ b/scripting/nativeMain/kotlin/com/dropbear/input/Gamepad.native.kt
@@ -3,17 +3,16 @@ package com.dropbear.input
 import com.dropbear.math.Vector2d
 
 internal actual fun Gamepad.isGamepadButtonPressed(
-    id: Long,
     button: GamepadButton
 ): Boolean {
     TODO("Not yet implemented")
 }
 
-internal actual fun Gamepad.getLeftStickPosition(id: Long): Vector2d {
+internal actual fun Gamepad.getLeftStickPosition(): Vector2d {
     TODO("Not yet implemented")
 }
 
-internal actual fun Gamepad.getRightStickPosition(id: Long): Vector2d {
+internal actual fun Gamepad.getRightStickPosition(): Vector2d {
     TODO("Not yet implemented")
 }