Skip to content

Commit

Permalink
Make our custom noises inherit Godot Noise.
Browse files Browse the repository at this point in the history
  • Loading branch information
Zylann committed Dec 20, 2024
1 parent d4dffef commit ee0b6fd
Show file tree
Hide file tree
Showing 13 changed files with 465 additions and 72 deletions.
1 change: 1 addition & 0 deletions common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions doc/source/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 40 additions & 13 deletions generators/graph/nodes/noise.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ void register_noise_nodes(Span<NodeType> types) {
const Runtime::Buffer &y = ctx.get_input(1);
Runtime::Buffer &out = ctx.get_output(0);
const Params p = ctx.get_params<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]);
}
Expand All @@ -149,23 +153,45 @@ void register_noise_nodes(Span<NodeType> types) {
ctx.make_error(String(ZN_TTR("{0} instance is null")).format(varray(Noise::get_class_static())));
return;
}
Ref<FastNoiseLite> 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<FastNoiseLite> 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_FastNoiseLite> 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;
};
}
{
Expand Down Expand Up @@ -204,6 +230,7 @@ void register_noise_nodes(Span<NodeType> types) {
const Runtime::Buffer &z = ctx.get_input(2);
Runtime::Buffer &out = ctx.get_output(0);
const Params p = ctx.get_params<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]);
}
Expand Down
2 changes: 2 additions & 0 deletions register_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ void initialize_voxel_module(ModuleInitializationLevel p_level) {
open_log_file();
#endif

ClassDB::register_class<ZN_Noise>();

// 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?
Expand Down
1 change: 1 addition & 0 deletions terrain/instancing/voxel_instance_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
82 changes: 82 additions & 0 deletions util/godot/classes/noise.cpp
Original file line number Diff line number Diff line change
@@ -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
41 changes: 41 additions & 0 deletions util/godot/classes/noise.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,52 @@
#ifndef ZN_GODOT_NOISE_H
#define ZN_GODOT_NOISE_H

#include "../core/version.h"

#if defined(ZN_GODOT)
#include <modules/noise/noise.h>
#elif defined(ZN_GODOT_EXTENSION)
#include <godot_cpp/classes/noise.hpp>
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
Loading

0 comments on commit ee0b6fd

Please sign in to comment.