Hi,

I tried to generate a field of mining resources (asteroids, etc.) in 3D space. As visual instances I used different MultiMeshInstances (one for each possible mesh) feeded by a MeshLibrary. Within a script I generate random positions, rotations and scalings and applied them to the different MultiMeshInstances.

func generate_field() -> void:
	for _element_index in range(max_element_count):
		var index: int = rng.get_random_int(0, _multi_mesh_instances.size()-1)
		var mmi = get_multi_mesh_instance(index)
		# get an unused position
		var element_position = Vector3.ZERO
		while not element_position in _positions:
			element_position = rng.get_random_vector_3d(-extension, extension) * element_density # !!! not clamped atm, so extension will be bigger
			if not element_position in _positions:
				_positions.append(element_position)
				_position_indices.append(index)
		
		var element_rotation_amount = rng.get_random_float(0, 359)
		var scale_factor = rng.get_random_float(min_scale_factor, max_scale_factor)
		var element_scale = Vector3.ONE * scale_factor
		var xform: Transform = Transform.translated(element_position).scaled(element_scale).rotated(Vector3.ONE.normalized(), element_rotation_amount)
		mmi.add_instance(xform)

The last function call (add_instance) is in a script representing my MultiMeshInstance to apply another visible instance of its MultiMesh with the given transform:

func add_instance(xform: Transform) -> void:
	multimesh.visible_instance_count += 1
	multimesh.set_instance_transform(multimesh.visible_instance_count-1, xform)
	var scale_factor = xform.basis.get_scale()
	
	# !!! create collision shape here
	
	var ps = PhysicsServer
	var body = ps.body_create()
	ps.body_set_mode(body, ps.BODY_MODE_RIGID)
	ps.body_set_state(body, ps.BODY_STATE_TRANSFORM, xform)
	ps.body_set_state(body, ps.BODY_STATE_CAN_SLEEP, true)
	ps.body_set_param(body, ps.BODY_PARAM_GRAVITY_SCALE, 0)
	ps.body_set_param(body, ps.BODY_PARAM_MASS, 100 * ((scale_factor.x * scale_factor.y * scale_factor.z) / 3))
	ps.body_set_enable_continuous_collision_detection(body, true)
	ps.body_set_max_contacts_reported(body, 10)
	ps.body_set_space(body, get_world().space)
	ps.body_set_collision_layer(body, 1)
	ps.body_set_collision_mask(body, 1)
	var shape = ps.shape_create(ps.SHAPE_CONVEX_POLYGON)
	ps.shape_set_data(shape, collision_shape.shape.points) #is this correct?
	ps.body_add_shape(body, shape)
	ps.body_set_shape_transform(body, 0, xform)
	
	ps.body_set_force_integration_callback(body, self, "_on_body_moved", 0)
	_bodies.append(body) # just to keep reference of the created physics bodies
	_shapes.append(shape)`# keep the created shapes too (not really neccessary)

In the same script i try to change the transform for the corresponding visible instance of the multi mesh (not sure if it will work this way):

func _on_body_moved(state, index) -> void:
	multimesh.set_instance_transform(index, state.transform)

The visual part works fine and all meshes are visible. But I can't get a collision with the player (a KinematicBody scene). I checked the collision layers and masks too (all are at 1). I also tried to comment out some lines out corresponding to some rigid body properties (continuous cd, contact reports, can sleep).

Example of the problem can be viewed at Youtube

What could have been missing or doing wrong?
Is it possible to draw collisions in debug for PhysicsServer-created elements too (like the ones from scenes) and if yes, how? (it would be helpful to see, if collisions are at the correct transform)

If anything is not clear, I can post more situation details.

Thx for help in advance and cheers

Z3R0PTR

Megalomaniak
Thx for your reply.

First, I forgot to mention that I use pre generated collision shapes. They are generated while getting the meshes from the mesh library to the single multi mesh instances.

Part from main scene script (mining field):

func create_multi_meshes() -> void:
	for index in mesh_lib.get_item_list():
		var mfmm = mfmm_scene.instance()
		mfmm.mesh = mesh_lib.get_item_mesh(index)
		_multi_mesh_instances.append(mfmm)
		add_child(mfmm)

Assigning the library mesh to a multi mesh instance leads to creating the collision shape (see set_mesh() below). I have my own export var for the mesh I assign to the internal one.

Parts from my multi mesh instance script:

extends MultiMeshInstance

export(Mesh) var mesh setget set_mesh
export(Material) var material setget set_material

var collision_shape: CollisionShape
...
func _init() -> void:
	multimesh = MultiMesh.new()
	multimesh.transform_format = MultiMesh.TRANSFORM_3D
	multimesh.color_format = MultiMesh.COLOR_NONE
	multimesh.custom_data_format = MultiMesh.CUSTOM_DATA_NONE
	multimesh.instance_count = 10000
	multimesh.visible_instance_count = 0
	
	collision_shape = CollisionShape.new()
...
func set_mesh(m: Mesh) -> void:
	mesh = m
	multimesh.mesh = mesh
	collision_shape.shape = mesh.create_convex_shape()

The linked approach I found while testing a similar one (create multiple rigid bodies as nodes using pre generated convex polygon shapes). But this was before I started using MultiMeshInstance. To get more performance, I decided to bypass the node system and switched to PhysicsServer for the collisions.
Maybe the approach with just one RigidBody could be performant enough. But how to get the information on which shape the collision appears? This is essential for me.

Is there a simple way to draw the collision shapes created via PhysicsServer (like Debug option in Editor)?

    Z3R0PTR Is there a simple way to draw the collision shapes created via PhysicsServer (like Debug option in Editor)?

    I think you might have to draw something custom in that case.

      Megalomaniak

      Yes, I think so.

      But now I believe, the collision work, but there must be something wrong with setting the transform of the multi mesh instance at collision index. I'm looking for that.

      Here is a version, I tried before (Video on Youtube. It's performant, but there are some issues.

      For every visible instance of the multi mesh, I create a rigid body node, attach the pre generated shape and put it as child of the multi mesh instance.

      func add_instance(xform: Transform) -> void:
      	multimesh.visible_instance_count += 1
      	multimesh.set_instance_transform(multimesh.visible_instance_count-1, xform)
      	var scale_factor = xform.basis.get_scale()	
      	var body: RigidBody = RigidBody.new()
      	var cs: CollisionShape = CollisionShape.new()
      	cs.shape = collision_shape.shape
      	body.add_child(cs)
      	body.contact_monitor = true
      	body.contacts_reported = 10
      	body.continuous_cd = true
      	body.transform = xform
      	body.gravity_scale = 0.0
      	body.mass = 1000
      	body.mass *= (scale_factor.x * scale_factor.y * scale_factor.z) / 3
      	body.connect("body_entered", self, "_on_body_entered")
      	body.connect("body_shape_entered", self, "_on_body_shape_entered")
      	add_child(body)

      In the player script (KinematicBody) the collisions are handled to apply a force to the collider via collision normal and then a "dirty" call to refresh the transform at the given index:

      var _v = move_and_slide(velocity * delta, Vector3.ZERO, false, 4, PI / 4, false)
      for index in get_slide_count():
      var collision = get_slide_collision(index)
      if collision.collider is RigidBody:
      	var rb: RigidBody = collision.collider as RigidBody
              rb.apply_central_impulse(-collision.normal * inertia)
      	if rb.get_parent().has_method("refresh_instance"):
      		rb.get_parent().refresh_instance(rb.get_index())

      Additionally the collisions (of the rigid bodies itself) are checked in the script of my own multi mesh instance and then set the transform:

      func _on_body_entered(body: Node) -> void:
      	if body.get_parent() == self:
      		refresh_instance(body.get_index())
      func refresh_instance(index: int) -> void:
      	var rb = get_child(index)
      	multimesh.set_instance_transform(index, rb.transform)

      It doesn't feel good to handle things from the outside (player calls refresh in other scene). I don't want to couple them. This is why I switched to PhysicsServer, because there is a signal reacting on movement of the physics bodies.

      ps.body_set_force_integration_callback(body, self, "_on_body_moved", 0)

      This way it could be handled within a single script (my MultiMeshInstance).

      Yes, it's not the intended way to render such an amount of multi mesh instances and all of them should be interactable. Mayb there are other approaches. I'll keep on researching and testing. It's just frustrating if anything doesn't work and you don't know why, but it's interesting to learn. 😃