Skip to content

Commit a99a169

Browse files
committedSep 3, 2024·
Add option to bake a mesh from animated skeleton pose
Adds option to bake a mesh from animated skeleton pose.
1 parent e2dd56b commit a99a169

File tree

3 files changed

+176
-0
lines changed

3 files changed

+176
-0
lines changed
 

‎doc/classes/MeshInstance3D.xml

+8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@
2121
[b]Performance:[/b] [Mesh] data needs to be received from the GPU, stalling the [RenderingServer] in the process.
2222
</description>
2323
</method>
24+
<method name="bake_mesh_from_current_skeleton_pose">
25+
<return type="ArrayMesh" />
26+
<param index="0" name="existing" type="ArrayMesh" default="null" />
27+
<description>
28+
Takes a snapshot of the current animated skeleton pose of the skinned mesh and bakes it to the provided [param existing] mesh. If no [param existing] mesh is provided a new [ArrayMesh] is created, baked, and returned. Requires a skeleton with a registered skin to work. Blendshapes are ignored. Mesh surface materials are not copied.
29+
[b]Performance:[/b] [Mesh] data needs to be retrieved from the GPU, stalling the [RenderingServer] in the process.
30+
</description>
31+
</method>
2432
<method name="create_convex_collision">
2533
<return type="void" />
2634
<param index="0" name="clean" type="bool" default="true" />

‎scene/3d/mesh_instance_3d.cpp

+167
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,172 @@ Ref<ArrayMesh> MeshInstance3D::bake_mesh_from_current_blend_shape_mix(Ref<ArrayM
671671
return bake_mesh;
672672
}
673673

674+
Ref<ArrayMesh> MeshInstance3D::bake_mesh_from_current_skeleton_pose(Ref<ArrayMesh> p_existing) {
675+
Ref<ArrayMesh> source_mesh = get_mesh();
676+
ERR_FAIL_COND_V_MSG(source_mesh.is_null(), Ref<ArrayMesh>(), "The source mesh must be a valid ArrayMesh.");
677+
678+
Ref<ArrayMesh> bake_mesh;
679+
680+
if (p_existing.is_valid()) {
681+
ERR_FAIL_COND_V_MSG(source_mesh == p_existing, Ref<ArrayMesh>(), "The source mesh can not be the same mesh as the existing mesh.");
682+
683+
bake_mesh = p_existing;
684+
} else {
685+
bake_mesh.instantiate();
686+
}
687+
688+
ERR_FAIL_COND_V_MSG(skin_ref.is_null(), Ref<ArrayMesh>(), "The source mesh must have a valid skin.");
689+
ERR_FAIL_COND_V_MSG(skin_internal.is_null(), Ref<ArrayMesh>(), "The source mesh must have a valid skin.");
690+
RID skeleton = skin_ref->get_skeleton();
691+
ERR_FAIL_COND_V_MSG(!skeleton.is_valid(), Ref<ArrayMesh>(), "The source mesh must have its skin registered with a valid skeleton.");
692+
693+
const int bone_count = RenderingServer::get_singleton()->skeleton_get_bone_count(skeleton);
694+
ERR_FAIL_COND_V(bone_count <= 0, Ref<ArrayMesh>());
695+
ERR_FAIL_COND_V(bone_count < skin_internal->get_bind_count(), Ref<ArrayMesh>());
696+
697+
LocalVector<Transform3D> bone_transforms;
698+
bone_transforms.resize(bone_count);
699+
for (int bone_index = 0; bone_index < bone_count; bone_index++) {
700+
bone_transforms[bone_index] = RenderingServer::get_singleton()->skeleton_bone_get_transform(skeleton, bone_index);
701+
}
702+
703+
bake_mesh->clear_surfaces();
704+
705+
int mesh_surface_count = source_mesh->get_surface_count();
706+
707+
for (int surface_index = 0; surface_index < mesh_surface_count; surface_index++) {
708+
ERR_CONTINUE(source_mesh->surface_get_primitive_type(surface_index) != Mesh::PRIMITIVE_TRIANGLES);
709+
710+
uint32_t surface_format = source_mesh->surface_get_format(surface_index);
711+
712+
ERR_CONTINUE(0 == (surface_format & Mesh::ARRAY_FORMAT_VERTEX));
713+
ERR_CONTINUE(0 == (surface_format & Mesh::ARRAY_FORMAT_BONES));
714+
ERR_CONTINUE(0 == (surface_format & Mesh::ARRAY_FORMAT_WEIGHTS));
715+
716+
unsigned int bones_per_vertex = surface_format & Mesh::ARRAY_FLAG_USE_8_BONE_WEIGHTS ? 8 : 4;
717+
718+
surface_format &= ~Mesh::ARRAY_FORMAT_BONES;
719+
surface_format &= ~Mesh::ARRAY_FORMAT_WEIGHTS;
720+
721+
const Array &source_mesh_arrays = source_mesh->surface_get_arrays(surface_index);
722+
723+
ERR_FAIL_COND_V(source_mesh_arrays.size() != RS::ARRAY_MAX, Ref<ArrayMesh>());
724+
725+
const Vector<Vector3> &source_mesh_vertex_array = source_mesh_arrays[Mesh::ARRAY_VERTEX];
726+
const Vector<Vector3> &source_mesh_normal_array = source_mesh_arrays[Mesh::ARRAY_NORMAL];
727+
const Vector<float> &source_mesh_tangent_array = source_mesh_arrays[Mesh::ARRAY_TANGENT];
728+
const Vector<int> &source_mesh_bones_array = source_mesh_arrays[Mesh::ARRAY_BONES];
729+
const Vector<float> &source_mesh_weights_array = source_mesh_arrays[Mesh::ARRAY_WEIGHTS];
730+
731+
unsigned int vertex_count = source_mesh_vertex_array.size();
732+
int expected_bone_array_size = vertex_count * bones_per_vertex;
733+
ERR_CONTINUE(source_mesh_bones_array.size() != expected_bone_array_size);
734+
ERR_CONTINUE(source_mesh_weights_array.size() != expected_bone_array_size);
735+
736+
Array new_mesh_arrays;
737+
new_mesh_arrays.resize(Mesh::ARRAY_MAX);
738+
for (int i = 0; i < source_mesh_arrays.size(); i++) {
739+
if (i == Mesh::ARRAY_VERTEX || i == Mesh::ARRAY_NORMAL || i == Mesh::ARRAY_TANGENT || i == Mesh::ARRAY_BONES || i == Mesh::ARRAY_WEIGHTS) {
740+
continue;
741+
}
742+
new_mesh_arrays[i] = source_mesh_arrays[i];
743+
}
744+
745+
bool use_normal_array = source_mesh_normal_array.size() == source_mesh_vertex_array.size();
746+
bool use_tangent_array = source_mesh_tangent_array.size() / 4 == source_mesh_vertex_array.size();
747+
748+
Vector<Vector3> lerped_vertex_array = source_mesh_vertex_array;
749+
Vector<Vector3> lerped_normal_array = source_mesh_normal_array;
750+
Vector<float> lerped_tangent_array = source_mesh_tangent_array;
751+
752+
const Vector3 *source_vertices_ptr = source_mesh_vertex_array.ptr();
753+
const Vector3 *source_normals_ptr = source_mesh_normal_array.ptr();
754+
const float *source_tangents_ptr = source_mesh_tangent_array.ptr();
755+
const int *source_bones_ptr = source_mesh_bones_array.ptr();
756+
const float *source_weights_ptr = source_mesh_weights_array.ptr();
757+
758+
Vector3 *lerped_vertices_ptrw = lerped_vertex_array.ptrw();
759+
Vector3 *lerped_normals_ptrw = lerped_normal_array.ptrw();
760+
float *lerped_tangents_ptrw = lerped_tangent_array.ptrw();
761+
762+
for (unsigned int vertex_index = 0; vertex_index < vertex_count; vertex_index++) {
763+
Vector3 lerped_vertex;
764+
Vector3 lerped_normal;
765+
Vector3 lerped_tangent;
766+
767+
const Vector3 &source_vertex = source_vertices_ptr[vertex_index];
768+
769+
Vector3 source_normal;
770+
if (use_normal_array) {
771+
source_normal = source_normals_ptr[vertex_index];
772+
}
773+
774+
int tangent_index = vertex_index * 4;
775+
Vector4 source_tangent;
776+
Vector3 source_tangent_vec3;
777+
if (use_tangent_array) {
778+
source_tangent = Vector4(
779+
source_tangents_ptr[tangent_index],
780+
source_tangents_ptr[tangent_index + 1],
781+
source_tangents_ptr[tangent_index + 2],
782+
source_tangents_ptr[tangent_index + 3]);
783+
784+
DEV_ASSERT(source_tangent.w == 1.0 || source_tangent.w == -1.0);
785+
786+
source_tangent_vec3 = Vector3(source_tangent.x, source_tangent.y, source_tangent.z);
787+
}
788+
789+
for (unsigned int weight_index = 0; weight_index < bones_per_vertex; weight_index++) {
790+
float bone_weight = source_weights_ptr[vertex_index * bones_per_vertex + weight_index];
791+
if (bone_weight < FLT_EPSILON) {
792+
continue;
793+
}
794+
int vertex_bone_index = source_bones_ptr[vertex_index * bones_per_vertex + weight_index];
795+
const Transform3D &bone_transform = bone_transforms[vertex_bone_index];
796+
const Basis bone_basis = bone_transform.basis.orthonormalized();
797+
798+
ERR_FAIL_INDEX_V(vertex_bone_index, static_cast<int>(bone_transforms.size()), Ref<ArrayMesh>());
799+
800+
lerped_vertex += source_vertex.lerp(bone_transform.xform(source_vertex), bone_weight) - source_vertex;
801+
;
802+
803+
if (use_normal_array) {
804+
lerped_normal += source_normal.lerp(bone_basis.xform(source_normal), bone_weight) - source_normal;
805+
}
806+
807+
if (use_tangent_array) {
808+
lerped_tangent += source_tangent_vec3.lerp(bone_basis.xform(source_tangent_vec3), bone_weight) - source_tangent_vec3;
809+
}
810+
}
811+
812+
lerped_vertices_ptrw[vertex_index] += lerped_vertex;
813+
814+
if (use_normal_array) {
815+
lerped_normals_ptrw[vertex_index] = (source_normal + lerped_normal).normalized();
816+
}
817+
818+
if (use_tangent_array) {
819+
lerped_tangent = (source_tangent_vec3 + lerped_tangent).normalized();
820+
lerped_tangents_ptrw[tangent_index] = lerped_tangent.x;
821+
lerped_tangents_ptrw[tangent_index + 1] = lerped_tangent.y;
822+
lerped_tangents_ptrw[tangent_index + 2] = lerped_tangent.z;
823+
}
824+
}
825+
826+
new_mesh_arrays[Mesh::ARRAY_VERTEX] = lerped_vertex_array;
827+
if (use_normal_array) {
828+
new_mesh_arrays[Mesh::ARRAY_NORMAL] = lerped_normal_array;
829+
}
830+
if (use_tangent_array) {
831+
new_mesh_arrays[Mesh::ARRAY_TANGENT] = lerped_tangent_array;
832+
}
833+
834+
bake_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, new_mesh_arrays, Array(), Dictionary(), surface_format);
835+
}
836+
837+
return bake_mesh;
838+
}
839+
674840
void MeshInstance3D::_bind_methods() {
675841
ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &MeshInstance3D::set_mesh);
676842
ClassDB::bind_method(D_METHOD("get_mesh"), &MeshInstance3D::get_mesh);
@@ -700,6 +866,7 @@ void MeshInstance3D::_bind_methods() {
700866
ClassDB::bind_method(D_METHOD("create_debug_tangents"), &MeshInstance3D::create_debug_tangents);
701867

702868
ClassDB::bind_method(D_METHOD("bake_mesh_from_current_blend_shape_mix", "existing"), &MeshInstance3D::bake_mesh_from_current_blend_shape_mix, DEFVAL(Ref<ArrayMesh>()));
869+
ClassDB::bind_method(D_METHOD("bake_mesh_from_current_skeleton_pose", "existing"), &MeshInstance3D::bake_mesh_from_current_skeleton_pose, DEFVAL(Ref<ArrayMesh>()));
703870

704871
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
705872
ADD_GROUP("Skeleton", "");

‎scene/3d/mesh_instance_3d.h

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ class MeshInstance3D : public GeometryInstance3D {
102102
virtual AABB get_aabb() const override;
103103

104104
Ref<ArrayMesh> bake_mesh_from_current_blend_shape_mix(Ref<ArrayMesh> p_existing = Ref<ArrayMesh>());
105+
Ref<ArrayMesh> bake_mesh_from_current_skeleton_pose(Ref<ArrayMesh> p_existing = Ref<ArrayMesh>());
105106

106107
MeshInstance3D();
107108
~MeshInstance3D();

0 commit comments

Comments
 (0)
Please sign in to comment.