// ============================================================================
// BLENDER'S VORONOI -- ONE TEXT, COMPILED AS BOTH GLSL AND C++.
//
// Ported from `source/blender/blenlib/intern/noise.cc` (the Voronoi section)
// in the Blender tree at commit 61a36e08, BLENDER_VERSION 502, and kept
// structurally identical to it so the two diff by eye when Blender moves.
//
// DEPENDS ON blender_hash.inc and does not include it -- GLSL has no
// #include, so the emitter concatenates hash-then-voronoi and the C++ shim
// includes them in that order.
//
// ⛔ VORONOI USES THE PCG INTEGER HASHES, NOT THE JENKINS FLOAT HASH -- except
// in 1D, which uses hash_float_to_float. That is read off Blender's source,
// not assumed: one hash per file would be the right shape with unrelated
// numbers, which is the failure this whole layer exists to prevent.
//
// This file was TRANSFORMED from blender_voronoi.h, not retyped. 1118 lines of
// working, gated arithmetic is exactly the wrong thing to copy by hand, and
// the equivalence run against the pre-conversion header is what says the
// transformation was faithful.
//
// ---------------------------------------------------------------------------
// THE SUBSET
//
// NO TEMPLATES. `fractal_voronoi_x_fx` and `fractal_voronoi_distance_to_edge`
// are template <typename T>; the body is written once and the preprocessor
// stamps it out per dimension, as blender_perlin.inc does.
// `voronoi_distance_bound` was a template with a Vector4 specialisation and
// is three overloads here -- GLSL resolves on argument type just as well.
//
// NO DEFAULT MEMBER INITIALISERS. GLSL structs cannot have them, so
// BvParams is filled by the caller. Every field, every time: a struct that
// used to default `randomness = 1.0f` and now defaults to garbage is a
// silent difference, which is why the adapter sets all eleven.
//
// NO godot METHODS. `(a - b).length_squared()` became bv_vlen_sq_v.
// ============================================================================
#ifndef PN_BLENDER_VORONOI
#define PN_BLENDER_VORONOI
#ifndef BN_FN
#define BN_FN
#endif
const int BV_EUCLIDEAN = 0;
const int BV_MANHATTAN = 1;
const int BV_CHEBYCHEV = 2;
const int BV_MINKOWSKI = 3;
// Blender's SHD_VORONOI_* order, which is NOT the order the UI lists them in.
const int BV_F1 = 0;
const int BV_F2 = 1;
const int BV_SMOOTH_F1 = 2;
const int BV_DISTANCE_TO_EDGE = 3;
const int BV_N_SPHERE_RADIUS = 4;
struct BvParams {
float scale;
float detail;
float roughness;
float lacunarity;
float smoothness;
float exponent;
float randomness;
float max_distance;
bool normalize;
int feature;
int metric;
};
struct BvOutput {
float distance;
vec3 color;
vec4 position;
};
// `mix` is Blender's: (1 - x) * v0 + x * v1, NOT v0 + x * (v1 - v0). They are
// the same in algebra and not in floating point.
BN_FN float bv_bmix(float v0, float v1, float x) { return (1.0f - x) * v0 + x * v1; }
BN_FN vec2 bv_bmix(vec2 a, vec2 b, float x) {
return a * (1.0f - x) + b * x;
}
BN_FN vec3 bv_bmix(vec3 a, vec3 b, float x) {
return a * (1.0f - x) + b * x;
}
BN_FN vec4 bv_bmix(vec4 a, vec4 b, float x) {
return a * (1.0f - x) + b * x;
}
// BLI's smoothstep, which clamps by BRANCHING on the edges rather than by
// clamping t -- same values, and written the same way to keep the diff honest.
BN_FN float bv_bsmoothstep(float edge0, float edge1, float x) {
if (x < edge0) {
return 0.0f;
}
if (x >= edge1) {
return 1.0f;
}
float t = (x - edge0) / (edge1 - edge0);
return (3.0f - 2.0f * t) * (t * t);
}
BN_FN vec2 bv_bfloor(vec2 v) { return vec2(floor(v.x), floor(v.y)); }
BN_FN vec3 bv_bfloor(vec3 v) {
return vec3(floor(v.x), floor(v.y), floor(v.z));
}
BN_FN vec4 bv_bfloor(vec4 v) {
return vec4(floor(v.x), floor(v.y), floor(v.z), floor(v.w));
}
BN_FN ivec2 bv_to_ivec(vec2 v) { return ivec2( int(v.x), int(v.y) ); }
BN_FN ivec3 bv_to_ivec(vec3 v) {
return ivec3( int(v.x), int(v.y), int(v.z) );
}
BN_FN ivec4 bv_to_ivec(vec4 v) {
return ivec4( int(v.x), int(v.y), int(v.z), int(v.w) );
}
BN_FN ivec2 bv_iadd(ivec2 a, ivec2 b) { return ivec2( a.x + b.x, a.y + b.y ); }
BN_FN ivec3 bv_iadd(ivec3 a, ivec3 b) {
return ivec3( a.x + b.x, a.y + b.y, a.z + b.z );
}
BN_FN ivec4 bv_iadd(ivec4 a, ivec4 b) {
return ivec4( a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w );
}
BN_FN vec2 bv_to_vec(ivec2 v) { return vec2(float(v.x), float(v.y)); }
BN_FN vec3 bv_to_vec(ivec3 v) {
return vec3(float(v.x), float(v.y), float(v.z));
}
BN_FN vec4 bv_to_vec(ivec4 v) {
return vec4(float(v.x), float(v.y), float(v.z), float(v.w));
}
BN_FN float bv_vdot(vec4 a, vec4 b) {
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
}
BN_FN float bv_vlen_sq(vec4 v) { return bv_vdot(v, v); }
BN_FN vec4 bv_vnorm(vec4 v) {
float l = sqrt(bv_vlen_sq(v));
return l > 0.0f ? v / l : vec4(0.0f);
}
/* ***** Distances ***** */
BN_FN float bv_voronoi_distance(float a, float b) { return abs(b - a); }
BN_FN float bv_voronoi_distance(vec2 a, vec2 b,
BvParams params) {
switch (params.metric) {
case BV_EUCLIDEAN:
return distance(a, b);
case BV_MANHATTAN:
return abs(a.x - b.x) + abs(a.y - b.y);
case BV_CHEBYCHEV:
return max(abs(a.x - b.x), abs(a.y - b.y));
case BV_MINKOWSKI:
return pow(pow(abs(a.x - b.x), params.exponent) +
pow(abs(a.y - b.y), params.exponent),
1.0f / params.exponent);
}
return 0.0f;
}
BN_FN float bv_voronoi_distance(vec3 a, vec3 b,
BvParams params) {
switch (params.metric) {
case BV_EUCLIDEAN:
return distance(a, b);
case BV_MANHATTAN:
return abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z);
case BV_CHEBYCHEV:
return max(abs(a.x - b.x), max(abs(a.y - b.y), abs(a.z - b.z)));
case BV_MINKOWSKI:
return pow(pow(abs(a.x - b.x), params.exponent) +
pow(abs(a.y - b.y), params.exponent) +
pow(abs(a.z - b.z), params.exponent),
1.0f / params.exponent);
}
return 0.0f;
}
BN_FN float bv_voronoi_distance(vec4 a, vec4 b,
BvParams params) {
switch (params.metric) {
case BV_EUCLIDEAN:
return sqrt(bv_vlen_sq(a - b));
case BV_MANHATTAN:
return abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z) + abs(a.w - b.w);
case BV_CHEBYCHEV:
return max(max(abs(a.x - b.x), abs(a.y - b.y)),
max(abs(a.z - b.z), abs(a.w - b.w)));
case BV_MINKOWSKI:
return pow(pow(abs(a.x - b.x), params.exponent) +
pow(abs(a.y - b.y), params.exponent) +
pow(abs(a.z - b.z), params.exponent) +
pow(abs(a.w - b.w), params.exponent),
1.0f / params.exponent);
}
return 0.0f;
}
// Cheaper comparison that does not change WHICH point is closest. Euclidean
// compares squared, so f1/f2 recompute the real distance after the search --
// which is why 3D's octave.distance is a second call and 1D's is not.
// Cheaper comparison that does not change WHICH point is closest. Euclidean
// compares squared, so f1/f2 recompute the real distance after the search --
// which is why 3D's octave.distance is a second call and 1D's is not.
//
// A template with a Vector4 specialisation in the C++; here it is three
// overloads, because GLSL resolves on the argument type just as well and the
// only thing the specialisation changed was which length-squared to call.
BN_FN float bv_vlen_sq_v(vec2 v) { return v.x * v.x + v.y * v.y; }
BN_FN float bv_vlen_sq_v(vec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; }
BN_FN float bv_vlen_sq_v(vec4 v) { return bv_vlen_sq(v); }
BN_FN float bv_voronoi_distance_bound(vec2 a, vec2 b, BvParams params) {
if (params.metric == BV_EUCLIDEAN) { return bv_vlen_sq_v(a - b); }
return bv_voronoi_distance(a, b, params);
}
BN_FN float bv_voronoi_distance_bound(vec3 a, vec3 b, BvParams params) {
if (params.metric == BV_EUCLIDEAN) { return bv_vlen_sq_v(a - b); }
return bv_voronoi_distance(a, b, params);
}
BN_FN float bv_voronoi_distance_bound(vec4 a, vec4 b, BvParams params) {
if (params.metric == BV_EUCLIDEAN) { return bv_vlen_sq(a - b); }
return bv_voronoi_distance(a, b, params);
}
/* **** 1D Voronoi **** */
BN_FN vec4 bv_voronoi_position(float coord) {
return vec4(0.0f, 0.0f, 0.0f, coord);
}
BN_FN BvOutput bv_voronoi_f1(BvParams params, float coord) {
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float minDistance = 3.402823466e+38f;
float targetOffset = 0.0f;
float targetPosition = 0.0f;
for (int i = -1; i <= 1; i++) {
float cellOffset = float(i);
float pointPosition = cellOffset +
bn_hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = bv_voronoi_distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
BvOutput octave;
octave.distance = minDistance;
octave.color = bn_hash_float_to_float3(cellPosition + targetOffset);
octave.position = bv_voronoi_position(targetPosition + cellPosition);
return octave;
}
BN_FN BvOutput bv_voronoi_smooth_f1(BvParams params, float coord,
bool calc_color) {
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float smoothDistance = 0.0f;
float smoothPosition = 0.0f;
vec3 smoothColor;
float h = -1.0f;
for (int i = -2; i <= 2; i++) {
float cellOffset = float(i);
float pointPosition = cellOffset +
bn_hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = bv_voronoi_distance(pointPosition, localPosition);
h = h == -1.0f ? 1.0f
: bv_bsmoothstep(0.0f, 1.0f,
0.5f + 0.5f * (smoothDistance - distanceToPoint) /
params.smoothness);
float correctionFactor = params.smoothness * h * (1.0f - h);
smoothDistance = bv_bmix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0f + 3.0f * params.smoothness;
vec3 cellColor = bn_hash_float_to_float3(cellPosition + cellOffset);
if (calc_color) {
smoothColor = bv_bmix(smoothColor, cellColor, h) - vec3(correctionFactor,
correctionFactor, correctionFactor);
}
smoothPosition = bv_bmix(smoothPosition, pointPosition, h) - correctionFactor;
}
BvOutput octave;
octave.distance = smoothDistance;
octave.color = smoothColor;
octave.position = bv_voronoi_position(cellPosition + smoothPosition);
return octave;
}
BN_FN BvOutput bv_voronoi_f2(BvParams params, float coord) {
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float distanceF1 = 3.402823466e+38f;
float distanceF2 = 3.402823466e+38f;
float offsetF1 = 0.0f;
float positionF1 = 0.0f;
float offsetF2 = 0.0f;
float positionF2 = 0.0f;
for (int i = -1; i <= 1; i++) {
float cellOffset = float(i);
float pointPosition = cellOffset +
bn_hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = bv_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;
}
}
BvOutput octave;
octave.distance = distanceF2;
octave.color = bn_hash_float_to_float3(cellPosition + offsetF2);
octave.position = bv_voronoi_position(positionF2 + cellPosition);
return octave;
}
BN_FN float bv_voronoi_distance_to_edge(BvParams params, float coord) {
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float midPointPosition = bn_hash_float_to_float(cellPosition) * params.randomness;
float leftPointPosition = -1.0f +
bn_hash_float_to_float(cellPosition - 1.0f) * params.randomness;
float rightPointPosition = 1.0f +
bn_hash_float_to_float(cellPosition + 1.0f) * params.randomness;
float distanceToMidLeft = abs((midPointPosition + leftPointPosition) / 2.0f - localPosition);
float distanceToMidRight = abs((midPointPosition + rightPointPosition) / 2.0f -
localPosition);
return min(distanceToMidLeft, distanceToMidRight);
}
BN_FN float bv_voronoi_n_sphere_radius(BvParams params, float coord) {
float cellPosition = floor(coord);
float localPosition = coord - cellPosition;
float closestPoint = 0.0f;
float closestPointOffset = 0.0f;
float minDistance = 3.402823466e+38f;
for (int i = -1; i <= 1; i++) {
float cellOffset = float(i);
float pointPosition = cellOffset +
bn_hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = abs(pointPosition - localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
minDistance = 3.402823466e+38f;
float closestPointToClosestPoint = 0.0f;
for (int i = -1; i <= 1; i++) {
if (i == 0) {
continue;
}
float cellOffset = float(i) + closestPointOffset;
float pointPosition = cellOffset +
bn_hash_float_to_float(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = abs(closestPoint - pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
closestPointToClosestPoint = pointPosition;
}
}
return abs(closestPointToClosestPoint - closestPoint) / 2.0f;
}
/* **** 2D Voronoi **** */
BN_FN vec4 bv_voronoi_position(vec2 coord) {
return vec4(coord.x, coord.y, 0.0f, 0.0f);
}
BN_FN BvOutput bv_voronoi_f1(BvParams params, vec2 coord) {
vec2 cellPosition_f = bv_bfloor(coord);
vec2 localPosition = coord - cellPosition_f;
ivec2 cellPosition = bv_to_ivec(cellPosition_f);
float minDistance = 3.402823466e+38f;
ivec2 targetOffset;
vec2 targetPosition;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
ivec2 cellOffset = ivec2( i, j );
vec2 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float2(bv_iadd(cellPosition, cellOffset)) * params.randomness;
float distanceToPoint = bv_voronoi_distance_bound(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
BvOutput octave;
octave.distance = bv_voronoi_distance(targetPosition, localPosition, params);
octave.color = bn_hash_int_to_float3(bv_iadd(cellPosition, targetOffset));
octave.position = bv_voronoi_position(targetPosition + cellPosition_f);
return octave;
}
BN_FN BvOutput bv_voronoi_smooth_f1(BvParams params, vec2 coord,
bool calc_color) {
vec2 cellPosition_f = bv_bfloor(coord);
vec2 localPosition = coord - cellPosition_f;
ivec2 cellPosition = bv_to_ivec(cellPosition_f);
float smoothDistance = 0.0f;
vec3 smoothColor;
vec2 smoothPosition;
float h = -1.0f;
for (int j = -2; j <= 2; j++) {
for (int i = -2; i <= 2; i++) {
ivec2 cellOffset = ivec2( i, j );
vec2 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float2(bv_iadd(cellPosition, cellOffset)) * params.randomness;
float distanceToPoint = bv_voronoi_distance(pointPosition, localPosition, params);
h = h == -1.0f ? 1.0f
: bv_bsmoothstep(0.0f, 1.0f,
0.5f + 0.5f * (smoothDistance - distanceToPoint) /
params.smoothness);
float correctionFactor = params.smoothness * h * (1.0f - h);
smoothDistance = bv_bmix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0f + 3.0f * params.smoothness;
if (calc_color) {
vec3 cellColor = bn_hash_int_to_float3(bv_iadd(cellPosition, cellOffset));
smoothColor = bv_bmix(smoothColor, cellColor, h) - vec3(correctionFactor,
correctionFactor, correctionFactor);
}
smoothPosition = bv_bmix(smoothPosition, pointPosition, h) - vec2(correctionFactor,
correctionFactor);
}
}
BvOutput octave;
octave.distance = smoothDistance;
octave.color = smoothColor;
octave.position = bv_voronoi_position(cellPosition_f + smoothPosition);
return octave;
}
BN_FN BvOutput bv_voronoi_f2(BvParams params, vec2 coord) {
vec2 cellPosition_f = bv_bfloor(coord);
vec2 localPosition = coord - cellPosition_f;
ivec2 cellPosition = bv_to_ivec(cellPosition_f);
float distanceF1 = 3.402823466e+38f;
float distanceF2 = 3.402823466e+38f;
ivec2 offsetF1;
vec2 positionF1;
ivec2 offsetF2;
vec2 positionF2;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
ivec2 cellOffset = ivec2( i, j );
vec2 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float2(bv_iadd(cellPosition, cellOffset)) * params.randomness;
float distanceToPoint = bv_voronoi_distance(pointPosition, localPosition, params);
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;
}
}
}
BvOutput octave;
octave.distance = distanceF2;
octave.color = bn_hash_int_to_float3(bv_iadd(cellPosition, offsetF2));
octave.position = bv_voronoi_position(positionF2 + cellPosition_f);
return octave;
}
BN_FN float bv_voronoi_distance_to_edge(BvParams params, vec2 coord) {
vec2 cellPosition_f = bv_bfloor(coord);
vec2 localPosition = coord - cellPosition_f;
ivec2 cellPosition = bv_to_ivec(cellPosition_f);
vec2 vectorToClosest;
float minDistance = 3.402823466e+38f;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
ivec2 cellOffset = ivec2( i, j );
vec2 vectorToPoint = bv_to_vec(cellOffset) +
bn_hash_int_to_float2(bv_iadd(cellPosition, cellOffset)) * params.randomness -
localPosition;
float distanceToPoint = dot(vectorToPoint, vectorToPoint);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
vectorToClosest = vectorToPoint;
}
}
}
minDistance = 3.402823466e+38f;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
ivec2 cellOffset = ivec2( i, j );
vec2 vectorToPoint = bv_to_vec(cellOffset) +
bn_hash_int_to_float2(bv_iadd(cellPosition, cellOffset)) * params.randomness -
localPosition;
vec2 perpendicularToEdge = vectorToPoint - vectorToClosest;
if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001f) {
float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0f, normalize(perpendicularToEdge));
minDistance = min(minDistance, distanceToEdge);
}
}
}
return minDistance;
}
BN_FN float bv_voronoi_n_sphere_radius(BvParams params, vec2 coord) {
vec2 cellPosition_f = bv_bfloor(coord);
vec2 localPosition = coord - cellPosition_f;
ivec2 cellPosition = bv_to_ivec(cellPosition_f);
vec2 closestPoint;
ivec2 closestPointOffset;
float minDistanceSq = 3.402823466e+38f;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
ivec2 cellOffset = ivec2( i, j );
vec2 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float2(bv_iadd(cellPosition, cellOffset)) * params.randomness;
float distanceToPointSq = bv_vlen_sq_v(pointPosition - localPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
minDistanceSq = 3.402823466e+38f;
vec2 closestPointToClosestPoint;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
if (i == 0 && j == 0) {
continue;
}
ivec2 cellOffset = bv_iadd(ivec2( i, j ), closestPointOffset);
vec2 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float2(bv_iadd(cellPosition, cellOffset)) * params.randomness;
float distanceToPointSq = bv_vlen_sq_v(closestPoint - pointPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPointToClosestPoint = pointPosition;
}
}
}
return distance(closestPointToClosestPoint, closestPoint) / 2.0f;
}
/* **** 3D Voronoi **** */
BN_FN vec4 bv_voronoi_position(vec3 coord) {
return vec4(coord.x, coord.y, coord.z, 0.0f);
}
BN_FN BvOutput bv_voronoi_f1(BvParams params, vec3 coord) {
vec3 cellPosition_f = bv_bfloor(coord);
vec3 localPosition = coord - cellPosition_f;
ivec3 cellPosition = bv_to_ivec(cellPosition_f);
float minDistance = 3.402823466e+38f;
ivec3 targetOffset;
vec3 targetPosition;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
ivec3 cellOffset = ivec3( i, j, k );
vec3 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float3(bv_iadd(cellPosition, cellOffset)) * params.randomness;
float distanceToPoint = bv_voronoi_distance_bound(pointPosition, localPosition,
params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
}
BvOutput octave;
octave.distance = bv_voronoi_distance(targetPosition, localPosition, params);
octave.color = bn_hash_int_to_float3(bv_iadd(cellPosition, targetOffset));
octave.position = bv_voronoi_position(targetPosition + cellPosition_f);
return octave;
}
BN_FN BvOutput bv_voronoi_smooth_f1(BvParams params, vec3 coord,
bool calc_color) {
vec3 cellPosition_f = bv_bfloor(coord);
vec3 localPosition = coord - cellPosition_f;
ivec3 cellPosition = bv_to_ivec(cellPosition_f);
float smoothDistance = 0.0f;
vec3 smoothColor;
vec3 smoothPosition;
float h = -1.0f;
for (int k = -2; k <= 2; k++) {
for (int j = -2; j <= 2; j++) {
for (int i = -2; i <= 2; i++) {
ivec3 cellOffset = ivec3( i, j, k );
vec3 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float3(bv_iadd(cellPosition, cellOffset)) * params.randomness;
float distanceToPoint = bv_voronoi_distance(pointPosition, localPosition, params);
h = h == -1.0f ? 1.0f
: bv_bsmoothstep(0.0f, 1.0f,
0.5f + 0.5f * (smoothDistance - distanceToPoint) /
params.smoothness);
float correctionFactor = params.smoothness * h * (1.0f - h);
smoothDistance = bv_bmix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0f + 3.0f * params.smoothness;
if (calc_color) {
vec3 cellColor = bn_hash_int_to_float3(bv_iadd(cellPosition, cellOffset));
smoothColor = bv_bmix(smoothColor, cellColor, h) - vec3(correctionFactor,
correctionFactor, correctionFactor);
}
smoothPosition = bv_bmix(smoothPosition, pointPosition, h) -
vec3(correctionFactor, correctionFactor, correctionFactor);
}
}
}
BvOutput octave;
octave.distance = smoothDistance;
octave.color = smoothColor;
octave.position = bv_voronoi_position(cellPosition_f + smoothPosition);
return octave;
}
BN_FN BvOutput bv_voronoi_f2(BvParams params, vec3 coord) {
vec3 cellPosition_f = bv_bfloor(coord);
vec3 localPosition = coord - cellPosition_f;
ivec3 cellPosition = bv_to_ivec(cellPosition_f);
float distanceF1 = 3.402823466e+38f;
float distanceF2 = 3.402823466e+38f;
ivec3 offsetF1;
vec3 positionF1;
ivec3 offsetF2;
vec3 positionF2;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
ivec3 cellOffset = ivec3( i, j, k );
vec3 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float3(bv_iadd(cellPosition, cellOffset)) * params.randomness;
float distanceToPoint = bv_voronoi_distance(pointPosition, localPosition, params);
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;
}
}
}
}
BvOutput octave;
octave.distance = distanceF2;
octave.color = bn_hash_int_to_float3(bv_iadd(cellPosition, offsetF2));
octave.position = bv_voronoi_position(positionF2 + cellPosition_f);
return octave;
}
BN_FN float bv_voronoi_distance_to_edge(BvParams params, vec3 coord) {
vec3 cellPosition_f = bv_bfloor(coord);
vec3 localPosition = coord - cellPosition_f;
ivec3 cellPosition = bv_to_ivec(cellPosition_f);
vec3 vectorToClosest;
float minDistance = 3.402823466e+38f;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
ivec3 cellOffset = ivec3( i, j, k );
vec3 vectorToPoint = bv_to_vec(cellOffset) +
bn_hash_int_to_float3(bv_iadd(cellPosition, cellOffset)) * params.randomness -
localPosition;
float distanceToPoint = dot(vectorToPoint, vectorToPoint);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
vectorToClosest = vectorToPoint;
}
}
}
}
minDistance = 3.402823466e+38f;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
ivec3 cellOffset = ivec3( i, j, k );
vec3 vectorToPoint = bv_to_vec(cellOffset) +
bn_hash_int_to_float3(bv_iadd(cellPosition, cellOffset)) * params.randomness -
localPosition;
vec3 perpendicularToEdge = vectorToPoint - vectorToClosest;
if (dot(perpendicularToEdge, perpendicularToEdge) > 0.0001f) {
float distanceToEdge = dot((vectorToClosest + vectorToPoint) / 2.0f, normalize(perpendicularToEdge));
minDistance = min(minDistance, distanceToEdge);
}
}
}
}
return minDistance;
}
BN_FN float bv_voronoi_n_sphere_radius(BvParams params, vec3 coord) {
vec3 cellPosition_f = bv_bfloor(coord);
vec3 localPosition = coord - cellPosition_f;
ivec3 cellPosition = bv_to_ivec(cellPosition_f);
vec3 closestPoint;
ivec3 closestPointOffset;
float minDistanceSq = 3.402823466e+38f;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
ivec3 cellOffset = ivec3( i, j, k );
vec3 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float3(bv_iadd(cellPosition, cellOffset)) * params.randomness;
float distanceToPointSq = bv_vlen_sq_v(pointPosition - localPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
}
minDistanceSq = 3.402823466e+38f;
vec3 closestPointToClosestPoint;
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;
}
ivec3 cellOffset = bv_iadd(ivec3( i, j, k ), closestPointOffset);
vec3 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float3(bv_iadd(cellPosition, cellOffset)) * params.randomness;
float distanceToPointSq = bv_vlen_sq_v(closestPoint - pointPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPointToClosestPoint = pointPosition;
}
}
}
}
return distance(closestPointToClosestPoint, closestPoint) / 2.0f;
}
/* **** 4D Voronoi **** */
BN_FN vec4 bv_voronoi_position(vec4 coord) { return coord; }
BN_FN BvOutput bv_voronoi_f1(BvParams params, vec4 coord) {
vec4 cellPosition_f = bv_bfloor(coord);
vec4 localPosition = coord - cellPosition_f;
ivec4 cellPosition = bv_to_ivec(cellPosition_f);
float minDistance = 3.402823466e+38f;
ivec4 targetOffset;
vec4 targetPosition;
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++) {
ivec4 cellOffset = ivec4( i, j, k, u );
vec4 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float4(bv_iadd(cellPosition, cellOffset)) *
params.randomness;
float distanceToPoint = bv_voronoi_distance_bound(pointPosition, localPosition,
params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
targetPosition = pointPosition;
}
}
}
}
}
BvOutput octave;
octave.distance = bv_voronoi_distance(targetPosition, localPosition, params);
octave.color = bn_hash_int_to_float3(bv_iadd(cellPosition, targetOffset));
octave.position = bv_voronoi_position(targetPosition + cellPosition_f);
return octave;
}
BN_FN BvOutput bv_voronoi_smooth_f1(BvParams params, vec4 coord,
bool calc_color) {
vec4 cellPosition_f = bv_bfloor(coord);
vec4 localPosition = coord - cellPosition_f;
ivec4 cellPosition = bv_to_ivec(cellPosition_f);
float smoothDistance = 0.0f;
vec3 smoothColor;
vec4 smoothPosition;
float h = -1.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++) {
ivec4 cellOffset = ivec4( i, j, k, u );
vec4 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float4(bv_iadd(cellPosition, cellOffset)) *
params.randomness;
float distanceToPoint = bv_voronoi_distance(pointPosition, localPosition, params);
h = h == -1.0f ? 1.0f
: bv_bsmoothstep(0.0f, 1.0f,
0.5f + 0.5f * (smoothDistance - distanceToPoint) /
params.smoothness);
float correctionFactor = params.smoothness * h * (1.0f - h);
smoothDistance = bv_bmix(smoothDistance, distanceToPoint, h) - correctionFactor;
correctionFactor /= 1.0f + 3.0f * params.smoothness;
if (calc_color) {
vec3 cellColor = bn_hash_int_to_float3(bv_iadd(cellPosition, cellOffset));
smoothColor = bv_bmix(smoothColor, cellColor, h) - vec3(correctionFactor,
correctionFactor, correctionFactor);
}
smoothPosition = bv_bmix(smoothPosition, pointPosition, h) -
vec4(correctionFactor, correctionFactor, correctionFactor,
correctionFactor);
}
}
}
}
BvOutput octave;
octave.distance = smoothDistance;
octave.color = smoothColor;
octave.position = bv_voronoi_position(cellPosition_f + smoothPosition);
return octave;
}
BN_FN BvOutput bv_voronoi_f2(BvParams params, vec4 coord) {
vec4 cellPosition_f = bv_bfloor(coord);
vec4 localPosition = coord - cellPosition_f;
ivec4 cellPosition = bv_to_ivec(cellPosition_f);
float distanceF1 = 3.402823466e+38f;
float distanceF2 = 3.402823466e+38f;
ivec4 offsetF1;
vec4 positionF1;
ivec4 offsetF2;
vec4 positionF2;
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++) {
ivec4 cellOffset = ivec4( i, j, k, u );
vec4 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float4(bv_iadd(cellPosition, cellOffset)) *
params.randomness;
float distanceToPoint = bv_voronoi_distance(pointPosition, localPosition, params);
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;
}
}
}
}
}
BvOutput octave;
octave.distance = distanceF2;
octave.color = bn_hash_int_to_float3(bv_iadd(cellPosition, offsetF2));
octave.position = bv_voronoi_position(positionF2 + cellPosition_f);
return octave;
}
BN_FN float bv_voronoi_distance_to_edge(BvParams params, vec4 coord) {
vec4 cellPosition_f = bv_bfloor(coord);
vec4 localPosition = coord - cellPosition_f;
ivec4 cellPosition = bv_to_ivec(cellPosition_f);
vec4 vectorToClosest;
float minDistance = 3.402823466e+38f;
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++) {
ivec4 cellOffset = ivec4( i, j, k, u );
vec4 vectorToPoint = bv_to_vec(cellOffset) +
bn_hash_int_to_float4(bv_iadd(cellPosition, cellOffset)) *
params.randomness -
localPosition;
float distanceToPoint = bv_vdot(vectorToPoint, vectorToPoint);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
vectorToClosest = vectorToPoint;
}
}
}
}
}
minDistance = 3.402823466e+38f;
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++) {
ivec4 cellOffset = ivec4( i, j, k, u );
vec4 vectorToPoint = bv_to_vec(cellOffset) +
bn_hash_int_to_float4(bv_iadd(cellPosition, cellOffset)) *
params.randomness -
localPosition;
vec4 perpendicularToEdge = vectorToPoint - vectorToClosest;
if (bv_vdot(perpendicularToEdge, perpendicularToEdge) > 0.0001f) {
float distanceToEdge = bv_vdot((vectorToClosest + vectorToPoint) / 2.0f,
bv_vnorm(perpendicularToEdge));
minDistance = min(minDistance, distanceToEdge);
}
}
}
}
}
return minDistance;
}
BN_FN float bv_voronoi_n_sphere_radius(BvParams params, vec4 coord) {
vec4 cellPosition_f = bv_bfloor(coord);
vec4 localPosition = coord - cellPosition_f;
ivec4 cellPosition = bv_to_ivec(cellPosition_f);
vec4 closestPoint;
ivec4 closestPointOffset;
float minDistanceSq = 3.402823466e+38f;
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++) {
ivec4 cellOffset = ivec4( i, j, k, u );
vec4 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float4(bv_iadd(cellPosition, cellOffset)) *
params.randomness;
float distanceToPointSq = bv_vlen_sq(pointPosition - localPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
}
}
minDistanceSq = 3.402823466e+38f;
vec4 closestPointToClosestPoint;
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;
}
ivec4 cellOffset = bv_iadd(ivec4( i, j, k, u ), closestPointOffset);
vec4 pointPosition = bv_to_vec(cellOffset) +
bn_hash_int_to_float4(bv_iadd(cellPosition, cellOffset)) *
params.randomness;
float distanceToPointSq = bv_vlen_sq(closestPoint - pointPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPointToClosestPoint = pointPosition;
}
}
}
}
}
return sqrt(bv_vlen_sq(closestPointToClosestPoint - closestPoint)) / 2.0f;
}
/* **** Fractal Voronoi **** */
// The fractalization is fBM's, except that some additions are replaced by lerps.
/* **** Fractal Voronoi **** */
// The fractalization is fBM's, except that some additions are replaced by
// lerps. `template <typename T>` in the C++; one body, stamped per dimension.
#define BV_FRACTALS(T, S) \
BN_FN BvOutput bv_fractal_voronoi_x_fx_##S(BvParams params, const T coord, \
bool calc_color) { \
float amplitude = 1.0f; \
float max_amplitude = 0.0f; \
float scale = 1.0f; \
\
BvOutput outp; \
outp.distance = 0.0f; \
outp.color = vec3(0.0f); \
outp.position = vec4(0.0f); \
bool zero_input = params.detail == 0.0f || params.roughness == 0.0f; \
\
for (int i = 0; i <= int(ceil(params.detail)); ++i) { \
BvOutput octave = (params.feature == BV_F2) \
? bv_voronoi_f2(params, coord * scale) \
: ((params.feature == BV_SMOOTH_F1 && params.smoothness != 0.0f) \
? bv_voronoi_smooth_f1(params, coord * scale, calc_color) \
: bv_voronoi_f1(params, coord * scale)); \
\
if (zero_input) { \
max_amplitude = 1.0f; \
outp = octave; \
break; \
} \
if (float(i) <= params.detail) { \
max_amplitude += amplitude; \
outp.distance += octave.distance * amplitude; \
outp.color += octave.color * amplitude; \
outp.position = bv_bmix(outp.position, octave.position / scale, amplitude); \
scale *= params.lacunarity; \
amplitude *= params.roughness; \
} else { \
float remainder = params.detail - floor(params.detail); \
if (remainder != 0.0f) { \
max_amplitude = bv_bmix(max_amplitude, max_amplitude + amplitude, remainder); \
outp.distance = bv_bmix(outp.distance, \
outp.distance + octave.distance * amplitude, remainder); \
outp.color = bv_bmix(outp.color, outp.color + octave.color * amplitude, \
remainder); \
outp.position = bv_bmix(outp.position, \
bv_bmix(outp.position, octave.position / scale, amplitude), remainder); \
} \
} \
} \
\
if (params.normalize) { \
outp.distance /= max_amplitude * params.max_distance; \
outp.color /= max_amplitude; \
} \
\
outp.position = (params.scale != 0.0f) ? outp.position / params.scale : vec4(0.0f); \
\
return outp; \
} \
\
BN_FN float bv_fractal_voronoi_distance_to_edge_##S(BvParams params, const T coord) { \
float amplitude = 1.0f; \
float max_amplitude = params.max_distance; \
float scale = 1.0f; \
float distance = 8.0f; \
\
bool zero_input = params.detail == 0.0f || params.roughness == 0.0f; \
\
for (int i = 0; i <= int(ceil(params.detail)); ++i) { \
float octave_distance = bv_voronoi_distance_to_edge(params, coord * scale); \
\
if (zero_input) { \
distance = octave_distance; \
break; \
} \
if (float(i) <= params.detail) { \
max_amplitude = bv_bmix(max_amplitude, params.max_distance / scale, amplitude); \
distance = bv_bmix(distance, min(distance, octave_distance / scale), amplitude); \
scale *= params.lacunarity; \
amplitude *= params.roughness; \
} else { \
float remainder = params.detail - floor(params.detail); \
if (remainder != 0.0f) { \
float lerp_amplitude = bv_bmix(max_amplitude, params.max_distance / scale, amplitude); \
max_amplitude = bv_bmix(max_amplitude, lerp_amplitude, remainder); \
float lerp_distance = bv_bmix(distance, min(distance, octave_distance / scale), \
amplitude); \
distance = bv_bmix(distance, min(distance, lerp_distance), remainder); \
} \
} \
} \
\
if (params.normalize) { \
distance /= max_amplitude; \
} \
\
return distance; \
}
BV_FRACTALS(float, 1)
BV_FRACTALS(vec2, 2)
BV_FRACTALS(vec3, 3)
BV_FRACTALS(vec4, 4)
#endif // PN_BLENDER_VORONOI