feat: godot-engine-source-4.3-stable

This commit is contained in:
Jan van der Weide 2025-01-17 16:36:38 +01:00
parent c59a7dcade
commit 7125d019b5
11149 changed files with 5070401 additions and 0 deletions

View file

@ -0,0 +1,17 @@
#!/usr/bin/env python
Import("env")
Import("env_modules")
env_noise = env_modules.Clone()
thirdparty_dir = "#thirdparty/noise/"
env_noise.Prepend(CPPPATH=[thirdparty_dir])
# Godot source files
module_obj = []
env_noise.add_source_files(module_obj, "*.cpp")
env_noise.add_source_files(module_obj, "editor/*.cpp")
env.modules_sources += module_obj

View file

@ -0,0 +1,19 @@
def can_build(env, platform):
return True
def configure(env):
pass
def get_doc_classes():
return [
"FastNoiseLite",
"Noise",
"NoiseTexture2D",
"NoiseTexture3D",
]
def get_doc_path():
return "doc_classes"

View file

@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="FastNoiseLite" inherits="Noise" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Generates noise using the FastNoiseLite library.
</brief_description>
<description>
This class generates noise using the FastNoiseLite library, which is a collection of several noise algorithms including Cellular, Perlin, Value, and more.
Most generated noise values are in the range of [code][-1, 1][/code], but not always. Some of the cellular noise algorithms return results above [code]1[/code].
</description>
<tutorials>
</tutorials>
<members>
<member name="cellular_distance_function" type="int" setter="set_cellular_distance_function" getter="get_cellular_distance_function" enum="FastNoiseLite.CellularDistanceFunction" default="0">
Determines how the distance to the nearest/second-nearest point is computed. See [enum CellularDistanceFunction] for options.
</member>
<member name="cellular_jitter" type="float" setter="set_cellular_jitter" getter="get_cellular_jitter" default="1.0">
Maximum distance a point can move off of its grid position. Set to [code]0[/code] for an even grid.
</member>
<member name="cellular_return_type" type="int" setter="set_cellular_return_type" getter="get_cellular_return_type" enum="FastNoiseLite.CellularReturnType" default="1">
Return type from cellular noise calculations. See [enum CellularReturnType].
</member>
<member name="domain_warp_amplitude" type="float" setter="set_domain_warp_amplitude" getter="get_domain_warp_amplitude" default="30.0">
Sets the maximum warp distance from the origin.
</member>
<member name="domain_warp_enabled" type="bool" setter="set_domain_warp_enabled" getter="is_domain_warp_enabled" default="false">
If enabled, another FastNoiseLite instance is used to warp the space, resulting in a distortion of the noise.
</member>
<member name="domain_warp_fractal_gain" type="float" setter="set_domain_warp_fractal_gain" getter="get_domain_warp_fractal_gain" default="0.5">
Determines the strength of each subsequent layer of the noise which is used to warp the space.
A low value places more emphasis on the lower frequency base layers, while a high value puts more emphasis on the higher frequency layers.
</member>
<member name="domain_warp_fractal_lacunarity" type="float" setter="set_domain_warp_fractal_lacunarity" getter="get_domain_warp_fractal_lacunarity" default="6.0">
Octave lacunarity of the fractal noise which warps the space. Increasing this value results in higher octaves producing noise with finer details and a rougher appearance.
</member>
<member name="domain_warp_fractal_octaves" type="int" setter="set_domain_warp_fractal_octaves" getter="get_domain_warp_fractal_octaves" default="5">
The number of noise layers that are sampled to get the final value for the fractal noise which warps the space.
</member>
<member name="domain_warp_fractal_type" type="int" setter="set_domain_warp_fractal_type" getter="get_domain_warp_fractal_type" enum="FastNoiseLite.DomainWarpFractalType" default="1">
The method for combining octaves into a fractal which is used to warp the space. See [enum DomainWarpFractalType].
</member>
<member name="domain_warp_frequency" type="float" setter="set_domain_warp_frequency" getter="get_domain_warp_frequency" default="0.05">
Frequency of the noise which warps the space. Low frequency results in smooth noise while high frequency results in rougher, more granular noise.
</member>
<member name="domain_warp_type" type="int" setter="set_domain_warp_type" getter="get_domain_warp_type" enum="FastNoiseLite.DomainWarpType" default="0">
Sets the warp algorithm. See [enum DomainWarpType].
</member>
<member name="fractal_gain" type="float" setter="set_fractal_gain" getter="get_fractal_gain" default="0.5">
Determines the strength of each subsequent layer of noise in fractal noise.
A low value places more emphasis on the lower frequency base layers, while a high value puts more emphasis on the higher frequency layers.
</member>
<member name="fractal_lacunarity" type="float" setter="set_fractal_lacunarity" getter="get_fractal_lacunarity" default="2.0">
Frequency multiplier between subsequent octaves. Increasing this value results in higher octaves producing noise with finer details and a rougher appearance.
</member>
<member name="fractal_octaves" type="int" setter="set_fractal_octaves" getter="get_fractal_octaves" default="5">
The number of noise layers that are sampled to get the final value for fractal noise types.
</member>
<member name="fractal_ping_pong_strength" type="float" setter="set_fractal_ping_pong_strength" getter="get_fractal_ping_pong_strength" default="2.0">
Sets the strength of the fractal ping pong type.
</member>
<member name="fractal_type" type="int" setter="set_fractal_type" getter="get_fractal_type" enum="FastNoiseLite.FractalType" default="1">
The method for combining octaves into a fractal. See [enum FractalType].
</member>
<member name="fractal_weighted_strength" type="float" setter="set_fractal_weighted_strength" getter="get_fractal_weighted_strength" default="0.0">
Higher weighting means higher octaves have less impact if lower octaves have a large impact.
</member>
<member name="frequency" type="float" setter="set_frequency" getter="get_frequency" default="0.01">
The frequency for all noise types. Low frequency results in smooth noise while high frequency results in rougher, more granular noise.
</member>
<member name="noise_type" type="int" setter="set_noise_type" getter="get_noise_type" enum="FastNoiseLite.NoiseType" default="1">
The noise algorithm used. See [enum NoiseType].
</member>
<member name="offset" type="Vector3" setter="set_offset" getter="get_offset" default="Vector3(0, 0, 0)">
Translate the noise input coordinates by the given [Vector3].
</member>
<member name="seed" type="int" setter="set_seed" getter="get_seed" default="0">
The random number seed for all noise types.
</member>
</members>
<constants>
<constant name="TYPE_VALUE" value="5" enum="NoiseType">
A lattice of points are assigned random values then interpolated based on neighboring values.
</constant>
<constant name="TYPE_VALUE_CUBIC" value="4" enum="NoiseType">
Similar to Value noise, but slower. Has more variance in peaks and valleys.
Cubic noise can be used to avoid certain artifacts when using value noise to create a bumpmap. In general, you should always use this mode if the value noise is being used for a heightmap or bumpmap.
</constant>
<constant name="TYPE_PERLIN" value="3" enum="NoiseType">
A lattice of random gradients. Their dot products are interpolated to obtain values in between the lattices.
</constant>
<constant name="TYPE_CELLULAR" value="2" enum="NoiseType">
Cellular includes both Worley noise and Voronoi diagrams which creates various regions of the same value.
</constant>
<constant name="TYPE_SIMPLEX" value="0" enum="NoiseType">
As opposed to [constant TYPE_PERLIN], gradients exist in a simplex lattice rather than a grid lattice, avoiding directional artifacts.
</constant>
<constant name="TYPE_SIMPLEX_SMOOTH" value="1" enum="NoiseType">
Modified, higher quality version of [constant TYPE_SIMPLEX], but slower.
</constant>
<constant name="FRACTAL_NONE" value="0" enum="FractalType">
No fractal noise.
</constant>
<constant name="FRACTAL_FBM" value="1" enum="FractalType">
Method using Fractional Brownian Motion to combine octaves into a fractal.
</constant>
<constant name="FRACTAL_RIDGED" value="2" enum="FractalType">
Method of combining octaves into a fractal resulting in a "ridged" look.
</constant>
<constant name="FRACTAL_PING_PONG" value="3" enum="FractalType">
Method of combining octaves into a fractal with a ping pong effect.
</constant>
<constant name="DISTANCE_EUCLIDEAN" value="0" enum="CellularDistanceFunction">
Euclidean distance to the nearest point.
</constant>
<constant name="DISTANCE_EUCLIDEAN_SQUARED" value="1" enum="CellularDistanceFunction">
Squared Euclidean distance to the nearest point.
</constant>
<constant name="DISTANCE_MANHATTAN" value="2" enum="CellularDistanceFunction">
Manhattan distance (taxicab metric) to the nearest point.
</constant>
<constant name="DISTANCE_HYBRID" value="3" enum="CellularDistanceFunction">
Blend of [constant DISTANCE_EUCLIDEAN] and [constant DISTANCE_MANHATTAN] to give curved cell boundaries
</constant>
<constant name="RETURN_CELL_VALUE" value="0" enum="CellularReturnType">
The cellular distance function will return the same value for all points within a cell.
</constant>
<constant name="RETURN_DISTANCE" value="1" enum="CellularReturnType">
The cellular distance function will return a value determined by the distance to the nearest point.
</constant>
<constant name="RETURN_DISTANCE2" value="2" enum="CellularReturnType">
The cellular distance function returns the distance to the second-nearest point.
</constant>
<constant name="RETURN_DISTANCE2_ADD" value="3" enum="CellularReturnType">
The distance to the nearest point is added to the distance to the second-nearest point.
</constant>
<constant name="RETURN_DISTANCE2_SUB" value="4" enum="CellularReturnType">
The distance to the nearest point is subtracted from the distance to the second-nearest point.
</constant>
<constant name="RETURN_DISTANCE2_MUL" value="5" enum="CellularReturnType">
The distance to the nearest point is multiplied with the distance to the second-nearest point.
</constant>
<constant name="RETURN_DISTANCE2_DIV" value="6" enum="CellularReturnType">
The distance to the nearest point is divided by the distance to the second-nearest point.
</constant>
<constant name="DOMAIN_WARP_SIMPLEX" value="0" enum="DomainWarpType">
The domain is warped using the simplex noise algorithm.
</constant>
<constant name="DOMAIN_WARP_SIMPLEX_REDUCED" value="1" enum="DomainWarpType">
The domain is warped using a simplified version of the simplex noise algorithm.
</constant>
<constant name="DOMAIN_WARP_BASIC_GRID" value="2" enum="DomainWarpType">
The domain is warped using a simple noise grid (not as smooth as the other methods, but more performant).
</constant>
<constant name="DOMAIN_WARP_FRACTAL_NONE" value="0" enum="DomainWarpFractalType">
No fractal noise for warping the space.
</constant>
<constant name="DOMAIN_WARP_FRACTAL_PROGRESSIVE" value="1" enum="DomainWarpFractalType">
Warping the space progressively, octave for octave, resulting in a more "liquified" distortion.
</constant>
<constant name="DOMAIN_WARP_FRACTAL_INDEPENDENT" value="2" enum="DomainWarpFractalType">
Warping the space independently for each octave, resulting in a more chaotic distortion.
</constant>
</constants>
</class>

View file

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Noise" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Abstract base class for noise generators.
</brief_description>
<description>
This class defines the interface for noise generation libraries to inherit from.
A default [method get_seamless_image] implementation is provided for libraries that do not provide seamless noise. This function requests a larger image from the [method get_image] method, reverses the quadrants of the image, then uses the strips of extra width to blend over the seams.
Inheriting noise classes can optionally override this function to provide a more optimal algorithm.
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_image" qualifiers="const">
<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="normalize" type="bool" default="true" />
<description>
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">
<return type="float" />
<param index="0" name="x" type="float" />
<description>
Returns the 1D noise value at the given (x) coordinate.
</description>
</method>
<method name="get_noise_2d" qualifiers="const">
<return type="float" />
<param index="0" name="x" type="float" />
<param index="1" name="y" type="float" />
<description>
Returns the 2D noise value at the given position.
</description>
</method>
<method name="get_noise_2dv" qualifiers="const">
<return type="float" />
<param index="0" name="v" type="Vector2" />
<description>
Returns the 2D noise value at the given position.
</description>
</method>
<method name="get_noise_3d" qualifiers="const">
<return type="float" />
<param index="0" name="x" type="float" />
<param index="1" name="y" type="float" />
<param index="2" name="z" type="float" />
<description>
Returns the 3D noise value at the given position.
</description>
</method>
<method name="get_noise_3dv" qualifiers="const">
<return type="float" />
<param index="0" name="v" type="Vector3" />
<description>
Returns the 3D noise value at the given position.
</description>
</method>
<method name="get_seamless_image" qualifiers="const">
<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="skirt" type="float" default="0.1" />
<param index="5" name="normalize" type="bool" default="true" />
<description>
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>
</class>

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NoiseTexture2D" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A 2D texture filled with noise generated by a [Noise] object.
</brief_description>
<description>
Uses the [FastNoiseLite] library or other noise generators to fill the texture data of your desired size. [NoiseTexture2D] can also generate normal map textures.
The class uses [Thread]s to generate the texture data internally, so [method Texture2D.get_image] 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:
[codeblock]
var texture = NoiseTexture2D.new()
texture.noise = FastNoiseLite.new()
await texture.changed
var image = texture.get_image()
var data = image.get_data()
[/codeblock]
</description>
<tutorials>
</tutorials>
<members>
<member name="as_normal_map" type="bool" setter="set_as_normal_map" getter="is_normal_map" default="false">
If [code]true[/code], the resulting texture contains a normal map created from the original noise interpreted as a bump map.
</member>
<member name="bump_strength" type="float" setter="set_bump_strength" getter="get_bump_strength" default="8.0">
Strength of the bump maps used in this texture. A higher value will make the bump maps appear larger while a lower value will make them appear softer.
</member>
<member name="color_ramp" type="Gradient" setter="set_color_ramp" getter="get_color_ramp">
A [Gradient] which is used to map the luminance of each pixel to a color value.
</member>
<member name="generate_mipmaps" type="bool" setter="set_generate_mipmaps" getter="is_generating_mipmaps" default="true">
Determines whether mipmaps are generated for this texture. Enabling this results in less texture aliasing in the distance, at the cost of increasing memory usage by roughly 33% and making the noise texture generation take longer.
[b]Note:[/b] [member generate_mipmaps] requires mipmap filtering to be enabled on the material using the [NoiseTexture2D] to have an effect.
</member>
<member name="height" type="int" setter="set_height" getter="get_height" default="512">
Height of the generated texture (in pixels).
</member>
<member name="in_3d_space" type="bool" setter="set_in_3d_space" getter="is_in_3d_space" default="false">
Determines whether the noise image is calculated in 3D space. May result in reduced contrast.
</member>
<member name="invert" type="bool" setter="set_invert" getter="get_invert" default="false">
If [code]true[/code], inverts the noise texture. White becomes black, black becomes white.
</member>
<member name="noise" type="Noise" setter="set_noise" getter="get_noise">
The instance of the [Noise] object.
</member>
<member name="normalize" type="bool" setter="set_normalize" getter="is_normalized" default="true">
If [code]true[/code], the noise image coming from the noise generator is normalized to the range [code]0.0[/code] to [code]1.0[/code].
Turning normalization off can affect the contrast and allows you to generate non repeating tileable noise textures.
</member>
<member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="false" />
<member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false">
If [code]true[/code], a seamless texture is requested from the [Noise] resource.
[b]Note:[/b] Seamless noise textures may take longer to generate and/or can have a lower contrast compared to non-seamless noise depending on the used [Noise] resource. This is because some implementations use higher dimensions for generating seamless noise.
[b]Note:[/b] The default [FastNoiseLite] implementation uses the fallback path for seamless generation. If using a [member width] or [member height] lower than the default, you may need to increase [member seamless_blend_skirt] to make seamless blending more effective.
</member>
<member name="seamless_blend_skirt" type="float" setter="set_seamless_blend_skirt" getter="get_seamless_blend_skirt" default="0.1">
Used for the default/fallback implementation of the seamless texture generation. It determines the distance over which the seams are blended. High values may result in less details and contrast. See [Noise] for further details.
[b]Note:[/b] If using a [member width] or [member height] lower than the default, you may need to increase [member seamless_blend_skirt] to make seamless blending more effective.
</member>
<member name="width" type="int" setter="set_width" getter="get_width" default="512">
Width of the generated texture (in pixels).
</member>
</members>
</class>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="NoiseTexture3D" inherits="Texture3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A 3D texture filled with noise generated by a [Noise] object.
</brief_description>
<description>
Uses the [FastNoiseLite] library or other noise generators 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:
[codeblock]
var texture = NoiseTexture3D.new()
texture.noise = FastNoiseLite.new()
await texture.changed
var data = texture.get_data()
[/codeblock]
</description>
<tutorials>
</tutorials>
<members>
<member name="color_ramp" type="Gradient" setter="set_color_ramp" getter="get_color_ramp">
A [Gradient] which is used to map the luminance of each pixel to a color value.
</member>
<member name="depth" type="int" setter="set_depth" getter="get_depth" default="64">
Depth of the generated texture (in pixels).
</member>
<member name="height" type="int" setter="set_height" getter="get_height" default="64">
Height of the generated texture (in pixels).
</member>
<member name="invert" type="bool" setter="set_invert" getter="get_invert" default="false">
If [code]true[/code], inverts the noise texture. White becomes black, black becomes white.
</member>
<member name="noise" type="Noise" setter="set_noise" getter="get_noise">
The instance of the [Noise] object.
</member>
<member name="normalize" type="bool" setter="set_normalize" getter="is_normalized" default="true">
If [code]true[/code], the noise image coming from the noise generator is normalized to the range [code]0.0[/code] to [code]1.0[/code].
Turning normalization off can affect the contrast and allows you to generate non repeating tileable noise textures.
</member>
<member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false">
If [code]true[/code], a seamless texture is requested from the [Noise] resource.
[b]Note:[/b] Seamless noise textures may take longer to generate and/or can have a lower contrast compared to non-seamless noise depending on the used [Noise] resource. This is because some implementations use higher dimensions for generating seamless noise.
[b]Note:[/b] The default [FastNoiseLite] implementation uses the fallback path for seamless generation. If using a [member width], [member height] or [member depth] lower than the default, you may need to increase [member seamless_blend_skirt] to make seamless blending more effective.
</member>
<member name="seamless_blend_skirt" type="float" setter="set_seamless_blend_skirt" getter="get_seamless_blend_skirt" default="0.1">
Used for the default/fallback implementation of the seamless texture generation. It determines the distance over which the seams are blended. High values may result in less details and contrast. See [Noise] for further details.
[b]Note:[/b] If using a [member width], [member height] or [member depth] lower than the default, you may need to increase [member seamless_blend_skirt] to make seamless blending more effective.
</member>
<member name="width" type="int" setter="set_width" getter="get_width" default="64">
Width of the generated texture (in pixels).
</member>
</members>
</class>

View file

@ -0,0 +1,152 @@
/**************************************************************************/
/* noise_editor_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "noise_editor_plugin.h"
#ifdef TOOLS_ENABLED
#include "../noise.h"
#include "../noise_texture_2d.h"
#include "editor/editor_inspector.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/button.h"
#include "scene/gui/texture_rect.h"
class NoisePreview : public Control {
GDCLASS(NoisePreview, Control)
static const int PREVIEW_HEIGHT = 150;
static const int PADDING_3D_SPACE_SWITCH = 2;
Ref<Noise> _noise;
Size2i _preview_texture_size;
TextureRect *_texture_rect = nullptr;
Button *_3d_space_switch = nullptr;
public:
NoisePreview() {
set_custom_minimum_size(Size2(0, EDSCALE * PREVIEW_HEIGHT));
_texture_rect = memnew(TextureRect);
_texture_rect->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
_texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_COVERED);
add_child(_texture_rect);
_3d_space_switch = memnew(Button);
_3d_space_switch->set_text(TTR("3D"));
_3d_space_switch->set_tooltip_text(TTR("Toggles whether the noise preview is computed in 3D space."));
_3d_space_switch->set_toggle_mode(true);
_3d_space_switch->set_offset(SIDE_LEFT, PADDING_3D_SPACE_SWITCH);
_3d_space_switch->set_offset(SIDE_TOP, PADDING_3D_SPACE_SWITCH);
_3d_space_switch->connect(SceneStringName(pressed), callable_mp(this, &NoisePreview::_on_3d_button_pressed));
add_child(_3d_space_switch);
}
void set_noise(Ref<Noise> noise) {
if (_noise == noise) {
return;
}
_noise = noise;
if (_noise.is_valid()) {
if (_noise->has_meta("_preview_in_3d_space_")) {
_3d_space_switch->set_pressed(true);
}
update_preview();
}
}
private:
void _on_3d_button_pressed() {
if (_3d_space_switch->is_pressed()) {
_noise->set_meta("_preview_in_3d_space_", true);
} else {
_noise->remove_meta("_preview_in_3d_space_");
}
}
void _notification(int p_what) {
switch (p_what) {
case NOTIFICATION_RESIZED: {
_preview_texture_size = get_size();
update_preview();
} break;
}
}
void update_preview() {
if (MIN(_preview_texture_size.width, _preview_texture_size.height) > 0) {
Ref<NoiseTexture2D> tex;
tex.instantiate();
tex->set_width(_preview_texture_size.width);
tex->set_height(_preview_texture_size.height);
tex->set_in_3d_space(_3d_space_switch->is_pressed());
tex->set_noise(_noise);
_texture_rect->set_texture(tex);
}
}
};
/////////////////////////////////////////////////////////////////////////////////
class NoiseEditorInspectorPlugin : public EditorInspectorPlugin {
GDCLASS(NoiseEditorInspectorPlugin, EditorInspectorPlugin)
public:
bool can_handle(Object *p_object) override {
return Object::cast_to<Noise>(p_object) != nullptr;
}
void parse_begin(Object *p_object) override {
Noise *noise_ptr = Object::cast_to<Noise>(p_object);
if (noise_ptr) {
Ref<Noise> noise(noise_ptr);
NoisePreview *viewer = memnew(NoisePreview);
viewer->set_noise(noise);
add_custom_control(viewer);
}
}
};
/////////////////////////////////////////////////////////////////////////////////
String NoiseEditorPlugin::get_name() const {
return Noise::get_class_static();
}
NoiseEditorPlugin::NoiseEditorPlugin() {
Ref<NoiseEditorInspectorPlugin> plugin;
plugin.instantiate();
add_inspector_plugin(plugin);
}
#endif // TOOLS_ENABLED

View file

@ -0,0 +1,49 @@
/**************************************************************************/
/* noise_editor_plugin.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef NOISE_EDITOR_PLUGIN_H
#define NOISE_EDITOR_PLUGIN_H
#ifdef TOOLS_ENABLED
#include "editor/plugins/editor_plugin.h"
class NoiseEditorPlugin : public EditorPlugin {
GDCLASS(NoiseEditorPlugin, EditorPlugin)
public:
String get_name() const override;
NoiseEditorPlugin();
};
#endif // TOOLS_ENABLED
#endif // NOISE_EDITOR_PLUGIN_H

View file

@ -0,0 +1,499 @@
/**************************************************************************/
/* fastnoise_lite.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "fastnoise_lite.h"
_FastNoiseLite::FractalType FastNoiseLite::_convert_domain_warp_fractal_type_enum(DomainWarpFractalType p_domain_warp_fractal_type) {
_FastNoiseLite::FractalType type;
switch (p_domain_warp_fractal_type) {
case DOMAIN_WARP_FRACTAL_NONE:
type = _FastNoiseLite::FractalType_None;
break;
case DOMAIN_WARP_FRACTAL_PROGRESSIVE:
type = _FastNoiseLite::FractalType_DomainWarpProgressive;
break;
case DOMAIN_WARP_FRACTAL_INDEPENDENT:
type = _FastNoiseLite::FractalType_DomainWarpIndependent;
break;
default:
type = _FastNoiseLite::FractalType_None;
}
return type;
}
FastNoiseLite::FastNoiseLite() {
_noise.SetNoiseType((_FastNoiseLite::NoiseType)noise_type);
_noise.SetSeed(seed);
_noise.SetFrequency(frequency);
_noise.SetFractalType((_FastNoiseLite::FractalType)fractal_type);
_noise.SetFractalOctaves(fractal_octaves);
_noise.SetFractalLacunarity(fractal_lacunarity);
_noise.SetFractalGain(fractal_gain);
_noise.SetFractalWeightedStrength(fractal_weighted_strength);
_noise.SetFractalPingPongStrength(fractal_ping_pong_strength);
_noise.SetCellularDistanceFunction((_FastNoiseLite::CellularDistanceFunction)cellular_distance_function);
_noise.SetCellularReturnType((_FastNoiseLite::CellularReturnType)cellular_return_type);
_noise.SetCellularJitter(cellular_jitter);
_domain_warp_noise.SetDomainWarpType((_FastNoiseLite::DomainWarpType)domain_warp_type);
_domain_warp_noise.SetSeed(seed);
_domain_warp_noise.SetDomainWarpAmp(domain_warp_amplitude);
_domain_warp_noise.SetFrequency(domain_warp_frequency);
_domain_warp_noise.SetFractalType(_convert_domain_warp_fractal_type_enum(domain_warp_fractal_type));
_domain_warp_noise.SetFractalOctaves(domain_warp_fractal_octaves);
_domain_warp_noise.SetFractalLacunarity(domain_warp_fractal_lacunarity);
_domain_warp_noise.SetFractalGain(domain_warp_fractal_gain);
}
FastNoiseLite::~FastNoiseLite() {
}
// General settings.
void FastNoiseLite::set_noise_type(NoiseType p_noise_type) {
noise_type = p_noise_type;
_noise.SetNoiseType((_FastNoiseLite::NoiseType)p_noise_type);
emit_changed();
notify_property_list_changed();
}
FastNoiseLite::NoiseType FastNoiseLite::get_noise_type() const {
return noise_type;
}
void FastNoiseLite::set_seed(int p_seed) {
seed = p_seed;
_noise.SetSeed(p_seed);
_domain_warp_noise.SetSeed(p_seed);
emit_changed();
}
int FastNoiseLite::get_seed() const {
return seed;
}
void FastNoiseLite::set_frequency(real_t p_freq) {
frequency = p_freq;
_noise.SetFrequency(p_freq);
emit_changed();
}
real_t FastNoiseLite::get_frequency() const {
return frequency;
}
void FastNoiseLite::set_offset(Vector3 p_offset) {
offset = p_offset;
emit_changed();
}
Vector3 FastNoiseLite::get_offset() const {
return offset;
}
// Fractal.
void FastNoiseLite::set_fractal_type(FractalType p_type) {
fractal_type = p_type;
_noise.SetFractalType((_FastNoiseLite::FractalType)p_type);
emit_changed();
notify_property_list_changed();
}
FastNoiseLite::FractalType FastNoiseLite::get_fractal_type() const {
return fractal_type;
}
void FastNoiseLite::set_fractal_octaves(int p_octaves) {
fractal_octaves = p_octaves;
_noise.SetFractalOctaves(p_octaves);
emit_changed();
}
int FastNoiseLite::get_fractal_octaves() const {
return fractal_octaves;
}
void FastNoiseLite::set_fractal_lacunarity(real_t p_lacunarity) {
fractal_lacunarity = p_lacunarity;
_noise.SetFractalLacunarity(p_lacunarity);
emit_changed();
}
real_t FastNoiseLite::get_fractal_lacunarity() const {
return fractal_lacunarity;
}
void FastNoiseLite::set_fractal_gain(real_t p_gain) {
fractal_gain = p_gain;
_noise.SetFractalGain(p_gain);
emit_changed();
}
real_t FastNoiseLite::get_fractal_gain() const {
return fractal_gain;
}
void FastNoiseLite::set_fractal_weighted_strength(real_t p_weighted_strength) {
fractal_weighted_strength = p_weighted_strength;
_noise.SetFractalWeightedStrength(p_weighted_strength);
emit_changed();
}
real_t FastNoiseLite::get_fractal_weighted_strength() const {
return fractal_weighted_strength;
}
void FastNoiseLite::set_fractal_ping_pong_strength(real_t p_ping_pong_strength) {
fractal_ping_pong_strength = p_ping_pong_strength;
_noise.SetFractalPingPongStrength(p_ping_pong_strength);
emit_changed();
}
real_t FastNoiseLite::get_fractal_ping_pong_strength() const {
return fractal_ping_pong_strength;
}
// Cellular.
void FastNoiseLite::set_cellular_distance_function(CellularDistanceFunction p_func) {
cellular_distance_function = p_func;
_noise.SetCellularDistanceFunction((_FastNoiseLite::CellularDistanceFunction)p_func);
emit_changed();
}
FastNoiseLite::CellularDistanceFunction FastNoiseLite::get_cellular_distance_function() const {
return cellular_distance_function;
}
void FastNoiseLite::set_cellular_jitter(real_t p_jitter) {
cellular_jitter = p_jitter;
_noise.SetCellularJitter(p_jitter);
emit_changed();
}
real_t FastNoiseLite::get_cellular_jitter() const {
return cellular_jitter;
}
void FastNoiseLite::set_cellular_return_type(CellularReturnType p_ret) {
cellular_return_type = p_ret;
_noise.SetCellularReturnType((_FastNoiseLite::CellularReturnType)p_ret);
emit_changed();
}
FastNoiseLite::CellularReturnType FastNoiseLite::get_cellular_return_type() const {
return cellular_return_type;
}
// Domain warp specific.
void FastNoiseLite::set_domain_warp_enabled(bool p_enabled) {
if (domain_warp_enabled != p_enabled) {
domain_warp_enabled = p_enabled;
emit_changed();
notify_property_list_changed();
}
}
bool FastNoiseLite::is_domain_warp_enabled() const {
return domain_warp_enabled;
}
void FastNoiseLite::set_domain_warp_type(DomainWarpType p_domain_warp_type) {
domain_warp_type = p_domain_warp_type;
_domain_warp_noise.SetDomainWarpType((_FastNoiseLite::DomainWarpType)p_domain_warp_type);
emit_changed();
}
FastNoiseLite::DomainWarpType FastNoiseLite::get_domain_warp_type() const {
return domain_warp_type;
}
void FastNoiseLite::set_domain_warp_amplitude(real_t p_amplitude) {
domain_warp_amplitude = p_amplitude;
_domain_warp_noise.SetDomainWarpAmp(p_amplitude);
emit_changed();
}
real_t FastNoiseLite::get_domain_warp_amplitude() const {
return domain_warp_amplitude;
}
void FastNoiseLite::set_domain_warp_frequency(real_t p_frequency) {
domain_warp_frequency = p_frequency;
_domain_warp_noise.SetFrequency(p_frequency);
emit_changed();
}
real_t FastNoiseLite::get_domain_warp_frequency() const {
return domain_warp_frequency;
}
void FastNoiseLite::set_domain_warp_fractal_type(DomainWarpFractalType p_domain_warp_fractal_type) {
domain_warp_fractal_type = p_domain_warp_fractal_type;
_domain_warp_noise.SetFractalType(_convert_domain_warp_fractal_type_enum(p_domain_warp_fractal_type));
emit_changed();
}
FastNoiseLite::DomainWarpFractalType FastNoiseLite::get_domain_warp_fractal_type() const {
return domain_warp_fractal_type;
}
void FastNoiseLite::set_domain_warp_fractal_octaves(int p_octaves) {
domain_warp_fractal_octaves = p_octaves;
_domain_warp_noise.SetFractalOctaves(p_octaves);
emit_changed();
}
int FastNoiseLite::get_domain_warp_fractal_octaves() const {
return domain_warp_fractal_octaves;
}
void FastNoiseLite::set_domain_warp_fractal_lacunarity(real_t p_lacunarity) {
domain_warp_fractal_lacunarity = p_lacunarity;
_domain_warp_noise.SetFractalLacunarity(p_lacunarity);
emit_changed();
}
real_t FastNoiseLite::get_domain_warp_fractal_lacunarity() const {
return domain_warp_fractal_lacunarity;
}
void FastNoiseLite::set_domain_warp_fractal_gain(real_t p_gain) {
domain_warp_fractal_gain = p_gain;
_domain_warp_noise.SetFractalGain(p_gain);
emit_changed();
}
real_t FastNoiseLite::get_domain_warp_fractal_gain() const {
return domain_warp_fractal_gain;
}
// Noise interface functions.
real_t FastNoiseLite::get_noise_1d(real_t p_x) const {
p_x += offset.x;
if (domain_warp_enabled) {
// Needed since DomainWarp expects a reference.
real_t y_dummy = 0;
_domain_warp_noise.DomainWarp(p_x, y_dummy);
}
return get_noise_2d(p_x, 0.0);
}
real_t FastNoiseLite::get_noise_2dv(Vector2 p_v) const {
return get_noise_2d(p_v.x, p_v.y);
}
real_t FastNoiseLite::get_noise_2d(real_t p_x, real_t p_y) const {
p_x += offset.x;
p_y += offset.y;
if (domain_warp_enabled) {
_domain_warp_noise.DomainWarp(p_x, p_y);
}
return _noise.GetNoise(p_x, p_y);
}
real_t FastNoiseLite::get_noise_3dv(Vector3 p_v) const {
return get_noise_3d(p_v.x, p_v.y, p_v.z);
}
real_t FastNoiseLite::get_noise_3d(real_t p_x, real_t p_y, real_t p_z) const {
p_x += offset.x;
p_y += offset.y;
p_z += offset.z;
if (domain_warp_enabled) {
_domain_warp_noise.DomainWarp(p_x, p_y, p_z);
}
return _noise.GetNoise(p_x, p_y, p_z);
}
void FastNoiseLite::_changed() {
emit_changed();
}
void FastNoiseLite::_bind_methods() {
// General settings.
ClassDB::bind_method(D_METHOD("set_noise_type", "type"), &FastNoiseLite::set_noise_type);
ClassDB::bind_method(D_METHOD("get_noise_type"), &FastNoiseLite::get_noise_type);
ClassDB::bind_method(D_METHOD("set_seed", "seed"), &FastNoiseLite::set_seed);
ClassDB::bind_method(D_METHOD("get_seed"), &FastNoiseLite::get_seed);
ClassDB::bind_method(D_METHOD("set_frequency", "freq"), &FastNoiseLite::set_frequency);
ClassDB::bind_method(D_METHOD("get_frequency"), &FastNoiseLite::get_frequency);
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &FastNoiseLite::set_offset);
ClassDB::bind_method(D_METHOD("get_offset"), &FastNoiseLite::get_offset);
// Fractal.
ClassDB::bind_method(D_METHOD("set_fractal_type", "type"), &FastNoiseLite::set_fractal_type);
ClassDB::bind_method(D_METHOD("get_fractal_type"), &FastNoiseLite::get_fractal_type);
ClassDB::bind_method(D_METHOD("set_fractal_octaves", "octave_count"), &FastNoiseLite::set_fractal_octaves);
ClassDB::bind_method(D_METHOD("get_fractal_octaves"), &FastNoiseLite::get_fractal_octaves);
ClassDB::bind_method(D_METHOD("set_fractal_lacunarity", "lacunarity"), &FastNoiseLite::set_fractal_lacunarity);
ClassDB::bind_method(D_METHOD("get_fractal_lacunarity"), &FastNoiseLite::get_fractal_lacunarity);
ClassDB::bind_method(D_METHOD("set_fractal_gain", "gain"), &FastNoiseLite::set_fractal_gain);
ClassDB::bind_method(D_METHOD("get_fractal_gain"), &FastNoiseLite::get_fractal_gain);
ClassDB::bind_method(D_METHOD("set_fractal_weighted_strength", "weighted_strength"), &FastNoiseLite::set_fractal_weighted_strength);
ClassDB::bind_method(D_METHOD("get_fractal_weighted_strength"), &FastNoiseLite::get_fractal_weighted_strength);
ClassDB::bind_method(D_METHOD("set_fractal_ping_pong_strength", "ping_pong_strength"), &FastNoiseLite::set_fractal_ping_pong_strength);
ClassDB::bind_method(D_METHOD("get_fractal_ping_pong_strength"), &FastNoiseLite::get_fractal_ping_pong_strength);
// Cellular.
ClassDB::bind_method(D_METHOD("set_cellular_distance_function", "func"), &FastNoiseLite::set_cellular_distance_function);
ClassDB::bind_method(D_METHOD("get_cellular_distance_function"), &FastNoiseLite::get_cellular_distance_function);
ClassDB::bind_method(D_METHOD("set_cellular_jitter", "jitter"), &FastNoiseLite::set_cellular_jitter);
ClassDB::bind_method(D_METHOD("get_cellular_jitter"), &FastNoiseLite::get_cellular_jitter);
ClassDB::bind_method(D_METHOD("set_cellular_return_type", "ret"), &FastNoiseLite::set_cellular_return_type);
ClassDB::bind_method(D_METHOD("get_cellular_return_type"), &FastNoiseLite::get_cellular_return_type);
// Domain warp.
ClassDB::bind_method(D_METHOD("set_domain_warp_enabled", "domain_warp_enabled"), &FastNoiseLite::set_domain_warp_enabled);
ClassDB::bind_method(D_METHOD("is_domain_warp_enabled"), &FastNoiseLite::is_domain_warp_enabled);
ClassDB::bind_method(D_METHOD("set_domain_warp_type", "domain_warp_type"), &FastNoiseLite::set_domain_warp_type);
ClassDB::bind_method(D_METHOD("get_domain_warp_type"), &FastNoiseLite::get_domain_warp_type);
ClassDB::bind_method(D_METHOD("set_domain_warp_amplitude", "domain_warp_amplitude"), &FastNoiseLite::set_domain_warp_amplitude);
ClassDB::bind_method(D_METHOD("get_domain_warp_amplitude"), &FastNoiseLite::get_domain_warp_amplitude);
ClassDB::bind_method(D_METHOD("set_domain_warp_frequency", "domain_warp_frequency"), &FastNoiseLite::set_domain_warp_frequency);
ClassDB::bind_method(D_METHOD("get_domain_warp_frequency"), &FastNoiseLite::get_domain_warp_frequency);
ClassDB::bind_method(D_METHOD("set_domain_warp_fractal_type", "domain_warp_fractal_type"), &FastNoiseLite::set_domain_warp_fractal_type);
ClassDB::bind_method(D_METHOD("get_domain_warp_fractal_type"), &FastNoiseLite::get_domain_warp_fractal_type);
ClassDB::bind_method(D_METHOD("set_domain_warp_fractal_octaves", "domain_warp_octave_count"), &FastNoiseLite::set_domain_warp_fractal_octaves);
ClassDB::bind_method(D_METHOD("get_domain_warp_fractal_octaves"), &FastNoiseLite::get_domain_warp_fractal_octaves);
ClassDB::bind_method(D_METHOD("set_domain_warp_fractal_lacunarity", "domain_warp_lacunarity"), &FastNoiseLite::set_domain_warp_fractal_lacunarity);
ClassDB::bind_method(D_METHOD("get_domain_warp_fractal_lacunarity"), &FastNoiseLite::get_domain_warp_fractal_lacunarity);
ClassDB::bind_method(D_METHOD("set_domain_warp_fractal_gain", "domain_warp_gain"), &FastNoiseLite::set_domain_warp_fractal_gain);
ClassDB::bind_method(D_METHOD("get_domain_warp_fractal_gain"), &FastNoiseLite::get_domain_warp_fractal_gain);
ClassDB::bind_method(D_METHOD("_changed"), &FastNoiseLite::_changed);
ADD_PROPERTY(PropertyInfo(Variant::INT, "noise_type", PROPERTY_HINT_ENUM, "Simplex,Simplex Smooth,Cellular,Perlin,Value Cubic,Value"), "set_noise_type", "get_noise_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frequency", PROPERTY_HINT_RANGE, ".0001,1,.0001,exp"), "set_frequency", "get_frequency");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "offset", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_less,or_greater"), "set_offset", "get_offset");
ADD_GROUP("Fractal", "fractal_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fractal_type", PROPERTY_HINT_ENUM, "None,FBM,Ridged,Ping-Pong"), "set_fractal_type", "get_fractal_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fractal_octaves", PROPERTY_HINT_RANGE, "1,10,1"), "set_fractal_octaves", "get_fractal_octaves");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fractal_lacunarity"), "set_fractal_lacunarity", "get_fractal_lacunarity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fractal_gain"), "set_fractal_gain", "get_fractal_gain");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fractal_weighted_strength", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_fractal_weighted_strength", "get_fractal_weighted_strength");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fractal_ping_pong_strength"), "set_fractal_ping_pong_strength", "get_fractal_ping_pong_strength");
ADD_GROUP("Cellular", "cellular_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cellular_distance_function", PROPERTY_HINT_ENUM, "Euclidean,Euclidean Squared,Manhattan,Hybrid"), "set_cellular_distance_function", "get_cellular_distance_function");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cellular_jitter"), "set_cellular_jitter", "get_cellular_jitter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cellular_return_type", PROPERTY_HINT_ENUM, "Cell Value,Distance,Distance2,Distance2Add,Distance2Sub,Distance2Mul,Distance2Div"), "set_cellular_return_type", "get_cellular_return_type");
ADD_GROUP("Domain Warp", "domain_warp_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "domain_warp_enabled"), "set_domain_warp_enabled", "is_domain_warp_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "domain_warp_type", PROPERTY_HINT_ENUM, "Simplex,Simplex Reduced,Basic Grid"), "set_domain_warp_type", "get_domain_warp_type");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "domain_warp_amplitude"), "set_domain_warp_amplitude", "get_domain_warp_amplitude");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "domain_warp_frequency"), "set_domain_warp_frequency", "get_domain_warp_frequency");
ADD_PROPERTY(PropertyInfo(Variant::INT, "domain_warp_fractal_type", PROPERTY_HINT_ENUM, "None,Progressive,Independent"), "set_domain_warp_fractal_type", "get_domain_warp_fractal_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "domain_warp_fractal_octaves", PROPERTY_HINT_RANGE, "1,10,1"), "set_domain_warp_fractal_octaves", "get_domain_warp_fractal_octaves");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "domain_warp_fractal_lacunarity"), "set_domain_warp_fractal_lacunarity", "get_domain_warp_fractal_lacunarity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "domain_warp_fractal_gain"), "set_domain_warp_fractal_gain", "get_domain_warp_fractal_gain");
BIND_ENUM_CONSTANT(TYPE_VALUE);
BIND_ENUM_CONSTANT(TYPE_VALUE_CUBIC);
BIND_ENUM_CONSTANT(TYPE_PERLIN);
BIND_ENUM_CONSTANT(TYPE_CELLULAR);
BIND_ENUM_CONSTANT(TYPE_SIMPLEX);
BIND_ENUM_CONSTANT(TYPE_SIMPLEX_SMOOTH);
BIND_ENUM_CONSTANT(FRACTAL_NONE);
BIND_ENUM_CONSTANT(FRACTAL_FBM);
BIND_ENUM_CONSTANT(FRACTAL_RIDGED);
BIND_ENUM_CONSTANT(FRACTAL_PING_PONG);
BIND_ENUM_CONSTANT(DISTANCE_EUCLIDEAN);
BIND_ENUM_CONSTANT(DISTANCE_EUCLIDEAN_SQUARED);
BIND_ENUM_CONSTANT(DISTANCE_MANHATTAN);
BIND_ENUM_CONSTANT(DISTANCE_HYBRID);
BIND_ENUM_CONSTANT(RETURN_CELL_VALUE);
BIND_ENUM_CONSTANT(RETURN_DISTANCE);
BIND_ENUM_CONSTANT(RETURN_DISTANCE2);
BIND_ENUM_CONSTANT(RETURN_DISTANCE2_ADD);
BIND_ENUM_CONSTANT(RETURN_DISTANCE2_SUB);
BIND_ENUM_CONSTANT(RETURN_DISTANCE2_MUL);
BIND_ENUM_CONSTANT(RETURN_DISTANCE2_DIV);
BIND_ENUM_CONSTANT(DOMAIN_WARP_SIMPLEX);
BIND_ENUM_CONSTANT(DOMAIN_WARP_SIMPLEX_REDUCED);
BIND_ENUM_CONSTANT(DOMAIN_WARP_BASIC_GRID);
BIND_ENUM_CONSTANT(DOMAIN_WARP_FRACTAL_NONE);
BIND_ENUM_CONSTANT(DOMAIN_WARP_FRACTAL_PROGRESSIVE);
BIND_ENUM_CONSTANT(DOMAIN_WARP_FRACTAL_INDEPENDENT);
}
void FastNoiseLite::_validate_property(PropertyInfo &p_property) const {
if (p_property.name.begins_with("cellular") && get_noise_type() != TYPE_CELLULAR) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
return;
}
if (p_property.name != "fractal_type" && p_property.name.begins_with("fractal") && get_fractal_type() == FRACTAL_NONE) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
return;
}
if (p_property.name == "fractal_ping_pong_strength" && get_fractal_type() != FRACTAL_PING_PONG) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
return;
}
if (p_property.name != "domain_warp_enabled" && p_property.name.begins_with("domain_warp") && !domain_warp_enabled) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
return;
}
}

View file

@ -0,0 +1,228 @@
/**************************************************************************/
/* fastnoise_lite.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef FASTNOISE_LITE_H
#define FASTNOISE_LITE_H
#include "noise.h"
#include "core/io/image.h"
#include "core/object/ref_counted.h"
#include "scene/resources/gradient.h"
#include <thirdparty/noise/FastNoiseLite.h>
typedef fastnoiselite::FastNoiseLite _FastNoiseLite;
class FastNoiseLite : public Noise {
GDCLASS(FastNoiseLite, Noise);
OBJ_SAVE_TYPE(FastNoiseLite);
public:
enum NoiseType {
TYPE_SIMPLEX = _FastNoiseLite::NoiseType_OpenSimplex2,
TYPE_SIMPLEX_SMOOTH = _FastNoiseLite::NoiseType_OpenSimplex2S,
TYPE_CELLULAR = _FastNoiseLite::NoiseType_Cellular,
TYPE_PERLIN = _FastNoiseLite::NoiseType_Perlin,
TYPE_VALUE_CUBIC = _FastNoiseLite::NoiseType_ValueCubic,
TYPE_VALUE = _FastNoiseLite::NoiseType_Value,
};
enum FractalType {
FRACTAL_NONE = _FastNoiseLite::FractalType_None,
FRACTAL_FBM = _FastNoiseLite::FractalType_FBm,
FRACTAL_RIDGED = _FastNoiseLite::FractalType_Ridged,
FRACTAL_PING_PONG = _FastNoiseLite::FractalType_PingPong,
};
enum CellularDistanceFunction {
DISTANCE_EUCLIDEAN = _FastNoiseLite::CellularDistanceFunction_Euclidean,
DISTANCE_EUCLIDEAN_SQUARED = _FastNoiseLite::CellularDistanceFunction_EuclideanSq,
DISTANCE_MANHATTAN = _FastNoiseLite::CellularDistanceFunction_Manhattan,
DISTANCE_HYBRID = _FastNoiseLite::CellularDistanceFunction_Hybrid
};
enum CellularReturnType {
RETURN_CELL_VALUE = _FastNoiseLite::CellularReturnType_CellValue,
RETURN_DISTANCE = _FastNoiseLite::CellularReturnType_Distance,
RETURN_DISTANCE2 = _FastNoiseLite::CellularReturnType_Distance2,
RETURN_DISTANCE2_ADD = _FastNoiseLite::CellularReturnType_Distance2Add,
RETURN_DISTANCE2_SUB = _FastNoiseLite::CellularReturnType_Distance2Sub,
RETURN_DISTANCE2_MUL = _FastNoiseLite::CellularReturnType_Distance2Mul,
RETURN_DISTANCE2_DIV = _FastNoiseLite::CellularReturnType_Distance2Div
};
enum DomainWarpType {
DOMAIN_WARP_SIMPLEX = _FastNoiseLite::DomainWarpType_OpenSimplex2,
DOMAIN_WARP_SIMPLEX_REDUCED = _FastNoiseLite::DomainWarpType_OpenSimplex2Reduced,
DOMAIN_WARP_BASIC_GRID = _FastNoiseLite::DomainWarpType_BasicGrid
};
enum DomainWarpFractalType {
DOMAIN_WARP_FRACTAL_NONE,
DOMAIN_WARP_FRACTAL_PROGRESSIVE,
DOMAIN_WARP_FRACTAL_INDEPENDENT
};
protected:
static void _bind_methods();
void _validate_property(PropertyInfo &p_property) const;
private:
_FastNoiseLite _noise;
_FastNoiseLite _domain_warp_noise;
Vector3 offset;
NoiseType noise_type = TYPE_SIMPLEX_SMOOTH;
int seed = 0;
real_t frequency = 0.01;
// Fractal specific.
FractalType fractal_type = FRACTAL_FBM;
int fractal_octaves = 5;
real_t fractal_lacunarity = 2;
real_t fractal_gain = 0.5;
real_t fractal_weighted_strength = 0;
real_t fractal_ping_pong_strength = 2;
// Cellular specific.
CellularDistanceFunction cellular_distance_function = DISTANCE_EUCLIDEAN;
CellularReturnType cellular_return_type = RETURN_DISTANCE;
real_t cellular_jitter = 1.0;
// Domain warp specific.
bool domain_warp_enabled = false;
DomainWarpType domain_warp_type = DOMAIN_WARP_SIMPLEX;
real_t domain_warp_amplitude = 30.0;
real_t domain_warp_frequency = 0.05;
DomainWarpFractalType domain_warp_fractal_type = DOMAIN_WARP_FRACTAL_PROGRESSIVE;
int domain_warp_fractal_octaves = 5;
real_t domain_warp_fractal_lacunarity = 6;
real_t domain_warp_fractal_gain = 0.5;
// This needs manual conversion because Godots Inspector property API does not support discontiguous enum indices.
_FastNoiseLite::FractalType _convert_domain_warp_fractal_type_enum(DomainWarpFractalType p_domain_warp_fractal_type);
public:
FastNoiseLite();
~FastNoiseLite();
// General noise settings.
void set_noise_type(NoiseType p_noise_type);
NoiseType get_noise_type() const;
void set_seed(int p_seed);
int get_seed() const;
void set_frequency(real_t p_freq);
real_t get_frequency() const;
void set_offset(Vector3 p_offset);
Vector3 get_offset() const;
// Fractal specific.
void set_fractal_type(FractalType p_type);
FractalType get_fractal_type() const;
void set_fractal_octaves(int p_octaves);
int get_fractal_octaves() const;
void set_fractal_lacunarity(real_t p_lacunarity);
real_t get_fractal_lacunarity() const;
void set_fractal_gain(real_t p_gain);
real_t get_fractal_gain() const;
void set_fractal_weighted_strength(real_t p_weighted_strength);
real_t get_fractal_weighted_strength() const;
void set_fractal_ping_pong_strength(real_t p_ping_pong_strength);
real_t get_fractal_ping_pong_strength() const;
// Cellular specific.
void set_cellular_distance_function(CellularDistanceFunction p_func);
CellularDistanceFunction get_cellular_distance_function() const;
void set_cellular_return_type(CellularReturnType p_ret);
CellularReturnType get_cellular_return_type() const;
void set_cellular_jitter(real_t p_jitter);
real_t get_cellular_jitter() const;
// Domain warp specific.
void set_domain_warp_enabled(bool p_enabled);
bool is_domain_warp_enabled() const;
void set_domain_warp_type(DomainWarpType p_domain_warp_type);
DomainWarpType get_domain_warp_type() const;
void set_domain_warp_amplitude(real_t p_amplitude);
real_t get_domain_warp_amplitude() const;
void set_domain_warp_frequency(real_t p_frequency);
real_t get_domain_warp_frequency() const;
void set_domain_warp_fractal_type(DomainWarpFractalType p_domain_warp_fractal_type);
DomainWarpFractalType get_domain_warp_fractal_type() const;
void set_domain_warp_fractal_octaves(int p_octaves);
int get_domain_warp_fractal_octaves() const;
void set_domain_warp_fractal_lacunarity(real_t p_lacunarity);
real_t get_domain_warp_fractal_lacunarity() const;
void set_domain_warp_fractal_gain(real_t p_gain);
real_t get_domain_warp_fractal_gain() const;
// Interface methods.
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;
void _changed();
};
VARIANT_ENUM_CAST(FastNoiseLite::NoiseType);
VARIANT_ENUM_CAST(FastNoiseLite::FractalType);
VARIANT_ENUM_CAST(FastNoiseLite::CellularDistanceFunction);
VARIANT_ENUM_CAST(FastNoiseLite::CellularReturnType);
VARIANT_ENUM_CAST(FastNoiseLite::DomainWarpType);
VARIANT_ENUM_CAST(FastNoiseLite::DomainWarpFractalType);
#endif // FASTNOISE_LITE_H

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zm1 2h10v8H3zm3 1v2h2V4zm2 2v2h2v2h2V4h-2v2zm0 2H6V6H4v4h4z"/></svg>

After

Width:  |  Height:  |  Size: 223 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M1 14a1 1 0 0 0 1 1h9.5a1 1 0 0 0 .707-.293l2.5-2.5A1 1 0 0 0 15 11.5V2a1 1 0 0 0-1-1H4.5a1 1 0 0 0-.707.293l-2.5 2.5A1 1 0 0 0 1 4.5zm1.25-9H11v7H2.25zm10 6.25v-6.5L14 3v6.5zm-1-7.5H3L4.75 2H13zM3 11h4l1.25-1.25V9H9l1.25-1.25v-2h-2L7 7v.75h-.75v-2h-2L3 7z"/><path fill-opacity=".4" d="M3 7h2l1.25-1.25h-2zm2 2h2V7.75h-.75zm2-2h2l1.25-1.25H8z"/><path fill-opacity=".2" d="M5 7v2l1.25-1.25v-2zm2 2v2l1.25-1.25V9zm2 0V7l1.25-1.25v2z"/></svg>

After

Width:  |  Height:  |  Size: 526 B

View file

@ -0,0 +1,199 @@
/**************************************************************************/
/* noise.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "noise.h"
#include <float.h>
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);
if (grayscale) {
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_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);
if (images.size() == 0) {
return Ref<Image>();
}
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.
template <>
uint8_t Noise::_alpha_blend<uint8_t>(uint8_t p_bg, uint8_t p_fg, int p_alpha) const {
uint16_t alpha = p_alpha + 1;
uint16_t inv_alpha = 256 - p_alpha;
return (uint8_t)((alpha * p_fg + inv_alpha * p_bg) >> 8);
}
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>>());
Vector<Ref<Image>> images;
images.resize(p_depth);
if (p_normalize) {
// Get all values and identify min/max values.
LocalVector<real_t> values;
values.resize(p_width * p_height * p_depth);
real_t min_val = FLT_MAX;
real_t max_val = -FLT_MAX;
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.
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++;
}
}
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].
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 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);
if (images.is_empty()) {
return Ref<Image>();
}
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() {
// Noise functions.
ClassDB::bind_method(D_METHOD("get_noise_1d", "x"), &Noise::get_noise_1d);
ClassDB::bind_method(D_METHOD("get_noise_2d", "x", "y"), &Noise::get_noise_2d);
ClassDB::bind_method(D_METHOD("get_noise_2dv", "v"), &Noise::get_noise_2dv);
ClassDB::bind_method(D_METHOD("get_noise_3d", "x", "y", "z"), &Noise::get_noise_3d);
ClassDB::bind_method(D_METHOD("get_noise_3dv", "v"), &Noise::get_noise_3dv);
// Textures.
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));
}

View file

@ -0,0 +1,302 @@
/**************************************************************************/
/* noise.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef NOISE_H
#define NOISE_H
#include "core/io/image.h"
#include "core/variant/typed_array.h"
class Noise : public Resource {
GDCLASS(Noise, Resource);
// Helper struct for get_seamless_image(). See comments in .cpp for usage.
template <typename T>
struct img_buff {
T *img = nullptr;
int width; // Array dimensions & default modulo for image.
int height;
int offset_x; // Offset index location on image (wrapped by specified modulo).
int offset_y;
int alt_width; // Alternate module for image.
int alt_height;
enum ALT_MODULO {
DEFAULT = 0,
ALT_X,
ALT_Y,
ALT_XY
};
// Multi-dimensional array indexer (e.g. img[x][y]) that supports multiple modulos.
T &operator()(int x, int y, ALT_MODULO mode = DEFAULT) {
switch (mode) {
case ALT_XY:
return img[(x + offset_x) % alt_width + ((y + offset_y) % alt_height) * width];
case ALT_X:
return img[(x + offset_x) % alt_width + ((y + offset_y) % height) * width];
case ALT_Y:
return img[(x + offset_x) % width + ((y + offset_y) % alt_height) * width];
default:
return img[(x + offset_x) % width + ((y + offset_y) % height) * width];
}
}
};
union l2c {
uint32_t l;
uint8_t c[4];
struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
};
template <typename T>
Vector<Ref<Image>> _generate_seamless_image(Vector<Ref<Image>> p_src, int p_width, int p_height, int p_depth, bool p_invert, real_t p_blend_skirt) const {
/*
To make a seamless image, we swap the quadrants so the edges are perfect matches.
We initially get a 10% larger image so we have an overlap we can use to blend over the seams.
Noise::img_buff::operator() acts as a multi-dimensional array indexer.
It does the array math, translates between the flipped and non-flipped quadrants, and manages offsets and modulos.
Here is how the larger source image and final output image map to each other:
Output size = p_width*p_height Source w/ extra 10% skirt `s` size = src_width*src_height
Q1 Q2 Q4 Q3 s1
Q3 Q4 Q2 Q1 s2
s5 s4 s3
All of the loops use output coordinates, so Output:Q1 == Source:Q1
Ex: Output(half_width, half_height) [the midpoint, corner of Q1/Q4] =>
on Source it's translated to
corner of Q1/s3 unless the ALT_XY modulo moves it to Q4
*/
ERR_FAIL_COND_V(p_blend_skirt < 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 src_width = p_width + skirt_width;
int src_height = p_height + skirt_height;
int half_width = p_width * 0.5;
int half_height = p_height * 0.5;
int skirt_edge_x = half_width + skirt_width;
int skirt_edge_y = half_height + skirt_height;
Image::Format format = p_src[0]->get_format();
int pixel_size = Image::get_format_pixel_size(format);
Vector<Ref<Image>> images;
images.resize(p_src.size());
// First blend across x and y for all slices.
for (int d = 0; d < images.size(); d++) {
Vector<uint8_t> dest;
dest.resize(p_width * p_height * pixel_size);
img_buff<T> rd_src = {
(T *)p_src[d]->get_data().ptr(),
src_width, src_height,
half_width, half_height,
p_width, p_height
};
// `wr` is setup for straight x/y coordinate array access.
img_buff<T> wr = {
(T *)dest.ptrw(),
p_width, p_height,
0, 0, 0, 0
};
// `rd_dest` is a readable pointer to `wr`, i.e. what has already been written to the output buffer.
img_buff<T> rd_dest = {
(T *)dest.ptr(),
p_width, p_height,
0, 0, 0, 0
};
// Swap the quadrants to make edges seamless.
for (int y = 0; y < p_height; y++) {
for (int x = 0; x < p_width; x++) {
// rd_src has a half offset and the shorter modulo ignores the skirt.
// It reads and writes in Q1-4 order (see map above), skipping the skirt.
wr(x, y) = rd_src(x, y, img_buff<T>::ALT_XY);
}
}
// Blend the vertical skirt over the middle seam.
for (int x = half_width; x < skirt_edge_x; x++) {
int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(x - half_width) / float(skirt_width)));
for (int y = 0; y < p_height; y++) {
// Skip the center square
if (y == half_height) {
y = skirt_edge_y - 1;
} else {
// Starts reading at s2, ALT_Y skips s3, and continues with s1.
wr(x, y) = _alpha_blend<T>(rd_dest(x, y), rd_src(x, y, img_buff<T>::ALT_Y), alpha);
}
}
}
// Blend the horizontal skirt over the middle seam.
for (int y = half_height; y < skirt_edge_y; y++) {
int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(y - half_height) / float(skirt_height)));
for (int x = 0; x < p_width; x++) {
// Skip the center square
if (x == half_width) {
x = skirt_edge_x - 1;
} else {
// Starts reading at s4, skips s3, continues with s5.
wr(x, y) = _alpha_blend<T>(rd_dest(x, y), rd_src(x, y, img_buff<T>::ALT_X), alpha);
}
}
}
// Fill in the center square. Wr starts at the top left of Q4, which is the equivalent of the top left of s3, unless a modulo is used.
for (int y = half_height; y < skirt_edge_y; y++) {
for (int x = half_width; x < skirt_edge_x; x++) {
int xpos = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(x - half_width) / float(skirt_width)));
int ypos = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(y - half_height) / float(skirt_height)));
// Blend s3(Q1) onto s5(Q2) for the top half.
T top_blend = _alpha_blend<T>(rd_src(x, y, img_buff<T>::ALT_X), rd_src(x, y, img_buff<T>::DEFAULT), xpos);
// Blend s1(Q3) onto Q4 for the bottom half.
T bottom_blend = _alpha_blend<T>(rd_src(x, y, img_buff<T>::ALT_XY), rd_src(x, y, img_buff<T>::ALT_Y), xpos);
// Blend the top half onto the bottom half.
wr(x, y) = _alpha_blend<T>(bottom_blend, top_blend, ypos);
}
}
Ref<Image> image = memnew(Image(p_width, p_height, false, format, dest));
p_src.write[d].unref();
images.write[d] = image;
}
// Now blend across z.
if (p_depth > 1) {
int skirt_depth = MAX(1, p_depth * p_blend_skirt);
int half_depth = p_depth * 0.5;
int skirt_edge_z = half_depth + skirt_depth;
// Swap halves on depth.
for (int i = 0; i < half_depth; i++) {
Ref<Image> img = images[i];
images.write[i] = images[i + half_depth];
images.write[i + half_depth] = img;
}
Vector<Ref<Image>> new_images = images;
new_images.resize(p_depth);
// Scale seamless generation to third dimension.
for (int z = half_depth; z < skirt_edge_z; z++) {
int alpha = 255 * (1 - Math::smoothstep(0.1f, 0.9f, float(z - half_depth) / float(skirt_depth)));
Vector<uint8_t> img = images[z % p_depth]->get_data();
Vector<uint8_t> skirt = images[(z - half_depth) + p_depth]->get_data();
Vector<uint8_t> dest;
dest.resize(images[0]->get_width() * images[0]->get_height() * Image::get_format_pixel_size(images[0]->get_format()));
for (int i = 0; i < img.size(); i++) {
uint8_t fg, bg, out;
fg = skirt[i];
bg = img[i];
uint16_t a = alpha + 1;
uint16_t inv_a = 256 - alpha;
out = (uint8_t)((a * fg + inv_a * bg) >> 8);
dest.write[i] = out;
}
Ref<Image> new_image = memnew(Image(images[0]->get_width(), images[0]->get_height(), false, images[0]->get_format(), dest));
new_images.write[z % p_depth] = new_image;
}
return new_images;
}
return images;
}
template <typename T>
T _alpha_blend(T p_bg, T p_fg, int p_alpha) const {
l2c fg, bg, out;
fg.l = p_fg;
bg.l = p_bg;
uint16_t alpha;
uint16_t inv_alpha;
// If no alpha argument specified, use the alpha channel in the color
if (p_alpha == -1) {
alpha = fg.c[3] + 1;
inv_alpha = 256 - fg.c[3];
} else {
alpha = p_alpha + 1;
inv_alpha = 256 - p_alpha;
}
out.c[0] = (uint8_t)((alpha * fg.c[0] + inv_alpha * bg.c[0]) >> 8);
out.c[1] = (uint8_t)((alpha * fg.c[1] + inv_alpha * bg.c[1]) >> 8);
out.c[2] = (uint8_t)((alpha * fg.c[2] + inv_alpha * bg.c[2]) >> 8);
out.c[3] = 0xFF;
return out.l;
}
protected:
static void _bind_methods();
public:
// Virtual destructor so we can delete any Noise derived object when referenced as a Noise*.
virtual ~Noise() {}
virtual real_t get_noise_1d(real_t p_x) const = 0;
virtual real_t get_noise_2dv(Vector2 p_v) const = 0;
virtual real_t get_noise_2d(real_t p_x, real_t p_y) const = 0;
virtual real_t get_noise_3dv(Vector3 p_v) const = 0;
virtual real_t get_noise_3d(real_t p_x, real_t p_y, real_t p_z) const = 0;
Vector<Ref<Image>> _get_image(int p_width, int p_height, int p_depth, bool p_invert = false, bool p_in_3d_space = false, bool p_normalize = true) const;
virtual Ref<Image> get_image(int p_width, int p_height, bool p_invert = false, bool p_in_3d_space = false, bool p_normalize = true) const;
virtual TypedArray<Image> get_image_3d(int p_width, int p_height, int p_depth, bool p_invert = false, bool p_normalize = true) const;
Vector<Ref<Image>> _get_seamless_image(int p_width, int p_height, int p_depth, bool p_invert = false, bool p_in_3d_space = false, real_t p_blend_skirt = 0.1, bool p_normalize = true) const;
virtual Ref<Image> 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;
virtual TypedArray<Image> 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;
};
#endif // NOISE_H

View file

@ -0,0 +1,388 @@
/**************************************************************************/
/* noise_texture_2d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "noise_texture_2d.h"
#include "noise.h"
NoiseTexture2D::NoiseTexture2D() {
noise = Ref<Noise>();
_queue_update();
}
NoiseTexture2D::~NoiseTexture2D() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
if (texture.is_valid()) {
RS::get_singleton()->free(texture);
}
if (noise_thread.is_started()) {
noise_thread.wait_to_finish();
}
}
void NoiseTexture2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_width", "width"), &NoiseTexture2D::set_width);
ClassDB::bind_method(D_METHOD("set_height", "height"), &NoiseTexture2D::set_height);
ClassDB::bind_method(D_METHOD("set_invert", "invert"), &NoiseTexture2D::set_invert);
ClassDB::bind_method(D_METHOD("get_invert"), &NoiseTexture2D::get_invert);
ClassDB::bind_method(D_METHOD("set_in_3d_space", "enable"), &NoiseTexture2D::set_in_3d_space);
ClassDB::bind_method(D_METHOD("is_in_3d_space"), &NoiseTexture2D::is_in_3d_space);
ClassDB::bind_method(D_METHOD("set_generate_mipmaps", "invert"), &NoiseTexture2D::set_generate_mipmaps);
ClassDB::bind_method(D_METHOD("is_generating_mipmaps"), &NoiseTexture2D::is_generating_mipmaps);
ClassDB::bind_method(D_METHOD("set_seamless", "seamless"), &NoiseTexture2D::set_seamless);
ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture2D::get_seamless);
ClassDB::bind_method(D_METHOD("set_seamless_blend_skirt", "seamless_blend_skirt"), &NoiseTexture2D::set_seamless_blend_skirt);
ClassDB::bind_method(D_METHOD("get_seamless_blend_skirt"), &NoiseTexture2D::get_seamless_blend_skirt);
ClassDB::bind_method(D_METHOD("set_as_normal_map", "as_normal_map"), &NoiseTexture2D::set_as_normal_map);
ClassDB::bind_method(D_METHOD("is_normal_map"), &NoiseTexture2D::is_normal_map);
ClassDB::bind_method(D_METHOD("set_bump_strength", "bump_strength"), &NoiseTexture2D::set_bump_strength);
ClassDB::bind_method(D_METHOD("get_bump_strength"), &NoiseTexture2D::get_bump_strength);
ClassDB::bind_method(D_METHOD("set_normalize", "normalize"), &NoiseTexture2D::set_normalize);
ClassDB::bind_method(D_METHOD("is_normalized"), &NoiseTexture2D::is_normalized);
ClassDB::bind_method(D_METHOD("set_color_ramp", "gradient"), &NoiseTexture2D::set_color_ramp);
ClassDB::bind_method(D_METHOD("get_color_ramp"), &NoiseTexture2D::get_color_ramp);
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture2D::set_noise);
ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture2D::get_noise);
ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_width", "get_width");
ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert"), "set_invert", "get_invert");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "in_3d_space"), "set_in_3d_space", "is_in_3d_space");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps"), "set_generate_mipmaps", "is_generating_mipmaps");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "seamless_blend_skirt", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_seamless_blend_skirt", "get_seamless_blend_skirt");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normal_map"), "set_as_normal_map", "is_normal_map");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bump_strength", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_bump_strength", "get_bump_strength");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "normalize"), "set_normalize", "is_normalized");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "Noise"), "set_noise", "get_noise");
}
void NoiseTexture2D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "bump_strength") {
if (!as_normal_map) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
if (p_property.name == "seamless_blend_skirt") {
if (!seamless) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
}
void NoiseTexture2D::_set_texture_image(const Ref<Image> &p_image) {
image = p_image;
if (image.is_valid()) {
if (texture.is_valid()) {
RID new_texture = RS::get_singleton()->texture_2d_create(p_image);
RS::get_singleton()->texture_replace(texture, new_texture);
} else {
texture = RS::get_singleton()->texture_2d_create(p_image);
}
RS::get_singleton()->texture_set_path(texture, get_path());
}
emit_changed();
}
void NoiseTexture2D::_thread_done(const Ref<Image> &p_image) {
_set_texture_image(p_image);
noise_thread.wait_to_finish();
if (regen_queued) {
noise_thread.start(_thread_function, this);
regen_queued = false;
}
}
void NoiseTexture2D::_thread_function(void *p_ud) {
NoiseTexture2D *tex = static_cast<NoiseTexture2D *>(p_ud);
callable_mp(tex, &NoiseTexture2D::_thread_done).call_deferred(tex->_generate_texture());
}
void NoiseTexture2D::_queue_update() {
if (update_queued) {
return;
}
update_queued = true;
callable_mp(this, &NoiseTexture2D::_update_texture).call_deferred();
}
Ref<Image> NoiseTexture2D::_generate_texture() {
// Prevent memdelete due to unref() on other thread.
Ref<Noise> ref_noise = noise;
if (ref_noise.is_null()) {
return Ref<Image>();
}
Ref<Image> new_image;
if (seamless) {
new_image = ref_noise->get_seamless_image(size.x, size.y, invert, in_3d_space, seamless_blend_skirt, normalize);
} else {
new_image = ref_noise->get_image(size.x, size.y, invert, in_3d_space, normalize);
}
if (color_ramp.is_valid()) {
new_image = _modulate_with_gradient(new_image, color_ramp);
}
if (as_normal_map) {
new_image->bump_map_to_normal_map(bump_strength);
}
if (generate_mipmaps) {
new_image->generate_mipmaps();
}
return new_image;
}
Ref<Image> NoiseTexture2D::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradient> p_gradient) {
int width = p_image->get_width();
int height = p_image->get_height();
Ref<Image> new_image = Image::create_empty(width, height, false, Image::FORMAT_RGBA8);
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
Color pixel_color = p_image->get_pixel(col, row);
Color ramp_color = p_gradient->get_color_at_offset(pixel_color.get_luminance());
new_image->set_pixel(col, row, ramp_color);
}
}
return new_image;
}
void NoiseTexture2D::_update_texture() {
bool use_thread = true;
if (first_time) {
use_thread = false;
first_time = false;
}
if (use_thread) {
if (!noise_thread.is_started()) {
noise_thread.start(_thread_function, this);
regen_queued = false;
} else {
regen_queued = true;
}
} else {
Ref<Image> new_image = _generate_texture();
_set_texture_image(new_image);
}
update_queued = false;
}
void NoiseTexture2D::set_noise(Ref<Noise> p_noise) {
if (p_noise == noise) {
return;
}
if (noise.is_valid()) {
noise->disconnect_changed(callable_mp(this, &NoiseTexture2D::_queue_update));
}
noise = p_noise;
if (noise.is_valid()) {
noise->connect_changed(callable_mp(this, &NoiseTexture2D::_queue_update));
}
_queue_update();
}
Ref<Noise> NoiseTexture2D::get_noise() {
return noise;
}
void NoiseTexture2D::set_width(int p_width) {
ERR_FAIL_COND(p_width <= 0);
if (p_width == size.x) {
return;
}
size.x = p_width;
_queue_update();
}
void NoiseTexture2D::set_height(int p_height) {
ERR_FAIL_COND(p_height <= 0);
if (p_height == size.y) {
return;
}
size.y = p_height;
_queue_update();
}
void NoiseTexture2D::set_invert(bool p_invert) {
if (p_invert == invert) {
return;
}
invert = p_invert;
_queue_update();
}
bool NoiseTexture2D::get_invert() const {
return invert;
}
void NoiseTexture2D::set_in_3d_space(bool p_enable) {
if (p_enable == in_3d_space) {
return;
}
in_3d_space = p_enable;
_queue_update();
}
bool NoiseTexture2D::is_in_3d_space() const {
return in_3d_space;
}
void NoiseTexture2D::set_generate_mipmaps(bool p_enable) {
if (p_enable == generate_mipmaps) {
return;
}
generate_mipmaps = p_enable;
_queue_update();
}
bool NoiseTexture2D::is_generating_mipmaps() const {
return generate_mipmaps;
}
void NoiseTexture2D::set_seamless(bool p_seamless) {
if (p_seamless == seamless) {
return;
}
seamless = p_seamless;
_queue_update();
notify_property_list_changed();
}
bool NoiseTexture2D::get_seamless() {
return seamless;
}
void NoiseTexture2D::set_seamless_blend_skirt(real_t p_blend_skirt) {
ERR_FAIL_COND(p_blend_skirt < 0 || p_blend_skirt > 1);
if (p_blend_skirt == seamless_blend_skirt) {
return;
}
seamless_blend_skirt = p_blend_skirt;
_queue_update();
}
real_t NoiseTexture2D::get_seamless_blend_skirt() {
return seamless_blend_skirt;
}
void NoiseTexture2D::set_as_normal_map(bool p_as_normal_map) {
if (p_as_normal_map == as_normal_map) {
return;
}
as_normal_map = p_as_normal_map;
_queue_update();
notify_property_list_changed();
}
bool NoiseTexture2D::is_normal_map() {
return as_normal_map;
}
void NoiseTexture2D::set_bump_strength(float p_bump_strength) {
if (p_bump_strength == bump_strength) {
return;
}
bump_strength = p_bump_strength;
if (as_normal_map) {
_queue_update();
}
}
float NoiseTexture2D::get_bump_strength() {
return bump_strength;
}
void NoiseTexture2D::set_color_ramp(const Ref<Gradient> &p_gradient) {
if (p_gradient == color_ramp) {
return;
}
if (color_ramp.is_valid()) {
color_ramp->disconnect_changed(callable_mp(this, &NoiseTexture2D::_queue_update));
}
color_ramp = p_gradient;
if (color_ramp.is_valid()) {
color_ramp->connect_changed(callable_mp(this, &NoiseTexture2D::_queue_update));
}
_queue_update();
}
void NoiseTexture2D::set_normalize(bool p_normalize) {
if (normalize == p_normalize) {
return;
}
normalize = p_normalize;
_queue_update();
}
bool NoiseTexture2D::is_normalized() const {
return normalize;
}
Ref<Gradient> NoiseTexture2D::get_color_ramp() const {
return color_ramp;
}
int NoiseTexture2D::get_width() const {
return size.x;
}
int NoiseTexture2D::get_height() const {
return size.y;
}
RID NoiseTexture2D::get_rid() const {
if (!texture.is_valid()) {
texture = RS::get_singleton()->texture_2d_placeholder_create();
}
return texture;
}
Ref<Image> NoiseTexture2D::get_image() const {
return image;
}

View file

@ -0,0 +1,127 @@
/**************************************************************************/
/* noise_texture_2d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef NOISE_TEXTURE_2D_H
#define NOISE_TEXTURE_2D_H
#include "noise.h"
#include "core/object/ref_counted.h"
#include "scene/resources/texture.h"
class NoiseTexture2D : public Texture2D {
GDCLASS(NoiseTexture2D, Texture2D);
private:
Ref<Image> image;
Thread noise_thread;
bool first_time = true;
bool update_queued = false;
bool regen_queued = false;
mutable RID texture;
uint32_t flags = 0;
Size2i size = Size2i(512, 512);
bool invert = false;
bool in_3d_space = false;
bool generate_mipmaps = true;
bool seamless = false;
real_t seamless_blend_skirt = 0.1;
bool as_normal_map = false;
float bump_strength = 8.0;
bool normalize = true;
Ref<Gradient> color_ramp;
Ref<Noise> noise;
void _thread_done(const Ref<Image> &p_image);
static void _thread_function(void *p_ud);
void _queue_update();
Ref<Image> _generate_texture();
void _update_texture();
void _set_texture_image(const Ref<Image> &p_image);
Ref<Image> _modulate_with_gradient(Ref<Image> p_image, Ref<Gradient> p_gradient);
protected:
static void _bind_methods();
void _validate_property(PropertyInfo &p_property) const;
public:
void set_noise(Ref<Noise> p_noise);
Ref<Noise> get_noise();
void set_width(int p_width);
void set_height(int p_height);
void set_invert(bool p_invert);
bool get_invert() const;
void set_in_3d_space(bool p_enable);
bool is_in_3d_space() const;
void set_generate_mipmaps(bool p_enable);
bool is_generating_mipmaps() const;
void set_seamless(bool p_seamless);
bool get_seamless();
void set_seamless_blend_skirt(real_t p_blend_skirt);
real_t get_seamless_blend_skirt();
void set_as_normal_map(bool p_as_normal_map);
bool is_normal_map();
void set_bump_strength(float p_bump_strength);
float get_bump_strength();
void set_normalize(bool p_normalize);
bool is_normalized() const;
void set_color_ramp(const Ref<Gradient> &p_gradient);
Ref<Gradient> get_color_ramp() const;
int get_width() const override;
int get_height() const override;
virtual RID get_rid() const override;
virtual bool has_alpha() const override { return false; }
virtual Ref<Image> get_image() const override;
NoiseTexture2D();
virtual ~NoiseTexture2D();
};
#endif // NOISE_TEXTURE_2D_H

View file

@ -0,0 +1,349 @@
/**************************************************************************/
/* noise_texture_3d.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "noise_texture_3d.h"
#include "noise.h"
NoiseTexture3D::NoiseTexture3D() {
noise = Ref<Noise>();
_queue_update();
}
NoiseTexture3D::~NoiseTexture3D() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
if (texture.is_valid()) {
RS::get_singleton()->free(texture);
}
if (noise_thread.is_started()) {
noise_thread.wait_to_finish();
}
}
void NoiseTexture3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_width", "width"), &NoiseTexture3D::set_width);
ClassDB::bind_method(D_METHOD("set_height", "height"), &NoiseTexture3D::set_height);
ClassDB::bind_method(D_METHOD("set_depth", "depth"), &NoiseTexture3D::set_depth);
ClassDB::bind_method(D_METHOD("set_invert", "invert"), &NoiseTexture3D::set_invert);
ClassDB::bind_method(D_METHOD("get_invert"), &NoiseTexture3D::get_invert);
ClassDB::bind_method(D_METHOD("set_seamless", "seamless"), &NoiseTexture3D::set_seamless);
ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture3D::get_seamless);
ClassDB::bind_method(D_METHOD("set_seamless_blend_skirt", "seamless_blend_skirt"), &NoiseTexture3D::set_seamless_blend_skirt);
ClassDB::bind_method(D_METHOD("get_seamless_blend_skirt"), &NoiseTexture3D::get_seamless_blend_skirt);
ClassDB::bind_method(D_METHOD("set_normalize", "normalize"), &NoiseTexture3D::set_normalize);
ClassDB::bind_method(D_METHOD("is_normalized"), &NoiseTexture3D::is_normalized);
ClassDB::bind_method(D_METHOD("set_color_ramp", "gradient"), &NoiseTexture3D::set_color_ramp);
ClassDB::bind_method(D_METHOD("get_color_ramp"), &NoiseTexture3D::get_color_ramp);
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture3D::set_noise);
ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture3D::get_noise);
ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_width", "get_width");
ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "depth", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_depth", "get_depth");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert"), "set_invert", "get_invert");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "seamless_blend_skirt", PROPERTY_HINT_RANGE, "0.05,1,0.001"), "set_seamless_blend_skirt", "get_seamless_blend_skirt");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "normalize"), "set_normalize", "is_normalized");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "Noise"), "set_noise", "get_noise");
}
void NoiseTexture3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "seamless_blend_skirt") {
if (!seamless) {
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
}
void NoiseTexture3D::_set_texture_data(const TypedArray<Image> &p_data) {
if (!p_data.is_empty()) {
Vector<Ref<Image>> data;
data.resize(p_data.size());
for (int i = 0; i < data.size(); i++) {
data.write[i] = p_data[i];
}
if (texture.is_valid()) {
RID new_texture = RS::get_singleton()->texture_3d_create(data[0]->get_format(), data[0]->get_width(), data[0]->get_height(), data.size(), false, data);
RS::get_singleton()->texture_replace(texture, new_texture);
} else {
texture = RS::get_singleton()->texture_3d_create(data[0]->get_format(), data[0]->get_width(), data[0]->get_height(), data.size(), false, data);
}
format = data[0]->get_format();
}
emit_changed();
}
void NoiseTexture3D::_thread_done(const TypedArray<Image> &p_data) {
_set_texture_data(p_data);
noise_thread.wait_to_finish();
if (regen_queued) {
noise_thread.start(_thread_function, this);
regen_queued = false;
}
}
void NoiseTexture3D::_thread_function(void *p_ud) {
NoiseTexture3D *tex = static_cast<NoiseTexture3D *>(p_ud);
callable_mp(tex, &NoiseTexture3D::_thread_done).call_deferred(tex->_generate_texture());
}
void NoiseTexture3D::_queue_update() {
if (update_queued) {
return;
}
update_queued = true;
callable_mp(this, &NoiseTexture3D::_update_texture).call_deferred();
}
TypedArray<Image> NoiseTexture3D::_generate_texture() {
// Prevent memdelete due to unref() on other thread.
Ref<Noise> ref_noise = noise;
if (ref_noise.is_null()) {
return TypedArray<Image>();
}
ERR_FAIL_COND_V_MSG((int64_t)width * height * depth > Image::MAX_PIXELS, TypedArray<Image>(), "The NoiseTexture3D is too big, consider lowering its width, height, or depth.");
Vector<Ref<Image>> images;
if (seamless) {
images = ref_noise->_get_seamless_image(width, height, depth, invert, true, seamless_blend_skirt, normalize);
} else {
images = ref_noise->_get_image(width, height, depth, invert, true, normalize);
}
if (color_ramp.is_valid()) {
for (int i = 0; i < images.size(); i++) {
images.write[i] = _modulate_with_gradient(images[i], color_ramp);
}
}
TypedArray<Image> new_data;
new_data.resize(images.size());
for (int i = 0; i < new_data.size(); i++) {
new_data[i] = images[i];
}
return new_data;
}
Ref<Image> NoiseTexture3D::_modulate_with_gradient(Ref<Image> p_image, Ref<Gradient> p_gradient) {
int w = p_image->get_width();
int h = p_image->get_height();
Ref<Image> new_image = Image::create_empty(w, h, false, Image::FORMAT_RGBA8);
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
Color pixel_color = p_image->get_pixel(col, row);
Color ramp_color = p_gradient->get_color_at_offset(pixel_color.get_luminance());
new_image->set_pixel(col, row, ramp_color);
}
}
return new_image;
}
void NoiseTexture3D::_update_texture() {
bool use_thread = true;
if (first_time) {
use_thread = false;
first_time = false;
}
if (use_thread) {
if (!noise_thread.is_started()) {
noise_thread.start(_thread_function, this);
regen_queued = false;
} else {
regen_queued = true;
}
} else {
TypedArray<Image> new_data = _generate_texture();
_set_texture_data(new_data);
}
update_queued = false;
}
void NoiseTexture3D::set_noise(Ref<Noise> p_noise) {
if (p_noise == noise) {
return;
}
if (noise.is_valid()) {
noise->disconnect_changed(callable_mp(this, &NoiseTexture3D::_queue_update));
}
noise = p_noise;
if (noise.is_valid()) {
noise->connect_changed(callable_mp(this, &NoiseTexture3D::_queue_update));
}
_queue_update();
}
Ref<Noise> NoiseTexture3D::get_noise() {
return noise;
}
void NoiseTexture3D::set_width(int p_width) {
ERR_FAIL_COND(p_width <= 0);
if (p_width == width) {
return;
}
width = p_width;
_queue_update();
}
void NoiseTexture3D::set_height(int p_height) {
ERR_FAIL_COND(p_height <= 0);
if (p_height == height) {
return;
}
height = p_height;
_queue_update();
}
void NoiseTexture3D::set_depth(int p_depth) {
ERR_FAIL_COND(p_depth <= 0);
if (p_depth == depth) {
return;
}
depth = p_depth;
_queue_update();
}
void NoiseTexture3D::set_invert(bool p_invert) {
if (p_invert == invert) {
return;
}
invert = p_invert;
_queue_update();
}
bool NoiseTexture3D::get_invert() const {
return invert;
}
void NoiseTexture3D::set_seamless(bool p_seamless) {
if (p_seamless == seamless) {
return;
}
seamless = p_seamless;
_queue_update();
notify_property_list_changed();
}
bool NoiseTexture3D::get_seamless() {
return seamless;
}
void NoiseTexture3D::set_seamless_blend_skirt(real_t p_blend_skirt) {
ERR_FAIL_COND(p_blend_skirt < 0.05 || p_blend_skirt > 1);
if (p_blend_skirt == seamless_blend_skirt) {
return;
}
seamless_blend_skirt = p_blend_skirt;
_queue_update();
}
real_t NoiseTexture3D::get_seamless_blend_skirt() {
return seamless_blend_skirt;
}
void NoiseTexture3D::set_color_ramp(const Ref<Gradient> &p_gradient) {
if (p_gradient == color_ramp) {
return;
}
if (color_ramp.is_valid()) {
color_ramp->disconnect_changed(callable_mp(this, &NoiseTexture3D::_queue_update));
}
color_ramp = p_gradient;
if (color_ramp.is_valid()) {
color_ramp->connect_changed(callable_mp(this, &NoiseTexture3D::_queue_update));
}
_queue_update();
}
void NoiseTexture3D::set_normalize(bool p_normalize) {
if (normalize == p_normalize) {
return;
}
normalize = p_normalize;
_queue_update();
}
bool NoiseTexture3D::is_normalized() const {
return normalize;
}
Ref<Gradient> NoiseTexture3D::get_color_ramp() const {
return color_ramp;
}
int NoiseTexture3D::get_width() const {
return width;
}
int NoiseTexture3D::get_height() const {
return height;
}
int NoiseTexture3D::get_depth() const {
return depth;
}
RID NoiseTexture3D::get_rid() const {
if (!texture.is_valid()) {
texture = RS::get_singleton()->texture_3d_placeholder_create();
}
return texture;
}
Vector<Ref<Image>> NoiseTexture3D::get_data() const {
ERR_FAIL_COND_V(!texture.is_valid(), Vector<Ref<Image>>());
return RS::get_singleton()->texture_3d_get(texture);
}
Image::Format NoiseTexture3D::get_format() const {
return format;
}

View file

@ -0,0 +1,115 @@
/**************************************************************************/
/* noise_texture_3d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef NOISE_TEXTURE_3D_H
#define NOISE_TEXTURE_3D_H
#include "noise.h"
#include "core/object/ref_counted.h"
#include "scene/resources/texture.h"
class NoiseTexture3D : public Texture3D {
GDCLASS(NoiseTexture3D, Texture3D);
private:
Thread noise_thread;
bool first_time = true;
bool update_queued = false;
bool regen_queued = false;
mutable RID texture;
uint32_t flags = 0;
int width = 64;
int height = 64;
int depth = 64;
bool invert = false;
bool seamless = false;
real_t seamless_blend_skirt = 0.1;
bool normalize = true;
Ref<Gradient> color_ramp;
Ref<Noise> noise;
Image::Format format = Image::FORMAT_L8;
void _thread_done(const TypedArray<Image> &p_data);
static void _thread_function(void *p_ud);
void _queue_update();
TypedArray<Image> _generate_texture();
void _update_texture();
void _set_texture_data(const TypedArray<Image> &p_data);
Ref<Image> _modulate_with_gradient(Ref<Image> p_image, Ref<Gradient> p_gradient);
protected:
static void _bind_methods();
void _validate_property(PropertyInfo &p_property) const;
public:
void set_noise(Ref<Noise> p_noise);
Ref<Noise> get_noise();
void set_width(int p_width);
void set_height(int p_height);
void set_depth(int p_depth);
void set_invert(bool p_invert);
bool get_invert() const;
void set_seamless(bool p_seamless);
bool get_seamless();
void set_seamless_blend_skirt(real_t p_blend_skirt);
real_t get_seamless_blend_skirt();
void set_normalize(bool p_normalize);
bool is_normalized() const;
void set_color_ramp(const Ref<Gradient> &p_gradient);
Ref<Gradient> get_color_ramp() const;
virtual int get_width() const override;
virtual int get_height() const override;
virtual int get_depth() const override;
virtual RID get_rid() const override;
virtual Vector<Ref<Image>> get_data() const override;
virtual Image::Format get_format() const override;
NoiseTexture3D();
virtual ~NoiseTexture3D();
};
#endif // NOISE_TEXTURE_3D_H

View file

@ -0,0 +1,66 @@
/**************************************************************************/
/* register_types.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#include "register_types.h"
#include "fastnoise_lite.h"
#include "noise.h"
#include "noise_texture_2d.h"
#include "noise_texture_3d.h"
#ifdef TOOLS_ENABLED
#include "editor/noise_editor_plugin.h"
#endif
#ifdef TOOLS_ENABLED
#include "editor/plugins/editor_plugin.h"
#endif
void initialize_noise_module(ModuleInitializationLevel p_level) {
if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
GDREGISTER_CLASS(NoiseTexture3D);
GDREGISTER_CLASS(NoiseTexture2D);
GDREGISTER_ABSTRACT_CLASS(Noise);
GDREGISTER_CLASS(FastNoiseLite);
ClassDB::add_compatibility_class("NoiseTexture", "NoiseTexture2D");
}
#ifdef TOOLS_ENABLED
if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
EditorPlugins::add_by_type<NoiseEditorPlugin>();
}
#endif
}
void uninitialize_noise_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
}

View file

@ -0,0 +1,39 @@
/**************************************************************************/
/* register_types.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef NOISE_REGISTER_TYPES_H
#define NOISE_REGISTER_TYPES_H
#include "modules/register_module_types.h"
void initialize_noise_module(ModuleInitializationLevel p_level);
void uninitialize_noise_module(ModuleInitializationLevel p_level);
#endif // NOISE_REGISTER_TYPES_H

View file

@ -0,0 +1,637 @@
/**************************************************************************/
/* test_fastnoise_lite.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TEST_FASTNOISE_LITE_H
#define TEST_FASTNOISE_LITE_H
#include "../fastnoise_lite.h"
#include "tests/test_macros.h"
namespace TestFastNoiseLite {
// Uitility functions for finding differences in noise generation
bool all_equal_approx(const Vector<real_t> &p_values_1, const Vector<real_t> &p_values_2) {
ERR_FAIL_COND_V_MSG(p_values_1.size() != p_values_2.size(), false, "Arrays must be the same size. This is a error in the test code.");
for (int i = 0; i < p_values_1.size(); i++) {
if (!Math::is_equal_approx(p_values_1[i], p_values_2[i])) {
return false;
}
}
return true;
}
Vector<Pair<size_t, size_t>> find_approx_equal_vec_pairs(std::initializer_list<Vector<real_t>> inputs) {
Vector<Vector<real_t>> p_array = Vector<Vector<real_t>>(inputs);
Vector<Pair<size_t, size_t>> result;
for (int i = 0; i < p_array.size(); i++) {
for (int j = i + 1; j < p_array.size(); j++) {
if (all_equal_approx(p_array[i], p_array[j])) {
result.push_back(Pair<size_t, size_t>(i, j));
}
}
}
return result;
}
#define CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(...) \
{ \
Vector<Pair<size_t, size_t>> equal_pairs = find_approx_equal_vec_pairs({ __VA_ARGS__ }); \
for (Pair<size_t, size_t> p : equal_pairs) { \
MESSAGE("Argument with index ", p.first, " is approximately equal to argument with index ", p.second); \
} \
CHECK_MESSAGE(equal_pairs.size() == 0, "All arguments should be pairwise distinct."); \
}
Vector<real_t> get_noise_samples_1d(const FastNoiseLite &p_noise, size_t p_count = 32) {
Vector<real_t> result;
result.resize(p_count);
for (size_t i = 0; i < p_count; i++) {
result.write[i] = p_noise.get_noise_1d(i);
}
return result;
}
Vector<real_t> get_noise_samples_2d(const FastNoiseLite &p_noise, size_t p_count = 32) {
Vector<real_t> result;
result.resize(p_count);
for (size_t i = 0; i < p_count; i++) {
result.write[i] = p_noise.get_noise_2d(i, i);
}
return result;
}
Vector<real_t> get_noise_samples_3d(const FastNoiseLite &p_noise, size_t p_count = 32) {
Vector<real_t> result;
result.resize(p_count);
for (size_t i = 0; i < p_count; i++) {
result.write[i] = p_noise.get_noise_3d(i, i, i);
}
return result;
}
// The following test suite is rather for testing the wrapper code than the actual noise generation.
TEST_CASE("[FastNoiseLite] Getter and setter") {
FastNoiseLite noise;
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX_SMOOTH);
CHECK(noise.get_noise_type() == FastNoiseLite::NoiseType::TYPE_SIMPLEX_SMOOTH);
noise.set_seed(123);
CHECK(noise.get_seed() == 123);
noise.set_frequency(0.123);
CHECK(noise.get_frequency() == doctest::Approx(0.123));
noise.set_offset(Vector3(1, 2, 3));
CHECK(noise.get_offset() == Vector3(1, 2, 3));
noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_PING_PONG);
CHECK(noise.get_fractal_type() == FastNoiseLite::FractalType::FRACTAL_PING_PONG);
noise.set_fractal_octaves(2);
CHECK(noise.get_fractal_octaves() == 2);
noise.set_fractal_lacunarity(1.123);
CHECK(noise.get_fractal_lacunarity() == doctest::Approx(1.123));
noise.set_fractal_gain(0.123);
CHECK(noise.get_fractal_gain() == doctest::Approx(0.123));
noise.set_fractal_weighted_strength(0.123);
CHECK(noise.get_fractal_weighted_strength() == doctest::Approx(0.123));
noise.set_fractal_ping_pong_strength(0.123);
CHECK(noise.get_fractal_ping_pong_strength() == doctest::Approx(0.123));
noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_MANHATTAN);
CHECK(noise.get_cellular_distance_function() == FastNoiseLite::CellularDistanceFunction::DISTANCE_MANHATTAN);
noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE2_SUB);
CHECK(noise.get_cellular_return_type() == FastNoiseLite::CellularReturnType::RETURN_DISTANCE2_SUB);
noise.set_cellular_jitter(0.123);
CHECK(noise.get_cellular_jitter() == doctest::Approx(0.123));
noise.set_domain_warp_enabled(true);
CHECK(noise.is_domain_warp_enabled() == true);
noise.set_domain_warp_enabled(false);
CHECK(noise.is_domain_warp_enabled() == false);
noise.set_domain_warp_type(FastNoiseLite::DomainWarpType::DOMAIN_WARP_SIMPLEX_REDUCED);
CHECK(noise.get_domain_warp_type() == FastNoiseLite::DomainWarpType::DOMAIN_WARP_SIMPLEX_REDUCED);
noise.set_domain_warp_amplitude(0.123);
CHECK(noise.get_domain_warp_amplitude() == doctest::Approx(0.123));
noise.set_domain_warp_frequency(0.123);
CHECK(noise.get_domain_warp_frequency() == doctest::Approx(0.123));
noise.set_domain_warp_fractal_type(FastNoiseLite::DomainWarpFractalType::DOMAIN_WARP_FRACTAL_INDEPENDENT);
CHECK(noise.get_domain_warp_fractal_type() == FastNoiseLite::DomainWarpFractalType::DOMAIN_WARP_FRACTAL_INDEPENDENT);
noise.set_domain_warp_fractal_octaves(2);
CHECK(noise.get_domain_warp_fractal_octaves() == 2);
noise.set_domain_warp_fractal_lacunarity(1.123);
CHECK(noise.get_domain_warp_fractal_lacunarity() == doctest::Approx(1.123));
noise.set_domain_warp_fractal_gain(0.123);
CHECK(noise.get_domain_warp_fractal_gain() == doctest::Approx(0.123));
}
TEST_CASE("[FastNoiseLite] Basic noise generation") {
FastNoiseLite noise;
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX);
noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_NONE);
noise.set_seed(123);
noise.set_offset(Vector3(10, 10, 10));
// 1D noise will be checked just in the cases where there's the possibility of
// finding a bug/regression in the wrapper function.
// (since it uses FastNoise's 2D noise generator with the Y coordinate set to 0).
SUBCASE("Determinacy of noise generation (all noise types)") {
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX);
CHECK(noise.get_noise_2d(0, 0) == doctest::Approx(noise.get_noise_2d(0, 0)));
CHECK(noise.get_noise_3d(0, 0, 0) == doctest::Approx(noise.get_noise_3d(0, 0, 0)));
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX_SMOOTH);
CHECK(noise.get_noise_2d(0, 0) == doctest::Approx(noise.get_noise_2d(0, 0)));
CHECK(noise.get_noise_3d(0, 0, 0) == doctest::Approx(noise.get_noise_3d(0, 0, 0)));
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_CELLULAR);
CHECK(noise.get_noise_2d(0, 0) == doctest::Approx(noise.get_noise_2d(0, 0)));
CHECK(noise.get_noise_3d(0, 0, 0) == doctest::Approx(noise.get_noise_3d(0, 0, 0)));
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_PERLIN);
CHECK(noise.get_noise_2d(0, 0) == doctest::Approx(noise.get_noise_2d(0, 0)));
CHECK(noise.get_noise_3d(0, 0, 0) == doctest::Approx(noise.get_noise_3d(0, 0, 0)));
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_VALUE);
CHECK(noise.get_noise_2d(0, 0) == doctest::Approx(noise.get_noise_2d(0, 0)));
CHECK(noise.get_noise_3d(0, 0, 0) == doctest::Approx(noise.get_noise_3d(0, 0, 0)));
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_VALUE_CUBIC);
CHECK(noise.get_noise_2d(0, 0) == doctest::Approx(noise.get_noise_2d(0, 0)));
CHECK(noise.get_noise_3d(0, 0, 0) == doctest::Approx(noise.get_noise_3d(0, 0, 0)));
}
SUBCASE("Different seeds should produce different noise") {
noise.set_seed(456);
Vector<real_t> noise_seed_1_1d = get_noise_samples_1d(noise);
Vector<real_t> noise_seed_1_2d = get_noise_samples_2d(noise);
Vector<real_t> noise_seed_1_3d = get_noise_samples_3d(noise);
noise.set_seed(123);
Vector<real_t> noise_seed_2_1d = get_noise_samples_1d(noise);
Vector<real_t> noise_seed_2_2d = get_noise_samples_2d(noise);
Vector<real_t> noise_seed_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(noise_seed_1_1d, noise_seed_2_1d));
CHECK_FALSE(all_equal_approx(noise_seed_1_2d, noise_seed_2_2d));
CHECK_FALSE(all_equal_approx(noise_seed_1_3d, noise_seed_2_3d));
}
SUBCASE("Different frequencies should produce different noise") {
noise.set_frequency(0.1);
Vector<real_t> noise_frequency_1_1d = get_noise_samples_1d(noise);
Vector<real_t> noise_frequency_1_2d = get_noise_samples_2d(noise);
Vector<real_t> noise_frequency_1_3d = get_noise_samples_3d(noise);
noise.set_frequency(1.0);
Vector<real_t> noise_frequency_2_1d = get_noise_samples_1d(noise);
Vector<real_t> noise_frequency_2_2d = get_noise_samples_2d(noise);
Vector<real_t> noise_frequency_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(noise_frequency_1_1d, noise_frequency_2_1d));
CHECK_FALSE(all_equal_approx(noise_frequency_1_2d, noise_frequency_2_2d));
CHECK_FALSE(all_equal_approx(noise_frequency_1_3d, noise_frequency_2_3d));
}
SUBCASE("Noise should be offset by the offset parameter") {
noise.set_offset(Vector3(1, 2, 3));
Vector<real_t> noise_offset_1_1d = get_noise_samples_1d(noise);
Vector<real_t> noise_offset_1_2d = get_noise_samples_2d(noise);
Vector<real_t> noise_offset_1_3d = get_noise_samples_3d(noise);
noise.set_offset(Vector3(4, 5, 6));
Vector<real_t> noise_offset_2_1d = get_noise_samples_1d(noise);
Vector<real_t> noise_offset_2_2d = get_noise_samples_2d(noise);
Vector<real_t> noise_offset_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(noise_offset_1_1d, noise_offset_2_1d));
CHECK_FALSE(all_equal_approx(noise_offset_1_2d, noise_offset_2_2d));
CHECK_FALSE(all_equal_approx(noise_offset_1_3d, noise_offset_2_3d));
}
SUBCASE("Different noise types should produce different noise") {
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX);
Vector<real_t> noise_type_simplex_2d = get_noise_samples_2d(noise);
Vector<real_t> noise_type_simplex_3d = get_noise_samples_3d(noise);
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX_SMOOTH);
Vector<real_t> noise_type_simplex_smooth_2d = get_noise_samples_2d(noise);
Vector<real_t> noise_type_simplex_smooth_3d = get_noise_samples_3d(noise);
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_CELLULAR);
Vector<real_t> noise_type_cellular_2d = get_noise_samples_2d(noise);
Vector<real_t> noise_type_cellular_3d = get_noise_samples_3d(noise);
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_PERLIN);
Vector<real_t> noise_type_perlin_2d = get_noise_samples_2d(noise);
Vector<real_t> noise_type_perlin_3d = get_noise_samples_3d(noise);
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_VALUE);
Vector<real_t> noise_type_value_2d = get_noise_samples_2d(noise);
Vector<real_t> noise_type_value_3d = get_noise_samples_3d(noise);
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_VALUE_CUBIC);
Vector<real_t> noise_type_value_cubic_2d = get_noise_samples_2d(noise);
Vector<real_t> noise_type_value_cubic_3d = get_noise_samples_3d(noise);
CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(noise_type_simplex_2d,
noise_type_simplex_smooth_2d,
noise_type_cellular_2d,
noise_type_perlin_2d,
noise_type_value_2d,
noise_type_value_cubic_2d);
CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(noise_type_simplex_3d,
noise_type_simplex_smooth_3d,
noise_type_cellular_3d,
noise_type_perlin_3d,
noise_type_value_3d,
noise_type_value_cubic_3d);
}
}
TEST_CASE("[FastNoiseLite] Fractal noise") {
FastNoiseLite noise;
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX);
noise.set_offset(Vector3(10, 10, 10));
noise.set_frequency(0.01);
noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_FBM);
noise.set_fractal_octaves(4);
noise.set_fractal_lacunarity(2.0);
noise.set_fractal_gain(0.5);
noise.set_fractal_weighted_strength(0.5);
noise.set_fractal_ping_pong_strength(2.0);
SUBCASE("Different fractal types should produce different results") {
noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_NONE);
Vector<real_t> fractal_type_none_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_type_none_3d = get_noise_samples_3d(noise);
noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_FBM);
Vector<real_t> fractal_type_fbm_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_type_fbm_3d = get_noise_samples_3d(noise);
noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_RIDGED);
Vector<real_t> fractal_type_ridged_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_type_ridged_3d = get_noise_samples_3d(noise);
noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_PING_PONG);
Vector<real_t> fractal_type_ping_pong_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_type_ping_pong_3d = get_noise_samples_3d(noise);
CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(fractal_type_none_2d,
fractal_type_fbm_2d,
fractal_type_ridged_2d,
fractal_type_ping_pong_2d);
CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(fractal_type_none_3d,
fractal_type_fbm_3d,
fractal_type_ridged_3d,
fractal_type_ping_pong_3d);
}
SUBCASE("Different octaves should produce different results") {
noise.set_fractal_octaves(1.0);
Vector<real_t> fractal_octaves_1_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_octaves_1_3d = get_noise_samples_3d(noise);
noise.set_fractal_octaves(8.0);
Vector<real_t> fractal_octaves_2_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_octaves_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(fractal_octaves_1_2d, fractal_octaves_2_2d));
CHECK_FALSE(all_equal_approx(fractal_octaves_1_3d, fractal_octaves_2_3d));
}
SUBCASE("Different lacunarity should produce different results") {
noise.set_fractal_lacunarity(1.0);
Vector<real_t> fractal_lacunarity_1_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_lacunarity_1_3d = get_noise_samples_3d(noise);
noise.set_fractal_lacunarity(2.0);
Vector<real_t> fractal_lacunarity_2_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_lacunarity_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(fractal_lacunarity_1_2d, fractal_lacunarity_2_2d));
CHECK_FALSE(all_equal_approx(fractal_lacunarity_1_3d, fractal_lacunarity_2_3d));
}
SUBCASE("Different gain should produce different results") {
noise.set_fractal_gain(0.5);
Vector<real_t> fractal_gain_1_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_gain_1_3d = get_noise_samples_3d(noise);
noise.set_fractal_gain(0.75);
Vector<real_t> fractal_gain_2_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_gain_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(fractal_gain_1_2d, fractal_gain_2_2d));
CHECK_FALSE(all_equal_approx(fractal_gain_1_3d, fractal_gain_2_3d));
}
SUBCASE("Different weights should produce different results") {
noise.set_fractal_weighted_strength(0.5);
Vector<real_t> fractal_weighted_strength_1_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_weighted_strength_1_3d = get_noise_samples_3d(noise);
noise.set_fractal_weighted_strength(0.75);
Vector<real_t> fractal_weighted_strength_2_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_weighted_strength_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(fractal_weighted_strength_1_2d, fractal_weighted_strength_2_2d));
CHECK_FALSE(all_equal_approx(fractal_weighted_strength_1_3d, fractal_weighted_strength_2_3d));
}
SUBCASE("Different ping pong strength should produce different results") {
noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_PING_PONG);
noise.set_fractal_ping_pong_strength(0.5);
Vector<real_t> fractal_ping_pong_strength_1_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_ping_pong_strength_1_3d = get_noise_samples_3d(noise);
noise.set_fractal_ping_pong_strength(0.75);
Vector<real_t> fractal_ping_pong_strength_2_2d = get_noise_samples_2d(noise);
Vector<real_t> fractal_ping_pong_strength_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(fractal_ping_pong_strength_1_2d, fractal_ping_pong_strength_2_2d));
CHECK_FALSE(all_equal_approx(fractal_ping_pong_strength_1_3d, fractal_ping_pong_strength_2_3d));
}
}
TEST_CASE("[FastNoiseLite] Cellular noise") {
FastNoiseLite noise;
noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_NONE);
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_CELLULAR);
noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_EUCLIDEAN);
noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE);
noise.set_frequency(1.0);
SUBCASE("Different distance functions should produce different results") {
noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_EUCLIDEAN);
Vector<real_t> cellular_distance_function_euclidean_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_distance_function_euclidean_3d = get_noise_samples_3d(noise);
noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_EUCLIDEAN_SQUARED);
Vector<real_t> cellular_distance_function_euclidean_squared_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_distance_function_euclidean_squared_3d = get_noise_samples_3d(noise);
noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_MANHATTAN);
Vector<real_t> cellular_distance_function_manhattan_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_distance_function_manhattan_3d = get_noise_samples_3d(noise);
noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_HYBRID);
Vector<real_t> cellular_distance_function_hybrid_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_distance_function_hybrid_3d = get_noise_samples_3d(noise);
CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(cellular_distance_function_euclidean_2d,
cellular_distance_function_euclidean_squared_2d,
cellular_distance_function_manhattan_2d,
cellular_distance_function_hybrid_2d);
CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(cellular_distance_function_euclidean_3d,
cellular_distance_function_euclidean_squared_3d,
cellular_distance_function_manhattan_3d,
cellular_distance_function_hybrid_3d);
}
SUBCASE("Different return function types should produce different results") {
noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_CELL_VALUE);
Vector<real_t> cellular_return_type_cell_value_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_return_type_cell_value_3d = get_noise_samples_3d(noise);
noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE);
Vector<real_t> cellular_return_type_distance_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_return_type_distance_3d = get_noise_samples_3d(noise);
noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE2);
Vector<real_t> cellular_return_type_distance2_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_return_type_distance2_3d = get_noise_samples_3d(noise);
noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE2_ADD);
Vector<real_t> cellular_return_type_distance2_add_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_return_type_distance2_add_3d = get_noise_samples_3d(noise);
noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE2_SUB);
Vector<real_t> cellular_return_type_distance2_sub_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_return_type_distance2_sub_3d = get_noise_samples_3d(noise);
noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE2_MUL);
Vector<real_t> cellular_return_type_distance2_mul_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_return_type_distance2_mul_3d = get_noise_samples_3d(noise);
noise.set_cellular_return_type(FastNoiseLite::CellularReturnType::RETURN_DISTANCE2_DIV);
Vector<real_t> cellular_return_type_distance2_div_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_return_type_distance2_div_3d = get_noise_samples_3d(noise);
CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(cellular_return_type_cell_value_2d,
cellular_return_type_distance_2d,
cellular_return_type_distance2_2d,
cellular_return_type_distance2_add_2d,
cellular_return_type_distance2_sub_2d,
cellular_return_type_distance2_mul_2d,
cellular_return_type_distance2_div_2d);
CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(cellular_return_type_cell_value_3d,
cellular_return_type_distance_3d,
cellular_return_type_distance2_3d,
cellular_return_type_distance2_add_3d,
cellular_return_type_distance2_sub_3d,
cellular_return_type_distance2_mul_3d,
cellular_return_type_distance2_div_3d);
}
SUBCASE("Different cellular jitter should produce different results") {
noise.set_cellular_jitter(0.0);
Vector<real_t> cellular_jitter_1_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_jitter_1_3d = get_noise_samples_3d(noise);
noise.set_cellular_jitter(0.5);
Vector<real_t> cellular_jitter_2_2d = get_noise_samples_2d(noise);
Vector<real_t> cellular_jitter_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(cellular_jitter_1_2d, cellular_jitter_2_2d));
CHECK_FALSE(all_equal_approx(cellular_jitter_1_3d, cellular_jitter_2_3d));
}
}
TEST_CASE("[FastNoiseLite] Domain warp") {
FastNoiseLite noise;
noise.set_frequency(1.0);
noise.set_domain_warp_amplitude(200.0);
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_SIMPLEX);
noise.set_domain_warp_enabled(true);
SUBCASE("Different domain warp types should produce different results") {
noise.set_domain_warp_type(FastNoiseLite::DomainWarpType::DOMAIN_WARP_SIMPLEX);
Vector<real_t> domain_warp_type_simplex_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_type_simplex_3d = get_noise_samples_3d(noise);
noise.set_domain_warp_type(FastNoiseLite::DomainWarpType::DOMAIN_WARP_SIMPLEX_REDUCED);
Vector<real_t> domain_warp_type_simplex_reduced_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_type_simplex_reduced_3d = get_noise_samples_3d(noise);
noise.set_domain_warp_type(FastNoiseLite::DomainWarpType::DOMAIN_WARP_BASIC_GRID);
Vector<real_t> domain_warp_type_basic_grid_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_type_basic_grid_3d = get_noise_samples_3d(noise);
CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(domain_warp_type_simplex_2d,
domain_warp_type_simplex_reduced_2d,
domain_warp_type_basic_grid_2d);
CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(domain_warp_type_simplex_3d,
domain_warp_type_simplex_reduced_3d,
domain_warp_type_basic_grid_3d);
}
SUBCASE("Different domain warp amplitude should produce different results") {
noise.set_domain_warp_amplitude(0.0);
Vector<real_t> domain_warp_amplitude_1_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_amplitude_1_3d = get_noise_samples_3d(noise);
noise.set_domain_warp_amplitude(100.0);
Vector<real_t> domain_warp_amplitude_2_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_amplitude_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(domain_warp_amplitude_1_2d, domain_warp_amplitude_2_2d));
CHECK_FALSE(all_equal_approx(domain_warp_amplitude_1_3d, domain_warp_amplitude_2_3d));
}
SUBCASE("Different domain warp frequency should produce different results") {
noise.set_domain_warp_frequency(0.1);
Vector<real_t> domain_warp_frequency_1_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_frequency_1_3d = get_noise_samples_3d(noise);
noise.set_domain_warp_frequency(2.0);
Vector<real_t> domain_warp_frequency_2_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_frequency_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(domain_warp_frequency_1_2d, domain_warp_frequency_2_2d));
CHECK_FALSE(all_equal_approx(domain_warp_frequency_1_3d, domain_warp_frequency_2_3d));
}
SUBCASE("Different domain warp fractal type should produce different results") {
noise.set_domain_warp_fractal_type(FastNoiseLite::DomainWarpFractalType::DOMAIN_WARP_FRACTAL_NONE);
Vector<real_t> domain_warp_fractal_type_none_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_fractal_type_none_3d = get_noise_samples_3d(noise);
noise.set_domain_warp_fractal_type(FastNoiseLite::DomainWarpFractalType::DOMAIN_WARP_FRACTAL_PROGRESSIVE);
Vector<real_t> domain_warp_fractal_type_progressive_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_fractal_type_progressive_3d = get_noise_samples_3d(noise);
noise.set_domain_warp_fractal_type(FastNoiseLite::DomainWarpFractalType::DOMAIN_WARP_FRACTAL_INDEPENDENT);
Vector<real_t> domain_warp_fractal_type_independent_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_fractal_type_independent_3d = get_noise_samples_3d(noise);
CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(domain_warp_fractal_type_none_2d,
domain_warp_fractal_type_progressive_2d,
domain_warp_fractal_type_independent_2d);
CHECK_ARGS_APPROX_PAIRWISE_DISTINCT_VECS(domain_warp_fractal_type_none_3d,
domain_warp_fractal_type_progressive_3d,
domain_warp_fractal_type_independent_3d);
}
SUBCASE("Different domain warp fractal octaves should produce different results") {
noise.set_domain_warp_fractal_octaves(1);
Vector<real_t> domain_warp_fractal_octaves_1_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_fractal_octaves_1_3d = get_noise_samples_3d(noise);
noise.set_domain_warp_fractal_octaves(6);
Vector<real_t> domain_warp_fractal_octaves_2_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_fractal_octaves_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(domain_warp_fractal_octaves_1_2d, domain_warp_fractal_octaves_2_2d));
CHECK_FALSE(all_equal_approx(domain_warp_fractal_octaves_1_3d, domain_warp_fractal_octaves_2_3d));
}
SUBCASE("Different domain warp fractal lacunarity should produce different results") {
noise.set_domain_warp_fractal_lacunarity(0.5);
Vector<real_t> domain_warp_fractal_lacunarity_1_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_fractal_lacunarity_1_3d = get_noise_samples_3d(noise);
noise.set_domain_warp_fractal_lacunarity(5.0);
Vector<real_t> domain_warp_fractal_lacunarity_2_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_fractal_lacunarity_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(domain_warp_fractal_lacunarity_1_2d, domain_warp_fractal_lacunarity_2_2d));
CHECK_FALSE(all_equal_approx(domain_warp_fractal_lacunarity_1_3d, domain_warp_fractal_lacunarity_2_3d));
}
SUBCASE("Different domain warp fractal gain should produce different results") {
noise.set_domain_warp_fractal_gain(0.1);
Vector<real_t> domain_warp_fractal_gain_1_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_fractal_gain_1_3d = get_noise_samples_3d(noise);
noise.set_domain_warp_fractal_gain(0.9);
Vector<real_t> domain_warp_fractal_gain_2_2d = get_noise_samples_2d(noise);
Vector<real_t> domain_warp_fractal_gain_2_3d = get_noise_samples_3d(noise);
CHECK_FALSE(all_equal_approx(domain_warp_fractal_gain_1_2d, domain_warp_fractal_gain_2_2d));
CHECK_FALSE(all_equal_approx(domain_warp_fractal_gain_1_3d, domain_warp_fractal_gain_2_3d));
}
}
// Raw image data for the reference images used in the regression tests.
// Generated with the following code:
// for (int y = 0; y < img->get_data().size(); y++) {
// printf("0x%x,", img->get_data()[y]);
// }
const Vector<uint8_t> ref_img_1_data = { 0xff, 0xe6, 0xd2, 0xc2, 0xb7, 0xb4, 0xb4, 0xb7, 0xc2, 0xd2, 0xe6, 0xe6, 0xcb, 0xb4, 0xa1, 0x94, 0x90, 0x90, 0x94, 0xa1, 0xb4, 0xcb, 0xd2, 0xb4, 0x99, 0x82, 0x72, 0x6c, 0x6c, 0x72, 0x82, 0x99, 0xb4, 0xc2, 0xa1, 0x82, 0x65, 0x50, 0x48, 0x48, 0x50, 0x65, 0x82, 0xa1, 0xb7, 0x94, 0x72, 0x50, 0x32, 0x24, 0x24, 0x32, 0x50, 0x72, 0x94, 0xb4, 0x90, 0x6c, 0x48, 0x24, 0x0, 0x0, 0x24, 0x48, 0x6c, 0x90, 0xb4, 0x90, 0x6c, 0x48, 0x24, 0x0, 0x0, 0x24, 0x48, 0x6c, 0x90, 0xb7, 0x94, 0x72, 0x50, 0x32, 0x24, 0x24, 0x33, 0x50, 0x72, 0x94, 0xc2, 0xa1, 0x82, 0x65, 0x50, 0x48, 0x48, 0x50, 0x66, 0x82, 0xa1, 0xd2, 0xb4, 0x99, 0x82, 0x72, 0x6c, 0x6c, 0x72, 0x82, 0x99, 0xb4, 0xe6, 0xcb, 0xb4, 0xa1, 0x94, 0x90, 0x90, 0x94, 0xa1, 0xb4, 0xcc };
const Vector<uint8_t> ref_img_2_data = { 0xff, 0xe6, 0xd2, 0xc2, 0xb7, 0xb4, 0xb4, 0xb7, 0xc2, 0xd2, 0xe6, 0xe6, 0xcb, 0xb4, 0xa1, 0x94, 0x90, 0x90, 0x94, 0xa1, 0xb4, 0xcb, 0xd2, 0xb4, 0x99, 0x82, 0x72, 0x6c, 0x6c, 0x72, 0x82, 0x99, 0xb4, 0xc2, 0xa1, 0x82, 0x65, 0x50, 0x48, 0x48, 0x50, 0x65, 0x82, 0xa1, 0xb7, 0x94, 0x72, 0x50, 0x32, 0x24, 0x24, 0x32, 0x50, 0x72, 0x94, 0xb4, 0x90, 0x6c, 0x48, 0x24, 0x0, 0x0, 0x24, 0x48, 0x6c, 0x90, 0xb4, 0x90, 0x6c, 0x48, 0x24, 0x0, 0x0, 0x24, 0x48, 0x6c, 0x90, 0xb7, 0x94, 0x72, 0x50, 0x32, 0x24, 0x24, 0x33, 0x50, 0x72, 0x94, 0xc2, 0xa1, 0x82, 0x65, 0x50, 0x48, 0x48, 0x50, 0x66, 0x82, 0xa1, 0xd2, 0xb4, 0x99, 0x82, 0x72, 0x6c, 0x6c, 0x72, 0x82, 0x99, 0xb4, 0xe6, 0xcb, 0xb4, 0xa1, 0x94, 0x90, 0x90, 0x94, 0xa1, 0xb4, 0xcc };
const Vector<uint8_t> ref_img_3_data = { 0xff, 0xe6, 0xd2, 0xc2, 0xb7, 0xb4, 0xb4, 0xb7, 0xc2, 0xd2, 0xe6, 0xe6, 0xcb, 0xb4, 0xa1, 0x94, 0x90, 0x90, 0x94, 0xa1, 0xb4, 0xcb, 0xd2, 0xb4, 0x99, 0x82, 0x72, 0x6c, 0x6c, 0x72, 0x82, 0x99, 0xb4, 0xc2, 0xa1, 0x82, 0x65, 0x50, 0x48, 0x48, 0x50, 0x65, 0x82, 0xa1, 0xb7, 0x94, 0x72, 0x50, 0x32, 0x24, 0x24, 0x32, 0x50, 0x72, 0x94, 0xb4, 0x90, 0x6c, 0x48, 0x24, 0x0, 0x0, 0x24, 0x48, 0x6c, 0x90, 0xb4, 0x90, 0x6c, 0x48, 0x24, 0x0, 0x0, 0x24, 0x48, 0x6c, 0x90, 0xb7, 0x94, 0x72, 0x50, 0x32, 0x24, 0x24, 0x33, 0x50, 0x72, 0x94, 0xc2, 0xa1, 0x82, 0x65, 0x50, 0x48, 0x48, 0x50, 0x66, 0x82, 0xa1, 0xd2, 0xb4, 0x99, 0x82, 0x72, 0x6c, 0x6c, 0x72, 0x82, 0x99, 0xb4, 0xe6, 0xcb, 0xb4, 0xa1, 0x94, 0x90, 0x90, 0x94, 0xa1, 0xb4, 0xcc };
// Utiliy function to compare two images pixel by pixel (for easy debugging of regressions)
void compare_image_with_reference(const Ref<Image> &p_img, const Ref<Image> &p_reference_img) {
for (int y = 0; y < p_img->get_height(); y++) {
for (int x = 0; x < p_img->get_width(); x++) {
CHECK(p_img->get_pixel(x, y) == p_reference_img->get_pixel(x, y));
}
}
}
TEST_CASE("[FastNoiseLite] Generating seamless 2D images (11x11px) and compare to reference images") {
FastNoiseLite noise;
noise.set_noise_type(FastNoiseLite::NoiseType::TYPE_CELLULAR);
noise.set_fractal_type(FastNoiseLite::FractalType::FRACTAL_NONE);
noise.set_cellular_distance_function(FastNoiseLite::CellularDistanceFunction::DISTANCE_EUCLIDEAN);
noise.set_frequency(0.1);
noise.set_cellular_jitter(0.0);
SUBCASE("Blend skirt 0.0") {
Ref<Image> img = noise.get_seamless_image(11, 11, false, false, 0.0);
Ref<Image> ref_img_1 = memnew(Image);
ref_img_1->set_data(11, 11, false, Image::FORMAT_L8, ref_img_1_data);
compare_image_with_reference(img, ref_img_1);
}
SUBCASE("Blend skirt 0.1") {
Ref<Image> img = noise.get_seamless_image(11, 11, false, false, 0.1);
Ref<Image> ref_img_2 = memnew(Image);
ref_img_2->set_data(11, 11, false, Image::FORMAT_L8, ref_img_2_data);
compare_image_with_reference(img, ref_img_2);
}
SUBCASE("Blend skirt 1.0") {
Ref<Image> img = noise.get_seamless_image(11, 11, false, false, 0.1);
Ref<Image> ref_img_3 = memnew(Image);
ref_img_3->set_data(11, 11, false, Image::FORMAT_L8, ref_img_3_data);
compare_image_with_reference(img, ref_img_3);
}
}
} //namespace TestFastNoiseLite
#endif // TEST_FASTNOISE_LITE_H

View file

@ -0,0 +1,267 @@
/**************************************************************************/
/* test_noise_texture_2d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TEST_NOISE_TEXTURE_2D_H
#define TEST_NOISE_TEXTURE_2D_H
#include "../noise_texture_2d.h"
#include "tests/test_macros.h"
namespace TestNoiseTexture2D {
class NoiseTextureTester : public RefCounted {
GDCLASS(NoiseTextureTester, RefCounted);
const NoiseTexture2D *const texture;
public:
NoiseTextureTester(const NoiseTexture2D *const p_texture) :
texture{ p_texture } {};
Color compute_average_color(const Ref<Image> &p_noise_image) {
Color r_avg_color{};
for (int i = 0; i < p_noise_image->get_width(); ++i) {
for (int j = 0; j < p_noise_image->get_height(); ++j) {
const Color pixel = p_noise_image->get_pixel(i, j);
r_avg_color += pixel;
}
}
int pixel_count = p_noise_image->get_width() * p_noise_image->get_height();
r_avg_color /= pixel_count;
return r_avg_color;
}
void check_mip_and_color_ramp() {
const Ref<Image> noise_image = texture->get_image();
CHECK(noise_image.is_valid());
CHECK(noise_image->get_width() == texture->get_width());
CHECK(noise_image->get_height() == texture->get_height());
CHECK(noise_image->get_format() == Image::FORMAT_RGBA8);
CHECK(noise_image->has_mipmaps());
Color avg_color = compute_average_color(noise_image);
// Check that the noise texture is modulated correctly by the color ramp (Gradient).
CHECK_FALSE_MESSAGE((avg_color.r + avg_color.g + avg_color.b) == doctest::Approx(0.0), "The noise texture should not be all black");
CHECK_FALSE_MESSAGE((avg_color.r + avg_color.g + avg_color.b) == doctest::Approx(noise_image->get_width() * noise_image->get_height() * 3.0), "The noise texture should not be all white");
CHECK_MESSAGE(avg_color.g == doctest::Approx(0.0), "The noise texture should not have any green when modulated correctly by the color ramp");
}
void check_normal_map() {
const Ref<Image> noise_image = texture->get_image();
CHECK(noise_image.is_valid());
CHECK(noise_image->get_width() == texture->get_width());
CHECK(noise_image->get_height() == texture->get_height());
CHECK(noise_image->get_format() == Image::FORMAT_RGBA8);
CHECK_FALSE(noise_image->has_mipmaps());
Color avg_color = compute_average_color(noise_image);
// Check for the characteristic color distribution (for tangent space) of a normal map.
CHECK(avg_color.r == doctest::Approx(0.5).epsilon(0.05));
CHECK(avg_color.g == doctest::Approx(0.5).epsilon(0.05));
CHECK(avg_color.b == doctest::Approx(1.0).epsilon(0.05));
}
void check_seamless_texture_grayscale() {
const Ref<Image> noise_image = texture->get_image();
CHECK(noise_image.is_valid());
CHECK(noise_image->get_width() == texture->get_width());
CHECK(noise_image->get_height() == texture->get_height());
CHECK(noise_image->get_format() == Image::FORMAT_L8);
Color avg_color = compute_average_color(noise_image);
// Since it's a grayscale image and every channel except the alpha channel has the
// same values (conversion happens in Image::get_pixel) we only need to test one channel.
CHECK(avg_color.r == doctest::Approx(0.5).epsilon(0.05));
}
void check_seamless_texture_rgba() {
const Ref<Image> noise_image = texture->get_image();
CHECK(noise_image.is_valid());
CHECK(noise_image->get_width() == texture->get_width());
CHECK(noise_image->get_height() == texture->get_height());
CHECK(noise_image->get_format() == Image::FORMAT_RGBA8);
// Check that the noise texture is modulated correctly by the color ramp (Gradient).
Color avg_color = compute_average_color(noise_image);
// We use a default (black to white) gradient, so the average of the red, green and blue channels should be the same.
CHECK(avg_color.r == doctest::Approx(0.5).epsilon(0.05));
CHECK(avg_color.g == doctest::Approx(0.5).epsilon(0.05));
CHECK(avg_color.b == doctest::Approx(0.5).epsilon(0.05));
}
};
TEST_CASE("[NoiseTexture][SceneTree] Getter and setter") {
Ref<NoiseTexture2D> noise_texture = memnew(NoiseTexture2D);
Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
noise_texture->set_noise(noise);
CHECK(noise_texture->get_noise() == noise);
noise_texture->set_noise(nullptr);
CHECK(noise_texture->get_noise() == nullptr);
noise_texture->set_width(8);
noise_texture->set_height(4);
CHECK(noise_texture->get_width() == 8);
CHECK(noise_texture->get_height() == 4);
ERR_PRINT_OFF;
noise_texture->set_width(-1);
noise_texture->set_height(-1);
ERR_PRINT_ON;
CHECK(noise_texture->get_width() == 8);
CHECK(noise_texture->get_height() == 4);
noise_texture->set_invert(true);
CHECK(noise_texture->get_invert() == true);
noise_texture->set_invert(false);
CHECK(noise_texture->get_invert() == false);
noise_texture->set_in_3d_space(true);
CHECK(noise_texture->is_in_3d_space() == true);
noise_texture->set_in_3d_space(false);
CHECK(noise_texture->is_in_3d_space() == false);
noise_texture->set_generate_mipmaps(true);
CHECK(noise_texture->is_generating_mipmaps() == true);
noise_texture->set_generate_mipmaps(false);
CHECK(noise_texture->is_generating_mipmaps() == false);
noise_texture->set_seamless(true);
CHECK(noise_texture->get_seamless() == true);
noise_texture->set_seamless(false);
CHECK(noise_texture->get_seamless() == false);
noise_texture->set_seamless_blend_skirt(0.45);
CHECK(noise_texture->get_seamless_blend_skirt() == doctest::Approx(0.45));
ERR_PRINT_OFF;
noise_texture->set_seamless_blend_skirt(-1.0);
noise_texture->set_seamless_blend_skirt(2.0);
CHECK(noise_texture->get_seamless_blend_skirt() == doctest::Approx(0.45));
ERR_PRINT_ON;
noise_texture->set_as_normal_map(true);
CHECK(noise_texture->is_normal_map() == true);
noise_texture->set_as_normal_map(false);
CHECK(noise_texture->is_normal_map() == false);
noise_texture->set_bump_strength(0.168);
CHECK(noise_texture->get_bump_strength() == doctest::Approx(0.168));
Ref<Gradient> gradient = memnew(Gradient);
noise_texture->set_color_ramp(gradient);
CHECK(noise_texture->get_color_ramp() == gradient);
noise_texture->set_color_ramp(nullptr);
CHECK(noise_texture->get_color_ramp() == nullptr);
}
TEST_CASE("[NoiseTexture2D][SceneTree] Generating a basic noise texture with mipmaps and color ramp modulation") {
Ref<NoiseTexture2D> noise_texture = memnew(NoiseTexture2D);
Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
noise_texture->set_noise(noise);
Ref<Gradient> gradient = memnew(Gradient);
Vector<Gradient::Point> points;
points.push_back({ 0.0, Color(1, 0, 0) });
points.push_back({ 1.0, Color(0, 0, 1) });
gradient->set_points(points);
noise_texture->set_color_ramp(gradient);
noise_texture->set_width(16);
noise_texture->set_height(16);
noise_texture->set_generate_mipmaps(true);
Ref<NoiseTextureTester> tester = memnew(NoiseTextureTester(noise_texture.ptr()));
noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_mip_and_color_ramp));
MessageQueue::get_singleton()->flush();
}
TEST_CASE("[NoiseTexture2D][SceneTree] Generating a normal map without mipmaps") {
Ref<NoiseTexture2D> noise_texture = memnew(NoiseTexture2D);
Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
noise->set_frequency(0.5);
noise_texture->set_noise(noise);
noise_texture->set_width(16);
noise_texture->set_height(16);
noise_texture->set_as_normal_map(true);
noise_texture->set_bump_strength(0.5);
noise_texture->set_generate_mipmaps(false);
Ref<NoiseTextureTester> tester = memnew(NoiseTextureTester(noise_texture.ptr()));
noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_normal_map));
MessageQueue::get_singleton()->flush();
}
TEST_CASE("[NoiseTexture2D][SceneTree] Generating a seamless noise texture") {
Ref<NoiseTexture2D> noise_texture = memnew(NoiseTexture2D);
Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
noise->set_frequency(0.5);
noise_texture->set_noise(noise);
noise_texture->set_width(16);
noise_texture->set_height(16);
noise_texture->set_seamless(true);
Ref<NoiseTextureTester> tester = memnew(NoiseTextureTester(noise_texture.ptr()));
SUBCASE("Grayscale(L8) 16x16, with seamless blend skirt of 0.05") {
noise_texture->set_seamless_blend_skirt(0.05);
noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_grayscale));
MessageQueue::get_singleton()->flush();
}
SUBCASE("16x16 modulated with default (transparent)black and white gradient (RGBA8), with seamless blend skirt of 1.0") {
Ref<Gradient> gradient = memnew(Gradient);
Vector<Gradient::Point> points;
points.push_back({ 0.0, Color(0, 0, 0, 0) });
points.push_back({ 1.0, Color(1, 1, 1, 1) });
gradient->set_points(points);
noise_texture->set_color_ramp(gradient);
noise_texture->set_seamless_blend_skirt(1.0);
noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTextureTester::check_seamless_texture_rgba));
MessageQueue::get_singleton()->flush();
}
}
} //namespace TestNoiseTexture2D
#endif // TEST_NOISE_TEXTURE_2D_H

View file

@ -0,0 +1,235 @@
/**************************************************************************/
/* test_noise_texture_3d.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef TEST_NOISE_TEXTURE_3D_H
#define TEST_NOISE_TEXTURE_3D_H
#include "../noise_texture_3d.h"
#include "tests/test_macros.h"
namespace TestNoiseTexture3D {
class NoiseTexture3DTester : public RefCounted {
GDCLASS(NoiseTexture3DTester, RefCounted);
const NoiseTexture3D *const texture;
public:
NoiseTexture3DTester(const NoiseTexture3D *const p_texture) :
texture{ p_texture } {};
Color compute_average_color(const Ref<Image> &p_noise_image) {
Color r_avg_color{};
for (int i = 0; i < p_noise_image->get_width(); ++i) {
for (int j = 0; j < p_noise_image->get_height(); ++j) {
const Color pixel = p_noise_image->get_pixel(i, j);
r_avg_color += pixel;
}
}
int pixel_count = p_noise_image->get_width() * p_noise_image->get_height();
r_avg_color /= pixel_count;
return r_avg_color;
}
void check_mip_and_color_ramp() {
const Vector<Ref<Image>> noise_data = texture->get_data();
for (int i = 0; i < noise_data.size(); i++) {
const Ref<Image> noise_image = noise_data[i];
CHECK(noise_image.is_valid());
CHECK(noise_image->get_width() == texture->get_width());
CHECK(noise_image->get_height() == texture->get_height());
CHECK(noise_image->get_format() == Image::FORMAT_RGBA8);
CHECK(!noise_image->has_mipmaps());
Color avg_color = compute_average_color(noise_image);
// Check that the noise texture is modulated correctly by the color ramp (Gradient).
CHECK_FALSE_MESSAGE((avg_color.r + avg_color.g + avg_color.b) == doctest::Approx(0.0), "The noise texture should not be all black");
CHECK_FALSE_MESSAGE((avg_color.r + avg_color.g + avg_color.b) == doctest::Approx(noise_image->get_width() * noise_image->get_height() * 3.0), "The noise texture should not be all white");
CHECK_MESSAGE(avg_color.g == doctest::Approx(0.0), "The noise texture should not have any green when modulated correctly by the color ramp");
}
}
void check_seamless_texture_grayscale() {
const Vector<Ref<Image>> noise_data = texture->get_data();
for (int i = 0; i < noise_data.size(); i++) {
const Ref<Image> noise_image = noise_data[i];
CHECK(noise_image.is_valid());
CHECK(noise_image->get_width() == texture->get_width());
CHECK(noise_image->get_height() == texture->get_height());
CHECK(noise_image->get_format() == Image::FORMAT_L8);
Color avg_color = compute_average_color(noise_image);
// Since it's a grayscale image and every channel except the alpha channel has the
// same values (conversion happens in Image::get_pixel) we only need to test one channel.
CHECK(avg_color.r == doctest::Approx(0.5).epsilon(0.05));
}
}
void check_seamless_texture_rgba() {
const Vector<Ref<Image>> noise_data = texture->get_data();
for (int i = 0; i < noise_data.size(); i++) {
const Ref<Image> noise_image = noise_data[i];
CHECK(noise_image.is_valid());
CHECK(noise_image->get_width() == texture->get_width());
CHECK(noise_image->get_height() == texture->get_height());
CHECK(noise_image->get_format() == Image::FORMAT_RGBA8);
// Check that the noise texture is modulated correctly by the color ramp (Gradient).
Color avg_color = compute_average_color(noise_image);
// We use a default (black to white) gradient, so the average of the red, green and blue channels should be the same.
CHECK(avg_color.r == doctest::Approx(0.5).epsilon(0.05));
CHECK(avg_color.g == doctest::Approx(0.5).epsilon(0.05));
CHECK(avg_color.b == doctest::Approx(0.5).epsilon(0.05));
}
}
};
TEST_CASE("[NoiseTexture][SceneTree] Getter and setter") {
Ref<NoiseTexture3D> noise_texture = memnew(NoiseTexture3D);
Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
noise_texture->set_noise(noise);
CHECK(noise_texture->get_noise() == noise);
noise_texture->set_noise(nullptr);
CHECK(noise_texture->get_noise() == nullptr);
noise_texture->set_width(8);
noise_texture->set_height(4);
noise_texture->set_depth(2);
CHECK(noise_texture->get_width() == 8);
CHECK(noise_texture->get_height() == 4);
CHECK(noise_texture->get_depth() == 2);
ERR_PRINT_OFF;
noise_texture->set_width(-1);
noise_texture->set_height(-1);
noise_texture->set_depth(-1);
ERR_PRINT_ON;
CHECK(noise_texture->get_width() == 8);
CHECK(noise_texture->get_height() == 4);
CHECK(noise_texture->get_depth() == 2);
noise_texture->set_invert(true);
CHECK(noise_texture->get_invert() == true);
noise_texture->set_invert(false);
CHECK(noise_texture->get_invert() == false);
noise_texture->set_seamless(true);
CHECK(noise_texture->get_seamless() == true);
noise_texture->set_seamless(false);
CHECK(noise_texture->get_seamless() == false);
noise_texture->set_seamless_blend_skirt(0.45);
CHECK(noise_texture->get_seamless_blend_skirt() == doctest::Approx(0.45));
ERR_PRINT_OFF;
noise_texture->set_seamless_blend_skirt(-1.0);
noise_texture->set_seamless_blend_skirt(2.0);
CHECK(noise_texture->get_seamless_blend_skirt() == doctest::Approx(0.45));
ERR_PRINT_ON;
Ref<Gradient> gradient = memnew(Gradient);
noise_texture->set_color_ramp(gradient);
CHECK(noise_texture->get_color_ramp() == gradient);
noise_texture->set_color_ramp(nullptr);
CHECK(noise_texture->get_color_ramp() == nullptr);
}
TEST_CASE("[NoiseTexture3D][SceneTree] Generating a basic noise texture with mipmaps and color ramp modulation") {
Ref<NoiseTexture3D> noise_texture = memnew(NoiseTexture3D);
Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
noise_texture->set_noise(noise);
Ref<Gradient> gradient = memnew(Gradient);
Vector<Gradient::Point> points;
points.push_back({ 0.0, Color(1, 0, 0) });
points.push_back({ 1.0, Color(0, 0, 1) });
gradient->set_points(points);
noise_texture->set_color_ramp(gradient);
noise_texture->set_width(16);
noise_texture->set_height(16);
noise_texture->set_depth(16);
Ref<NoiseTexture3DTester> tester = memnew(NoiseTexture3DTester(noise_texture.ptr()));
noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTexture3DTester::check_mip_and_color_ramp));
MessageQueue::get_singleton()->flush();
}
TEST_CASE("[NoiseTexture3D][SceneTree] Generating a seamless noise texture") {
Ref<NoiseTexture3D> noise_texture = memnew(NoiseTexture3D);
Ref<FastNoiseLite> noise = memnew(FastNoiseLite);
noise->set_frequency(0.5);
noise_texture->set_noise(noise);
noise_texture->set_width(16);
noise_texture->set_height(16);
noise_texture->set_depth(16);
noise_texture->set_seamless(true);
Ref<NoiseTexture3DTester> tester = memnew(NoiseTexture3DTester(noise_texture.ptr()));
SUBCASE("Grayscale(L8) 16x16x16, with seamless blend skirt of 0.05") {
noise_texture->set_seamless_blend_skirt(0.05);
noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_grayscale));
MessageQueue::get_singleton()->flush();
}
SUBCASE("16x16x16 modulated with default (transparent)black and white gradient (RGBA8), with seamless blend skirt of 1.0") {
Ref<Gradient> gradient = memnew(Gradient);
Vector<Gradient::Point> points;
points.push_back({ 0.0, Color(0, 0, 0, 0) });
points.push_back({ 1.0, Color(1, 1, 1, 1) });
gradient->set_points(points);
noise_texture->set_color_ramp(gradient);
noise_texture->set_seamless_blend_skirt(1.0);
noise_texture->connect_changed(callable_mp(tester.ptr(), &NoiseTexture3DTester::check_seamless_texture_rgba));
MessageQueue::get_singleton()->flush();
}
}
} //namespace TestNoiseTexture3D
#endif // TEST_NOISE_TEXTURE_3D_H