From 104887800c0f221fbcffa84bb360dd9ff001d7f1 Mon Sep 17 00:00:00 2001 From: Charlie Jolly Date: Fri, 15 Oct 2021 15:27:16 +0100 Subject: Geometry Nodes: Add Voronoi Texture Port shader Voronoi to GN Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D12725 --- release/scripts/startup/nodeitems_builtins.py | 1 + source/blender/blenlib/BLI_float2.hh | 25 + source/blender/blenlib/BLI_float3.hh | 15 + source/blender/blenlib/BLI_float4.hh | 52 ++ source/blender/blenlib/BLI_noise.hh | 94 +++ source/blender/blenlib/intern/math_base_inline.c | 16 + source/blender/blenlib/intern/math_vector_inline.c | 13 + source/blender/blenlib/intern/noise.cc | 890 ++++++++++++++++++++ .../modifiers/intern/MOD_nodes_evaluator.cc | 1 + .../nodes/shader/nodes/node_shader_tex_voronoi.cc | 911 ++++++++++++++++++++- 10 files changed, 2016 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 04e9fba02f2..26d586ac859 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -720,6 +720,7 @@ geometry_node_categories = [ GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ NodeItem("ShaderNodeTexGradient"), NodeItem("ShaderNodeTexNoise"), + NodeItem("ShaderNodeTexVoronoi"), NodeItem("ShaderNodeTexWhiteNoise"), ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ diff --git a/source/blender/blenlib/BLI_float2.hh b/source/blender/blenlib/BLI_float2.hh index cf6e00ba938..bb4229db86e 100644 --- a/source/blender/blenlib/BLI_float2.hh +++ b/source/blender/blenlib/BLI_float2.hh @@ -115,6 +115,11 @@ struct float2 { return {a.x - b.x, a.y - b.y}; } + friend float2 operator-(const float2 &a, const float &b) + { + return {a.x - b, a.y - b}; + } + friend float2 operator*(const float2 &a, float b) { return {a.x * b, a.y * b}; @@ -137,6 +142,26 @@ struct float2 { return stream; } + static float2 safe_divide(const float2 &a, const float b) + { + return (b != 0.0f) ? a / b : float2(0.0f); + } + + static float2 floor(const float2 &a) + { + return float2(floorf(a.x), floorf(a.y)); + } + + /** + * Returns a normalized vector. The original vector is not changed. + */ + float2 normalized() const + { + float2 result; + normalize_v2_v2(result, *this); + return result; + } + static float dot(const float2 &a, const float2 &b) { return a.x * b.x + a.y * b.y; diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index 04aae375889..8263ef72584 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -80,6 +80,11 @@ struct float3 { return {-a.x, -a.y, -a.z}; } + friend float3 operator-(const float3 &a, const float &b) + { + return {a.x - b, a.y - b, a.z - b}; + } + float3 &operator-=(const float3 &b) { this->x -= b.x; @@ -218,6 +223,16 @@ struct float3 { return result; } + static float3 safe_divide(const float3 &a, const float b) + { + return (b != 0.0f) ? a / b : float3(0.0f); + } + + static float3 floor(const float3 &a) + { + return float3(floorf(a.x), floorf(a.y), floorf(a.z)); + } + void invert() { x = -x; diff --git a/source/blender/blenlib/BLI_float4.hh b/source/blender/blenlib/BLI_float4.hh index b1feee3121b..5b487f6d029 100644 --- a/source/blender/blenlib/BLI_float4.hh +++ b/source/blender/blenlib/BLI_float4.hh @@ -44,6 +44,11 @@ struct float4 { return &x; } + friend float4 operator+(const float4 &a, const float &b) + { + return {a.x + b, a.y + b, a.z + b, a.w + b}; + } + operator const float *() const { return &x; @@ -58,11 +63,27 @@ struct float4 { return *this; } + friend float4 operator-(const float4 &a, const float4 &b) + { + return {a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w}; + } + + friend float4 operator-(const float4 &a, const float &b) + { + return {a.x - b, a.y - b, a.z - b, a.w - b}; + } + friend float4 operator+(const float4 &a, const float4 &b) { return {a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w}; } + friend float4 operator/(const float4 &a, float f) + { + BLI_assert(f != 0.0f); + return a * (1.0f / f); + } + float4 &operator*=(float factor) { x *= factor; @@ -81,6 +102,37 @@ struct float4 { { return b * a; } + + float length() const + { + return len_v4(*this); + } + + static float distance(const float4 &a, const float4 &b) + { + return (a - b).length(); + } + + static float4 safe_divide(const float4 &a, const float b) + { + return (b != 0.0f) ? a / b : float4(0.0f); + } + + static float4 interpolate(const float4 &a, const float4 &b, float t) + { + return a * (1 - t) + b * t; + } + + static float4 floor(const float4 &a) + { + return float4(floorf(a.x), floorf(a.y), floorf(a.z), floorf(a.w)); + } + + static float4 normalize(const float4 &a) + { + const float t = len_v4(a); + return (t != 0.0f) ? a / t : float4(0.0f); + } }; } // namespace blender diff --git a/source/blender/blenlib/BLI_noise.hh b/source/blender/blenlib/BLI_noise.hh index 12e7aa57ab0..93980e3569e 100644 --- a/source/blender/blenlib/BLI_noise.hh +++ b/source/blender/blenlib/BLI_noise.hh @@ -112,4 +112,98 @@ float3 perlin_float3_fractal_distorted(float4 position, /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Voronoi Noise + * \{ */ + +void voronoi_f1( + const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w); +void voronoi_smooth_f1(const float w, + const float smoothness, + const float randomness, + float *r_distance, + float3 *r_color, + float *r_w); +void voronoi_f2( + const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w); +void voronoi_distance_to_edge(const float w, const float randomness, float *r_distance); +void voronoi_n_sphere_radius(const float w, const float randomness, float *r_radius); + +void voronoi_f1(const float2 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position); +void voronoi_smooth_f1(const float2 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position); +void voronoi_f2(const float2 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position); +void voronoi_distance_to_edge(const float2 coord, const float randomness, float *r_distance); +void voronoi_n_sphere_radius(const float2 coord, const float randomness, float *r_radius); + +void voronoi_f1(const float3 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position); +void voronoi_smooth_f1(const float3 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position); +void voronoi_f2(const float3 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position); +void voronoi_distance_to_edge(const float3 coord, const float randomness, float *r_distance); +void voronoi_n_sphere_radius(const float3 coord, const float randomness, float *r_radius); + +void voronoi_f1(const float4 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position); +void voronoi_smooth_f1(const float4 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position); +void voronoi_f2(const float4 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position); +void voronoi_distance_to_edge(const float4 coord, const float randomness, float *r_distance); +void voronoi_n_sphere_radius(const float4 coord, const float randomness, float *r_radius); + +/** \} */ + } // namespace blender::noise diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index 49f9faf1704..f609d5f8e8b 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -511,6 +511,22 @@ MINLINE float smoothminf(float a, float b, float c) } } +MINLINE float smoothstep(float edge0, float edge1, float x) +{ + float result; + if (x < edge0) { + result = 0.0f; + } + else if (x >= edge1) { + result = 1.0f; + } + else { + float t = (x - edge0) / (edge1 - edge0); + result = (3.0f - 2.0f * t) * (t * t); + } + return result; +} + MINLINE double min_dd(double a, double b) { return (a < b) ? a : b; diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index 8be066bb0e9..bb32b511005 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -1145,6 +1145,19 @@ MINLINE float len_v3v3(const float a[3], const float b[3]) return len_v3(d); } +MINLINE float len_v4(const float a[4]) +{ + return sqrtf(dot_v4v4(a, a)); +} + +MINLINE float len_v4v4(const float a[4], const float b[4]) +{ + float d[4]; + + sub_v4_v4v4(d, b, a); + return len_v4(d); +} + /** * \note any vectors containing `nan` will be zeroed out. */ diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index ce2e9594059..a6c3377b71f 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -52,6 +52,7 @@ #include "BLI_float2.hh" #include "BLI_float3.hh" #include "BLI_float4.hh" +#include "BLI_math_base_safe.h" #include "BLI_noise.hh" #include "BLI_utildefines.h" @@ -755,4 +756,893 @@ float3 perlin_float3_fractal_distorted(float4 position, perlin_fractal(position + random_float4_offset(5.0f), octaves, roughness)); } +/* + * Voronoi: Ported from Cycles code. + * + * Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez. + * + * Smooth Voronoi: + * + * - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi + * + * Distance To Edge based on: + * + * - https://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm + * - https://www.shadertoy.com/view/ldl3W8 + * + * With optimization to change -2..2 scan window to -1..1 for better performance, + * as explained in https://www.shadertoy.com/view/llG3zy. + */ + +/* **** 1D Voronoi **** */ + +/* Ensure to align with DNA. */ +enum { + NOISE_SHD_VORONOI_EUCLIDEAN = 0, + NOISE_SHD_VORONOI_MANHATTAN = 1, + NOISE_SHD_VORONOI_CHEBYCHEV = 2, + NOISE_SHD_VORONOI_MINKOWSKI = 3, +}; + +BLI_INLINE float voronoi_distance(const float a, const float b) +{ + return fabsf(b - a); +} + +void voronoi_f1( + const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + float minDistance = 8.0f; + float targetOffset = 0.0f; + float targetPosition = 0.0f; + for (int i = -1; i <= 1; i++) { + const float cellOffset = i; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance(pointPosition, localPosition); + if (distanceToPoint < minDistance) { + targetOffset = cellOffset; + minDistance = distanceToPoint; + targetPosition = pointPosition; + } + } + *r_distance = minDistance; + *r_color = hash_float_to_float3(cellPosition + targetOffset); + *r_w = targetPosition + cellPosition; +} + +void voronoi_smooth_f1(const float w, + const float smoothness, + const float randomness, + float *r_distance, + float3 *r_color, + float *r_w) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + float smoothDistance = 8.0f; + float smoothPosition = 0.0f; + float3 smoothColor = float3(0.0f, 0.0f, 0.0f); + for (int i = -2; i <= 2; i++) { + const float cellOffset = i; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance(pointPosition, localPosition); + const float h = smoothstep( + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); + float correctionFactor = smoothness * h * (1.0f - h); + smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; + correctionFactor /= 1.0f + 3.0f * smoothness; + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor; + } + *r_distance = smoothDistance; + *r_color = smoothColor; + *r_w = cellPosition + smoothPosition; +} + +void voronoi_f2( + const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + float distanceF1 = 8.0f; + float distanceF2 = 8.0f; + float offsetF1 = 0.0f; + float positionF1 = 0.0f; + float offsetF2 = 0.0f; + float positionF2 = 0.0f; + for (int i = -1; i <= 1; i++) { + const float cellOffset = i; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance(pointPosition, localPosition); + if (distanceToPoint < distanceF1) { + distanceF2 = distanceF1; + distanceF1 = distanceToPoint; + offsetF2 = offsetF1; + offsetF1 = cellOffset; + positionF2 = positionF1; + positionF1 = pointPosition; + } + else if (distanceToPoint < distanceF2) { + distanceF2 = distanceToPoint; + offsetF2 = cellOffset; + positionF2 = pointPosition; + } + } + *r_distance = distanceF2; + *r_color = hash_float_to_float3(cellPosition + offsetF2); + *r_w = positionF2 + cellPosition; +} + +void voronoi_distance_to_edge(const float w, const float randomness, float *r_distance) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + const float midPointPosition = hash_float_to_float(cellPosition) * randomness; + const float leftPointPosition = -1.0f + hash_float_to_float(cellPosition - 1.0f) * randomness; + const float rightPointPosition = 1.0f + hash_float_to_float(cellPosition + 1.0f) * randomness; + const float distanceToMidLeft = fabsf((midPointPosition + leftPointPosition) / 2.0f - + localPosition); + const float distanceToMidRight = fabsf((midPointPosition + rightPointPosition) / 2.0f - + localPosition); + + *r_distance = std::min(distanceToMidLeft, distanceToMidRight); +} + +void voronoi_n_sphere_radius(const float w, const float randomness, float *r_radius) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + float closestPoint = 0.0f; + float closestPointOffset = 0.0f; + float minDistance = 8.0f; + for (int i = -1; i <= 1; i++) { + const float cellOffset = i; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = fabsf(pointPosition - localPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPoint = pointPosition; + closestPointOffset = cellOffset; + } + } + + minDistance = 8.0f; + float closestPointToClosestPoint = 0.0f; + for (int i = -1; i <= 1; i++) { + if (i == 0) { + continue; + } + const float cellOffset = i + closestPointOffset; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = fabsf(closestPoint - pointPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPointToClosestPoint = pointPosition; + } + } + *r_radius = fabsf(closestPointToClosestPoint - closestPoint) / 2.0f; +} + +/* **** 2D Voronoi **** */ + +static float voronoi_distance(const float2 a, + const float2 b, + const int metric, + const float exponent) +{ + switch (metric) { + case NOISE_SHD_VORONOI_EUCLIDEAN: + return float2::distance(a, b); + case NOISE_SHD_VORONOI_MANHATTAN: + return fabsf(a.x - b.x) + fabsf(a.y - b.y); + case NOISE_SHD_VORONOI_CHEBYCHEV: + return std::max(fabsf(a.x - b.x), fabsf(a.y - b.y)); + case NOISE_SHD_VORONOI_MINKOWSKI: + return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent), + 1.0f / exponent); + default: + BLI_assert_unreachable(); + break; + } + return 0.0f; +} + +void voronoi_f1(const float2 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float minDistance = 8.0f; + float2 targetOffset = float2(0.0f, 0.0f); + float2 targetPosition = float2(0.0f, 0.0f); + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent); + if (distanceToPoint < minDistance) { + targetOffset = cellOffset; + minDistance = distanceToPoint; + targetPosition = pointPosition; + } + } + } + *r_distance = minDistance; + *r_color = hash_float_to_float3(cellPosition + targetOffset); + *r_position = targetPosition + cellPosition; +} + +void voronoi_smooth_f1(const float2 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float smoothDistance = 8.0f; + float3 smoothColor = float3(0.0f, 0.0f, 0.0f); + float2 smoothPosition = float2(0.0f, 0.0f); + for (int j = -2; j <= 2; j++) { + for (int i = -2; i <= 2; i++) { + const float2 cellOffset = float2(i, j); + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + const float h = smoothstep( + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); + float correctionFactor = smoothness * h * (1.0f - h); + smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; + correctionFactor /= 1.0f + 3.0f * smoothness; + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + smoothPosition = float2::interpolate(smoothPosition, pointPosition, h) - correctionFactor; + } + } + *r_distance = smoothDistance; + *r_color = smoothColor; + *r_position = cellPosition + smoothPosition; +} + +void voronoi_f2(const float2 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float distanceF1 = 8.0f; + float distanceF2 = 8.0f; + float2 offsetF1 = float2(0.0f, 0.0f); + float2 positionF1 = float2(0.0f, 0.0f); + float2 offsetF2 = float2(0.0f, 0.0f); + float2 positionF2 = float2(0.0f, 0.0f); + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < distanceF1) { + distanceF2 = distanceF1; + distanceF1 = distanceToPoint; + offsetF2 = offsetF1; + offsetF1 = cellOffset; + positionF2 = positionF1; + positionF1 = pointPosition; + } + else if (distanceToPoint < distanceF2) { + distanceF2 = distanceToPoint; + offsetF2 = cellOffset; + positionF2 = pointPosition; + } + } + } + *r_distance = distanceF2; + *r_color = hash_float_to_float3(cellPosition + offsetF2); + *r_position = positionF2 + cellPosition; +} + +void voronoi_distance_to_edge(const float2 coord, const float randomness, float *r_distance) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float2 vectorToClosest = float2(0.0f, 0.0f); + float minDistance = 8.0f; + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 vectorToPoint = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness - + localPosition; + const float distanceToPoint = dot_v2v2(vectorToPoint, vectorToPoint); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + vectorToClosest = vectorToPoint; + } + } + } + + minDistance = 8.0f; + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 vectorToPoint = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness - + localPosition; + const float2 perpendicularToEdge = vectorToPoint - vectorToClosest; + if (dot_v2v2(perpendicularToEdge, perpendicularToEdge) > 0.0001f) { + const float distanceToEdge = dot_v2v2((vectorToClosest + vectorToPoint) / 2.0f, + perpendicularToEdge.normalized()); + minDistance = std::min(minDistance, distanceToEdge); + } + } + } + *r_distance = minDistance; +} + +void voronoi_n_sphere_radius(const float2 coord, const float randomness, float *r_radius) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float2 closestPoint = float2(0.0f, 0.0f); + float2 closestPointOffset = float2(0.0f, 0.0f); + float minDistance = 8.0f; + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + const float distanceToPoint = float2::distance(pointPosition, localPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPoint = pointPosition; + closestPointOffset = cellOffset; + } + } + } + + minDistance = 8.0f; + float2 closestPointToClosestPoint = float2(0.0f, 0.0f); + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + if (i == 0 && j == 0) { + continue; + } + const float2 cellOffset = float2(i, j) + closestPointOffset; + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + const float distanceToPoint = float2::distance(closestPoint, pointPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPointToClosestPoint = pointPosition; + } + } + } + *r_radius = float2::distance(closestPointToClosestPoint, closestPoint) / 2.0f; +} + +/* **** 3D Voronoi **** */ + +static float voronoi_distance(const float3 a, + const float3 b, + const int metric, + const float exponent) +{ + switch (metric) { + case NOISE_SHD_VORONOI_EUCLIDEAN: + return float3::distance(a, b); + case NOISE_SHD_VORONOI_MANHATTAN: + return fabsf(a.x - b.x) + fabsf(a.y - b.y) + fabsf(a.z - b.z); + case NOISE_SHD_VORONOI_CHEBYCHEV: + return std::max(fabsf(a.x - b.x), std::max(fabsf(a.y - b.y), fabsf(a.z - b.z))); + case NOISE_SHD_VORONOI_MINKOWSKI: + return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent) + + powf(fabsf(a.z - b.z), exponent), + 1.0f / exponent); + default: + BLI_assert_unreachable(); + break; + } + return 0.0f; +} + +void voronoi_f1(const float3 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float minDistance = 8.0f; + float3 targetOffset = float3(0.0f, 0.0f, 0.0f); + float3 targetPosition = float3(0.0f, 0.0f, 0.0f); + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < minDistance) { + targetOffset = cellOffset; + minDistance = distanceToPoint; + targetPosition = pointPosition; + } + } + } + } + *r_distance = minDistance; + *r_color = hash_float_to_float3(cellPosition + targetOffset); + *r_position = targetPosition + cellPosition; +} + +void voronoi_smooth_f1(const float3 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float smoothDistance = 8.0f; + float3 smoothColor = float3(0.0f, 0.0f, 0.0f); + float3 smoothPosition = float3(0.0f, 0.0f, 0.0f); + for (int k = -2; k <= 2; k++) { + for (int j = -2; j <= 2; j++) { + for (int i = -2; i <= 2; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + const float h = smoothstep( + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); + float correctionFactor = smoothness * h * (1.0f - h); + smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; + correctionFactor /= 1.0f + 3.0f * smoothness; + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + smoothPosition = float3::interpolate(smoothPosition, pointPosition, h) - correctionFactor; + } + } + } + *r_distance = smoothDistance; + *r_color = smoothColor; + *r_position = cellPosition + smoothPosition; +} + +void voronoi_f2(const float3 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float distanceF1 = 8.0f; + float distanceF2 = 8.0f; + float3 offsetF1 = float3(0.0f, 0.0f, 0.0f); + float3 positionF1 = float3(0.0f, 0.0f, 0.0f); + float3 offsetF2 = float3(0.0f, 0.0f, 0.0f); + float3 positionF2 = float3(0.0f, 0.0f, 0.0f); + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < distanceF1) { + distanceF2 = distanceF1; + distanceF1 = distanceToPoint; + offsetF2 = offsetF1; + offsetF1 = cellOffset; + positionF2 = positionF1; + positionF1 = pointPosition; + } + else if (distanceToPoint < distanceF2) { + distanceF2 = distanceToPoint; + offsetF2 = cellOffset; + positionF2 = pointPosition; + } + } + } + } + *r_distance = distanceF2; + *r_color = hash_float_to_float3(cellPosition + offsetF2); + *r_position = positionF2 + cellPosition; +} + +void voronoi_distance_to_edge(const float3 coord, const float randomness, float *r_distance) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float3 vectorToClosest = float3(0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 vectorToPoint = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness - + localPosition; + const float distanceToPoint = dot_v3v3(vectorToPoint, vectorToPoint); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + vectorToClosest = vectorToPoint; + } + } + } + } + + minDistance = 8.0f; + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 vectorToPoint = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness - + localPosition; + const float3 perpendicularToEdge = vectorToPoint - vectorToClosest; + if (dot_v3v3(perpendicularToEdge, perpendicularToEdge) > 0.0001f) { + const float distanceToEdge = dot_v3v3((vectorToClosest + vectorToPoint) / 2.0f, + perpendicularToEdge.normalized()); + minDistance = std::min(minDistance, distanceToEdge); + } + } + } + } + *r_distance = minDistance; +} + +void voronoi_n_sphere_radius(const float3 coord, const float randomness, float *r_radius) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float3 closestPoint = float3(0.0f, 0.0f, 0.0f); + float3 closestPointOffset = float3(0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = float3::distance(pointPosition, localPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPoint = pointPosition; + closestPointOffset = cellOffset; + } + } + } + } + + minDistance = 8.0f; + float3 closestPointToClosestPoint = float3(0.0f, 0.0f, 0.0f); + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + if (i == 0 && j == 0 && k == 0) { + continue; + } + const float3 cellOffset = float3(i, j, k) + closestPointOffset; + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = float3::distance(closestPoint, pointPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPointToClosestPoint = pointPosition; + } + } + } + } + *r_radius = float3::distance(closestPointToClosestPoint, closestPoint) / 2.0f; +} + +/* **** 4D Voronoi **** */ + +static float voronoi_distance(const float4 a, + const float4 b, + const int metric, + const float exponent) +{ + switch (metric) { + case NOISE_SHD_VORONOI_EUCLIDEAN: + return float4::distance(a, b); + case NOISE_SHD_VORONOI_MANHATTAN: + return fabsf(a.x - b.x) + fabsf(a.y - b.y) + fabsf(a.z - b.z) + fabsf(a.w - b.w); + case NOISE_SHD_VORONOI_CHEBYCHEV: + return std::max(fabsf(a.x - b.x), + std::max(fabsf(a.y - b.y), std::max(fabsf(a.z - b.z), fabsf(a.w - b.w)))); + case NOISE_SHD_VORONOI_MINKOWSKI: + return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent) + + powf(fabsf(a.z - b.z), exponent) + powf(fabsf(a.w - b.w), exponent), + 1.0f / exponent); + default: + BLI_assert_unreachable(); + break; + } + return 0.0f; +} + +void voronoi_f1(const float4 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float minDistance = 8.0f; + float4 targetOffset = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 targetPosition = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < minDistance) { + targetOffset = cellOffset; + minDistance = distanceToPoint; + targetPosition = pointPosition; + } + } + } + } + } + *r_distance = minDistance; + *r_color = hash_float_to_float3(cellPosition + targetOffset); + *r_position = targetPosition + cellPosition; +} + +void voronoi_smooth_f1(const float4 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float smoothDistance = 8.0f; + float3 smoothColor = float3(0.0f, 0.0f, 0.0f); + float4 smoothPosition = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -2; u <= 2; u++) { + for (int k = -2; k <= 2; k++) { + for (int j = -2; j <= 2; j++) { + for (int i = -2; i <= 2; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + const float h = smoothstep( + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness); + float correctionFactor = smoothness * h * (1.0f - h); + smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; + correctionFactor /= 1.0f + 3.0f * smoothness; + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + smoothPosition = float4::interpolate(smoothPosition, pointPosition, h) - + correctionFactor; + } + } + } + } + *r_distance = smoothDistance; + *r_color = smoothColor; + *r_position = cellPosition + smoothPosition; +} + +void voronoi_f2(const float4 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float distanceF1 = 8.0f; + float distanceF2 = 8.0f; + float4 offsetF1 = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 positionF1 = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 offsetF2 = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 positionF2 = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < distanceF1) { + distanceF2 = distanceF1; + distanceF1 = distanceToPoint; + offsetF2 = offsetF1; + offsetF1 = cellOffset; + positionF2 = positionF1; + positionF1 = pointPosition; + } + else if (distanceToPoint < distanceF2) { + distanceF2 = distanceToPoint; + offsetF2 = cellOffset; + positionF2 = pointPosition; + } + } + } + } + } + *r_distance = distanceF2; + *r_color = hash_float_to_float3(cellPosition + offsetF2); + *r_position = positionF2 + cellPosition; +} + +void voronoi_distance_to_edge(const float4 coord, const float randomness, float *r_distance) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float4 vectorToClosest = float4(0.0f, 0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 vectorToPoint = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness - + localPosition; + const float distanceToPoint = dot_v4v4(vectorToPoint, vectorToPoint); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + vectorToClosest = vectorToPoint; + } + } + } + } + } + + minDistance = 8.0f; + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 vectorToPoint = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness - + localPosition; + const float4 perpendicularToEdge = vectorToPoint - vectorToClosest; + if (dot_v4v4(perpendicularToEdge, perpendicularToEdge) > 0.0001f) { + const float distanceToEdge = dot_v4v4((vectorToClosest + vectorToPoint) / 2.0f, + float4::normalize(perpendicularToEdge)); + minDistance = std::min(minDistance, distanceToEdge); + } + } + } + } + } + *r_distance = minDistance; +} + +void voronoi_n_sphere_radius(const float4 coord, const float randomness, float *r_radius) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float4 closestPoint = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 closestPointOffset = float4(0.0f, 0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = float4::distance(pointPosition, localPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPoint = pointPosition; + closestPointOffset = cellOffset; + } + } + } + } + } + + minDistance = 8.0f; + float4 closestPointToClosestPoint = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + if (i == 0 && j == 0 && k == 0 && u == 0) { + continue; + } + const float4 cellOffset = float4(i, j, k, u) + closestPointOffset; + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = float4::distance(closestPoint, pointPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPointToClosestPoint = pointPosition; + } + } + } + } + } + *r_radius = float4::distance(closestPointToClosestPoint, closestPoint) / 2.0f; +} + } // namespace blender::noise diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index e0803e546b3..3c51ef83311 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -333,6 +333,7 @@ static void get_socket_value(const SocketRef &socket, void *r_value) GEO_NODE_SET_POSITION, SH_NODE_TEX_GRADIENT, SH_NODE_TEX_NOISE, + SH_NODE_TEX_VORONOI, SH_NODE_TEX_WHITE_NOISE, GEO_NODE_MESH_TO_POINTS, GEO_NODE_PROXIMITY)) { diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index e12e5724e8e..07904123ace 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -19,12 +19,14 @@ #include "../node_shader_util.h" +#include "BLI_noise.hh" + namespace blender::nodes { static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b) { b.is_function_node(); - b.add_input("Vector").hide_value(); + b.add_input("Vector").hide_value().implicit_field(); b.add_input("W").min(-1000.0f).max(1000.0f); b.add_input("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f); b.add_input("Smoothness") @@ -143,6 +145,7 @@ static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node tex->distance == SHD_VORONOI_MINKOWSKI && tex->dimensions != 1 && !ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS)); nodeSetSocketAvailability(inSmoothnessSock, tex->feature == SHD_VORONOI_SMOOTH_F1); + nodeSetSocketAvailability(outDistanceSock, tex->feature != SHD_VORONOI_N_SPHERE_RADIUS); nodeSetSocketAvailability(outColorSock, tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && @@ -158,17 +161,921 @@ static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node nodeSetSocketAvailability(outRadiusSock, tex->feature == SHD_VORONOI_N_SPHERE_RADIUS); } +namespace blender::nodes { + +class VoronoiMinowskiFunction : public fn::MultiFunction { + private: + int dimensions_; + int feature_; + + public: + VoronoiMinowskiFunction(int dimensions, int feature) : dimensions_(dimensions), feature_(feature) + { + BLI_assert(dimensions >= 2 && dimensions <= 4); + BLI_assert(feature >= 0 && feature <= 2); + static std::array signatures{ + create_signature(2, SHD_VORONOI_F1), + create_signature(3, SHD_VORONOI_F1), + create_signature(4, SHD_VORONOI_F1), + + create_signature(2, SHD_VORONOI_F2), + create_signature(3, SHD_VORONOI_F2), + create_signature(4, SHD_VORONOI_F2), + + create_signature(2, SHD_VORONOI_SMOOTH_F1), + create_signature(3, SHD_VORONOI_SMOOTH_F1), + create_signature(4, SHD_VORONOI_SMOOTH_F1), + }; + this->set_signature(&signatures[(dimensions - 1) + feature * 3 - 1]); + } + + static fn::MFSignature create_signature(int dimensions, int feature) + { + fn::MFSignatureBuilder signature{"voronoi_minowski"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + signature.single_input("Scale"); + if (feature == SHD_VORONOI_SMOOTH_F1) { + signature.single_input("Smoothness"); + } + signature.single_input("Exponent"); + signature.single_input("Randomness"); + signature.single_output("Distance"); + signature.single_output("Color"); + + if (dimensions != 1) { + signature.single_output("Position"); + } + if ((dimensions == 1 || dimensions == 4)) { + signature.single_output("W"); + } + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Scale"); + }; + auto get_smoothness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Smoothness"); + }; + auto get_exponent = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Exponent"); + }; + auto get_randomness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Randomness"); + }; + auto get_r_distance = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Distance"); + }; + auto get_r_color = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Color"); + }; + auto get_r_position = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Position"); + }; + auto get_r_w = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "W"); + }; + + int param = 0; + switch (dimensions_) { + case 2: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f1(float2(vector[i].x, vector[i].y) * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + &r_distance[i], + &col, + &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f2(float2(vector[i].x, vector[i].y) * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + &r_distance[i], + &col, + &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_smooth_f1(float2(vector[i].x, vector[i].y) * scale[i], + smth, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + &r_distance[i], + &col, + &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + break; + } + } + break; + } + case 3: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f1(vector[i] * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + &r_distance[i], + &col, + &r_position[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f2(vector[i] * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + &r_distance[i], + &col, + &r_position[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(vector[i] * scale[i], + smth, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + &r_distance[i], + &col, + &r_position[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + break; + } + } + break; + } + case 4: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f1(p, exponent[i], rand, SHD_VORONOI_F1, &r_distance[i], &col, &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float4::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, pos.z); + r_w[i] = pos.w; + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f2( + p, exponent[i], rand, SHD_VORONOI_MINKOWSKI, &r_distance[i], &col, &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float4::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, pos.z); + r_w[i] = pos.w; + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_smooth_f1( + p, smth, exponent[i], rand, SHD_VORONOI_MINKOWSKI, &r_distance[i], &col, &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float4::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, pos.z); + r_w[i] = pos.w; + } + break; + } + } + break; + } + } + } +}; + +class VoronoiMetricFunction : public fn::MultiFunction { + private: + int dimensions_; + int feature_; + int metric_; + + public: + VoronoiMetricFunction(int dimensions, int feature, int metric) + : dimensions_(dimensions), feature_(feature), metric_(metric) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + BLI_assert(feature >= 0 && feature <= 4); + static std::array signatures{ + create_signature(1, SHD_VORONOI_F1), + create_signature(2, SHD_VORONOI_F1), + create_signature(3, SHD_VORONOI_F1), + create_signature(4, SHD_VORONOI_F1), + + create_signature(1, SHD_VORONOI_F2), + create_signature(2, SHD_VORONOI_F2), + create_signature(3, SHD_VORONOI_F2), + create_signature(4, SHD_VORONOI_F2), + + create_signature(1, SHD_VORONOI_SMOOTH_F1), + create_signature(2, SHD_VORONOI_SMOOTH_F1), + create_signature(3, SHD_VORONOI_SMOOTH_F1), + create_signature(4, SHD_VORONOI_SMOOTH_F1), + }; + this->set_signature(&signatures[dimensions + feature * 4 - 1]); + } + + static fn::MFSignature create_signature(int dimensions, int feature) + { + fn::MFSignatureBuilder signature{"voronoi_metric"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + signature.single_input("Scale"); + if (feature == SHD_VORONOI_SMOOTH_F1) { + signature.single_input("Smoothness"); + } + signature.single_input("Randomness"); + signature.single_output("Distance"); + signature.single_output("Color"); + + if (dimensions != 1) { + signature.single_output("Position"); + } + if ((dimensions == 1 || dimensions == 4)) { + signature.single_output("W"); + } + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Scale"); + }; + auto get_smoothness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Smoothness"); + }; + auto get_randomness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Randomness"); + }; + auto get_r_distance = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Distance"); + }; + auto get_r_color = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Color"); + }; + auto get_r_position = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Position"); + }; + auto get_r_w = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "W"); + }; + + int param = 0; + switch (dimensions_) { + case 1: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float p = w[i] * scale[i]; + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f1(p, rand, &r_distance[i], &col, &r_w[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_w[i] = safe_divide(r_w[i], scale[i]); + } + break; + } + case SHD_VORONOI_F2: { + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float p = w[i] * scale[i]; + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f2(p, rand, &r_distance[i], &col, &r_w[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_w[i] = safe_divide(r_w[i], scale[i]); + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float p = w[i] * scale[i]; + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(p, smth, rand, &r_distance[i], &col, &r_w[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_w[i] = safe_divide(r_w[i], scale[i]); + } + break; + } + } + break; + } + case 2: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f1(float2(vector[i].x, vector[i].y) * scale[i], + 0.0f, + rand, + metric_, + &r_distance[i], + &col, + &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f2(float2(vector[i].x, vector[i].y) * scale[i], + 0.0f, + rand, + metric_, + &r_distance[i], + &col, + &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_smooth_f1(float2(vector[i].x, vector[i].y) * scale[i], + smth, + 0.0f, + rand, + metric_, + &r_distance[i], + &col, + &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + break; + } + } + break; + } + case 3: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f1( + vector[i] * scale[i], 0.0f, rand, metric_, &r_distance[i], &col, &r_position[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f2( + vector[i] * scale[i], 0.0f, rand, metric_, &r_distance[i], &col, &r_position[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(vector[i] * scale[i], + smth, + 0.0f, + rand, + metric_, + &r_distance[i], + &col, + &r_position[i]); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + break; + } + } + break; + } + case 4: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f1(p, 0.0f, rand, metric_, &r_distance[i], &col, &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float4::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, pos.z); + r_w[i] = pos.w; + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f2(p, 0.0f, rand, metric_, &r_distance[i], &col, &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float4::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, pos.z); + r_w[i] = pos.w; + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_smooth_f1(p, smth, 0.0f, rand, metric_, &r_distance[i], &col, &pos); + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + pos = float4::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, pos.z); + r_w[i] = pos.w; + } + break; + } + } + break; + } + } + } +}; + +class VoronoiEdgeFunction : public fn::MultiFunction { + private: + int dimensions_; + int feature_; + + public: + VoronoiEdgeFunction(int dimensions, int feature) : dimensions_(dimensions), feature_(feature) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + BLI_assert(feature >= 3 && feature <= 4); + static std::array signatures{ + create_signature(1, SHD_VORONOI_DISTANCE_TO_EDGE), + create_signature(2, SHD_VORONOI_DISTANCE_TO_EDGE), + create_signature(3, SHD_VORONOI_DISTANCE_TO_EDGE), + create_signature(4, SHD_VORONOI_DISTANCE_TO_EDGE), + + create_signature(1, SHD_VORONOI_N_SPHERE_RADIUS), + create_signature(2, SHD_VORONOI_N_SPHERE_RADIUS), + create_signature(3, SHD_VORONOI_N_SPHERE_RADIUS), + create_signature(4, SHD_VORONOI_N_SPHERE_RADIUS), + }; + this->set_signature(&signatures[dimensions + (feature - 3) * 4 - 1]); + } + + static fn::MFSignature create_signature(int dimensions, int feature) + { + fn::MFSignatureBuilder signature{"voronoi_edge"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + signature.single_input("Scale"); + signature.single_input("Randomness"); + + if (feature == SHD_VORONOI_DISTANCE_TO_EDGE) { + signature.single_output("Distance"); + } + if (feature == SHD_VORONOI_N_SPHERE_RADIUS) { + signature.single_output("Radius"); + } + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Scale"); + }; + auto get_randomness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Randomness"); + }; + auto get_r_distance = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Distance"); + }; + auto get_r_radius = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Radius"); + }; + + int param = 0; + switch (dimensions_) { + case 1: { + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float p = w[i] * scale[i]; + noise::voronoi_distance_to_edge(p, rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float p = w[i] * scale[i]; + noise::voronoi_n_sphere_radius(p, rand, &r_radius[i]); + } + break; + } + } + break; + } + case 2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float2 p = float2(vector[i].x, vector[i].y) * scale[i]; + noise::voronoi_distance_to_edge(p, rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float2 p = float2(vector[i].x, vector[i].y) * scale[i]; + noise::voronoi_n_sphere_radius(p, rand, &r_radius[i]); + } + break; + } + } + break; + } + case 3: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + noise::voronoi_distance_to_edge(vector[i] * scale[i], rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + noise::voronoi_n_sphere_radius(vector[i] * scale[i], rand, &r_radius[i]); + } + break; + } + } + break; + } + case 4: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + noise::voronoi_distance_to_edge(p, rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + noise::voronoi_n_sphere_radius(p, rand, &r_radius[i]); + } + break; + } + } + break; + } + } + }; +}; + +static void sh_node_voronoi_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexVoronoi *tex = (NodeTexVoronoi *)node.storage; + bool minowski = (tex->distance == SHD_VORONOI_MINKOWSKI && tex->dimensions != 1 && + !ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS)); + bool dist_radius = (tex->feature == SHD_VORONOI_DISTANCE_TO_EDGE || + tex->feature == SHD_VORONOI_N_SPHERE_RADIUS); + if (dist_radius) { + builder.construct_and_set_matching_fn(tex->dimensions, tex->feature); + } + else if (minowski) { + builder.construct_and_set_matching_fn(tex->dimensions, tex->feature); + } + else { + builder.construct_and_set_matching_fn( + tex->dimensions, tex->feature, tex->distance); + } +} + +} // namespace blender::nodes + void register_node_type_sh_tex_voronoi(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0); ntype.declare = blender::nodes::sh_node_tex_voronoi_declare; node_type_init(&ntype, node_shader_init_tex_voronoi); node_type_storage( &ntype, "NodeTexVoronoi", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_voronoi); node_type_update(&ntype, node_shader_update_tex_voronoi); + ntype.build_multi_function = blender::nodes::sh_node_voronoi_build_multi_function; nodeRegisterType(&ntype); } -- cgit v1.2.3