| … | … | @@ -4,7 +4,7 @@ A procedural geometry node graph system for Godot 4, implemented as a GDExtensio |
| 4 | 4 | |
| 5 | 5 | ## Features |
| 6 | 6 | |
| 7 | | - **59 node types** across 11 categories (mesh primitives, math, geometry ops, mesh ops, curves, points, instances, attributes, noise, zones, utility) |
| 7 | - **122 node types** across 14 categories (mesh primitives, math, geometry ops, mesh ops, curves, points, instances, attributes, noise, volume, zones, utility) |
| 8 | 8 | - **GPU compute pipeline** using Vulkan compute shaders via Godot's RenderingDevice API, with automatic CPU fallback |
| 9 | 9 | - **Zone system** with Repeat, ForEach, and Simulation zones for iterative and per-element operations |
| 10 | 10 | - **Graph serialization** to Godot `.tres` resources via custom `_get_property_list`/`_set`/`_get`, with delta-only default storage |
| … | … | @@ -65,91 +65,24 @@ Automatic type conversions are applied when reading inputs (e.g., Float to Vecto |
| 65 | 65 | |
| 66 | 66 | ## Node Catalog |
| 67 | 67 | |
| 68 | | ### Input (4 nodes) |
| 69 | | - **Group Input** -- Configurable graph entry point (Geometry output) |
| 70 | | - **Group Output** -- Configurable graph exit point (Geometry passthrough) |
| 71 | | - **Value** -- Outputs a single float constant (GPU) |
| 72 | | - **Vector** -- Outputs a Vector3 constant (GPU) |
| 73 | | |
| 74 | | ### Math (4 nodes) |
| 75 | | - **Math** -- 21 scalar operations: Add, Subtract, Multiply, Divide, Power, Sqrt, Abs, Min, Max, Modulo, Sin, Cos, Tan, Atan2, Floor, Ceil, Round, Fract, Clamp, Lerp, Smoothstep, Negate (GPU + GLSL) |
| 76 | | - **Vector Math** -- 11 vector operations: Add, Subtract, Multiply, Divide, Cross, Dot, Normalize, Length, Distance, Scale, Negate (GPU + GLSL) |
| 77 | | - **Combine XYZ** -- 3 floats to Vector3 (GPU) |
| 78 | | - **Separate XYZ** -- Vector3 to 3 floats (GPU) |
| 79 | | |
| 80 | | ### Mesh Primitives (7 nodes) |
| 81 | | - **Mesh Cube** -- Box with per-face normals and UVs (Size: Vector3) |
| 82 | | - **Mesh Grid** -- Subdivided XZ plane (Size X, Size Y, Vertices X, Vertices Y) (GPU) |
| 83 | | - **Mesh Circle** -- Polygon or edge ring (Vertices, Radius, Fill) |
| 84 | | - **Mesh Cylinder** -- Cylinder with optional caps (Vertices, Radius, Depth, Fill Caps) |
| 85 | | - **Mesh UV Sphere** -- Latitude/longitude sphere (Segments, Rings, Radius) |
| 86 | | - **Mesh Ico Sphere** -- Subdivided icosahedron (Radius, Subdivisions 0-6) |
| 87 | | - **Mesh Line** -- Point cloud along a line (Count, Start, End) (GPU) |
| 88 | | |
| 89 | | ### Geometry Operations (7 nodes) |
| 90 | | - **Transform Geometry** -- Full TRS transform on all components (Translation, Rotation, Scale) (GPU flag, CPU execution) |
| 91 | | - **Set Position** -- Offset all positions by a vector (GPU compute) |
| 92 | | - **Join Geometry** -- Merge two geometries into one |
| 93 | | - **Delete Geometry** -- Remove geometry based on boolean selection |
| 94 | | - **Merge by Distance** -- Weld nearby vertices (Distance threshold) |
| 95 | | - **Bounding Box** -- Compute AABB (outputs Min, Max, Center, Size) |
| 96 | | - **Separate Components** -- Split into Mesh, Curves, Point Cloud, Instances |
| 97 | | |
| 98 | | ### Mesh Operations (7 nodes) |
| 99 | | - **Flip Faces** -- Reverse triangle winding and negate normals |
| 100 | | - **Set Shade Smooth** -- Toggle smooth (area-weighted vertex normals) or flat (per-face normals with vertex splitting) |
| 101 | | - **Subdivide Mesh** -- Loop-style edge midpoint subdivision (Level 0-5) |
| 102 | | - **Scale Elements** -- Scale vertices around a center point |
| 103 | | - **Extrude Mesh** -- Duplicate and offset vertices along normals |
| 104 | | - **Merge by Distance** -- Weld vertices within distance threshold |
| 105 | | - **Dual Mesh** -- Topological dual (faces become vertices, vertices become faces) |
| 106 | | |
| 107 | | ### Curve Primitives (3 nodes) |
| 108 | | - **Curve Circle** -- Closed circular polyline (Radius, Resolution) |
| 109 | | - **Curve Line** -- 2-point polyline (Start, End) |
| 110 | | - **Bezier Segment** -- 4-point cubic bezier (Start, Handle 1, Handle 2, End) |
| 111 | | |
| 112 | | ### Curve Operations (5 nodes) |
| 113 | | - **Curve to Mesh** -- Sweep profile along path with Frenet framing, cap fill, and closed path support (Path, Profile, Resolution, Closed, Fill Caps) |
| 114 | | - **Curve to Points** -- Sample points along curve as point cloud (Count) |
| 115 | | - **Resample Curve** -- Evenly redistribute control points (Count) |
| 116 | | - **Set Curve Radius** -- Set uniform radius for all points (GPU) |
| 117 | | - **Trim Curve** -- Extract sub-range by parameter (Start 0-1, End 0-1) |
| 118 | | |
| 119 | | ### Point Operations (3 nodes) |
| 120 | | - **Points** -- Create N points at origin (Count) |
| 121 | | - **Points to Vertices** -- Convert point cloud to mesh vertices |
| 122 | | - **Distribute Points on Faces** -- Area-weighted random scatter on mesh triangles (Count, Seed) |
| 123 | | |
| 124 | | ### Instance Operations (5 nodes) |
| 125 | | - **Instance on Points** -- Place geometry at each point position (Points, Instance, Scale) |
| 126 | | - **Rotate Instances** -- Apply Euler rotation to all instance transforms (GPU) |
| 127 | | - **Scale Instances** -- Scale all instance transforms (GPU) |
| 128 | | - **Translate Instances** -- Offset all instance origins (GPU) |
| 129 | | - **Realize Instances** -- Flatten instance hierarchy into a single merged mesh |
| 130 | | |
| 131 | | ### Attribute Operations (7 nodes) |
| 132 | | - **Position** -- Read mesh centroid position (GPU) |
| 133 | | - **Normal** -- Read average mesh normal (GPU) |
| 134 | | - **Index** -- Output element index (GPU) |
| 135 | | - **Store Named Attribute** -- Write a value to a named attribute (name, domain, type) |
| 136 | | - **Named Attribute** -- Read a named attribute value |
| 137 | | - **Capture Attribute** -- Snapshot a scalar as "captured" attribute on all points |
| 138 | | - **Remove Named Attribute** -- Delete a named attribute from all domains |
| 139 | | - **Domain Size** -- Output element counts (Point, Face, Corner, Curve, Instance) |
| 140 | | |
| 141 | | ### Noise (2 nodes) |
| 142 | | - **Noise Texture** -- 3D simplex noise with fBM (Vector, Scale, Detail) outputs Value + Color (GPU + GLSL) |
| 143 | | - **Displace** -- Displace mesh vertices along normals using fBM noise (Strength, Scale, Detail, Roughness) (GPU compute) |
| 144 | | |
| 145 | | ### Utility (2 nodes) |
| 146 | | - **Switch** -- Select between two geometry inputs based on boolean |
| 147 | | - **Random Value** -- Hash-based pseudo-random float (Min, Max, Seed) (GPU) |
| 148 | | |
| 149 | | ### Zone Nodes (6 nodes) |
| 150 | | - **Repeat Zone Input/Output** -- Loop body N times, passing geometry between iterations |
| 151 | | - **ForEach Zone Input/Output** -- Execute body once per mesh vertex, collecting results |
| 152 | | - **Simulation Zone Input/Output** -- Frame-persistent state with delta time, cached across evaluations |
| 68 | 122 node classes, all deriving directly from `ProcNode`; 121 are creatable by name through |
| 69 | `GraphImporter`'s factory. |
| 70 | |
| 71 | | category | nodes | | category | nodes | |
| 72 | |---|---:|---|---|---:| |
| 73 | | Utility | 20 | | Mesh Primitives | 7 | |
| 74 | | Noise | 13 | | Zones | 6 | |
| 75 | | Math | 12 | | Volume | 6 | |
| 76 | | Input | 12 | | Instances | 5 | |
| 77 | | Geometry Operations | 11 | | Attributes | 5 | |
| 78 | | Mesh Operations | 11 | | Points | 3 | |
| 79 | | Curves | 8 | | Selection/Utility | 2 | |
| 80 | |
| 81 | **[Every node with its inputs, outputs, defaults and GPU support → `docs/nodes.md`](docs/nodes.md)** |
| 82 | — though note that file currently covers 63 of the 122. |
| 83 | |
| 84 | The authority for what exists is `_create_node_by_type()` in |
| 85 | `src/graph/graph_importer.cpp`; the class definitions are in `src/nodes/*.h`. |
| 153 | 86 | |
| 154 | 87 | ## GPU Compute Pipeline |
| 155 | 88 | |
| … | … | @@ -246,60 +179,13 @@ scons target=template_release -j$(nproc) |
| 246 | 179 | |
| 247 | 180 | Output goes to `bin/<platform>/`. Register the GDExtension in your project's `.gdextension` file. |
| 248 | 181 | |
| 249 | | ## Extending: Adding a New Node Type |
| 250 | | |
| 251 | | 1. **Declare** the class in the appropriate `src/nodes/<category>_nodes.h`: |
| 252 | | |
| 253 | | ```cpp |
| 254 | | class MyCustomNode : public ProcNode { |
| 255 | | GDCLASS(MyCustomNode, ProcNode); |
| 256 | | protected: |
| 257 | | static void _bind_methods(); |
| 258 | | public: |
| 259 | | StringName get_node_type() const override { return "MyCustom"; } |
| 260 | | String get_node_title() const override { return "My Custom Node"; } |
| 261 | | String get_node_category() const override { return "Custom"; } |
| 262 | | void execute(ProcEvalContext &p_ctx, int p_node_idx) override; |
| 263 | | MyCustomNode(); |
| 264 | | }; |
| 265 | | ``` |
| 266 | | |
| 267 | | 2. **Implement** the constructor (define input/output sockets) and `execute()`: |
| 268 | | |
| 269 | | ```cpp |
| 270 | | MyCustomNode::MyCustomNode() { |
| 271 | | input_defs.push_back({ "Geometry", SocketType::Geometry, Variant() }); |
| 272 | | input_defs.push_back({ "Amount", SocketType::Float, 1.0f }); |
| 273 | | output_defs.push_back({ "Geometry", SocketType::Geometry, Variant() }); |
| 274 | | } |
| 275 | | |
| 276 | | void MyCustomNode::execute(ProcEvalContext &p_ctx, int p_node_idx) { |
| 277 | | Ref<ProcGeometry> geo = p_ctx.read_geometry(p_node_idx, 0); |
| 278 | | float amount = p_ctx.read_float(p_node_idx, 1); |
| 279 | | // ... process geometry ... |
| 280 | | p_ctx.write_geometry(p_node_idx, 0, geo); |
| 281 | | } |
| 282 | | ``` |
| 283 | | |
| 284 | | 3. **Register** in `src/register_types.cpp`: |
| 285 | | |
| 286 | | ```cpp |
| 287 | | GDREGISTER_CLASS(MyCustomNode); |
| 288 | | ``` |
| 289 | | |
| 290 | | 4. **Add to importer** factory in `src/graph/graph_importer.cpp`: |
| 291 | | |
| 292 | | ```cpp |
| 293 | | if (p_type == "MyCustom") return Ref<ProcNode>(memnew(MyCustomNode)); |
| 294 | | ``` |
| 295 | | |
| 296 | | 5. For **GPU support**, override `supports_gpu()` to return `true` and implement `compile_glsl()` returning a GLSL snippet that operates on `pos` and `normal` variables. |
| 297 | | |
| 298 | 182 | ## Documentation |
| 299 | 183 | |
| 300 | | - [API Reference](docs/api.md) -- Complete API for ProcNodeGraph, ProcNode, ProcEvalContext, serialization format, GPU compilation |
| 301 | | - [Node Reference](docs/nodes.md) -- Every node type with inputs, outputs, defaults, and behavior |
| 184 | - [API Reference](docs/api.md) — ProcNodeGraph, ProcNode, ProcEvalContext, serialization format, GPU compilation |
| 185 | - [Node Reference](docs/nodes.md) — per-node inputs, outputs, defaults, behaviour and GPU support (63 of 122 nodes so far) |
| 186 | - [Extending](docs/extending.md) — adding a new node type |
| 187 | - [Graph composition spec](docs/spec-graph-composition.md) · [GLSL emitter](docs/glsl-emitter-design.md) · [CPU twin](docs/cpu-twin-design.md) · [Output terminals](docs/output-terminals-design.md) · [Performance vs Blender](docs/performance-audit-vs-blender.md) |
| 302 | 188 | |
| 303 | 189 | ## License |
| 304 | 190 | |
| 305 | | GDExtension for Godot 4. See the project license file for details. |
| 191 | Not yet declared — there is no LICENSE file in this repo. |