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:
…but, as you can see in this pic (ignore the z-fighting) a few of the vertices are all stretched out weird:
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:
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.
-
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 populatespolygon_bone_data
, so there shouldn’t be any inaccuracies in that. -
There’s more than 4 bones per some of the vertices
printerr("AAAAAAAAAAAAAAAAHHHHHH!!!!!")
would have gone off, so no. -
.slice(0, max_bw)
is cutting out important values.
Removed it once, and nothing changed. -
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. -
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:
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:
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.