diff options
Diffstat (limited to 'modules/noise')
-rw-r--r-- | modules/noise/SCsub | 33 | ||||
-rw-r--r-- | modules/noise/config.py | 18 | ||||
-rw-r--r-- | modules/noise/doc_classes/FastNoiseLite.xml | 169 | ||||
-rw-r--r-- | modules/noise/doc_classes/Noise.xml | 72 | ||||
-rw-r--r-- | modules/noise/doc_classes/NoiseTexture.xml | 47 | ||||
-rw-r--r-- | modules/noise/fastnoise_lite.cpp | 577 | ||||
-rw-r--r-- | modules/noise/fastnoise_lite.h | 237 | ||||
-rw-r--r-- | modules/noise/icons/NoiseTexture.svg | 1 | ||||
-rw-r--r-- | modules/noise/noise.cpp | 68 | ||||
-rw-r--r-- | modules/noise/noise.h | 240 | ||||
-rw-r--r-- | modules/noise/noise_texture.cpp | 299 | ||||
-rw-r--r-- | modules/noise/noise_texture.h | 109 | ||||
-rw-r--r-- | modules/noise/register_types.cpp | 44 | ||||
-rw-r--r-- | modules/noise/register_types.h | 37 |
14 files changed, 1951 insertions, 0 deletions
diff --git a/modules/noise/SCsub b/modules/noise/SCsub new file mode 100644 index 0000000000..3e8395b9b1 --- /dev/null +++ b/modules/noise/SCsub @@ -0,0 +1,33 @@ +#!/usr/bin/env python + +Import("env") +Import("env_modules") + +env_noise = env_modules.Clone() + +# Thirdparty source files + +thirdparty_obj = [] + +thirdparty_dir = "#thirdparty/noise/" +thirdparty_sources = [ + # Add C++ source files for noise modules here +] +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] + +env_noise.Prepend(CPPPATH=[thirdparty_dir]) + +env_thirdparty = env_noise.Clone() +env_thirdparty.disable_warnings() +env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) +env.modules_sources += thirdparty_obj + +# Godot source files + +module_obj = [] + +env_noise.add_source_files(module_obj, "*.cpp") +env.modules_sources += module_obj + +# Needed to force rebuilding the module files when the thirdparty library is updated. +env.Depends(module_obj, thirdparty_obj) diff --git a/modules/noise/config.py b/modules/noise/config.py new file mode 100644 index 0000000000..74db20f2a4 --- /dev/null +++ b/modules/noise/config.py @@ -0,0 +1,18 @@ +def can_build(env, platform): + return True + + +def configure(env): + pass + + +def get_doc_classes(): + return [ + "FastNoiseLite", + "Noise", + "NoiseTexture", + ] + + +def get_doc_path(): + return "doc_classes" diff --git a/modules/noise/doc_classes/FastNoiseLite.xml b/modules/noise/doc_classes/FastNoiseLite.xml new file mode 100644 index 0000000000..b6d91850c4 --- /dev/null +++ b/modules/noise/doc_classes/FastNoiseLite.xml @@ -0,0 +1,169 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="FastNoiseLite" inherits="Noise" version="4.0" 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], however not always. Some of the cellular noise algorithms return results above [code]1[/code]. + </description> + <tutorials> + </tutorials> + <members> + <member name="cellular_distance_function" type="int" setter="set_cellular_distance_function" getter="get_cellular_distance_function" enum="FastNoiseLite.CellularDistanceFunction" default="0"> + Determines how the distance to the nearest/second-nearest point is computed. See [enum CellularDistanceFunction] for options. + </member> + <member name="cellular_jitter" type="float" setter="set_cellular_jitter" getter="get_cellular_jitter" default="0.45"> + 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="0"> + Return type from cellular noise calculations. See [enum CellularReturnType]. + </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="domain_warp_amplitude" type="float" setter="set_domain_warp_amplitude" getter="get_domain_warp_amplitude" default="30.0"> + Sets the maximum warp distance from the origin. + </member> + <member name="domain_warp_enabled" type="bool" setter="set_domain_warp_enabled" getter="is_domain_warp_enabled" default="false"> + If enabled, another FastNoiseLite instance is used to warp the space, resulting in a distortion of the noise. + </member> + <member name="domain_warp_fractal_gain" type="float" setter="set_domain_warp_fractal_gain" getter="get_domain_warp_fractal_gain" default="0.5"> + Determines the strength of each subsequent layer of the noise which is used to warp the space. + A low value places more emphasis on the lower frequency base layers, while a high value puts more emphasis on the higher frequency layers. + </member> + <member name="domain_warp_fractal_lacunarity" type="float" setter="set_domain_warp_fractal_lacunarity" getter="get_domain_warp_fractal_lacunarity" default="6.0"> + Octave lacunarity of the fractal noise which warps the space. Increasing this value results in higher octaves producing noise with finer details and a rougher appearance. + </member> + <member name="domain_warp_fractal_octaves" type="int" setter="set_domain_warp_fractal_octaves" getter="get_domain_warp_fractal_octaves" default="5"> + The number of noise layers that are sampled to get the final value for the fractal noise which warps the space. + </member> + <member name="domain_warp_fractal_type" type="int" setter="set_domain_warp_fractal_type" getter="get_domain_warp_fractal_type" enum="FastNoiseLite.DomainWarpFractalType" default="1"> + The method for combining octaves into a fractal which is used to warp the space. See [enum DomainWarpFractalType]. + </member> + <member name="domain_warp_frequency" type="float" setter="set_domain_warp_frequency" getter="get_domain_warp_frequency" default="0.05"> + Frequency of the noise which warps the space. Low frequency results in smooth noise while high frequency results in rougher, more granular noise. + </member> + <member name="domain_warp_type" type="int" setter="set_domain_warp_type" getter="get_domain_warp_type" enum="FastNoiseLite.DomainWarpType" default="0"> + Sets the warp algorithm. See [enum DomainWarpType]. + </member> + <member name="fractal_gain" type="float" setter="set_fractal_gain" getter="get_fractal_gain" default="0.5"> + Determines the strength of each subsequent layer of noise in fractal noise. + A low value places more emphasis on the lower frequency base layers, while a high value puts more emphasis on the higher frequency layers. + </member> + <member name="fractal_lacunarity" type="float" setter="set_fractal_lacunarity" getter="get_fractal_lacunarity" default="2.0"> + Frequency multiplier between subsequent octaves. Increasing this value results in higher octaves producing noise with finer details and a rougher appearance. + </member> + <member name="fractal_octaves" type="int" setter="set_fractal_octaves" getter="get_fractal_octaves" default="5"> + The number of noise layers that are sampled to get the final value for fractal noise types. + </member> + <member name="fractal_ping_pong_strength" type="float" setter="set_fractal_ping_pong_strength" getter="get_fractal_ping_pong_strength" default="2.0"> + Sets the strength of the fractal ping pong type. + </member> + <member name="fractal_type" type="int" setter="set_fractal_type" getter="get_fractal_type" enum="FastNoiseLite.FractalType" default="1"> + The method for combining octaves into a fractal. See [enum FractalType]. + </member> + <member name="fractal_weighted_strength" type="float" setter="set_fractal_weighted_strength" getter="get_fractal_weighted_strength" default="0.0"> + Higher weighting means higher octaves have less impact if lower octaves have a large impact. + </member> + <member name="frequency" type="float" setter="set_frequency" getter="get_frequency" default="0.01"> + The frequency for all noise types. Low frequency results in smooth noise while high frequency results in rougher, more granular noise. + </member> + <member name="in_3d_space" type="bool" setter="set_in_3d_space" getter="is_in_3d_space" default="false"> + Determines whether the noise image returned by [method Noise.get_image] is calculated in 3d space. May result in reduced contrast. + </member> + <member name="noise_type" type="int" setter="set_noise_type" getter="get_noise_type" enum="FastNoiseLite.NoiseType" default="1"> + The noise algorithm used. See [enum NoiseType]. + </member> + <member name="offset" type="Vector3" setter="set_offset" getter="get_offset" default="Vector3(0, 0, 0)"> + Translate the noise input coordinates by the given [Vector3]. + </member> + <member name="seed" type="int" setter="set_seed" getter="get_seed" default="0"> + The random number seed for all noise types. + </member> + </members> + <constants> + <constant name="TYPE_VALUE" value="5" enum="NoiseType"> + A lattice of points are assigned random values then interpolated based on neighboring values. + </constant> + <constant name="TYPE_VALUE_CUBIC" value="4" enum="NoiseType"> + Similar to Value noise, but slower. Has more variance in peaks and valleys. + Cubic noise can be used to avoid certain artifacts when using value noise to create a bumpmap. In general, you should always use this mode if the value noise is being used for a heightmap or bumpmap. + </constant> + <constant name="TYPE_PERLIN" value="3" enum="NoiseType"> + A lattice of random gradients. Their dot products are interpolated to obtain values in between the lattices. + </constant> + <constant name="TYPE_CELLULAR" value="2" enum="NoiseType"> + Cellular includes both Worley noise and Voronoi diagrams which creates various regions of the same value. + </constant> + <constant name="TYPE_SIMPLEX" value="0" enum="NoiseType"> + As opposed to [constant TYPE_PERLIN], gradients exist in a simplex lattice rather than a grid lattice, avoiding directional artifacts. + </constant> + <constant name="TYPE_SIMPLEX_SMOOTH" value="1" enum="NoiseType"> + Modified, higher quality version of [constant TYPE_SIMPLEX], but slower. + </constant> + <constant name="FRACTAL_NONE" value="0" enum="FractalType"> + No fractal noise. + </constant> + <constant name="FRACTAL_FBM" value="1" enum="FractalType"> + Method using Fractional Brownian Motion to combine octaves into a fractal. + </constant> + <constant name="FRACTAL_RIDGED" value="2" enum="FractalType"> + Method of combining octaves into a fractal resulting in a "ridged" look. + </constant> + <constant name="FRACTAL_PING_PONG" value="3" enum="FractalType"> + Method of combining octaves into a fractal with a ping pong effect. + </constant> + <constant name="DISTANCE_EUCLIDEAN" value="0" enum="CellularDistanceFunction"> + Euclidean distance to the nearest point. + </constant> + <constant name="DISTANCE_EUCLIDEAN_SQUARED" value="1" enum="CellularDistanceFunction"> + Squared Euclidean distance to the nearest point. + </constant> + <constant name="DISTANCE_MANHATTAN" value="2" enum="CellularDistanceFunction"> + Manhattan distance (taxicab metric) to the nearest point. + </constant> + <constant name="DISTANCE_HYBRID" value="3" enum="CellularDistanceFunction"> + Blend of [constant DISTANCE_EUCLIDEAN] and [constant DISTANCE_MANHATTAN] to give curved cell boundaries + </constant> + <constant name="RETURN_CELL_VALUE" value="0" enum="CellularReturnType"> + The cellular distance function will return the same value for all points within a cell. + </constant> + <constant name="RETURN_DISTANCE" value="1" enum="CellularReturnType"> + The cellular distance function will return a value determined by the distance to the nearest point. + </constant> + <constant name="RETURN_DISTANCE2" value="2" enum="CellularReturnType"> + The cellular distance function returns the distance to the second-nearest point. + </constant> + <constant name="RETURN_DISTANCE2_ADD" value="3" enum="CellularReturnType"> + The distance to the nearest point is added to the distance to the second-nearest point. + </constant> + <constant name="RETURN_DISTANCE2_SUB" value="4" enum="CellularReturnType"> + The distance to the nearest point is subtracted from the distance to the second-nearest point. + </constant> + <constant name="RETURN_DISTANCE2_MUL" value="5" enum="CellularReturnType"> + The distance to the nearest point is multiplied with the distance to the second-nearest point. + </constant> + <constant name="RETURN_DISTANCE2_DIV" value="6" enum="CellularReturnType"> + The distance to the nearest point is divided by the distance to the second-nearest point. + </constant> + <constant name="DOMAIN_WARP_SIMPLEX" value="0" enum="DomainWarpType"> + The domain is warped using the simplex noise algorithm. + </constant> + <constant name="DOMAIN_WARP_SIMPLEX_REDUCED" value="1" enum="DomainWarpType"> + The domain is warped using a simplified version of the simplex noise algorithm. + </constant> + <constant name="DOMAIN_WARP_BASIC_GRID" value="2" enum="DomainWarpType"> + The domain is warped using a simple noise grid (not as smooth as the other methods, but more performant). + </constant> + <constant name="DOMAIN_WARP_FRACTAL_NONE" value="0" enum="DomainWarpFractalType"> + No fractal noise for warping the space. + </constant> + <constant name="DOMAIN_WARP_FRACTAL_PROGRESSIVE" value="1" enum="DomainWarpFractalType"> + Warping the space progressively, octave for octave, resulting in a more "liquified" distortion. + </constant> + <constant name="DOMAIN_WARP_FRACTAL_INDEPENDENT" value="2" enum="DomainWarpFractalType"> + Warping the space independently for each octave, resulting in a more chaotic distortion. + </constant> + </constants> +</class> diff --git a/modules/noise/doc_classes/Noise.xml b/modules/noise/doc_classes/Noise.xml new file mode 100644 index 0000000000..db0dec18d2 --- /dev/null +++ b/modules/noise/doc_classes/Noise.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="Noise" inherits="Resource" version="4.0" 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 get_seamless_noise() implementation is provided for libraries that do not provide seamless noise. This function requests a larger image from get_image(), 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"> + <return type="Image" /> + <argument index="0" name="width" type="int" /> + <argument index="1" name="height" type="int" /> + <argument index="2" name="invert" type="bool" default="false" /> + <description> + Returns a 2D [Image] noise image. + </description> + </method> + <method name="get_noise_1d"> + <return type="float" /> + <argument index="0" name="x" type="float" /> + <description> + Returns the 1D noise value at the given (x) coordinate. + </description> + </method> + <method name="get_noise_2d"> + <return type="float" /> + <argument index="0" name="x" type="float" /> + <argument index="1" name="y" type="float" /> + <description> + Returns the 2D noise value at the given position. + </description> + </method> + <method name="get_noise_2dv"> + <return type="float" /> + <argument index="0" name="v" type="Vector2" /> + <description> + Returns the 2D noise value at the given position. + </description> + </method> + <method name="get_noise_3d"> + <return type="float" /> + <argument index="0" name="x" type="float" /> + <argument index="1" name="y" type="float" /> + <argument index="2" name="z" type="float" /> + <description> + Returns the 3D noise value at the given position. + </description> + </method> + <method name="get_noise_3dv"> + <return type="float" /> + <argument index="0" name="v" type="Vector3" /> + <description> + Returns the 3D noise value at the given position. + </description> + </method> + <method name="get_seamless_image"> + <return type="Image" /> + <argument index="0" name="width" type="int" /> + <argument index="1" name="height" type="int" /> + <argument index="2" name="invert" type="bool" default="false" /> + <argument index="3" name="skirt" type="float" default="0.1" /> + <description> + Returns a seamless 2D [Image] noise image. + </description> + </method> + </methods> +</class> diff --git a/modules/noise/doc_classes/NoiseTexture.xml b/modules/noise/doc_classes/NoiseTexture.xml new file mode 100644 index 0000000000..63630eccde --- /dev/null +++ b/modules/noise/doc_classes/NoiseTexture.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="NoiseTexture" inherits="Texture2D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> + <brief_description> + A texture filled with noise generated by a [Noise] object. + </brief_description> + <description> + Uses [FastNoiseLite] or other libraries to fill the texture data of your desired size. + NoiseTexture can also generate normalmap 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 = NoiseTexture.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="height" type="int" setter="set_height" getter="get_height" default="512"> + Height of the generated texture. + </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="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. + </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. + </member> + <member name="width" type="int" setter="set_width" getter="get_width" default="512"> + Width of the generated texture. + </member> + </members> +</class> diff --git a/modules/noise/fastnoise_lite.cpp b/modules/noise/fastnoise_lite.cpp new file mode 100644 index 0000000000..974b5c55bb --- /dev/null +++ b/modules/noise/fastnoise_lite.cpp @@ -0,0 +1,577 @@ +/*************************************************************************/ +/* fastnoise_lite.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "fastnoise_lite.h" + +FastNoiseLite::FastNoiseLite() { + // Most defaults copied from the library. + set_noise_type(TYPE_SIMPLEX_SMOOTH); + set_seed(0); + set_frequency(0.01); + set_in_3d_space(false); + + set_fractal_type(FRACTAL_FBM); + set_fractal_octaves(5); + set_fractal_lacunarity(2.0); + set_fractal_gain(0.5); + set_fractal_weighted_strength(0.0); + set_fractal_ping_pong_strength(2.0); + + set_cellular_distance_function(DISTANCE_EUCLIDEAN); + set_cellular_return_type(RETURN_CELL_VALUE); + set_cellular_jitter(0.45); + + set_domain_warp_enabled(false); + set_domain_warp_type(DOMAIN_WARP_SIMPLEX); + set_domain_warp_amplitude(30.0); + set_domain_warp_frequency(0.05); + set_domain_warp_fractal_type(DOMAIN_WARP_FRACTAL_PROGRESSIVE); + set_domain_warp_fractal_octaves(5); + set_domain_warp_fractal_lacunarity(6); + set_domain_warp_fractal_gain(0.5); +} + +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); + 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_in_3d_space(bool p_enable) { + in_3d_space = p_enable; + emit_changed(); +} +bool FastNoiseLite::is_in_3d_space() const { + return in_3d_space; +} + +void FastNoiseLite::set_offset(Vector3 p_offset) { + offset = p_offset; + emit_changed(); +} + +Vector3 FastNoiseLite::get_offset() const { + return offset; +} + +void FastNoiseLite::set_color_ramp(const Ref<Gradient> &p_gradient) { + color_ramp = p_gradient; + if (color_ramp.is_valid()) { + color_ramp->connect(SNAME("changed"), callable_mp(this, &FastNoiseLite::_changed)); + emit_changed(); + } +} + +Ref<Gradient> FastNoiseLite::get_color_ramp() const { + return color_ramp; +} + +// Noise functions. + +real_t FastNoiseLite::get_noise_1d(real_t p_x) { + return get_noise_2d(p_x, 0.0); +} + +real_t FastNoiseLite::get_noise_2dv(Vector2 p_v) { + return get_noise_2d(p_v.x, p_v.y); +} + +real_t FastNoiseLite::get_noise_2d(real_t p_x, real_t p_y) { + if (domain_warp_enabled) { + _domain_warp_noise.DomainWarp(p_x, p_y); + } + return _noise.GetNoise(p_x + offset.x, p_y + offset.y); +} + +real_t FastNoiseLite::get_noise_3dv(Vector3 p_v) { + 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) { + if (domain_warp_enabled) { + _domain_warp_noise.DomainWarp(p_x, p_y, p_z); + } + return _noise.GetNoise(p_x + offset.x, p_y + offset.y, p_z + offset.z); +} + +// 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_pinp_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_pinp_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; + + // This needs manual conversion because Godots Inspector property API does not support discontiguous enum indices. + _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; + } + + _domain_warp_noise.SetFractalType(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; +} + +// Textures. + +Ref<Image> FastNoiseLite::get_image(int p_width, int p_height, bool p_invert) { + bool grayscale = color_ramp.is_null(); + + Vector<uint8_t> data; + data.resize(p_width * p_height * (grayscale ? 1 : 4)); + + uint8_t *wd8 = data.ptrw(); + + // Get all values and identify min/max values. + Vector<real_t> values; + values.resize(p_width * p_height); + real_t min_val = 100; + real_t max_val = -100; + + for (int y = 0, i = 0; y < p_height; y++) { + for (int x = 0; x < p_width; x++, i++) { + values.set(i, is_in_3d_space() ? get_noise_3d(x, y, 0.0) : get_noise_2d(x, y)); + if (values[i] > max_val) { + max_val = values[i]; + } + if (values[i] < min_val) { + min_val = values[i]; + } + } + } + + // Normalize values and write to texture. + uint8_t value; + for (int i = 0, x = 0; i < p_height; i++) { + for (int j = 0; j < p_width; j++, x++) { + if (max_val == min_val) { + value = 0; + } else { + value = uint8_t(CLAMP((values[x] - min_val) / (max_val - min_val) * 255.f, 0, 255)); + } + if (p_invert) { + value = 255 - value; + } + if (grayscale) { + wd8[x] = value; + } else { + float luminance = value / 255.0; + Color ramp_color = color_ramp->get_color_at_offset(luminance); + wd8[x * 4 + 0] = uint8_t(CLAMP(ramp_color.r * 255, 0, 255)); + wd8[x * 4 + 1] = uint8_t(CLAMP(ramp_color.g * 255, 0, 255)); + wd8[x * 4 + 2] = uint8_t(CLAMP(ramp_color.b * 255, 0, 255)); + wd8[x * 4 + 3] = uint8_t(CLAMP(ramp_color.a * 255, 0, 255)); + } + } + } + if (grayscale) { + return memnew(Image(p_width, p_height, false, Image::FORMAT_L8, data)); + } else { + return memnew(Image(p_width, p_height, false, Image::FORMAT_RGBA8, data)); + } +} + +Ref<Image> FastNoiseLite::get_seamless_image(int p_width, int p_height, bool p_invert, real_t p_blend_skirt) { + // Just return parent function. This is here only so Godot will properly document this function. + return Noise::get_seamless_image(p_width, p_height, p_invert, p_blend_skirt); +} + +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); + ADD_PROPERTY(PropertyInfo(Variant::INT, "noise_type", PROPERTY_HINT_ENUM, "Simplex,Simplex Smooth,Cellular,Perlin,Value Cubic,Value"), "set_noise_type", "get_noise_type"); + + ClassDB::bind_method(D_METHOD("set_seed", "seed"), &FastNoiseLite::set_seed); + ClassDB::bind_method(D_METHOD("get_seed"), &FastNoiseLite::get_seed); + ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed"); + + ClassDB::bind_method(D_METHOD("set_frequency", "freq"), &FastNoiseLite::set_frequency); + ClassDB::bind_method(D_METHOD("get_frequency"), &FastNoiseLite::get_frequency); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frequency", PROPERTY_HINT_RANGE, ".001,1"), "set_frequency", "get_frequency"); + + ClassDB::bind_method(D_METHOD("set_in_3d_space", "enable"), &FastNoiseLite::set_in_3d_space); + ClassDB::bind_method(D_METHOD("is_in_3d_space"), &FastNoiseLite::is_in_3d_space); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "in_3d_space"), "set_in_3d_space", "is_in_3d_space"); + + ClassDB::bind_method(D_METHOD("set_offset", "offset"), &FastNoiseLite::set_offset); + ClassDB::bind_method(D_METHOD("get_offset"), &FastNoiseLite::get_offset); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "offset", PROPERTY_HINT_RANGE, "-999999999,999999999,1"), "set_offset", "get_offset"); + + ClassDB::bind_method(D_METHOD("set_color_ramp", "gradient"), &FastNoiseLite::set_color_ramp); + ClassDB::bind_method(D_METHOD("get_color_ramp"), &FastNoiseLite::get_color_ramp); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp"); + + // Fractal. + + ADD_GROUP("Fractal", "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); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fractal_type", PROPERTY_HINT_ENUM, "None,FBM,Ridged,PingPong"), "set_fractal_type", "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); + ADD_PROPERTY(PropertyInfo(Variant::INT, "fractal_octaves", PROPERTY_HINT_RANGE, "1,10,1"), "set_fractal_octaves", "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); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fractal_lacunarity"), "set_fractal_lacunarity", "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); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fractal_gain"), "set_fractal_gain", "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); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fractal_weighted_strength", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_fractal_weighted_strength", "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); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fractal_ping_pong_strength"), "set_fractal_ping_pong_strength", "get_fractal_ping_pong_strength"); + + // Cellular. + + ADD_GROUP("Cellular", "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); + ADD_PROPERTY(PropertyInfo(Variant::INT, "cellular_distance_function", PROPERTY_HINT_ENUM, "Euclidean,EuclideanSquared,Manhattan,Hybrid"), "set_cellular_distance_function", "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); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cellular_jitter"), "set_cellular_jitter", "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); + ADD_PROPERTY(PropertyInfo(Variant::INT, "cellular_return_type", PROPERTY_HINT_ENUM, "CellValue,Distance,Distance2,Distance2Add,Distance2Sub,Distance2Mul,Distance2Div"), "set_cellular_return_type", "get_cellular_return_type"); + + // Domain warp. + + ADD_GROUP("Domain Warp", "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); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "domain_warp_enabled"), "set_domain_warp_enabled", "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); + ADD_PROPERTY(PropertyInfo(Variant::INT, "domain_warp_type", PROPERTY_HINT_ENUM, "Simplex,SimplexReduced,BasicGrid"), "set_domain_warp_type", "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); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "domain_warp_amplitude"), "set_domain_warp_amplitude", "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); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "domain_warp_frequency"), "set_domain_warp_frequency", "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); + 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"); + + 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); + 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"); + + 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); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "domain_warp_fractal_lacunarity"), "set_domain_warp_fractal_lacunarity", "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); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "domain_warp_fractal_gain"), "set_domain_warp_fractal_gain", "get_domain_warp_fractal_gain"); + + ClassDB::bind_method(D_METHOD("_changed"), &FastNoiseLite::_changed); + + 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 &property) const { + if (property.name.begins_with("cellular") && get_noise_type() != TYPE_CELLULAR) { + property.usage = PROPERTY_USAGE_NO_EDITOR; + return; + } + + if (property.name != "fractal_type" && property.name.begins_with("fractal") && get_fractal_type() == FRACTAL_NONE) { + property.usage = PROPERTY_USAGE_NO_EDITOR; + return; + } + + if (property.name == "fractal_ping_pong_strength" && get_fractal_type() != FRACTAL_PING_PONG) { + property.usage = PROPERTY_USAGE_NO_EDITOR; + return; + } + + if (property.name != "domain_warp_enabled" && property.name.begins_with("domain_warp") && !domain_warp_enabled) { + property.usage = PROPERTY_USAGE_NO_EDITOR; + return; + } +} diff --git a/modules/noise/fastnoise_lite.h b/modules/noise/fastnoise_lite.h new file mode 100644 index 0000000000..4635e26d28 --- /dev/null +++ b/modules/noise/fastnoise_lite.h @@ -0,0 +1,237 @@ +/*************************************************************************/ +/* fastnoise_lite.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FASTNOISE_LITE_H +#define FASTNOISE_LITE_H + +#include "core/io/image.h" +#include "core/object/ref_counted.h" +#include "noise.h" +#include "scene/resources/gradient.h" + +#include <thirdparty/noise/FastNoiseLite.h> + +typedef fastnoiselite::FastNoiseLite _FastNoiseLite; + +class FastNoiseLite : public Noise { + GDCLASS(FastNoiseLite, Noise); + OBJ_SAVE_TYPE(FastNoiseLite); + +public: + enum NoiseType { + TYPE_SIMPLEX = _FastNoiseLite::NoiseType_OpenSimplex2, + TYPE_SIMPLEX_SMOOTH = _FastNoiseLite::NoiseType_OpenSimplex2S, + TYPE_CELLULAR = _FastNoiseLite::NoiseType_Cellular, + TYPE_PERLIN = _FastNoiseLite::NoiseType_Perlin, + TYPE_VALUE_CUBIC = _FastNoiseLite::NoiseType_ValueCubic, + TYPE_VALUE = _FastNoiseLite::NoiseType_Value, + }; + + enum FractalType { + FRACTAL_NONE = _FastNoiseLite::FractalType_None, + FRACTAL_FBM = _FastNoiseLite::FractalType_FBm, + FRACTAL_RIDGED = _FastNoiseLite::FractalType_Ridged, + FRACTAL_PING_PONG = _FastNoiseLite::FractalType_PingPong, + }; + + enum CellularDistanceFunction { + DISTANCE_EUCLIDEAN = _FastNoiseLite::CellularDistanceFunction_Euclidean, + DISTANCE_EUCLIDEAN_SQUARED = _FastNoiseLite::CellularDistanceFunction_EuclideanSq, + DISTANCE_MANHATTAN = _FastNoiseLite::CellularDistanceFunction_Manhattan, + DISTANCE_HYBRID = _FastNoiseLite::CellularDistanceFunction_Hybrid + }; + + enum CellularReturnType { + RETURN_CELL_VALUE = _FastNoiseLite::CellularReturnType_CellValue, + RETURN_DISTANCE = _FastNoiseLite::CellularReturnType_Distance, + RETURN_DISTANCE2 = _FastNoiseLite::CellularReturnType_Distance2, + RETURN_DISTANCE2_ADD = _FastNoiseLite::CellularReturnType_Distance2Add, + RETURN_DISTANCE2_SUB = _FastNoiseLite::CellularReturnType_Distance2Sub, + RETURN_DISTANCE2_MUL = _FastNoiseLite::CellularReturnType_Distance2Mul, + RETURN_DISTANCE2_DIV = _FastNoiseLite::CellularReturnType_Distance2Div + }; + + enum DomainWarpType { + DOMAIN_WARP_SIMPLEX = _FastNoiseLite::DomainWarpType_OpenSimplex2, + DOMAIN_WARP_SIMPLEX_REDUCED = _FastNoiseLite::DomainWarpType_OpenSimplex2Reduced, + DOMAIN_WARP_BASIC_GRID = _FastNoiseLite::DomainWarpType_BasicGrid + }; + + enum DomainWarpFractalType { + DOMAIN_WARP_FRACTAL_NONE, + DOMAIN_WARP_FRACTAL_PROGRESSIVE, + DOMAIN_WARP_FRACTAL_INDEPENDENT + }; + +protected: + static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const override; + +private: + _FastNoiseLite _noise; + _FastNoiseLite _domain_warp_noise; + + Vector3 offset; + NoiseType noise_type; + Ref<Gradient> color_ramp; + + int seed; + real_t frequency; + bool in_3d_space; + + // Fractal specific. + FractalType fractal_type; + int fractal_octaves; + real_t fractal_lacunarity; + real_t fractal_gain; + real_t fractal_weighted_strength; + real_t fractal_pinp_pong_strength; + + // Cellular specific. + CellularDistanceFunction cellular_distance_function; + CellularReturnType cellular_return_type; + real_t cellular_jitter; + + // Domain warp specific. + bool domain_warp_enabled; + DomainWarpType domain_warp_type; + real_t domain_warp_frequency; + real_t domain_warp_amplitude; + + DomainWarpFractalType domain_warp_fractal_type; + int domain_warp_fractal_octaves; + real_t domain_warp_fractal_lacunarity; + real_t domain_warp_fractal_gain; + +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_in_3d_space(bool p_enable); + bool is_in_3d_space() const; + + void set_offset(Vector3 p_offset); + Vector3 get_offset() const; + + void set_color_ramp(const Ref<Gradient> &p_gradient); + Ref<Gradient> get_color_ramp() 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. + + Ref<Image> get_image(int p_width, int p_height, bool p_invert = false) override; + Ref<Image> get_seamless_image(int p_width, int p_height, bool p_invert = false, real_t p_blend_skirt = 0.1) override; + + real_t get_noise_1d(real_t p_x) override; + + real_t get_noise_2dv(Vector2 p_v) override; + real_t get_noise_2d(real_t p_x, real_t p_y) override; + + real_t get_noise_3dv(Vector3 p_v) override; + real_t get_noise_3d(real_t p_x, real_t p_y, real_t p_z) override; + + void _changed(); +}; + +VARIANT_ENUM_CAST(FastNoiseLite::NoiseType); +VARIANT_ENUM_CAST(FastNoiseLite::FractalType); +VARIANT_ENUM_CAST(FastNoiseLite::CellularDistanceFunction); +VARIANT_ENUM_CAST(FastNoiseLite::CellularReturnType); +VARIANT_ENUM_CAST(FastNoiseLite::DomainWarpType); +VARIANT_ENUM_CAST(FastNoiseLite::DomainWarpFractalType); + +#endif // FASTNOISE_LITE_H diff --git a/modules/noise/icons/NoiseTexture.svg b/modules/noise/icons/NoiseTexture.svg new file mode 100644 index 0000000000..479684cde2 --- /dev/null +++ b/modules/noise/icons/NoiseTexture.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1c-.55228 0-1 .44772-1 1v12c0 .55228.44772 1 1 1h12c.55228 0 1-.44772 1-1v-12c0-.55228-.44772-1-1-1zm1 2h10v8h-10zm3 1v2h2v-2zm2 2v2h2v2h2v-6h-2v2zm0 2h-2v-2h-2v4h4z" fill="#e0e0e0" fill-opacity=".99608"/></svg> diff --git a/modules/noise/noise.cpp b/modules/noise/noise.cpp new file mode 100644 index 0000000000..430e8c87cf --- /dev/null +++ b/modules/noise/noise.cpp @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* noise.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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" + +Ref<Image> Noise::get_seamless_image(int p_width, int p_height, bool p_invert, real_t p_blend_skirt) { + int skirt_width = p_width * p_blend_skirt; + int skirt_height = p_height * p_blend_skirt; + int src_width = p_width + skirt_width; + int src_height = p_height + skirt_height; + + Ref<Image> src = get_image(src_width, src_height, p_invert); + bool grayscale = (src->get_format() == Image::FORMAT_L8); + if (grayscale) { + return _generate_seamless_image<uint8_t>(src, p_width, p_height, p_invert, p_blend_skirt); + } else { + return _generate_seamless_image<uint32_t>(src, p_width, p_height, p_invert, p_blend_skirt); + } +} + +// 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); +} + +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"), &Noise::get_image, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_seamless_image", "width", "height", "invert", "skirt"), &Noise::get_seamless_image, DEFVAL(false), DEFVAL(0.1)); +} diff --git a/modules/noise/noise.h b/modules/noise/noise.h new file mode 100644 index 0000000000..853c24b485 --- /dev/null +++ b/modules/noise/noise.h @@ -0,0 +1,240 @@ +/*************************************************************************/ +/* noise.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NOISE_H +#define NOISE_H + +#include "core/io/image.h" + +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> + Ref<Image> _generate_seamless_image(Ref<Image> p_src, int p_width, int p_height, bool p_invert, real_t p_blend_skirt) { + /* + 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, 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 * .5; + int half_height = p_height * .5; + int skirt_edge_x = half_width + skirt_width; + int skirt_edge_y = half_height + skirt_height; + + Vector<uint8_t> dest; + dest.resize(p_width * p_height * Image::get_format_pixel_size(p_src->get_format())); + + img_buff<T> rd_src = { + (T *)p_src->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(.1f, .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(.1f, .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(.1f, .9f, float(x - half_width) / float(skirt_width))); + int ypos = 255 * (1 - Math::smoothstep(.1f, .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, p_src->get_format(), dest)); + p_src.unref(); + return image; + } + + 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) = 0; + + virtual real_t get_noise_2dv(Vector2 p_v) = 0; + virtual real_t get_noise_2d(real_t p_x, real_t p_y) = 0; + + virtual real_t get_noise_3dv(Vector3 p_v) = 0; + virtual real_t get_noise_3d(real_t p_x, real_t p_y, real_t p_z) = 0; + + virtual Ref<Image> get_image(int p_width, int p_height, bool p_invert = false) = 0; + virtual Ref<Image> get_seamless_image(int p_width, int p_height, bool p_invert = false, real_t p_blend_skirt = 0.1); +}; + +#endif // NOISE_H diff --git a/modules/noise/noise_texture.cpp b/modules/noise/noise_texture.cpp new file mode 100644 index 0000000000..276335797a --- /dev/null +++ b/modules/noise/noise_texture.cpp @@ -0,0 +1,299 @@ +/*************************************************************************/ +/* noise_texture.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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.h" + +#include "core/core_string_names.h" +#include "noise.h" + +NoiseTexture::NoiseTexture() { + noise = Ref<Noise>(); + + _queue_update(); +} + +NoiseTexture::~NoiseTexture() { + if (texture.is_valid()) { + RS::get_singleton()->free(texture); + } + noise_thread.wait_to_finish(); +} + +void NoiseTexture::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_width", "width"), &NoiseTexture::set_width); + ClassDB::bind_method(D_METHOD("set_height", "height"), &NoiseTexture::set_height); + + ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture::set_noise); + ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture::get_noise); + + ClassDB::bind_method(D_METHOD("set_invert", "invert"), &NoiseTexture::set_invert); + ClassDB::bind_method(D_METHOD("get_invert"), &NoiseTexture::get_invert); + + ClassDB::bind_method(D_METHOD("set_seamless", "seamless"), &NoiseTexture::set_seamless); + ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture::get_seamless); + + ClassDB::bind_method(D_METHOD("set_seamless_blend_skirt", "seamless_blend_skirt"), &NoiseTexture::set_seamless_blend_skirt); + ClassDB::bind_method(D_METHOD("get_seamless_blend_skirt"), &NoiseTexture::get_seamless_blend_skirt); + + ClassDB::bind_method(D_METHOD("set_as_normal_map", "as_normal_map"), &NoiseTexture::set_as_normal_map); + ClassDB::bind_method(D_METHOD("is_normal_map"), &NoiseTexture::is_normal_map); + + ClassDB::bind_method(D_METHOD("set_bump_strength", "bump_strength"), &NoiseTexture::set_bump_strength); + ClassDB::bind_method(D_METHOD("get_bump_strength"), &NoiseTexture::get_bump_strength); + + ClassDB::bind_method(D_METHOD("_update_texture"), &NoiseTexture::_update_texture); + ClassDB::bind_method(D_METHOD("_generate_texture"), &NoiseTexture::_generate_texture); + ClassDB::bind_method(D_METHOD("_thread_done", "image"), &NoiseTexture::_thread_done); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert"), "set_invert", "get_invert"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "seamless_blend_skirt", PROPERTY_HINT_RANGE, "0.05,1,0.001"), "set_seamless_blend_skirt", "get_seamless_blend_skirt"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normal_map"), "set_as_normal_map", "is_normal_map"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bump_strength", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_bump_strength", "get_bump_strength"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "Noise"), "set_noise", "get_noise"); +} + +void NoiseTexture::_validate_property(PropertyInfo &property) const { + if (property.name == "bump_strength") { + if (!as_normal_map) { + property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL; + } + } + + if (property.name == "seamless_blend_skirt") { + if (!seamless) { + property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } +} + +void NoiseTexture::_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); + } + } + emit_changed(); +} + +void NoiseTexture::_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 NoiseTexture::_thread_function(void *p_ud) { + NoiseTexture *tex = static_cast<NoiseTexture *>(p_ud); + tex->call_deferred(SNAME("_thread_done"), tex->_generate_texture()); +} + +void NoiseTexture::_queue_update() { + if (update_queued) { + return; + } + + update_queued = true; + call_deferred(SNAME("_update_texture")); +} + +Ref<Image> NoiseTexture::_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> image; + + if (seamless) { + image = ref_noise->get_seamless_image(size.x, size.y, invert, seamless_blend_skirt); + } else { + image = ref_noise->get_image(size.x, size.y, invert); + } + + if (as_normal_map) { + image->bump_map_to_normal_map(bump_strength); + } + + return image; +} + +void NoiseTexture::_update_texture() { + bool use_thread = true; + if (first_time) { + use_thread = false; + first_time = false; + } +#ifdef NO_THREADS + use_thread = false; +#endif + if (use_thread) { + if (!noise_thread.is_started()) { + noise_thread.start(_thread_function, this); + regen_queued = false; + } else { + regen_queued = true; + } + + } else { + Ref<Image> image = _generate_texture(); + _set_texture_image(image); + } + update_queued = false; +} + +void NoiseTexture::set_noise(Ref<Noise> p_noise) { + if (p_noise == noise) { + return; + } + if (noise.is_valid()) { + noise->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture::_queue_update)); + } + noise = p_noise; + if (noise.is_valid()) { + noise->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &NoiseTexture::_queue_update)); + } + _queue_update(); +} + +Ref<Noise> NoiseTexture::get_noise() { + return noise; +} + +void NoiseTexture::set_width(int p_width) { + ERR_FAIL_COND(p_width <= 0); + if (p_width == size.x) { + return; + } + size.x = p_width; + _queue_update(); +} + +void NoiseTexture::set_height(int p_height) { + ERR_FAIL_COND(p_height <= 0); + if (p_height == size.y) { + return; + } + size.y = p_height; + _queue_update(); +} + +void NoiseTexture::set_invert(bool p_invert) { + if (p_invert == invert) { + return; + } + invert = p_invert; + _queue_update(); +} + +bool NoiseTexture::get_invert() const { + return invert; +} + +void NoiseTexture::set_seamless(bool p_seamless) { + if (p_seamless == seamless) { + return; + } + seamless = p_seamless; + _queue_update(); + notify_property_list_changed(); +} + +bool NoiseTexture::get_seamless() { + return seamless; +} + +void NoiseTexture::set_seamless_blend_skirt(real_t p_blend_skirt) { + if (p_blend_skirt == seamless_blend_skirt) { + return; + } + seamless_blend_skirt = p_blend_skirt; + _queue_update(); +} +real_t NoiseTexture::get_seamless_blend_skirt() { + return seamless_blend_skirt; +} + +void NoiseTexture::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 NoiseTexture::is_normal_map() { + return as_normal_map; +} + +void NoiseTexture::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 NoiseTexture::get_bump_strength() { + return bump_strength; +} + +int NoiseTexture::get_width() const { + return size.x; +} + +int NoiseTexture::get_height() const { + return size.y; +} + +RID NoiseTexture::get_rid() const { + if (!texture.is_valid()) { + texture = RS::get_singleton()->texture_2d_placeholder_create(); + } + + return texture; +} + +Ref<Image> NoiseTexture::get_image() const { + return image; +} diff --git a/modules/noise/noise_texture.h b/modules/noise/noise_texture.h new file mode 100644 index 0000000000..2a94df39d4 --- /dev/null +++ b/modules/noise/noise_texture.h @@ -0,0 +1,109 @@ +/*************************************************************************/ +/* noise_texture.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NOISE_TEXTURE_H +#define NOISE_TEXTURE_H + +#include "noise.h" + +#include "core/object/ref_counted.h" +#include "scene/resources/texture.h" + +class NoiseTexture : public Texture2D { + GDCLASS(NoiseTexture, 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; + + Ref<Noise> noise; + bool invert = false; + Vector2i size = Vector2i(512, 512); + Vector2 noise_offset; + bool seamless = false; + real_t seamless_blend_skirt = 0.1; + bool as_normal_map = false; + float bump_strength = 8.0; + + 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); + +protected: + static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const override; + +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_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(); + + 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; + + NoiseTexture(); + virtual ~NoiseTexture(); +}; + +#endif // NOISE_TEXTURE_H diff --git a/modules/noise/register_types.cpp b/modules/noise/register_types.cpp new file mode 100644 index 0000000000..81bb0317c1 --- /dev/null +++ b/modules/noise/register_types.cpp @@ -0,0 +1,44 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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.h" + +void register_noise_types() { + GDREGISTER_CLASS(NoiseTexture); + GDREGISTER_ABSTRACT_CLASS(Noise); + GDREGISTER_CLASS(FastNoiseLite); +} + +void unregister_noise_types() { +} diff --git a/modules/noise/register_types.h b/modules/noise/register_types.h new file mode 100644 index 0000000000..e441a48518 --- /dev/null +++ b/modules/noise/register_types.h @@ -0,0 +1,37 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NOISE_REGISTER_TYPES_H +#define NOISE_REGISTER_TYPES_H + +void register_noise_types(); +void unregister_noise_types(); + +#endif // NOISE_REGISTER_TYPES_H |