diff --git a/godot-codegen/src/lib.rs b/godot-codegen/src/lib.rs index 470af9672..e9ec07c30 100644 --- a/godot-codegen/src/lib.rs +++ b/godot-codegen/src/lib.rs @@ -258,6 +258,7 @@ const SELECTED_CLASSES: &[&str] = &[ "AudioStreamPlayer", "BaseButton", "Button", + "BoxMesh", "Camera2D", "Camera3D", "CanvasItem", @@ -276,6 +277,7 @@ const SELECTED_CLASSES: &[&str] = &[ "Label", "MainLoop", "Marker2D", + "Mesh", "Node", "Node2D", "Node3D", @@ -285,9 +287,11 @@ const SELECTED_CLASSES: &[&str] = &[ "PackedScene", "PathFollow2D", "PhysicsBody2D", + "PrimitiveMesh", "RefCounted", "RenderingServer", "Resource", + "ResourceFormatLoader", "ResourceLoader", "RigidBody2D", "SceneTree", diff --git a/godot-codegen/src/util.rs b/godot-codegen/src/util.rs index 08c8bd18f..ace6a7ea1 100644 --- a/godot-codegen/src/util.rs +++ b/godot-codegen/src/util.rs @@ -99,6 +99,8 @@ pub fn make_enum_definition(enum_: &Enum) -> TokenStream { self.ord } } + // SAFETY: + // The enums are transparently represented as an `i32`, so `*mut Self` is sound. unsafe impl sys::GodotFfi for #enum_name { sys::ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. } } diff --git a/godot-core/src/builtin/dictionary.rs b/godot-core/src/builtin/dictionary.rs index bf1845cee..324cb7294 100644 --- a/godot-core/src/builtin/dictionary.rs +++ b/godot-core/src/builtin/dictionary.rs @@ -250,7 +250,7 @@ unsafe impl GodotFfi for Dictionary { } // SAFETY: - // Dictionaries are properly initialized through a `from_sys` call, but the ref-count should be + // Dictionaries are properly initialized through a `from_sys` call, but the ref-count should be // incremented as that is the callee's responsibility. // // Using `std::mem::forget(dictionary.share())` increments the ref count. diff --git a/godot-ffi/src/godot_ffi.rs b/godot-ffi/src/godot_ffi.rs index dbb8778d6..ead75826b 100644 --- a/godot-ffi/src/godot_ffi.rs +++ b/godot-ffi/src/godot_ffi.rs @@ -313,7 +313,7 @@ macro_rules! ffi_methods_rest { /// /// ## Using `Opaque` /// -/// Turning pointer call arguments into a value is simply calling `from_opaue` on the argument pointer. +/// Turning pointer call arguments into a value is simply calling `from_opaque` on the argument pointer. /// Returning a value from a pointer call is simply calling [`std::ptr::swap`] on the return pointer /// and the `opaque` field transmuted into a pointer. /// diff --git a/itest/rust/src/virtual_methods_test.rs b/itest/rust/src/virtual_methods_test.rs index e17c5be60..0cc01b996 100644 --- a/itest/rust/src/virtual_methods_test.rs +++ b/itest/rust/src/virtual_methods_test.rs @@ -10,12 +10,19 @@ use crate::TestContext; use godot::bind::{godot_api, GodotClass}; use godot::builtin::GodotString; use godot::engine::node::InternalMode; +use godot::engine::resource_loader::CacheMode; use godot::engine::{ - InputEvent, InputEventAction, Node, Node2D, Node2DVirtual, NodeVirtual, RefCounted, - RefCountedVirtual, + BoxMesh, InputEvent, InputEventAction, Node, Node2D, Node2DVirtual, PrimitiveMesh, + PrimitiveMeshVirtual, RefCounted, RefCountedVirtual, ResourceFormatLoader, + ResourceFormatLoaderVirtual, ResourceLoader, }; use godot::obj::{Base, Gd, Share}; -use godot::prelude::PackedStringArray; +use godot::prelude::{ + is_equal_approx, varray, Color, PackedByteArray, PackedColorArray, PackedFloat32Array, + PackedInt32Array, PackedStringArray, PackedVector2Array, PackedVector3Array, ToVariant, + Variant, VariantArray, Vector2, Vector3, +}; +use godot::private::class_macros::assert_eq_approx; use godot::test::itest; /// Simple class, that deliberately has no constructor accessible from GDScript @@ -96,25 +103,34 @@ impl Node2DVirtual for TreeVirtualTest { } #[derive(GodotClass, Debug)] -#[class(base=Node)] +#[class(init, base=PrimitiveMesh)] struct ReturnVirtualTest { #[base] - base: Base, + base: Base, } #[godot_api] -impl NodeVirtual for ReturnVirtualTest { - fn init(base: Base) -> Self { - ReturnVirtualTest { base } - } - - fn get_configuration_warnings(&self) -> PackedStringArray { - let mut output = PackedStringArray::new(); - output.push("Hello".into()); - output +impl PrimitiveMeshVirtual for ReturnVirtualTest { + fn create_mesh_array(&self) -> VariantArray { + varray![ + PackedVector3Array::from_iter([Vector3::LEFT].into_iter()), + PackedVector3Array::from_iter([Vector3::LEFT].into_iter()), + PackedFloat32Array::from_iter([0.0, 0.0, 0.0, 1.0].into_iter()), + PackedColorArray::from_iter([Color::from_rgb(1.0, 1.0, 1.0)]), + PackedVector2Array::from_iter([Vector2::LEFT]), + PackedVector2Array::from_iter([Vector2::LEFT]), + PackedByteArray::from_iter([0, 1, 2, 3].into_iter()), + PackedByteArray::from_iter([0, 1, 2, 3].into_iter()), + PackedByteArray::from_iter([0, 1, 2, 3].into_iter()), + PackedByteArray::from_iter([0, 1, 2, 3].into_iter()), + PackedInt32Array::from_iter([0, 1, 2, 3].into_iter()), + PackedFloat32Array::from_iter([0.0, 1.0, 2.0, 3.0].into_iter()), + PackedInt32Array::from_iter([0].into_iter()), + ] } } +#[derive(GodotClass, Debug)] #[class(base=Node2D)] struct InputVirtualTest { #[base] @@ -129,11 +145,52 @@ impl Node2DVirtual for InputVirtualTest { } fn input(&mut self, event: Gd) { - println!("nya"); self.event = Some(event); } } +#[derive(GodotClass, Debug)] +#[class(init, base=ResourceFormatLoader)] +struct FormatLoaderTest { + #[base] + base: Base, +} + +impl FormatLoaderTest { + fn resource_type() -> GodotString { + GodotString::from("foo") + } +} + +#[godot_api] +impl ResourceFormatLoaderVirtual for FormatLoaderTest { + fn get_recognized_extensions(&self) -> PackedStringArray { + [GodotString::from("extension")].into_iter().collect() + } + + fn handles_type(&self, type_: godot::prelude::StringName) -> bool { + type_.to_string() == Self::resource_type().to_string() + } + + fn get_resource_type(&self, _path: GodotString) -> GodotString { + Self::resource_type() + } + + fn exists(&self, _path: GodotString) -> bool { + true + } + + fn load( + &self, + _path: GodotString, + _original_path: GodotString, + _use_sub_threads: bool, + _cache_mode: i64, + ) -> Variant { + BoxMesh::new().to_variant() + } +} + // ---------------------------------------------------------------------------------------------------------------------------------------------- #[itest] @@ -262,12 +319,64 @@ fn test_tree_enters_exits(test_context: &TestContext) { #[itest] fn test_virtual_method_with_return(_test_context: &TestContext) { let obj = Gd::::new_default(); - let output = obj.bind().get_configuration_warnings(); - assert!(output.contains("Hello".into())); - assert_eq!(output.len(), 1); - obj.free(); + let arr = obj.share().upcast::().get_mesh_arrays(); + let arr_rust = obj.bind().create_mesh_array(); + assert_eq!(arr.len(), arr_rust.len()); + // can't just assert_eq because the values of some floats change slightly + assert_eq_approx!( + arr.get(0).to::().get(0), + arr_rust.get(0).to::().get(0), + Vector3::is_equal_approx + ); + assert_eq_approx!( + arr.get(2).to::().get(3), + arr_rust.get(2).to::().get(3), + is_equal_approx + ); + assert_eq_approx!( + arr.get(3).to::().get(0), + arr_rust.get(3).to::().get(0), + Color::is_equal_approx + ); + assert_eq_approx!( + arr.get(4).to::().get(0), + arr_rust.get(4).to::().get(0), + Vector2::is_equal_approx + ); + assert_eq!( + arr.get(6).to::(), + arr_rust.get(6).to::(), + ); + assert_eq!( + arr.get(10).to::(), + arr_rust.get(10).to::() + ); +} + +// TODO: Fix memory leak in this test. +#[itest(skip)] +fn test_format_loader(_test_context: &TestContext) { + let format_loader = Gd::::new_default(); + let mut loader = ResourceLoader::singleton(); + loader.add_resource_format_loader(format_loader.share().upcast(), true); + + let extensions = loader.get_recognized_extensions_for_type(FormatLoaderTest::resource_type()); + let mut extensions_rust = format_loader.bind().get_recognized_extensions(); + extensions_rust.push("tres".into()); + assert_eq!(extensions, extensions_rust); + let resource = loader + .load( + "path.extension".into(), + "".into(), + CacheMode::CACHE_MODE_IGNORE, + ) + .unwrap(); + assert!(resource.try_cast::().is_some()); + + loader.remove_resource_format_loader(format_loader.upcast()); } +#[itest] fn test_input_event(test_context: &TestContext) { let obj = Gd::::new_default(); assert_eq!(obj.bind().event, None);