0 votes

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!

asked May 10 in Engine by Macryc (289 points)
edited May 10 by Macryc

1 Answer

0 votes

You want to do similar to the tutorial here https://docs.godotengine.org/en/stable/tutorials/3d/vertex_animation/animating_thousands_of_fish.html#making-a-school-of-fish

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)))
answered May 10 by Tim Martin (108 points)

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?

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]))

Hey Tim, it worked!:)
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...:)

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.

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...

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])

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?

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.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.