Weight & bone application on meshes created from Polygon2Ds cause vertexes to point towards the center for seemingly no discernable reason

Godot Version

Godot v4.3.stable (77dcf97d8)

Question

What I’m trying to do is make a custom node that takes in Polygon2Ds, gets their weights & bones, recreate the bones in Skeleton3D, apply them to 3D meshes created from the Polygon2Ds, and ensure that the Skeleton3D bones mimic their Bone2D equivalents.

Now that we got that out of the way, here’s my problem. You see, I’m at the stage where I create the bones, apply rest position, reset them to rest position, and apply the weights and bones to the generated meshes. There’s a problem though. Ideally, it should look exactly like this pic:
a3UCrYO

…but, as you can see in this pic (ignore the z-fighting) a few of the vertices are all stretched out weird:
image

Adjusting the meshes’ vertices upon mesh creation reveals that they’re all attracted to the center for some reason. Also, probably should have clarified it earlier, but the red dots are meant to represent bone positions:
image

Here’s the script responsible for the skinned mesh generation:

func create_3d_mesh_from_polygon2d(polygon2d: Polygon2D, z_index: int) -> void:
	var mesh_instance := MeshInstance3D.new()
	polygon2d_to_mesh_instance[polygon2d] = mesh_instance

	var st := SurfaceTool.new()
	st.begin(Mesh.PRIMITIVE_TRIANGLES)

	create_skinned_mesh_from_polygon2d(polygon2d, st)

	var mesh := st.commit()
	mesh_instance.mesh = mesh
	mesh_instance.skeleton = skeleton3d.get_path()

	add_child(mesh_instance)
	update_material(mesh_instance)
	mesh_instance_to_z_index[mesh_instance] = z_index

func create_skinned_mesh_from_polygon2d(polygon2d: Polygon2D, st: SurfaceTool) -> void:
	var max_bw: int = 4
	var vertices := []

	for i in range(polygon2d.polygon.size()):
		var vertex: Vector2 = polygon2d.polygon[i]
		vertex *= polygon2d.get_global_transform().affine_inverse()
		vertex += polygon2d.offset
		# Debug thing. Remove later after vertex stretching bug is fixed.
		vertex += Vector2(1000,600) / 2

		var vertex3d = Vector3(vertex.x, -vertex.y, 0)
		vertices.append(vertex3d)

		# Use create_uv function to get proper UV coordinates.
		var uv: Vector2 = create_uv(polygon2d, polygon2d.uv[i])
		st.set_uv(uv)

		var vertex_bones := []
		var vertex_weights := []
		var bones_used: int = 0

		for bone in polygon_bone_data.keys():
			if polygon2d in polygon_bone_data[bone]:
				var weight = polygon_bone_data[bone][polygon2d][i]
				if weight > 0:
					vertex_bones.append(bone_map[bone])
					vertex_weights.append(weight)

		# Ensure we have exactly 4 weights and bones per vertex
		while vertex_bones.size() < max_bw:
			vertex_bones.append(0)
			vertex_weights.append(0)
		if vertex_bones.size() > max_bw:
			# Placeholder for debugging.
			# I'll rework the code later to account for 8 bones per vertex when needed.
			# And just return an error if someone has over 8 bones on a vertex and ask them what the hell they're doing.
			printerr("AAAAAAAAAAAAAAAAHHHHHH!!!!!")

		st.set_bones(vertex_bones.slice(0, max_bw))
		print(str(vertex_bones) + " | " + str(vertex_weights))
		st.set_weights(vertex_weights.slice(0, max_bw))

		st.add_vertex(vertex3d)

	for polygon: PackedInt32Array in polygon2d.polygons:
		for i in range(1, polygon.size() - 1):
			var a: Vector3 = vertices[polygon[0]]
			var b: Vector3 = vertices[polygon[i]]
			var c: Vector3 = vertices[polygon[i + 1]]
			var normal: Vector3 = (c - a).cross(b - a).normalized()

			if normal.z < 0:
				st.add_index(polygon[0])
				st.add_index(polygon[i+1])
				st.add_index(polygon[i])
			else:
				st.add_index(polygon[0])
				st.add_index(polygon[i])
				st.add_index(polygon[i+1])

I’m not sure what’s causing this, but here’s what (I think) I know can’t be the problem.

  1. polygon_bone_data has the wrong data
    I made an earlier version of this that used ImmediateMesh and remade the mesh every frame depending on bone position and weight data. It worked perfectly, aside from the fact that it wasn’t very performant, hence why I’m switching to this approach. Shame that GDScript doesn’t let me adjust vertices manually. Anyway, both versions use the same exact function that populates polygon_bone_data, so there shouldn’t be any inaccuracies in that.

  2. There’s more than 4 bones per some of the vertices
    printerr("AAAAAAAAAAAAAAAAHHHHHH!!!!!") would have gone off, so no.

  3. .slice(0, max_bw) is cutting out important values.
    Removed it once, and nothing changed.

  4. The bones aren’t applied properly at all on any of the vertices, or they’re applied to the wrong bones.
    Ran some simple scripts that manually moved around my bones, and nah, they function fine, and the weird stretching vertices aren’t bound to an incorrect bone, just that one center point for some reason.

  5. vertex.y is negated and that messes something up somehow.
    The Y axis’ direction is inverted depending on if you’re in 3D or 2D scenes, so yeah, that’s intentional, because these meshes would be upside down otherwise.

Now, I have only two possible leads to the issue I have here. In this code snippet, it’s meant to fill in for the unused bones and weights:

while vertex_bones.size() < max_bw:
	vertex_bones.append(0)
	vertex_weights.append(0)

When I set vertex_weights.append(0) to vertex_weights.append(1), it changes to this:
image
Better, but obviously not desirable. Changing vertex_bones.append(0) to any other number seemingly doesn’t do anything either.

Oh yeah, and here’s it with the offset re-enabled. Take note of how it’s massive now:
image

And for the second lead, I get this message for every frame that I am selecting the custom node:
servers/rendering/renderer_scene_cull.cpp:935 - Condition "!v.is_finite()" is true.

Despite these possible leads though, I have no idea how to approach this whatsoever. I can’t think of any other way to approach applying the bones and weights. I originally made this more manually with ArrayMesh, and switched to using SurfaceTool out of desperation, but nothing at all changed. I would really genuinely appreciate some help on this, because I feel really stumped here, and I’d really rather not abandon this project.

1 Like

Okay, I finally figured it out. After this:

while vertex_bones.size() < max_bw:
	vertex_bones.append(0)
	vertex_weights.append(0)

…just add this:

var total_weight = 0.0
for weight in vertex_weights:
	total_weight += weight

for j in range(vertex_weights.size()):
	vertex_weights[j] /= total_weight

And everything should be hunky-dory. I mean, I’m still getting servers/rendering/renderer_scene_cull.cpp:935 - Condition "!v.is_finite()" is true. every frame that the node is selected, but I’m just gonna assume that it’s caused by something entirely different.

1 Like

Elaboration, on the servers/rendering/renderer_scene_cull.cpp:935 - Condition "!v.is_finite()" is true. thing. If you’re suffering from it, check your raw scene data and look to see if any of your node’s transform values were set to nan for some reason. Dunno why mine was like that, but changing them to normal values fixes the error.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.