diff --git a/common.py b/common.py index e5452c6e0..b5375b8ec 100644 --- a/common.py +++ b/common.py @@ -86,6 +86,7 @@ def get_sources(env, is_editor_build, include_tests): "util/godot/classes/mesh.cpp", "util/godot/classes/multimesh.cpp", "util/godot/classes/node.cpp", + "util/godot/classes/noise.cpp", "util/godot/classes/object.cpp", "util/godot/classes/project_settings.cpp", "util/godot/classes/rendering_device.cpp", diff --git a/doc/source/changelog.md b/doc/source/changelog.md index 78660599b..35a80868e 100644 --- a/doc/source/changelog.md +++ b/doc/source/changelog.md @@ -12,6 +12,7 @@ Semver is not yet in place, so each version can have breaking changes, although Primarily developped with Godot 4.3. +- Noises (`ZN_FastNoiseLite`, `FastNoise2`, `SpotNoise`) now inherit from `Noise` in Godot 4.4 so they can be used in more places. - `VoxelBlockyModel`: Added option to turn off "LOD skirts" when used with `VoxelLodTerrain`, which may be useful with transparent models - `VoxelBlockyModelCube`: Added support for mesh rotation like `VoxelBlockyMesh` (prior to that, rotation buttons in the editor only swapped tiles around) - `VoxelInstanceGenerator`: Added `OnePerTriangle` emission mode diff --git a/generators/graph/nodes/noise.h b/generators/graph/nodes/noise.h index 3635a2a3f..bb1832f26 100644 --- a/generators/graph/nodes/noise.h +++ b/generators/graph/nodes/noise.h @@ -130,6 +130,10 @@ void register_noise_nodes(Span types) { const Runtime::Buffer &y = ctx.get_input(1); Runtime::Buffer &out = ctx.get_output(0); const Params p = ctx.get_params(); + // TODO Fastpath for our custom noises, so separate nodes are no longer necessary? + // There is overhead in using the generic Godot interface, it goes through virtual functions, maybe even + // GDExtension API bridge. It is always better if inlining is possible (or having an API that can take + // series) for (uint32_t i = 0; i < out.size; ++i) { out.data[i] = p.noise->get_noise_2d(x.data[i], y.data[i]); } @@ -149,23 +153,45 @@ void register_noise_nodes(Span types) { ctx.make_error(String(ZN_TTR("{0} instance is null")).format(varray(Noise::get_class_static()))); return; } - Ref fnl = noise; - if (fnl.is_null()) { - ctx.make_error(String(ZN_TTR("Shader generation with {0} is not supported.")) - .format(varray(noise->get_class()))); + + Ref godot_fnl = noise; + if (godot_fnl.is_valid()) { + if (godot_fnl->is_domain_warp_enabled()) { + // TODO Support domain warp shader generation with Godot's implementation of FastNoiseLite + ctx.make_error( + String(ZN_TTR("Shader generation from {0} with domain warp is not supported. Use {1}.")) + .format(varray(noise->get_class(), ZN_FastNoiseLiteGradient::get_class_static())) + ); + return; + } + ctx.require_lib_code("vg_fnl", g_fast_noise_lite_shader); + add_fast_noise_lite_state_config(ctx, **godot_fnl); + ctx.add_format( + "{} = fnlGetNoise2D(state, {}, {});\n", + ctx.get_output_name(0), + ctx.get_input_name(0), + ctx.get_input_name(1) + ); return; } - if (fnl->is_domain_warp_enabled()) { - // TODO Support domain warp shader generation with Godot's implementation of FastNoiseLite - ctx.make_error( - String(ZN_TTR("Shader generation from {0} with domain warp is not supported. Use {1}.")) - .format(varray(noise->get_class(), ZN_FastNoiseLiteGradient::get_class_static()))); + + Ref zn_fnl = noise; + if (zn_fnl.is_valid()) { + ctx.require_lib_code("vg_fnl", g_fast_noise_lite_shader); + add_fast_noise_lite_state_config(ctx, **zn_fnl); + ctx.add_format( + "{} = fnlGetNoise2D(state, {}, {});\n", + ctx.get_output_name(0), + ctx.get_input_name(0), + ctx.get_input_name(1) + ); return; } - ctx.require_lib_code("vg_fnl", g_fast_noise_lite_shader); - add_fast_noise_lite_state_config(ctx, **fnl); - ctx.add_format("{} = fnlGetNoise2D(state, {}, {});\n", ctx.get_output_name(0), ctx.get_input_name(0), - ctx.get_input_name(1)); + + ctx.make_error( + String(ZN_TTR("Shader generation with {0} is not supported.")).format(varray(noise->get_class())) + ); + return; }; } { @@ -204,6 +230,7 @@ void register_noise_nodes(Span types) { const Runtime::Buffer &z = ctx.get_input(2); Runtime::Buffer &out = ctx.get_output(0); const Params p = ctx.get_params(); + // TODO Fastpath for our custom noises, so separate nodes are no longer necessary? for (uint32_t i = 0; i < out.size; ++i) { out.data[i] = p.noise->get_noise_3d(x.data[i], y.data[i], z.data[i]); } diff --git a/register_types.cpp b/register_types.cpp index e7ee9d5b1..a41c037ce 100644 --- a/register_types.cpp +++ b/register_types.cpp @@ -207,6 +207,8 @@ void initialize_voxel_module(ModuleInitializationLevel p_level) { open_log_file(); #endif + ClassDB::register_class(); + // TODO Enhancement: can I prevent users from instancing `VoxelEngine`? // This class is used as a singleton so it's not really abstract. // Should I use `register_abstract_class` anyways? diff --git a/terrain/instancing/voxel_instance_generator.cpp b/terrain/instancing/voxel_instance_generator.cpp index 19b38b69b..7d9c4f274 100644 --- a/terrain/instancing/voxel_instance_generator.cpp +++ b/terrain/instancing/voxel_instance_generator.cpp @@ -546,6 +546,7 @@ void VoxelInstanceGenerator::generate_transforms( // Legacy noise (noise graph is more versatile, but this remains for compatibility) if (noise.is_valid()) { noise_cache.resize(vertex_cache.size()); + // TODO Fastpath for our custom noises switch (_noise_dimension) { case DIMENSION_2D: { diff --git a/util/godot/classes/noise.cpp b/util/godot/classes/noise.cpp new file mode 100644 index 000000000..fe47dcda7 --- /dev/null +++ b/util/godot/classes/noise.cpp @@ -0,0 +1,82 @@ +#include "noise.h" +#include "../../io/log.h" + +namespace zylann { + +#if defined(ZN_GODOT) + +real_t ZN_Noise::get_noise_1d(real_t p_x) const { + return _zn_get_noise_1d(p_x); +} + +real_t ZN_Noise::get_noise_2dv(Vector2 p_v) const { + return _zn_get_noise_2d(p_v); +} + +real_t ZN_Noise::get_noise_2d(real_t p_x, real_t p_y) const { + return _zn_get_noise_2d(Vector2(p_x, p_y)); +} + +real_t ZN_Noise::get_noise_3dv(Vector3 p_v) const { + return _zn_get_noise_3d(p_v); +} + +real_t ZN_Noise::get_noise_3d(real_t p_x, real_t p_y, real_t p_z) const { + return _zn_get_noise_3d(Vector3(p_x, p_y, p_z)); +} + +#elif defined(ZN_GODOT_EXTENSION) && (GODOT_VERSION_MAJOR == 4 && GODOT_VERSION_MINOR >= 4) + +real_t ZN_Noise::_get_noise_1d(real_t p_x) const { + return _zn_get_noise_1d(p_x); +} + +real_t ZN_Noise::_get_noise_2d(Vector2 p_v) const { + return _zn_get_noise_2d(p_v); +} + +real_t ZN_Noise::_get_noise_3d(Vector3 p_v) const { + return _zn_get_noise_3d(p_v); +} + +#endif + +real_t ZN_Noise::_zn_get_noise_1d(real_t p_x) const { + // Should be implemented in derived classes + ZN_PRINT_ERROR_ONCE("Not implemented"); + return 0.0; +} + +real_t ZN_Noise::_zn_get_noise_2d(Vector2 p_v) const { + // Should be implemented in derived classes + ZN_PRINT_ERROR_ONCE("Not implemented"); + return 0.0; +} + +real_t ZN_Noise::_zn_get_noise_3d(Vector3 p_v) const { + // Should be implemented in derived classes + ZN_PRINT_ERROR_ONCE("Not implemented"); + return 0.0; +} + +// #if defined(ZN_GODOT_EXTENSION) && (GODOT_VERSION_MAJOR == 4 && GODOT_VERSION_MINOR <= 3) +// void real_t ZN_Noise::_b_get_noise_2dv(real_t p_x, real_t p_y) const { +// return _zn_get_noise_2d(Vector2(p_x, p_y)); +// } + +// void real_t ZN_Noise::_b_get_noise_3dv(real_t p_x, real_t p_y, real_t p_z) const { +// return _zn_get_noise_3d(Vector3(p_x, p_y, p_z)); +// } +// #endif + +void ZN_Noise::_bind_methods() { + // #if defined(ZN_GODOT_EXTENSION) && (GODOT_VERSION_MAJOR == 4 && GODOT_VERSION_MINOR <= 3) + // ClassDB::bind_method(D_METHOD("get_noise_2d", "x", "y"), &ZN_FastNoiseLite::_zn_get_noise_2dc); + // ClassDB::bind_method(D_METHOD("get_noise_3d", "x", "y", "z"), &ZN_FastNoiseLite::_zn_get_noise_2dc); + + // ClassDB::bind_method(D_METHOD("get_noise_2dv", "position"), &ZN_FastNoiseLite::_zn_get_noise_2d); + // ClassDB::bind_method(D_METHOD("get_noise_3dv", "position"), &ZN_FastNoiseLite::_zn_get_noise_3d); + // #endif +} + +} // namespace zylann diff --git a/util/godot/classes/noise.h b/util/godot/classes/noise.h index 27c3e1f69..b50a54c6e 100644 --- a/util/godot/classes/noise.h +++ b/util/godot/classes/noise.h @@ -1,6 +1,8 @@ #ifndef ZN_GODOT_NOISE_H #define ZN_GODOT_NOISE_H +#include "../core/version.h" + #if defined(ZN_GODOT) #include #elif defined(ZN_GODOT_EXTENSION) @@ -8,4 +10,43 @@ using namespace godot; #endif +namespace zylann { + +#if defined(ZN_GODOT) || (GODOT_VERSION_MAJOR == 4 && GODOT_VERSION_MINOR >= 4) +class ZN_Noise : public Noise { + GDCLASS(ZN_Noise, Noise) +#else +// Noise is abstract in the extension API and cannot be inherited +class ZN_Noise : public Resource { + GDCLASS(ZN_Noise, Resource) +#endif +public: +#if defined(ZN_GODOT) + real_t get_noise_1d(real_t p_x) const override; + real_t get_noise_2dv(Vector2 p_v) const override; + real_t get_noise_2d(real_t p_x, real_t p_y) const override; + real_t get_noise_3dv(Vector3 p_v) const override; + real_t get_noise_3d(real_t p_x, real_t p_y, real_t p_z) const override; +#elif defined(ZN_GODOT_EXTENSION) && (GODOT_VERSION_MAJOR == 4 && GODOT_VERSION_MINOR >= 4) + real_t _get_noise_1d(real_t p_x) const override; + real_t _get_noise_2d(Vector2 p_v) const override; + real_t _get_noise_3d(Vector3 p_v) const override; +#endif + +protected: + virtual real_t _zn_get_noise_1d(real_t p_x) const; + virtual real_t _zn_get_noise_2d(Vector2 p_v) const; + virtual real_t _zn_get_noise_3d(Vector3 p_v) const; + + // #if defined(ZN_GODOT_EXTENSION) && (GODOT_VERSION_MAJOR == 4 && GODOT_VERSION_MINOR <= 3) + // void real_t _b_get_noise_2dc(real_t p_x, real_t p_y) const; + // void real_t _b_get_noise_3dc(real_t p_x, real_t p_y, real_t p_z) const; + // #endif + +private: + static void _bind_methods(); +}; + +} // namespace zylann + #endif // ZN_GODOT_NOISE_H diff --git a/util/noise/fast_noise_2.cpp b/util/noise/fast_noise_2.cpp index b20a95c6b..624975551 100644 --- a/util/noise/fast_noise_2.cpp +++ b/util/noise/fast_noise_2.cpp @@ -559,6 +559,173 @@ String FastNoise2::_b_get_simd_level_name(SIMDLevel level) { return get_simd_level_name(level); } +real_t FastNoise2::_zn_get_noise_1d(real_t p_x) const { + return get_noise_2d_single(Vector2(p_x, 0.0)); +} + +real_t FastNoise2::_zn_get_noise_2d(Vector2 p_v) const { + return get_noise_2d_single(p_v); +} + +real_t FastNoise2::_zn_get_noise_3d(Vector3 p_v) const { + return get_noise_3d_single(p_v); +} + +float get_sum(Span values) { + float sum = 0.f; + for (const float v : values) { + sum += v; + } + return sum; +} + +void normalize(Span values) { + const float sum = get_sum(values); + if (sum > 0.0001f) { + const float inv_sum = 1.f / sum; + for (float &v : values) { + v *= inv_sum; + } + } +} + +PackedByteArray f32_snorm_to_u8_packed_byte_array(Span src) { + PackedByteArray bytes; + bytes.resize(src.size()); + Span bytes_s(bytes.ptrw(), bytes.size()); + for (size_t i = 0; i < src.size(); ++i) { + const float n = src[i]; + bytes_s[i] = math::clamp(static_cast((n * 0.5f + 0.5f) * 255.f), 0, 255); + } + return bytes; +} + +#if defined(ZN_GODOT) +Ref FastNoise2::get_image( +#elif defined(ZN_GODOT_EXTENSION) +Ref FastNoise2::_get_image( +#endif + int p_width, + int p_height, + bool p_invert, + bool p_in_3d_space, + bool p_normalize +) const { + ZN_ASSERT_RETURN_V(p_width >= 0, Ref()); + ZN_ASSERT_RETURN_V(p_height >= 0, Ref()); + + StdVector noise_buffer; + noise_buffer.resize(p_width * p_height); + + if (p_in_3d_space) { + get_noise_3d_grid(Vector3(), Vector3i(p_width, p_height, 1), to_span(noise_buffer)); + } else { + get_noise_2d_grid(Vector2(), Vector2i(p_width, p_height), to_span(noise_buffer)); + } + + if (p_invert) { + for (float &v : noise_buffer) { + v = 1.f - v; + } + } + + if (p_normalize) { + normalize(to_span(noise_buffer)); + } + + const PackedByteArray image_bytes = f32_snorm_to_u8_packed_byte_array(to_span(noise_buffer)); + + const Ref image = Image::create_from_data(p_width, p_height, false, Image::FORMAT_L8, image_bytes); + + return image; +} + +#if defined(ZN_GODOT) +TypedArray FastNoise2::get_image_3d( +#elif defined(ZN_GODOT_EXTENSION) +TypedArray FastNoise2::_get_image_3d( +#endif + int p_width, + int p_height, + int p_depth, + bool p_invert, + bool p_normalize +) const { + ZN_ASSERT_RETURN_V(p_width >= 0, TypedArray()); + ZN_ASSERT_RETURN_V(p_height >= 0, TypedArray()); + ZN_ASSERT_RETURN_V(p_depth >= 0, TypedArray()); + + StdVector noise_buffer; + noise_buffer.resize(p_width * p_height * p_depth); + + get_noise_3d_grid(Vector3(), Vector3i(p_width, p_height, p_depth), to_span(noise_buffer)); + + if (p_invert) { + for (float &v : noise_buffer) { + v = 1.f - v; + } + } + + if (p_normalize) { + normalize(to_span(noise_buffer)); + } + + TypedArray images; + images.resize(p_height); + + const unsigned int deck_size = p_width * p_height; + + for (int z = 0; z < p_depth; ++z) { + const Span deck = to_span(noise_buffer).sub(z * deck_size, deck_size); + const PackedByteArray image_bytes = f32_snorm_to_u8_packed_byte_array(deck); + const Ref image = Image::create_from_data(p_width, p_height, false, Image::FORMAT_L8, image_bytes); + images[z] = image; + } + + return images; +} + +#if defined(ZN_GODOT) +Ref FastNoise2::get_seamless_image( +#elif defined(ZN_GODOT_EXTENSION) +Ref FastNoise2::_get_seamless_image( +#endif + int p_width, + int p_height, + bool p_invert, + bool p_in_3d_space, + real_t p_blend_skirt, + bool p_normalize +) const { + ZN_ASSERT_RETURN_V(p_width >= 0, Ref()); + ZN_ASSERT_RETURN_V(p_height >= 0, Ref()); + + StdVector noise_buffer; + noise_buffer.resize(p_width * p_height); + + if (p_in_3d_space) { + ZN_PRINT_ERROR("Not supported"); + } else { + get_noise_2d_grid_tileable(Vector2i(p_width, p_height), to_span(noise_buffer)); + } + + if (p_invert) { + for (float &v : noise_buffer) { + v = 1.f - v; + } + } + + if (p_normalize) { + normalize(to_span(noise_buffer)); + } + + const PackedByteArray image_bytes = f32_snorm_to_u8_packed_byte_array(to_span(noise_buffer)); + + const Ref image = Image::create_from_data(p_width, p_height, false, Image::FORMAT_L8, image_bytes); + + return image; +} + void FastNoise2::_bind_methods() { ClassDB::bind_method(D_METHOD("set_noise_type", "type"), &FastNoise2::set_noise_type); ClassDB::bind_method(D_METHOD("get_noise_type"), &FastNoise2::get_noise_type); @@ -638,6 +805,7 @@ void FastNoise2::_bind_methods() { ClassDB::bind_method(D_METHOD("get_noise_2d_single", "pos"), &FastNoise2::get_noise_2d_single); ClassDB::bind_method(D_METHOD("get_noise_3d_single", "pos"), &FastNoise2::get_noise_3d_single); + // TODO Rename `get_image_3d_simd`, or have Godot expose get_image* functions as virtual ClassDB::bind_method(D_METHOD("generate_image", "image", "tileable"), &FastNoise2::generate_image); ClassDB::bind_method(D_METHOD("get_simd_level_name", "level"), &FastNoise2::_b_get_simd_level_name); diff --git a/util/noise/fast_noise_2.h b/util/noise/fast_noise_2.h index b6f394cc2..16d6824b5 100644 --- a/util/noise/fast_noise_2.h +++ b/util/noise/fast_noise_2.h @@ -2,6 +2,7 @@ #define VOXEL_FAST_NOISE_2_H #include "../containers/span.h" +#include "../godot/classes/noise.h" #include "../math/interval.h" #include "FastNoise/FastNoise.h" #include @@ -11,8 +12,8 @@ class Image; namespace zylann { // Can't call it FastNoise? because FastNoise is a namespace already -class FastNoise2 : public Resource { - GDCLASS(FastNoise2, Resource) +class FastNoise2 : public ZN_Noise { + GDCLASS(FastNoise2, ZN_Noise) public: static const int MAX_OCTAVES = 32; // This minimum size exists to fix an issue with SIMD operations, which need to use more than one element. @@ -177,6 +178,82 @@ class FastNoise2 : public Resource { math::Interval get_estimated_output_range() const; +protected: + real_t _zn_get_noise_1d(real_t p_x) const override; + real_t _zn_get_noise_2d(Vector2 p_v) const override; + real_t _zn_get_noise_3d(Vector3 p_v) const override; + +#if defined(ZN_GODOT) + Ref get_image( + int p_width, + int p_height, + bool p_invert = false, + bool p_in_3d_space = false, + bool p_normalize = true + ) const override; + + TypedArray get_image_3d( + int p_width, + int p_height, + int p_depth, + bool p_invert = false, + bool p_normalize = true + ) const override; + + Ref get_seamless_image( + int p_width, + int p_height, + bool p_invert = false, + bool p_in_3d_space = false, + real_t p_blend_skirt = 0.1, + bool p_normalize = true + ) const override; + + // TypedArray get_seamless_image_3d( + // int p_width, + // int p_height, + // int p_depth, + // bool p_invert = false, + // real_t p_blend_skirt = 0.1, + // bool p_normalize = true + // ) const override; + +#elif defined(ZN_GODOT_EXTENSION) + Ref _get_image( + int p_width, + int p_height, + bool p_invert = false, + bool p_in_3d_space = false, + bool p_normalize = true + ) const override; + + TypedArray _get_image_3d( + int p_width, + int p_height, + int p_depth, + bool p_invert = false, + bool p_normalize = true + ) const override; + + Ref _get_seamless_image( + int p_width, + int p_height, + bool p_invert = false, + bool p_in_3d_space = false, + real_t p_blend_skirt = 0.1, + bool p_normalize = true + ) const override; + + // TypedArray _get_seamless_image_3d( + // int p_width, + // int p_height, + // int p_depth, + // bool p_invert = false, + // real_t p_blend_skirt = 0.1, + // bool p_normalize = true + // ) const override; +#endif + private: // Non-static method for scripts because Godot4 does not support binding static methods (it's only // implemented for primitive types) diff --git a/util/noise/fast_noise_lite/fast_noise_lite.cpp b/util/noise/fast_noise_lite/fast_noise_lite.cpp index 45dedbca2..b3d796f57 100644 --- a/util/noise/fast_noise_lite/fast_noise_lite.cpp +++ b/util/noise/fast_noise_lite/fast_noise_lite.cpp @@ -226,6 +226,18 @@ void ZN_FastNoiseLite::_on_warp_noise_changed() { emit_changed(); } +real_t ZN_FastNoiseLite::_zn_get_noise_1d(real_t p_x) const { + return get_noise_2d_inline(p_x, 0.f); +} + +real_t ZN_FastNoiseLite::_zn_get_noise_2d(Vector2 p_v) const { + return get_noise_2d_inline(p_v.x, p_v.y); +} + +real_t ZN_FastNoiseLite::_zn_get_noise_3d(Vector3 p_v) const { + return get_noise_3d_inline(p_v.x, p_v.y, p_v.z); +} + void ZN_FastNoiseLite::_bind_methods() { ClassDB::bind_method(D_METHOD("set_noise_type", "type"), &ZN_FastNoiseLite::set_noise_type); ClassDB::bind_method(D_METHOD("get_noise_type"), &ZN_FastNoiseLite::get_noise_type); @@ -278,12 +290,6 @@ void ZN_FastNoiseLite::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rotation_type_3d", "type"), &ZN_FastNoiseLite::set_rotation_type_3d); ClassDB::bind_method(D_METHOD("get_rotation_type_3d"), &ZN_FastNoiseLite::get_rotation_type_3d); - ClassDB::bind_method(D_METHOD("get_noise_2d", "x", "y"), &ZN_FastNoiseLite::_b_get_noise_2d); - ClassDB::bind_method(D_METHOD("get_noise_3d", "x", "y", "z"), &ZN_FastNoiseLite::_b_get_noise_3d); - - ClassDB::bind_method(D_METHOD("get_noise_2dv", "position"), &ZN_FastNoiseLite::_b_get_noise_2dv); - ClassDB::bind_method(D_METHOD("get_noise_3dv", "position"), &ZN_FastNoiseLite::_b_get_noise_3dv); - ADD_PROPERTY( PropertyInfo( Variant::INT, diff --git a/util/noise/fast_noise_lite/fast_noise_lite.h b/util/noise/fast_noise_lite/fast_noise_lite.h index 134e7159c..4359cc7d6 100644 --- a/util/noise/fast_noise_lite/fast_noise_lite.h +++ b/util/noise/fast_noise_lite/fast_noise_lite.h @@ -1,6 +1,7 @@ #ifndef ZYLANN_FAST_NOISE_LITE_H #define ZYLANN_FAST_NOISE_LITE_H +#include "../../godot/classes/noise.h" #include "fast_noise_lite_gradient.h" namespace zylann { @@ -12,10 +13,6 @@ namespace zylann { // - get_noise* methods are not inline. That means there is a potential performance loss when calling it many times // (basically all the time in this module). // -// - get_noise* methods are not `const`. Means any person creating a Noise implementation can mutate internal state, -// which is bad for multithreaded usage. IMO noise should not have state, and if it does, it must be explicit and not -// change "lazily". Devs aren't sure yet if they should change that. -// // - `real_t` is used everywhere, instead of just coordinates. That means builds with `float=64` might be slower, // especially in cases where such precision isn't necessary *for the use case of noise generation*. // @@ -30,9 +27,10 @@ namespace zylann { // `VoxelGeneratorGraph`. // // - Does not use GDVirtual, so it can only be extended by modules, and cannot be extended with GDExtensions +// TODO See https://github.com/godotengine/godot/pull/100443 // -class ZN_FastNoiseLite : public Resource { - GDCLASS(ZN_FastNoiseLite, Resource) +class ZN_FastNoiseLite : public ZN_Noise { + GDCLASS(ZN_FastNoiseLite, ZN_Noise) typedef ::fast_noise_lite::FastNoiseLite _FastNoise; @@ -126,14 +124,14 @@ class ZN_FastNoiseLite : public Resource { // Queries - inline float get_noise_2d(real_t x, real_t y) const { + inline float get_noise_2d_inline(real_t x, real_t y) const { if (_warp_noise.is_valid()) { _warp_noise->warp_2d(x, y); } return _fn.GetNoise(x, y); } - inline float get_noise_3d(real_t x, real_t y, real_t z) const { + inline float get_noise_3d_inline(real_t x, real_t y, real_t z) const { if (_warp_noise.is_valid()) { _warp_noise->warp_3d(x, y, z); } @@ -149,25 +147,16 @@ class ZN_FastNoiseLite : public Resource { return _fn; } +protected: + real_t _zn_get_noise_1d(real_t p_x) const override; + real_t _zn_get_noise_2d(Vector2 p_v) const override; + real_t _zn_get_noise_3d(Vector3 p_v) const override; + private: static void _bind_methods(); void _on_warp_noise_changed(); - float _b_get_noise_2d(real_t x, real_t y) { - return get_noise_2d(x, y); - } - float _b_get_noise_3d(real_t x, real_t y, real_t z) { - return get_noise_3d(x, y, z); - } - - float _b_get_noise_2dv(Vector2 p) { - return get_noise_2d(p.x, p.y); - } - float _b_get_noise_3dv(Vector3 p) { - return get_noise_3d(p.x, p.y, p.z); - } - ::fast_noise_lite::FastNoiseLite _fn; // TODO FastNoiseLite should rather have getters diff --git a/util/noise/spot_noise_gd.cpp b/util/noise/spot_noise_gd.cpp index a2ce5e39d..8806e3723 100644 --- a/util/noise/spot_noise_gd.cpp +++ b/util/noise/spot_noise_gd.cpp @@ -1,6 +1,5 @@ #include "spot_noise_gd.h" #include "../math/conv.h" -#include "spot_noise.h" namespace zylann { @@ -55,22 +54,6 @@ void ZN_SpotNoise::set_jitter(float jitter) { emit_changed(); } -float ZN_SpotNoise::get_noise_2d(real_t x, real_t y) const { - return SpotNoise::spot_noise_2d(Vector2f(x, y), _cell_size, _spot_radius, _jitter, _seed); -} - -float ZN_SpotNoise::get_noise_3d(real_t x, real_t y, real_t z) const { - return SpotNoise::spot_noise_3d(Vector3f(x, y, z), _cell_size, _spot_radius, _jitter, _seed); -} - -float ZN_SpotNoise::get_noise_2dv(Vector2 pos) const { - return SpotNoise::spot_noise_2d(Vector2f(pos.x, pos.y), _cell_size, _spot_radius, _jitter, _seed); -} - -float ZN_SpotNoise::get_noise_3dv(Vector3 pos) const { - return SpotNoise::spot_noise_3d(Vector3f(pos.x, pos.y, pos.z), _cell_size, _spot_radius, _jitter, _seed); -} - PackedVector2Array ZN_SpotNoise::get_spot_positions_in_area_2d(Rect2 rect) const { PackedVector2Array positions; const Rect2 norm_rect(rect.position / _cell_size, rect.size / _cell_size); @@ -107,6 +90,18 @@ PackedVector3Array ZN_SpotNoise::get_spot_positions_in_area_3d(AABB aabb) const return positions; } +real_t ZN_SpotNoise::_zn_get_noise_1d(real_t p_x) const { + return get_noise_2d_inline(Vector2f(p_x, 0)); +} + +real_t ZN_SpotNoise::_zn_get_noise_2d(Vector2 p_v) const { + return get_noise_2d_inline(Vector2f(p_v.x, p_v.y)); +} + +real_t ZN_SpotNoise::_zn_get_noise_3d(Vector3 p_v) const { + return get_noise_3d_inline(Vector3f(p_v.x, p_v.y, p_v.z)); +} + void ZN_SpotNoise::_bind_methods() { ClassDB::bind_method(D_METHOD("set_seed", "seed"), &ZN_SpotNoise::set_seed); ClassDB::bind_method(D_METHOD("get_seed"), &ZN_SpotNoise::get_seed); @@ -120,12 +115,6 @@ void ZN_SpotNoise::_bind_methods() { ClassDB::bind_method(D_METHOD("set_jitter", "jitter"), &ZN_SpotNoise::set_jitter); ClassDB::bind_method(D_METHOD("get_jitter"), &ZN_SpotNoise::get_jitter); - ClassDB::bind_method(D_METHOD("get_noise_2d", "x", "y"), &ZN_SpotNoise::get_noise_2d); - ClassDB::bind_method(D_METHOD("get_noise_3d", "x", "y", "z"), &ZN_SpotNoise::get_noise_3d); - - ClassDB::bind_method(D_METHOD("get_noise_2dv", "pos"), &ZN_SpotNoise::get_noise_2dv); - ClassDB::bind_method(D_METHOD("get_noise_3dv", "pos"), &ZN_SpotNoise::get_noise_3dv); - ClassDB::bind_method( D_METHOD("get_spot_positions_in_area_2d", "rect"), &ZN_SpotNoise::get_spot_positions_in_area_2d ); diff --git a/util/noise/spot_noise_gd.h b/util/noise/spot_noise_gd.h index 25871d0a0..758f4a3c6 100644 --- a/util/noise/spot_noise_gd.h +++ b/util/noise/spot_noise_gd.h @@ -1,12 +1,14 @@ #ifndef ZN_SPOT_NOISE_GD_H #define ZN_SPOT_NOISE_GD_H +#include "../godot/classes/noise.h" #include "../godot/classes/resource.h" +#include "spot_noise.h" namespace zylann { -class ZN_SpotNoise : public Resource { - GDCLASS(ZN_SpotNoise, Resource); +class ZN_SpotNoise : public ZN_Noise { + GDCLASS(ZN_SpotNoise, ZN_Noise); public: int get_seed() const; @@ -21,15 +23,22 @@ class ZN_SpotNoise : public Resource { float get_jitter() const; void set_jitter(float jitter); - float get_noise_2d(real_t x, real_t y) const; - float get_noise_3d(real_t x, real_t y, real_t z) const; + inline float get_noise_2d_inline(const Vector2f pos) const { + return SpotNoise::spot_noise_2d(pos, _cell_size, _spot_radius, _jitter, _seed); + } - float get_noise_2dv(Vector2 pos) const; - float get_noise_3dv(Vector3 pos) const; + inline float get_noise_3d_inline(const Vector3f pos) const { + return SpotNoise::spot_noise_3d(pos, _cell_size, _spot_radius, _jitter, _seed); + } PackedVector2Array get_spot_positions_in_area_2d(Rect2 rect) const; PackedVector3Array get_spot_positions_in_area_3d(AABB aabb) const; +protected: + real_t _zn_get_noise_1d(real_t p_x) const override; + real_t _zn_get_noise_2d(Vector2 p_v) const override; + real_t _zn_get_noise_3d(Vector3 p_v) const override; + private: static void _bind_methods();