Python: JSON Foliage Import and Export. Not Recognizing points? UE5-0

Hi there! I have been building a script to import point data from a JSON file generated in Houdini. It seems to work great for bringing the points in (with some small snags), but if I wanted to modify the points and send them back out, UE only recognizes foliage points which were created with the foliage tool, not the python-generated ones.
I HAVE A FEELING that it is because I’m attaching the points to the wrong world_context_object in the InstancedFoliageActor.add_instances() function… but I haven’t been able to sort that out properly.

Specifically when I use the unreal.InstancedFoliageActor.get_instance_transforms() function, it only returns points created with the foliage tool. Even though I can adjust ones brought in with JSON using that as well. The points DO delete when I use the remove_all_instances() function, they register when I switch to Foliage mode and look at the foliage used, but I don’t know how to access their transforms which I need.
I’ve attached the different scripts below. I’m not sure if the problem is in the import or the export, but I suspect import because the export script is pretty straightforward so far.

Before it is suggested, I’m also testing Houdini Engine but this JSON method is needed for a more specific problem. Thank you for any help!

The JSON structure being imported:

{
"oakTree/master/geo/oakTree0001": {
    "name": "oakTree0001",
    "index": 0,
    "tag": "Trees",
    "unreal_transforms": [
        [
            [239108.4531, -35297.6211, 2450.9072],
            [0.0013, -0.0257, -0.4446, 0.8953],
            [1.5956, 1.5956, 1.5956]
        ], [
            [-148679.1406, -24746.959, 2714.4912],
            [-0.0146, -0.014, -0.9997, -0.0128],
            [1.6463, 1.6463, 1.6463]
        ]
    ]
}

}

The python function for importing from JSON:

def import_and_layout_foliage_from_JSON(json_file, asset_path='/Game', import_meshes=False, import_source=0, delete_existing=1, layout=True):

sel, sel_class = get_first_selected_level_object()
if sel_class == 'InstancedFoliageActor':
  data = read_JSON(json_file)
  if data is not None:
      # Remove existing instances of ANY foliage in object if enabled.
      foliage_meshes_existing = sel.get_used_foliage_types()
      if delete_existing==2 and layout:
          for mesh in foliage_meshes_existing:
              sel.remove_all_instances(sel, mesh)
      
      # Process each JSON entry and create foliage from it.
      for path_key in data.keys():
          in_data = data[path_key]
          name_key = in_data['name']
          assets_to_create = name_key
          filename_key = None
          transform_list = in_data['unreal_transforms']
          if 'filename' in in_data:
              filename_key = in_data['filename']
              if import_meshes and len(filename_key):
                  assets_to_create = import_static_meshes(asset_path=asset_path+'/'+path_key, filepaths=[filename_key], import_source=import_source, combine_meshes=True)
              
          foliage_meshes_new = create_foliage_instanced_static_meshes(asset_path=asset_path+'/'+path_key, assets_to_create=[assets_to_create], create_foliage_folder=True)
          # Remove existing instances of SELECTED foliage in object if enabled.
          if delete_existing==1 and layout:
              for mesh in foliage_meshes_new:
                  sel.remove_all_instances(sel, mesh)
          
          # Add new instances
          if layout:
              point_array = unreal.Array(unreal.Transform)
              for point_list in transform_list:
                  transform = unreal.Transform()
                  trans_list = point_list[0]
                  rotate_list = point_list[1]
                  scale_list = point_list[2]
                  transform.set_editor_property('translation', trans_list)
                  transform.set_editor_property('rotation', rotate_list)
                  transform.set_editor_property('scale3d', scale_list)
                  point_array.append(transform)
              sel.add_instances(sel, foliage_meshes_new[0], point_array)
      
  else:
      unreal.log_error(
          '''The selected object is not of the class InstancedFoliageActor. Create terrain,
          add foliage, and select the foliage in the viewport then try again.'''
          )

The beginning of the python function for exporting to JSON:

def export_to_JSON():
"""
Returns a dictionary containing the transform data of the foliage when you select 
foliage (InstancedFoliageActor) created on terrain and run the function.

"""    
sel, sel_class = get_first_selected_level_object()
houdini_dict = {}

if sel_class == 'InstancedFoliageActor':
  fol_types = sel.get_used_foliage_types()
  for fol_type in fol_types:
      unreal.log(fol_type)
      fol_transforms = sel.get_instance_transforms(fol_type)
      unreal.log(fol_transforms)
      
  return houdini_dict

else:
    unreal.log_error(
        """The selected object is not of the class InstancedFoliageActor. Create terrain,
        add foliage, and select the foliage then try again."""
    )

Anyone have any ideas? I’m very stumped with this.

Hi buddy, are you planting trees using python code?

How do you switch to unreal Editor mode?

Can you help me

Hey, the idea with this at the time was to be able to import points generated from a point cloud made in houdini but have them be recognized as native foliage points in UE. They can come in, but they don’t get treated the same way as the UE points which makes it not very useful.

I am guessing the main thing you are wanting to do is use the foliage tools within Unreal to edit those points? Otherwise I would just put them on a instanced static mesh component on a blueprint. I have done this workflow before with JSON and that works great. But if you plan to inject it straight into the native foliage system, that might have some complications.

Yes, this is exactly what the goal was. Though, if they were brought in as an instanced static mesh, could you move the individual instances after the fact? I am thinking no, but worth asking.

Hi, I was also struggle with importing pointcloud data to instancedFoliageActor. and I able to import it with input correct world object to InstancedFoliageActor.add_instances() method

you may try change your code from
“sel.add_instances(sel, foliage_meshes_new[0], point_array)”
to “sel.add_instances(sel.get_world(), foliage_meshes_new[0], point_array)”

and just quick look, I think you made transform array that named “point_array” is a bit incorrect. you don’t need to “set_editor_property()” to modify Transform struct. it used for properties that you can see in editor slate.

try this way to make transform array. (I used dictionary data by the way.)

point_array = unreal.Array(unreal.Transform) ##or just point_array=
for transform_data in pointcloud:
transform = unreal.Transform()
quaternion = unreal.Quat(transform_data[“rot_quaternion”][0],
transform_data[“rot_quaternion”][1],
transform_data[“rot_quaternion”][2],
transform_data[“rot_quaternion”][3],
)
transform.translation = unreal.Vector(transform_data[“position”][0],
transform_data[“position”][1],
transform_data[“position”][2]
)

        transform.scale3d = unreal.Vector(transform_data["scale"][0],
                                        transform_data["scale"][1],
                                        transform_data["scale"][2],
                                        )
         point_array .append(transform)

I’m bad for explaining but hope you get some imformation for get throuh what you want to do.

1 Like