summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSaracenOne <SaracenOne@gmail.com>2022-09-07 20:13:10 +0100
committerSaracenOne <SaracenOne@gmail.com>2022-09-14 02:46:10 +0100
commit13f5c6212432bcc611364ca5df7fd67da37e3929 (patch)
treeb4d48fe8bfdfa2670bac5446f8d0a7bd3725d156
parent200b9cde88da485659a6376266af8e9bf7be186f (diff)
Fix LOD-generation on skinned meshes.
-rw-r--r--doc/classes/ImporterMesh.xml2
-rw-r--r--editor/import/resource_importer_scene.cpp36
-rw-r--r--editor/import/resource_importer_scene.h2
-rw-r--r--scene/resources/importer_mesh.cpp41
-rw-r--r--scene/resources/importer_mesh.h2
5 files changed, 79 insertions, 4 deletions
diff --git a/doc/classes/ImporterMesh.xml b/doc/classes/ImporterMesh.xml
index 3c3dbe4d87..1c9655c266 100644
--- a/doc/classes/ImporterMesh.xml
+++ b/doc/classes/ImporterMesh.xml
@@ -43,10 +43,12 @@
<return type="void" />
<param index="0" name="normal_merge_angle" type="float" />
<param index="1" name="normal_split_angle" type="float" />
+ <param index="2" name="bone_transform_array" type="Array" />
<description>
Generates all lods for this ImporterMesh.
[param normal_merge_angle] and [param normal_split_angle] are in degrees and used in the same way as the importer settings in [code]lods[/code]. As a good default, use 25 and 60 respectively.
The number of generated lods can be accessed using [method get_surface_lod_count], and each LOD is available in [method get_surface_lod_size] and [method get_surface_lod_indices].
+ [param bone_transform_array] is an [Array] which can be either empty or contain [Transform3D]s which, for each of the mesh's bone IDs, will apply mesh skinning when generating the LOD mesh variations. This is usually used to account for discrepancies in scale between the mesh itself and its skinning data.
</description>
</method>
<method name="get_blend_shape_count" qualifiers="const">
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 41061c3fc3..b5798a5784 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -1891,6 +1891,39 @@ void ResourceImporterScene::_replace_owner(Node *p_node, Node *p_scene, Node *p_
}
}
+Array ResourceImporterScene::_get_skinned_pose_transforms(ImporterMeshInstance3D *p_src_mesh_node) {
+ Array skin_pose_transform_array;
+
+ const Ref<Skin> skin = p_src_mesh_node->get_skin();
+ if (skin.is_valid()) {
+ NodePath skeleton_path = p_src_mesh_node->get_skeleton_path();
+ const Node *node = p_src_mesh_node->get_node_or_null(skeleton_path);
+ const Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
+ if (skeleton) {
+ int bind_count = skin->get_bind_count();
+
+ for (int i = 0; i < bind_count; i++) {
+ Transform3D bind_pose = skin->get_bind_pose(i);
+ String bind_name = skin->get_bind_name(i);
+
+ int bone_idx = bind_name.is_empty() ? skin->get_bind_bone(i) : skeleton->find_bone(bind_name);
+ ERR_FAIL_COND_V(bone_idx >= skeleton->get_bone_count(), Array());
+
+ Transform3D bp_global_rest;
+ if (bone_idx >= 0) {
+ bp_global_rest = skeleton->get_bone_global_pose(bone_idx);
+ } else {
+ bp_global_rest = skeleton->get_bone_global_pose(i);
+ }
+
+ skin_pose_transform_array.push_back(bp_global_rest * bind_pose);
+ }
+ }
+ }
+
+ return skin_pose_transform_array;
+}
+
void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches) {
ImporterMeshInstance3D *src_mesh_node = Object::cast_to<ImporterMeshInstance3D>(p_node);
if (src_mesh_node) {
@@ -2007,7 +2040,8 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
}
if (generate_lods) {
- src_mesh_node->get_mesh()->generate_lods(merge_angle, split_angle);
+ Array skin_pose_transform_array = _get_skinned_pose_transforms(src_mesh_node);
+ src_mesh_node->get_mesh()->generate_lods(merge_angle, split_angle, skin_pose_transform_array);
}
if (create_shadow_meshes) {
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index da37893cc5..77bc06533c 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -34,6 +34,7 @@
#include "core/error/error_macros.h"
#include "core/io/resource_importer.h"
#include "core/variant/dictionary.h"
+#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/node_3d.h"
#include "scene/resources/animation.h"
#include "scene/resources/mesh.h"
@@ -208,6 +209,7 @@ class ResourceImporterScene : public ResourceImporter {
SHAPE_TYPE_CAPSULE,
};
+ Array _get_skinned_pose_transforms(ImporterMeshInstance3D *p_src_mesh_node);
void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner);
void _generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches);
void _add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes);
diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp
index 0afca95de0..de3d502102 100644
--- a/scene/resources/importer_mesh.cpp
+++ b/scene/resources/importer_mesh.cpp
@@ -254,7 +254,20 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma
mesh.unref();
}
-void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle) {
+#define VERTEX_SKIN_FUNC(bone_count, vert_idx, read_array, write_array, transform_array, bone_array, weight_array) \
+ Vector3 transformed_vert = Vector3(); \
+ for (unsigned int weight_idx = 0; weight_idx < bone_count; weight_idx++) { \
+ int bone_idx = bone_array[vert_idx * bone_count + weight_idx]; \
+ float w = weight_array[vert_idx * bone_count + weight_idx]; \
+ if (w < FLT_EPSILON) { \
+ continue; \
+ } \
+ ERR_FAIL_INDEX(bone_idx, static_cast<int>(transform_array.size())); \
+ transformed_vert += transform_array[bone_idx].xform(read_array[vert_idx]) * w; \
+ } \
+ write_array[vert_idx] = transformed_vert;
+
+void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_bone_transform_array) {
if (!SurfaceTool::simplify_scale_func) {
return;
}
@@ -265,6 +278,12 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
return;
}
+ LocalVector<Transform3D> bone_transform_vector;
+ for (int i = 0; i < p_bone_transform_array.size(); i++) {
+ ERR_FAIL_COND(p_bone_transform_array[i].get_type() != Variant::TRANSFORM3D);
+ bone_transform_vector.push_back(p_bone_transform_array[i]);
+ }
+
for (int i = 0; i < surfaces.size(); i++) {
if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) {
continue;
@@ -276,6 +295,8 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL];
Vector<Vector2> uvs = surfaces[i].arrays[RS::ARRAY_TEX_UV];
Vector<Vector2> uv2s = surfaces[i].arrays[RS::ARRAY_TEX_UV2];
+ Vector<int> bones = surfaces[i].arrays[RS::ARRAY_BONES];
+ Vector<float> weights = surfaces[i].arrays[RS::ARRAY_WEIGHTS];
unsigned int index_count = indices.size();
unsigned int vertex_count = vertices.size();
@@ -301,6 +322,22 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
}
}
+ if (bones.size() > 0 && weights.size() && bone_transform_vector.size() > 0) {
+ Vector3 *vertices_ptrw = vertices.ptrw();
+
+ // Apply bone transforms to regular surface.
+ unsigned int bone_weight_length = surfaces[i].flags & Mesh::ARRAY_FLAG_USE_8_BONE_WEIGHTS ? 8 : 4;
+
+ const int *bo = bones.ptr();
+ const float *we = weights.ptr();
+
+ for (unsigned int j = 0; j < vertex_count; j++) {
+ VERTEX_SKIN_FUNC(bone_weight_length, j, vertices_ptr, vertices_ptrw, bone_transform_vector, bo, we)
+ }
+
+ vertices_ptr = vertices.ptr();
+ }
+
float normal_merge_threshold = Math::cos(Math::deg_to_rad(p_normal_merge_angle));
float normal_pre_split_threshold = Math::cos(Math::deg_to_rad(MIN(180.0f, p_normal_split_angle * 2.0f)));
float normal_split_threshold = Math::cos(Math::deg_to_rad(p_normal_split_angle));
@@ -1246,7 +1283,7 @@ void ImporterMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_surface_name", "surface_idx", "name"), &ImporterMesh::set_surface_name);
ClassDB::bind_method(D_METHOD("set_surface_material", "surface_idx", "material"), &ImporterMesh::set_surface_material);
- ClassDB::bind_method(D_METHOD("generate_lods", "normal_merge_angle", "normal_split_angle"), &ImporterMesh::generate_lods);
+ ClassDB::bind_method(D_METHOD("generate_lods", "normal_merge_angle", "normal_split_angle", "bone_transform_array"), &ImporterMesh::generate_lods);
ClassDB::bind_method(D_METHOD("get_mesh", "base_mesh"), &ImporterMesh::get_mesh, DEFVAL(Ref<ArrayMesh>()));
ClassDB::bind_method(D_METHOD("clear"), &ImporterMesh::clear);
diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h
index dce2638c19..088a77edd1 100644
--- a/scene/resources/importer_mesh.h
+++ b/scene/resources/importer_mesh.h
@@ -112,7 +112,7 @@ public:
void set_surface_material(int p_surface, const Ref<Material> &p_material);
- void generate_lods(float p_normal_merge_angle, float p_normal_split_angle);
+ void generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array);
void create_shadow_mesh();
Ref<ImporterMesh> get_shadow_mesh() const;