Topic was automatically imported from the old Question2Answer platform.

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.

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.

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)