diff options
Diffstat (limited to 'editor/import')
-rw-r--r-- | editor/import/dynamic_font_import_settings.cpp | 4 | ||||
-rw-r--r-- | editor/import/post_import_plugin_skeleton_renamer.cpp | 2 | ||||
-rw-r--r-- | editor/import/post_import_plugin_skeleton_rest_fixer.cpp | 348 | ||||
-rw-r--r-- | editor/import/resource_importer_scene.cpp | 237 | ||||
-rw-r--r-- | editor/import/resource_importer_scene.h | 2 | ||||
-rw-r--r-- | editor/import/resource_importer_texture.cpp | 193 | ||||
-rw-r--r-- | editor/import/scene_import_settings.h | 2 |
7 files changed, 571 insertions, 217 deletions
diff --git a/editor/import/dynamic_font_import_settings.cpp b/editor/import/dynamic_font_import_settings.cpp index 405d8d2169..d1f37179f3 100644 --- a/editor/import/dynamic_font_import_settings.cpp +++ b/editor/import/dynamic_font_import_settings.cpp @@ -1343,8 +1343,8 @@ DynamicFontImportSettings::DynamicFontImportSettings() { for (int i = 0; i < 16; i++) { glyph_table->set_column_title(i + 1, String::num_int64(i, 16)); } - glyph_table->add_theme_style_override("selected", glyph_table->get_theme_stylebox(SNAME("bg"))); - glyph_table->add_theme_style_override("selected_focus", glyph_table->get_theme_stylebox(SNAME("bg"))); + glyph_table->add_theme_style_override("selected", glyph_table->get_theme_stylebox(SNAME("panel"))); + glyph_table->add_theme_style_override("selected_focus", glyph_table->get_theme_stylebox(SNAME("panel"))); glyph_table->add_theme_constant_override("h_separation", 0); glyph_table->set_h_size_flags(Control::SIZE_EXPAND_FILL); glyph_table->set_v_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/import/post_import_plugin_skeleton_renamer.cpp b/editor/import/post_import_plugin_skeleton_renamer.cpp index 69c0a047e4..72ccb832c7 100644 --- a/editor/import/post_import_plugin_skeleton_renamer.cpp +++ b/editor/import/post_import_plugin_skeleton_renamer.cpp @@ -143,7 +143,7 @@ void PostImportPluginSkeletonRenamer::internal_process(InternalImportCategory p_ // Make unique skeleton. if (bool(p_options["retarget/bone_renamer/unique_node/make_unique"])) { String unique_name = String(p_options["retarget/bone_renamer/unique_node/skeleton_name"]); - ERR_FAIL_COND_MSG(unique_name == String(), "Skeleton unique name cannot be empty."); + ERR_FAIL_COND_MSG(unique_name.is_empty(), "Skeleton unique name cannot be empty."); TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer"); while (nodes.size()) { diff --git a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/post_import_plugin_skeleton_rest_fixer.cpp index 6f775c7ea8..a5ef2e7f97 100644 --- a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp +++ b/editor/import/post_import_plugin_skeleton_rest_fixer.cpp @@ -90,16 +90,9 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory } } - // Apply node transforms. + // Get global transform. + Transform3D global_transform; if (bool(p_options["retarget/rest_fixer/apply_node_transforms"])) { - LocalVector<Transform3D> old_skeleton_rest; - LocalVector<Transform3D> old_skeleton_global_rest; - for (int i = 0; i < src_skeleton->get_bone_count(); i++) { - old_skeleton_rest.push_back(src_skeleton->get_bone_rest(i)); - old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i)); - } - - Transform3D global_transform; Node *pr = src_skeleton; while (pr) { Node3D *pr3d = Object::cast_to<Node3D>(pr); @@ -109,6 +102,47 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory } pr = pr->get_parent(); } + global_transform.origin = Vector3(); // Translation by a Node is not a bone animation, so the retargeted model should be at the origin. + } + + // Calc IBM difference. + LocalVector<Vector<Transform3D>> ibm_diffs; + { + TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D"); + while (nodes.size()) { + ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back()); + ERR_CONTINUE(!mi); + + Ref<Skin> skin = mi->get_skin(); + ERR_CONTINUE(!skin.is_valid()); + + Node *node = mi->get_node(mi->get_skeleton_path()); + ERR_CONTINUE(!node); + + Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node); + if (!mesh_skeleton || mesh_skeleton != src_skeleton) { + continue; + } + + Vector<Transform3D> ibm_diff; + ibm_diff.resize(src_skeleton->get_bone_count()); + Transform3D *ibm_diff_w = ibm_diff.ptrw(); + + int skin_len = skin->get_bind_count(); + for (int i = 0; i < skin_len; i++) { + StringName bn = skin->get_bind_name(i); + int bone_idx = src_skeleton->find_bone(bn); + if (bone_idx >= 0) { + ibm_diff_w[bone_idx] = global_transform * src_skeleton->get_bone_global_rest(bone_idx) * skin->get_bind_pose(i); + } + } + + ibm_diffs.push_back(ibm_diff); + } + } + + // Apply node transforms. + if (bool(p_options["retarget/rest_fixer/apply_node_transforms"])) { Vector3 scl = global_transform.basis.get_scale_local(); Vector<int> bones_to_process = src_skeleton->get_parentless_bones(); @@ -148,38 +182,42 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory String track_path = String(anim->track_get_path(i).get_concatenated_names()); Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); - if (node) { - Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); - if (track_skeleton && track_skeleton == src_skeleton) { - StringName bn = anim->track_get_path(i).get_subname(0); - if (bn) { - int bone_idx = src_skeleton->find_bone(bn); - int key_len = anim->track_get_key_count(i); - if (anim->track_get_type(i) == Animation::TYPE_POSITION_3D) { - if (bones_to_process.has(bone_idx)) { - for (int j = 0; j < key_len; j++) { - Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j)); - anim->track_set_key_value(i, j, global_transform.basis.xform(ps) + global_transform.origin); - } - } else { - for (int j = 0; j < key_len; j++) { - Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j)); - anim->track_set_key_value(i, j, ps * scl); - } - } - } else if (bones_to_process.has(bone_idx)) { - if (anim->track_get_type(i) == Animation::TYPE_ROTATION_3D) { - for (int j = 0; j < key_len; j++) { - Quaternion qt = static_cast<Quaternion>(anim->track_get_key_value(i, j)); - anim->track_set_key_value(i, j, global_transform.basis.get_rotation_quaternion() * qt); - } - } else { - for (int j = 0; j < key_len; j++) { - Basis sc = Basis().scaled(static_cast<Vector3>(anim->track_get_key_value(i, j))); - anim->track_set_key_value(i, j, (global_transform.basis * sc).get_scale()); - } - } - } + ERR_CONTINUE(!node); + + Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); + if (!track_skeleton || track_skeleton != src_skeleton) { + continue; + } + + StringName bn = anim->track_get_path(i).get_subname(0); + if (!bn) { + continue; + } + + int bone_idx = src_skeleton->find_bone(bn); + int key_len = anim->track_get_key_count(i); + if (anim->track_get_type(i) == Animation::TYPE_POSITION_3D) { + if (bones_to_process.has(bone_idx)) { + for (int j = 0; j < key_len; j++) { + Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j)); + anim->track_set_key_value(i, j, global_transform.basis.xform(ps) + global_transform.origin); + } + } else { + for (int j = 0; j < key_len; j++) { + Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j)); + anim->track_set_key_value(i, j, ps * scl); + } + } + } else if (bones_to_process.has(bone_idx)) { + if (anim->track_get_type(i) == Animation::TYPE_ROTATION_3D) { + for (int j = 0; j < key_len; j++) { + Quaternion qt = static_cast<Quaternion>(anim->track_get_key_value(i, j)); + anim->track_set_key_value(i, j, global_transform.basis.get_rotation_quaternion() * qt); + } + } else { + for (int j = 0; j < key_len; j++) { + Basis sc = Basis().scaled(static_cast<Vector3>(anim->track_get_key_value(i, j))); + anim->track_set_key_value(i, j, (global_transform.basis * sc).get_scale()); } } } @@ -220,24 +258,26 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory } } - if (found_skeleton) { - // Search and insert rot track if it doesn't exist. - for (int prof_idx = 0; prof_idx < prof_skeleton->get_bone_count(); prof_idx++) { - String bone_name = is_renamed ? prof_skeleton->get_bone_name(prof_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_idx))); - if (bone_name == String()) { - continue; - } - int src_idx = src_skeleton->find_bone(bone_name); - if (src_idx == -1) { - continue; - } - String insert_path = track_path + ":" + bone_name; - int rot_track = anim->find_track(insert_path, Animation::TYPE_ROTATION_3D); - if (rot_track == -1) { - int track = anim->add_track(Animation::TYPE_ROTATION_3D); - anim->track_set_path(track, insert_path); - anim->rotation_track_insert_key(track, 0, src_skeleton->get_bone_rest(src_idx).basis.get_rotation_quaternion()); - } + if (!found_skeleton) { + continue; + } + + // Search and insert rot track if it doesn't exist. + for (int prof_idx = 0; prof_idx < prof_skeleton->get_bone_count(); prof_idx++) { + String bone_name = is_renamed ? prof_skeleton->get_bone_name(prof_idx) : String(bone_map->get_skeleton_bone_name(prof_skeleton->get_bone_name(prof_idx))); + if (bone_name.is_empty()) { + continue; + } + int src_idx = src_skeleton->find_bone(bone_name); + if (src_idx == -1) { + continue; + } + String insert_path = track_path + ":" + bone_name; + int rot_track = anim->find_track(insert_path, Animation::TYPE_ROTATION_3D); + if (rot_track == -1) { + int track = anim->add_track(Animation::TYPE_ROTATION_3D); + anim->track_set_path(track, insert_path); + anim->rotation_track_insert_key(track, 0, src_skeleton->get_bone_rest(src_idx).basis.get_rotation_quaternion()); } } } @@ -385,19 +425,23 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory String track_path = String(anim->track_get_path(i).get_concatenated_names()); Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); - if (node) { - Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); - if (track_skeleton && track_skeleton == src_skeleton) { - StringName bn = anim->track_get_path(i).get_concatenated_subnames(); - if (bn == scale_base_bone_name) { - int key_len = anim->track_get_key_count(i); - for (int j = 0; j < key_len; j++) { - Vector3 pos = static_cast<Vector3>(anim->track_get_key_value(i, j)); - pos.y += base_adjustment; - anim->track_set_key_value(i, j, pos); - } - } - } + ERR_CONTINUE(!node); + + Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); + if (!track_skeleton || track_skeleton != src_skeleton) { + continue; + } + + StringName bn = anim->track_get_path(i).get_concatenated_subnames(); + if (bn != scale_base_bone_name) { + continue; + } + + int key_len = anim->track_get_key_count(i); + for (int j = 0; j < key_len; j++) { + Vector3 pos = static_cast<Vector3>(anim->track_get_key_value(i, j)); + pos.y += base_adjustment; + anim->track_set_key_value(i, j, pos); } } } @@ -441,16 +485,18 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory String track_path = String(anim->track_get_path(i).get_concatenated_names()); Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); - if (node) { - Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); - if (track_skeleton && track_skeleton == src_skeleton) { - real_t mlt = 1 / src_skeleton->get_motion_scale(); - int key_len = anim->track_get_key_count(i); - for (int j = 0; j < key_len; j++) { - Vector3 pos = static_cast<Vector3>(anim->track_get_key_value(i, j)); - anim->track_set_key_value(i, j, pos * mlt); - } - } + ERR_CONTINUE(!node); + + Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); + if (!track_skeleton || track_skeleton != src_skeleton) { + continue; + } + + real_t mlt = 1 / src_skeleton->get_motion_scale(); + int key_len = anim->track_get_key_count(i); + for (int j = 0; j < key_len; j++) { + Vector3 pos = static_cast<Vector3>(anim->track_get_key_value(i, j)); + anim->track_set_key_value(i, j, pos * mlt); } } } @@ -518,6 +564,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer"); while (nodes.size()) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); + ERR_CONTINUE(!ap); List<StringName> anims; ap->get_animation_list(&anims); for (const StringName &name : anims) { @@ -534,53 +581,57 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory String track_path = String(anim->track_get_path(i).get_concatenated_names()); Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); - if (node) { - Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); - if (track_skeleton && track_skeleton == src_skeleton) { - StringName bn = anim->track_get_path(i).get_subname(0); - if (bn) { - int bone_idx = src_skeleton->find_bone(bn); - - Transform3D old_rest = old_skeleton_rest[bone_idx]; - Transform3D new_rest = src_skeleton->get_bone_rest(bone_idx); - Transform3D old_pg; - Transform3D new_pg; - int parent_idx = src_skeleton->get_bone_parent(bone_idx); - if (parent_idx >= 0) { - old_pg = old_skeleton_global_rest[parent_idx]; - new_pg = src_skeleton->get_bone_global_rest(parent_idx); - } - - int key_len = anim->track_get_key_count(i); - if (anim->track_get_type(i) == Animation::TYPE_ROTATION_3D) { - Quaternion old_rest_q = old_rest.basis.get_rotation_quaternion(); - Quaternion new_rest_q = new_rest.basis.get_rotation_quaternion(); - Quaternion old_pg_q = old_pg.basis.get_rotation_quaternion(); - Quaternion new_pg_q = new_pg.basis.get_rotation_quaternion(); - for (int j = 0; j < key_len; j++) { - Quaternion qt = static_cast<Quaternion>(anim->track_get_key_value(i, j)); - anim->track_set_key_value(i, j, new_pg_q.inverse() * old_pg_q * qt * old_rest_q.inverse() * old_pg_q.inverse() * new_pg_q * new_rest_q); - } - } else if (anim->track_get_type(i) == Animation::TYPE_SCALE_3D) { - Basis old_rest_b = old_rest.basis; - Basis new_rest_b = new_rest.basis; - Basis old_pg_b = old_pg.basis; - Basis new_pg_b = new_pg.basis; - for (int j = 0; j < key_len; j++) { - Basis sc = Basis().scaled(static_cast<Vector3>(anim->track_get_key_value(i, j))); - anim->track_set_key_value(i, j, (new_pg_b.inverse() * old_pg_b * sc * old_rest_b.inverse() * old_pg_b.inverse() * new_pg_b * new_rest_b).get_scale()); - } - } else { - Vector3 old_rest_o = old_rest.origin; - Vector3 new_rest_o = new_rest.origin; - Quaternion old_pg_q = old_pg.basis.get_rotation_quaternion(); - Quaternion new_pg_q = new_pg.basis.get_rotation_quaternion(); - for (int j = 0; j < key_len; j++) { - Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j)); - anim->track_set_key_value(i, j, new_pg_q.xform_inv(old_pg_q.xform(ps - old_rest_o)) + new_rest_o); - } - } - } + ERR_CONTINUE(!node); + + Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); + if (!track_skeleton || track_skeleton != src_skeleton) { + continue; + } + + StringName bn = anim->track_get_path(i).get_subname(0); + if (!bn) { + continue; + } + + int bone_idx = src_skeleton->find_bone(bn); + + Transform3D old_rest = old_skeleton_rest[bone_idx]; + Transform3D new_rest = src_skeleton->get_bone_rest(bone_idx); + Transform3D old_pg; + Transform3D new_pg; + int parent_idx = src_skeleton->get_bone_parent(bone_idx); + if (parent_idx >= 0) { + old_pg = old_skeleton_global_rest[parent_idx]; + new_pg = src_skeleton->get_bone_global_rest(parent_idx); + } + + int key_len = anim->track_get_key_count(i); + if (anim->track_get_type(i) == Animation::TYPE_ROTATION_3D) { + Quaternion old_rest_q = old_rest.basis.get_rotation_quaternion(); + Quaternion new_rest_q = new_rest.basis.get_rotation_quaternion(); + Quaternion old_pg_q = old_pg.basis.get_rotation_quaternion(); + Quaternion new_pg_q = new_pg.basis.get_rotation_quaternion(); + for (int j = 0; j < key_len; j++) { + Quaternion qt = static_cast<Quaternion>(anim->track_get_key_value(i, j)); + anim->track_set_key_value(i, j, new_pg_q.inverse() * old_pg_q * qt * old_rest_q.inverse() * old_pg_q.inverse() * new_pg_q * new_rest_q); + } + } else if (anim->track_get_type(i) == Animation::TYPE_SCALE_3D) { + Basis old_rest_b = old_rest.basis; + Basis new_rest_b = new_rest.basis; + Basis old_pg_b = old_pg.basis; + Basis new_pg_b = new_pg.basis; + for (int j = 0; j < key_len; j++) { + Basis sc = Basis().scaled(static_cast<Vector3>(anim->track_get_key_value(i, j))); + anim->track_set_key_value(i, j, (new_pg_b.inverse() * old_pg_b * sc * old_rest_b.inverse() * old_pg_b.inverse() * new_pg_b * new_rest_b).get_scale()); + } + } else { + Vector3 old_rest_o = old_rest.origin; + Vector3 new_rest_o = new_rest.origin; + Quaternion old_pg_q = old_pg.basis.get_rotation_quaternion(); + Quaternion new_pg_q = new_pg.basis.get_rotation_quaternion(); + for (int j = 0; j < key_len; j++) { + Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j)); + anim->track_set_key_value(i, j, new_pg_q.xform_inv(old_pg_q.xform(ps - old_rest_o)) + new_rest_o); } } } @@ -595,26 +646,35 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory // Fix skin. { TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D"); + int skin_idx = 0; while (nodes.size()) { ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back()); + ERR_CONTINUE(!mi); + Ref<Skin> skin = mi->get_skin(); - if (skin.is_valid()) { - Node *node = mi->get_node(mi->get_skeleton_path()); - if (node) { - Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node); - if (mesh_skeleton && node == src_skeleton) { - int skin_len = skin->get_bind_count(); - for (int i = 0; i < skin_len; i++) { - StringName bn = skin->get_bind_name(i); - int bone_idx = src_skeleton->find_bone(bn); - if (bone_idx >= 0) { - Transform3D new_rest = silhouette_diff[i] * src_skeleton->get_bone_global_rest(bone_idx); - skin->set_bind_pose(i, new_rest.inverse()); - } - } - } + ERR_CONTINUE(!skin.is_valid()); + + Node *node = mi->get_node(mi->get_skeleton_path()); + ERR_CONTINUE(!node); + + Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node); + if (!mesh_skeleton || mesh_skeleton != src_skeleton) { + continue; + } + + Vector<Transform3D> ibm_diff = ibm_diffs[skin_idx]; + + int skin_len = skin->get_bind_count(); + for (int i = 0; i < skin_len; i++) { + StringName bn = skin->get_bind_name(i); + int bone_idx = src_skeleton->find_bone(bn); + if (bone_idx >= 0) { + Transform3D new_rest = silhouette_diff[i] * src_skeleton->get_bone_global_rest(bone_idx); + skin->set_bind_pose(i, new_rest.inverse() * ibm_diff[bone_idx]); } } + + skin_idx++; } } diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 62cb6e4167..b5798a5784 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -368,6 +368,185 @@ static void _pre_gen_shape_list(Ref<ImporterMesh> &mesh, Vector<Ref<Shape3D>> &r } } +struct ScalableNodeCollection { + HashSet<Node3D *> node_3ds; + HashSet<Ref<ImporterMesh>> importer_meshes; + HashSet<Ref<Skin>> skins; + HashSet<Ref<Animation>> animations; +}; + +void _rescale_importer_mesh(Vector3 p_scale, Ref<ImporterMesh> p_mesh, bool is_shadow = false) { + // MESH and SKIN data divide, to compensate for object position multiplying. + + const int surf_count = p_mesh->get_surface_count(); + const int blendshape_count = p_mesh->get_blend_shape_count(); + struct LocalSurfData { + Mesh::PrimitiveType prim = {}; + Array arr; + Array bsarr; + Dictionary lods; + String name; + Ref<Material> mat; + int fmt_compress_flags = 0; + }; + + Vector<LocalSurfData> surf_data_by_mesh; + + Vector<String> blendshape_names; + for (int bsidx = 0; bsidx < blendshape_count; bsidx++) { + blendshape_names.append(p_mesh->get_blend_shape_name(bsidx)); + } + + for (int surf_idx = 0; surf_idx < surf_count; surf_idx++) { + Mesh::PrimitiveType prim = p_mesh->get_surface_primitive_type(surf_idx); + const int fmt_compress_flags = p_mesh->get_surface_format(surf_idx); + Array arr = p_mesh->get_surface_arrays(surf_idx); + String name = p_mesh->get_surface_name(surf_idx); + Dictionary lods = Dictionary(); + Ref<Material> mat = p_mesh->get_surface_material(surf_idx); + { + Vector<Vector3> vertex_array = arr[ArrayMesh::ARRAY_VERTEX]; + for (int vert_arr_i = 0; vert_arr_i < vertex_array.size(); vert_arr_i++) { + vertex_array.write[vert_arr_i] = vertex_array[vert_arr_i] * p_scale; + } + arr[ArrayMesh::ARRAY_VERTEX] = vertex_array; + } + Array blendshapes; + for (int bsidx = 0; bsidx < blendshape_count; bsidx++) { + Array current_bsarr = p_mesh->get_surface_blend_shape_arrays(surf_idx, bsidx); + Vector<Vector3> current_bs_vertex_array = current_bsarr[ArrayMesh::ARRAY_VERTEX]; + int current_bs_vert_arr_len = current_bs_vertex_array.size(); + for (int32_t bs_vert_arr_i = 0; bs_vert_arr_i < current_bs_vert_arr_len; bs_vert_arr_i++) { + current_bs_vertex_array.write[bs_vert_arr_i] = current_bs_vertex_array[bs_vert_arr_i] * p_scale; + } + current_bsarr[ArrayMesh::ARRAY_VERTEX] = current_bs_vertex_array; + blendshapes.push_back(current_bsarr); + } + + LocalSurfData surf_data_dictionary = LocalSurfData(); + surf_data_dictionary.prim = prim; + surf_data_dictionary.arr = arr; + surf_data_dictionary.bsarr = blendshapes; + surf_data_dictionary.lods = lods; + surf_data_dictionary.fmt_compress_flags = fmt_compress_flags; + surf_data_dictionary.name = name; + surf_data_dictionary.mat = mat; + + surf_data_by_mesh.push_back(surf_data_dictionary); + } + + p_mesh->clear(); + + for (int bsidx = 0; bsidx < blendshape_count; bsidx++) { + p_mesh->add_blend_shape(blendshape_names[bsidx]); + } + + for (int surf_idx = 0; surf_idx < surf_count; surf_idx++) { + const Mesh::PrimitiveType prim = surf_data_by_mesh[surf_idx].prim; + const Array arr = surf_data_by_mesh[surf_idx].arr; + const Array bsarr = surf_data_by_mesh[surf_idx].bsarr; + const Dictionary lods = surf_data_by_mesh[surf_idx].lods; + const int fmt_compress_flags = surf_data_by_mesh[surf_idx].fmt_compress_flags; + const String name = surf_data_by_mesh[surf_idx].name; + const Ref<Material> mat = surf_data_by_mesh[surf_idx].mat; + + p_mesh->add_surface(prim, arr, bsarr, lods, mat, name, fmt_compress_flags); + } + + if (!is_shadow && p_mesh->get_shadow_mesh() != p_mesh && p_mesh->get_shadow_mesh().is_valid()) { + _rescale_importer_mesh(p_scale, p_mesh->get_shadow_mesh(), true); + } +} + +void _rescale_skin(Vector3 p_scale, Ref<Skin> p_skin) { + // MESH and SKIN data divide, to compensate for object position multiplying. + for (int i = 0; i < p_skin->get_bind_count(); i++) { + Transform3D transform = p_skin->get_bind_pose(i); + p_skin->set_bind_pose(i, Transform3D(transform.basis, p_scale * transform.origin)); + } +} + +void _rescale_animation(Vector3 p_scale, Ref<Animation> p_animation) { + for (int track_idx = 0; track_idx < p_animation->get_track_count(); track_idx++) { + if (p_animation->track_get_type(track_idx) == Animation::TYPE_POSITION_3D) { + for (int key_idx = 0; key_idx < p_animation->track_get_key_count(track_idx); key_idx++) { + Vector3 value = p_animation->track_get_key_value(track_idx, key_idx); + value = p_scale * value; + p_animation->track_set_key_value(track_idx, key_idx, value); + } + } + } +} + +void _apply_basis_to_scalable_node_collection(ScalableNodeCollection &p_dictionary, Vector3 p_scale) { + for (Node3D *node_3d : p_dictionary.node_3ds) { + if (node_3d) { + node_3d->set_position(p_scale * node_3d->get_position()); + + Skeleton3D *skeleton_3d = Object::cast_to<Skeleton3D>(node_3d); + if (skeleton_3d) { + for (int i = 0; i < skeleton_3d->get_bone_count(); i++) { + Transform3D rest = skeleton_3d->get_bone_rest(i); + skeleton_3d->set_bone_rest(i, Transform3D(rest.basis, p_scale * rest.origin)); + skeleton_3d->set_bone_pose_position(i, p_scale * rest.origin); + } + } + } + } + for (Ref<ImporterMesh> mesh : p_dictionary.importer_meshes) { + _rescale_importer_mesh(p_scale, mesh, false); + } + for (Ref<Skin> skin : p_dictionary.skins) { + _rescale_skin(p_scale, skin); + } + for (Ref<Animation> animation : p_dictionary.animations) { + _rescale_animation(p_scale, animation); + } +} + +void _populate_scalable_nodes_collection(Node *p_node, ScalableNodeCollection &p_dictionary) { + if (!p_node) { + return; + } + Node3D *node_3d = Object::cast_to<Node3D>(p_node); + if (node_3d) { + p_dictionary.node_3ds.insert(node_3d); + ImporterMeshInstance3D *mesh_instance_3d = Object::cast_to<ImporterMeshInstance3D>(p_node); + if (mesh_instance_3d) { + Ref<ImporterMesh> mesh = mesh_instance_3d->get_mesh(); + if (mesh.is_valid()) { + p_dictionary.importer_meshes.insert(mesh); + } + Ref<Skin> skin = mesh_instance_3d->get_skin(); + if (skin.is_valid()) { + p_dictionary.skins.insert(skin); + } + } + } + AnimationPlayer *animation_player = Object::cast_to<AnimationPlayer>(p_node); + if (animation_player) { + List<StringName> animation_list; + animation_player->get_animation_list(&animation_list); + + for (const StringName &E : animation_list) { + Ref<Animation> animation = animation_player->get_animation(E); + p_dictionary.animations.insert(animation); + } + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + _populate_scalable_nodes_collection(child, p_dictionary); + } +} + +void _apply_permanent_rotation_scale_to_node(Node *p_node) { + Transform3D transform = Object::cast_to<Node3D>(p_node)->get_transform(); + ScalableNodeCollection scalable_node_collection; + _populate_scalable_nodes_collection(p_node, scalable_node_collection); + _apply_basis_to_scalable_node_collection(scalable_node_collection, transform.basis.get_scale()); +} + Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &r_collision_map, Pair<PackedVector3Array, PackedInt32Array> *r_occluder_arrays, List<Pair<NodePath, Node *>> &r_node_renames) { // Children first. for (int i = 0; i < p_node->get_child_count(); i++) { @@ -1678,6 +1857,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import script_ext_hint += "*." + E; } + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/apply_root_scale"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true)); @@ -1711,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) { @@ -1827,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) { @@ -2144,6 +2358,21 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p return err; } + bool apply_root = true; + if (p_options.has("nodes/apply_root_scale")) { + apply_root = p_options["nodes/apply_root_scale"]; + } + real_t root_scale = 1; + if (p_options.has("nodes/root_scale")) { + root_scale = p_options["nodes/root_scale"]; + } + if (Object::cast_to<Node3D>(scene)) { + Object::cast_to<Node3D>(scene)->scale(Vector3(root_scale, root_scale, root_scale)); + } + if (apply_root) { + _apply_permanent_rotation_scale_to_node(scene); + Object::cast_to<Node3D>(scene)->scale(Vector3(root_scale, root_scale, root_scale).inverse()); + } Dictionary subresources = p_options["_subresources"]; Dictionary node_data; @@ -2199,12 +2428,6 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p scene->set_script(Variant(root_script)); } - float root_scale = 1.0; - if (Object::cast_to<Node3D>(scene)) { - root_scale = p_options["nodes/root_scale"]; - Object::cast_to<Node3D>(scene)->scale(Vector3(root_scale, root_scale, root_scale)); - } - if (p_options["nodes/root_name"] != "Scene Root") { scene->set_name(p_options["nodes/root_name"]); } else { 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/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 17b94ec706..c06756ff0b 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -36,6 +36,8 @@ #include "core/version.h" #include "editor/editor_file_system.h" #include "editor/editor_node.h" +#include "editor/editor_scale.h" +#include "editor/editor_settings.h" void ResourceImporterTexture::_texture_reimport_roughness(const Ref<CompressedTexture2D> &p_tex, const String &p_normal_path, RS::TextureDetectRoughnessChannel p_channel) { ERR_FAIL_COND(p_tex.is_null()); @@ -233,6 +235,10 @@ void ResourceImporterTexture::get_import_options(const String &p_path, List<Impo if (p_path.get_extension() == "svg") { r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "svg/scale", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 1.0)); + + // Editor use only, applies to SVG. + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "editor/scale_with_editor_scale"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "editor/convert_colors_with_editor_theme"), false)); } } @@ -447,6 +453,14 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String scale = p_options["svg/scale"]; } + // Editor-specific options. + bool use_editor_scale = p_options.has("editor/scale_with_editor_scale") && p_options["editor/scale_with_editor_scale"]; + bool convert_editor_colors = p_options.has("editor/convert_colors_with_editor_theme") && p_options["editor/convert_colors_with_editor_theme"]; + + // Start importing images. + List<Ref<Image>> images_imported; + + // Load the normal image. Ref<Image> normal_image; Image::RoughnessChannel roughness_channel = Image::ROUGHNESS_CHANNEL_R; if (mipmaps && roughness > 1 && FileAccess::exists(normal_map)) { @@ -456,88 +470,117 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String } } + // Load the main image. Ref<Image> image; image.instantiate(); Error err = ImageLoader::load_image(p_source_file, image, nullptr, loader_flags, scale); if (err != OK) { return err; } + images_imported.push_back(image); - Array formats_imported; - - if (size_limit > 0 && (image->get_width() > size_limit || image->get_height() > size_limit)) { - //limit size - if (image->get_width() >= image->get_height()) { - int new_width = size_limit; - int new_height = image->get_height() * new_width / image->get_width(); - - image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC); - } else { - int new_height = size_limit; - int new_width = image->get_width() * new_height / image->get_height(); + // Load the editor-only image. + Ref<Image> editor_image; + bool import_editor_image = use_editor_scale || convert_editor_colors; + if (import_editor_image) { + float editor_scale = scale; + if (use_editor_scale) { + editor_scale = scale * EDSCALE; + } - image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC); + int32_t editor_loader_flags = loader_flags; + if (convert_editor_colors) { + editor_loader_flags |= ImageFormatLoader::FLAG_CONVERT_COLORS; } - if (normal == 1) { - image->normalize(); + editor_image.instantiate(); + err = ImageLoader::load_image(p_source_file, editor_image, nullptr, editor_loader_flags, editor_scale); + if (err != OK) { + WARN_PRINT("Failed to import an image resource for editor use from '" + p_source_file + "'"); + } else { + images_imported.push_back(editor_image); } } - if (fix_alpha_border) { - image->fix_alpha_edges(); - } + for (Ref<Image> &target_image : images_imported) { + // Apply the size limit. + if (size_limit > 0 && (target_image->get_width() > size_limit || target_image->get_height() > size_limit)) { + if (target_image->get_width() >= target_image->get_height()) { + int new_width = size_limit; + int new_height = target_image->get_height() * new_width / target_image->get_width(); - if (premult_alpha) { - image->premultiply_alpha(); - } + target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC); + } else { + int new_height = size_limit; + int new_width = target_image->get_width() * new_height / target_image->get_height(); - if (normal_map_invert_y) { - // Inverting the green channel can be used to flip a normal map's direction. - // There's no standard when it comes to normal map Y direction, so this is - // sometimes needed when using a normal map exported from another program. - // See <http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates>. - const int height = image->get_height(); - const int width = image->get_width(); + target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC); + } - for (int i = 0; i < width; i++) { - for (int j = 0; j < height; j++) { - const Color color = image->get_pixel(i, j); - image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b)); + if (normal == 1) { + target_image->normalize(); } } - } - if (hdr_clamp_exposure) { - // Clamp HDR exposure following Filament's tonemapping formula. - // This can be used to reduce fireflies in environment maps or reduce the influence - // of the sun from an HDRI panorama on environment lighting (when a DirectionalLight3D is used instead). - const int height = image->get_height(); - const int width = image->get_width(); - - // These values are chosen arbitrarily and seem to produce good results with 4,096 samples. - const float linear = 4096.0; - const float compressed = 16384.0; + // Fix alpha border. + if (fix_alpha_border) { + target_image->fix_alpha_edges(); + } - for (int i = 0; i < width; i++) { - for (int j = 0; j < height; j++) { - const Color color = image->get_pixel(i, j); - const float luma = color.get_luminance(); + // Premultiply the alpha. + if (premult_alpha) { + target_image->premultiply_alpha(); + } - Color clamped_color; - if (luma <= linear) { - clamped_color = color; - } else { - clamped_color = (color / luma) * ((linear * linear - compressed * luma) / (2 * linear - compressed - luma)); + // Invert the green channel of the image to flip the normal map it contains. + if (normal_map_invert_y) { + // Inverting the green channel can be used to flip a normal map's direction. + // There's no standard when it comes to normal map Y direction, so this is + // sometimes needed when using a normal map exported from another program. + // See <http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates>. + const int height = target_image->get_height(); + const int width = target_image->get_width(); + + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + const Color color = target_image->get_pixel(i, j); + target_image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b)); } + } + } - image->set_pixel(i, j, clamped_color); + // Clamp HDR exposure. + if (hdr_clamp_exposure) { + // Clamp HDR exposure following Filament's tonemapping formula. + // This can be used to reduce fireflies in environment maps or reduce the influence + // of the sun from an HDRI panorama on environment lighting (when a DirectionalLight3D is used instead). + const int height = target_image->get_height(); + const int width = target_image->get_width(); + + // These values are chosen arbitrarily and seem to produce good results with 4,096 samples. + const float linear = 4096.0; + const float compressed = 16384.0; + + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + const Color color = target_image->get_pixel(i, j); + const float luma = color.get_luminance(); + + Color clamped_color; + if (luma <= linear) { + clamped_color = color; + } else { + clamped_color = (color / luma) * ((linear * linear - compressed * luma) / (2 * linear - compressed - luma)); + } + + target_image->set_pixel(i, j, clamped_color); + } } } } if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) { - //basis universal does not support float formats, fall back + // Basis universal does not support float formats, fallback. compress_mode = COMPRESS_VRAM_COMPRESSED; } @@ -547,9 +590,11 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String bool force_normal = normal == 1; bool srgb_friendly_pack = pack_channels == 0; + Array formats_imported; + if (compress_mode == COMPRESS_VRAM_COMPRESSED) { - //must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc). - //Android, GLES 2.x + // Must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc). + // Android, GLES 2.x const bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995); bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565); @@ -557,7 +602,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String const bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_s3tc"); if (can_bptc) { - //add to the list anyway + // Add to the list anyway. formats_imported.push_back("bptc"); } @@ -566,9 +611,9 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String if (is_hdr && can_compress_hdr) { if (has_alpha) { - //can compress hdr, but hdr with alpha is not compressible + // Can compress HDR, but HDR with alpha is not compressible. if (hdr_compression == 2) { - //but user selected to compress hdr anyway, so force an alpha-less format. + // But user selected to compress HDR anyway, so force an alpha-less format. if (image->get_format() == Image::FORMAT_RGBAF) { image->convert(Image::FORMAT_RGBF); } else if (image->get_format() == Image::FORMAT_RGBAH) { @@ -580,7 +625,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String } if (!can_compress_hdr) { - //fallback to RGBE99995 + // Fallback to RGBE99995. if (image->get_format() != Image::FORMAT_RGBE9995) { image->convert(Image::FORMAT_RGBE9995); } @@ -615,16 +660,31 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String EditorNode::add_io_error(vformat(TTR("%s: No suitable desktop VRAM compression algorithm enabled in Project Settings (S3TC or BPTC). This texture may not display correctly on desktop platforms."), p_source_file)); } } else { - //import normally + // Import normally. _save_ctex(image, p_save_path + ".ctex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel); } + if (editor_image.is_valid()) { + _save_ctex(editor_image, p_save_path + ".editor.ctex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel); + } + if (r_metadata) { Dictionary metadata; metadata["vram_texture"] = compress_mode == COMPRESS_VRAM_COMPRESSED; if (formats_imported.size()) { metadata["imported_formats"] = formats_imported; } + + if (editor_image.is_valid()) { + metadata["has_editor_variant"] = true; + if (use_editor_scale) { + metadata["editor_scale"] = EDSCALE; + } + if (convert_editor_colors) { + metadata["editor_dark_theme"] = EditorSettings::get_singleton()->is_dark_theme(); + } + } + *r_metadata = metadata; } return OK; @@ -657,13 +717,22 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co //will become invalid if formats are missing to import Dictionary metadata = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path); + if (metadata.has("has_editor_variant")) { + if (metadata.has("editor_scale") && (float)metadata["editor_scale"] != EDSCALE) { + return false; + } + if (metadata.has("editor_dark_theme") && (bool)metadata["editor_dark_theme"] != EditorSettings::get_singleton()->is_dark_theme()) { + return false; + } + } + if (!metadata.has("vram_texture")) { return false; } bool vram = metadata["vram_texture"]; if (!vram) { - return true; //do not care about non vram + return true; // Do not care about non-VRAM. } Vector<String> formats_imported; diff --git a/editor/import/scene_import_settings.h b/editor/import/scene_import_settings.h index 104a7a9f7e..0e12a83116 100644 --- a/editor/import/scene_import_settings.h +++ b/editor/import/scene_import_settings.h @@ -192,7 +192,7 @@ class SceneImportSettings : public ConfirmationDialog { bool editing_animation = false; - Timer *update_view_timer; + Timer *update_view_timer = nullptr; protected: void _notification(int p_what); |