initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
20
modules/noise/SCsub
Normal file
20
modules/noise/SCsub
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
Import("env_modules")
|
||||
|
||||
env_noise = env_modules.Clone()
|
||||
|
||||
thirdparty_dir = "#thirdparty/noise/"
|
||||
env_noise.Prepend(CPPEXTPATH=[thirdparty_dir])
|
||||
|
||||
# Godot source files
|
||||
|
||||
module_obj = []
|
||||
|
||||
env_noise.add_source_files(module_obj, "*.cpp")
|
||||
if env.editor_build:
|
||||
env_noise.add_source_files(module_obj, "editor/*.cpp")
|
||||
|
||||
env.modules_sources += module_obj
|
19
modules/noise/config.py
Normal file
19
modules/noise/config.py
Normal 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"
|
163
modules/noise/doc_classes/FastNoiseLite.xml
Normal file
163
modules/noise/doc_classes/FastNoiseLite.xml
Normal 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.
|
||||
</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.
|
||||
</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">
|
||||
The change in frequency between octaves, also known as "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.
|
||||
</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">
|
||||
The warp algorithm.
|
||||
</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.
|
||||
</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.
|
||||
</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 ([constant TYPE_VALUE]), 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. Internally uses FastNoiseLite's OpenSimplex2 noise type.
|
||||
</constant>
|
||||
<constant name="TYPE_SIMPLEX_SMOOTH" value="1" enum="NoiseType">
|
||||
Modified, higher quality version of [constant TYPE_SIMPLEX], but slower. Internally uses FastNoiseLite's OpenSimplex2S noise type.
|
||||
</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>
|
103
modules/noise/doc_classes/Noise.xml
Normal file
103
modules/noise/doc_classes/Noise.xml
Normal 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>
|
63
modules/noise/doc_classes/NoiseTexture2D.xml
Normal file
63
modules/noise/doc_classes/NoiseTexture2D.xml
Normal 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>
|
51
modules/noise/doc_classes/NoiseTexture3D.xml
Normal file
51
modules/noise/doc_classes/NoiseTexture3D.xml
Normal 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>
|
148
modules/noise/editor/noise_editor_plugin.cpp
Normal file
148
modules/noise/editor/noise_editor_plugin.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
/**************************************************************************/
|
||||
/* 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"
|
||||
|
||||
#include "../noise.h"
|
||||
#include "../noise_texture_2d.h"
|
||||
|
||||
#include "editor/inspector/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_plugin_name() const {
|
||||
return Noise::get_class_static();
|
||||
}
|
||||
|
||||
NoiseEditorPlugin::NoiseEditorPlugin() {
|
||||
Ref<NoiseEditorInspectorPlugin> plugin;
|
||||
plugin.instantiate();
|
||||
add_inspector_plugin(plugin);
|
||||
}
|
42
modules/noise/editor/noise_editor_plugin.h
Normal file
42
modules/noise/editor/noise_editor_plugin.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "editor/plugins/editor_plugin.h"
|
||||
|
||||
class NoiseEditorPlugin : public EditorPlugin {
|
||||
GDCLASS(NoiseEditorPlugin, EditorPlugin)
|
||||
|
||||
public:
|
||||
String get_plugin_name() const override;
|
||||
|
||||
NoiseEditorPlugin();
|
||||
};
|
504
modules/noise/fastnoise_lite.cpp
Normal file
504
modules/noise/fastnoise_lite.cpp
Normal file
@@ -0,0 +1,504 @@
|
||||
/**************************************************************************/
|
||||
/* 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"
|
||||
|
||||
#include "core/config/engine.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 (!Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
221
modules/noise/fastnoise_lite.h
Normal file
221
modules/noise/fastnoise_lite.h
Normal file
@@ -0,0 +1,221 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "noise.h"
|
||||
|
||||
#include "thirdparty/misc/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);
|
1
modules/noise/icons/NoiseTexture2D.svg
Normal file
1
modules/noise/icons/NoiseTexture2D.svg
Normal 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: 224 B |
1
modules/noise/icons/NoiseTexture3D.svg
Normal file
1
modules/noise/icons/NoiseTexture3D.svg
Normal 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: 527 B |
197
modules/noise/noise.cpp
Normal file
197
modules/noise/noise.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/**************************************************************************/
|
||||
/* 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"
|
||||
|
||||
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.is_empty()) {
|
||||
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));
|
||||
}
|
299
modules/noise/noise.h
Normal file
299
modules/noise/noise.h
Normal file
@@ -0,0 +1,299 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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;
|
||||
};
|
394
modules/noise/noise_texture_2d.cpp
Normal file
394
modules/noise/noise_texture_2d.cpp
Normal file
@@ -0,0 +1,394 @@
|
||||
/**************************************************************************/
|
||||
/* 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_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_noise", "noise"), &NoiseTexture2D::set_noise);
|
||||
ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture2D::get_noise);
|
||||
|
||||
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_seamless", "seamless"), &NoiseTexture2D::set_seamless);
|
||||
ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture2D::get_seamless);
|
||||
|
||||
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_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_normalize", "normalize"), &NoiseTexture2D::set_normalize);
|
||||
ClassDB::bind_method(D_METHOD("is_normalized"), &NoiseTexture2D::is_normalized);
|
||||
|
||||
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_bump_strength", "bump_strength"), &NoiseTexture2D::set_bump_strength);
|
||||
ClassDB::bind_method(D_METHOD("get_bump_strength"), &NoiseTexture2D::get_bump_strength);
|
||||
|
||||
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, "generate_mipmaps"), "set_generate_mipmaps", "is_generating_mipmaps");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "Noise"), "set_noise", "get_noise");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless");
|
||||
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, "as_normal_map"), "set_as_normal_map", "is_normal_map");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "normalize"), "set_normalize", "is_normalized");
|
||||
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::FLOAT, "bump_strength", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_bump_strength", "get_bump_strength");
|
||||
}
|
||||
|
||||
void NoiseTexture2D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (!Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
#ifndef THREADS_ENABLED
|
||||
use_thread = false;
|
||||
#endif
|
||||
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;
|
||||
}
|
124
modules/noise/noise_texture_2d.h
Normal file
124
modules/noise/noise_texture_2d.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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();
|
||||
};
|
359
modules/noise/noise_texture_3d.cpp
Normal file
359
modules/noise/noise_texture_3d.cpp
Normal file
@@ -0,0 +1,359 @@
|
||||
/**************************************************************************/
|
||||
/* 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_noise", "noise"), &NoiseTexture3D::set_noise);
|
||||
ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture3D::get_noise);
|
||||
|
||||
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_seamless", "seamless"), &NoiseTexture3D::set_seamless);
|
||||
ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture3D::get_seamless);
|
||||
|
||||
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_normalize", "normalize"), &NoiseTexture3D::set_normalize);
|
||||
ClassDB::bind_method(D_METHOD("is_normalized"), &NoiseTexture3D::is_normalized);
|
||||
|
||||
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);
|
||||
|
||||
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::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "Noise"), "set_noise", "get_noise");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert"), "set_invert", "get_invert");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "normalize"), "set_normalize", "is_normalized");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "seamless_blend_skirt", PROPERTY_HINT_RANGE, "0.05,1,0.001"), "set_seamless_blend_skirt", "get_seamless_blend_skirt");
|
||||
}
|
||||
|
||||
void NoiseTexture3D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (!Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
#ifndef THREADS_ENABLED
|
||||
use_thread = false;
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
bool NoiseTexture3D::has_mipmaps() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
114
modules/noise/noise_texture_3d.h
Normal file
114
modules/noise/noise_texture_3d.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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 bool has_mipmaps() 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();
|
||||
};
|
66
modules/noise/register_types.cpp
Normal file
66
modules/noise/register_types.cpp
Normal 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;
|
||||
}
|
||||
}
|
36
modules/noise/register_types.h
Normal file
36
modules/noise/register_types.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "modules/register_module_types.h"
|
||||
|
||||
void initialize_noise_module(ModuleInitializationLevel p_level);
|
||||
void uninitialize_noise_module(ModuleInitializationLevel p_level);
|
634
modules/noise/tests/test_fastnoise_lite.h
Normal file
634
modules/noise/tests/test_fastnoise_lite.h
Normal file
@@ -0,0 +1,634 @@
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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
|
267
modules/noise/tests/test_noise_texture_2d.h
Normal file
267
modules/noise/tests/test_noise_texture_2d.h
Normal 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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().is_null());
|
||||
|
||||
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().is_null());
|
||||
}
|
||||
|
||||
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<float> offsets = { 0.0, 1.0 };
|
||||
Vector<Color> colors = { Color(1, 0, 0), Color(0, 0, 1) };
|
||||
gradient->set_offsets(offsets);
|
||||
gradient->set_colors(colors);
|
||||
|
||||
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<float> offsets = { 0.0, 1.0 };
|
||||
Vector<Color> colors = { Color(0, 0, 0, 0), Color(1, 1, 1, 1) };
|
||||
gradient->set_offsets(offsets);
|
||||
gradient->set_colors(colors);
|
||||
|
||||
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
|
235
modules/noise/tests/test_noise_texture_3d.h
Normal file
235
modules/noise/tests/test_noise_texture_3d.h
Normal 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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().is_null());
|
||||
|
||||
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().is_null());
|
||||
}
|
||||
|
||||
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<float> offsets = { 0.0, 1.0 };
|
||||
Vector<Color> colors = { Color(1, 0, 0), Color(0, 0, 1) };
|
||||
gradient->set_offsets(offsets);
|
||||
gradient->set_colors(colors);
|
||||
|
||||
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<float> offsets = { 0.0, 1.0 };
|
||||
Vector<Color> colors = { Color(0, 0, 0, 0), Color(1, 1, 1, 1) };
|
||||
gradient->set_offsets(offsets);
|
||||
gradient->set_colors(colors);
|
||||
|
||||
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
|
Reference in New Issue
Block a user