#ifndef PROC_NODES_GLSL_EMIT_CONTEXT_H
#define PROC_NODES_GLSL_EMIT_CONTEXT_H
#include "../graph/socket_value.h"
#include <godot_cpp/variant/string.hpp>
namespace godot {
class ProcNodeGraph;
// HOW A NODE'S GLSL GETS ITS INPUTS.
//
// The emitter used to be a concatenator: it appended each node's snippet in
// graph-index order and replaced `pc.paramN` with THAT NODE'S OWN socket
// defaults. `grep -c connections gpu_compiler.cpp` was 0 -- the graph's edges
// were never read -- so values moved between nodes only where two of them
// agreed on a hard-coded local name (gn_height, gn_slope, en_erosion). A node
// with inputs from anywhere else could not be expressed at all, which is why
// nine node types have no GLSL and writing their arithmetic would have produced
// nodes that compute on their own defaults and are silently constant.
//
// This is the missing half. A node asks the context for an input and gets an
// EXPRESSION: the producer's variable when the socket is connected, the
// socket's own default as a literal when it is not.
//
// in(node, socket) "n3_s0" or "0.150000000"
// out(node, socket) "n7_s0" the variable to assign
// declare(node, socket) "float n7_s0" that variable's declaration
//
// GEOMETRY IS NOT A VALUE HERE. A density graph is a displacement pipeline:
// nodes read and move the shared `pos`, and the density is how far pos.y ended
// up from the query height. That channel stays implicit, because it is what
// every shipping terrain graph is built on. Only non-geometry sockets get
// variables.
struct GlslEmitContext {
const ProcNodeGraph *graph = nullptr;
// Graph node indices emitted into THIS shader. A connected socket whose
// producer is in here resolves to that producer's local; one whose producer
// is not has to keep coming through the push-constant block, because the
// local it would name does not exist in this translation unit.
Vector<int> emitted_nodes;
bool is_emitted(int p_node_idx) const {
for (int i = 0; i < emitted_nodes.size(); i++) {
if (emitted_nodes[i] == p_node_idx) {
return true;
}
}
return false;
}
// Wrap integer (octave/detail) parameters in min(N, pn_max_octaves). The
// density path passes true; the batch path does not have that uniform.
bool clamp_octaves = false;
// WHICH PATH IS BEING EMITTED, and it is not cosmetic.
//
// compile_batch produces a compute shader with a push-constant block that
// GraphExecutor refills on every dispatch, so a scalar a node reads from a
// socket must arrive through `pc.paramN` -- the pipeline is cached in the
// ExecutionPlan and set_input_default() emits no `changed`, so a literal
// baked into that shader would freeze at the value it had when the plan was
// built. compile_to_function produces a standalone `float proc_density`
// with NO push-constant block at all, so the same `pc.paramN` there is a
// reference to something that does not exist.
//
// The two used to be reconciled by compile_to_function TEXTUALLY replacing
// `pc.paramN` with socket N's default. That is a different convention from
// the batch path's, where N is a push-constant SLOT the executor packs by
// hand, and the two collided on every node that used both. Measured
// 2026-09-06 on the emitted density text:
//
// Displace every argument shifted one socket, and `* pc.param0`
// became `* 0.0` (socket 0 is Geometry, which has no
// literal) -- the displacement multiplied by zero
// SimplexNoise the same shift, plus `pc.param3` left VERBATIM because
// the node has only three sockets, in a function where `pc`
// is not declared: the shader cannot compile
// SetPosition `pos += vec3(0.0, 0.0, 0.0)`
//
// So the choice is made once, here, and `param()` below is the only place a
// node has to know about it.
bool push_constants = false;
// The expression for an input socket. Connected -> the producer's variable,
// coerced if the types differ; unconnected -> the socket default as a
// literal.
String in(int p_node_idx, int p_socket) const;
// An input socket that the BATCH path carries in the push-constant block.
//
// push_constants -> "pc.param2", or "vec3(pc.param0, pc.param1, pc.param2)"
// otherwise -> in(node, socket), the same as any other input
//
// The slot is computed by walking the node's own get_push_constant_sockets()
// and summing the widths, and GraphExecutor packs by walking the SAME list.
// One list, so the slot a node reads and the slot the executor fills cannot
// drift -- which they had, on Set Position, for as long as its sockets have
// matched Blender's (see graph_executor.cpp).
//
// A socket the node does not declare falls through to in(): a node with no
// map at all -- Simplex Noise is one, it is not a geometry node and never
// reaches compile_batch -- behaves exactly as if this did not exist.
String param(int p_node_idx, int p_socket) const;
// The variable an output socket is written to, and its declaration.
String out(int p_node_idx, int p_socket) const;
String declare(int p_node_idx, int p_socket) const;
// GLSL spelling of a socket type. String and Geometry have none -- a node
// carrying either cannot be GPU-capable, and asking returns "" so a caller
// cannot quietly emit a broken type name.
static String type_name(SocketType p_type);
// A literal for a socket's default value, at the precision the shader needs.
// NINE digits: four is fine for a frequency and destroys anything small --
// a y-scale of 5/21000 = 0.000238 truncated to 0.0002 is a 16% error in the
// vertical mapping, which moved a terrain surface ~1770 m.
String literal(int p_node_idx, int p_socket) const;
// The same formatter, for a value that is not on an INPUT socket. Group
// Input's parameters live on its OUTPUTS -- execute() writes them from
// output_defs[i].default_value -- and spelling them a second time here is
// how the two paths would come to disagree about a number neither of them
// computes.
String literal_value(SocketType p_type, const Variant &p_value) const;
};
} // namespace godot
#endif // PROC_NODES_GLSL_EMIT_CONTEXT_H