ue-mcp-plugin-voxel-plugin 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,13 +4,16 @@
4
4
 
5
5
  ## What ships
6
6
 
7
- One injected action in v0.2.0, cited to its source:
7
+ Each action is cited to the C++ header it wraps. Full API reference under [`docs/`](docs/).
8
8
 
9
- | Category | Action | Wraps |
10
- |----------|------------------------------|-------|
11
- | `pcg` | `voxel_build_scatter_graph` | `UPCGVoxelSamplerSettings` (`VoxelPCG/Public/PCGVoxelSampler.h`) feeding `UPCGStaticMeshSpawnerSettings` |
9
+ | Category | Action | Wraps |
10
+ |----------|---------------------------------|-------|
11
+ | `level` | `voxel_spawn_voxel_world` | `level.place_actor` for `AVoxelWorld` (`Voxel/Public/VoxelWorld.h`) + property defaults so the world renders |
12
+ | `level` | `voxel_get_voxel_world_status` | 5 zero-arg lifecycle UFUNCTIONs on `AVoxelWorld` (`Voxel/Public/VoxelWorld.h`) |
13
+ | `pcg` | `voxel_build_scatter_graph` | `UPCGVoxelSamplerSettings` (`VoxelPCG/Public/PCGVoxelSampler.h`) feeding `UPCGStaticMeshSpawnerSettings` |
14
+ | `pcg` | `voxel_ensure_wait_for_world` | Splices `UPCGWaitForVoxelWorldSettings` (`VoxelPCG/Public/PCGWaitForVoxelWorld.h`) into an existing graph |
12
15
 
13
- More tools are tracked in [`TODO.md`](TODO.md) — each entry cites the header and class it would wrap. The v0.1.0 release shipped three actions that called ue-mcp tasks with wrong parameter names and passed PCG node-type strings that did not exist; v0.1.1 removed them.
16
+ The v0.1.0 release shipped three actions that called ue-mcp tasks with wrong parameter names and passed PCG node-type strings that did not exist; v0.1.1 removed them.
14
17
 
15
18
  ## Install
16
19
 
@@ -20,9 +23,24 @@ ue-mcp plugin install ue-mcp-plugin-voxel-plugin
20
23
 
21
24
  The CLI adds an entry under `plugins:` in your `ue-mcp.yml`. Restart ue-mcp; the injected action shows up under `pcg`.
22
25
 
23
- ## Usage
26
+ ## 0-to-1 workflow
24
27
 
25
28
  ```text
29
+ # 1. drop a voxel world into the level
30
+ level(action="voxel_spawn_voxel_world", label="MyVoxelWorld")
31
+
32
+ # 2. poll until the runtime finishes its first generation pass
33
+ level(action="voxel_get_voxel_world_status", actorLabel="MyVoxelWorld")
34
+ # => { isRuntimeCreated, isVoxelWorldReady, isProcessingNewState, progress, numPendingTasks }
35
+ # wait for isVoxelWorldReady && !isProcessingNewState before doing anything else.
36
+ ```
37
+
38
+ That's the hello-world. `spawn_voxel_world` defaults `LayerStack` to the plugin-bundled `/Voxel/Default/DefaultStack.DefaultStack` so the actor renders without further setup.
39
+
40
+ ## PCG actions (once a world is live)
41
+
42
+ ```text
43
+ # Build a scatter graph that drops weighted meshes on the voxel surface.
26
44
  pcg(action="voxel_build_scatter_graph",
27
45
  assetPath="/Game/PCG/RockScatter",
28
46
  meshes=[
@@ -31,14 +49,22 @@ pcg(action="voxel_build_scatter_graph",
31
49
  ],
32
50
  pointsPerSquaredMeter=0.05,
33
51
  seed=42)
52
+
53
+ # Attach the graph to a PCG component, then materialize:
54
+ pcg(action="execute", actorLabel="MyPCGActor")
34
55
  ```
35
56
 
36
- The call creates a `UPCGGraph` at `assetPath`, adds a Voxel Sampler Static Mesh Spawner pipeline, and populates the weighted mesh table. Attach the resulting graph to a PCG component on or near your `AVoxelWorld`, then:
57
+ If a PCG graph scatters before the voxel runtime finishes generating, you get empty / stale output. The gate is a `WaitForVoxelWorld` node splice one into any graph idempotently:
37
58
 
38
59
  ```text
39
- pcg(action="execute", actorLabel="MyPCGActor")
60
+ pcg(action="voxel_ensure_wait_for_world",
61
+ assetPath="/Game/PCG/RockScatter",
62
+ beforeNode="PCGStaticMeshSpawner")
63
+ # => { waitNode, inserted: true, rewiredEdges: N } # or inserted:false if already gated
40
64
  ```
41
65
 
66
+ `beforeNode` is whichever node you want to gate — almost always your spawner.
67
+
42
68
  ## Requirements
43
69
 
44
70
  - ue-mcp `>= 1.0.15`
@@ -0,0 +1,32 @@
1
+ import { BaseTask, type TaskResult } from "@db-lyon/flowkit";
2
+ interface Options {
3
+ assetPath: string;
4
+ /**
5
+ * The PCG node you want to gate on the voxel world being ready —
6
+ * almost always the spawner that materializes the final result
7
+ * (PCGStaticMeshSpawner, PCGVoxelStampSpawner, etc.). Whichever
8
+ * nodes currently feed `beforeNode` get rerouted through a new
9
+ * `WaitForVoxelWorld` node.
10
+ */
11
+ beforeNode: string;
12
+ }
13
+ /**
14
+ * Insert a `WaitForVoxelWorld` node immediately upstream of `beforeNode`
15
+ * in an existing PCG graph. Idempotent: if any node already feeding
16
+ * `beforeNode` is a Wait node, returns `inserted: false` and changes
17
+ * nothing.
18
+ *
19
+ * Header: `VoxelPCG/Public/PCGWaitForVoxelWorld.h`
20
+ * (`UPCGWaitForVoxelWorldSettings`, control-flow node, no settings).
21
+ *
22
+ * Fixes the most common PCG-on-voxel footgun documented in
23
+ * `docs/VoxelPCG.md`: pipelines that scatter / stamp before the voxel
24
+ * runtime finishes generating produce empty or stale output. The Wait
25
+ * node blocks downstream execution until the voxel world is ready.
26
+ */
27
+ export default class EnsureWaitForWorld extends BaseTask<Options> {
28
+ get taskName(): string;
29
+ protected validate(): void;
30
+ execute(): Promise<TaskResult>;
31
+ }
32
+ export {};
@@ -0,0 +1,91 @@
1
+ import { BaseTask } from "@db-lyon/flowkit";
2
+ const WAIT_NODE_TITLE = "Wait For Voxel World";
3
+ const WAIT_NODE_TYPE = "/Script/VoxelPCG.PCGWaitForVoxelWorldSettings";
4
+ /**
5
+ * Insert a `WaitForVoxelWorld` node immediately upstream of `beforeNode`
6
+ * in an existing PCG graph. Idempotent: if any node already feeding
7
+ * `beforeNode` is a Wait node, returns `inserted: false` and changes
8
+ * nothing.
9
+ *
10
+ * Header: `VoxelPCG/Public/PCGWaitForVoxelWorld.h`
11
+ * (`UPCGWaitForVoxelWorldSettings`, control-flow node, no settings).
12
+ *
13
+ * Fixes the most common PCG-on-voxel footgun documented in
14
+ * `docs/VoxelPCG.md`: pipelines that scatter / stamp before the voxel
15
+ * runtime finishes generating produce empty or stale output. The Wait
16
+ * node blocks downstream execution until the voxel world is ready.
17
+ */
18
+ export default class EnsureWaitForWorld extends BaseTask {
19
+ get taskName() { return "voxel.ensure_wait_for_world"; }
20
+ validate() {
21
+ if (!this.options.assetPath)
22
+ throw new Error("assetPath is required");
23
+ if (!this.options.beforeNode)
24
+ throw new Error("beforeNode is required");
25
+ }
26
+ async execute() {
27
+ const { assetPath, beforeNode } = this.options;
28
+ const graphR = await this.call("pcg.read_graph", { assetPath });
29
+ if (!graphR.success)
30
+ return graphR;
31
+ const graph = (graphR.data ?? {});
32
+ const nodes = graph.nodes ?? [];
33
+ const edges = graph.edges ?? [];
34
+ if (!nodes.some(n => n.name === beforeNode)) {
35
+ return { success: false, error: new Error(`node '${beforeNode}' not found in ${assetPath}`) };
36
+ }
37
+ const inbound = edges.filter(e => e.to === beforeNode);
38
+ if (inbound.length === 0) {
39
+ return { success: false, error: new Error(`no edges flow into '${beforeNode}' in ${assetPath} — nothing to gate`) };
40
+ }
41
+ const nodesByName = new Map(nodes.map(n => [n.name, n]));
42
+ const alreadyGated = inbound
43
+ .map(e => nodesByName.get(e.from))
44
+ .find(n => n?.title === WAIT_NODE_TITLE);
45
+ if (alreadyGated) {
46
+ return {
47
+ success: true,
48
+ data: { assetPath, beforeNode, waitNode: alreadyGated.name, inserted: false, rewiredEdges: 0 },
49
+ };
50
+ }
51
+ const waitR = await this.call("pcg.add_node", { assetPath, nodeType: WAIT_NODE_TYPE });
52
+ if (!waitR.success)
53
+ return waitR;
54
+ const waitName = waitR.data?.nodeName;
55
+ if (!waitName) {
56
+ return { success: false, error: new Error("pcg.add_node WaitForVoxelWorld returned no nodeName") };
57
+ }
58
+ for (const e of inbound) {
59
+ const dis = await this.call("pcg.disconnect_nodes", {
60
+ assetPath,
61
+ sourceNode: e.from,
62
+ sourcePin: e.fromPin,
63
+ targetNode: e.to,
64
+ targetPin: e.toPin,
65
+ });
66
+ if (!dis.success)
67
+ return dis;
68
+ const c1 = await this.call("pcg.connect_nodes", {
69
+ assetPath,
70
+ sourceNode: e.from,
71
+ sourcePin: e.fromPin,
72
+ targetNode: waitName,
73
+ });
74
+ if (!c1.success)
75
+ return c1;
76
+ const c2 = await this.call("pcg.connect_nodes", {
77
+ assetPath,
78
+ sourceNode: waitName,
79
+ targetNode: beforeNode,
80
+ targetPin: e.toPin,
81
+ });
82
+ if (!c2.success)
83
+ return c2;
84
+ }
85
+ return {
86
+ success: true,
87
+ data: { assetPath, beforeNode, waitNode: waitName, inserted: true, rewiredEdges: inbound.length },
88
+ };
89
+ }
90
+ }
91
+ //# sourceMappingURL=EnsureWaitForWorld.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EnsureWaitForWorld.js","sourceRoot":"","sources":["../../src/tasks/EnsureWaitForWorld.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { BaseTask, type TaskResult } from "@db-lyon/flowkit";
2
+ interface Options {
3
+ actorLabel: string;
4
+ world?: "editor" | "pie";
5
+ }
6
+ /**
7
+ * One-call snapshot of an `AVoxelWorld`'s lifecycle state.
8
+ *
9
+ * Composes 5x `editor.invoke_function` against the zero-arg lifecycle
10
+ * UFUNCTIONs on `AVoxelWorld` (`Voxel/Public/VoxelWorld.h`):
11
+ *
12
+ * bool IsRuntimeCreated()
13
+ * bool IsVoxelWorldReady()
14
+ * bool IsProcessingNewState()
15
+ * float GetProgress()
16
+ * int32 GetNumPendingTasks()
17
+ *
18
+ * Use it before any operation that requires the voxel runtime to be live —
19
+ * scattering meshes / placing stamps into a half-built world produces empty
20
+ * or stale output. The five calls fan out in parallel.
21
+ */
22
+ export default class GetWorldStatus extends BaseTask<Options> {
23
+ get taskName(): string;
24
+ protected validate(): void;
25
+ execute(): Promise<TaskResult>;
26
+ }
27
+ export {};
@@ -0,0 +1,57 @@
1
+ import { BaseTask } from "@db-lyon/flowkit";
2
+ /**
3
+ * One-call snapshot of an `AVoxelWorld`'s lifecycle state.
4
+ *
5
+ * Composes 5x `editor.invoke_function` against the zero-arg lifecycle
6
+ * UFUNCTIONs on `AVoxelWorld` (`Voxel/Public/VoxelWorld.h`):
7
+ *
8
+ * bool IsRuntimeCreated()
9
+ * bool IsVoxelWorldReady()
10
+ * bool IsProcessingNewState()
11
+ * float GetProgress()
12
+ * int32 GetNumPendingTasks()
13
+ *
14
+ * Use it before any operation that requires the voxel runtime to be live —
15
+ * scattering meshes / placing stamps into a half-built world produces empty
16
+ * or stale output. The five calls fan out in parallel.
17
+ */
18
+ export default class GetWorldStatus extends BaseTask {
19
+ get taskName() { return "voxel.get_world_status"; }
20
+ validate() {
21
+ if (!this.options.actorLabel)
22
+ throw new Error("actorLabel is required");
23
+ }
24
+ async execute() {
25
+ const { actorLabel, world } = this.options;
26
+ const baseParams = { actorLabel };
27
+ if (world)
28
+ baseParams.world = world;
29
+ const fns = [
30
+ "IsRuntimeCreated",
31
+ "IsVoxelWorldReady",
32
+ "IsProcessingNewState",
33
+ "GetProgress",
34
+ "GetNumPendingTasks",
35
+ ];
36
+ const results = await Promise.all(fns.map(functionName => this.call("editor.invoke_function", { ...baseParams, functionName })));
37
+ for (let i = 0; i < results.length; i++) {
38
+ if (!results[i].success)
39
+ return results[i];
40
+ }
41
+ const ret = (r) => r.data?.returnValues?.ReturnValue;
42
+ const asBool = (s) => s === "true" || s === "True";
43
+ const asNum = (s) => (s == null ? NaN : Number(s));
44
+ return {
45
+ success: true,
46
+ data: {
47
+ actorLabel,
48
+ isRuntimeCreated: asBool(ret(results[0])),
49
+ isVoxelWorldReady: asBool(ret(results[1])),
50
+ isProcessingNewState: asBool(ret(results[2])),
51
+ progress: asNum(ret(results[3])),
52
+ numPendingTasks: asNum(ret(results[4])),
53
+ },
54
+ };
55
+ }
56
+ }
57
+ //# sourceMappingURL=GetWorldStatus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GetWorldStatus.js","sourceRoot":"","sources":["../../src/tasks/GetWorldStatus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAmB,MAAM,kBAAkB,CAAC;AAW7D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,QAAiB;IAC3D,IAAI,QAAQ,KAAa,OAAO,wBAAwB,CAAC,CAAC,CAAC;IAEjD,QAAQ;QAChB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC1E,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAC3C,MAAM,UAAU,GAA4B,EAAE,UAAU,EAAE,CAAC;QAC3D,IAAI,KAAK;YAAE,UAAU,CAAC,KAAK,GAAG,KAAK,CAAC;QAEpC,MAAM,GAAG,GAAG;YACV,kBAAkB;YAClB,mBAAmB;YACnB,sBAAsB;YACtB,aAAa;YACb,oBAAoB;SACZ,CAAC;QAEX,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CACrB,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,GAAG,UAAU,EAAE,YAAY,EAAE,CAAC,CACrE,CACF,CAAC;QAEF,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO;gBAAE,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;QAED,MAAM,GAAG,GAAG,CAAC,CAAa,EAAsB,EAAE,CAC/C,CAAC,CAAC,IAA+B,EAAE,YAAY,EAAE,WAAW,CAAC;QAChE,MAAM,MAAM,GAAG,CAAC,CAAqB,EAAW,EAAE,CAAC,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,MAAM,CAAC;QAChF,MAAM,KAAK,GAAG,CAAC,CAAqB,EAAU,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE/E,OAAO;YACL,OAAO,EAAE,IAAI;YACb,IAAI,EAAE;gBACJ,UAAU;gBACV,gBAAgB,EAAM,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC7C,iBAAiB,EAAK,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC7C,oBAAoB,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC7C,QAAQ,EAAc,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC5C,eAAe,EAAO,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;aAC7C;SACF,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,37 @@
1
+ import { BaseTask, type TaskResult } from "@db-lyon/flowkit";
2
+ interface Vec3 {
3
+ x: number;
4
+ y: number;
5
+ z: number;
6
+ }
7
+ interface Options {
8
+ label?: string;
9
+ location?: Vec3;
10
+ voxelSize?: number;
11
+ /**
12
+ * Asset path of a UVoxelLayerStack. Defaults to the plugin-bundled
13
+ * `/Voxel/Default/DefaultStack.DefaultStack`. Pass an empty string to
14
+ * leave the property unset (the world will load but render nothing
15
+ * until you assign a stack yourself).
16
+ */
17
+ layerStack?: string;
18
+ /** Optional UVoxelMegaMaterial asset path. */
19
+ megaMaterial?: string;
20
+ }
21
+ /**
22
+ * Drop an `AVoxelWorld` actor into the active level — the first thing
23
+ * any Voxel-Plugin workflow needs. Wraps `level.place_actor` with the
24
+ * Voxel-Plugin class path, then applies `voxelSize` / `LayerStack` /
25
+ * `MegaMaterial` via `level.set_actor_property` so the resulting actor
26
+ * is renderable out of the box.
27
+ *
28
+ * Header: `Voxel/Public/VoxelWorld.h` (`AVoxelWorld`).
29
+ *
30
+ * Pair with `level.voxel_get_voxel_world_status` to poll the runtime
31
+ * until `isVoxelWorldReady` flips true before scattering / stamping.
32
+ */
33
+ export default class SpawnWorld extends BaseTask<Options> {
34
+ get taskName(): string;
35
+ execute(): Promise<TaskResult>;
36
+ }
37
+ export {};
@@ -0,0 +1,72 @@
1
+ import { BaseTask } from "@db-lyon/flowkit";
2
+ const DEFAULT_LAYER_STACK = "/Voxel/Default/DefaultStack.DefaultStack";
3
+ /**
4
+ * Drop an `AVoxelWorld` actor into the active level — the first thing
5
+ * any Voxel-Plugin workflow needs. Wraps `level.place_actor` with the
6
+ * Voxel-Plugin class path, then applies `voxelSize` / `LayerStack` /
7
+ * `MegaMaterial` via `level.set_actor_property` so the resulting actor
8
+ * is renderable out of the box.
9
+ *
10
+ * Header: `Voxel/Public/VoxelWorld.h` (`AVoxelWorld`).
11
+ *
12
+ * Pair with `level.voxel_get_voxel_world_status` to poll the runtime
13
+ * until `isVoxelWorldReady` flips true before scattering / stamping.
14
+ */
15
+ export default class SpawnWorld extends BaseTask {
16
+ get taskName() { return "voxel.spawn_world"; }
17
+ async execute() {
18
+ const { label, location, voxelSize, layerStack, megaMaterial } = this.options;
19
+ const placeParams = {
20
+ actorClass: "/Script/Voxel.VoxelWorld",
21
+ };
22
+ if (label)
23
+ placeParams.label = label;
24
+ if (location)
25
+ placeParams.location = location;
26
+ const placed = await this.call("level.place_actor", placeParams);
27
+ if (!placed.success)
28
+ return placed;
29
+ const actorLabel = placed.data?.actorLabel
30
+ ?? placed.data?.label
31
+ ?? label;
32
+ if (!actorLabel) {
33
+ return { success: false, error: new Error("level.place_actor did not return an actorLabel") };
34
+ }
35
+ const applied = {};
36
+ // Default the LayerStack so a bare AVoxelWorld actually renders.
37
+ // Explicit empty string opts out.
38
+ const resolvedLayerStack = layerStack === "" ? undefined
39
+ : (layerStack ?? DEFAULT_LAYER_STACK);
40
+ const setProp = async (propertyName, value) => {
41
+ const r = await this.call("level.set_actor_property", { actorLabel, propertyName, value });
42
+ if (!r.success)
43
+ return r;
44
+ applied[propertyName] = value;
45
+ return r;
46
+ };
47
+ if (typeof voxelSize === "number") {
48
+ const r = await setProp("VoxelSize", voxelSize);
49
+ if (!r.success)
50
+ return r;
51
+ }
52
+ if (resolvedLayerStack) {
53
+ const r = await setProp("LayerStack", resolvedLayerStack);
54
+ if (!r.success)
55
+ return r;
56
+ }
57
+ if (megaMaterial) {
58
+ const r = await setProp("MegaMaterial", megaMaterial);
59
+ if (!r.success)
60
+ return r;
61
+ }
62
+ return {
63
+ success: true,
64
+ data: {
65
+ actorLabel,
66
+ actorClass: "/Script/Voxel.VoxelWorld",
67
+ applied,
68
+ },
69
+ };
70
+ }
71
+ }
72
+ //# sourceMappingURL=SpawnWorld.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpawnWorld.js","sourceRoot":"","sources":["../../src/tasks/SpawnWorld.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAmB,MAAM,kBAAkB,CAAC;AAmB7D,MAAM,mBAAmB,GAAG,0CAA0C,CAAC;AAEvE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,QAAiB;IACvD,IAAI,QAAQ,KAAa,OAAO,mBAAmB,CAAC,CAAC,CAAC;IAEtD,KAAK,CAAC,OAAO;QACX,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAE9E,MAAM,WAAW,GAA4B;YAC3C,UAAU,EAAE,0BAA0B;SACvC,CAAC;QACF,IAAI,KAAK;YAAE,WAAW,CAAC,KAAK,GAAG,KAAK,CAAC;QACrC,IAAI,QAAQ;YAAE,WAAW,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAE9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,WAAW,CAAC,CAAC;QACjE,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO,MAAM,CAAC;QAEnC,MAAM,UAAU,GAAI,MAAM,CAAC,IAA4D,EAAE,UAAU;eAC/E,MAAM,CAAC,IAAuC,EAAE,KAAK;eACtD,KAAK,CAAC;QACzB,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,gDAAgD,CAAC,EAAE,CAAC;QAChG,CAAC;QAED,MAAM,OAAO,GAA4B,EAAE,CAAC;QAE5C,iEAAiE;QACjE,kCAAkC;QAClC,MAAM,kBAAkB,GAAG,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS;YAC/B,CAAC,CAAC,CAAC,UAAU,IAAI,mBAAmB,CAAC,CAAC;QAE/D,MAAM,OAAO,GAAG,KAAK,EAAE,YAAoB,EAAE,KAAc,EAAE,EAAE;YAC7D,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;YAC3F,IAAI,CAAC,CAAC,CAAC,OAAO;gBAAE,OAAO,CAAC,CAAC;YACzB,OAAO,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC;YAC9B,OAAO,CAAC,CAAC;QACX,CAAC,CAAC;QAEF,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAClC,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAChD,IAAI,CAAC,CAAC,CAAC,OAAO;gBAAE,OAAO,CAAC,CAAC;QAC3B,CAAC;QACD,IAAI,kBAAkB,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC;YAC1D,IAAI,CAAC,CAAC,CAAC,OAAO;gBAAE,OAAO,CAAC,CAAC;QAC3B,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;YACtD,IAAI,CAAC,CAAC,CAAC,OAAO;gBAAE,OAAO,CAAC,CAAC;QAC3B,CAAC;QAED,OAAO;YACL,OAAO,EAAE,IAAI;YACb,IAAI,EAAE;gBACJ,UAAU;gBACV,UAAU,EAAE,0BAA0B;gBACtC,OAAO;aACR;SACF,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,107 @@
1
+ # Voxel Plugin — Module Map
2
+
3
+ The plugin ships **twelve modules** plus a UHT (UnrealHeaderTool) extension. Every module funnels through a single `VoxelGlobalMethods.SetupVoxelModule` shim defined in `VoxelCore/VoxelCore.Build.cs`, which standardizes C++20, IWYU=None, unity builds, the shared PCH (`VoxelPCH.h` for runtime, `VoxelCoreEditorPCH.h` for editor), and `FPSemanticsMode.Precise` (so PCG point output is identical on client and server).
4
+
5
+ ## Module list
6
+
7
+ | Module | Type | Loading phase | Public headers? | Role |
8
+ |---|---|---|---|---|
9
+ | `VoxelCore` | Runtime | `PostConfigInit` | yes | Foundational containers, math, threading, messaging, ISPC bridge, dependency tracking. No voxel concepts. |
10
+ | `VoxelCoreEditor` | Editor | `Default` | yes | Slate widgets, detail customizations, asset wizards used by every other editor module. |
11
+ | `VoxelGraph` | Runtime | `Default` | yes | Visual-graph asset, compilation pipeline, typed buffers, function-library nodes. |
12
+ | `VoxelGraphEditor` | Editor | `Default` | yes | Graph editor UI, node panels, preview viewport, message log. |
13
+ | `Voxel` | Runtime | `PostConfigInit` | yes | The gameplay-facing module: `AVoxelWorld`, stamps, layers, mega-material, render/collision/navigation, sculpt/spline/shape/scatter. |
14
+ | `VoxelEditor` | Editor | `Default` | yes | World/stamp/layer detail panels, interactive sculpt tools, placement mode integration. |
15
+ | `VoxelBlueprint` | UncookedOnly | `PostConfigInit` | yes | Kismet (BP) K2 nodes that expose voxel-graph parameters and the make/break pin-value flow. Editor-time only. |
16
+ | `VoxelBlueprintEditor` | Editor | `Default` | no (private only) | Slate pieces specific to the BP K2 nodes. |
17
+ | `VoxelPCG` | Runtime | `Default` | yes | Bridge to Epic's PCG framework. PCG nodes that read/write voxel state plus voxel-graph nodes that operate on point sets. |
18
+ | `VoxelPCGEditor` | Editor | `Default` | no (private only) | PCG node UI/customizations. |
19
+ | `VoxelTests` | Runtime | `Default` | no | Automation tests; include-correctness checks when `VoxelDevWorkflow.txt` is present. |
20
+ | `VoxelUHT` | UBT plugin | n/a | n/a | Custom UnrealHeaderTool exporter (`*.generated.voxel.cpp`) that codegens function-library wrappers around `UVoxelFunctionLibrary` and `FVoxelRuntimePinValue`. |
21
+
22
+ `UncookedOnly` means `VoxelBlueprint` is built into editor/uncooked targets only — it ships nothing in packaged builds, because BP K2 node classes only exist to extend the Kismet compiler.
23
+
24
+ ## Loading phases
25
+
26
+ Two modules load at `PostConfigInit` (very early, before most engine systems):
27
+
28
+ - `VoxelCore` — needed early so `FVoxelMessageManager`, the `FVoxelDeveloperSettings` CDO, and the ISPC runtime are available before subsystems initialize.
29
+ - `Voxel` — needs early load so `AVoxelWorld` subsystems can register themselves before the world begins streaming.
30
+
31
+ `VoxelBlueprint` also loads `PostConfigInit` (but as `UncookedOnly`) so its K2 node factories are registered before the Blueprint editor opens any assets.
32
+
33
+ Everything else loads at the `Default` phase.
34
+
35
+ ## Dependency graph
36
+
37
+ ```
38
+ VoxelCore ←─────────────────────────┐
39
+ ▲ │
40
+ ┌───────────────┼───────────────┐ │
41
+ │ │ │ │
42
+ VoxelGraph Voxel VoxelPCG ───→ Voxel + VoxelGraph
43
+ ▲ ▲ ▲ │
44
+ │ │ │ │
45
+ │ │ │ │
46
+ VoxelGraphEditor VoxelEditor VoxelPCGEditor │
47
+ ▲ │
48
+ │ │
49
+ VoxelBlueprint ──→ Voxel + VoxelGraph + VoxelEditor + VoxelGraphEditor + VoxelCoreEditor
50
+
51
+
52
+ VoxelBlueprintEditor
53
+ ```
54
+
55
+ (Arrow direction: A → B means A depends on B.)
56
+
57
+ Concrete declarations from the `*.Build.cs` files:
58
+
59
+ - `VoxelCore` adds engine deps `Chaos`, `Renderer`, `Projects`, `ApplicationCore`, `TraceLog`. Private deps include `zlib`, `UElibPNG`, `Json`, `HTTP`, `Landscape`, `EventLoop`, `MoviePlayer`. Editor builds pull in `MaterialEditor`, `UATHelper`, `GraphEditor`, `DesktopPlatform`.
60
+ - `Voxel` depends on `VoxelGraph`, plus `Chaos`, `Renderer`, `PhysicsCore`, `Landscape`, `NavigationSystem`, `PCG`, `MeshDescription`, `StaticMeshDescription`.
61
+ - `VoxelGraph` depends only on the standard set plus `TraceLog`, `Chaos`, `PhysicsCore`, `Json` (and `MessageLog` in editor).
62
+ - `VoxelPCG` depends on `Voxel`, `VoxelGraph`, `PCG`.
63
+ - `VoxelBlueprint` depends on `VoxelCoreEditor`, `Voxel`, `VoxelEditor`, `VoxelGraph`, `VoxelGraphEditor`, plus `BlueprintGraph` and `KismetCompiler` for K2 node integration.
64
+ - `VoxelEditor` is the heaviest editor module — it depends on `Voxel`, `VoxelPCG`, `VoxelGraph`, `VoxelGraphEditor`, plus the full editor surface (`LevelEditor`, `AssetTools`, `PlacementMode`, `InteractiveToolsFramework`, `SceneOutliner`, `DetailCustomizations`, etc.).
65
+ - `VoxelTests` depends only on `Json` and `NavigationSystem`.
66
+
67
+ ## ISPC and the build script
68
+
69
+ `VoxelCore.Build.cs` defines `VoxelISPCCompiler`, which scans every module's `Source/<Module>/**.ispc` and `**.isph` files and generates a per-platform build script (`Intermediate/ISPC/<Platform>/<Arch>/<Editor>/<Config>/Build.ps1` or `Build.sh`). On Windows it downloads the same ISPC binary Unreal uses (`UnrealEngine-28863921/...`) if the engine copy is missing. ISPC is built into per-module static libraries (`libVoxel.a`, `libVoxelCore.a`, etc.) linked via `PublicAdditionalLibraries`.
70
+
71
+ The cross-platform target matrix is hard-coded to:
72
+
73
+ - `Win64` / `Linux` / `Mac (x64)` / `Android (x64)`: `avx512skx-i32x8`, `avx2`, `avx`, `sse4`
74
+ - `LinuxArm64` / `Mac (arm64)` / `Android (arm64)` / `iOS` / `VisionOS`: `neon`
75
+
76
+ ## Build toggles
77
+
78
+ Two opt-in flags live as marker files in the plugin's parent `Plugins/` directory:
79
+
80
+ - `VoxelDevWorkflow.txt` — disables unity builds for `Win64` and `Mac` and switches off forced code optimization so iteration with a debugger is bearable. Also installs the IDE-friendly include-path that lets ReSharper/Rider see ISPC generated headers.
81
+ - `VoxelDebug.txt` — forces the `VOXEL_DEBUG=1` define regardless of build configuration. Equivalently, building `Debug` (or `DebugGame` with dev workflow on) flips this on automatically.
82
+
83
+ There's also `CheckPackaging.txt`: if present, all optimization is disabled (build-only sanity pass).
84
+
85
+ ## UHT extension
86
+
87
+ `VoxelUHT/Program.cs` defines a single `[UhtExporter]` named `"VoxelGraph"` that:
88
+
89
+ 1. Locates `UVoxelFunctionLibrary` and `FVoxelRuntimePinValue` in the parsed metadata.
90
+ 2. Walks every header in the `VoxelGraph` module.
91
+ 3. For every class deriving from `UVoxelFunctionLibrary`, emits a `*.generated.voxel.cpp` file with the C++ glue that turns `UFUNCTION`-tagged static methods into compute-graph nodes.
92
+
93
+ This is why writing a new voxel-graph function library node only requires a `UFUNCTION` declaration — the boilerplate that wires it into the graph system is generated by this UHT plugin at build time.
94
+
95
+ ## What's in each module's `Public/`
96
+
97
+ | Module | Subfolders under `Public/` |
98
+ |---|---|
99
+ | `VoxelCore` | `VoxelMinimal/`, `VoxelMinimal/Containers/`, `VoxelMinimal/Utilities/`, root |
100
+ | `Voxel` | `Collision/`, `Graphs/`, `Heightmap/`, `MegaMaterial/`, `Nanite/`, `Navigation/`, `Render/`, `Scatter/`, `Sculpt/`, `Shape/`, `Spline/`, `StaticMesh/`, `Surface/`, `Texture/`, root |
101
+ | `VoxelGraph` | `Buffer/`, `FunctionLibrary/`, `Nodes/`, `Preview/`, `Utilities/`, root |
102
+ | `VoxelPCG` | flat (no subfolders) |
103
+ | `VoxelBlueprint` | flat (five K2 node headers) |
104
+ | `VoxelCoreEditor`, `VoxelEditor`, `VoxelGraphEditor` | each has a `Public/`, but most concrete UI lives in `Private/` |
105
+ | `VoxelBlueprintEditor`, `VoxelPCGEditor`, `VoxelTests` | private-only, no `Public/` |
106
+
107
+ For per-module API detail see [VoxelCore](VoxelCore.md), [Voxel](Voxel.md), [VoxelGraph](VoxelGraph.md), [VoxelPCG](VoxelPCG.md), [VoxelBlueprint](VoxelBlueprint.md).
package/docs/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # Voxel Plugin — Reference Docs
2
+
3
+ Reference docs for the [Voxel Plugin](https://voxelplugin.com) (Phyronnaz) — module map, public C++ surface per module, how the pieces fit together. They live in this repo because this is the ue-mcp wrapper for the Voxel Plugin, and the same reference material that helps wrapper-action authors also helps anyone building against the plugin from C++ or Blueprint.
4
+
5
+ These docs are **API-level reference** — what's in each module, what the public types do, how the pieces relate. They complement, not replace, the official knowledgebase at <https://docs.voxelplugin.com/knowledgebase>, which covers task-oriented "how do I do X" workflows.
6
+
7
+ ## When to read what
8
+
9
+ | You want… | Go here |
10
+ |---|---|
11
+ | "How do I author a stamp / use the graph editor / set up PCG?" | [Official KB](https://docs.voxelplugin.com/knowledgebase) — those pages exist and are decent. |
12
+ | "What modules does this plugin actually ship, and how do they depend on each other?" | [Modules.md](Modules.md) |
13
+ | "What's in the foundation module — containers, math, threading, ISPC?" | [VoxelCore.md](VoxelCore.md) |
14
+ | "What's `AVoxelWorld`? What are stamps, layers, the runtime, sampling?" | [Voxel.md](Voxel.md) |
15
+ | "How does the graph compile pipeline work? What's a buffer? What nodes ship?" | [VoxelGraph.md](VoxelGraph.md) |
16
+ | "How do PCG and Voxel talk to each other?" | [VoxelPCG.md](VoxelPCG.md) |
17
+ | "What K2 nodes does the plugin add to Blueprint?" | [VoxelBlueprint.md](VoxelBlueprint.md) |
18
+ | "Is the test suite worth borrowing patterns from?" | [Tests.md](Tests.md) |
19
+
20
+ ## Doc scope
21
+
22
+ These docs cover the **runtime + Blueprint** public API. Editor modules (`VoxelCoreEditor`, `VoxelEditor`, `VoxelGraphEditor`, `VoxelBlueprintEditor`, `VoxelPCGEditor`) are mentioned in [Modules.md](Modules.md) but not given their own reference pages — most of their public surface is detail customizations and Slate widgets that aren't useful to call from game code.
23
+
24
+ The plugin's `Tests/` content and `VoxelTests` module are covered as a *pattern-mining* exercise in [Tests.md](Tests.md), not as exhaustive reference.
25
+
26
+ ## Conventions
27
+
28
+ - File paths are written relative to the plugin source root (`Plugins/Voxel/Source/...`) unless they're inside a code block.
29
+ - C++ type names use their actual prefixes (`F`, `U`, `A`, `T`, `S`, `I`) — `AVoxelWorld`, `UVoxelGraph`, `FVoxelBox`, `TVoxelArray<T>`.
30
+ - Where the official KB has a corresponding page, the module doc links out to it.
31
+ - Cross-doc links between these pages use relative paths so they work on disk and on a wiki host.
32
+
33
+ ## Plugin metadata
34
+
35
+ - Source: <https://voxelplugin.com>
36
+ - Docs (official, task-focused): <https://docs.voxelplugin.com/knowledgebase>
37
+ - Discord (support): <https://discord.voxelplugin.com>
38
+ - License: see `Plugins/Voxel/LICENSES.txt` in the plugin itself. Note in particular the Transvoxel attribution requirement called out in [VoxelCore.md](VoxelCore.md#transvoxel-data).
39
+ - Loaded by default (`EnabledByDefault: true` in `Voxel.uplugin`).
40
+
41
+ ## Doc maintenance
42
+
43
+ When the upstream plugin is bumped to a new version, this is what to refresh:
44
+
45
+ 1. [Modules.md](Modules.md) — re-read every `*.Build.cs`; phases and module-name changes happen at major version bumps.
46
+ 2. Per-module docs — `Glob` `Public/**/*.h` and reconcile against the listings here. New top-level types deserve a row; deleted ones should be removed.
47
+ 3. [Tests.md](Tests.md) — re-run the include-test count and check `VoxelTests.Build.cs` for new infrastructure files.