#ifndef PROC_NODES_MATH_NODES_H
#define PROC_NODES_MATH_NODES_H
#include "../graph/proc_node.h"
namespace godot {
// Math operations for scalars
// The operation is BLENDER'S DNA enum value, not one of our own -- see
// src/shared/blender_math_ops.h. There used to be a MathOp enum here with its
// own numbering and a hand-written table in the serializer reconciling the two,
// which listed 21 of Blender's 41 operations and turned the other 20 into ADD.
class MathNode : public ProcNode {
GDCLASS(MathNode, ProcNode);
protected:
static void _bind_methods();
int operation = 0; // bmath::MATH_*
public:
StringName get_node_type() const override { return "Math"; }
String get_node_title() const override { return "Math"; }
String get_node_category() const override { return "Math"; }
// The GLSL was a DIFFERENT, smaller operation set under the old numbering,
// so after renumbering to Blender's it would have computed the wrong
// operation with nothing to say so. It is all 41 now, against the same
// numbering bmath::math_op uses, and every one of them is compared to that
// function on a real device by the standalone -lvulkan gate.
bool supports_gpu() const override { return true; }
String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;
String compile_glsl_prelude(int p_node_idx) const override;
void set_operation(int p_op) { operation = p_op; }
int get_operation() const { return operation; }
void execute(ProcEvalContext &p_ctx, int p_node_idx) override;
MathNode();
};
// Vector math operations
// Blender's DNA enum value, as for MathNode. The old table listed 11 of
// Blender's 30 vector operations.
class VectorMathNode : public ProcNode {
GDCLASS(VectorMathNode, ProcNode);
protected:
static void _bind_methods();
int operation = 0; // bmath::VEC_*
public:
StringName get_node_type() const override { return "VectorMath"; }
String get_node_title() const override { return "Vector Math"; }
String get_node_category() const override { return "Math"; }
// The GLSL implements a DIFFERENT, smaller operation set under the old
// numbering. After renumbering to Blender's it would compute the wrong
// operation on the GPU with nothing to say so. Off until it is ported.
bool supports_gpu() const override { return true; }
String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;
String compile_glsl_prelude(int p_node_idx) const override;
void set_operation(int p_op) { operation = p_op; }
int get_operation() const { return operation; }
void execute(ProcEvalContext &p_ctx, int p_node_idx) override;
VectorMathNode();
};
// Combine XYZ — 3 floats → Vector3
class CombineXYZNode : public ProcNode {
GDCLASS(CombineXYZNode, ProcNode);
protected:
static void _bind_methods();
public:
StringName get_node_type() const override { return "CombineXYZ"; }
String get_node_title() const override { return "Combine XYZ"; }
String get_node_category() const override { return "Math"; }
// Emits a real statement through GlslEmitContext now; it used to return an
// expression FRAGMENT referencing identifiers nothing declared, which made
// every shader using it fail to compile.
bool supports_gpu() const override { return true; }
void execute(ProcEvalContext &p_ctx, int p_node_idx) override;
String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;
CombineXYZNode();
};
// Separate XYZ — Vector3 → 3 floats
class SeparateXYZNode : public ProcNode {
GDCLASS(SeparateXYZNode, ProcNode);
protected:
static void _bind_methods();
public:
StringName get_node_type() const override { return "SeparateXYZ"; }
String get_node_title() const override { return "Separate XYZ"; }
String get_node_category() const override { return "Math"; }
// Emits a real statement through GlslEmitContext now; it used to return an
// expression FRAGMENT referencing identifiers nothing declared, which made
// every shader using it fail to compile.
bool supports_gpu() const override { return true; }
void execute(ProcEvalContext &p_ctx, int p_node_idx) override;
String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;
SeparateXYZNode();
};
// Boolean math operations
enum class BooleanMathOp : int32_t {
And = 0,
Or,
Not,
Xor,
Nand,
Nor,
Xnor,
};
class BooleanMathNode : public ProcNode {
GDCLASS(BooleanMathNode, ProcNode);
protected:
static void _bind_methods();
BooleanMathOp operation = BooleanMathOp::And;
public:
StringName get_node_type() const override { return "BooleanMath"; }
String get_node_title() const override { return "Boolean Math"; }
String get_node_category() const override { return "Math"; }
// Emits a real statement through GlslEmitContext now; it used to return an
// expression FRAGMENT referencing identifiers nothing declared, which made
// every shader using it fail to compile.
bool supports_gpu() const override { return true; }
void set_operation(int p_op) { operation = static_cast<BooleanMathOp>(p_op); }
int get_operation() const { return static_cast<int>(operation); }
void execute(ProcEvalContext &p_ctx, int p_node_idx) override;
String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;
BooleanMathNode();
};
// Compare operations
enum class CompareOp : int32_t {
Less = 0,
LessEqual,
Greater,
GreaterEqual,
Equal,
NotEqual,
};
class CompareNode : public ProcNode {
GDCLASS(CompareNode, ProcNode);
protected:
static void _bind_methods();
CompareOp operation = CompareOp::Less;
public:
StringName get_node_type() const override { return "Compare"; }
String get_node_title() const override { return "Compare"; }
String get_node_category() const override { return "Math"; }
// Emits a real statement through GlslEmitContext now; it used to return an
// expression FRAGMENT referencing identifiers nothing declared, which made
// every shader using it fail to compile.
bool supports_gpu() const override { return true; }
void set_operation(int p_op) { operation = static_cast<CompareOp>(p_op); }
int get_operation() const { return static_cast<int>(operation); }
void execute(ProcEvalContext &p_ctx, int p_node_idx) override;
String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;
CompareNode();
};
// Clamp — clamp float to range
class ClampNode : public ProcNode {
GDCLASS(ClampNode, ProcNode);
protected:
static void _bind_methods();
public:
StringName get_node_type() const override { return "Clamp"; }
String get_node_title() const override { return "Clamp"; }
String get_node_category() const override { return "Math"; }
// Emits a real statement through GlslEmitContext now; it used to return an
// expression FRAGMENT referencing identifiers nothing declared, which made
// every shader using it fail to compile.
bool supports_gpu() const override { return true; }
void execute(ProcEvalContext &p_ctx, int p_node_idx) override;
String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;
ClampNode();
};
// Map Range — remap value from one range to another
// Map Range — Blender's, which is twelve inputs (a float set and a vector set),
// two outputs, four interpolations and a clamp checkbox. Ours was five inputs,
// one output, one interpolation and no clamp, so a clamped window passed values
// straight through instead of holding them at the end of the range.
class MapRangeNode : public ProcNode {
GDCLASS(MapRangeNode, ProcNode);
int data_type = 0; // 0 = FLOAT, 1 = FLOAT_VECTOR
int interpolation_type = 0; // LINEAR, STEPPED, SMOOTHSTEP, SMOOTHERSTEP
bool clamp_result = false;
protected:
static void _bind_methods();
public:
StringName get_node_type() const override { return "MapRange"; }
String get_node_title() const override { return "Map Range"; }
String get_node_category() const override { return "Math"; }
// The GLSL is the old single-interpolation, unclamped arithmetic.
bool supports_gpu() const override { return true; }
String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;
String compile_glsl_prelude(int p_node_idx) const override;
void set_data_type(int p_t) { data_type = p_t; }
int get_data_type() const { return data_type; }
void set_interpolation_type(int p_t) { interpolation_type = p_t; }
int get_interpolation_type() const { return interpolation_type; }
void set_clamp(bool p_c) { clamp_result = p_c; }
bool get_clamp() const { return clamp_result; }
void execute(ProcEvalContext &p_ctx, int p_node_idx) override;
MapRangeNode();
};
// Mix — linear interpolation between two floats
// Mix — Blender's, which is FOUR data types over ten input sockets and four
// output ones, with a nineteen-mode colour blend behind the RGBA one.
//
// Ours was a single float lerp with three sockets and no properties, so a
// colour Mix came across as MIX whatever it said in Blender, a vector Mix read
// the float sockets, and the non-uniform vector factor did not exist.
class MixNode : public ProcNode {
GDCLASS(MixNode, ProcNode);
// THE RNA ENUM VALUE, which for this property is NOT the DNA socket-type
// enum: RNA reports FLOAT 0, VECTOR 1, RGBA 2, ROTATION 14, while SOCK_* is
// 1, 2, 3, 11. The serializer reads the RNA value off the node, so this
// side must read RNA's numbering too. Measured, not assumed -- Math's
// `operation` RNA values DO equal its DNA ones, so the two cannot be
// inferred from each other.
int data_type = 0;
int blend_type = 0; // bramp::RAMP_*
bool clamp_factor = true;
bool clamp_result = false;
int factor_mode = 0; // 0 UNIFORM, 1 NON_UNIFORM
protected:
static void _bind_methods();
public:
StringName get_node_type() const override { return "Mix"; }
String get_node_title() const override { return "Mix"; }
String get_node_category() const override { return "Math"; }
// ALL FOUR CONFIGURATIONS since 2026-09-05. It used to answer per
// configuration, true for Float and Vector only, because Colour needs
// ramp_blend's nineteen modes with the HSV and HSL conversions under them
// and Rotation needs a quaternion slerp. Both exist now:
// blender_ramp_blend.inc for the first, and a port of godot's own
// Quaternion::slerp for the second.
bool supports_gpu() const override { return true; }
String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;
String compile_glsl_prelude(int p_node_idx) const override;
void set_data_type(int p_t) { data_type = p_t; }
int get_data_type() const { return data_type; }
void set_blend_type(int p_t) { blend_type = p_t; }
int get_blend_type() const { return blend_type; }
void set_clamp_factor(bool p_c) { clamp_factor = p_c; }
bool get_clamp_factor() const { return clamp_factor; }
void set_clamp_result(bool p_c) { clamp_result = p_c; }
bool get_clamp_result() const { return clamp_result; }
void set_factor_mode(int p_m) { factor_mode = p_m; }
int get_factor_mode() const { return factor_mode; }
void execute(ProcEvalContext &p_ctx, int p_node_idx) override;
MixNode();
};
// ColorRamp — maps a float onto a gradient, as Blender's Color Ramp does.
//
// THE STOPS ARE THE NODE'S DATA, and they are held FLAT: five floats per stop,
// position then r, g, b, a. One array rather than a parallel positions/colours
// pair because the two must not be able to disagree in length, and because a
// flat float array is what survives both a `.tres` and the bridge's JSON
// without a bespoke encoding on either side.
class ColorRampNode : public ProcNode {
GDCLASS(ColorRampNode, ProcNode);
PackedFloat32Array ramp_stops;
int interpolation = 0; // COLBAND_INTERP_*, Blender's DNA values
int color_mode = 0; // COLBAND_BLEND_*
int hue_interpolation = 0; // COLBAND_HUE_*
protected:
static void _bind_methods();
public:
StringName get_node_type() const override { return "ColorRamp"; }
String get_node_title() const override { return "Color Ramp"; }
String get_node_category() const override { return "Math"; }
void set_ramp_stops(const PackedFloat32Array &p_stops);
PackedFloat32Array get_ramp_stops() const { return ramp_stops; }
void set_interpolation(int p_i) { interpolation = p_i; }
int get_interpolation() const { return interpolation; }
void set_color_mode(int p_m) { color_mode = p_m; }
int get_color_mode() const { return color_mode; }
void set_hue_interpolation(int p_h) { hue_interpolation = p_h; }
int get_hue_interpolation() const { return hue_interpolation; }
void execute(ProcEvalContext &p_ctx, int p_node_idx) override;
ColorRampNode();
};
// FloatCurve — maps a float through a curve, as Blender's Float Curve does.
//
// THE CURVE IS THE NODE'S DATA, held as three parallel arrays so one shape
// serves this node and RGB Curves, which has four curves rather than one:
//
// curve_points two floats per point, x then y, all curves concatenated
// curve_handles one per point, Blender's handle_type FLAG value
// curve_counts points per curve -- length 1 here, 4 on RGB Curves
//
// A count array rather than one property per curve: eleven named properties for
// RGB Curves is worse to read and cannot be decoded by shared code.
class FloatCurveNode : public ProcNode {
GDCLASS(FloatCurveNode, ProcNode);
PackedFloat32Array curve_points;
PackedInt32Array curve_handles;
PackedInt32Array curve_counts;
int extend = 16; // CUMA_EXTEND_EXTRAPOLATE, Blender's default
bool use_clip = true; // CUMA_DO_CLIP, on for a fresh Blender node
PackedFloat32Array clip_rect; // xmin, ymin, xmax, ymax
protected:
static void _bind_methods();
public:
StringName get_node_type() const override { return "FloatCurve"; }
String get_node_title() const override { return "Float Curve"; }
String get_node_category() const override { return "Math"; }
void set_curve_points(const PackedFloat32Array &p) { curve_points = p; }
PackedFloat32Array get_curve_points() const { return curve_points; }
void set_curve_handles(const PackedInt32Array &p) { curve_handles = p; }
PackedInt32Array get_curve_handles() const { return curve_handles; }
void set_curve_counts(const PackedInt32Array &p) { curve_counts = p; }
PackedInt32Array get_curve_counts() const { return curve_counts; }
void set_extend(int p) { extend = p; }
int get_extend() const { return extend; }
void set_use_clip(bool p) { use_clip = p; }
bool get_use_clip() const { return use_clip; }
void set_clip_rect(const PackedFloat32Array &p) { clip_rect = p; }
PackedFloat32Array get_clip_rect() const { return clip_rect; }
void execute(ProcEvalContext &p_ctx, int p_node_idx) override;
FloatCurveNode();
};
// RGBCurves — Blender's RGB Curves. FOUR curves, and the fourth is not a
// channel: cm[0..2] are R, G and B and cm[3] is the COMBINED curve, which runs
// FIRST and feeds each channel's own curve. Same three-array encoding as
// FloatCurveNode, with curve_counts holding four entries instead of one.
class RGBCurvesNode : public ProcNode {
GDCLASS(RGBCurvesNode, ProcNode);
PackedFloat32Array curve_points;
PackedInt32Array curve_handles;
PackedInt32Array curve_counts;
int extend = 16; // CUMA_EXTEND_EXTRAPOLATE
bool use_clip = true;
PackedFloat32Array clip_rect;
protected:
static void _bind_methods();
public:
StringName get_node_type() const override { return "RGBCurves"; }
String get_node_title() const override { return "RGB Curves"; }
String get_node_category() const override { return "Math"; }
void set_curve_points(const PackedFloat32Array &p) { curve_points = p; }
PackedFloat32Array get_curve_points() const { return curve_points; }
void set_curve_handles(const PackedInt32Array &p) { curve_handles = p; }
PackedInt32Array get_curve_handles() const { return curve_handles; }
void set_curve_counts(const PackedInt32Array &p) { curve_counts = p; }
PackedInt32Array get_curve_counts() const { return curve_counts; }
void set_extend(int p) { extend = p; }
int get_extend() const { return extend; }
void set_use_clip(bool p) { use_clip = p; }
bool get_use_clip() const { return use_clip; }
void set_clip_rect(const PackedFloat32Array &p) { clip_rect = p; }
PackedFloat32Array get_clip_rect() const { return clip_rect; }
void execute(ProcEvalContext &p_ctx, int p_node_idx) override;
RGBCurvesNode();
};
} // namespace godot
#endif // PROC_NODES_MATH_NODES_H