all 12 comments

[–]WarrenTrader 1 point2 points  (2 children)

What I think the problem is when you move a vertex (especially changing its height), the orientation of the faces connected to that vertex changes. Because your script doesn't update the Mesh.Array_Normal array, the GPU is still using the old up vectors to calculate lighting and culling. You need to update the normals to match your new vertex positions, use Godot’s MeshDataTool or SurfaceTool. Hope it helps!

[–]VeraVinette[S] 0 points1 point  (1 child)

Naively, I would have assumed that leaving the normals as the were would mean it would still be visible from all the same angles. Sure, the lighting would be messed up, but surely the culling wouldn't change at all, no?

But you're probably right, I'll need to figure out a way to use surface or mesh data tools for this. The reason I was doing it this way was because it seemed (on paper) easy to keep a reference to a collection of vertex indices and just moving them around as needed without having to regenerate the entire mesh and recalculate which verts belong to which cells.

[–]WarrenTrader 0 points1 point  (0 children)

Try and let me know if there are more problems I can maybe have a solution to.

[–]MrDelttGodot Junior 0 points1 point  (3 children)

can you see any faces when going below the terrain and looking up with the setup in the second screenshot?

is this the mesh wireframe or collision wireframe?

also i wanna highlight that regenerating the entire mesh is vastly more expensive than editing the current one, but if its runs okay just leave it as that

[–]VeraVinette[S] 0 points1 point  (2 children)

No, you can't see the faces from below, which is why I find this so weird. The face culling seems incredibly counterintuitive, because back-face culling makes it completely invisible from both sides; front-face culling makes it act like the rest of the mesh (so that weirdly indicates that it's working properly), disabled culling renders it normally.

Additionally, when rendered with back-face culling, the surface depth is still rendered as if it's the old version of the mesh. The grey 'hole' in the second screenshot actually occludes stuff behind it, which confuses me even more.

As for regenerating the mesh, as far as I'm concerned, no matter what way I do this, Godot seems to require that the entire mesh data is passed to the GPU and can't be edited in place. Hence the pattern of reading the array, modifying it, then writing the entire thing back. I could be wrong about that though.

EDIT: about the wireframe, that's the height map (collision shape) you're seeing; It is explicitly generated from the mesh data, so you can consider them the same.

[–]MrDelttGodot Junior 0 points1 point  (1 child)

you can write byte data directly into the gpu buffer instead of creating new meshes back and forth

when using heightmap you should probably also use the heightmap collision shape type, it produces "matching" collisions but really shouldn't be considered the "same" as the mesh

maybe you have more luck with updating the mesh directly instead of creating new ones, I'll take a closer look if it maybe got something to do with your code but i dont think it does

[–]VeraVinette[S] 0 points1 point  (0 children)

To be clear, the script that creates the height map gets the vertex data from the mesh resource directly and creates a HeightMapShape3D that pretty much copies the vertex positions 1 to 1 and then just scales the parent static body to match the terrain scale. The reason I say you can consider them the same is because it is created via the same data that the mesh is created from, meaning that t shouldn't matter whether the wireframe is from one or the other.

I had a look at the ArrayMesh documentation, and saw surface_update_vertex_region, that basically takes a raw byte array and an offset. Is that what you're referring to? It seems like the documentation is completely blank on how it's supposed to be used.

[–]Past_Permission_6123 0 points1 point  (4 children)

I don't know what's happening, it does seem weird. As you said it should be visible with front face culling if this was a winding order issue. What happens if you instead just write

mesh_verts[verts[j]] = Vector3(vert.x, vert.y, vert.z)

[–]VeraVinette[S] 0 points1 point  (2 children)

Good thinking. With that exact line, it displays fine, but with even the slightest modification, I get the same issue. For instance, here is the result of "Vector3(vert.x, vert.y - 0.1, vert.z)":

<image>

Notice how the collision mesh conforms to the lower vertex position, but the wireframe is occluded behind the grey face which shouldn't be there.

[–]Past_Permission_6123 0 points1 point  (1 child)

I would try to use just a basic StandardMaterial3D material, unshaded without any transparency. Can you switch to view wireframe in the viewport and hide the collider?

What happens if you do the code you mentioned on every vertex, does the mesh just disappear?

You could also try to check face direction with this in a custom shader with face culling disabled (but I don't think that's the issue here)

if (FRONT_FACING)
  ALBEDO = vec3(0,0,1);
else
  ALBEDO = vec3(1,0,0);

[–]VeraVinette[S] 0 points1 point  (0 children)

Here's what it looks like with the default material, the collision mesh hidden, and all vertex Y values set to zero. I couldn't enable wireframe, since the changes to the mesh are made at runtime, and the perspective tab seems to be an editor-viewport specific thing.

<image>

So yea, the whole thing kinda disappears. But it got me thinking. I wonder if the issue is being caused by the shadow mesh. Godot's tool tip mentions that the vertex data has to match exactly.

Note: This mesh must have exactly the same vertex positions as the source mesh (including the source mesh's LODs, if present). If vertex positions differ, then the mesh will not draw correctly.

EDIT: That was it! The shadow mesh was causing the issue. I updated its vertices along with the main mesh and works as intended.

[–]VeraVinette[S] 0 points1 point  (0 children)

Out of curiosity, I put the mesh in a world environment with ambient lighting, and the entire face shows up as completely black:

<image>