unreal-engine-mcp-server 0.2.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.
Files changed (155) hide show
  1. package/.dockerignore +57 -0
  2. package/.env.production +25 -0
  3. package/.eslintrc.json +54 -0
  4. package/.github/workflows/publish-mcp.yml +75 -0
  5. package/Dockerfile +54 -0
  6. package/LICENSE +21 -0
  7. package/Public/icon.png +0 -0
  8. package/README.md +209 -0
  9. package/claude_desktop_config_example.json +13 -0
  10. package/dist/cli.d.ts +3 -0
  11. package/dist/cli.js +7 -0
  12. package/dist/index.d.ts +31 -0
  13. package/dist/index.js +484 -0
  14. package/dist/prompts/index.d.ts +14 -0
  15. package/dist/prompts/index.js +38 -0
  16. package/dist/python-utils.d.ts +29 -0
  17. package/dist/python-utils.js +54 -0
  18. package/dist/resources/actors.d.ts +13 -0
  19. package/dist/resources/actors.js +83 -0
  20. package/dist/resources/assets.d.ts +23 -0
  21. package/dist/resources/assets.js +245 -0
  22. package/dist/resources/levels.d.ts +17 -0
  23. package/dist/resources/levels.js +94 -0
  24. package/dist/tools/actors.d.ts +51 -0
  25. package/dist/tools/actors.js +459 -0
  26. package/dist/tools/animation.d.ts +196 -0
  27. package/dist/tools/animation.js +579 -0
  28. package/dist/tools/assets.d.ts +21 -0
  29. package/dist/tools/assets.js +304 -0
  30. package/dist/tools/audio.d.ts +170 -0
  31. package/dist/tools/audio.js +416 -0
  32. package/dist/tools/blueprint.d.ts +144 -0
  33. package/dist/tools/blueprint.js +652 -0
  34. package/dist/tools/build_environment_advanced.d.ts +66 -0
  35. package/dist/tools/build_environment_advanced.js +484 -0
  36. package/dist/tools/consolidated-tool-definitions.d.ts +2598 -0
  37. package/dist/tools/consolidated-tool-definitions.js +607 -0
  38. package/dist/tools/consolidated-tool-handlers.d.ts +2 -0
  39. package/dist/tools/consolidated-tool-handlers.js +1050 -0
  40. package/dist/tools/debug.d.ts +185 -0
  41. package/dist/tools/debug.js +265 -0
  42. package/dist/tools/editor.d.ts +88 -0
  43. package/dist/tools/editor.js +365 -0
  44. package/dist/tools/engine.d.ts +30 -0
  45. package/dist/tools/engine.js +36 -0
  46. package/dist/tools/foliage.d.ts +155 -0
  47. package/dist/tools/foliage.js +525 -0
  48. package/dist/tools/introspection.d.ts +98 -0
  49. package/dist/tools/introspection.js +683 -0
  50. package/dist/tools/landscape.d.ts +158 -0
  51. package/dist/tools/landscape.js +375 -0
  52. package/dist/tools/level.d.ts +110 -0
  53. package/dist/tools/level.js +362 -0
  54. package/dist/tools/lighting.d.ts +159 -0
  55. package/dist/tools/lighting.js +1179 -0
  56. package/dist/tools/materials.d.ts +34 -0
  57. package/dist/tools/materials.js +146 -0
  58. package/dist/tools/niagara.d.ts +145 -0
  59. package/dist/tools/niagara.js +289 -0
  60. package/dist/tools/performance.d.ts +163 -0
  61. package/dist/tools/performance.js +412 -0
  62. package/dist/tools/physics.d.ts +189 -0
  63. package/dist/tools/physics.js +784 -0
  64. package/dist/tools/rc.d.ts +110 -0
  65. package/dist/tools/rc.js +363 -0
  66. package/dist/tools/sequence.d.ts +112 -0
  67. package/dist/tools/sequence.js +675 -0
  68. package/dist/tools/tool-definitions.d.ts +4919 -0
  69. package/dist/tools/tool-definitions.js +891 -0
  70. package/dist/tools/tool-handlers.d.ts +47 -0
  71. package/dist/tools/tool-handlers.js +830 -0
  72. package/dist/tools/ui.d.ts +171 -0
  73. package/dist/tools/ui.js +337 -0
  74. package/dist/tools/visual.d.ts +29 -0
  75. package/dist/tools/visual.js +67 -0
  76. package/dist/types/env.d.ts +10 -0
  77. package/dist/types/env.js +18 -0
  78. package/dist/types/index.d.ts +323 -0
  79. package/dist/types/index.js +28 -0
  80. package/dist/types/tool-types.d.ts +274 -0
  81. package/dist/types/tool-types.js +13 -0
  82. package/dist/unreal-bridge.d.ts +126 -0
  83. package/dist/unreal-bridge.js +992 -0
  84. package/dist/utils/cache-manager.d.ts +64 -0
  85. package/dist/utils/cache-manager.js +176 -0
  86. package/dist/utils/error-handler.d.ts +66 -0
  87. package/dist/utils/error-handler.js +243 -0
  88. package/dist/utils/errors.d.ts +133 -0
  89. package/dist/utils/errors.js +256 -0
  90. package/dist/utils/http.d.ts +26 -0
  91. package/dist/utils/http.js +135 -0
  92. package/dist/utils/logger.d.ts +12 -0
  93. package/dist/utils/logger.js +32 -0
  94. package/dist/utils/normalize.d.ts +17 -0
  95. package/dist/utils/normalize.js +49 -0
  96. package/dist/utils/response-validator.d.ts +34 -0
  97. package/dist/utils/response-validator.js +121 -0
  98. package/dist/utils/safe-json.d.ts +4 -0
  99. package/dist/utils/safe-json.js +97 -0
  100. package/dist/utils/stdio-redirect.d.ts +2 -0
  101. package/dist/utils/stdio-redirect.js +20 -0
  102. package/dist/utils/validation.d.ts +50 -0
  103. package/dist/utils/validation.js +173 -0
  104. package/mcp-config-example.json +14 -0
  105. package/package.json +63 -0
  106. package/server.json +60 -0
  107. package/src/cli.ts +7 -0
  108. package/src/index.ts +543 -0
  109. package/src/prompts/index.ts +51 -0
  110. package/src/python/editor_compat.py +181 -0
  111. package/src/python-utils.ts +57 -0
  112. package/src/resources/actors.ts +92 -0
  113. package/src/resources/assets.ts +251 -0
  114. package/src/resources/levels.ts +83 -0
  115. package/src/tools/actors.ts +480 -0
  116. package/src/tools/animation.ts +713 -0
  117. package/src/tools/assets.ts +305 -0
  118. package/src/tools/audio.ts +548 -0
  119. package/src/tools/blueprint.ts +736 -0
  120. package/src/tools/build_environment_advanced.ts +526 -0
  121. package/src/tools/consolidated-tool-definitions.ts +619 -0
  122. package/src/tools/consolidated-tool-handlers.ts +1093 -0
  123. package/src/tools/debug.ts +368 -0
  124. package/src/tools/editor.ts +360 -0
  125. package/src/tools/engine.ts +32 -0
  126. package/src/tools/foliage.ts +652 -0
  127. package/src/tools/introspection.ts +778 -0
  128. package/src/tools/landscape.ts +523 -0
  129. package/src/tools/level.ts +410 -0
  130. package/src/tools/lighting.ts +1316 -0
  131. package/src/tools/materials.ts +148 -0
  132. package/src/tools/niagara.ts +312 -0
  133. package/src/tools/performance.ts +549 -0
  134. package/src/tools/physics.ts +924 -0
  135. package/src/tools/rc.ts +437 -0
  136. package/src/tools/sequence.ts +791 -0
  137. package/src/tools/tool-definitions.ts +907 -0
  138. package/src/tools/tool-handlers.ts +941 -0
  139. package/src/tools/ui.ts +499 -0
  140. package/src/tools/visual.ts +60 -0
  141. package/src/types/env.ts +27 -0
  142. package/src/types/index.ts +414 -0
  143. package/src/types/tool-types.ts +343 -0
  144. package/src/unreal-bridge.ts +1118 -0
  145. package/src/utils/cache-manager.ts +213 -0
  146. package/src/utils/error-handler.ts +320 -0
  147. package/src/utils/errors.ts +312 -0
  148. package/src/utils/http.ts +184 -0
  149. package/src/utils/logger.ts +30 -0
  150. package/src/utils/normalize.ts +54 -0
  151. package/src/utils/response-validator.ts +145 -0
  152. package/src/utils/safe-json.ts +112 -0
  153. package/src/utils/stdio-redirect.ts +18 -0
  154. package/src/utils/validation.ts +212 -0
  155. package/tsconfig.json +33 -0
@@ -0,0 +1,66 @@
1
+ import { UnrealBridge } from '../unreal-bridge.js';
2
+ /**
3
+ * Advanced Build Environment Tools
4
+ * Implements procedural terrain and foliage using documented Unreal Engine Python APIs
5
+ */
6
+ export declare class BuildEnvironmentAdvanced {
7
+ private bridge;
8
+ constructor(bridge: UnrealBridge);
9
+ /**
10
+ * Create procedural terrain using ProceduralMeshComponent
11
+ * This works around the landscape API limitations
12
+ */
13
+ createProceduralTerrain(params: {
14
+ name: string;
15
+ location?: [number, number, number];
16
+ sizeX?: number;
17
+ sizeY?: number;
18
+ subdivisions?: number;
19
+ heightFunction?: string;
20
+ material?: string;
21
+ }): Promise<any>;
22
+ /**
23
+ * Create procedural foliage using ProceduralFoliageSpawner
24
+ * Uses the documented Unreal Engine API
25
+ */
26
+ createProceduralFoliage(params: {
27
+ name: string;
28
+ bounds: {
29
+ location: [number, number, number];
30
+ size: [number, number, number];
31
+ };
32
+ foliageTypes: Array<{
33
+ meshPath: string;
34
+ density: number;
35
+ minScale?: number;
36
+ maxScale?: number;
37
+ alignToNormal?: boolean;
38
+ randomYaw?: boolean;
39
+ }>;
40
+ seed?: number;
41
+ }): Promise<any>;
42
+ /**
43
+ * Add foliage instances using InstancedFoliageActor
44
+ * Direct instance placement approach
45
+ */
46
+ addFoliageInstances(params: {
47
+ foliageType: string;
48
+ transforms: Array<{
49
+ location: [number, number, number];
50
+ rotation?: [number, number, number];
51
+ scale?: [number, number, number];
52
+ }>;
53
+ }): Promise<any>;
54
+ /**
55
+ * Create landscape grass type for automatic foliage on landscape
56
+ */
57
+ createLandscapeGrassType(params: {
58
+ name: string;
59
+ meshPath: string;
60
+ density?: number;
61
+ minScale?: number;
62
+ maxScale?: number;
63
+ }): Promise<any>;
64
+ private parseResponse;
65
+ }
66
+ //# sourceMappingURL=build_environment_advanced.d.ts.map
@@ -0,0 +1,484 @@
1
+ /**
2
+ * Advanced Build Environment Tools
3
+ * Implements procedural terrain and foliage using documented Unreal Engine Python APIs
4
+ */
5
+ export class BuildEnvironmentAdvanced {
6
+ bridge;
7
+ constructor(bridge) {
8
+ this.bridge = bridge;
9
+ }
10
+ /**
11
+ * Create procedural terrain using ProceduralMeshComponent
12
+ * This works around the landscape API limitations
13
+ */
14
+ async createProceduralTerrain(params) {
15
+ const pythonScript = `
16
+ import unreal
17
+ import json
18
+ import math
19
+
20
+ name = ${JSON.stringify(params.name)}
21
+ location = unreal.Vector(${params.location?.[0] || 0}, ${params.location?.[1] || 0}, ${params.location?.[2] || 0})
22
+ size_x = ${params.sizeX || 2000}
23
+ size_y = ${params.sizeY || 2000}
24
+ subdivisions = ${params.subdivisions || 50}
25
+ height_function = ${JSON.stringify(params.heightFunction || 'math.sin(x/100) * 50 + math.cos(y/100) * 30')}
26
+
27
+ result = {}
28
+
29
+ try:
30
+ # Get editor subsystem
31
+ subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
32
+
33
+ # Create ProceduralMeshActor
34
+ proc_actor = None
35
+ proc_mesh_comp = None
36
+
37
+ # Try ProceduralMeshActor first
38
+ try:
39
+ proc_actor = subsys.spawn_actor_from_class(
40
+ unreal.ProceduralMeshActor,
41
+ location,
42
+ unreal.Rotator(0, 0, 0)
43
+ )
44
+ proc_actor.set_actor_label(f"{name}_ProceduralTerrain")
45
+ proc_mesh_comp = proc_actor.get_component_by_class(unreal.ProceduralMeshComponent)
46
+ except:
47
+ # Fallback: Create empty actor and add ProceduralMeshComponent
48
+ proc_actor = subsys.spawn_actor_from_class(
49
+ unreal.Actor,
50
+ location,
51
+ unreal.Rotator(0, 0, 0)
52
+ )
53
+ proc_actor.set_actor_label(f"{name}_ProceduralTerrain")
54
+
55
+ # Add procedural mesh component
56
+ proc_mesh_comp = unreal.ProceduralMeshComponent()
57
+ proc_actor.add_instance_component(proc_mesh_comp)
58
+ proc_mesh_comp.register_component()
59
+
60
+ if proc_mesh_comp:
61
+ # Generate terrain mesh
62
+ vertices = []
63
+ triangles = []
64
+ normals = []
65
+ uvs = []
66
+ vertex_colors = []
67
+
68
+ step_x = size_x / subdivisions
69
+ step_y = size_y / subdivisions
70
+
71
+ # Create vertices with height variation
72
+ for y in range(subdivisions + 1):
73
+ for x in range(subdivisions + 1):
74
+ # Position
75
+ vert_x = x * step_x - size_x / 2
76
+ vert_y = y * step_y - size_y / 2
77
+
78
+ # Calculate height using the provided function
79
+ try:
80
+ vert_z = eval(height_function, {"x": vert_x, "y": vert_y, "math": math})
81
+ except:
82
+ vert_z = 0 # Fallback to flat if function fails
83
+
84
+ vertices.append(unreal.Vector(vert_x, vert_y, vert_z))
85
+ normals.append(unreal.Vector(0, 0, 1)) # Will be recalculated
86
+ uvs.append(unreal.Vector2D(x / subdivisions, y / subdivisions))
87
+
88
+ # Color based on height
89
+ height_normalized = min(1.0, max(0.0, (vert_z + 100) / 200))
90
+ vertex_colors.append(unreal.LinearColor(height_normalized, 1 - height_normalized, 0.2, 1))
91
+
92
+ # Create triangles
93
+ for y in range(subdivisions):
94
+ for x in range(subdivisions):
95
+ idx = y * (subdivisions + 1) + x
96
+
97
+ # First triangle
98
+ triangles.extend([idx, idx + subdivisions + 1, idx + 1])
99
+ # Second triangle
100
+ triangles.extend([idx + 1, idx + subdivisions + 1, idx + subdivisions + 2])
101
+
102
+ # Create mesh section
103
+ proc_mesh_comp.create_mesh_section_linear_color(
104
+ 0, # Section index
105
+ vertices,
106
+ triangles,
107
+ normals,
108
+ uvs,
109
+ vertex_colors,
110
+ [], # Tangents
111
+ True # Create collision
112
+ )
113
+
114
+ # Apply material if specified
115
+ if ${JSON.stringify(params.material || '')}:
116
+ material = unreal.EditorAssetLibrary.load_asset(${JSON.stringify(params.material || '/Engine/MapTemplates/Materials/BasicGrid01')})
117
+ if material:
118
+ proc_mesh_comp.set_material(0, material)
119
+
120
+ # Enable collision
121
+ proc_mesh_comp.set_collision_enabled(unreal.CollisionEnabled.QUERY_AND_PHYSICS)
122
+
123
+ result = {
124
+ "success": True,
125
+ "message": f"Created procedural terrain '{name}'",
126
+ "actor_name": proc_actor.get_actor_label(),
127
+ "vertices": len(vertices),
128
+ "triangles": len(triangles) // 3,
129
+ "size": [size_x, size_y],
130
+ "subdivisions": subdivisions
131
+ }
132
+ else:
133
+ result = {"success": False, "error": "Could not create ProceduralMeshComponent"}
134
+
135
+ except Exception as e:
136
+ result = {"success": False, "error": str(e)}
137
+
138
+ print(f"RESULT:{json.dumps(result)}")
139
+ `.trim();
140
+ const response = await this.bridge.executePython(pythonScript);
141
+ const output = this.parseResponse(response);
142
+ const match = output.match(/RESULT:({.*})/);
143
+ if (match) {
144
+ try {
145
+ return JSON.parse(match[1]);
146
+ }
147
+ catch (_e) {
148
+ return { success: false, error: 'Failed to parse result', details: output };
149
+ }
150
+ }
151
+ return { success: false, error: 'No result found', output };
152
+ }
153
+ /**
154
+ * Create procedural foliage using ProceduralFoliageSpawner
155
+ * Uses the documented Unreal Engine API
156
+ */
157
+ async createProceduralFoliage(params) {
158
+ const pythonScript = `
159
+ import unreal
160
+ import json
161
+
162
+ name = ${JSON.stringify(params.name)}
163
+ bounds_location = unreal.Vector(${params.bounds.location[0]}, ${params.bounds.location[1]}, ${params.bounds.location[2]})
164
+ bounds_size = unreal.Vector(${params.bounds.size[0]}, ${params.bounds.size[1]}, ${params.bounds.size[2]})
165
+ seed = ${params.seed || 12345}
166
+
167
+ result = {}
168
+
169
+ try:
170
+ subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
171
+ asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
172
+
173
+ # Create ProceduralFoliageVolume
174
+ volume_actor = subsys.spawn_actor_from_class(
175
+ unreal.ProceduralFoliageVolume,
176
+ bounds_location,
177
+ unreal.Rotator(0, 0, 0)
178
+ )
179
+ volume_actor.set_actor_label(f"{name}_ProceduralFoliageVolume")
180
+ volume_actor.set_actor_scale3d(bounds_size / 100) # Scale is in meters
181
+
182
+ # Get the procedural component
183
+ proc_comp = volume_actor.procedural_component
184
+ if not proc_comp:
185
+ proc_comp = volume_actor.get_component_by_class(unreal.ProceduralFoliageComponent)
186
+
187
+ if proc_comp:
188
+ # Create ProceduralFoliageSpawner asset
189
+ spawner_path = f"/Game/Foliage/Spawners/{name}_Spawner"
190
+ package_path = "/Game/Foliage/Spawners"
191
+
192
+ # Ensure directory exists
193
+ if not unreal.EditorAssetLibrary.does_directory_exist(package_path):
194
+ unreal.EditorAssetLibrary.make_directory(package_path)
195
+
196
+ # Create spawner
197
+ spawner = None
198
+ if unreal.EditorAssetLibrary.does_asset_exist(spawner_path):
199
+ spawner = unreal.EditorAssetLibrary.load_asset(spawner_path)
200
+ else:
201
+ # Create new spawner
202
+ factory = unreal.ProceduralFoliageSpawnerFactory()
203
+ spawner = asset_tools.create_asset(
204
+ asset_name=f"{name}_Spawner",
205
+ package_path=package_path,
206
+ asset_class=unreal.ProceduralFoliageSpawner,
207
+ factory=factory
208
+ )
209
+
210
+ if spawner:
211
+ # Configure spawner
212
+ spawner.random_seed = seed
213
+ spawner.tile_size = max(bounds_size.x, bounds_size.y)
214
+
215
+ # Create foliage types
216
+ foliage_types = []
217
+ for ft_params in ${JSON.stringify(params.foliageTypes)}:
218
+ # Load mesh
219
+ mesh = unreal.EditorAssetLibrary.load_asset(ft_params['meshPath'])
220
+ if mesh:
221
+ # Create FoliageTypeObject
222
+ ft_obj = unreal.FoliageTypeObject()
223
+
224
+ # Try to create or load FoliageType_InstancedStaticMesh
225
+ ft_asset_name = f"FT_{name}_{len(foliage_types)}"
226
+ ft_asset_path = f"/Game/Foliage/Types/{ft_asset_name}"
227
+
228
+ ft_asset = None
229
+ if unreal.EditorAssetLibrary.does_asset_exist(ft_asset_path):
230
+ ft_asset = unreal.EditorAssetLibrary.load_asset(ft_asset_path)
231
+ else:
232
+ # Create simple foliage type
233
+ ft_asset = unreal.FoliageType_InstancedStaticMesh()
234
+
235
+ if ft_asset:
236
+ # Configure foliage type
237
+ ft_asset.mesh = mesh
238
+ ft_asset.density = ft_params.get('density', 1.0)
239
+ ft_asset.random_yaw = ft_params.get('randomYaw', True)
240
+ ft_asset.align_to_normal = ft_params.get('alignToNormal', True)
241
+
242
+ min_scale = ft_params.get('minScale', 0.8)
243
+ max_scale = ft_params.get('maxScale', 1.2)
244
+ ft_asset.scale_x = unreal.FloatInterval(min_scale, max_scale)
245
+ ft_asset.scale_y = unreal.FloatInterval(min_scale, max_scale)
246
+ ft_asset.scale_z = unreal.FloatInterval(min_scale, max_scale)
247
+
248
+ ft_obj.foliage_type_object = ft_asset
249
+ foliage_types.append(ft_obj)
250
+
251
+ # Set foliage types on spawner
252
+ spawner.foliage_types = foliage_types
253
+
254
+ # Assign spawner to component
255
+ proc_comp.foliage_spawner = spawner
256
+
257
+ # Save spawner asset
258
+ unreal.EditorAssetLibrary.save_asset(spawner.get_path_name())
259
+
260
+ # Resimulate
261
+ try:
262
+ unreal.ProceduralFoliageEditorLibrary.resimulate_procedural_foliage_volumes([volume_actor])
263
+ result['resimulated'] = True
264
+ except:
265
+ # Manual simulation
266
+ spawner.simulate(num_steps=-1)
267
+ result['resimulated'] = False
268
+ result['note'] = 'Used manual simulation'
269
+
270
+ result['success'] = True
271
+ result['message'] = f"Created procedural foliage volume '{name}'"
272
+ result['volume_actor'] = volume_actor.get_actor_label()
273
+ result['spawner_path'] = spawner.get_path_name()
274
+ result['foliage_types_count'] = len(foliage_types)
275
+ else:
276
+ result['success'] = False
277
+ result['error'] = 'Could not create ProceduralFoliageSpawner'
278
+ else:
279
+ result['success'] = False
280
+ result['error'] = 'Could not get ProceduralFoliageComponent'
281
+
282
+ except Exception as e:
283
+ result['success'] = False
284
+ result['error'] = str(e)
285
+
286
+ print(f"RESULT:{json.dumps(result)}")
287
+ `.trim();
288
+ const response = await this.bridge.executePython(pythonScript);
289
+ const output = this.parseResponse(response);
290
+ const match = output.match(/RESULT:({.*})/);
291
+ if (match) {
292
+ try {
293
+ return JSON.parse(match[1]);
294
+ }
295
+ catch (_e) {
296
+ return { success: false, error: 'Failed to parse result', details: output };
297
+ }
298
+ }
299
+ return { success: false, error: 'No result found', output };
300
+ }
301
+ /**
302
+ * Add foliage instances using InstancedFoliageActor
303
+ * Direct instance placement approach
304
+ */
305
+ async addFoliageInstances(params) {
306
+ const pythonScript = `
307
+ import unreal
308
+ import json
309
+
310
+ foliage_type_path = ${JSON.stringify(params.foliageType)}
311
+ transforms_data = ${JSON.stringify(params.transforms)}
312
+
313
+ result = {}
314
+
315
+ try:
316
+ # Get world context
317
+ editor_subsystem = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem)
318
+ world = editor_subsystem.get_editor_world()
319
+
320
+ # Load foliage type or mesh
321
+ foliage_asset = unreal.EditorAssetLibrary.load_asset(foliage_type_path)
322
+
323
+ if foliage_asset:
324
+ # Prepare transforms
325
+ transforms = []
326
+ for t_data in transforms_data:
327
+ location = unreal.Vector(t_data['location'][0], t_data['location'][1], t_data['location'][2])
328
+ rotation = unreal.Rotator(
329
+ t_data.get('rotation', [0, 0, 0])[0],
330
+ t_data.get('rotation', [0, 0, 0])[1],
331
+ t_data.get('rotation', [0, 0, 0])[2]
332
+ )
333
+ scale = unreal.Vector(
334
+ t_data.get('scale', [1, 1, 1])[0],
335
+ t_data.get('scale', [1, 1, 1])[1],
336
+ t_data.get('scale', [1, 1, 1])[2]
337
+ )
338
+
339
+ transform = unreal.Transform(location, rotation, scale)
340
+ transforms.append(transform)
341
+
342
+ # Add instances using InstancedFoliageActor
343
+ unreal.InstancedFoliageActor.add_instances(
344
+ world,
345
+ foliage_asset,
346
+ transforms
347
+ )
348
+
349
+ result['success'] = True
350
+ result['message'] = f"Added {len(transforms)} foliage instances"
351
+ result['instances_count'] = len(transforms)
352
+ else:
353
+ result['success'] = False
354
+ result['error'] = f"Could not load foliage asset: {foliage_type_path}"
355
+
356
+ except Exception as e:
357
+ result['success'] = False
358
+ result['error'] = str(e)
359
+
360
+ print(f"RESULT:{json.dumps(result)}")
361
+ `.trim();
362
+ const response = await this.bridge.executePython(pythonScript);
363
+ const output = this.parseResponse(response);
364
+ const match = output.match(/RESULT:({.*})/);
365
+ if (match) {
366
+ try {
367
+ return JSON.parse(match[1]);
368
+ }
369
+ catch (_e) {
370
+ return { success: false, error: 'Failed to parse result', details: output };
371
+ }
372
+ }
373
+ return { success: false, error: 'No result found', output };
374
+ }
375
+ /**
376
+ * Create landscape grass type for automatic foliage on landscape
377
+ */
378
+ async createLandscapeGrassType(params) {
379
+ const pythonScript = `
380
+ import unreal
381
+ import json
382
+
383
+ name = ${JSON.stringify(params.name)}
384
+ mesh_path = ${JSON.stringify(params.meshPath)}
385
+ density = ${params.density || 1.0}
386
+ min_scale = ${params.minScale || 0.8}
387
+ max_scale = ${params.maxScale || 1.2}
388
+
389
+ result = {}
390
+
391
+ try:
392
+ asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
393
+
394
+ # Create directory
395
+ package_path = "/Game/Landscape/GrassTypes"
396
+ if not unreal.EditorAssetLibrary.does_directory_exist(package_path):
397
+ unreal.EditorAssetLibrary.make_directory(package_path)
398
+
399
+ # Create LandscapeGrassType
400
+ grass_type_path = f"{package_path}/{name}"
401
+
402
+ if not unreal.EditorAssetLibrary.does_asset_exist(grass_type_path):
403
+ # Create using factory
404
+ factory = unreal.LandscapeGrassTypeFactory()
405
+ grass_type = asset_tools.create_asset(
406
+ asset_name=name,
407
+ package_path=package_path,
408
+ asset_class=unreal.LandscapeGrassType,
409
+ factory=factory
410
+ )
411
+
412
+ if grass_type:
413
+ # Load mesh
414
+ mesh = unreal.EditorAssetLibrary.load_asset(mesh_path)
415
+ if mesh:
416
+ # Configure grass type
417
+ grass_variety = unreal.GrassVariety()
418
+ grass_variety.grass_mesh = mesh
419
+ grass_variety.grass_density = density * 100 # Convert to per square meter
420
+ grass_variety.use_grid = True
421
+ grass_variety.placement_jitter = 1.0
422
+ grass_variety.start_cull_distance = 10000
423
+ grass_variety.end_cull_distance = 20000
424
+ grass_variety.min_lod = -1
425
+ grass_variety.scaling = unreal.GrassScaling.UNIFORM
426
+ grass_variety.scale_x = unreal.FloatInterval(min_scale, max_scale)
427
+ grass_variety.scale_y = unreal.FloatInterval(min_scale, max_scale)
428
+ grass_variety.scale_z = unreal.FloatInterval(min_scale, max_scale)
429
+ grass_variety.random_rotation = True
430
+ grass_variety.align_to_surface = True
431
+
432
+ grass_type.grass_varieties = [grass_variety]
433
+
434
+ # Save asset
435
+ unreal.EditorAssetLibrary.save_asset(grass_type.get_path_name())
436
+
437
+ result['success'] = True
438
+ result['message'] = f"Created landscape grass type '{name}'"
439
+ result['asset_path'] = grass_type.get_path_name()
440
+ else:
441
+ result['success'] = False
442
+ result['error'] = f"Could not load mesh: {mesh_path}"
443
+ else:
444
+ result['success'] = False
445
+ result['error'] = "Could not create LandscapeGrassType"
446
+ else:
447
+ result['success'] = False
448
+ result['error'] = f"Grass type already exists: {grass_type_path}"
449
+
450
+ except Exception as e:
451
+ result['success'] = False
452
+ result['error'] = str(e)
453
+
454
+ print(f"RESULT:{json.dumps(result)}")
455
+ `.trim();
456
+ const response = await this.bridge.executePython(pythonScript);
457
+ const output = this.parseResponse(response);
458
+ const match = output.match(/RESULT:({.*})/);
459
+ if (match) {
460
+ try {
461
+ return JSON.parse(match[1]);
462
+ }
463
+ catch (_e) {
464
+ return { success: false, error: 'Failed to parse result', details: output };
465
+ }
466
+ }
467
+ return { success: false, error: 'No result found', output };
468
+ }
469
+ parseResponse(response) {
470
+ if (response && typeof response === 'object') {
471
+ if (response.LogOutput && Array.isArray(response.LogOutput)) {
472
+ return response.LogOutput.map((log) => log.Output || '').join('');
473
+ }
474
+ else if (response.CommandResult) {
475
+ return response.CommandResult;
476
+ }
477
+ else if (response.ReturnValue) {
478
+ return JSON.stringify(response);
479
+ }
480
+ }
481
+ return typeof response === 'string' ? response : JSON.stringify(response);
482
+ }
483
+ }
484
+ //# sourceMappingURL=build_environment_advanced.js.map