Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix compatibility breakage from adding NoiseTexture3D #76557

Merged
merged 1 commit into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 32 additions & 9 deletions modules/noise/doc_classes/Noise.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,24 @@
<return type="Image" />
<param index="0" name="width" type="int" />
<param index="1" name="height" type="int" />
<param index="2" name="invert" type="bool" default="false" />
<param index="3" name="in_3d_space" type="bool" default="false" />
<param index="4" name="normalize" type="bool" default="true" />
<description>
Returns an [Image] containing 2D noise values.
[b]Note:[/b] With [param normalize] set to [code]false[/code], the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code].
</description>
</method>
<method name="get_image_3d" qualifiers="const">
<return type="Image[]" />
<param index="0" name="width" type="int" />
<param index="1" name="height" type="int" />
<param index="2" name="depth" type="int" />
<param index="3" name="invert" type="bool" default="false" />
<param index="4" name="in_3d_space" type="bool" default="false" />
<param index="5" name="normalize" type="bool" default="true" />
<param index="4" name="normalize" type="bool" default="true" />
<description>
Returns a 2D [Image] noise image.
Note: With [param normalize] set to [code]false[/code] the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code].
Returns an [Array] of [Image]s containing 3D noise values for use with [method ImageTexture3D.create].
[b]Note:[/b] With [param normalize] set to [code]false[/code], the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code].
</description>
</method>
<method name="get_noise_1d" qualifiers="const">
Expand Down Expand Up @@ -66,14 +77,26 @@
<return type="Image" />
<param index="0" name="width" type="int" />
<param index="1" name="height" type="int" />
<param index="2" name="invert" type="bool" default="false" />
<param index="3" name="in_3d_space" type="bool" default="false" />
<param index="4" name="skirt" type="float" default="0.1" />
<param index="5" name="normalize" type="bool" default="true" />
<description>
Returns an [Image] containing seamless 2D noise values.
[b]Note:[/b] With [param normalize] set to [code]false[/code], the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code].
</description>
</method>
<method name="get_seamless_image_3d" qualifiers="const">
<return type="Image[]" />
<param index="0" name="width" type="int" />
<param index="1" name="height" type="int" />
<param index="2" name="depth" type="int" />
<param index="3" name="invert" type="bool" default="false" />
<param index="4" name="in_3d_space" type="bool" default="false" />
<param index="5" name="skirt" type="float" default="0.1" />
<param index="6" name="normalize" type="bool" default="true" />
<param index="4" name="skirt" type="float" default="0.1" />
<param index="5" name="normalize" type="bool" default="true" />
<description>
Returns a seamless 2D [Image] noise image.
Note: With [param normalize] set to [code]false[/code] the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code].
Returns an [Array] of [Image]s containing seamless 3D noise values for use with [method ImageTexture3D.create].
[b]Note:[/b] With [param normalize] set to [code]false[/code], the default implementation expects the noise generator to return values in the range [code]-1.0[/code] to [code]1.0[/code].
</description>
</method>
</methods>
Expand Down
3 changes: 1 addition & 2 deletions modules/noise/doc_classes/NoiseTexture3D.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@
</brief_description>
<description>
Uses [FastNoiseLite] or other libraries to fill the texture data of your desired size.
The class uses [Thread]s to generate the texture data internally, so [method Texture3D.get_data] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image and the generated byte data:
The class uses [Thread]s to generate the texture data internally, so [method Texture3D.get_data] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image:
[codeblock]
var texture = NoiseTexture3D.new()
texture.noise = FastNoiseLite.new()
await texture.changed
var data = texture.get_data()
var image = data[0]
[/codeblock]
</description>
<tutorials>
Expand Down
152 changes: 108 additions & 44 deletions modules/noise/noise.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,40 @@

#include <float.h>

Ref<Image> Noise::get_seamless_image(int p_width, int p_height, int p_depth, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt, bool p_normalize) const {
ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0, Ref<Image>());
Vector<Ref<Image>> Noise::_get_seamless_image(int p_width, int p_height, int p_depth, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt, bool p_normalize) const {
ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0 || p_depth <= 0, Vector<Ref<Image>>());

int skirt_width = MAX(1, p_width * p_blend_skirt);
int skirt_height = MAX(1, p_height * p_blend_skirt);
int skirt_depth = MAX(1, p_depth * p_blend_skirt);
int src_width = p_width + skirt_width;
int src_height = p_height + skirt_height;
int src_depth = p_depth + skirt_depth;

Vector<Ref<Image>> src = _get_image(src_width, src_height, src_depth, p_invert, p_in_3d_space, p_normalize);
bool grayscale = (src[0]->get_format() == Image::FORMAT_L8);

Ref<Image> src = get_image(src_width, src_height, p_depth, p_invert, p_in_3d_space, p_normalize);
bool grayscale = (src->get_format() == Image::FORMAT_L8);
if (grayscale) {
return _generate_seamless_image<uint8_t>(src, p_width, p_height, p_invert, p_blend_skirt);
return _generate_seamless_image<uint8_t>(src, p_width, p_height, p_depth, p_invert, p_blend_skirt);
} else {
return _generate_seamless_image<uint32_t>(src, p_width, p_height, p_invert, p_blend_skirt);
return _generate_seamless_image<uint32_t>(src, p_width, p_height, p_depth, p_invert, p_blend_skirt);
}
}

Ref<Image> Noise::get_seamless_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, real_t p_blend_skirt, bool p_normalize) const {
Vector<Ref<Image>> images = _get_seamless_image(p_width, p_height, 1, p_invert, p_in_3d_space, p_blend_skirt, p_normalize);
return images[0];
}

TypedArray<Image> Noise::get_seamless_image_3d(int p_width, int p_height, int p_depth, bool p_invert, real_t p_blend_skirt, bool p_normalize) const {
Vector<Ref<Image>> images = _get_seamless_image(p_width, p_height, p_depth, p_invert, true, p_blend_skirt, p_normalize);

TypedArray<Image> ret;
ret.resize(images.size());
for (int i = 0; i < images.size(); i++) {
ret[i] = images[i];
}
return ret;
}

// Template specialization for faster grayscale blending.
Expand All @@ -58,61 +77,104 @@ uint8_t Noise::_alpha_blend<uint8_t>(uint8_t p_bg, uint8_t p_fg, int p_alpha) co
return (uint8_t)((alpha * p_fg + inv_alpha * p_bg) >> 8);
}

Ref<Image> Noise::get_image(int p_width, int p_height, int p_depth, bool p_invert, bool p_in_3d_space, bool p_normalize) const {
ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0, Ref<Image>());

Vector<uint8_t> data;
data.resize(p_width * p_height);
Vector<Ref<Image>> Noise::_get_image(int p_width, int p_height, int p_depth, bool p_invert, bool p_in_3d_space, bool p_normalize) const {
ERR_FAIL_COND_V(p_width <= 0 || p_height <= 0 || p_depth <= 0, Vector<Ref<Image>>());

uint8_t *wd8 = data.ptrw();
Vector<Ref<Image>> images;
images.resize(p_depth);

if (p_normalize) {
// Get all values and identify min/max values.
Vector<real_t> values;
values.resize(p_width * p_height);
LocalVector<real_t> values;
values.resize(p_width * p_height * p_depth);

real_t min_val = FLT_MAX;
real_t max_val = -FLT_MAX;
for (int y = 0, i = 0; y < p_height; y++) {
for (int x = 0; x < p_width; x++, i++) {
values.set(i, p_in_3d_space ? get_noise_3d(x, y, p_depth) : get_noise_2d(x, y));
if (values[i] > max_val) {
max_val = values[i];
}
if (values[i] < min_val) {
min_val = values[i];
int idx = 0;
for (int d = 0; d < p_depth; d++) {
for (int y = 0; y < p_height; y++) {
for (int x = 0; x < p_width; x++) {
values[idx] = p_in_3d_space ? get_noise_3d(x, y, d) : get_noise_2d(x, y);
if (values[idx] > max_val) {
max_val = values[idx];
}
if (values[idx] < min_val) {
min_val = values[idx];
}
idx++;
}
}
}
idx = 0;
// Normalize values and write to texture.
uint8_t ivalue;
for (int i = 0, x = 0; i < p_height; i++) {
for (int j = 0; j < p_width; j++, x++) {
if (max_val == min_val) {
ivalue = 0;
} else {
ivalue = static_cast<uint8_t>(CLAMP((values[x] - min_val) / (max_val - min_val) * 255.f, 0, 255));
}

if (p_invert) {
ivalue = 255 - ivalue;
for (int d = 0; d < p_depth; d++) {
Vector<uint8_t> data;
data.resize(p_width * p_height);

uint8_t *wd8 = data.ptrw();
uint8_t ivalue;

for (int y = 0; y < p_height; y++) {
for (int x = 0; x < p_width; x++) {
if (max_val == min_val) {
ivalue = 0;
} else {
ivalue = static_cast<uint8_t>(CLAMP((values[idx] - min_val) / (max_val - min_val) * 255.f, 0, 255));
}

if (p_invert) {
ivalue = 255 - ivalue;
}

wd8[x + y * p_width] = ivalue;
idx++;
}

wd8[x] = ivalue;
}
Ref<Image> img = memnew(Image(p_width, p_height, false, Image::FORMAT_L8, data));
images.write[d] = img;
}
} else {
// Without normalization, the expected range of the noise function is [-1, 1].
uint8_t ivalue;
for (int y = 0, i = 0; y < p_height; y++) {
for (int x = 0; x < p_width; x++, i++) {
float value = (p_in_3d_space ? get_noise_3d(x, y, p_depth) : get_noise_2d(x, y));
ivalue = static_cast<uint8_t>(CLAMP(value * 127.5f + 127.5f, 0.0f, 255.0f));
wd8[i] = p_invert ? (255 - ivalue) : ivalue;

for (int d = 0; d < p_depth; d++) {
Vector<uint8_t> data;
data.resize(p_width * p_height);

uint8_t *wd8 = data.ptrw();

uint8_t ivalue;
int idx = 0;
for (int y = 0; y < p_height; y++) {
for (int x = 0; x < p_width; x++) {
float value = (p_in_3d_space ? get_noise_3d(x, y, d) : get_noise_2d(x, y));
ivalue = static_cast<uint8_t>(CLAMP(value * 127.5f + 127.5f, 0.0f, 255.0f));
wd8[idx] = p_invert ? (255 - ivalue) : ivalue;
idx++;
}
}

Ref<Image> img = memnew(Image(p_width, p_height, false, Image::FORMAT_L8, data));
images.write[d] = img;
}
}

return memnew(Image(p_width, p_height, false, Image::FORMAT_L8, data));
return images;
}

Ref<Image> Noise::get_image(int p_width, int p_height, bool p_invert, bool p_in_3d_space, bool p_normalize) const {
Vector<Ref<Image>> images = _get_image(p_width, p_height, 1, p_invert, p_in_3d_space, p_normalize);
return images[0];
}

TypedArray<Image> Noise::get_image_3d(int p_width, int p_height, int p_depth, bool p_invert, bool p_normalize) const {
Vector<Ref<Image>> images = _get_image(p_width, p_height, p_depth, p_invert, true, p_normalize);

TypedArray<Image> ret;
ret.resize(images.size());
for (int i = 0; i < images.size(); i++) {
ret[i] = images[i];
}
return ret;
}

void Noise::_bind_methods() {
Expand All @@ -124,6 +186,8 @@ void Noise::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_noise_3dv", "v"), &Noise::get_noise_3dv);

// Textures.
ClassDB::bind_method(D_METHOD("get_image", "width", "height", "depth", "invert", "in_3d_space", "normalize"), &Noise::get_image, DEFVAL(false), DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_seamless_image", "width", "height", "depth", "invert", "in_3d_space", "skirt", "normalize"), &Noise::get_seamless_image, DEFVAL(false), DEFVAL(false), DEFVAL(0.1), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_image", "width", "height", "invert", "in_3d_space", "normalize"), &Noise::get_image, DEFVAL(false), DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_seamless_image", "width", "height", "invert", "in_3d_space", "skirt", "normalize"), &Noise::get_seamless_image, DEFVAL(false), DEFVAL(false), DEFVAL(0.1), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_image_3d", "width", "height", "depth", "invert", "normalize"), &Noise::get_image_3d, DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_seamless_image_3d", "width", "height", "depth", "invert", "skirt", "normalize"), &Noise::get_seamless_image_3d, DEFVAL(false), DEFVAL(0.1), DEFVAL(true));
}
Loading