How to distribute meshes within triangle face bounds?

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

My goal is to distribute grass mesh instances on a terrain mesh, created in Blender, using MultiMeshInstance.
Script gets all the triangles of a terrain mesh through MeshDataTool and places a MultiMeshInstance for each triangle. How to evenly distribute meshes inside a given triangle with respect to height and a normal direction of a triangle?
I have these 3 points, from documentation.

for i in mesh_data_tool.get_face_count():
	
	# Get the index in the vertex array.
	var a = mesh_data_tool.get_face_vertex(i, 0)
	var b = mesh_data_tool.get_face_vertex(i, 1)
	var c = mesh_data_tool.get_face_vertex(i, 2)
	
	# Get vertex position using vertex index.
	var ap = mesh_data_tool.get_vertex(a)
	var bp = mesh_data_tool.get_vertex(b)
	var cp = mesh_data_tool.get_vertex(c)

I set the instance’s transform, like this:

	#Random scale and rotation
	var t = Transform().scaled(Vector3(rand_scale, rand_scale, rand_scale)).rotated(Vector3.UP, randf() * TAU)
	
	# This gives a point within a rectangle
	# Actually this doesn't work
	var rand_x = randf() * ap.length()
	var rand_z = randf() * bp.length()
	
	# Setting the transform
	t.origin = Vector3(rand_x, 0, rand_z)
	mm.set_instance_transform(i, t)

Why I don’t use Populate Surface, because this way I can hide multi meshes on triangles which player can’t see.

:bust_in_silhouette: Reply From: Defaultsound

So I was trying to solve the exact same problem and also limit mesh instances based on vertex color.

I ended up looking into the Populate function used in the editor to get some ideas.

I noticed that it uses a function on line 190 Vector3 pos = face.get_random_point_inside();

Unfortunately the face3 math library isn’t exposed as far as I am aware. But looking in https://github.com/godotengine/godot/blob/07025e607dda2ab3b35bee65614347dc39f6f236/core/math/face3.cpp

We can find the function on line 153.

Using your bit of code I was able to figure it all out.

func get_valid_verts():
for i in mdt.get_face_count():
	# Get the index in the vertex array.
	var a = mdt.get_face_vertex(i, 0)
	var b = mdt.get_face_vertex(i, 1)
	var c = mdt.get_face_vertex(i, 2)
	# Get vertex position using vertex index.
	var ap = mdt.get_vertex(a)
	var bp = mdt.get_vertex(b)
	var cp = mdt.get_vertex(c)
	if mdt.get_vertex_color(a).r > 0.1:
		verts.append({"verts":[ap,bp,cp],"vertcol":mdt.get_vertex_color(a)})

So here for my approach I get all the face verts, but at the end I filter out those whose vertex colour is less than 0.1. This allows one to paint vertex colours in Blender for example or potentially also make use of the VPainter tool I imagine.

func get_random_point_inside(vertices) -> Vector3:
var a
var b
a = rand_range(0.0,1.0)
b = rand_range(0.0,1.0)
return vertices[0] * a + vertices[1] * (b - a) + vertices[2] * (1.0 - b)

Rewrote the Face3 function to return a random point given a set of 3 vertices.

func addGrass(a):
get_valid_verts()
multimesh.multimesh.instance_count = 5000
for i in multimesh.multimesh.instance_count:
	var xform = Transform()
	verts.shuffle()
	var randpos = get_random_point_inside(verts[0].verts)
	var pos = global_transform.xform(randpos)
	#print(pos)
	var basis_scale = Vector3(verts[0].vertcol.r,verts[0].vertcol.g,verts[0].vertcol.b)
	var basis = Basis(Vector3.UP, deg2rad(rand_range(0,359)))
	multimesh.multimesh.set_instance_custom_data(i,Color(
		basis_scale.x,
		basis_scale.y,
		basis_scale.z
	))
	xform.origin = pos
	xform.basis = basis
	multimesh.multimesh.set_instance_transform(i,xform)

Put it all together.

Sorry code is a bit of a mess as I was tinkering to get this to work. Hope this helps.
enter image description here

Just noticed there is a slight issue with the height as some are floating. Think this might be because they aren’t being aligned to the normal of the face.

Defaultsound | 2020-09-19 14:42

Figured out the floating instances. Turns out the “swap” is fairly important to ensure that the point is within the range of the triangle. I also flipped the test to be less than rather than greater than, so that was also causing issues.

As Godot doesn’t have a swap function, I came up with this:

func get_random_point_inside(vertices,amount) -> Vector3:
var a
var b
a = rand_range(0.0,amount)
b = rand_range(0.0,amount)
if  a > b:
	var x
	var y
	x = a
	y = b
	b = x
	a = y
return vertices[0] * a + vertices[1] * (b - a) + vertices[2] * (1.0 - b)

enter image description here

Defaultsound | 2020-09-20 12:43

This is cool! Sorry for the late reply.

wowzzers | 2020-12-02 19:52