Moving and rotating trees in multimesh

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

Hi,

So I have a tree mesh attached to my multimesh, and I have a exported variable to pick how many trees I run.

When my game runs, a landscape mesh is generated and positions to place trees are picked by raycasting random positions over the mesh. After I have the positions, I loop over the instances in my mesh and translate the trees to the appropriate position.

Now, I want to add a random rotation to my trees so that they son’t all look the same.

This is my multimesh code:

extends MultiMeshInstance

export(int) var treeScale = 5

func _ready():
	self.scale = Vector3(treeScale, treeScale, treeScale)
	


func updatePositions(posArray):
	print("Update tree positions")
	for i in self.multimesh.instance_count:
	
		var position = posArray[i]
		position.y -= 2
		position /= treeScale
		
		var rotation = rand_range(0, PI * 2)		
		var transformation = Transform(Basis(), position)
		self.multimesh.set_instance_transform(i, transformation)

I have been messing around with transformation.rotated but sometimes it rotates the trees off of my mesh.

How should I implement rotation?

Thanks.

:bust_in_silhouette: Reply From: klaas

Hi,
the easiest way to rotate around the up-axis is rotate_y( radiants )

rotate_y( rand_range(0, PI * 2) )

https://docs.godotengine.org/en/stable/classes/class_spatial.html#class-spatial-method-rotate-y

How would I use rotate_y in this case? I cannot apply it to the transform, and I can’t apply it to the position vector I am moving the trees to. If I could get the actual instances of the trees perhaps I could apply it to them, but I’m not sure how to do that.

I could do rotate_y on the multimesh node, but that would rotate the entire plane of trees.

As far as I know I have to use the .rotated() function on my transform.

I can do

transformation.rotated(Vector3(0, 1, 0), rand_range(0, PI * 2))

but this makes some of my trees rotate off of my terrain mesh.

In the meantime I have abandoned the multimesh in my actual project and I am just instancing new tree scenes and placing them at the coordinates, this allows me to use the rotate_y function, but I thought using a multimesh would be better for performance.

Thummper | 2020-07-20 12:51

You would use it on multiMeshInstance wich is a decendand of spatial that implements the method rotate_y

klaas | 2020-07-20 13:36

Is there any way I can rotate individual instances of trees in my multimesh?

If I just rotate the whole multimesh then some of the trees will end up rotating off of my terrain - I want to apply a randomized individual rotation to each tree.

Thummper | 2020-07-20 16:54

Ok, got it now.
You have to set transform_format of your MultiMeshInstance to 3D (you can only do so if the instance count is zero, so put it back to zero make it 3D then set the count back to your value).
Here is a litle script that rotates and places the instances randomly on startup:

func _ready():
	for i in range(multimesh.instance_count):
		var transform = Transform()
		transform = transform.translated(Vector3(rand_range(-5,5), 0, rand_range(-5,5)))
		transform = transform.rotated(Vector3.UP,rand_range(0,PI * 2))
		multimesh.set_instance_transform(i, transform)

make sure your trees have there pivot right in the middle bottom of the mesh

There is although a tool to populate a surface with instances … read about it here:
Using MultiMeshInstance3D — Godot Engine (stable) documentation in English

klaas | 2020-07-20 17:21

So when I use rotate like that I still end up with trees being rotated off of the map for some reason.

extends MultiMeshInstance

export(int) var treeScale = 6

func _ready():
	self.scale = Vector3(treeScale, treeScale, treeScale)
	
func updatePositions(posArray):
	print("Update tree positions")
	for i in self.multimesh.instance_count:
		var pos = posArray[i]
		pos.y -= 6
		pos /= treeScale
		
	
		var transformation = Transform()
		transformation = transformation.translated(pos)
		transformation = transformation.rotated(Vector3.UP, rand_range(0, PI*2))
		multimesh.set_instance_transform(i, transformation)

With rotation in I get trees like this that go off the map: Imgur: The magic of the Internet

With rotation commented out I get trees that stay on the map: Imgur: The magic of the Internet

Any ideas on how to fix this?

Thummper | 2020-07-20 19:23

Is the pivot of the tree is in the exact center of the tree?

klaas | 2020-07-20 19:25

I believe it is. I get the same behavior when I replace the tree mesh with a new cube mesh that is definitely positioned properly as well.

I get my desired behavior (i think), when I use this:

extends MultiMeshInstance

export(int) var treeScale = 6

func _ready():
	self.scale = Vector3(treeScale, treeScale, treeScale)
	
func updatePositions(posArray):
	print("Update tree positions")
	for i in self.multimesh.instance_count:
		var pos = posArray[i]
		pos /= treeScale
		
		var b = transform.basis.rotated(Vector3.UP, rand_range(0, PI*2))
		var t = Transform(b, pos)
		multimesh.set_instance_transform(i, t)

Not sure why this works but the other doesn’t.

Thummper | 2020-07-20 20:03

okay, i messed it up … but now

first rotate then reposition

func _ready():
	for i in range(multimesh.instance_count):
		var transform = Transform()
		
		transform = transform.rotated(Vector3.UP,rand_range(0,PI * 2))
		transform.origin = Vector3(rand_range(-4.8,4.8),0,rand_range(-4.8,4.8))
		multimesh.set_instance_transform(i, transform)

klaas | 2020-07-20 20:20