Procedural meshes - what am I missing?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Shaolin_Funk
:warning: Old Version Published before Godot 3 was released.

As the title suggests, I’m trying to create a mesh at runtime through code (GDScript). However I’m not having much luck.

I’m starting with a simple quad for now, with the intention to move on to more interesting things once I understand how this works. The code looks like this:

extends StaticBody

func make_quad(): #mesh
	var array = Array()
	array.push_back(Vector3(0,0,0))
	array.push_back(Vector3(0,1,0))
	array.push_back(Vector3(1,1,0))
	array.push_back(Vector3(1,0,0))

    print(array.size()) #expected: 4, result: 4
var mesh = Mesh.new()
mesh.add_surface(Mesh.PRIMITIVE_TRIANGLE_STRIP, array)
print(mesh.surface_get_array_len(0)) #expected: 4, result: -1
	return mesh

func _init():
	var mesh = make_quad()
	var meshinst = MeshInstance.new()
	meshinst.set_mesh(mesh)
	add_child(meshinst)
	print("surface count ", mesh.get_surface_count()) #expected: 1, result: 0
	print("format ", mesh.surface_get_format(0)) #expected: ???, result: 0
	print("primitive ", mesh.surface_get_primitive_type(0)) #expected: 5, result: 1

Then in the root Spatial node’s _ready() function I load the script file, instantiate an object and add_child() it to the scene.

The docs aren’t clear on what format the array parameter to add_surface() takes; I’ve tried an Array of Vector3s as in the above code, a Vector3Array and an Array of [x, y, z] triplets. All give the same result.

The code appears to be quietly failing on the add_surface() function, as the debugger shows an error at that line and also on the subsequent surface_* calls, but it doesn’t give any details of the error which makes it hard to debug!

The doc page for Mesh talks a lot about “format” and the Mesh class contains a number of flags related to format, but there is no way to pass a format parameter so I don’t see how to set this.

:bust_in_silhouette: Reply From: Toger5

Hello,
I also messed around with procedural meshes. And I also tried to use the add_surface() function with no success ;(
But there are some Classes provided wich make creating a mesh even easier and also give you the power to access UV’s and so on…
You use the Surface tool which allows you to create a surface pretty easy and with a lot of control.
Here is some simple example code:

var surface = SurfaceTool.new()
var datatool = MeshDataTool.new()
var mesh = Mesh.new()

var array = Array()
array.push_back(Vector3(0,0,0))
array.push_back(Vector3(0,1,0))
array.push_back(Vector3(1,1,0))
array.push_back(Vector3(1,0,0))

wg.surface.begin(VisualServer.PRIMITIVE_TRIANGLES)
for v in vertex_array
	#surface.set_material(material)
	#surface.add_uv(Vector2())
	#surface.add_normal(Vector3())
	surface.add_vertex(v)
datatool.create_from_surface(surface.commit(), 0)
datatool.commit_to_surface(mesh)

I think you also can skip the part with the MehsDatatTool and just use the commit() function like that:

Mesh surfaceTool.commit( Mesh existing=Object() )

I think when I tried it, it didn’t worked for me but it was some time ago so maybe I made another mistake.
If you are interested you also can have a look at a voxel game I created with Godot “a minecraft clone” there I use the surface tool too.
Godot-Voxel-Game-MineCraftClone

Thanks for the reply! I would at some point like to get to the bottom of how to create meshes directly, simply because I feel it’s useful to have a deeper understanding of such things, but that surface tool looks great. My next question would have been how to set up uv arrays but you’ve already answered that so thanks again!

Shaolin_Funk | 2016-02-25 12:06

If you are messing around next time could you try if it works without the MeshDataTool?
Because I have no time today. And I am really interested if it does work!
If you don’t i may try it tomorrow. Than I will tell you about the results.

Toger5 | 2016-02-25 12:38

Yeah I had a go and it seems to work fine without using MeshDataTool. I was able to use the Mesh returned by SurfaceTool.commit() directly.

Shaolin_Funk | 2016-02-25 12:53

Ah I spoke too soon about the uv question being answered. I can set up the vertices fine now so I’m rendering a black quad, but the code is silently failing on the calls to SurfaceTool.add_uv() so I’m unable to render textures on my mesh.

Here’s what I’ve got so far:

func make_quad(): #mesh
	var vert_array = Array()
	var uv_array = Array()
	var st = SurfaceTool.new()
	
	vert_array.push_back(Vector3(0,0,0))
	vert_array.push_back(Vector3(0,1,0))
	vert_array.push_back(Vector3(1,1,0))
	
	vert_array.push_back(Vector3(0,0,0))
	vert_array.push_back(Vector3(1,1,0))
	vert_array.push_back(Vector3(1,0,0))
	
	uv_array.push_back(Vector2(0,0))
	uv_array.push_back(Vector2(0,1))
	uv_array.push_back(Vector2(1,1))
	
	uv_array.push_back(Vector2(0,0))
	uv_array.push_back(Vector2(1,1))
	uv_array.push_back(Vector2(1,0))
	
	st.begin(Mesh.PRIMITIVE_TRIANGLES)
	for i in range(6):
		st.add_vertex(vert_array[i])
		st.add_uv(uv_array[i])
	return st.commit()

Debugger is reporting 6 errors, one for each call to add_uv(). Any ideas what I’m doing wrong here?

Shaolin_Funk | 2016-02-25 13:22

I think the way it works is that you first set up the properties for one vertex than the next added vertex will have these settings:

surface.set_material(material)
surface.add_uv(Vector2())
surface.add_normal(Vector3())
surface.add_vertex(v)

So you have to do it in this order.

Toger5 | 2016-02-25 13:49

Yes! You’re right, all I had to do was reverse the calls to add_vertex() and add_uv() to eliminate the errors and make it render correctly. Thanks dude.

Shaolin_Funk | 2016-02-25 14:57

:bust_in_silhouette: Reply From: michaelb

So, I dug through the Godot source code a bit, and finally managed to get your example to work and display a black triangle. The three undocumented “gotchas” are that #1 it expects an array of length 9 (VisualSurface::ARRAY_MAX), and #2, the vertices actually go into a slot based on a flag (e.g. ARRAY_VERTEX), and #3, it must be a Vector3Array.

Working example based on above example:

const ARRAY_MAX = 9

func make_quad():
    var vertices = Vector3Array()
    vertices.push_back(Vector3(0,0,0))
    vertices.push_back(Vector3(0,1,0))
    vertices.push_back(Vector3(1,1,0))
    vertices.push_back(Vector3(1,0,0))

    var array = Array()
    array.resize(ARRAY_MAX)
    array[Mesh.ARRAY_VERTEX] = vertices

    var mesh = Mesh.new()
    mesh.add_surface(Mesh.PRIMITIVE_TRIANGLE_STRIP, array)
    print(mesh.surface_get_format(0))
    print(mesh.surface_get_array_len(0))
    return mesh

func _init():
    var mesh = make_quad()
    var meshinst = MeshInstance.new()
    meshinst.set_mesh(mesh)
    add_child(meshinst)
    print("surface count ", mesh.get_surface_count())

Thank you for the 'gotchas"!

And one more thing, the vertices[2] should be swaped with vertices[3] if we want to draw quad because the Clock-Wise orientation.

pinem | 2017-02-22 08:00

:bust_in_silhouette: Reply From: procedural

Also, you can use ImmediateGeometry class for procedural meshes, as explained in this video tutorial: https://youtu.be/y0mat87aWTk?t=11m30s

I’m downloading the tutorial to watch it later on media player, by the way do you know whether one can resize the arrays on the fly and change the vertices position without having to recreate some new ones.

rraallvv | 2017-11-01 18:45