mirror of
https://github.com/godotengine/godot.git
synced 2024-11-22 20:23:53 +00:00
Fix LOD-generation on skinned meshes.
This commit is contained in:
parent
200b9cde88
commit
13f5c62124
@ -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">
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user