From 0dce8bef80322afcce77abcf8323e9941c3281bc Mon Sep 17 00:00:00 2001 From: karroffel Date: Fri, 30 Mar 2018 18:17:09 +0200 Subject: [PATCH] WIP --- Cargo.toml | 3 +- examples/low_level_registering/Cargo.toml | 10 + examples/low_level_registering/Main.tscn | 28 +++ .../low_level_registering/default_env.tres | 101 ++++++++++ examples/low_level_registering/icon.png | Bin 0 -> 3498 bytes .../low_level_registering/icon.png.import | 29 +++ .../low_level_registering_library.gdnlib | 14 ++ examples/low_level_registering/project.godot | 19 ++ examples/low_level_registering/src/lib.rs | 78 ++++++++ gdnative/src/class.rs | 183 +++++++++++++++++- gdnative/src/macros.rs | 166 ++++++++++++++++ 11 files changed, 629 insertions(+), 2 deletions(-) create mode 100644 examples/low_level_registering/Cargo.toml create mode 100644 examples/low_level_registering/Main.tscn create mode 100644 examples/low_level_registering/default_env.tres create mode 100644 examples/low_level_registering/icon.png create mode 100644 examples/low_level_registering/icon.png.import create mode 100644 examples/low_level_registering/low_level_registering_library.gdnlib create mode 100644 examples/low_level_registering/project.godot create mode 100644 examples/low_level_registering/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 15e1686ec..f62a65d0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,6 @@ members = [ "geom", "test", "examples/hello_world", - "examples/misc_mesh_playground" + "examples/misc_mesh_playground", + "examples/low_level_registering", ] diff --git a/examples/low_level_registering/Cargo.toml b/examples/low_level_registering/Cargo.toml new file mode 100644 index 000000000..aaca60342 --- /dev/null +++ b/examples/low_level_registering/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "low_level_registering" +version = "0.1.0" +authors = ["karroffel "] + +[lib] +crate-type = ["cdylib"] + +[dependencies] +gdnative = { path = "../../gdnative" } diff --git a/examples/low_level_registering/Main.tscn b/examples/low_level_registering/Main.tscn new file mode 100644 index 000000000..116dbccc7 --- /dev/null +++ b/examples/low_level_registering/Main.tscn @@ -0,0 +1,28 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://low_level_registering_library.gdnlib" type="GDNativeLibrary" id=1] + +[sub_resource type="NativeScript" id=1] + +resource_name = "Test" +class_name = "Test" +library = ExtResource( 1 ) + +[sub_resource type="GDScript" id=2] + +script/source = "extends Node + +func _ready(): + yield(get_tree().create_timer(1.0), \"timeout\") + $\"..\".queue_free() +" + +[node name="Node" type="Node" index="0"] + +script = SubResource( 1 ) + +[node name="Node" type="Node" parent="." index="0"] + +script = SubResource( 2 ) + + diff --git a/examples/low_level_registering/default_env.tres b/examples/low_level_registering/default_env.tres new file mode 100644 index 000000000..ad86b722a --- /dev/null +++ b/examples/low_level_registering/default_env.tres @@ -0,0 +1,101 @@ +[gd_resource type="Environment" load_steps=2 format=2] + +[sub_resource type="ProceduralSky" id=1] + +radiance_size = 4 +sky_top_color = Color( 0.0470588, 0.454902, 0.976471, 1 ) +sky_horizon_color = Color( 0.556863, 0.823529, 0.909804, 1 ) +sky_curve = 0.25 +sky_energy = 1.0 +ground_bottom_color = Color( 0.101961, 0.145098, 0.188235, 1 ) +ground_horizon_color = Color( 0.482353, 0.788235, 0.952941, 1 ) +ground_curve = 0.01 +ground_energy = 1.0 +sun_color = Color( 1, 1, 1, 1 ) +sun_latitude = 35.0 +sun_longitude = 0.0 +sun_angle_min = 1.0 +sun_angle_max = 100.0 +sun_curve = 0.05 +sun_energy = 16.0 +texture_size = 2 + +[resource] + +background_mode = 2 +background_sky = SubResource( 1 ) +background_sky_custom_fov = 0.0 +background_color = Color( 0, 0, 0, 1 ) +background_energy = 1.0 +background_canvas_max_layer = 0 +ambient_light_color = Color( 0, 0, 0, 1 ) +ambient_light_energy = 1.0 +ambient_light_sky_contribution = 1.0 +fog_enabled = false +fog_color = Color( 0.5, 0.6, 0.7, 1 ) +fog_sun_color = Color( 1, 0.9, 0.7, 1 ) +fog_sun_amount = 0.0 +fog_depth_enabled = true +fog_depth_begin = 10.0 +fog_depth_curve = 1.0 +fog_transmit_enabled = false +fog_transmit_curve = 1.0 +fog_height_enabled = false +fog_height_min = 0.0 +fog_height_max = 100.0 +fog_height_curve = 1.0 +tonemap_mode = 0 +tonemap_exposure = 1.0 +tonemap_white = 1.0 +auto_exposure_enabled = false +auto_exposure_scale = 0.4 +auto_exposure_min_luma = 0.05 +auto_exposure_max_luma = 8.0 +auto_exposure_speed = 0.5 +ss_reflections_enabled = false +ss_reflections_max_steps = 64 +ss_reflections_fade_in = 0.15 +ss_reflections_fade_out = 2.0 +ss_reflections_depth_tolerance = 0.2 +ss_reflections_roughness = true +ssao_enabled = false +ssao_radius = 1.0 +ssao_intensity = 1.0 +ssao_radius2 = 0.0 +ssao_intensity2 = 1.0 +ssao_bias = 0.01 +ssao_light_affect = 0.0 +ssao_color = Color( 0, 0, 0, 1 ) +ssao_quality = 0 +ssao_blur = 3 +ssao_edge_sharpness = 4.0 +dof_blur_far_enabled = false +dof_blur_far_distance = 10.0 +dof_blur_far_transition = 5.0 +dof_blur_far_amount = 0.1 +dof_blur_far_quality = 1 +dof_blur_near_enabled = false +dof_blur_near_distance = 2.0 +dof_blur_near_transition = 1.0 +dof_blur_near_amount = 0.1 +dof_blur_near_quality = 1 +glow_enabled = false +glow_levels/1 = false +glow_levels/2 = false +glow_levels/3 = true +glow_levels/4 = false +glow_levels/5 = true +glow_levels/6 = false +glow_levels/7 = false +glow_intensity = 0.8 +glow_strength = 1.0 +glow_bloom = 0.0 +glow_blend_mode = 2 +glow_hdr_threshold = 1.0 +glow_hdr_scale = 2.0 +glow_bicubic_upscale = false +adjustment_enabled = false +adjustment_brightness = 1.0 +adjustment_contrast = 1.0 +adjustment_saturation = 1.0 + diff --git a/examples/low_level_registering/icon.png b/examples/low_level_registering/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a0b64eee944cb4cfb2aa8dc0d8d78faf6ca44dc1 GIT binary patch literal 3498 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 Y|LV|6es%hBO8@`>07*qoM6N<$f`}~Z-v9sr literal 0 HcmV?d00001 diff --git a/examples/low_level_registering/icon.png.import b/examples/low_level_registering/icon.png.import new file mode 100644 index 000000000..0041ef86c --- /dev/null +++ b/examples/low_level_registering/icon.png.import @@ -0,0 +1,29 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" + +[deps] + +source_file="res://icon.png" +dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/examples/low_level_registering/low_level_registering_library.gdnlib b/examples/low_level_registering/low_level_registering_library.gdnlib new file mode 100644 index 000000000..34fb1143e --- /dev/null +++ b/examples/low_level_registering/low_level_registering_library.gdnlib @@ -0,0 +1,14 @@ +[entry] + +X11.64="../../target/debug/liblow_level_registering.so" + +[dependencies] + +X11.64=[ ] + +[general] + +singleton=false +load_once=true +symbol_prefix="godot_rust_" +reloadable=true diff --git a/examples/low_level_registering/project.godot b/examples/low_level_registering/project.godot new file mode 100644 index 000000000..39265cf02 --- /dev/null +++ b/examples/low_level_registering/project.godot @@ -0,0 +1,19 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=3 + +[application] + +config/name="Godot Rust - Low Level Registering" +run/main_scene="res://Main.tscn" +config/icon="res://icon.png" + +[rendering] + +environment/default_environment="res://default_env.tres" diff --git a/examples/low_level_registering/src/lib.rs b/examples/low_level_registering/src/lib.rs new file mode 100644 index 000000000..e92123ca7 --- /dev/null +++ b/examples/low_level_registering/src/lib.rs @@ -0,0 +1,78 @@ +#[macro_use] +extern crate gdnative as godot; + +use godot::*; + +struct MyClass { + class_info: GodotClassInfo, + + elapsed_time: f64, +} + +impl MyClass { + fn new(info: GodotClassInfo) -> Self { + MyClass { + class_info: info, + elapsed_time: 0.0, + } + } + + fn _ready(&mut self) { + godot_print!("Hello World!"); + } + + fn _process(&mut self, delta: f64) { + self.elapsed_time += delta; + } + + fn _exit_tree(&mut self) { + godot_print!("MyClass node was running for {} seconds", + self.elapsed_time); + } +} + +godot_gdnative_init!(godot_rust_gdnative_init); + +godot_gdnative_terminate!(godot_rust_gdnative_terminate); + +godot_nativescript_init! { + godot_rust_nativescript_init, + |handle| { + + let constructor = godot_create_constructor!(MyClass, MyClass::new); + let destructor = godot_create_destructor!(MyClass); + + let ready_method = godot_create_method!(MyClass, + fn _ready(&mut self) -> ()); + + let ready_method = GodotScriptMethod { + name: "_ready", + method_ptr: Some(ready_method), + attributes: GodotScriptMethodAttributes { + rpc_mode: GodotRpcMode::Disabled + }, + method_data: std::ptr::null_mut(), + free_func: None + }; + + let process_method = godot_create_method!(MyClass, + fn _process(&mut self, delta: f64) -> ()); + + let exit_tree_method = godot_create_method!(MyClass, + fn _exit_tree(&mut self) -> ()); + + let builder = GodotScriptClassBuilder::new(); + builder + .set_class_name("Test") + .set_base_class_name("Node") + .set_constructor(Some(constructor)) + .set_destructor(Some(destructor)) + + .add_method_advanced(ready_method) + + .add_method("_process", process_method) + .add_method("_exit_tree", exit_tree_method) + + .build(handle); + } +} \ No newline at end of file diff --git a/gdnative/src/class.rs b/gdnative/src/class.rs index 37191ef22..892e3e04c 100644 --- a/gdnative/src/class.rs +++ b/gdnative/src/class.rs @@ -1,7 +1,9 @@ use libc; use sys; +use std::ffi::CString; +use std::ptr; use std::ops::Deref; -use GodotString; +use super::*; #[macro_export] #[doc(hidden)] @@ -447,5 +449,184 @@ impl Drop for GodotRef { (::get_api().godot_object_destroy)(self.this); } } + } +} + + + +// Class builder + +pub type GodotScriptMethodType = unsafe extern "C" fn( + *mut sys::godot_object, + *mut libc::c_void, + *mut libc::c_void, + libc::c_int, + *mut *mut sys::godot_variant +) -> sys::godot_variant; + +pub type GodotScriptConstructorType = unsafe extern "C" fn( + *mut sys::godot_object, + *mut libc::c_void +) -> *mut libc::c_void; + +pub type GodotScriptDestructorType = unsafe extern "C" fn( + *mut sys::godot_object, + *mut libc::c_void, + *mut libc::c_void +) -> (); + +pub enum GodotRpcMode { + Disabled, + Remote, + Sync, + Mater, + Slave +} + +pub struct GodotScriptMethodAttributes { + pub rpc_mode: GodotRpcMode +} + +pub struct GodotScriptMethod<'l> { + pub name: &'l str, + pub method_ptr: Option, + pub attributes: GodotScriptMethodAttributes, + + pub method_data: *mut libc::c_void, + pub free_func: Option ()>, +} + +pub struct GodotScriptClassBuilder<'l> { + + is_tool: bool, + + class_name: &'l str, + base_class_name: &'l str, + + constructor: Option, + destructor: Option, + + methods: Vec>, + properties: Vec<(String)>, + signals: Vec<(String)>, + +} + +impl<'l> GodotScriptClassBuilder<'l> { + pub fn new() -> Self { + GodotScriptClassBuilder { + is_tool: false, + + class_name: "", + base_class_name: "", + + constructor: None, + destructor: None, + + methods: vec![], + properties: vec![], + signals: vec![], + } + } + + pub fn set_tool(mut self, is_tool: bool) -> Self { + self.is_tool = is_tool; + self + } + + pub fn set_class_name(mut self, name: &'l str) -> Self { + self.class_name = name; + self + } + + pub fn set_base_class_name(mut self, name: &'l str) -> Self { + self.base_class_name = name; + self + } + + pub fn add_method_advanced(mut self, method: GodotScriptMethod<'l>) -> Self { + self.methods.push(method); + self + } + + pub fn add_method(mut self, name: &'l str, method: GodotScriptMethodType) -> Self { + let method = GodotScriptMethod { + name: name, + method_ptr: Some(method), + attributes: GodotScriptMethodAttributes { + rpc_mode: GodotRpcMode::Disabled + }, + method_data: std::ptr::null_mut(), + free_func: None + }; + self.methods.push(method); + self + } + + pub fn set_constructor(mut self, constructor: Option) -> Self { + self.constructor = constructor; + self + } + + pub fn set_destructor(mut self, destructor: Option) -> Self { + self.destructor = destructor; + self + } + + pub fn build(&mut self, handle: *mut libc::c_void) { + let api = get_api(); + + // register class + + unsafe { + let name = CString::new(self.class_name).unwrap(); + let base_name = CString::new(self.base_class_name).unwrap(); + + let create = sys::godot_instance_create_func { + create_func: self.constructor, + method_data: ptr::null_mut(), + free_func: None, + }; + + let destroy = sys::godot_instance_destroy_func { + destroy_func: self.destructor, + method_data: ptr::null_mut(), + free_func: None, + }; + + (api.godot_nativescript_register_class)( + handle as *mut _, + name.as_ptr() as *const _, + base_name.as_ptr() as *const _, + create, + destroy + ); + + + // register methods + for method in self.methods.iter() { + let method_name = CString::new(method.name).unwrap(); + + let attr = sys::godot_method_attributes { + rpc_type: sys::godot_method_rpc_mode::GODOT_METHOD_RPC_MODE_DISABLED + }; + + let method_desc = sys::godot_instance_method { + method: method.method_ptr, + method_data: method.method_data, + free_func: method.free_func + }; + + (api.godot_nativescript_register_method)( + handle as *mut _, + name.as_ptr() as *const _, + method_name.as_ptr() as *const _, + attr, + method_desc + ); + } + } + + } } \ No newline at end of file diff --git a/gdnative/src/macros.rs b/gdnative/src/macros.rs index da7abcc07..d38948dbb 100644 --- a/gdnative/src/macros.rs +++ b/gdnative/src/macros.rs @@ -1,5 +1,56 @@ #![macro_use] + +#[macro_export] +macro_rules! godot_gdnative_init { + ($name:ident) => { + godot_gdnative_init!($name, |_| {}); + }; + ($name:ident, $fun:expr) => { + #[no_mangle] + #[doc(hidden)] + pub extern "C" fn $name(options: *mut $crate::sys::godot_gdnative_init_options) { + unsafe { + $crate::GODOT_API = Some($crate::GodotApi::from_raw((*options).api_struct)); + } + + $fun(options); + } + }; +} + +#[macro_export] +macro_rules! godot_gdnative_terminate { + ($name:ident) => { + godot_gdnative_terminate!($name, |_| {}); + }; + ($name:ident, $fun:expr) => { + #[no_mangle] + #[doc(hidden)] + pub extern "C" fn $name(options: *mut $crate::sys::godot_gdnative_terminate_options) { + $fun(options); + + unsafe { + $crate::GODOT_API = None; + } + } + }; +} + +#[macro_export] +macro_rules! godot_nativescript_init { + ($name:ident) => { + godot_nativescript_init!($name, |_| {}); + }; + ($name:ident, $fun:expr) => { + #[no_mangle] + #[doc(hidden)] + pub extern "C" fn $name(handle: *mut $crate::libc::c_void) { + $fun(handle); + } + }; +} + #[macro_export] macro_rules! godot_init { ( @@ -226,3 +277,118 @@ macro_rules! godot_test { )* } } + + +#[macro_export] +macro_rules! godot_create_constructor { + ($_name:ty, $c:expr) => { + { + unsafe extern "C" fn constructor( + this: *mut $crate::sys::godot_object, + method_data: *mut $crate::libc::c_void + ) -> *mut $crate::libc::c_void { + use std::cell::RefCell; + use std::boxed::Box; + + let val = $c($crate::GodotClassInfo{ this: this }); + + let wrapper = Box::new(RefCell::new(val)); + Box::into_raw(wrapper) as *mut _ + } + + constructor + } + } +} + +#[macro_export] +macro_rules! godot_create_destructor { + ($name:ty) => { + { + unsafe extern "C" fn destructor( + this: *mut $crate::sys::godot_object, + method_data: *mut $crate::libc::c_void, + user_data: *mut $crate::libc::c_void + ) -> () { + use std::cell::RefCell; + use std::boxed::Box; + + let wrapper: Box> = unsafe { Box::from_raw(user_data as *mut _) }; + drop(wrapper) + } + + destructor + } + } +} + +#[macro_export] +macro_rules! godot_create_method_parameter_count { + () => { + 0 + }; + ($name:ident, $($other:ident,)*) => { + 1 + godot_create_method_parameter_count!($($other,)*) + } +} + +#[macro_export] +macro_rules! godot_create_method { + ( + $type_name:ty, + fn $method_name:ident( + &mut $self:ident + $(,$pname:ident : $pty:ty)* + ) -> $retty:ty + ) => { + { + unsafe extern "C" fn method( + this: *mut $crate::sys::godot_object, + method_data: *mut $crate::libc::c_void, + user_data: *mut $crate::libc::c_void, + num_args: $crate::libc::c_int, + args: *mut *mut $crate::sys::godot_variant + ) -> $crate::sys::godot_variant { + + use std::cell::RefCell; + use std::panic::{self, AssertUnwindSafe}; + + let num_params = godot_create_method_parameter_count!($($pname,)*); + + if num_args != num_params { + godot_error!("Incorrect number of parameters: expected {} but got {}", num_params, num_args); + return $crate::Variant::new().to_sys(); + } + + + let mut offset = 0; + $( + let $pname = if let Some(val) = <$pty as $crate::GodotType>::from_sys_variant(&mut *(*args).offset(offset)) { + val + } else { + godot_error!("Incorrect argument type for argument {}", offset); + return $crate::Variant::new().to_sys(); + }; + + offset += 1; + )* + + let __rust_val = &*(user_data as *mut RefCell<$type_name>); + let mut __rust_val = __rust_val.borrow_mut(); + + let rust_ret = match panic::catch_unwind(AssertUnwindSafe(|| { + __rust_val.$method_name($($pname,)*); + })) { + Ok(val) => val, + Err(err) => { + return $crate::Variant::new().to_sys(); + } + }; + + <$retty as $crate::GodotType>::to_variant(&rust_ret).forget() + } + + method + } + }; +} \ No newline at end of file