This is the whole thing. The properties appear in the property inspector, but I can’t read them using method calls.
Errors:
ERROR: Cannot call GDExtension method bind 'get_octaves' on placeholder instance.
ERROR: Cannot call GDExtension method bind 'generate_height_map' on placeholder instance.
ERROR: Invalid image
//world_surface_noise.h
#ifndef WORLD_SURFACE_NOISE_H
#define WORLD_SURFACE_NOISE_H
#include <godot_cpp/classes/resource.hpp>
#include <godot_cpp/classes/curve.hpp>
#include <godot_cpp/classes/fast_noise_lite.hpp>
#include <godot_cpp/classes/image.hpp>
//#include <godot_cpp/classes/packed_float32_array.hpp>
#include <string>
namespace godot {
class WorldSurfaceNoise : public Resource {
GDCLASS(WorldSurfaceNoise, Resource)
private:
int seed = 0;
int octaves = 4;
float lacunarity = 2;
float gain = .5;
bool wrap_x = true;
bool wrap_y = true;
Vector2 world_radius = Vector2(1, 1);
protected:
static void _bind_methods();
public:
WorldSurfaceNoise() {}
~WorldSurfaceNoise() {}
int get_seed() const;
void set_seed(int _seed);
int get_octaves() const;
void set_octaves(int _octaves);
float get_lacunarity() const;
void set_lacunarity(float _lacunarity);
float get_gain() const;
void set_gain(float _gain);
bool is_wrap_x() const;
void set_wrap_x(bool _wrap_x);
bool is_wrap_y() const;
void set_wrap_y(bool _wrap_y);
Vector2 get_world_radius() const;
void set_world_radius(Vector2 radius);
Ref<Image> generate_height_map(Vector2i map_size, Rect2 uv_region) const;
float sample_height(Vector2 uv) const;
};
}
#endif
//world_surface_noise.cpp
#include "world_surface_noise.h"
#include <godot_cpp/core/class_db.hpp>
using namespace godot;
void WorldSurfaceNoise::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_seed"), &WorldSurfaceNoise::get_seed);
ClassDB::bind_method(D_METHOD("set_seed", "p_seed"), &WorldSurfaceNoise::set_seed);
ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed");
ClassDB::bind_method(D_METHOD("get_octaves"), &WorldSurfaceNoise::get_octaves);
ClassDB::bind_method(D_METHOD("set_octaves", "p_octaves"), &WorldSurfaceNoise::set_octaves);
ADD_PROPERTY(PropertyInfo(Variant::INT, "octaves", PROPERTY_HINT_RANGE, "1,16,1,or_greater"), "set_octaves", "get_octaves");
ClassDB::bind_method(D_METHOD("get_lacunarity"), &WorldSurfaceNoise::get_lacunarity);
ClassDB::bind_method(D_METHOD("set_lacunarity", "p_lacunarity"), &WorldSurfaceNoise::set_lacunarity);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lacunarity", PROPERTY_HINT_RANGE, "0.0,4.0,0.01,or_greater"), "set_lacunarity", "get_lacunarity");
ClassDB::bind_method(D_METHOD("get_gain"), &WorldSurfaceNoise::get_gain);
ClassDB::bind_method(D_METHOD("set_gain", "p_gain"), &WorldSurfaceNoise::set_gain);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gain", PROPERTY_HINT_RANGE, "0.0,1.0,0.01,or_greater"), "set_gain", "get_gain");
ClassDB::bind_method(D_METHOD("is_wrap_x"), &WorldSurfaceNoise::is_wrap_x);
ClassDB::bind_method(D_METHOD("set_wrap_x", "p_wrap_x"), &WorldSurfaceNoise::set_wrap_x);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_x"), "set_wrap_x", "is_wrap_x");
ClassDB::bind_method(D_METHOD("is_wrap_y"), &WorldSurfaceNoise::is_wrap_y);
ClassDB::bind_method(D_METHOD("set_wrap_y", "p_wrap_y"), &WorldSurfaceNoise::set_wrap_y);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_y"), "set_wrap_y", "is_wrap_y");
ClassDB::bind_method(D_METHOD("get_world_radius"), &WorldSurfaceNoise::get_world_radius);
ClassDB::bind_method(D_METHOD("set_world_radius", "p_world_radius"), &WorldSurfaceNoise::set_world_radius);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "world_radius"), "set_world_radius", "get_world_radius");
ClassDB::bind_method(D_METHOD("sample_height", "uv"), &WorldSurfaceNoise::sample_height);
ClassDB::bind_method(D_METHOD("generate_height_map", "map_size", "uv_region"), &WorldSurfaceNoise::generate_height_map);
}
int WorldSurfaceNoise::get_seed() const {
return seed;
}
void WorldSurfaceNoise::set_seed(int p_seed) {
if (seed == p_seed) {
return; // No change, no need to emit changed signal
}
seed = p_seed;
emit_changed();
}
int WorldSurfaceNoise::get_octaves() const {
return octaves;
}
void WorldSurfaceNoise::set_octaves(int p_octaves) {
if (octaves == p_octaves) {
return; // No change, no need to emit changed signal
}
octaves = p_octaves;
emit_changed();
}
float WorldSurfaceNoise::get_lacunarity() const {
return lacunarity;
}
void WorldSurfaceNoise::set_lacunarity(float p_lacunarity) {
if (lacunarity == p_lacunarity) {
return; // No change, no need to emit changed signal
}
lacunarity = p_lacunarity;
emit_changed();
}
float WorldSurfaceNoise::get_gain() const {
return gain;
}
void WorldSurfaceNoise::set_gain(float p_gain) {
if (gain == p_gain) {
return; // No change, no need to emit changed signal
}
gain = p_gain;
emit_changed();
}
bool WorldSurfaceNoise::is_wrap_x() const {
return wrap_x;
}
void WorldSurfaceNoise::set_wrap_x(bool p_wrap_x) {
if (wrap_x == p_wrap_x) {
return; // No change, no need to emit changed signal
}
wrap_x = p_wrap_x;
emit_changed();
}
bool WorldSurfaceNoise::is_wrap_y() const {
return wrap_y;
}
void WorldSurfaceNoise::set_wrap_y(bool p_wrap_y) {
if (wrap_y == p_wrap_y) {
return; // No change, no need to emit changed signal
}
wrap_y = p_wrap_y;
emit_changed();
}
Vector2 WorldSurfaceNoise::get_world_radius() const {
return world_radius;
}
void WorldSurfaceNoise::set_world_radius(Vector2 radius) {
if (world_radius == radius) {
return; // No change, no need to emit changed signal
}
world_radius = radius;
emit_changed();
}
Ref<Image> WorldSurfaceNoise::generate_height_map(Vector2i map_size, Rect2 uv_region) const {
PackedFloat32Array data;
data.resize(map_size.x * map_size.y);
for (int y = 0; y < map_size.y; y++) {
for (int x = 0; x < map_size.x; x++) {
Vector2 uv = Vector2(x, y) / Vector2(map_size);
uv = uv_region.position + uv * uv_region.size;
data[x + y * map_size.x] = sample_height(uv);
}
}
Ref<Image> image = Image::create_from_data(map_size.x, map_size.y, false, Image::FORMAT_RF, data.to_byte_array());
return image;
}
float WorldSurfaceNoise::sample_height(Vector2 uv) const {
FastNoiseLite noise;
noise.set_seed(seed);
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_PERLIN);
noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_FBM);
noise.set_fractal_octaves(octaves);
noise.set_fractal_lacunarity(lacunarity);
noise.set_fractal_gain(gain);
float height;
if (wrap_x && wrap_y) {
Vector4 _uv = Vector4(sinf(uv.x * Math_PI * 2.0f) * world_radius.x,
sinf(uv.y * Math_PI * 2.0f) * world_radius.y,
cosf(uv.x * Math_PI * 2.0f) * world_radius.x,
cosf(uv.y * Math_PI * 2.0f) * world_radius.y);
height = noise.get_noise_3d(_uv.x, _uv.y, _uv.z);
}
else if (wrap_x) {
Vector3 _uv = Vector3(sinf(uv.x * Math_PI * 2.0f) * world_radius.x,
uv.y * world_radius.y,
cosf(uv.x * Math_PI * 2.0f) * world_radius.x);
height = noise.get_noise_3dv(_uv);
}
else {
height = noise.get_noise_2dv(uv * world_radius);
}
return height;
}
#MyTexture2D.gd
@tool
extends ImageTexture
class_name MyTexture2D
@export var noise_source:WorldSurfaceNoise:
set(v):
if v == noise_source:
return
noise_source = v
update_image()
@export var image_size:Vector2i = Vector2i(512, 512):
set(v):
if v == image_size:
return
image_size = v
update_image()
func update_image():
if noise_source:
print("update_image()")
var octaves:int = noise_source.get_octaves()
print ("ocatves ", octaves)
var img:Image = noise_source.generate_height_map(image_size, Rect2(0, 0, 1, 1))
print("img ", img)
set_image(img)
pass