Multimesh: how to instance multimesh instances in predefined V3 positions?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Macryc

Hi All. I have a large 3d grid map in my level. I have a global function that captures translation of all cells of a specified type. I also have a grass clump node, which I spawn in all positions where I have a cell type: grass.
With bigger maps, this method takes forever to load all instances of grass clump (even though they are simple textured planes).

NEED:
I want to try multimesh instancing instead. I want a grass clump mesh spawned in all positions where there is a cell of the type: grass. All translations where I want those multimesh instances to spawn are stored in an array.

PROBLEM:
I do not know how tu use multimesh’s set_instance_transform method to make that happen. Any tips appreciated!

:bust_in_silhouette: Reply From: Tim Martin

You want to do similar to the tutorial here Animating thousands of fish with MultiMeshInstance3D — Godot Engine (stable) documentation in English

Note that when you set instance_count you effectively lock the multimesh, so do the setup first, set the number, and then set their positions.

E.g. script to be attached to a multimesh node

extends MultiMeshInstance
const HOW_MANY : int = 10

func _ready():
	multimesh = MultiMesh.new()
	multimesh.transform_format = MultiMesh.TRANSFORM_3D
	multimesh.mesh = $"../MY_MESH_INSTANCE".mesh.duplicate()
	multimesh.instance_count = HOW_MANY
	for i in range(HOW_MANY):
		# Set offset coordinates here. Replace Basis() if you need rotation too
		multimesh.set_instance_transform(i, Transform(Basis(), Vector3(0, 0, 0)))

I know how to spawn instanced. That’s not my question (I may have been imprecise).
The question is:
How do i manipulate set_instance_transform to have each instance spawn at a location that is stored in a separate array. So it’s 2 separate arrays, one for multimesh instances (instance count), and one for 3d locations in the world. How do I pair them up?

Macryc | 2020-05-10 11:37

Hello Macryc,

At this point you can just use the same index i over both arrays?

Supposing you have var positions : Array with size HOW_MANY and with each entry a Vector3, the final line would then be multimesh.set_instance_transform(i, Transform(Basis(), positions[i]))

Tim Martin | 2020-05-10 11:50

Hey Tim, it worked!:slight_smile:
Thanks. My next question is going to be: how do I remove an multimesh instance when the ‘grass’ cell is replaced with a different type. Definitely don’t want to reload the whole thing each time…:slight_smile:

Macryc | 2020-05-10 12:20

While it might be considered a bit of a hack,

func remove_instance(var i : int):
    var t : Transform = multimesh.get_instance_transform(i)
    t.origin.y -= 1000
    multimesh.set_instance_transform(i, t)

is a valid way of doing this. I.e. just hide it deep, deep underground.

Tim Martin | 2020-05-10 12:46

Awesome, I’ll try that. Cheers.
BTW, do you happen to know how one should go about rotating each instance by 90 degrees on one of the axes? Trying to find this in the docs, no luck so far…

Macryc | 2020-05-10 12:50

Manipulate the Basis in the Transform, this is where the rotation data is held (edit - typo)

var b := Basis()
b = b.rotated(Vector3.UP, deg2rad(90))
var t := Transform(b, positions[i])

Tim Martin | 2020-05-10 13:00

thanks. I’ve played around with these and got it to work more or less the way I intended. Just to confirm though - there is not way to queue_free a multimesh instance, is there? As long as the number of instances has no bearing on performance, that’s ok. It’s just that I’m worried about having dozens or hundreds of instances buried underground and still impacting on performance. Or is this a silly thing to fear?

Macryc | 2020-05-10 13:54

It’s unavoidable, that’s the trade off of using a MultiMesh. It’s batching together one draw call to the GPU for all of your instances, as opposed to sending a single call per mesh.

As a consequence it can either send all of them, or none of them. The occluded ones will have some performance penalty, but it should be very small. MultiMesh is designed to scale to be drawing thousands of instances.

Tim Martin | 2020-05-10 15:09