#ifndef PROC_NODES_BLENDER_NOISE_CPP_SHIM_H
#define PROC_NODES_BLENDER_NOISE_CPP_SHIM_H
// =========================================================================
// blender_noise_cpp_shim.h — the C++ half of blender_hash.inc's dual
// compilation, and the exact counterpart of erosion_cpp_shim.h.
//
// blender_hash.inc is written in the subset GLSL and C++ agree on. In a
// shader every type and builtin it names is native. In C++ nothing called
// uvec3 or floatBitsToUint exists, so this header supplies them — with the
// SAME semantics — and then the .inc compiles unchanged.
//
// THE ONE RULE FOR THIS FILE: every type and function here must match its
// GLSL spelling exactly, including on the edges. The hash layer is integer
// arithmetic, so "exactly" is available and is the standard: GLSL requires
// unsigned overflow to wrap and C++ defines it as modulo 2^32, which is
// precisely why the .inc routes every signed multiply through uint.
//
// DELIBERATELY MINIMAL. The vector types carry construction and component
// access and NOTHING else, because the hash layer only ever builds one and
// reads its parts. An arithmetic operator nobody calls is somewhere for GLSL
// and C++ to differ unnoticed — the same reasoning that left erosion's vec4
// without operators.
//
// This header is godot-free by design, so an oracle with no engine can
// evaluate the hash. Do not add godot_cpp includes; convert at the call
// site, which is what blender_noise.h now is.
// =========================================================================
#include <cmath>
#include <cstdint>
#include <cstring>
namespace godot {
namespace bnoisegl {
// GLSL leaves BN_FN empty. Here it is `inline`, because blender_noise.h is
// included by more than one translation unit and a non-inline definition in
// a header is a multiple-definition link error the moment a second one
// appears — the trap erosion_cpp_shim.h has, and the reason its debug_*
// statics can only be defined in noise_nodes.cpp.
#define BN_FN inline
typedef uint32_t uint;
// GLSL indexes a vector with [] and my structs are not arrays, so each one
// carries the accessor. blender_ramp_blend.inc keeps Blender's own signature
// -- `r_col` is both the A input and the result -- and that body indexes.
struct vec2 {
float x, y;
float &operator[](int i) { return i == 0 ? x : y; }
const float &operator[](int i) const { return i == 0 ? x : y; }
vec2() : x(0.0f), y(0.0f) {}
explicit vec2(float s) : x(s), y(s) {}
vec2(float a, float b) : x(a), y(b) {}
};
struct vec3 {
float x, y, z;
float &operator[](int i) { return i == 0 ? x : (i == 1 ? y : z); }
const float &operator[](int i) const { return i == 0 ? x : (i == 1 ? y : z); }
vec3() : x(0.0f), y(0.0f), z(0.0f) {}
explicit vec3(float s) : x(s), y(s), z(s) {}
vec3(float a, float b, float c) : x(a), y(b), z(c) {}
};
struct vec4 {
float x, y, z, w;
float &operator[](int i) { return i == 0 ? x : (i == 1 ? y : (i == 2 ? z : w)); }
const float &operator[](int i) const { return i == 0 ? x : (i == 1 ? y : (i == 2 ? z : w)); }
vec4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) {}
explicit vec4(float s) : x(s), y(s), z(s), w(s) {}
vec4(float a, float b, float c, float d) : x(a), y(b), z(c), w(d) {}
};
// uvec3 exists for one reason: hash_bit_mix and hash_bit_final took C++
// references, GLSL's `inout` is not the same thing, and erosion_filter.inc
// records the same decision — carry the three words in and out by value.
struct uvec3 {
uint x, y, z;
uvec3() : x(0u), y(0u), z(0u) {}
explicit uvec3(uint s) : x(s), y(s), z(s) {}
uvec3(uint a, uint b, uint c) : x(a), y(b), z(c) {}
};
struct ivec2 {
int32_t x, y;
ivec2() : x(0), y(0) {}
ivec2(int32_t a, int32_t b) : x(a), y(b) {}
};
struct ivec3 {
int32_t x, y, z;
ivec3() : x(0), y(0), z(0) {}
ivec3(int32_t a, int32_t b, int32_t c) : x(a), y(b), z(c) {}
};
struct ivec4 {
int32_t x, y, z, w;
ivec4() : x(0), y(0), z(0), w(0) {}
ivec4(int32_t a, int32_t b, int32_t c, int32_t d) : x(a), y(b), z(c), w(d) {}
};
// GLSL's bit cast. memcpy rather than a union or a reinterpret_cast, which
// is the only spelling that is not undefined behaviour.
inline uint floatBitsToUint(float f) {
uint i;
memcpy(&i, &f, sizeof(i));
return i;
}
// =========================================================================
// ARITHMETIC, for blender_perlin.inc.
//
// The hash layer needed NONE of this -- it only ever built a vector and read
// its components, and an operator nobody calls is somewhere for GLSL and C++
// to differ unnoticed. Perlin genuinely does vector arithmetic (p * fscale,
// p + offset), so the operators exist now, and every one of them is
// COMPONENTWISE because that is what GLSL means by vec * vec. A dot product
// here would be silent and catastrophic.
// =========================================================================
inline vec2 operator+(vec2 a, vec2 b) { return vec2(a.x + b.x, a.y + b.y); }
inline vec2 operator-(vec2 a, vec2 b) { return vec2(a.x - b.x, a.y - b.y); }
inline vec2 operator*(vec2 a, vec2 b) { return vec2(a.x * b.x, a.y * b.y); }
inline vec2 operator*(vec2 a, float s) { return vec2(a.x * s, a.y * s); }
inline vec2 operator*(float s, vec2 a) { return vec2(s * a.x, s * a.y); }
inline vec2 operator+(vec2 a, float s) { return vec2(a.x + s, a.y + s); }
inline vec2 operator-(vec2 a) { return vec2(-a.x, -a.y); }
inline vec3 operator+(vec3 a, vec3 b) { return vec3(a.x + b.x, a.y + b.y, a.z + b.z); }
inline vec3 operator-(vec3 a, vec3 b) { return vec3(a.x - b.x, a.y - b.y, a.z - b.z); }
inline vec3 operator*(vec3 a, vec3 b) { return vec3(a.x * b.x, a.y * b.y, a.z * b.z); }
inline vec3 operator*(vec3 a, float s) { return vec3(a.x * s, a.y * s, a.z * s); }
inline vec3 operator*(float s, vec3 a) { return vec3(s * a.x, s * a.y, s * a.z); }
inline vec3 operator+(vec3 a, float s) { return vec3(a.x + s, a.y + s, a.z + s); }
inline vec3 operator-(vec3 a) { return vec3(-a.x, -a.y, -a.z); }
inline vec4 operator+(vec4 a, vec4 b) { return vec4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); }
inline vec4 operator-(vec4 a, vec4 b) { return vec4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); }
inline vec4 operator*(vec4 a, vec4 b) { return vec4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); }
inline vec4 operator*(vec4 a, float s) { return vec4(a.x * s, a.y * s, a.z * s, a.w * s); }
inline vec4 operator*(float s, vec4 a) { return vec4(s * a.x, s * a.y, s * a.z, s * a.w); }
inline vec4 operator+(vec4 a, float s) { return vec4(a.x + s, a.y + s, a.z + s, a.w + s); }
inline vec4 operator-(vec4 a) { return vec4(-a.x, -a.y, -a.z, -a.w); }
// -- Division and compound assignment, and the rest of the vector builtins.
// Voronoi needs these where perlin did not: it divides positions by a
// scale, accumulates colour per octave, and measures real distances.
inline vec2 operator/(vec2 a, float s) { return vec2(a.x / s, a.y / s); }
inline vec3 operator/(vec3 a, float s) { return vec3(a.x / s, a.y / s, a.z / s); }
inline vec4 operator/(vec4 a, float s) { return vec4(a.x / s, a.y / s, a.z / s, a.w / s); }
inline vec2 &operator+=(vec2 &a, vec2 b) { a = a + b; return a; }
inline vec3 &operator+=(vec3 &a, vec3 b) { a = a + b; return a; }
inline vec4 &operator+=(vec4 &a, vec4 b) { a = a + b; return a; }
inline vec2 &operator/=(vec2 &a, float s) { a = a / s; return a; }
inline vec3 &operator/=(vec3 &a, float s) { a = a / s; return a; }
inline vec4 &operator/=(vec4 &a, float s) { a = a / s; return a; }
inline vec2 operator-(vec2 a, float s) { return vec2(a.x - s, a.y - s); }
inline vec3 operator-(vec3 a, float s) { return vec3(a.x - s, a.y - s, a.z - s); }
inline vec4 operator-(vec4 a, float s) { return vec4(a.x - s, a.y - s, a.z - s, a.w - s); }
inline vec3 floor(vec3 v) { return vec3(std::floor(v.x), std::floor(v.y), std::floor(v.z)); }
inline vec4 floor(vec4 v) {
return vec4(std::floor(v.x), std::floor(v.y), std::floor(v.z), std::floor(v.w));
}
inline vec4 abs(vec4 v) {
return vec4(std::fabs(v.x), std::fabs(v.y), std::fabs(v.z), std::fabs(v.w));
}
inline vec3 min(vec3 a, vec3 b) {
return vec3(a.x < b.x ? a.x : b.x, a.y < b.y ? a.y : b.y, a.z < b.z ? a.z : b.z);
}
inline vec3 max(vec3 a, vec3 b) {
return vec3(a.x > b.x ? a.x : b.x, a.y > b.y ? a.y : b.y, a.z > b.z ? a.z : b.z);
}
inline vec4 max(vec4 a, vec4 b) {
return vec4(a.x > b.x ? a.x : b.x, a.y > b.y ? a.y : b.y,
a.z > b.z ? a.z : b.z, a.w > b.w ? a.w : b.w);
}
// GLSL step(edge, x) is x < edge ? 0 : 1 -- note the ARGUMENT ORDER, which is
// the opposite way round from how it reads.
inline float step(float edge, float x) { return x < edge ? 0.0f : 1.0f; }
inline vec3 step(vec3 e, vec3 x) {
return vec3(step(e.x, x.x), step(e.y, x.y), step(e.z, x.z));
}
inline vec4 step(vec4 e, vec4 x) {
return vec4(step(e.x, x.x), step(e.y, x.y), step(e.z, x.z), step(e.w, x.w));
}
inline vec3 &operator*=(vec3 &a, float s) { a = a * s; return a; }
inline vec4 operator-(float s, vec4 a) { return vec4(s - a.x, s - a.y, s - a.z, s - a.w); }
inline vec3 operator-(float s, vec3 a) { return vec3(s - a.x, s - a.y, s - a.z); }
inline float dot(vec2 a, vec2 b) { return a.x * b.x + a.y * b.y; }
inline float dot(vec3 a, vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
inline float dot(vec4 a, vec4 b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; }
inline float length(vec2 v) { return std::sqrt(dot(v, v)); }
inline float length(vec3 v) { return std::sqrt(dot(v, v)); }
inline float length(vec4 v) { return std::sqrt(dot(v, v)); }
inline float distance(vec2 a, vec2 b) { return length(a - b); }
inline float distance(vec3 a, vec3 b) { return length(a - b); }
inline float distance(vec4 a, vec4 b) { return length(a - b); }
inline vec2 normalize(vec2 v) { float l = length(v); return l > 0.0f ? v / l : vec2(); }
inline vec3 normalize(vec3 v) { float l = length(v); return l > 0.0f ? v / l : vec3(); }
inline vec4 normalize(vec4 v) { float l = length(v); return l > 0.0f ? v / l : vec4(); }
// -- Scalar builtins. float-only, because the .inc writes GLSL literals which
// are DOUBLE in C++, and letting one of these promote would compute a
// perlin octave in double on the CPU and in float on the GPU.
inline float floor(float x) { return std::floor(x); }
inline float trunc(float x) { return std::trunc(x); }
inline float abs(float x) { return std::fabs(x); }
inline float min(float a, float b) { return a < b ? a : b; }
inline float max(float a, float b) { return a > b ? a : b; }
inline float clamp(float v, float lo, float hi) { return v < lo ? lo : (v > hi ? hi : v); }
inline float ceil(float x) { return std::ceil(x); }
// ⚠ FLOAT OVERLOADS, NOT std::. Without these a bare sqrt()/pow() in the .inc
// resolves to the DOUBLE version from <math.h>, so the CPU computes an octave
// in double where the GPU computes it in float. Measured: 208 of 24960
// Voronoi comparisons differed by ~1e-7 for exactly this reason, and every
// one of them came back to bit-exact once these existed.
inline float sqrt(float x) { return std::sqrt(x); }
inline float pow(float a, float b) { return std::pow(a, b); }
inline float exp(float x) { return std::exp(x); }
// The algorithms themselves. Same text the shader compiles, and in the same
// order the emitter concatenates them: perlin's lattice gradients ARE the
// hash, and GLSL has no #include to say so.
#include "blender_hash.inc"
#include "blender_perlin.inc"
#include "blender_voronoi.inc"
#include "blender_ramp_blend.inc"
#include "proc_simplex.inc"
} // namespace bnoisegl
} // namespace godot
#endif // PROC_NODES_BLENDER_NOISE_CPP_SHIM_H