OverviewFilesCommitsIssuesDocsReleasesPeers

proc-nodes / src / nodes / input_nodes.h

295 lines · c ref:
#ifndef PROC_NODES_INPUT_NODES_H
#define PROC_NODES_INPUT_NODES_H

#include "../graph/proc_node.h"

namespace godot {

// Group Input — the graph's INTERFACE: the caller's geometry, and every
// parameter the graph exposes.
//
// A Blender geometry tree is only half of what evaluates; the modifier instance
// holds the other half. Subdivide level, cone power, peak height -- each is a
// link from a Group Input output socket, and its VALUE lives on the modifier.
// This node used to declare exactly ONE output, Geometry, no matter what the
// graph said, so every parameter link addressed a socket that did not exist and
// the whole value chain was inert while the geometry chain flowed.
//
// `interface` is a bound property, so a graph saved as a resource keeps its
// parameters, the same way input_defaults does for ordinary nodes.
class GroupInputNode : public ProcNode {
	GDCLASS(GroupInputNode, ProcNode);

	Array interface_spec;

protected:
	static void _bind_methods();

public:
	StringName get_node_type() const override { return "GroupInput"; }
	String get_node_title() const override { return "Group Input"; }
	String get_node_category() const override { return "Input"; }

	// [{ name: String, type: int (SocketType), value: Variant }, ...].
	// An empty spec leaves the single Geometry output a bare graph expects.
	void set_interface(const Array &p_spec);
	Array get_interface() const { return interface_spec; }

	// ⛔ TRANSPARENT USED TO MEAN "EMITS NOTHING", and that is what broke.
	//
	// gpu_transparent() was written when Group Input had ONE output, the
	// Geometry the caller hands in -- which the emitted shader carries
	// implicitly as `pos`, so emitting nothing for the node was right. Since
	// the node started carrying the modifier's exposed PARAMETERS on its other
	// outputs, a transparent Group Input means every one of those is a variable
	// the shader references and nothing declares:
	//
	//     error: 'n0_s19' : undeclared identifier      (x16, the bridged
	//     error: 'n0_s6'  : undeclared identifier       voronoi mountain)
	//
	// compile_to_glsl_function skips transparent nodes outright, so the density
	// shader for EVERY bridged Blender graph with a group interface -- which is
	// every real modifier graph, since that is how a knob is exposed -- failed
	// to compile. The refusal machinery could not see it: each node reported GPU
	// support and emitted real GLSL, and the missing text was the node that had
	// been excused from emitting.
	//
	// Reading the value from output_defs is what execute() does, so the two
	// paths take the same number from the same place rather than agreeing by
	// coincidence.
	// STILL TRANSPARENT, because the BATCH path's runs are built from
	// non-transparent nodes and putting Group Input into them moved the
	// boundaries: gpu_batch_gate went red with SetPosition reported 'cpu'.
	// Transparent now means "may emit nothing", not "emits nothing" --
	// compile_to_glsl_function includes these and accepts an empty result from
	// them, which is how Group Input can declare its parameters for the density
	// shader without joining a batch.
	bool gpu_transparent() const override { return true; }
	bool supports_gpu() const override { return true; }
	String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;

	void execute(ProcEvalContext &p_ctx, int p_node_idx) override;

	GroupInputNode();
};

// Group Output — configurable inputs, marks graph output
class GroupOutputNode : public ProcNode {
	GDCLASS(GroupOutputNode, ProcNode);

protected:
	static void _bind_methods();

public:
	StringName get_node_type() const override { return "GroupOutput"; }
	String get_node_title() const override { return "Group Output"; }
	String get_node_category() const override { return "Input"; }

	// The graph's OUTPUT INTERFACE. Blender's tree says what it produces
	// through its output sockets, and this node carries them. It used to be
	// built with one fixed Geometry input whatever the tree declared -- the
	// mirror of the Group Input bug #62 found -- so a tree returning a float
	// had nowhere to put it, and the GLSL emitter could not be told what the
	// graph's result was.
	void set_interface(const Array &p_spec);

	bool gpu_transparent() const override { return true; }

	void execute(ProcEvalContext &p_ctx, int p_node_idx) override;

	GroupOutputNode();
};

// Value — outputs a single float
class ValueNode : public ProcNode {
	GDCLASS(ValueNode, ProcNode);

protected:
	static void _bind_methods();

	float value = 0.0f;

public:
	StringName get_node_type() const override { return "Value"; }
	String get_node_title() const override { return "Value"; }
	String get_node_category() const override { return "Input"; }
	bool supports_gpu() const override { return true; }
	// A CONSTANT THAT EMITTED NOTHING. These five claimed GPU support with no
	// compile_glsl at all, so the emitter skipped them silently and any node
	// reading one referenced a variable that was never declared -- measured on a
	// bridged graph whose Group Output returned `n2_s0` with nothing assigning
	// it. Their value is a node PROPERTY rather than a socket, which is why it
	// cannot come from the context's input resolution.
	String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;

	void set_value(float p_val) { value = p_val; }
	float get_value() const { return value; }

	void execute(ProcEvalContext &p_ctx, int p_node_idx) override;

	ValueNode();
};

// Vector — outputs a Vector3
class VectorNode : public ProcNode {
	GDCLASS(VectorNode, ProcNode);

protected:
	static void _bind_methods();

	Vector3 value;

public:
	StringName get_node_type() const override { return "Vector"; }
	String get_node_title() const override { return "Vector"; }
	String get_node_category() const override { return "Input"; }
	bool supports_gpu() const override { return true; }
	// A CONSTANT THAT EMITTED NOTHING. These five claimed GPU support with no
	// compile_glsl at all, so the emitter skipped them silently and any node
	// reading one referenced a variable that was never declared -- measured on a
	// bridged graph whose Group Output returned `n2_s0` with nothing assigning
	// it. Their value is a node PROPERTY rather than a socket, which is why it
	// cannot come from the context's input resolution.
	String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;

	void set_value(const Vector3 &p_val) { value = p_val; }
	Vector3 get_value() const { return value; }

	void execute(ProcEvalContext &p_ctx, int p_node_idx) override;

	VectorNode();
};

// Integer — outputs a single integer
class IntegerNode : public ProcNode {
	GDCLASS(IntegerNode, ProcNode);

protected:
	static void _bind_methods();

	int value = 0;

public:
	StringName get_node_type() const override { return "Integer"; }
	String get_node_title() const override { return "Integer"; }
	String get_node_category() const override { return "Input"; }
	bool supports_gpu() const override { return true; }
	// A CONSTANT THAT EMITTED NOTHING. These five claimed GPU support with no
	// compile_glsl at all, so the emitter skipped them silently and any node
	// reading one referenced a variable that was never declared -- measured on a
	// bridged graph whose Group Output returned `n2_s0` with nothing assigning
	// it. Their value is a node PROPERTY rather than a socket, which is why it
	// cannot come from the context's input resolution.
	String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;

	void set_value(int p_val) { value = p_val; }
	int get_value() const { return value; }

	void execute(ProcEvalContext &p_ctx, int p_node_idx) override;

	IntegerNode();
};

// Boolean — outputs a single boolean
class BooleanNode : public ProcNode {
	GDCLASS(BooleanNode, ProcNode);

protected:
	static void _bind_methods();

	bool value = false;

public:
	StringName get_node_type() const override { return "Boolean"; }
	String get_node_title() const override { return "Boolean"; }
	String get_node_category() const override { return "Input"; }
	bool supports_gpu() const override { return true; }
	// A CONSTANT THAT EMITTED NOTHING. These five claimed GPU support with no
	// compile_glsl at all, so the emitter skipped them silently and any node
	// reading one referenced a variable that was never declared -- measured on a
	// bridged graph whose Group Output returned `n2_s0` with nothing assigning
	// it. Their value is a node PROPERTY rather than a socket, which is why it
	// cannot come from the context's input resolution.
	String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;

	void set_value(bool p_val) { value = p_val; }
	bool get_value() const { return value; }

	void execute(ProcEvalContext &p_ctx, int p_node_idx) override;

	BooleanNode();
};

// Color — outputs a single Color
class ColorNode : public ProcNode {
	GDCLASS(ColorNode, ProcNode);

protected:
	static void _bind_methods();

	Color value = Color(1, 1, 1, 1);

public:
	StringName get_node_type() const override { return "Color"; }
	String get_node_title() const override { return "Color"; }
	String get_node_category() const override { return "Input"; }
	bool supports_gpu() const override { return true; }
	// A CONSTANT THAT EMITTED NOTHING. These five claimed GPU support with no
	// compile_glsl at all, so the emitter skipped them silently and any node
	// reading one referenced a variable that was never declared -- measured on a
	// bridged graph whose Group Output returned `n2_s0` with nothing assigning
	// it. Their value is a node PROPERTY rather than a socket, which is why it
	// cannot come from the context's input resolution.
	String compile_glsl(const GlslEmitContext &p_ctx, int p_node_idx) const override;

	void set_value(const Color &p_val) { value = p_val; }
	Color get_value() const { return value; }

	void execute(ProcEvalContext &p_ctx, int p_node_idx) override;

	ColorNode();
};

// String — outputs a single String
class StringNode : public ProcNode {
	GDCLASS(StringNode, ProcNode);

protected:
	static void _bind_methods();

	String value;

public:
	StringName get_node_type() const override { return "String"; }
	String get_node_title() const override { return "String"; }
	String get_node_category() const override { return "Input"; }

	void set_value(const String &p_val) { value = p_val; }
	String get_value() const { return value; }

	void execute(ProcEvalContext &p_ctx, int p_node_idx) override;

	StringNode();
};

// ObjectInfo — provides object metadata (placeholder)
class ObjectInfoNode : public ProcNode {
	GDCLASS(ObjectInfoNode, ProcNode);

protected:
	static void _bind_methods();

public:
	StringName get_node_type() const override { return "ObjectInfo"; }
	String get_node_title() const override { return "Object Info"; }
	String get_node_category() const override { return "Input"; }

	void execute(ProcEvalContext &p_ctx, int p_node_idx) override;

	ObjectInfoNode();
};

} // namespace godot

#endif // PROC_NODES_INPUT_NODES_H