unreal-engine-mcp-server 0.3.1 → 0.4.0

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.
@@ -53,17 +53,8 @@ try:
53
53
  proc_mesh_comp = proc_actor.get_component_by_class(unreal.ProceduralMeshComponent)
54
54
  except:
55
55
  # Fallback: Create empty actor and add ProceduralMeshComponent
56
- proc_actor = subsys.spawn_actor_from_class(
57
- unreal.Actor,
58
- location,
59
- unreal.Rotator(0, 0, 0)
60
- )
61
- proc_actor.set_actor_label(f"{name}_ProceduralTerrain")
62
-
63
- # Add procedural mesh component
64
- proc_mesh_comp = unreal.ProceduralMeshComponent()
65
- proc_actor.add_instance_component(proc_mesh_comp)
66
- proc_mesh_comp.register_component()
56
+ # If spawning ProceduralMeshActor failed, surface a clear error about the plugin requirement
57
+ raise Exception("Failed to spawn ProceduralMeshActor. Ensure the 'Procedural Mesh Component' plugin is enabled and available.")
67
58
 
68
59
  if proc_mesh_comp:
69
60
  # Generate terrain mesh
@@ -193,6 +184,10 @@ try:
193
184
  subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
194
185
  asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
195
186
 
187
+ # Validate Procedural Foliage plugin/classes are available
188
+ if not hasattr(unreal, 'ProceduralFoliageVolume') or not hasattr(unreal, 'ProceduralFoliageSpawner'):
189
+ raise Exception("Procedural Foliage plugin not available. Please enable the 'Procedural Foliage' plugin and try again.")
190
+
196
191
  # Create ProceduralFoliageVolume
197
192
  volume_actor = subsys.spawn_actor_from_class(
198
193
  unreal.ProceduralFoliageVolume,
@@ -200,7 +195,7 @@ try:
200
195
  unreal.Rotator(0, 0, 0)
201
196
  )
202
197
  volume_actor.set_actor_label(f"{name}_ProceduralFoliageVolume")
203
- volume_actor.set_actor_scale3d(bounds_size / 100) # Scale is in meters
198
+ volume_actor.set_actor_scale3d(unreal.Vector(bounds_size.x/100.0, bounds_size.y/100.0, bounds_size.z/100.0)) # Scale is in meters
204
199
 
205
200
  # Get the procedural component
206
201
  proc_comp = volume_actor.procedural_component
@@ -231,13 +226,14 @@ try:
231
226
  )
232
227
 
233
228
  if spawner:
234
- # Configure spawner
235
- spawner.random_seed = seed
236
- spawner.tile_size = max(bounds_size.x, bounds_size.y)
229
+ # Configure spawner (use set_editor_property for read-only attributes)
230
+ spawner.set_editor_property('random_seed', seed)
231
+ spawner.set_editor_property('tile_size', max(bounds_size.x, bounds_size.y))
237
232
 
238
233
  # Create foliage types
239
234
  foliage_types = []
240
- for ft_params in ${JSON.stringify(params.foliageTypes)}:
235
+ ft_input = json.loads(r'''${JSON.stringify(params.foliageTypes)}''')
236
+ for ft_params in ft_input:
241
237
  # Load mesh
242
238
  mesh = unreal.EditorAssetLibrary.load_asset(ft_params['meshPath'])
243
239
  if mesh:
@@ -256,26 +252,26 @@ try:
256
252
  ft_asset = unreal.FoliageType_InstancedStaticMesh()
257
253
 
258
254
  if ft_asset:
259
- # Configure foliage type
260
- ft_asset.mesh = mesh
261
- ft_asset.density = ft_params.get('density', 1.0)
262
- ft_asset.random_yaw = ft_params.get('randomYaw', True)
263
- ft_asset.align_to_normal = ft_params.get('alignToNormal', True)
255
+ # Configure foliage type (use set_editor_property)
256
+ ft_asset.set_editor_property('mesh', mesh)
257
+ ft_asset.set_editor_property('density', ft_params.get('density', 1.0))
258
+ ft_asset.set_editor_property('random_yaw', ft_params.get('randomYaw', True))
259
+ ft_asset.set_editor_property('align_to_normal', ft_params.get('alignToNormal', True))
264
260
 
265
261
  min_scale = ft_params.get('minScale', 0.8)
266
262
  max_scale = ft_params.get('maxScale', 1.2)
267
- ft_asset.scale_x = unreal.FloatInterval(min_scale, max_scale)
268
- ft_asset.scale_y = unreal.FloatInterval(min_scale, max_scale)
269
- ft_asset.scale_z = unreal.FloatInterval(min_scale, max_scale)
263
+ ft_asset.set_editor_property('scale_x', unreal.FloatInterval(min_scale, max_scale))
264
+ ft_asset.set_editor_property('scale_y', unreal.FloatInterval(min_scale, max_scale))
265
+ ft_asset.set_editor_property('scale_z', unreal.FloatInterval(min_scale, max_scale))
270
266
 
271
- ft_obj.foliage_type_object = ft_asset
267
+ ft_obj.set_editor_property('foliage_type_object', ft_asset)
272
268
  foliage_types.append(ft_obj)
273
269
 
274
270
  # Set foliage types on spawner
275
- spawner.foliage_types = foliage_types
271
+ spawner.set_editor_property('foliage_types', foliage_types)
276
272
 
277
273
  # Assign spawner to component
278
- proc_comp.foliage_spawner = spawner
274
+ proc_comp.set_editor_property('foliage_spawner', spawner)
279
275
 
280
276
  # Save spawner asset
281
277
  unreal.EditorAssetLibrary.save_asset(spawner.get_path_name())
@@ -455,23 +451,31 @@ try:
455
451
  # Load mesh
456
452
  mesh = unreal.EditorAssetLibrary.load_asset(mesh_path)
457
453
  if mesh:
458
- # Configure grass type
454
+ # Configure grass type (use set_editor_property)
459
455
  grass_variety = unreal.GrassVariety()
460
- grass_variety.grass_mesh = mesh
461
- grass_variety.grass_density = density * 100 # Convert to per square meter
462
- grass_variety.use_grid = True
463
- grass_variety.placement_jitter = 1.0
464
- grass_variety.start_cull_distance = 10000
465
- grass_variety.end_cull_distance = 20000
466
- grass_variety.min_lod = -1
467
- grass_variety.scaling = unreal.GrassScaling.UNIFORM
468
- grass_variety.scale_x = unreal.FloatInterval(min_scale, max_scale)
469
- grass_variety.scale_y = unreal.FloatInterval(min_scale, max_scale)
470
- grass_variety.scale_z = unreal.FloatInterval(min_scale, max_scale)
471
- grass_variety.random_rotation = True
472
- grass_variety.align_to_surface = True
456
+ grass_variety.set_editor_property('grass_mesh', mesh)
457
+ # GrassDensity is PerPlatformFloat in UE5+; set via struct instance
458
+ pp_density = unreal.PerPlatformFloat()
459
+ pp_density.set_editor_property('Default', float(density * 100.0))
460
+ grass_variety.set_editor_property('grass_density', pp_density)
461
+ grass_variety.set_editor_property('use_grid', True)
462
+ grass_variety.set_editor_property('placement_jitter', 1.0)
463
+ # Set cull distances as PerPlatformInt and LOD as int (engine uses mixed types here)
464
+ pp_start = unreal.PerPlatformInt()
465
+ pp_start.set_editor_property('Default', 10000)
466
+ grass_variety.set_editor_property('start_cull_distance', pp_start)
467
+ pp_end = unreal.PerPlatformInt()
468
+ pp_end.set_editor_property('Default', 20000)
469
+ grass_variety.set_editor_property('end_cull_distance', pp_end)
470
+ grass_variety.set_editor_property('min_lod', -1)
471
+ grass_variety.set_editor_property('scaling', unreal.GrassScaling.UNIFORM)
472
+ grass_variety.set_editor_property('scale_x', unreal.FloatInterval(min_scale, max_scale))
473
+ grass_variety.set_editor_property('scale_y', unreal.FloatInterval(min_scale, max_scale))
474
+ grass_variety.set_editor_property('scale_z', unreal.FloatInterval(min_scale, max_scale))
475
+ grass_variety.set_editor_property('random_rotation', True)
476
+ grass_variety.set_editor_property('align_to_surface', True)
473
477
 
474
- grass_type.grass_varieties = [grass_variety]
478
+ grass_type.set_editor_property('grass_varieties', [grass_variety])
475
479
 
476
480
  # Save asset
477
481
  unreal.EditorAssetLibrary.save_asset(grass_type.get_path_name())
@@ -526,7 +526,7 @@ Examples:
526
526
  properties: {
527
527
  action: {
528
528
  type: 'string',
529
- enum: ['create_landscape', 'sculpt', 'add_foliage', 'paint_foliage'],
529
+ enum: ['create_landscape', 'sculpt', 'add_foliage', 'paint_foliage', 'create_procedural_terrain', 'create_procedural_foliage', 'add_foliage_instances', 'create_landscape_grass_type'],
530
530
  description: 'Environment action'
531
531
  },
532
532
  // Common
@@ -538,9 +538,52 @@ Examples:
538
538
  type: 'string',
539
539
  description: 'Sculpt tool (Sculpt, Smooth, Flatten, etc.)'
540
540
  },
541
- // Foliage
541
+ // Advanced: procedural terrain
542
+ location: {
543
+ type: 'object',
544
+ properties: { x: { type: 'number' }, y: { type: 'number' }, z: { type: 'number' } }
545
+ },
546
+ subdivisions: { type: 'number' },
547
+ heightFunction: { type: 'string' },
548
+ materialPath: { type: 'string' },
549
+ // Advanced: procedural foliage
550
+ bounds: {
551
+ type: 'object',
552
+ properties: {
553
+ location: { type: 'object', properties: { x: { type: 'number' }, y: { type: 'number' }, z: { type: 'number' } } },
554
+ size: { type: 'object', properties: { x: { type: 'number' }, y: { type: 'number' }, z: { type: 'number' } } }
555
+ }
556
+ },
557
+ foliageTypes: {
558
+ type: 'array',
559
+ items: {
560
+ type: 'object',
561
+ properties: {
562
+ meshPath: { type: 'string' },
563
+ density: { type: 'number' },
564
+ minScale: { type: 'number' },
565
+ maxScale: { type: 'number' },
566
+ alignToNormal: { type: 'boolean' },
567
+ randomYaw: { type: 'boolean' }
568
+ }
569
+ }
570
+ },
571
+ seed: { type: 'number' },
572
+ // Advanced: direct foliage instances
573
+ foliageType: { type: 'string' },
574
+ transforms: {
575
+ type: 'array',
576
+ items: {
577
+ type: 'object',
578
+ properties: {
579
+ location: { type: 'object', properties: { x: { type: 'number' }, y: { type: 'number' }, z: { type: 'number' } } },
580
+ rotation: { type: 'object', properties: { pitch: { type: 'number' }, yaw: { type: 'number' }, roll: { type: 'number' } } },
581
+ scale: { type: 'object', properties: { x: { type: 'number' }, y: { type: 'number' }, z: { type: 'number' } } }
582
+ }
583
+ }
584
+ },
585
+ // Foliage (for add_foliage)
542
586
  meshPath: { type: 'string', description: 'Mesh path' },
543
- foliageType: { type: 'string', description: 'Foliage type' },
544
587
  density: { type: 'number', description: 'Density' },
545
588
  // Painting
546
589
  position: {