OverviewFilesCommitsIssuesDocsReleasesPeers

proc-nodes / src / shared / blender_perlin.h

177 lines · c ref:
#ifndef PROC_NODES_BLENDER_PERLIN_H
#define PROC_NODES_BLENDER_PERLIN_H

// BLENDER'S PERLIN NOISE — NOW AN ADAPTER, NOT AN IMPLEMENTATION.
//
// The arithmetic that used to live here is in src/shared/blender_perlin.inc,
// compiled BOTH as GLSL and as C++, the same arrangement erosion_filter.inc
// and blender_hash.inc have. This header is the godot-typed face of it, and
// every name and signature callers used is still here -- including the
// explicit template spellings `perlin_fractal_distorted<Vector3>(...)` and
// `perlin_fbm<Vector3>(...)`, which noise_nodes.cpp writes -- so nothing
// downstream changed.
//
// WHY IT MOVED. There were two Perlins: this port, and a hand-written GLSL
// copy in GpuCompiler's hand-written noise library, carrying its own Jenkins hash,
// its own fade, its own gradients and the same unexplained 0.9820. Nothing
// compared them. The Noise Texture node could not be given GPU support without
// choosing which of the two to believe.
//
// GLSL HAS NO TEMPLATES, so the five fractal types -- which are
// `template <typename T>` in Blender's C++ -- are expanded per dimension by
// the preprocessor in the .inc, and the templates here are thin
// specialisations that pick the matching suffix. The dimension is chosen ONCE,
// by C++ overload resolution on the caller's type, instead of by a hand-copied
// suffix in twenty places.
//
// Two things in the .inc could not have been guessed and are worth repeating
// where people look:
//
//   the per-dimension SCALE CONSTANTS on perlin_signed -- 0.2500, 0.6616,
//   0.9820, 0.8344 -- found experimentally by the OSL developers to remap each
//   dimension's output to [-1, 1]. 0.9820 is the 3D one, and it is the same
//   0.982 that appeared as an unexplained "normalization factor matching
//   iq/Blender" in ProcNodes_Noised, where it did not belong.
//
//   the SEEDS of the Colour output's second and third components, which differ
//   per dimension: 1D uses 1 and 2, 2D uses 2 and 3, 3D uses 3 and 4, 4D uses
//   4 and 5. "Arbitrary seeds ... for compatibility with shading functions",
//   and there is no rule to derive them from.

#include "blender_noise.h"

#include <godot_cpp/variant/vector2.hpp>
#include <godot_cpp/variant/vector3.hpp>
#include <godot_cpp/variant/vector4.hpp>

namespace godot {
namespace bperlin {

using namespace bnoise;

enum {
	PERLIN_MULTIFRACTAL = 0,
	PERLIN_FBM = 1,
	PERLIN_HYBRID_MULTIFRACTAL = 2,
	PERLIN_RIDGED_MULTIFRACTAL = 3,
	PERLIN_HETERO_TERRAIN = 4,
};

namespace detail {
inline bnoisegl::vec2 gl(const Vector2 &v) { return bnoisegl::vec2(v.x, v.y); }
inline bnoisegl::vec3 gl(const Vector3 &v) { return bnoisegl::vec3(v.x, v.y, v.z); }
inline bnoisegl::vec4 gl(const Vector4 &v) { return bnoisegl::vec4(v.x, v.y, v.z, v.w); }
inline Vector3 gd(const bnoisegl::vec3 &v) { return Vector3(v.x, v.y, v.z); }
} // namespace detail

// --- the fractal families, dispatched by the caller's coordinate type ---

template <typename T>
float perlin_fbm(T p, float detail_v, float roughness, float lacunarity, bool normalize);

template <>
inline float perlin_fbm<float>(float p, float d, float r, float l, bool n) {
	return bnoisegl::bp_perlin_fbm_1(p, d, r, l, n);
}
template <>
inline float perlin_fbm<Vector2>(Vector2 p, float d, float r, float l, bool n) {
	return bnoisegl::bp_perlin_fbm_2(detail::gl(p), d, r, l, n);
}
template <>
inline float perlin_fbm<Vector3>(Vector3 p, float d, float r, float l, bool n) {
	return bnoisegl::bp_perlin_fbm_3(detail::gl(p), d, r, l, n);
}
template <>
inline float perlin_fbm<Vector4>(Vector4 p, float d, float r, float l, bool n) {
	return bnoisegl::bp_perlin_fbm_4(detail::gl(p), d, r, l, n);
}

template <typename T>
float perlin_select(T p, float detail_v, float roughness, float lacunarity, float offset,
		float gain, int type, bool normalize);

template <>
inline float perlin_select<float>(float p, float d, float r, float l, float o, float g, int t,
		bool n) {
	return bnoisegl::bp_perlin_select_1(p, d, r, l, o, g, t, n);
}
template <>
inline float perlin_select<Vector2>(Vector2 p, float d, float r, float l, float o, float g, int t,
		bool n) {
	return bnoisegl::bp_perlin_select_2(detail::gl(p), d, r, l, o, g, t, n);
}
template <>
inline float perlin_select<Vector3>(Vector3 p, float d, float r, float l, float o, float g, int t,
		bool n) {
	return bnoisegl::bp_perlin_select_3(detail::gl(p), d, r, l, o, g, t, n);
}
template <>
inline float perlin_select<Vector4>(Vector4 p, float d, float r, float l, float o, float g, int t,
		bool n) {
	return bnoisegl::bp_perlin_select_4(detail::gl(p), d, r, l, o, g, t, n);
}

template <typename T>
float perlin_fractal_distorted(T p, float detail_v, float roughness, float lacunarity,
		float offset, float gain, float distortion, int type, bool normalize);

template <>
inline float perlin_fractal_distorted<float>(float p, float d, float r, float l, float o, float g,
		float dist, int t, bool n) {
	return bnoisegl::bp_perlin_fractal_distorted_1(p, d, r, l, o, g, dist, t, n);
}
template <>
inline float perlin_fractal_distorted<Vector2>(Vector2 p, float d, float r, float l, float o,
		float g, float dist, int t, bool n) {
	return bnoisegl::bp_perlin_fractal_distorted_2(detail::gl(p), d, r, l, o, g, dist, t, n);
}
template <>
inline float perlin_fractal_distorted<Vector3>(Vector3 p, float d, float r, float l, float o,
		float g, float dist, int t, bool n) {
	return bnoisegl::bp_perlin_fractal_distorted_3(detail::gl(p), d, r, l, o, g, dist, t, n);
}
template <>
inline float perlin_fractal_distorted<Vector4>(Vector4 p, float d, float r, float l, float o,
		float g, float dist, int t, bool n) {
	return bnoisegl::bp_perlin_fractal_distorted_4(detail::gl(p), d, r, l, o, g, dist, t, n);
}

// The Colour output. Overloads rather than specialisations, because that is
// what the callers write and what Blender's own source does.
inline Vector3 perlin_float3_fractal_distorted(float p, float d, float r, float l, float o,
		float g, float dist, int t, bool n) {
	return detail::gd(bnoisegl::bp_perlin_float3_fractal_distorted(p, d, r, l, o, g, dist, t, n));
}
inline Vector3 perlin_float3_fractal_distorted(Vector2 p, float d, float r, float l, float o,
		float g, float dist, int t, bool n) {
	return detail::gd(
			bnoisegl::bp_perlin_float3_fractal_distorted(detail::gl(p), d, r, l, o, g, dist, t, n));
}
inline Vector3 perlin_float3_fractal_distorted(Vector3 p, float d, float r, float l, float o,
		float g, float dist, int t, bool n) {
	return detail::gd(
			bnoisegl::bp_perlin_float3_fractal_distorted(detail::gl(p), d, r, l, o, g, dist, t, n));
}
inline Vector3 perlin_float3_fractal_distorted(Vector4 p, float d, float r, float l, float o,
		float g, float dist, int t, bool n) {
	return detail::gd(
			bnoisegl::bp_perlin_float3_fractal_distorted(detail::gl(p), d, r, l, o, g, dist, t, n));
}

// --- the layers below, for probes and gates ---

inline float perlin_noise(float p) { return bnoisegl::bp_perlin_noise(p); }
inline float perlin_noise(const Vector2 &p) { return bnoisegl::bp_perlin_noise(detail::gl(p)); }
inline float perlin_noise(const Vector3 &p) { return bnoisegl::bp_perlin_noise(detail::gl(p)); }
inline float perlin_noise(const Vector4 &p) { return bnoisegl::bp_perlin_noise(detail::gl(p)); }

inline float perlin_signed(float p) { return bnoisegl::bp_perlin_signed(p); }
inline float perlin_signed(const Vector2 &p) { return bnoisegl::bp_perlin_signed(detail::gl(p)); }
inline float perlin_signed(const Vector3 &p) { return bnoisegl::bp_perlin_signed(detail::gl(p)); }
inline float perlin_signed(const Vector4 &p) { return bnoisegl::bp_perlin_signed(detail::gl(p)); }

} // namespace bperlin
} // namespace godot

#endif // PROC_NODES_BLENDER_PERLIN_H