|
|
|
 |
Reply From: |
DaddyMonster |
The way I tackled this was to make the procedural terrain generation run separately, save the mesh to file and load it on start up. This brings a couple of advantages:
- You use the same mesh for collision
- You can implement lod and have a lower poly collision mesh if you want
- It’s much quicker to launch
- It works well with chucks
- Simplicity
That said, I don’t have experience with tri strips, maybe that changes thing, not sure.
Thanks for the suggestion.
The terrain I’m working on is endless, so it wont be possible to generate the entire thing beforehand unfortunately. I guess it would be possible to save the mesh of each chunk that has been built. But I’m afraid I don’t see how that would help with the collisions unless the export or import step somehow rebuilds the mesh differently.
Ninfur | 2022-01-10 16:20
Ah, I see. No, my suggestion is not suitable for an endless terrain scenario.
Hmm, this is genuinely tricky. Maybe try something like this:
func _ready():
make_col_shape(CubeMesh.new())
func make_col_shape(mesh):
var col = CollisionShape.new()
var cps = ConvexPolygonShape.new()
cps.points = mesh.get_mesh_arrays()
col.shape = cps
add_child(col)
Replace the CubeMesh with your MeshInstance mesh chunk.
Huge caveat: I’ve not tested any of this. I’m worried we may have to separate the verts from the normals, UVs and so forth so I’m not confident about this as is… Try it, you never know. We want to avoid more expensive looping…
I assume you’re using MeshDataTool to loop as you make the mesh? Maybe you can grab a vert only PoolVector3Array and just apply that??
DaddyMonster | 2022-01-10 18:14
I’m manually creating the verts, UVs, indices and normals and adding them to the ArrayMesh, so accessing them won’t be a problem.
I’ve tried something similar to what you are suggesting, and it kind of works if you throw in a StaticBody node in the mix. The problem I’m facing with this approach is that I don’t think a convex shape will be accurate enough for a terrain that can be both convex and concave in places. This results in objects flying in the air above the concave parts of the terrain.
I thought it would be easy enough to just use a ConcavePolygonShape instead, but it needs to be populated with faces rather than vertices. And so far I haven’t found a way to get the mesh faces besides creating them myself. But I feel like doing so would kind of defeat the purpose of using PRIMITIVE_TRIANGLE_STRIP over PRIMITIVE_TRIANGLES. Normally you would be able to get the faces using mesh.get_faces(), but this does not work for triangle strips for some reason. I’m not sure if this is a bug or if there is some logical explanation as to why.
Perhaps ConcavePolygonShape’s and PRIMITIVE_TRIANGLE_STRIP’s are simply not compatible, since the shape seems to expect faces with 3 unique vertices and the strip is built to allow faces with shared vertices.
Ninfur | 2022-01-10 20:15
Ah, concave… Impressive sounding project. Ok, so we need to effectively construct a tres along these lines:
[gd_resource type="ConcavePolygonShape" format=2]
[resource]
data = PoolVector3Array( 0.4536, 0.8618, 0.6113 )
How about this: when you’re done with the procedural looping with MeshDataTool, grab faces = mdt.get_vertex_faces()
I’d try it first with a CubeMesh for simplicity and see how the data looks. In theory you can just make an obj and add it to the shape.
DaddyMonster | 2022-01-11 10:54
I should clarify that I’m not using MeshDataTool for the mesh generation. My understanding is that MeshDataTool is mainly used for modifying existing geometry, rather than creating new.
My mesh creation looks something like this
var data = []
data.resize(ArrayMesh.ARRAY_MAX)
data[ArrayMesh.ARRAY_VERTEX] = vertices
data[ArrayMesh.ARRAY_TEX_UV] = UVs
data[ArrayMesh.ARRAY_INDEX] = indices
data[ArrayMesh.ARRAY_NORMAL] = normals
var terrain_mesh = ArrayMesh.new()
terrain_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLE_STRIP, data)
However, I tried adding my mesh to a MeshDataTool’s instance. But when calling mdt.create_from_surface()
it fails with the error ERR_INVALID_PARAMETER. Looking at the documentation for create_from_surface(), it actually states that it requires a mesh with primitive type Mesh.PRIMITIVE_TRIANGLES.
Ninfur | 2022-01-11 16:47
Ah ok, I work a little differently and start with a primitive, subdivide, then loop - hence MeshDataTool.
I’ve been playing around with it but, honestly, I’m not sure what form the face data takes (as opposed to vertex data)… The vector maths is different for concave but I’m just not sure what form they want it. I mean, a face is just three verts with an index…
I’ll copy my musings below so you can see my train of thought if it’s any help:
func make_col_shape(mesh):
var am = ArrayMesh.new()
am.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, mesh.get_mesh_arrays())
var mdt = MeshDataTool.new()
mdt.create_from_surface(am, 0)
print("face count: ", mdt.get_vertex_count())
var data = []
for face in range(mdt.get_face_count()):
data.append(mdt.get_face_vertex(0, face))
data.append(mdt.get_face_vertex(1, face))
data.append(mdt.get_face_vertex(2, face))
print(data)
There’s almost certainly a really easy way to do this… The editor has a Create Trimesh Static Body
tool but there’s no api sadly. Maybe just make a CollisionShape and add your vertices
(or verts & indices) variable above as a PoolVector3Array.
Btw, the docs explicitly say: You can only use concave shapes within StaticBodies
which I wasn’t aware of until now.
Collision shapes (3D) — Godot Engine (stable) documentation in English
DaddyMonster | 2022-01-11 21:33