ue-mcp-plugin-voxel-plugin 0.1.0 → 0.1.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
@@ -2,55 +2,24 @@
2
2
 
3
3
  [Voxel Plugin](https://voxelplugin.com) actions for [ue-mcp](https://github.com/db-lyon/ue-mcp).
4
4
 
5
- Injects three actions into the existing built-in categories:
5
+ ## Status
6
6
 
7
- | Category | Action | What it does |
8
- |-------------|---------------------------|-----------------------------------------------------------------------|
9
- | `pcg` | `voxel_scatter_meshes` | Builds a PCG graph that scatters static meshes on a voxel terrain. |
10
- | `pcg` | `voxel_spawn_stamps` | Builds a PCG graph that places voxel stamp assets. |
11
- | `landscape` | `voxel_bake_heightmap` | Bakes a region of a voxel terrain into a standard Landscape heightmap.|
7
+ **v0.1.1 ships no actions.** The `0.1.0` release injected three placeholder
8
+ actions (`voxel_scatter_meshes`, `voxel_spawn_stamps`, `voxel_bake_heightmap`)
9
+ that called ue-mcp tasks with wrong parameter names and passed PCG node-type
10
+ strings that do not exist. They have been removed rather than left as broken
11
+ surface.
12
12
 
13
- Plus two ready-to-run flows: `voxel_scatter_setup` and `voxel_stamps_setup`.
13
+ Real tools, grounded in the actual Voxel Plugin C++ API, are tracked in
14
+ [`TODO.md`](TODO.md). Every entry there cites the header file and class it
15
+ wraps.
14
16
 
15
- ## Install
17
+ Do not depend on this package yet — it intentionally exposes nothing.
16
18
 
17
- In your project directory:
19
+ ## Requirements (for future versions)
18
20
 
19
- ```bash
20
- ue-mcp plugin install ue-mcp-plugin-voxel-plugin
21
- ```
22
-
23
- The CLI runs `npm install --save`, validates the plugin manifest, adds an entry under `plugins:` in your `ue-mcp.yml`, and prints a restart instruction. Injected actions appear on the next ue-mcp server start.
24
-
25
- ## Requirements
26
-
27
- - ue-mcp `>= 1.0.15`.
28
- - Voxel Plugin enabled in your `.uproject` (`Plugins[].Name == "Voxel"`). The installer warns at install time if it's missing.
29
- - An active Voxel Plugin Pro subscription if your project's use case requires it - the upstream source repo is public-readable but commercial use is gated by the upstream license.
30
-
31
- ## Usage
32
-
33
- The actions appear natively inside their host categories - no new tool to learn:
34
-
35
- ```text
36
- pcg(action="voxel_scatter_meshes",
37
- graphPath="/Game/PCG/Scatter",
38
- mesh="/Game/Foliage/Rock.Rock",
39
- density=0.25)
40
-
41
- pcg(action="execute", graphPath="/Game/PCG/Scatter")
42
- ```
43
-
44
- Or via the bundled flow:
45
-
46
- ```text
47
- flow(action="run", flowName="voxel_scatter_setup", params={
48
- graphPath: "/Game/PCG/Scatter",
49
- mesh: "/Game/Foliage/Rock.Rock"
50
- })
51
- ```
52
-
53
- See `knowledge/pcg.md` and `knowledge/landscape.md` for the full parameter reference. The same content is attached to the host category's AI-facing docs at server start so MCP agents see it natively.
21
+ - ue-mcp `>= 1.0.15`
22
+ - Voxel Plugin enabled in your `.uproject` (`Plugins[].Name == "Voxel"`)
54
23
 
55
24
  ## Develop
56
25
 
@@ -61,12 +30,6 @@ npm install
61
30
  npm run build
62
31
  ```
63
32
 
64
- The plugin compiles to `dist/`. To test against a local checkout of ue-mcp, `npm link` this package and `npm link ue-mcp-plugin-voxel-plugin` from your project directory.
65
-
66
- ## Why a plugin?
67
-
68
- ue-mcp's plugin model injects new actions into existing built-in categories rather than creating a separate tool. The agent already working in `pcg` discovers `voxel_scatter_meshes` exactly when it's relevant - it does not need to know there is a separate Voxel surface to open. See the [ue-mcp plugin docs](https://db-lyon.github.io/ue-mcp/plugins/) for the full author contract.
69
-
70
33
  ## License
71
34
 
72
35
  MIT - see `LICENSE`.
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ue-mcp-plugin-voxel-plugin",
3
- "version": "0.1.0",
4
- "description": "Voxel Plugin actions for ue-mcp - voxel terrain scatter, stamps, and heightmap baking via PCG and Landscape.",
3
+ "version": "0.1.1",
4
+ "description": "Voxel Plugin actions for ue-mcp. Scaffold only at this version - real tools tracked in TODO.md.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "files": [
@@ -34,7 +34,8 @@
34
34
  "typescript": "^5.7.0"
35
35
  },
36
36
  "scripts": {
37
- "build": "tsc",
37
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
38
+ "build": "npm run clean && tsc",
38
39
  "prepublishOnly": "npm run build"
39
40
  },
40
41
  "engines": {
package/ue-mcp.plugin.yml CHANGED
@@ -2,63 +2,14 @@ actionPrefix: voxel
2
2
  minServerVersion: 1.0.15
3
3
  uePluginDependency: Voxel
4
4
 
5
- inject:
6
- pcg:
7
- scatter_meshes:
8
- task: voxel.scatter_meshes
9
- description: "Build a PCG graph that scatters static meshes on a voxel terrain via Voxel Sampler. Params: graphPath, mesh, density?, minScale?, maxScale?, seed?"
10
- schema:
11
- graphPath: { type: string, required: true }
12
- mesh: { type: string, required: true }
13
- density: { type: number }
14
- minScale: { type: number }
15
- maxScale: { type: number }
16
- seed: { type: number }
17
- spawn_stamps:
18
- task: voxel.spawn_stamps
19
- description: "Build a PCG graph that places voxel stamp assets on a voxel terrain. Params: graphPath, stampAsset, count?, radius?, seed?"
20
- schema:
21
- graphPath: { type: string, required: true }
22
- stampAsset: { type: string, required: true }
23
- count: { type: number }
24
- radius: { type: number }
25
- seed: { type: number }
26
-
27
- landscape:
28
- bake_heightmap:
29
- task: voxel.bake_heightmap
30
- description: "Bake a region of a voxel terrain into a standard Landscape heightmap. Params: landscapeLabel, bounds, resolution?"
31
- schema:
32
- landscapeLabel: { type: string, required: true }
33
- bounds: { type: object, required: true }
34
- resolution: { type: number }
35
-
36
- knowledge:
37
- pcg: knowledge/pcg.md
38
- landscape: knowledge/landscape.md
39
-
40
- tasks:
41
- voxel.scatter_meshes:
42
- class_path: tasks/ScatterMeshes
43
- description: "Build the PCG graph for voxel-terrain mesh scatter"
44
- voxel.spawn_stamps:
45
- class_path: tasks/SpawnStamps
46
- description: "Build the PCG graph for voxel stamps"
47
- voxel.bake_heightmap:
48
- class_path: tasks/BakeHeightmap
49
- description: "Bake a voxel-terrain region into a Landscape heightmap"
50
-
51
- flows:
52
- voxel_scatter_setup:
53
- description: "Create a PCG mesh-scatter graph on a voxel terrain and execute it"
54
- rollback_on_failure: true
55
- steps:
56
- 1: { task: voxel.scatter_meshes }
57
- 2: { task: pcg.execute }
58
-
59
- voxel_stamps_setup:
60
- description: "Create a PCG voxel-stamp graph and execute it"
61
- rollback_on_failure: true
62
- steps:
63
- 1: { task: voxel.spawn_stamps }
64
- 2: { task: pcg.execute }
5
+ # No actions ship in this version. The 0.1.0 release injected three
6
+ # placeholder actions (voxel_scatter_meshes, voxel_spawn_stamps,
7
+ # voxel_bake_heightmap) that called ue-mcp tasks with the wrong parameter
8
+ # names and passed fabricated PCG node-type strings - none of them worked.
9
+ # They have been removed. See TODO.md in the repo for the real API surface
10
+ # and the prioritized list of tools being built against it.
11
+
12
+ inject: {}
13
+ knowledge: {}
14
+ tasks: {}
15
+ flows: {}
@@ -1,31 +0,0 @@
1
- /**
2
- * Cross-task helpers for building Voxel-Plugin-flavoured PCG graphs. None of
3
- * these are referenced by ue-mcp.plugin.yml directly - task files import them.
4
- *
5
- * The exact PCG node-type names depend on the Voxel Plugin version installed.
6
- * They are intentionally string constants so they can be updated in one
7
- * place when the plugin's exposed nodes shift.
8
- */
9
- export declare const VOXEL_NODES: {
10
- /** Samples the active voxel volume in PCG space. */
11
- readonly voxelSampler: "VoxelSampler";
12
- /** Surface sampler that filters points to voxel-terrain surface positions. */
13
- readonly surfaceSampler: "PCGSurfaceSampler";
14
- /** Static-mesh spawner consuming the points from the surface sampler. */
15
- readonly staticMeshSpawner: "PCGStaticMeshSpawner";
16
- /** Stamp-spawner specific to Voxel Plugin's stamp asset format. */
17
- readonly voxelStamp: "VoxelStamp";
18
- /** Density filter for thinning out sampled points. */
19
- readonly densityFilter: "PCGDensityFilter";
20
- /** Transform-randomiser for scale/rotation jitter. */
21
- readonly transformPoints: "PCGTransformPoints";
22
- };
23
- export type VoxelNodeKey = keyof typeof VOXEL_NODES;
24
- /**
25
- * Type-safe wrapper around `this.call('pcg.add_node', ...)`. Returns the
26
- * caller-visible node id reported by the bridge, or throws so the task gets
27
- * a meaningful error.
28
- */
29
- export declare function addNode(task: unknown, graphPath: string, nodeKey: VoxelNodeKey): Promise<string>;
30
- export declare function connectNodes(task: unknown, graphPath: string, sourceId: string, targetId: string): Promise<void>;
31
- export declare function setNodeSettings(task: unknown, graphPath: string, nodeId: string, settings: Record<string, unknown>): Promise<void>;
@@ -1,65 +0,0 @@
1
- /**
2
- * Cross-task helpers for building Voxel-Plugin-flavoured PCG graphs. None of
3
- * these are referenced by ue-mcp.plugin.yml directly - task files import them.
4
- *
5
- * The exact PCG node-type names depend on the Voxel Plugin version installed.
6
- * They are intentionally string constants so they can be updated in one
7
- * place when the plugin's exposed nodes shift.
8
- */
9
- export const VOXEL_NODES = {
10
- /** Samples the active voxel volume in PCG space. */
11
- voxelSampler: "VoxelSampler",
12
- /** Surface sampler that filters points to voxel-terrain surface positions. */
13
- surfaceSampler: "PCGSurfaceSampler",
14
- /** Static-mesh spawner consuming the points from the surface sampler. */
15
- staticMeshSpawner: "PCGStaticMeshSpawner",
16
- /** Stamp-spawner specific to Voxel Plugin's stamp asset format. */
17
- voxelStamp: "VoxelStamp",
18
- /** Density filter for thinning out sampled points. */
19
- densityFilter: "PCGDensityFilter",
20
- /** Transform-randomiser for scale/rotation jitter. */
21
- transformPoints: "PCGTransformPoints",
22
- };
23
- function callable(task) {
24
- return task;
25
- }
26
- /**
27
- * Type-safe wrapper around `this.call('pcg.add_node', ...)`. Returns the
28
- * caller-visible node id reported by the bridge, or throws so the task gets
29
- * a meaningful error.
30
- */
31
- export async function addNode(task, graphPath, nodeKey) {
32
- const r = await callable(task).call("pcg.add_node", {
33
- graphPath,
34
- nodeType: VOXEL_NODES[nodeKey],
35
- });
36
- if (!r.success) {
37
- throw new Error(`pcg.add_node ${nodeKey} failed: ${r.error?.message ?? "unknown"}`);
38
- }
39
- const id = (r.data?.nodeId ?? r.data?.id);
40
- if (!id) {
41
- throw new Error(`pcg.add_node ${nodeKey} did not return a node id`);
42
- }
43
- return id;
44
- }
45
- export async function connectNodes(task, graphPath, sourceId, targetId) {
46
- const r = await callable(task).call("pcg.connect_nodes", {
47
- graphPath,
48
- sourceNodeId: sourceId,
49
- targetNodeId: targetId,
50
- });
51
- if (!r.success) {
52
- throw new Error(`pcg.connect_nodes failed: ${r.error?.message ?? "unknown"}`);
53
- }
54
- }
55
- export async function setNodeSettings(task, graphPath, nodeId, settings) {
56
- const r = await callable(task).call("pcg.set_node_settings", {
57
- graphPath,
58
- nodeId,
59
- settings,
60
- });
61
- if (!r.success) {
62
- throw new Error(`pcg.set_node_settings failed: ${r.error?.message ?? "unknown"}`);
63
- }
64
- }
65
- //# sourceMappingURL=voxelGraph.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"voxelGraph.js","sourceRoot":"","sources":["../../src/shared/voxelGraph.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,oDAAoD;IACpD,YAAY,EAAE,cAAc;IAC5B,8EAA8E;IAC9E,cAAc,EAAE,mBAAmB;IACnC,yEAAyE;IACzE,iBAAiB,EAAE,sBAAsB;IACzC,mEAAmE;IACnE,UAAU,EAAE,YAAY;IACxB,sDAAsD;IACtD,aAAa,EAAE,kBAAkB;IACjC,sDAAsD;IACtD,eAAe,EAAE,oBAAoB;CAC7B,CAAC;AAaX,SAAS,QAAQ,CAAC,IAAa;IAC7B,OAAO,IAAmB,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,IAAa,EACb,SAAiB,EACjB,OAAqB;IAErB,MAAM,CAAC,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,EAAE;QAClD,SAAS;QACT,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC;KAC/B,CAAC,CAAC;IACH,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,gBAAgB,OAAO,YAAY,CAAC,CAAC,KAAK,EAAE,OAAO,IAAI,SAAS,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAuB,CAAC;IAChE,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,gBAAgB,OAAO,2BAA2B,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAa,EACb,SAAiB,EACjB,QAAgB,EAChB,QAAgB;IAEhB,MAAM,CAAC,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,mBAAmB,EAAE;QACvD,SAAS;QACT,YAAY,EAAE,QAAQ;QACtB,YAAY,EAAE,QAAQ;KACvB,CAAC,CAAC;IACH,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,KAAK,EAAE,OAAO,IAAI,SAAS,EAAE,CAAC,CAAC;IAChF,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAa,EACb,SAAiB,EACjB,MAAc,EACd,QAAiC;IAEjC,MAAM,CAAC,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,uBAAuB,EAAE;QAC3D,SAAS;QACT,MAAM;QACN,QAAQ;KACT,CAAC,CAAC;IACH,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC,KAAK,EAAE,OAAO,IAAI,SAAS,EAAE,CAAC,CAAC;IACpF,CAAC;AACH,CAAC"}
@@ -1,37 +0,0 @@
1
- import { BaseTask, type TaskResult } from "@db-lyon/flowkit";
2
- interface Bounds {
3
- min: {
4
- x: number;
5
- y: number;
6
- z?: number;
7
- };
8
- max: {
9
- x: number;
10
- y: number;
11
- z?: number;
12
- };
13
- }
14
- interface Options {
15
- landscapeLabel: string;
16
- bounds: Bounds;
17
- /** Heightmap side length in samples. Default 1009 (UE landscape-friendly). */
18
- resolution?: number;
19
- }
20
- /**
21
- * Bakes a region of a Voxel Plugin terrain to a Landscape heightmap.
22
- *
23
- * Pipeline:
24
- * 1. Sample the voxel surface at a grid resolution by issuing Python through
25
- * the editor (the C++ bridge has no direct Voxel Plugin API surface).
26
- * 2. Pipe the resulting heightfield into `landscape.import_heightmap`.
27
- *
28
- * The Python step is the escape hatch documented in CLAUDE.md and is
29
- * acceptable here because Voxel Plugin exposes no native PCG-side way to
30
- * extract raw voxel heights into the engine's landscape format.
31
- */
32
- export default class BakeHeightmap extends BaseTask<Options> {
33
- get taskName(): string;
34
- protected validate(): void;
35
- execute(): Promise<TaskResult>;
36
- }
37
- export {};
@@ -1,101 +0,0 @@
1
- import { BaseTask } from "@db-lyon/flowkit";
2
- /**
3
- * Bakes a region of a Voxel Plugin terrain to a Landscape heightmap.
4
- *
5
- * Pipeline:
6
- * 1. Sample the voxel surface at a grid resolution by issuing Python through
7
- * the editor (the C++ bridge has no direct Voxel Plugin API surface).
8
- * 2. Pipe the resulting heightfield into `landscape.import_heightmap`.
9
- *
10
- * The Python step is the escape hatch documented in CLAUDE.md and is
11
- * acceptable here because Voxel Plugin exposes no native PCG-side way to
12
- * extract raw voxel heights into the engine's landscape format.
13
- */
14
- export default class BakeHeightmap extends BaseTask {
15
- get taskName() { return "voxel.bake_heightmap"; }
16
- validate() {
17
- if (!this.options.landscapeLabel)
18
- throw new Error("landscapeLabel is required");
19
- if (!this.options.bounds?.min || !this.options.bounds?.max) {
20
- throw new Error("bounds.min and bounds.max are required");
21
- }
22
- }
23
- async execute() {
24
- const { landscapeLabel, bounds, resolution = 1009 } = this.options;
25
- // 1. Sample voxel heights via Python. The script writes a raw uint16
26
- // heightmap to the project's intermediate dir and returns its path.
27
- const sample = await this.call("editor.execute_python", {
28
- script: buildVoxelSampleScript(bounds, resolution),
29
- });
30
- if (!sample.success)
31
- return sample;
32
- const heightmapPath = (sample.data?.result ?? sample.data?.output ?? sample.data?.heightmapPath);
33
- if (!heightmapPath) {
34
- return {
35
- success: false,
36
- error: new Error("voxel sample script did not return a heightmap path"),
37
- };
38
- }
39
- // 2. Import the heightmap into the target landscape actor.
40
- const imported = await this.call("landscape.import_heightmap", {
41
- actorLabel: landscapeLabel,
42
- heightmapPath,
43
- resolution,
44
- });
45
- if (!imported.success)
46
- return imported;
47
- return {
48
- success: true,
49
- data: {
50
- landscapeLabel,
51
- heightmapPath,
52
- resolution,
53
- bounds,
54
- },
55
- };
56
- }
57
- }
58
- function buildVoxelSampleScript(bounds, resolution) {
59
- // The script is kept in a single string so the bridge ships it verbatim to
60
- // Python. It is intentionally defensive: any Voxel Plugin API the host
61
- // installation may not have is caught and reported as a clear error rather
62
- // than a traceback.
63
- return `
64
- import unreal, os, struct
65
-
66
- bounds_min = (${bounds.min.x}, ${bounds.min.y})
67
- bounds_max = (${bounds.max.x}, ${bounds.max.y})
68
- res = ${resolution}
69
-
70
- try:
71
- voxel_lib = unreal.VoxelBlueprintLibrary # exposed by Voxel Plugin at runtime
72
- except AttributeError:
73
- raise RuntimeError("Voxel Plugin not loaded - voxel.bake_heightmap requires the Voxel plugin")
74
-
75
- dx = (bounds_max[0] - bounds_min[0]) / float(res - 1)
76
- dy = (bounds_max[1] - bounds_min[1]) / float(res - 1)
77
-
78
- samples = bytearray()
79
- for j in range(res):
80
- for i in range(res):
81
- x = bounds_min[0] + i * dx
82
- y = bounds_min[1] + j * dy
83
- try:
84
- h = voxel_lib.get_voxel_height_at(x, y)
85
- except Exception:
86
- h = 0.0
87
- # Map [-32768, 32767] cm into uint16. Adjust per project needs.
88
- sample = max(0, min(65535, int(h + 32768)))
89
- samples += struct.pack("<H", sample)
90
-
91
- out_dir = os.path.join(unreal.Paths.project_intermediate_dir(), "Voxel")
92
- os.makedirs(out_dir, exist_ok=True)
93
- out_path = os.path.join(out_dir, "voxel_heightmap.r16")
94
- with open(out_path, "wb") as f:
95
- f.write(samples)
96
-
97
- # The bridge serialises whatever we print as the script result.
98
- print(out_path)
99
- `.trim();
100
- }
101
- //# sourceMappingURL=BakeHeightmap.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BakeHeightmap.js","sourceRoot":"","sources":["../../src/tasks/BakeHeightmap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAmB,MAAM,kBAAkB,CAAC;AAc7D;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,QAAiB;IAC1D,IAAI,QAAQ,KAAa,OAAO,sBAAsB,CAAC,CAAC,CAAC;IAE/C,QAAQ;QAChB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChF,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;YAC3D,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAEnE,qEAAqE;QACrE,uEAAuE;QACvE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE;YACtD,MAAM,EAAE,sBAAsB,CAAC,MAAM,EAAE,UAAU,CAAC;SACnD,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,OAAO,MAAM,CAAC;QAEnC,MAAM,aAAa,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,aAAa,CAAuB,CAAC;QACvH,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,IAAI,KAAK,CAAC,qDAAqD,CAAC;aACxE,CAAC;QACJ,CAAC;QAED,2DAA2D;QAC3D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,4BAA4B,EAAE;YAC7D,UAAU,EAAE,cAAc;YAC1B,aAAa;YACb,UAAU;SACX,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC;QAEvC,OAAO;YACL,OAAO,EAAE,IAAI;YACb,IAAI,EAAE;gBACJ,cAAc;gBACd,aAAa;gBACb,UAAU;gBACV,MAAM;aACP;SACF,CAAC;IACJ,CAAC;CACF;AAED,SAAS,sBAAsB,CAAC,MAAc,EAAE,UAAkB;IAChE,2EAA2E;IAC3E,uEAAuE;IACvE,2EAA2E;IAC3E,oBAAoB;IACpB,OAAO;;;gBAGO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC7B,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,CAAC;QACrC,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BjB,CAAC,IAAI,EAAE,CAAC;AACT,CAAC"}
@@ -1,25 +0,0 @@
1
- import { BaseTask, type TaskResult } from "@db-lyon/flowkit";
2
- interface Options {
3
- graphPath: string;
4
- mesh: string;
5
- density?: number;
6
- minScale?: number;
7
- maxScale?: number;
8
- seed?: number;
9
- }
10
- /**
11
- * Builds a PCG graph that scatters static meshes on a Voxel Plugin
12
- * terrain. Pipeline: VoxelSampler → SurfaceSampler → DensityFilter →
13
- * TransformPoints → StaticMeshSpawner.
14
- *
15
- * Idempotency: if the graph already exists, the task assumes the caller
16
- * wants a fresh build and will append nodes. Callers expecting strict
17
- * idempotency should delete the graph first via `pcg(action="delete_graph")`
18
- * (or skip the call entirely when no rebuild is needed).
19
- */
20
- export default class ScatterMeshes extends BaseTask<Options> {
21
- get taskName(): string;
22
- protected validate(): void;
23
- execute(): Promise<TaskResult>;
24
- }
25
- export {};
@@ -1,52 +0,0 @@
1
- import { BaseTask } from "@db-lyon/flowkit";
2
- import { addNode, connectNodes, setNodeSettings } from "../shared/voxelGraph.js";
3
- /**
4
- * Builds a PCG graph that scatters static meshes on a Voxel Plugin
5
- * terrain. Pipeline: VoxelSampler → SurfaceSampler → DensityFilter →
6
- * TransformPoints → StaticMeshSpawner.
7
- *
8
- * Idempotency: if the graph already exists, the task assumes the caller
9
- * wants a fresh build and will append nodes. Callers expecting strict
10
- * idempotency should delete the graph first via `pcg(action="delete_graph")`
11
- * (or skip the call entirely when no rebuild is needed).
12
- */
13
- export default class ScatterMeshes extends BaseTask {
14
- get taskName() { return "voxel.scatter_meshes"; }
15
- validate() {
16
- if (!this.options.graphPath)
17
- throw new Error("graphPath is required");
18
- if (!this.options.mesh)
19
- throw new Error("mesh is required");
20
- }
21
- async execute() {
22
- const { graphPath, mesh, density = 0.25, minScale = 0.8, maxScale = 1.4, seed = 1 } = this.options;
23
- // Create the graph (no-op if pcg.create_graph rejects an existing one;
24
- // we proceed regardless so add_node can land on a pre-existing graph).
25
- await this.call("pcg.create_graph", { path: graphPath });
26
- const sampler = await addNode(this, graphPath, "voxelSampler");
27
- const surface = await addNode(this, graphPath, "surfaceSampler");
28
- const density_ = await addNode(this, graphPath, "densityFilter");
29
- const transform = await addNode(this, graphPath, "transformPoints");
30
- const spawner = await addNode(this, graphPath, "staticMeshSpawner");
31
- await setNodeSettings(this, graphPath, surface, { PointsPerSquaredMeter: density });
32
- await setNodeSettings(this, graphPath, density_, { LowerBound: 0.0, UpperBound: 1.0, Seed: seed });
33
- await setNodeSettings(this, graphPath, transform, { MinScale: minScale, MaxScale: maxScale, Seed: seed });
34
- await setNodeSettings(this, graphPath, spawner, { StaticMesh: mesh });
35
- await connectNodes(this, graphPath, sampler, surface);
36
- await connectNodes(this, graphPath, surface, density_);
37
- await connectNodes(this, graphPath, density_, transform);
38
- await connectNodes(this, graphPath, transform, spawner);
39
- return {
40
- success: true,
41
- data: {
42
- graphPath,
43
- nodes: { sampler, surface, density: density_, transform, spawner },
44
- mesh,
45
- },
46
- // No automatic rollback - PCG node creation/connection has no inverse
47
- // exposed by the bridge. Callers wanting safety should run inside a
48
- // flow with git_snapshot enabled.
49
- };
50
- }
51
- }
52
- //# sourceMappingURL=ScatterMeshes.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ScatterMeshes.js","sourceRoot":"","sources":["../../src/tasks/ScatterMeshes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAmB,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAWjF;;;;;;;;;GASG;AACH,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,QAAiB;IAC1D,IAAI,QAAQ,KAAa,OAAO,sBAAsB,CAAC,CAAC,CAAC;IAE/C,QAAQ;QAChB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACtE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,EAAE,QAAQ,GAAG,GAAG,EAAE,QAAQ,GAAG,GAAG,EAAE,IAAI,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAEnG,uEAAuE;QACvE,uEAAuE;QACvE,MAAM,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAEzD,MAAM,OAAO,GAAK,MAAM,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;QACjE,MAAM,OAAO,GAAK,MAAM,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;QACnE,MAAM,QAAQ,GAAI,MAAM,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;QAClE,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC;QACpE,MAAM,OAAO,GAAK,MAAM,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,mBAAmB,CAAC,CAAC;QAEtE,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAC,CAAC;QACtF,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAG,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACpG,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1G,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAExE,MAAM,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAI,OAAO,CAAC,CAAC;QACxD,MAAM,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAI,QAAQ,CAAC,CAAC;QACzD,MAAM,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAG,SAAS,CAAC,CAAC;QAC1D,MAAM,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAExD,OAAO;YACL,OAAO,EAAE,IAAI;YACb,IAAI,EAAE;gBACJ,SAAS;gBACT,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE;gBAClE,IAAI;aACL;YACD,sEAAsE;YACtE,oEAAoE;YACpE,kCAAkC;SACnC,CAAC;IACJ,CAAC;CACF"}
@@ -1,23 +0,0 @@
1
- import { BaseTask, type TaskResult } from "@db-lyon/flowkit";
2
- interface Options {
3
- graphPath: string;
4
- stampAsset: string;
5
- count?: number;
6
- radius?: number;
7
- seed?: number;
8
- }
9
- /**
10
- * Builds a PCG graph that places voxel stamp assets across a terrain. Pipeline:
11
- * VoxelSampler → SurfaceSampler → DensityFilter (thinning by `count`) →
12
- * VoxelStamp.
13
- *
14
- * `count` is a soft cap - the density filter approximates it from the sampled
15
- * point cloud, so the final placement count varies with the underlying voxel
16
- * volume density.
17
- */
18
- export default class SpawnStamps extends BaseTask<Options> {
19
- get taskName(): string;
20
- protected validate(): void;
21
- execute(): Promise<TaskResult>;
22
- }
23
- export {};
@@ -1,51 +0,0 @@
1
- import { BaseTask } from "@db-lyon/flowkit";
2
- import { addNode, connectNodes, setNodeSettings } from "../shared/voxelGraph.js";
3
- /**
4
- * Builds a PCG graph that places voxel stamp assets across a terrain. Pipeline:
5
- * VoxelSampler → SurfaceSampler → DensityFilter (thinning by `count`) →
6
- * VoxelStamp.
7
- *
8
- * `count` is a soft cap - the density filter approximates it from the sampled
9
- * point cloud, so the final placement count varies with the underlying voxel
10
- * volume density.
11
- */
12
- export default class SpawnStamps extends BaseTask {
13
- get taskName() { return "voxel.spawn_stamps"; }
14
- validate() {
15
- if (!this.options.graphPath)
16
- throw new Error("graphPath is required");
17
- if (!this.options.stampAsset)
18
- throw new Error("stampAsset is required");
19
- }
20
- async execute() {
21
- const { graphPath, stampAsset, count = 50, radius = 500, seed = 1 } = this.options;
22
- await this.call("pcg.create_graph", { path: graphPath });
23
- const sampler = await addNode(this, graphPath, "voxelSampler");
24
- const surface = await addNode(this, graphPath, "surfaceSampler");
25
- const density_ = await addNode(this, graphPath, "densityFilter");
26
- const stamp = await addNode(this, graphPath, "voxelStamp");
27
- // PointsPerSquaredMeter is approximated from count/radius² to give the
28
- // surface sampler a sensible density for the desired stamp count.
29
- const pointsPerSqM = Math.max(0.0001, count / (Math.PI * radius * radius));
30
- await setNodeSettings(this, graphPath, surface, {
31
- PointsPerSquaredMeter: pointsPerSqM,
32
- Radius: radius,
33
- Seed: seed,
34
- });
35
- await setNodeSettings(this, graphPath, density_, { LowerBound: 0.5, UpperBound: 1.0, Seed: seed });
36
- await setNodeSettings(this, graphPath, stamp, { StampAsset: stampAsset });
37
- await connectNodes(this, graphPath, sampler, surface);
38
- await connectNodes(this, graphPath, surface, density_);
39
- await connectNodes(this, graphPath, density_, stamp);
40
- return {
41
- success: true,
42
- data: {
43
- graphPath,
44
- nodes: { sampler, surface, density: density_, stamp },
45
- stampAsset,
46
- countEstimate: count,
47
- },
48
- };
49
- }
50
- }
51
- //# sourceMappingURL=SpawnStamps.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SpawnStamps.js","sourceRoot":"","sources":["../../src/tasks/SpawnStamps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAmB,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAUjF;;;;;;;;GAQG;AACH,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,QAAiB;IACxD,IAAI,QAAQ,KAAa,OAAO,oBAAoB,CAAC,CAAC,CAAC;IAE7C,QAAQ;QAChB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACtE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC1E,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,GAAG,EAAE,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QAEnF,MAAM,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAEzD,MAAM,OAAO,GAAI,MAAM,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;QAChE,MAAM,OAAO,GAAI,MAAM,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;QAClE,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;QACjE,MAAM,KAAK,GAAM,MAAM,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;QAE9D,uEAAuE;QACvE,kEAAkE;QAClE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;QAE3E,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE;YAC9C,qBAAqB,EAAE,YAAY;YACnC,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;QACH,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACnG,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAK,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;QAE7E,MAAM,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAG,OAAO,CAAC,CAAC;QACvD,MAAM,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAG,QAAQ,CAAC,CAAC;QACxD,MAAM,YAAY,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;QAErD,OAAO;YACL,OAAO,EAAE,IAAI;YACb,IAAI,EAAE;gBACJ,SAAS;gBACT,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE;gBACrD,UAAU;gBACV,aAAa,EAAE,KAAK;aACrB;SACF,CAAC;IACJ,CAAC;CACF"}
@@ -1,34 +0,0 @@
1
- # Voxel Plugin - Landscape actions
2
-
3
- This plugin contributes one landscape-side action for [Voxel Plugin](https://voxelplugin.com):
4
-
5
- - `landscape(action="voxel_bake_heightmap", ...)` - bake a region of a voxel terrain into a standard UE Landscape heightmap.
6
-
7
- ## When to use
8
-
9
- Use it when the user wants to convert a voxel terrain (or a region of it) into a standard Landscape actor for the parts of the workflow that need it: high-quality runtime LOD, foliage painting, landscape materials, etc.
10
-
11
- The reverse conversion (Landscape → voxel) is not part of this plugin.
12
-
13
- ## Typical sequence
14
-
15
- ```
16
- landscape(action="voxel_bake_heightmap",
17
- landscapeLabel="Landscape",
18
- bounds={ min: {x: 0, y: 0},
19
- max: {x: 8064, y: 8064} },
20
- resolution=1009)
21
- ```
22
-
23
- The action samples voxel heights on a `resolution x resolution` grid spanning `bounds`, writes a raw uint16 heightmap to `Intermediate/Voxel/voxel_heightmap.r16`, then imports it into the target landscape via the built-in `landscape.import_heightmap` action.
24
-
25
- ## Parameters
26
-
27
- - `landscapeLabel` (required) - the in-editor label of the target Landscape actor.
28
- - `bounds` (required) - `{ min: {x, y}, max: {x, y} }` in world centimetres.
29
- - `resolution` (optional, default 1009) - heightmap side length in samples. Use a UE-friendly value: 127, 253, 505, 1009, 2017, 4033, 8129.
30
-
31
- ## Notes
32
-
33
- - The Python escape hatch is used here because Voxel Plugin exposes its voxel heights to Python but not to PCG or the C++ bridge directly. The script is defensive: if `VoxelBlueprintLibrary` is not loaded it returns a clear error rather than a traceback.
34
- - Height mapping uses a centred uint16 range (offset +32768). If your voxel volume uses a different scale, adjust the script or wrap this action.
package/knowledge/pcg.md DELETED
@@ -1,50 +0,0 @@
1
- # Voxel Plugin - PCG actions
2
-
3
- This plugin contributes two PCG-side actions for [Voxel Plugin](https://voxelplugin.com) terrains:
4
-
5
- - `pcg(action="voxel_scatter_meshes", ...)` - build a graph that scatters static meshes on a voxel terrain via Voxel Sampler.
6
- - `pcg(action="voxel_spawn_stamps", ...)` - build a graph that places voxel stamp assets across a voxel terrain.
7
-
8
- ## When to use
9
-
10
- Reach for these when the user wants procedural placement on a **voxel** terrain. For standard UE Landscape, use the built-in PCG nodes directly.
11
-
12
- ## Typical sequence
13
-
14
- ```
15
- pcg(action="create_graph", path="/Game/PCG/MyScatter") # if no graph exists
16
- pcg(action="voxel_scatter_meshes",
17
- graphPath="/Game/PCG/MyScatter",
18
- mesh="/Game/Foliage/Rock01.Rock01",
19
- density=0.25)
20
- pcg(action="execute", graphPath="/Game/PCG/MyScatter") # materialise
21
- ```
22
-
23
- The full setup is also available as a flow:
24
-
25
- ```
26
- flow(action="run", flowName="voxel_scatter_setup", params={
27
- graphPath: "/Game/PCG/MyScatter",
28
- mesh: "/Game/Foliage/Rock01.Rock01"
29
- })
30
- ```
31
-
32
- ## Parameters
33
-
34
- `voxel_scatter_meshes`:
35
- - `graphPath` (required) - asset path of the PCG graph to build (created if missing).
36
- - `mesh` (required) - StaticMesh asset path to scatter.
37
- - `density` (optional, default 0.25) - points per square metre at the surface sampler.
38
- - `minScale` / `maxScale` (optional) - uniform scale jitter, default 0.8 .. 1.4.
39
- - `seed` (optional) - deterministic seed for the density filter and transform jitter.
40
-
41
- `voxel_spawn_stamps`:
42
- - `graphPath` (required).
43
- - `stampAsset` (required) - Voxel stamp asset path.
44
- - `count` (optional, default 50) - target stamp count; approximated via density.
45
- - `radius` (optional, default 500) - radius of the scatter region in cm.
46
- - `seed` (optional).
47
-
48
- ## Requirements
49
-
50
- The host project must have the Voxel Plugin enabled in its `.uproject` as `Voxel` (the `.uplugin` filename). The plugin installer warns at install time if `Voxel` is missing; the actions themselves will fail with a clear error if the runtime types aren't loaded.