feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include "collada.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "core/config/project_settings.h"
|
||||
|
||||
//#define DEBUG_DEFAULT_ANIMATION
|
||||
//#define DEBUG_COLLADA
|
||||
|
|
@ -1808,10 +1808,10 @@ void Collada::_parse_animation(XMLParser &p_parser) {
|
|||
}
|
||||
}
|
||||
|
||||
if (target.contains("/")) { //transform component
|
||||
if (target.contains_char('/')) { //transform component
|
||||
track.target = target.get_slicec('/', 0);
|
||||
track.param = target.get_slicec('/', 1);
|
||||
if (track.param.contains(".")) {
|
||||
if (track.param.contains_char('.')) {
|
||||
track.component = track.param.get_slice(".", 1).to_upper();
|
||||
}
|
||||
track.param = track.param.get_slice(".", 0);
|
||||
|
|
|
|||
|
|
@ -31,10 +31,7 @@
|
|||
#ifndef COLLADA_H
|
||||
#define COLLADA_H
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/xml_parser.h"
|
||||
#include "core/templates/rb_map.h"
|
||||
#include "scene/resources/material.h"
|
||||
|
||||
class Collada {
|
||||
public:
|
||||
|
|
@ -359,7 +356,7 @@ public:
|
|||
for (int i = 0; i < children.size(); i++) {
|
||||
memdelete(children[i]);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct NodeSkeleton : public Node {
|
||||
|
|
|
|||
|
|
@ -30,20 +30,17 @@
|
|||
|
||||
#include "editor_import_collada.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "editor/import/3d/collada.h"
|
||||
#include "scene/3d/camera_3d.h"
|
||||
#include "scene/3d/importer_mesh_instance_3d.h"
|
||||
#include "scene/3d/light_3d.h"
|
||||
#include "scene/3d/mesh_instance_3d.h"
|
||||
#include "scene/3d/node_3d.h"
|
||||
#include "scene/3d/path_3d.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/resources/3d/importer_mesh.h"
|
||||
#include "scene/resources/animation.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "scene/resources/surface_tool.h"
|
||||
|
||||
struct ColladaImport {
|
||||
|
|
@ -1263,7 +1260,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
|
|||
//bleh, must ignore invalid
|
||||
|
||||
ERR_FAIL_COND_V(!collada.state.mesh_data_map.has(meshid), ERR_INVALID_DATA);
|
||||
mesh = Ref<ImporterMesh>(memnew(ImporterMesh));
|
||||
mesh.instantiate();
|
||||
const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid];
|
||||
String name = meshdata.name;
|
||||
if (name.is_empty()) {
|
||||
|
|
@ -1290,7 +1287,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
|
|||
}
|
||||
}
|
||||
|
||||
if (!mesh.is_null()) {
|
||||
if (mesh.is_valid()) {
|
||||
mi->set_mesh(mesh);
|
||||
if (!use_mesh_builtin_materials) {
|
||||
const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid];
|
||||
|
|
@ -1799,10 +1796,6 @@ void ColladaImport::create_animation(int p_clip, bool p_import_value_tracks) {
|
|||
/*************************************** SCENE ***********************************/
|
||||
/*********************************************************************************/
|
||||
|
||||
uint32_t EditorSceneFormatImporterCollada::get_import_flags() const {
|
||||
return IMPORT_SCENE | IMPORT_ANIMATION;
|
||||
}
|
||||
|
||||
void EditorSceneFormatImporterCollada::get_extensions(List<String> *r_extensions) const {
|
||||
r_extensions->push_back("dae");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ class EditorSceneFormatImporterCollada : public EditorSceneFormatImporter {
|
|||
GDCLASS(EditorSceneFormatImporterCollada, EditorSceneFormatImporter);
|
||||
|
||||
public:
|
||||
virtual uint32_t get_import_flags() const override;
|
||||
virtual void get_extensions(List<String> *r_extensions) const override;
|
||||
virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps = nullptr, Error *r_err = nullptr) override;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "post_import_plugin_skeleton_renamer.h"
|
||||
|
||||
#include "editor/import/3d/scene_import_settings.h"
|
||||
#include "scene/3d/bone_attachment_3d.h"
|
||||
#include "scene/3d/importer_mesh_instance_3d.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
|
|
@ -39,7 +38,7 @@
|
|||
|
||||
void PostImportPluginSkeletonRenamer::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) {
|
||||
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/rename_bones"), true));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/rename_bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/unique_node/make_unique"), true));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::STRING, "retarget/bone_renamer/unique_node/skeleton_name"), "GeneralSkeleton"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@
|
|||
|
||||
#include "post_import_plugin_skeleton_rest_fixer.h"
|
||||
|
||||
#include "editor/import/3d/scene_import_settings.h"
|
||||
#include "scene/3d/bone_attachment_3d.h"
|
||||
#include "scene/3d/importer_mesh_instance_3d.h"
|
||||
#include "scene/3d/retarget_modifier_3d.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/resources/bone_map.h"
|
||||
|
|
@ -42,19 +42,29 @@ void PostImportPluginSkeletonRestFixer::get_internal_import_options(InternalImpo
|
|||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/apply_node_transforms"), true));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/normalize_position_tracks"), true));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/reset_all_bone_poses_after_import"), true));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/overwrite_axis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
|
||||
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "retarget/rest_fixer/retarget_method", PROPERTY_HINT_ENUM, "None,Overwrite Axis,Use Retarget Modifier", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/keep_global_rest_on_leftovers"), true));
|
||||
String skeleton_bones_must_be_renamed_warning = String(
|
||||
"The skeleton modifier option uses SkeletonProfile as a list of bone names and retargets by name matching. Without renaming, retargeting by modifier will not work and the track path of the animation will be broken and it will be not playbacked correctly."); // TODO: translate.
|
||||
r_options->push_back(ResourceImporter::ImportOption(
|
||||
PropertyInfo(
|
||||
Variant::STRING, U"retarget/rest_fixer/\u26A0_validation_warning/skeleton_bones_must_be_renamed",
|
||||
PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY),
|
||||
Variant(skeleton_bones_must_be_renamed_warning)));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/use_global_pose"), true));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::STRING, "retarget/rest_fixer/original_skeleton_name"), "OriginalSkeleton"));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/fix_silhouette/enable", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
|
||||
// TODO: PostImportPlugin need to be implemented such as validate_option(PropertyInfo &property, const Dictionary &p_options).
|
||||
// get_internal_option_visibility() is not sufficient because it can only retrieve options implemented in the core and can only read option values.
|
||||
// r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::ARRAY, "retarget/rest_fixer/filter", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::STRING_NAME, PROPERTY_HINT_ENUM, "Hips,Spine,Chest")), Array()));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::ARRAY, "retarget/rest_fixer/fix_silhouette/filter", PROPERTY_HINT_ARRAY_TYPE, "StringName"), Array()));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::ARRAY, "retarget/rest_fixer/fix_silhouette/filter", PROPERTY_HINT_ARRAY_TYPE, vformat("%s:", Variant::STRING_NAME)), Array()));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "retarget/rest_fixer/fix_silhouette/threshold"), 15));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "retarget/rest_fixer/fix_silhouette/base_height_adjustment", PROPERTY_HINT_RANGE, "-1,1,0.01"), 0.0));
|
||||
}
|
||||
}
|
||||
|
||||
Variant PostImportPluginSkeletonRestFixer::get_internal_option_visibility(InternalImportCategory p_category, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
Variant PostImportPluginSkeletonRestFixer::get_internal_option_visibility(InternalImportCategory p_category, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
|
||||
if (p_option.begins_with("retarget/rest_fixer/fix_silhouette/")) {
|
||||
if (!bool(p_options["retarget/rest_fixer/fix_silhouette/enable"])) {
|
||||
|
|
@ -63,7 +73,11 @@ Variant PostImportPluginSkeletonRestFixer::get_internal_option_visibility(Intern
|
|||
}
|
||||
}
|
||||
} else if (p_option == "retarget/rest_fixer/keep_global_rest_on_leftovers") {
|
||||
return bool(p_options["retarget/rest_fixer/overwrite_axis"]);
|
||||
return int(p_options["retarget/rest_fixer/retarget_method"]) == 1;
|
||||
} else if (p_option == "retarget/rest_fixer/original_skeleton_name" || p_option == "retarget/rest_fixer/use_global_pose") {
|
||||
return int(p_options["retarget/rest_fixer/retarget_method"]) == 2;
|
||||
} else if (p_option.begins_with("retarget/") && p_option.ends_with("skeleton_bones_must_be_renamed")) {
|
||||
return int(p_options["retarget/rest_fixer/retarget_method"]) == 2 && bool(p_options["retarget/bone_renamer/rename_bones"]) == false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
@ -78,7 +92,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
}
|
||||
BoneMap *bone_map = Object::cast_to<BoneMap>(map);
|
||||
Ref<SkeletonProfile> profile = bone_map->get_profile();
|
||||
if (!profile.is_valid()) {
|
||||
if (profile.is_null()) {
|
||||
return;
|
||||
}
|
||||
Skeleton3D *src_skeleton = Object::cast_to<Skeleton3D>(p_node);
|
||||
|
|
@ -147,7 +161,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
src_skeleton->set_bone_pose_position(src_idx, src_skeleton->get_bone_pose_position(src_idx) * scl);
|
||||
}
|
||||
|
||||
// Fix animation.
|
||||
// Fix animation by changing node transform.
|
||||
bones_to_process = src_skeleton->get_parentless_bones();
|
||||
{
|
||||
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
|
||||
|
|
@ -224,6 +238,10 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
List<StringName> anims;
|
||||
ap->get_animation_list(&anims);
|
||||
for (const StringName &name : anims) {
|
||||
if (String(name).contains_char('/')) {
|
||||
continue; // Avoid animation library which may be created by importer dynamically.
|
||||
}
|
||||
|
||||
Ref<Animation> anim = ap->get_animation(name);
|
||||
int track_len = anim->get_track_count();
|
||||
|
||||
|
|
@ -454,8 +472,13 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
}
|
||||
}
|
||||
|
||||
// Overwrite axis.
|
||||
if (bool(p_options["retarget/rest_fixer/overwrite_axis"])) {
|
||||
bool is_using_modifier = int(p_options["retarget/rest_fixer/retarget_method"]) == 2;
|
||||
bool is_using_global_pose = bool(p_options["retarget/rest_fixer/use_global_pose"]);
|
||||
Skeleton3D *orig_skeleton = nullptr;
|
||||
Skeleton3D *profile_skeleton = nullptr;
|
||||
|
||||
// Retarget in some way.
|
||||
if (int(p_options["retarget/rest_fixer/retarget_method"]) > 0) {
|
||||
LocalVector<Transform3D> old_skeleton_rest;
|
||||
LocalVector<Transform3D> old_skeleton_global_rest;
|
||||
for (int i = 0; i < src_skeleton->get_bone_count(); i++) {
|
||||
|
|
@ -463,11 +486,151 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i));
|
||||
}
|
||||
|
||||
// Build structure for modifier.
|
||||
if (is_using_modifier) {
|
||||
orig_skeleton = src_skeleton;
|
||||
|
||||
// Duplicate src_skeleton to modify animation tracks, it will memdelele after that animation track modification.
|
||||
src_skeleton = memnew(Skeleton3D);
|
||||
for (int i = 0; i < orig_skeleton->get_bone_count(); i++) {
|
||||
src_skeleton->add_bone(orig_skeleton->get_bone_name(i));
|
||||
src_skeleton->set_bone_rest(i, orig_skeleton->get_bone_rest(i));
|
||||
src_skeleton->set_bone_pose(i, orig_skeleton->get_bone_pose(i));
|
||||
}
|
||||
for (int i = 0; i < orig_skeleton->get_bone_count(); i++) {
|
||||
src_skeleton->set_bone_parent(i, orig_skeleton->get_bone_parent(i));
|
||||
}
|
||||
src_skeleton->set_motion_scale(orig_skeleton->get_motion_scale());
|
||||
|
||||
// Rename orig_skeleton (previous src_skeleton), since it is not animated by animation track with GeneralSkeleton.
|
||||
String original_skeleton_name = String(p_options["retarget/rest_fixer/original_skeleton_name"]);
|
||||
String skel_name = orig_skeleton->get_name();
|
||||
ERR_FAIL_COND_MSG(original_skeleton_name.is_empty(), "Original skeleton name cannot be empty.");
|
||||
ERR_FAIL_COND_MSG(original_skeleton_name == skel_name, "Original skeleton name must be different from unique skeleton name.");
|
||||
|
||||
// Rename profile skeleton to be general skeleton.
|
||||
profile_skeleton = memnew(Skeleton3D);
|
||||
bool is_unique = orig_skeleton->is_unique_name_in_owner();
|
||||
if (is_unique) {
|
||||
orig_skeleton->set_unique_name_in_owner(false);
|
||||
}
|
||||
orig_skeleton->set_name(original_skeleton_name);
|
||||
profile_skeleton->set_name(skel_name);
|
||||
if (is_unique) {
|
||||
profile_skeleton->set_unique_name_in_owner(true);
|
||||
}
|
||||
// Build profile skeleton bones.
|
||||
int len = profile->get_bone_size();
|
||||
for (int i = 0; i < len; i++) {
|
||||
profile_skeleton->add_bone(profile->get_bone_name(i));
|
||||
profile_skeleton->set_bone_rest(i, profile->get_reference_pose(i));
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
int target_parent = profile_skeleton->find_bone(profile->get_bone_parent(i));
|
||||
if (target_parent >= 0) {
|
||||
profile_skeleton->set_bone_parent(i, target_parent);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < len; i++) {
|
||||
Vector3 origin;
|
||||
int found = orig_skeleton->find_bone(profile->get_bone_name(i));
|
||||
String parent_name = profile->get_bone_parent(i);
|
||||
if (found >= 0) {
|
||||
origin = orig_skeleton->get_bone_global_rest(found).origin;
|
||||
if (profile->get_bone_name(i) != profile->get_root_bone()) {
|
||||
int src_parent = -1;
|
||||
while (src_parent < 0 && !parent_name.is_empty()) {
|
||||
src_parent = orig_skeleton->find_bone(parent_name);
|
||||
parent_name = profile->get_bone_parent(profile->find_bone(parent_name));
|
||||
}
|
||||
if (src_parent >= 0) {
|
||||
Transform3D parent_grest = orig_skeleton->get_bone_global_rest(src_parent);
|
||||
origin = origin - parent_grest.origin;
|
||||
}
|
||||
}
|
||||
}
|
||||
int target_parent = profile_skeleton->find_bone(profile->get_bone_parent(i));
|
||||
if (target_parent >= 0) {
|
||||
origin = profile_skeleton->get_bone_global_rest(target_parent).basis.get_rotation_quaternion().xform_inv(origin);
|
||||
}
|
||||
profile_skeleton->set_bone_rest(i, Transform3D(profile_skeleton->get_bone_rest(i).basis, origin));
|
||||
}
|
||||
profile_skeleton->set_motion_scale(orig_skeleton->get_motion_scale());
|
||||
profile_skeleton->reset_bone_poses();
|
||||
// Make structure with modifier.
|
||||
Node *owner = p_node->get_owner();
|
||||
|
||||
Node *pr = orig_skeleton->get_parent();
|
||||
pr->add_child(profile_skeleton);
|
||||
profile_skeleton->set_owner(owner);
|
||||
|
||||
RetargetModifier3D *mod = memnew(RetargetModifier3D);
|
||||
profile_skeleton->add_child(mod);
|
||||
mod->set_owner(owner);
|
||||
mod->set_name("RetargetModifier3D");
|
||||
|
||||
orig_skeleton->set_owner(nullptr);
|
||||
orig_skeleton->reparent(mod, false);
|
||||
orig_skeleton->set_owner(owner);
|
||||
orig_skeleton->set_unique_name_in_owner(true);
|
||||
|
||||
mod->set_use_global_pose(is_using_global_pose);
|
||||
mod->set_profile(profile);
|
||||
|
||||
// Fix skeleton name in animation.
|
||||
// Mapped skeleton is animated by %GenerarSkeleton:RenamedBoneName.
|
||||
// Unmapped skeleton is animated by %OriginalSkeleton:OriginalBoneName.
|
||||
if (is_using_modifier) {
|
||||
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
|
||||
String general_skeleton_pathname = UNIQUE_NODE_PREFIX + profile_skeleton->get_name();
|
||||
while (nodes.size()) {
|
||||
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
|
||||
List<StringName> anims;
|
||||
ap->get_animation_list(&anims);
|
||||
for (const StringName &name : anims) {
|
||||
Ref<Animation> anim = ap->get_animation(name);
|
||||
int track_len = anim->get_track_count();
|
||||
for (int i = 0; i < track_len; i++) {
|
||||
if (anim->track_get_path(i).get_name_count() == 0) {
|
||||
return;
|
||||
}
|
||||
if (anim->track_get_path(i).get_name(0) == general_skeleton_pathname) {
|
||||
bool replace = false;
|
||||
if (anim->track_get_path(i).get_subname_count() > 0) {
|
||||
int found = profile_skeleton->find_bone(anim->track_get_path(i).get_concatenated_subnames());
|
||||
if (found < 0) {
|
||||
replace = true;
|
||||
}
|
||||
} else {
|
||||
replace = true;
|
||||
}
|
||||
if (replace) {
|
||||
String path_string = UNIQUE_NODE_PREFIX + original_skeleton_name;
|
||||
if (anim->track_get_path(i).get_name_count() > 1) {
|
||||
Vector<StringName> names = anim->track_get_path(i).get_names();
|
||||
names.remove_at(0);
|
||||
for (int j = 0; j < names.size(); j++) {
|
||||
path_string += "/" + names[i].operator String();
|
||||
}
|
||||
}
|
||||
if (anim->track_get_path(i).get_subname_count() > 0) {
|
||||
path_string = path_string + String(":") + anim->track_get_path(i).get_concatenated_subnames();
|
||||
}
|
||||
anim->track_set_path(i, path_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool keep_global_rest_leftovers = bool(p_options["retarget/rest_fixer/keep_global_rest_on_leftovers"]);
|
||||
|
||||
// Scan hierarchy and populate a whitelist of unmapped bones without mapped descendants.
|
||||
// When both is_using_modifier and is_using_global_pose are enabled, this array is used for detecting warning.
|
||||
Vector<int> keep_bone_rest;
|
||||
if (keep_global_rest_leftovers) {
|
||||
if (is_using_modifier || keep_global_rest_leftovers) {
|
||||
Vector<int> bones_to_process = src_skeleton->get_parentless_bones();
|
||||
while (bones_to_process.size() > 0) {
|
||||
int src_idx = bones_to_process[0];
|
||||
|
|
@ -482,13 +645,13 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
// Scan descendants for mapped bones.
|
||||
bool found_mapped = false;
|
||||
|
||||
Vector<int> decendants_to_process = src_skeleton->get_bone_children(src_idx);
|
||||
while (decendants_to_process.size() > 0) {
|
||||
int desc_idx = decendants_to_process[0];
|
||||
decendants_to_process.erase(desc_idx);
|
||||
Vector<int> descendants_to_process = src_skeleton->get_bone_children(src_idx);
|
||||
while (descendants_to_process.size() > 0) {
|
||||
int desc_idx = descendants_to_process[0];
|
||||
descendants_to_process.erase(desc_idx);
|
||||
Vector<int> desc_children = src_skeleton->get_bone_children(desc_idx);
|
||||
for (const int &desc_child : desc_children) {
|
||||
decendants_to_process.push_back(desc_child);
|
||||
descendants_to_process.push_back(desc_child);
|
||||
}
|
||||
|
||||
StringName desc_bone_name = is_renamed ? StringName(src_skeleton->get_bone_name(desc_idx)) : bone_map->find_profile_bone_name(src_skeleton->get_bone_name(desc_idx));
|
||||
|
|
@ -526,12 +689,14 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
if (src_parent_idx >= 0) {
|
||||
src_pg = src_skeleton->get_bone_global_rest(src_parent_idx).basis;
|
||||
}
|
||||
|
||||
int prof_idx = profile->find_bone(src_bone_name);
|
||||
if (prof_idx >= 0) {
|
||||
tgt_rot = src_pg.inverse() * prof_skeleton->get_bone_global_rest(prof_idx).basis; // Mapped bone uses reference pose.
|
||||
// Mapped bone uses reference pose.
|
||||
// It is fine to change rest here even though is_using_modifier is enabled, since next process is aborted with unmapped bones.
|
||||
tgt_rot = src_pg.inverse() * prof_skeleton->get_bone_global_rest(prof_idx).basis;
|
||||
} else if (keep_global_rest_leftovers && keep_bone_rest.has(src_idx)) {
|
||||
tgt_rot = src_pg.inverse() * old_skeleton_global_rest[src_idx].basis; // Non-Mapped bone without mapped children keeps global rest.
|
||||
// Non-Mapped bones without mapped children keeps global rest.
|
||||
tgt_rot = src_pg.inverse() * old_skeleton_global_rest[src_idx].basis;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -548,7 +713,8 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
src_skeleton->set_bone_rest(src_idx, Transform3D(tgt_rot, diff.xform(src_skeleton->get_bone_rest(src_idx).origin)));
|
||||
}
|
||||
|
||||
// Fix animation.
|
||||
// Fix animation by changing rest.
|
||||
bool warning_detected = false;
|
||||
{
|
||||
TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer");
|
||||
while (nodes.size()) {
|
||||
|
|
@ -573,7 +739,9 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
ERR_CONTINUE(!node);
|
||||
|
||||
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
|
||||
if (!track_skeleton || track_skeleton != src_skeleton) {
|
||||
if (!track_skeleton ||
|
||||
(is_using_modifier && track_skeleton != profile_skeleton && track_skeleton != orig_skeleton) ||
|
||||
(!is_using_modifier && track_skeleton != src_skeleton)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -584,6 +752,16 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
|
||||
int bone_idx = src_skeleton->find_bone(bn);
|
||||
|
||||
if (is_using_modifier) {
|
||||
int prof_idx = profile->find_bone(bn);
|
||||
if (prof_idx < 0) {
|
||||
if (keep_bone_rest.has(bone_idx)) {
|
||||
warning_detected = true;
|
||||
}
|
||||
continue; // If is_using_modifier, the original skeleton rest is not changed.
|
||||
}
|
||||
}
|
||||
|
||||
Transform3D old_rest = old_skeleton_rest[bone_idx];
|
||||
Transform3D new_rest = src_skeleton->get_bone_rest(bone_idx);
|
||||
Transform3D old_pg;
|
||||
|
|
@ -629,6 +807,13 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
}
|
||||
}
|
||||
}
|
||||
if (is_using_global_pose && warning_detected) {
|
||||
// TODO:
|
||||
// Theoretically, if A and its conversion are calculated correctly taking into account the difference in the number of bones,
|
||||
// there is no need to disable use_global_pose, but this is probably a fairly niche case.
|
||||
WARN_PRINT_ED("Animated extra bone between mapped bones detected, consider disabling Use Global Pose option to prevent that the pose origin be overridden by the RetargetModifier3D.");
|
||||
}
|
||||
|
||||
if (p_options.has("retarget/rest_fixer/reset_all_bone_poses_after_import") && !bool(p_options["retarget/rest_fixer/reset_all_bone_poses_after_import"])) {
|
||||
// If Reset All Bone Poses After Import is disabled, preserve the original bone pose, adjusted for the new bone rolls.
|
||||
for (int bone_idx = 0; bone_idx < src_skeleton->get_bone_count(); bone_idx++) {
|
||||
|
|
@ -654,6 +839,11 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
}
|
||||
}
|
||||
|
||||
if (is_using_modifier) {
|
||||
memdelete(src_skeleton);
|
||||
src_skeleton = profile_skeleton;
|
||||
}
|
||||
|
||||
is_rest_changed = true;
|
||||
}
|
||||
|
||||
|
|
@ -681,7 +871,9 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
ERR_CONTINUE(!node);
|
||||
|
||||
Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node);
|
||||
if (!track_skeleton || track_skeleton != src_skeleton) {
|
||||
if (!track_skeleton ||
|
||||
(is_using_modifier && track_skeleton != profile_skeleton && track_skeleton != orig_skeleton) ||
|
||||
(!is_using_modifier && track_skeleton != src_skeleton)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -696,7 +888,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
}
|
||||
}
|
||||
|
||||
if (is_rest_changed) {
|
||||
if (!is_using_modifier && is_rest_changed) {
|
||||
// Fix skin.
|
||||
{
|
||||
HashSet<Ref<Skin>> mutated_skins;
|
||||
|
|
@ -766,6 +958,14 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory
|
|||
src_skeleton->set_bone_pose_rotation(i, fixed_rest.basis.get_rotation_quaternion());
|
||||
src_skeleton->set_bone_pose_scale(i, fixed_rest.basis.get_scale());
|
||||
}
|
||||
if (orig_skeleton) {
|
||||
for (int i = 0; i < orig_skeleton->get_bone_count(); i++) {
|
||||
Transform3D fixed_rest = orig_skeleton->get_bone_rest(i);
|
||||
orig_skeleton->set_bone_pose_position(i, fixed_rest.origin);
|
||||
orig_skeleton->set_bone_pose_rotation(i, fixed_rest.basis.get_rotation_quaternion());
|
||||
orig_skeleton->set_bone_pose_scale(i, fixed_rest.basis.get_scale());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class PostImportPluginSkeletonRestFixer : public EditorScenePostImportPlugin {
|
|||
|
||||
public:
|
||||
virtual void get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) override;
|
||||
virtual Variant get_internal_option_visibility(InternalImportCategory p_category, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
virtual Variant get_internal_option_visibility(InternalImportCategory p_category, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
virtual void internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) override;
|
||||
|
||||
PostImportPluginSkeletonRestFixer();
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "post_import_plugin_skeleton_track_organizer.h"
|
||||
|
||||
#include "editor/import/3d/scene_import_settings.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/resources/bone_map.h"
|
||||
|
|
@ -39,7 +38,7 @@ void PostImportPluginSkeletonTrackOrganizer::get_internal_import_options(Interna
|
|||
if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) {
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/remove_tracks/except_bone_transform"), false));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/remove_tracks/unimportant_positions"), true));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/remove_tracks/unmapped_bones"), false));
|
||||
r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "retarget/remove_tracks/unmapped_bones", PROPERTY_HINT_ENUM, "None,Remove,Separate Library"), 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +51,7 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate
|
|||
}
|
||||
BoneMap *bone_map = Object::cast_to<BoneMap>(map);
|
||||
Ref<SkeletonProfile> profile = bone_map->get_profile();
|
||||
if (!profile.is_valid()) {
|
||||
if (profile.is_null()) {
|
||||
return;
|
||||
}
|
||||
Skeleton3D *src_skeleton = Object::cast_to<Skeleton3D>(p_node);
|
||||
|
|
@ -61,9 +60,9 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate
|
|||
}
|
||||
bool remove_except_bone = bool(p_options["retarget/remove_tracks/except_bone_transform"]);
|
||||
bool remove_positions = bool(p_options["retarget/remove_tracks/unimportant_positions"]);
|
||||
bool remove_unmapped_bones = bool(p_options["retarget/remove_tracks/unmapped_bones"]);
|
||||
int separate_unmapped_bones = int(p_options["retarget/remove_tracks/unmapped_bones"]);
|
||||
|
||||
if (!remove_positions && !remove_unmapped_bones) {
|
||||
if (!remove_positions && separate_unmapped_bones == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -72,10 +71,16 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate
|
|||
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back());
|
||||
List<StringName> anims;
|
||||
ap->get_animation_list(&anims);
|
||||
|
||||
Ref<AnimationLibrary> unmapped_al;
|
||||
unmapped_al.instantiate();
|
||||
|
||||
for (const StringName &name : anims) {
|
||||
Ref<Animation> anim = ap->get_animation(name);
|
||||
int track_len = anim->get_track_count();
|
||||
Vector<int> remove_indices;
|
||||
Vector<int> mapped_bone_indices;
|
||||
Vector<int> unmapped_bone_indices;
|
||||
for (int i = 0; i < track_len; i++) {
|
||||
String track_path = String(anim->track_get_path(i).get_concatenated_names());
|
||||
Node *node = (ap->get_node(ap->get_root_node()))->get_node(NodePath(track_path));
|
||||
|
|
@ -96,16 +101,19 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate
|
|||
StringName bn = anim->track_get_path(i).get_subname(0);
|
||||
if (bn) {
|
||||
int prof_idx = profile->find_bone(bone_map->find_profile_bone_name(bn));
|
||||
if (remove_unmapped_bones && prof_idx < 0) {
|
||||
remove_indices.push_back(i);
|
||||
if (prof_idx < 0) {
|
||||
unmapped_bone_indices.push_back(i);
|
||||
continue;
|
||||
}
|
||||
if (remove_positions && anim->track_get_type(i) == Animation::TYPE_POSITION_3D && prof_idx >= 0) {
|
||||
StringName prof_bn = profile->get_bone_name(prof_idx);
|
||||
if (prof_bn == profile->get_root_bone() || prof_bn == profile->get_scale_base_bone()) {
|
||||
mapped_bone_indices.push_back(i);
|
||||
continue;
|
||||
}
|
||||
remove_indices.push_back(i);
|
||||
} else {
|
||||
mapped_bone_indices.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -114,11 +122,34 @@ void PostImportPluginSkeletonTrackOrganizer::internal_process(InternalImportCate
|
|||
}
|
||||
}
|
||||
|
||||
if (separate_unmapped_bones == 2 && !unmapped_bone_indices.is_empty()) {
|
||||
Ref<Animation> unmapped_anim = anim->duplicate();
|
||||
Vector<int> to_delete;
|
||||
to_delete.append_array(mapped_bone_indices);
|
||||
to_delete.append_array(remove_indices);
|
||||
to_delete.sort();
|
||||
to_delete.reverse();
|
||||
for (int E : to_delete) {
|
||||
unmapped_anim->remove_track(E);
|
||||
}
|
||||
unmapped_al->add_animation(name, unmapped_anim);
|
||||
}
|
||||
|
||||
if (separate_unmapped_bones >= 1) {
|
||||
remove_indices.append_array(unmapped_bone_indices);
|
||||
remove_indices.sort();
|
||||
}
|
||||
remove_indices.reverse();
|
||||
for (int i = 0; i < remove_indices.size(); i++) {
|
||||
anim->remove_track(remove_indices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (unmapped_al->get_animation_list_size() == 0) {
|
||||
unmapped_al.unref();
|
||||
} else if (separate_unmapped_bones == 2) {
|
||||
ap->add_animation_library("unmapped_bones", unmapped_al);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,16 +33,11 @@
|
|||
#include "core/io/file_access.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "scene/3d/importer_mesh_instance_3d.h"
|
||||
#include "scene/3d/mesh_instance_3d.h"
|
||||
#include "scene/3d/node_3d.h"
|
||||
#include "scene/resources/3d/importer_mesh.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
#include "scene/resources/surface_tool.h"
|
||||
|
||||
uint32_t EditorOBJImporter::get_import_flags() const {
|
||||
return IMPORT_SCENE;
|
||||
}
|
||||
|
||||
static Error _parse_material_library(const String &p_path, HashMap<String, Ref<StandardMaterial3D>> &material_map, List<String> *r_missing_deps) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open MTL file '%s', it may not exist or not be readable.", p_path));
|
||||
|
|
@ -202,12 +197,12 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S
|
|||
return OK;
|
||||
}
|
||||
|
||||
static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_optimize, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, List<String> *r_missing_deps) {
|
||||
static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_generate_lods, bool p_generate_shadow_mesh, bool p_generate_lightmap_uv2, float p_generate_lightmap_uv2_texel_size, const PackedByteArray &p_src_lightmap_cache, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, Vector<Vector<uint8_t>> &r_lightmap_caches, List<String> *r_missing_deps) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path));
|
||||
|
||||
// Avoid trying to load/interpret potential build artifacts from Visual Studio (e.g. when compiling native plugins inside the project tree)
|
||||
// This should only match, if it's indeed a COFF file header
|
||||
// Avoid trying to load/interpret potential build artifacts from Visual Studio (e.g. when compiling native plugins inside the project tree).
|
||||
// This should only match if it's indeed a COFF file header.
|
||||
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
|
||||
const int first_bytes = f->get_16();
|
||||
static const Vector<int> coff_header_machines{
|
||||
|
|
@ -246,6 +241,8 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
|
|||
bool smoothing = true;
|
||||
const uint32_t no_smoothing_smooth_group = (uint32_t)-1;
|
||||
|
||||
bool uses_uvs = false;
|
||||
|
||||
while (true) {
|
||||
String l = f->get_line().strip_edges();
|
||||
while (l.length() && l[l.length() - 1] == '\\') {
|
||||
|
|
@ -320,26 +317,7 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
|
|||
idx = 1 ^ idx;
|
||||
}
|
||||
|
||||
if (face[idx].size() == 3) {
|
||||
int norm = face[idx][2].to_int() - 1;
|
||||
if (norm < 0) {
|
||||
norm += normals.size() + 1;
|
||||
}
|
||||
ERR_FAIL_INDEX_V(norm, normals.size(), ERR_FILE_CORRUPT);
|
||||
surf_tool->set_normal(normals[norm]);
|
||||
if (generate_tangents && uvs.is_empty()) {
|
||||
// We can't generate tangents without UVs, so create dummy tangents.
|
||||
Vector3 tan = Vector3(normals[norm].z, -normals[norm].x, normals[norm].y).cross(normals[norm].normalized()).normalized();
|
||||
surf_tool->set_tangent(Plane(tan.x, tan.y, tan.z, 1.0));
|
||||
}
|
||||
} else {
|
||||
// No normals, use a dummy tangent since normals and tangents will be generated.
|
||||
if (generate_tangents && uvs.is_empty()) {
|
||||
// We can't generate tangents without UVs, so create dummy tangents.
|
||||
surf_tool->set_tangent(Plane(1.0, 0.0, 0.0, 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
// Check UVs before faces as we may need to generate dummy tangents if there are no UVs.
|
||||
if (face[idx].size() >= 2 && !face[idx][1].is_empty()) {
|
||||
int uv = face[idx][1].to_int() - 1;
|
||||
if (uv < 0) {
|
||||
|
|
@ -347,6 +325,27 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
|
|||
}
|
||||
ERR_FAIL_INDEX_V(uv, uvs.size(), ERR_FILE_CORRUPT);
|
||||
surf_tool->set_uv(uvs[uv]);
|
||||
uses_uvs = true;
|
||||
}
|
||||
|
||||
if (face[idx].size() == 3) {
|
||||
int norm = face[idx][2].to_int() - 1;
|
||||
if (norm < 0) {
|
||||
norm += normals.size() + 1;
|
||||
}
|
||||
ERR_FAIL_INDEX_V(norm, normals.size(), ERR_FILE_CORRUPT);
|
||||
surf_tool->set_normal(normals[norm]);
|
||||
if (generate_tangents && !uses_uvs) {
|
||||
// We can't generate tangents without UVs, so create dummy tangents.
|
||||
Vector3 tan = Vector3(normals[norm].z, -normals[norm].x, normals[norm].y).cross(normals[norm].normalized()).normalized();
|
||||
surf_tool->set_tangent(Plane(tan.x, tan.y, tan.z, 1.0));
|
||||
}
|
||||
} else {
|
||||
// No normals, use a dummy tangent since normals and tangents will be generated.
|
||||
if (generate_tangents && !uses_uvs) {
|
||||
// We can't generate tangents without UVs, so create dummy tangents.
|
||||
surf_tool->set_tangent(Plane(1.0, 0.0, 0.0, 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
int vtx = face[idx][0].to_int() - 1;
|
||||
|
|
@ -407,7 +406,7 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
|
|||
surf_tool->generate_normals();
|
||||
}
|
||||
|
||||
if (generate_tangents && uvs.size()) {
|
||||
if (generate_tangents && uses_uvs) {
|
||||
surf_tool->generate_tangents();
|
||||
}
|
||||
|
||||
|
|
@ -426,10 +425,11 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
|
|||
|
||||
Array array = surf_tool->commit_to_arrays();
|
||||
|
||||
if (mesh_flags & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES && generate_tangents) {
|
||||
// Compression is enabled, so let's validate that the normals and tangents are correct.
|
||||
if (mesh_flags & RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES && generate_tangents && uses_uvs) {
|
||||
// Compression is enabled, so let's validate that the normals and generated tangents are correct.
|
||||
Vector<Vector3> norms = array[Mesh::ARRAY_NORMAL];
|
||||
Vector<float> tangents = array[Mesh::ARRAY_TANGENT];
|
||||
ERR_FAIL_COND_V(tangents.is_empty(), ERR_FILE_CORRUPT);
|
||||
for (int vert = 0; vert < norms.size(); vert++) {
|
||||
Vector3 tan = Vector3(tangents[vert * 4 + 0], tangents[vert * 4 + 1], tangents[vert * 4 + 2]);
|
||||
if (abs(tan.dot(norms[vert])) > 0.0001) {
|
||||
|
|
@ -440,6 +440,7 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
|
|||
}
|
||||
|
||||
mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, array, TypedArray<Array>(), Dictionary(), material, name, mesh_flags);
|
||||
|
||||
print_verbose("OBJ: Added surface :" + mesh->get_surface_name(mesh->get_surface_count() - 1));
|
||||
|
||||
if (!current_material.is_empty()) {
|
||||
|
|
@ -454,6 +455,7 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
|
|||
|
||||
surf_tool->clear();
|
||||
surf_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
|
||||
uses_uvs = false;
|
||||
}
|
||||
|
||||
if (l.begins_with("o ") || f->eof_reached()) {
|
||||
|
|
@ -502,6 +504,43 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
|
|||
}
|
||||
}
|
||||
|
||||
if (p_generate_lightmap_uv2) {
|
||||
Vector<uint8_t> lightmap_cache;
|
||||
mesh->lightmap_unwrap_cached(Transform3D(), p_generate_lightmap_uv2_texel_size, p_src_lightmap_cache, lightmap_cache);
|
||||
|
||||
if (!lightmap_cache.is_empty()) {
|
||||
if (r_lightmap_caches.is_empty()) {
|
||||
r_lightmap_caches.push_back(lightmap_cache);
|
||||
} else {
|
||||
// MD5 is stored at the beginning of the cache data.
|
||||
const String new_md5 = String::md5(lightmap_cache.ptr());
|
||||
|
||||
for (int i = 0; i < r_lightmap_caches.size(); i++) {
|
||||
const String md5 = String::md5(r_lightmap_caches[i].ptr());
|
||||
if (new_md5 < md5) {
|
||||
r_lightmap_caches.insert(i, lightmap_cache);
|
||||
break;
|
||||
}
|
||||
|
||||
if (new_md5 == md5) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p_generate_lods) {
|
||||
// Use normal merge/split angles that match the defaults used for 3D scene importing.
|
||||
mesh->generate_lods(60.0f, {});
|
||||
}
|
||||
|
||||
if (p_generate_shadow_mesh) {
|
||||
mesh->create_shadow_mesh();
|
||||
}
|
||||
|
||||
mesh->optimize_indices();
|
||||
|
||||
if (p_single_mesh && mesh->get_surface_count() > 0) {
|
||||
r_meshes.push_back(mesh);
|
||||
}
|
||||
|
|
@ -512,7 +551,10 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
|
|||
Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {
|
||||
List<Ref<ImporterMesh>> meshes;
|
||||
|
||||
Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, r_missing_deps);
|
||||
// LOD, shadow mesh and lightmap UV2 generation are handled by ResourceImporterScene in this case,
|
||||
// so disable it within the OBJ mesh import.
|
||||
Vector<Vector<uint8_t>> mesh_lightmap_caches;
|
||||
Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, false, false, 0.2, PackedByteArray(), Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, mesh_lightmap_caches, r_missing_deps);
|
||||
|
||||
if (err != OK) {
|
||||
if (r_err) {
|
||||
|
|
@ -581,20 +623,51 @@ String ResourceImporterOBJ::get_preset_name(int p_idx) const {
|
|||
|
||||
void ResourceImporterOBJ::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_tangents"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_lods"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_shadow_mesh"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_lightmap_uv2", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "generate_lightmap_uv2_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "scale_mesh"), Vector3(1, 1, 1)));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "offset_mesh"), Vector3(0, 0, 0)));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimize_mesh"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_disable_mesh_compression"), false));
|
||||
}
|
||||
|
||||
bool ResourceImporterOBJ::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
if (p_option == "generate_lightmap_uv2_texel_size" && !p_options["generate_lightmap_uv2"]) {
|
||||
// Only display the lightmap texel size import option when lightmap UV2 generation is enabled.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Error ResourceImporterOBJ::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Error ResourceImporterOBJ::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
List<Ref<ImporterMesh>> meshes;
|
||||
|
||||
Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["optimize_mesh"], p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], nullptr);
|
||||
Vector<uint8_t> src_lightmap_cache;
|
||||
Vector<Vector<uint8_t>> mesh_lightmap_caches;
|
||||
|
||||
Error err;
|
||||
{
|
||||
src_lightmap_cache = FileAccess::get_file_as_bytes(p_source_file + ".unwrap_cache", &err);
|
||||
if (err != OK) {
|
||||
src_lightmap_cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["generate_lods"], p_options["generate_shadow_mesh"], p_options["generate_lightmap_uv2"], p_options["generate_lightmap_uv2_texel_size"], src_lightmap_cache, p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], mesh_lightmap_caches, nullptr);
|
||||
|
||||
if (mesh_lightmap_caches.size()) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE);
|
||||
if (f.is_valid()) {
|
||||
f->store_32(mesh_lightmap_caches.size());
|
||||
for (int i = 0; i < mesh_lightmap_caches.size(); i++) {
|
||||
String md5 = String::md5(mesh_lightmap_caches[i].ptr());
|
||||
f->store_buffer(mesh_lightmap_caches[i].ptr(), mesh_lightmap_caches[i].size());
|
||||
}
|
||||
}
|
||||
}
|
||||
err = OK;
|
||||
|
||||
ERR_FAIL_COND_V(err != OK, err);
|
||||
ERR_FAIL_COND_V(meshes.size() != 1, ERR_BUG);
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ class EditorOBJImporter : public EditorSceneFormatImporter {
|
|||
GDCLASS(EditorOBJImporter, EditorSceneFormatImporter);
|
||||
|
||||
public:
|
||||
virtual uint32_t get_import_flags() const override;
|
||||
virtual void get_extensions(List<String> *r_extensions) const override;
|
||||
virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err = nullptr) override;
|
||||
|
||||
|
|
@ -61,10 +60,7 @@ public:
|
|||
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
|
||||
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
// Threaded import can currently cause deadlocks, see GH-48265.
|
||||
virtual bool can_import_threaded() const override { return false; }
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
ResourceImporterOBJ();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "core/io/dir_access.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "editor/editor_interface.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/import/3d/scene_import_settings.h"
|
||||
|
|
@ -43,7 +44,6 @@
|
|||
#include "scene/3d/occluder_instance_3d.h"
|
||||
#include "scene/3d/physics/area_3d.h"
|
||||
#include "scene/3d/physics/collision_shape_3d.h"
|
||||
#include "scene/3d/physics/physics_body_3d.h"
|
||||
#include "scene/3d/physics/static_body_3d.h"
|
||||
#include "scene/3d/physics/vehicle_body_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
|
|
@ -56,16 +56,6 @@
|
|||
#include "scene/resources/bone_map.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
#include "scene/resources/resource_format_text.h"
|
||||
#include "scene/resources/surface_tool.h"
|
||||
|
||||
uint32_t EditorSceneFormatImporter::get_import_flags() const {
|
||||
uint32_t ret;
|
||||
if (GDVIRTUAL_CALL(_get_import_flags, ret)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ERR_FAIL_V(0);
|
||||
}
|
||||
|
||||
void EditorSceneFormatImporter::get_extensions(List<String> *r_extensions) const {
|
||||
Vector<String> arr;
|
||||
|
|
@ -92,18 +82,33 @@ Node *EditorSceneFormatImporter::import_scene(const String &p_path, uint32_t p_f
|
|||
ERR_FAIL_V(nullptr);
|
||||
}
|
||||
|
||||
void EditorSceneFormatImporter::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) {
|
||||
GDVIRTUAL_CALL(_get_import_options, p_path);
|
||||
void EditorSceneFormatImporter::add_import_option(const String &p_name, const Variant &p_default_value) {
|
||||
ERR_FAIL_NULL_MSG(current_option_list, "add_import_option() can only be called from get_import_options().");
|
||||
add_import_option_advanced(p_default_value.get_type(), p_name, p_default_value);
|
||||
}
|
||||
|
||||
Variant EditorSceneFormatImporter::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) {
|
||||
void EditorSceneFormatImporter::add_import_option_advanced(Variant::Type p_type, const String &p_name, const Variant &p_default_value, PropertyHint p_hint, const String &p_hint_string, int p_usage_flags) {
|
||||
ERR_FAIL_NULL_MSG(current_option_list, "add_import_option_advanced() can only be called from get_import_options().");
|
||||
current_option_list->push_back(ResourceImporter::ImportOption(PropertyInfo(p_type, p_name, p_hint, p_hint_string, p_usage_flags), p_default_value));
|
||||
}
|
||||
|
||||
void EditorSceneFormatImporter::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options) {
|
||||
current_option_list = r_options;
|
||||
GDVIRTUAL_CALL(_get_import_options, p_path);
|
||||
current_option_list = nullptr;
|
||||
}
|
||||
|
||||
Variant EditorSceneFormatImporter::get_option_visibility(const String &p_path, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options) {
|
||||
Variant ret;
|
||||
GDVIRTUAL_CALL(_get_option_visibility, p_path, p_for_animation, p_option, ret);
|
||||
// For compatibility with the old API, pass the import type as a boolean.
|
||||
GDVIRTUAL_CALL(_get_option_visibility, p_path, p_scene_import_type == "AnimationLibrary", p_option, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EditorSceneFormatImporter::_bind_methods() {
|
||||
GDVIRTUAL_BIND(_get_import_flags);
|
||||
ClassDB::bind_method(D_METHOD("add_import_option", "name", "value"), &EditorSceneFormatImporter::add_import_option);
|
||||
ClassDB::bind_method(D_METHOD("add_import_option_advanced", "type", "name", "default_value", "hint", "hint_string", "usage_flags"), &EditorSceneFormatImporter::add_import_option_advanced, DEFVAL(PROPERTY_HINT_NONE), DEFVAL(""), DEFVAL(PROPERTY_USAGE_DEFAULT));
|
||||
|
||||
GDVIRTUAL_BIND(_get_extensions);
|
||||
GDVIRTUAL_BIND(_import_scene, "path", "flags", "options");
|
||||
GDVIRTUAL_BIND(_get_import_options, "path");
|
||||
|
|
@ -172,13 +177,16 @@ void EditorScenePostImportPlugin::get_internal_import_options(InternalImportCate
|
|||
GDVIRTUAL_CALL(_get_internal_import_options, p_category);
|
||||
current_option_list = nullptr;
|
||||
}
|
||||
Variant EditorScenePostImportPlugin::get_internal_option_visibility(InternalImportCategory p_category, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
|
||||
Variant EditorScenePostImportPlugin::get_internal_option_visibility(InternalImportCategory p_category, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
current_options = &p_options;
|
||||
Variant ret;
|
||||
GDVIRTUAL_CALL(_get_internal_option_visibility, p_category, p_for_animation, p_option, ret);
|
||||
// For compatibility with the old API, pass the import type as a boolean.
|
||||
GDVIRTUAL_CALL(_get_internal_option_visibility, p_category, p_scene_import_type == "AnimationLibrary", p_option, ret);
|
||||
current_options = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Variant EditorScenePostImportPlugin::get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
current_options = &p_options;
|
||||
Variant ret;
|
||||
|
|
@ -198,10 +206,10 @@ void EditorScenePostImportPlugin::get_import_options(const String &p_path, List<
|
|||
GDVIRTUAL_CALL(_get_import_options, p_path);
|
||||
current_option_list = nullptr;
|
||||
}
|
||||
Variant EditorScenePostImportPlugin::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
Variant EditorScenePostImportPlugin::get_option_visibility(const String &p_path, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
current_options = &p_options;
|
||||
Variant ret;
|
||||
GDVIRTUAL_CALL(_get_option_visibility, p_path, p_for_animation, p_option, ret);
|
||||
GDVIRTUAL_CALL(_get_option_visibility, p_path, p_scene_import_type == "AnimationLibrary", p_option, ret);
|
||||
current_options = nullptr;
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -245,11 +253,22 @@ void EditorScenePostImportPlugin::_bind_methods() {
|
|||
/////////////////////////////////////////////////////////
|
||||
|
||||
String ResourceImporterScene::get_importer_name() const {
|
||||
return animation_importer ? "animation_library" : "scene";
|
||||
// For compatibility with 4.2 and earlier we need to keep the "scene" and "animation_library" names.
|
||||
// However this is arbitrary so for new import types we can use any string.
|
||||
if (_scene_import_type == "PackedScene") {
|
||||
return "scene";
|
||||
} else if (_scene_import_type == "AnimationLibrary") {
|
||||
return "animation_library";
|
||||
}
|
||||
return _scene_import_type;
|
||||
}
|
||||
|
||||
String ResourceImporterScene::get_visible_name() const {
|
||||
return animation_importer ? "Animation Library" : "Scene";
|
||||
// This is displayed on the UI. Friendly names here are nice but not vital, so fall back to the type.
|
||||
if (_scene_import_type == "PackedScene") {
|
||||
return "Scene";
|
||||
}
|
||||
return _scene_import_type.capitalize();
|
||||
}
|
||||
|
||||
void ResourceImporterScene::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
|
|
@ -257,11 +276,14 @@ void ResourceImporterScene::get_recognized_extensions(List<String> *p_extensions
|
|||
}
|
||||
|
||||
String ResourceImporterScene::get_save_extension() const {
|
||||
return animation_importer ? "res" : "scn";
|
||||
if (_scene_import_type == "PackedScene") {
|
||||
return "scn";
|
||||
}
|
||||
return "res";
|
||||
}
|
||||
|
||||
String ResourceImporterScene::get_resource_type() const {
|
||||
return animation_importer ? "AnimationLibrary" : "PackedScene";
|
||||
return _scene_import_type;
|
||||
}
|
||||
|
||||
int ResourceImporterScene::get_format_version() const {
|
||||
|
|
@ -269,27 +291,28 @@ int ResourceImporterScene::get_format_version() const {
|
|||
}
|
||||
|
||||
bool ResourceImporterScene::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
|
||||
if (animation_importer) {
|
||||
if (_scene_import_type == "PackedScene") {
|
||||
if (p_option.begins_with("animation/")) {
|
||||
if (p_option != "animation/import" && !bool(p_options["animation/import"])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (_scene_import_type == "AnimationLibrary") {
|
||||
if (p_option == "animation/import") { // Option ignored, animation always imported.
|
||||
return false;
|
||||
}
|
||||
} else if (p_option.begins_with("animation/")) {
|
||||
if (p_option != "animation/import" && !bool(p_options["animation/import"])) {
|
||||
return false;
|
||||
if (p_option == "nodes/root_type" || p_option == "nodes/root_name" || p_option.begins_with("meshes/") || p_option.begins_with("skins/")) {
|
||||
return false; // Nothing to do here for animations.
|
||||
}
|
||||
}
|
||||
|
||||
if (animation_importer && (p_option == "nodes/root_type" || p_option == "nodes/root_name" || p_option.begins_with("meshes/") || p_option.begins_with("skins/"))) {
|
||||
return false; // Nothing to do here for animations.
|
||||
}
|
||||
|
||||
if (p_option == "meshes/lightmap_texel_size" && int(p_options["meshes/light_baking"]) != 2) {
|
||||
// Only display the lightmap texel size import option when using the Static Lightmaps light baking mode.
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < post_importer_plugins.size(); i++) {
|
||||
Variant ret = post_importer_plugins.write[i]->get_option_visibility(p_path, animation_importer, p_option, p_options);
|
||||
Variant ret = post_importer_plugins.write[i]->get_option_visibility(p_path, _scene_import_type, p_option, p_options);
|
||||
if (ret.get_type() == Variant::BOOL) {
|
||||
if (!ret) {
|
||||
return false;
|
||||
|
|
@ -298,7 +321,7 @@ bool ResourceImporterScene::get_option_visibility(const String &p_path, const St
|
|||
}
|
||||
|
||||
for (Ref<EditorSceneFormatImporter> importer : scene_importers) {
|
||||
Variant ret = importer->get_option_visibility(p_path, animation_importer, p_option, p_options);
|
||||
Variant ret = importer->get_option_visibility(p_path, _scene_import_type, p_option, p_options);
|
||||
if (ret.get_type() == Variant::BOOL) {
|
||||
if (!ret) {
|
||||
return false;
|
||||
|
|
@ -427,7 +450,7 @@ static String _fixstr(const String &p_what, const String &p_str) {
|
|||
}
|
||||
|
||||
static void _pre_gen_shape_list(Ref<ImporterMesh> &mesh, Vector<Ref<Shape3D>> &r_shape_list, bool p_convex) {
|
||||
ERR_FAIL_NULL_MSG(mesh, "Cannot generate shape list with null mesh value.");
|
||||
ERR_FAIL_COND_MSG(mesh.is_null(), "Cannot generate shape list with null mesh value.");
|
||||
if (!p_convex) {
|
||||
Ref<ConcavePolygonShape3D> shape = mesh->create_trimesh_shape();
|
||||
r_shape_list.push_back(shape);
|
||||
|
|
@ -618,10 +641,10 @@ void _apply_permanent_scale_to_descendants(Node *p_root_node, Vector3 p_scale) {
|
|||
_apply_scale_to_scalable_node_collection(scalable_node_collection, p_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) {
|
||||
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, const HashMap<StringName, Variant> &p_options) {
|
||||
// Children first.
|
||||
for (int i = 0; i < p_node->get_child_count(); i++) {
|
||||
Node *r = _pre_fix_node(p_node->get_child(i), p_root, r_collision_map, r_occluder_arrays, r_node_renames);
|
||||
Node *r = _pre_fix_node(p_node->get_child(i), p_root, r_collision_map, r_occluder_arrays, r_node_renames, p_options);
|
||||
if (!r) {
|
||||
i--; // Was erased.
|
||||
}
|
||||
|
|
@ -630,6 +653,9 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R
|
|||
String name = p_node->get_name();
|
||||
NodePath original_path = p_root->get_path_to(p_node); // Used to detect renames due to import hints.
|
||||
|
||||
Ref<Resource> original_meta = memnew(Resource); // Create temp resource to hold original meta
|
||||
original_meta->merge_meta_from(p_node);
|
||||
|
||||
bool isroot = p_node == p_root;
|
||||
|
||||
if (!isroot && _teststr(name, "noimp")) {
|
||||
|
|
@ -646,7 +672,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R
|
|||
if (m.is_valid()) {
|
||||
for (int i = 0; i < m->get_surface_count(); i++) {
|
||||
Ref<BaseMaterial3D> mat = m->get_surface_material(i);
|
||||
if (!mat.is_valid()) {
|
||||
if (mat.is_null()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -728,6 +754,14 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R
|
|||
}
|
||||
}
|
||||
|
||||
bool use_node_type_suffixes = true;
|
||||
if (p_options.has("nodes/use_node_type_suffixes")) {
|
||||
use_node_type_suffixes = p_options["nodes/use_node_type_suffixes"];
|
||||
}
|
||||
if (!use_node_type_suffixes) {
|
||||
return p_node;
|
||||
}
|
||||
|
||||
if (_teststr(name, "colonly") || _teststr(name, "convcolonly")) {
|
||||
if (isroot) {
|
||||
return p_node;
|
||||
|
|
@ -968,7 +1002,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R
|
|||
ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node);
|
||||
|
||||
Ref<ImporterMesh> mesh = mi->get_mesh();
|
||||
if (!mesh.is_null()) {
|
||||
if (mesh.is_valid()) {
|
||||
Vector<Ref<Shape3D>> shapes;
|
||||
if (r_collision_map.has(mesh)) {
|
||||
shapes = r_collision_map[mesh];
|
||||
|
|
@ -1003,6 +1037,8 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R
|
|||
print_verbose(vformat("Fix: Renamed %s to %s", original_path, new_path));
|
||||
r_node_renames.push_back({ original_path, p_node });
|
||||
}
|
||||
// If we created new node instead, merge meta values from the original node.
|
||||
p_node->merge_meta_from(*original_meta);
|
||||
}
|
||||
|
||||
return p_node;
|
||||
|
|
@ -1552,7 +1588,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
|
|||
col->set_transform(get_collision_shapes_transform(node_settings));
|
||||
col->set_position(p_applied_root_scale * col->get_position());
|
||||
const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];
|
||||
if (!pmo.is_null()) {
|
||||
if (pmo.is_valid()) {
|
||||
col->set_physics_material_override(pmo);
|
||||
}
|
||||
base = col;
|
||||
|
|
@ -1569,7 +1605,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
|
|||
rigid_body->add_child(mi, true);
|
||||
mi->set_owner(rigid_body->get_owner());
|
||||
const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];
|
||||
if (!pmo.is_null()) {
|
||||
if (pmo.is_valid()) {
|
||||
rigid_body->set_physics_material_override(pmo);
|
||||
}
|
||||
base = rigid_body;
|
||||
|
|
@ -1585,7 +1621,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<
|
|||
memdelete(p_node);
|
||||
p_node = col;
|
||||
const Ref<PhysicsMaterial> &pmo = node_settings["physics/physics_material_override"];
|
||||
if (!pmo.is_null()) {
|
||||
if (pmo.is_valid()) {
|
||||
col->set_physics_material_override(pmo);
|
||||
}
|
||||
base = col;
|
||||
|
|
@ -1981,29 +2017,29 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
|
|||
decomposition_default.instantiate();
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/advanced", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/precision", PROPERTY_HINT_RANGE, "1,10,1"), 5));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/max_concavity", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_max_concavity()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/symmetry_planes_clipping_bias", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_symmetry_planes_clipping_bias()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/revolution_axes_clipping_bias", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_revolution_axes_clipping_bias()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/min_volume_per_convex_hull", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_min_volume_per_convex_hull()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/resolution", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_resolution()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_num_vertices_per_convex_hull", PROPERTY_HINT_RANGE, "5,512,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_max_num_vertices_per_convex_hull()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/plane_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_plane_downsampling()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/convexhull_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_convex_hull_downsampling()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/normalize_mesh", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_normalize_mesh()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/mode", PROPERTY_HINT_ENUM, "Voxel,Tetrahedron", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), static_cast<int>(decomposition_default->get_mode())));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/convexhull_approximation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_convex_hull_approximation()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_convex_hulls", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_max_convex_hulls()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/project_hull_vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default->get_project_hull_vertices()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/max_concavity", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT), decomposition_default->get_max_concavity()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/symmetry_planes_clipping_bias", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT), decomposition_default->get_symmetry_planes_clipping_bias()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/revolution_axes_clipping_bias", PROPERTY_HINT_RANGE, "0.0,1.0,0.001", PROPERTY_USAGE_DEFAULT), decomposition_default->get_revolution_axes_clipping_bias()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "decomposition/min_volume_per_convex_hull", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_min_volume_per_convex_hull()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/resolution", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_resolution()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_num_vertices_per_convex_hull", PROPERTY_HINT_RANGE, "5,512,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_max_num_vertices_per_convex_hull()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/plane_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_plane_downsampling()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/convexhull_downsampling", PROPERTY_HINT_RANGE, "1,16,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_convex_hull_downsampling()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/normalize_mesh", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_normalize_mesh()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/mode", PROPERTY_HINT_ENUM, "Voxel,Tetrahedron", PROPERTY_USAGE_DEFAULT), static_cast<int>(decomposition_default->get_mode())));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/convexhull_approximation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_convex_hull_approximation()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_convex_hulls", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT), decomposition_default->get_max_convex_hulls()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "decomposition/project_hull_vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), decomposition_default->get_project_hull_vertices()));
|
||||
|
||||
// Primitives: Box, Sphere, Cylinder, Capsule.
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3(2.0, 2.0, 2.0)));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/height", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1.0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/radius", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 1.0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Vector3()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3(2.0, 2.0, 2.0)));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/height", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), 1.0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "primitive/radius", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), 1.0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3()));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "primitive/rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT), Vector3()));
|
||||
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/occluder", PROPERTY_HINT_ENUM, "Disabled,Mesh + Occluder,Occluder Only", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "occluder/simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0.1f));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "occluder/simplification_distance", PROPERTY_HINT_RANGE, "0.0,2.0,0.01", PROPERTY_USAGE_DEFAULT), 0.1f));
|
||||
} break;
|
||||
case INTERNAL_IMPORT_CATEGORY_MESH: {
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
|
||||
|
|
@ -2011,7 +2047,6 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/shadow_meshes", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lightmap_uv", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lods", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_split_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), 25.0f));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_merge_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), 60.0f));
|
||||
} break;
|
||||
case INTERNAL_IMPORT_CATEGORY_MATERIAL: {
|
||||
|
|
@ -2281,8 +2316,9 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: If there are more than 2 or equal get_internal_option_visibility method, visibility state is broken.
|
||||
for (int i = 0; i < post_importer_plugins.size(); i++) {
|
||||
Variant ret = post_importer_plugins.write[i]->get_internal_option_visibility(EditorScenePostImportPlugin::InternalImportCategory(p_category), animation_importer, p_option, p_options);
|
||||
Variant ret = post_importer_plugins.write[i]->get_internal_option_visibility(EditorScenePostImportPlugin::InternalImportCategory(p_category), _scene_import_type, p_option, p_options);
|
||||
if (ret.get_type() == Variant::BOOL) {
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -2332,12 +2368,12 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_type", PROPERTY_HINT_TYPE_STRING, "Node"), ""));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_name"), ""));
|
||||
|
||||
List<String> script_extentions;
|
||||
ResourceLoader::get_recognized_extensions_for_type("Script", &script_extentions);
|
||||
List<String> script_extensions;
|
||||
ResourceLoader::get_recognized_extensions_for_type("Script", &script_extensions);
|
||||
|
||||
String script_ext_hint;
|
||||
|
||||
for (const String &E : script_extentions) {
|
||||
for (const String &E : script_extensions) {
|
||||
if (!script_ext_hint.is_empty()) {
|
||||
script_ext_hint += ",";
|
||||
}
|
||||
|
|
@ -2348,6 +2384,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
|
|||
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, "nodes/import_as_skeleton_bones"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "nodes/use_node_type_suffixes"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true));
|
||||
|
|
@ -2432,13 +2469,14 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
|
|||
mesh_node->set_transform(src_mesh_node->get_transform());
|
||||
mesh_node->set_skin(src_mesh_node->get_skin());
|
||||
mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path());
|
||||
mesh_node->merge_meta_from(src_mesh_node);
|
||||
|
||||
if (src_mesh_node->get_mesh().is_valid()) {
|
||||
Ref<ArrayMesh> mesh;
|
||||
if (!src_mesh_node->get_mesh()->has_mesh()) {
|
||||
//do mesh processing
|
||||
|
||||
bool generate_lods = p_generate_lods;
|
||||
float split_angle = 25.0f;
|
||||
float merge_angle = 60.0f;
|
||||
bool create_shadow_meshes = p_create_shadow_meshes;
|
||||
bool bake_lightmaps = p_light_bake_mode == LIGHT_BAKE_STATIC_LIGHTMAPS;
|
||||
|
|
@ -2486,10 +2524,6 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
|
|||
}
|
||||
}
|
||||
|
||||
if (mesh_settings.has("lods/normal_split_angle")) {
|
||||
split_angle = mesh_settings["lods/normal_split_angle"];
|
||||
}
|
||||
|
||||
if (mesh_settings.has("lods/normal_merge_angle")) {
|
||||
merge_angle = mesh_settings["lods/normal_merge_angle"];
|
||||
}
|
||||
|
|
@ -2540,13 +2574,15 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
|
|||
|
||||
if (generate_lods) {
|
||||
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);
|
||||
src_mesh_node->get_mesh()->generate_lods(merge_angle, skin_pose_transform_array);
|
||||
}
|
||||
|
||||
if (create_shadow_meshes) {
|
||||
src_mesh_node->get_mesh()->create_shadow_mesh();
|
||||
}
|
||||
|
||||
src_mesh_node->get_mesh()->optimize_indices();
|
||||
|
||||
if (!save_to_file.is_empty()) {
|
||||
Ref<Mesh> existing = ResourceCache::get_ref(save_to_file);
|
||||
if (existing.is_valid()) {
|
||||
|
|
@ -2572,6 +2608,7 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
|
|||
for (int i = 0; i < mesh->get_surface_count(); i++) {
|
||||
mesh_node->set_surface_override_material(i, src_mesh_node->get_surface_material(i));
|
||||
}
|
||||
mesh->merge_meta_from(*src_mesh_node->get_mesh());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2590,6 +2627,7 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
|
|||
|
||||
mesh_node->set_layer_mask(src_mesh_node->get_layer_mask());
|
||||
mesh_node->set_cast_shadows_setting(src_mesh_node->get_cast_shadows_setting());
|
||||
mesh_node->set_visible(src_mesh_node->is_visible());
|
||||
mesh_node->set_visibility_range_begin(src_mesh_node->get_visibility_range_begin());
|
||||
mesh_node->set_visibility_range_begin_margin(src_mesh_node->get_visibility_range_begin_margin());
|
||||
mesh_node->set_visibility_range_end(src_mesh_node->get_visibility_range_end());
|
||||
|
|
@ -2781,6 +2819,15 @@ void ResourceImporterScene::_optimize_track_usage(AnimationPlayer *p_player, Ani
|
|||
}
|
||||
}
|
||||
|
||||
void ResourceImporterScene::_generate_editor_preview_for_scene(const String &p_path, Node *p_scene) {
|
||||
if (!Engine::get_singleton()->is_editor_hint()) {
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_COND_MSG(p_path.is_empty(), "Path is empty, cannot generate preview.");
|
||||
ERR_FAIL_NULL_MSG(p_scene, "Scene is null, cannot generate preview.");
|
||||
EditorInterface::get_singleton()->make_scene_preview(p_path, p_scene, 1024);
|
||||
}
|
||||
|
||||
Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashMap<StringName, Variant> &p_options) {
|
||||
Ref<EditorSceneFormatImporter> importer;
|
||||
String ext = p_source_file.get_extension().to_lower();
|
||||
|
|
@ -2805,7 +2852,7 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashM
|
|||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(!importer.is_valid(), nullptr);
|
||||
ERR_FAIL_COND_V(importer.is_null(), nullptr);
|
||||
ERR_FAIL_COND_V(p_options.is_empty(), nullptr);
|
||||
|
||||
Error err = OK;
|
||||
|
|
@ -2819,7 +2866,7 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashM
|
|||
|
||||
HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
|
||||
List<Pair<NodePath, Node *>> node_renames;
|
||||
_pre_fix_node(scene, scene, collision_map, nullptr, node_renames);
|
||||
_pre_fix_node(scene, scene, collision_map, nullptr, node_renames, p_options);
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
|
@ -2830,8 +2877,7 @@ Error ResourceImporterScene::_check_resource_save_paths(const Dictionary &p_data
|
|||
const Dictionary &settings = p_data[keys[i]];
|
||||
|
||||
if (bool(settings.get("save_to_file/enabled", false)) && settings.has("save_to_file/path")) {
|
||||
const String &save_path = settings["save_to_file/path"];
|
||||
|
||||
const String save_path = ResourceUID::ensure_path(settings["save_to_file/path"]);
|
||||
ERR_FAIL_COND_V(!save_path.is_empty() && !DirAccess::exists(save_path.get_base_dir()), ERR_FILE_BAD_PATH);
|
||||
}
|
||||
}
|
||||
|
|
@ -2839,7 +2885,7 @@ Error ResourceImporterScene::_check_resource_save_paths(const Dictionary &p_data
|
|||
return OK;
|
||||
}
|
||||
|
||||
Error ResourceImporterScene::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Error ResourceImporterScene::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
const String &src_path = p_source_file;
|
||||
|
||||
Ref<EditorSceneFormatImporter> importer;
|
||||
|
|
@ -2864,18 +2910,16 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
|
|||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(!importer.is_valid(), ERR_FILE_UNRECOGNIZED);
|
||||
ERR_FAIL_COND_V(importer.is_null(), ERR_FILE_UNRECOGNIZED);
|
||||
ERR_FAIL_COND_V(p_options.is_empty(), ERR_BUG);
|
||||
|
||||
int import_flags = 0;
|
||||
|
||||
if (animation_importer) {
|
||||
if (_scene_import_type == "AnimationLibrary") {
|
||||
import_flags |= EditorSceneFormatImporter::IMPORT_ANIMATION;
|
||||
import_flags |= EditorSceneFormatImporter::IMPORT_DISCARD_MESHES_AND_MATERIALS;
|
||||
} else {
|
||||
if (bool(p_options["animation/import"])) {
|
||||
import_flags |= EditorSceneFormatImporter::IMPORT_ANIMATION;
|
||||
}
|
||||
} else if (bool(p_options["animation/import"])) {
|
||||
import_flags |= EditorSceneFormatImporter::IMPORT_ANIMATION;
|
||||
}
|
||||
|
||||
if (bool(p_options["skins/use_named_skins"])) {
|
||||
|
|
@ -2894,38 +2938,22 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
|
|||
|
||||
Dictionary subresources = p_options["_subresources"];
|
||||
|
||||
Dictionary node_data;
|
||||
if (subresources.has("nodes")) {
|
||||
node_data = subresources["nodes"];
|
||||
}
|
||||
|
||||
Dictionary material_data;
|
||||
if (subresources.has("materials")) {
|
||||
material_data = subresources["materials"];
|
||||
}
|
||||
|
||||
Dictionary animation_data;
|
||||
if (subresources.has("animations")) {
|
||||
animation_data = subresources["animations"];
|
||||
}
|
||||
|
||||
Dictionary mesh_data;
|
||||
if (subresources.has("meshes")) {
|
||||
mesh_data = subresources["meshes"];
|
||||
}
|
||||
|
||||
Error err = OK;
|
||||
|
||||
// Check whether any of the meshes or animations have nonexistent save paths
|
||||
// and if they do, fail the import immediately.
|
||||
err = _check_resource_save_paths(mesh_data);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
if (subresources.has("meshes")) {
|
||||
err = _check_resource_save_paths(subresources["meshes"]);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = _check_resource_save_paths(animation_data);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
if (subresources.has("animations")) {
|
||||
err = _check_resource_save_paths(subresources["animations"]);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
List<String> missing_deps; // for now, not much will be done with this
|
||||
|
|
@ -2959,12 +2987,33 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
|
|||
Pair<PackedVector3Array, PackedInt32Array> occluder_arrays;
|
||||
List<Pair<NodePath, Node *>> node_renames;
|
||||
|
||||
_pre_fix_node(scene, scene, collision_map, &occluder_arrays, node_renames);
|
||||
_pre_fix_node(scene, scene, collision_map, &occluder_arrays, node_renames, p_options);
|
||||
|
||||
for (int i = 0; i < post_importer_plugins.size(); i++) {
|
||||
post_importer_plugins.write[i]->pre_process(scene, p_options);
|
||||
}
|
||||
|
||||
// data in _subresources may be modified by pre_process(), so wait until now to check.
|
||||
Dictionary node_data;
|
||||
if (subresources.has("nodes")) {
|
||||
node_data = subresources["nodes"];
|
||||
}
|
||||
|
||||
Dictionary material_data;
|
||||
if (subresources.has("materials")) {
|
||||
material_data = subresources["materials"];
|
||||
}
|
||||
|
||||
Dictionary animation_data;
|
||||
if (subresources.has("animations")) {
|
||||
animation_data = subresources["animations"];
|
||||
}
|
||||
|
||||
Dictionary mesh_data;
|
||||
if (subresources.has("meshes")) {
|
||||
mesh_data = subresources["meshes"];
|
||||
}
|
||||
|
||||
float fps = 30;
|
||||
if (p_options.has(SNAME("animation/fps"))) {
|
||||
fps = (float)p_options[SNAME("animation/fps")];
|
||||
|
|
@ -3050,14 +3099,18 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
|
|||
progress.step(TTR("Running Custom Script..."), 2);
|
||||
|
||||
String post_import_script_path = p_options["import_script/path"];
|
||||
|
||||
Ref<EditorScenePostImport> post_import_script;
|
||||
|
||||
if (!post_import_script_path.is_empty()) {
|
||||
if (post_import_script_path.is_relative_path()) {
|
||||
post_import_script_path = p_source_file.get_base_dir().path_join(post_import_script_path);
|
||||
}
|
||||
Ref<Script> scr = ResourceLoader::load(post_import_script_path);
|
||||
if (!scr.is_valid()) {
|
||||
if (scr.is_null()) {
|
||||
EditorNode::add_io_error(TTR("Couldn't load post-import script:") + " " + post_import_script_path);
|
||||
} else {
|
||||
post_import_script = Ref<EditorScenePostImport>(memnew(EditorScenePostImport));
|
||||
post_import_script.instantiate();
|
||||
post_import_script->set_script(scr);
|
||||
if (!post_import_script->get_script_instance()) {
|
||||
EditorNode::add_io_error(TTR("Invalid/broken script for post-import (check console):") + " " + post_import_script_path);
|
||||
|
|
@ -3067,6 +3120,19 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
|
|||
}
|
||||
}
|
||||
|
||||
// Apply RESET animation before serializing.
|
||||
if (_scene_import_type == "PackedScene") {
|
||||
int scene_child_count = scene->get_child_count();
|
||||
for (int i = 0; i < scene_child_count; i++) {
|
||||
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(scene->get_child(i));
|
||||
if (ap) {
|
||||
if (ap->can_apply_reset()) {
|
||||
ap->apply_reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (post_import_script.is_valid()) {
|
||||
post_import_script->init(p_source_file);
|
||||
scene = post_import_script->post_import(scene);
|
||||
|
|
@ -3085,11 +3151,11 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
|
|||
progress.step(TTR("Saving..."), 104);
|
||||
|
||||
int flags = 0;
|
||||
if (EDITOR_GET("filesystem/on_save/compress_binary_resources")) {
|
||||
if (EditorSettings::get_singleton() && EDITOR_GET("filesystem/on_save/compress_binary_resources")) {
|
||||
flags |= ResourceSaver::FLAG_COMPRESS;
|
||||
}
|
||||
|
||||
if (animation_importer) {
|
||||
if (_scene_import_type == "AnimationLibrary") {
|
||||
Ref<AnimationLibrary> library;
|
||||
for (int i = 0; i < scene->get_child_count(); i++) {
|
||||
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(scene->get_child(i));
|
||||
|
|
@ -3103,20 +3169,22 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
|
|||
}
|
||||
}
|
||||
|
||||
if (!library.is_valid()) {
|
||||
if (library.is_null()) {
|
||||
library.instantiate(); // Will be empty
|
||||
}
|
||||
|
||||
print_verbose("Saving animation to: " + p_save_path + ".res");
|
||||
err = ResourceSaver::save(library, p_save_path + ".res", flags); //do not take over, let the changed files reload themselves
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save animation to file '" + p_save_path + ".res'.");
|
||||
|
||||
} else {
|
||||
} else if (_scene_import_type == "PackedScene") {
|
||||
Ref<PackedScene> packer = memnew(PackedScene);
|
||||
packer->pack(scene);
|
||||
print_verbose("Saving scene to: " + p_save_path + ".scn");
|
||||
err = ResourceSaver::save(packer, p_save_path + ".scn", flags); //do not take over, let the changed files reload themselves
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save scene to file '" + p_save_path + ".scn'.");
|
||||
_generate_editor_preview_for_scene(p_source_file, scene);
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown scene import type: " + _scene_import_type);
|
||||
}
|
||||
|
||||
memdelete(scene);
|
||||
|
|
@ -3138,20 +3206,20 @@ bool ResourceImporterScene::has_advanced_options() const {
|
|||
}
|
||||
|
||||
void ResourceImporterScene::show_advanced_options(const String &p_path) {
|
||||
SceneImportSettingsDialog::get_singleton()->open_settings(p_path, animation_importer);
|
||||
SceneImportSettingsDialog::get_singleton()->open_settings(p_path, _scene_import_type);
|
||||
}
|
||||
|
||||
ResourceImporterScene::ResourceImporterScene(bool p_animation_import, bool p_singleton) {
|
||||
ResourceImporterScene::ResourceImporterScene(const String &p_scene_import_type, bool p_singleton) {
|
||||
// This should only be set through the EditorNode.
|
||||
if (p_singleton) {
|
||||
if (p_animation_import) {
|
||||
if (p_scene_import_type == "AnimationLibrary") {
|
||||
animation_singleton = this;
|
||||
} else {
|
||||
} else if (p_scene_import_type == "PackedScene") {
|
||||
scene_singleton = this;
|
||||
}
|
||||
}
|
||||
|
||||
animation_importer = p_animation_import;
|
||||
_scene_import_type = p_scene_import_type;
|
||||
}
|
||||
|
||||
ResourceImporterScene::~ResourceImporterScene() {
|
||||
|
|
@ -3202,10 +3270,6 @@ void ResourceImporterScene::get_scene_importer_extensions(List<String> *p_extens
|
|||
|
||||
///////////////////////////////////////
|
||||
|
||||
uint32_t EditorSceneFormatImporterESCN::get_import_flags() const {
|
||||
return IMPORT_SCENE;
|
||||
}
|
||||
|
||||
void EditorSceneFormatImporterESCN::get_extensions(List<String> *r_extensions) const {
|
||||
r_extensions->push_back("escn");
|
||||
}
|
||||
|
|
@ -3213,7 +3277,7 @@ void EditorSceneFormatImporterESCN::get_extensions(List<String> *r_extensions) c
|
|||
Node *EditorSceneFormatImporterESCN::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {
|
||||
Error error;
|
||||
Ref<PackedScene> ps = ResourceFormatLoaderText::singleton->load(p_path, p_path, &error);
|
||||
ERR_FAIL_COND_V_MSG(!ps.is_valid(), nullptr, "Cannot load scene as text resource from path '" + p_path + "'.");
|
||||
ERR_FAIL_COND_V_MSG(ps.is_null(), nullptr, "Cannot load scene as text resource from path '" + p_path + "'.");
|
||||
Node *scene = ps->instantiate();
|
||||
TypedArray<Node> nodes = scene->find_children("*", "MeshInstance3D");
|
||||
for (int32_t node_i = 0; node_i < nodes.size(); node_i++) {
|
||||
|
|
|
|||
|
|
@ -39,25 +39,25 @@
|
|||
#include "scene/resources/3d/capsule_shape_3d.h"
|
||||
#include "scene/resources/3d/cylinder_shape_3d.h"
|
||||
#include "scene/resources/3d/importer_mesh.h"
|
||||
#include "scene/resources/3d/skin.h"
|
||||
#include "scene/resources/3d/sphere_shape_3d.h"
|
||||
#include "scene/resources/animation.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
|
||||
class Material;
|
||||
class AnimationPlayer;
|
||||
|
||||
class ImporterMesh;
|
||||
class Material;
|
||||
|
||||
class EditorSceneFormatImporter : public RefCounted {
|
||||
GDCLASS(EditorSceneFormatImporter, RefCounted);
|
||||
|
||||
List<ResourceImporter::ImportOption> *current_option_list = nullptr;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
Node *import_scene_wrapper(const String &p_path, uint32_t p_flags, const Dictionary &p_options);
|
||||
Ref<Animation> import_animation_wrapper(const String &p_path, uint32_t p_flags, const Dictionary &p_options);
|
||||
|
||||
GDVIRTUAL0RC(uint32_t, _get_import_flags)
|
||||
GDVIRTUAL0RC(Vector<String>, _get_extensions)
|
||||
GDVIRTUAL3R(Object *, _import_scene, String, uint32_t, Dictionary)
|
||||
GDVIRTUAL1(_get_import_options, String)
|
||||
|
|
@ -74,11 +74,12 @@ public:
|
|||
IMPORT_FORCE_DISABLE_MESH_COMPRESSION = 64,
|
||||
};
|
||||
|
||||
virtual uint32_t get_import_flags() const;
|
||||
void add_import_option(const String &p_name, const Variant &p_default_value);
|
||||
void add_import_option_advanced(Variant::Type p_type, const String &p_name, const Variant &p_default_value, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = String(), int p_usage_flags = PROPERTY_USAGE_DEFAULT);
|
||||
virtual void get_extensions(List<String> *r_extensions) const;
|
||||
virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err = nullptr);
|
||||
virtual void get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options);
|
||||
virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options);
|
||||
virtual Variant get_option_visibility(const String &p_path, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options);
|
||||
virtual void handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {}
|
||||
|
||||
EditorSceneFormatImporter() {}
|
||||
|
|
@ -140,13 +141,13 @@ public:
|
|||
void add_import_option_advanced(Variant::Type p_type, const String &p_name, const Variant &p_default_value, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = String(), int p_usage_flags = PROPERTY_USAGE_DEFAULT);
|
||||
|
||||
virtual void get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options);
|
||||
virtual Variant get_internal_option_visibility(InternalImportCategory p_category, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) const;
|
||||
virtual Variant get_internal_option_visibility(InternalImportCategory p_category, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options) const;
|
||||
virtual Variant get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const HashMap<StringName, Variant> &p_options) const;
|
||||
|
||||
virtual void internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options);
|
||||
|
||||
virtual void get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options);
|
||||
virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options) const;
|
||||
virtual Variant get_option_visibility(const String &p_path, const String &p_scene_import_type, const String &p_option, const HashMap<StringName, Variant> &p_options) const;
|
||||
|
||||
virtual void pre_process(Node *p_scene, const HashMap<StringName, Variant> &p_options);
|
||||
virtual void post_process(Node *p_scene, const HashMap<StringName, Variant> &p_options);
|
||||
|
|
@ -236,8 +237,9 @@ class ResourceImporterScene : public ResourceImporter {
|
|||
};
|
||||
|
||||
void _optimize_track_usage(AnimationPlayer *p_player, AnimationImportTracks *p_track_actions);
|
||||
void _generate_editor_preview_for_scene(const String &p_path, Node *p_scene);
|
||||
|
||||
bool animation_importer = false;
|
||||
String _scene_import_type = "PackedScene";
|
||||
|
||||
public:
|
||||
static ResourceImporterScene *get_scene_singleton() { return scene_singleton; }
|
||||
|
|
@ -253,6 +255,9 @@ public:
|
|||
|
||||
static void clean_up_importer_plugins();
|
||||
|
||||
String get_scene_import_type() const { return _scene_import_type; }
|
||||
void set_scene_import_type(const String &p_type) { _scene_import_type = p_type; }
|
||||
|
||||
virtual String get_importer_name() const override;
|
||||
virtual String get_visible_name() const override;
|
||||
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
|
||||
|
|
@ -285,7 +290,7 @@ public:
|
|||
virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; }
|
||||
|
||||
void _pre_fix_global(Node *p_scene, const HashMap<StringName, Variant> &p_options) const;
|
||||
Node *_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);
|
||||
Node *_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, const HashMap<StringName, Variant> &p_options);
|
||||
Node *_pre_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps);
|
||||
Node *_post_fix_node(Node *p_node, Node *p_root, HashMap<Ref<ImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Pair<PackedVector3Array, PackedInt32Array> &r_occluder_arrays, HashSet<Ref<ImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps, float p_applied_root_scale);
|
||||
Node *_post_fix_animations(Node *p_node, Node *p_root, const Dictionary &p_node_data, const Dictionary &p_animation_data, float p_animation_fps, bool p_remove_immutable_tracks);
|
||||
|
|
@ -296,14 +301,12 @@ public:
|
|||
void _compress_animations(AnimationPlayer *anim, int p_page_size_kb);
|
||||
|
||||
Node *pre_import(const String &p_source_file, const HashMap<StringName, Variant> &p_options);
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
virtual bool has_advanced_options() const override;
|
||||
virtual void show_advanced_options(const String &p_path) override;
|
||||
|
||||
virtual bool can_import_threaded() const override { return false; }
|
||||
|
||||
ResourceImporterScene(bool p_animation_import = false, bool p_singleton = false);
|
||||
ResourceImporterScene(const String &p_scene_import_type = "PackedScene", bool p_singleton = false);
|
||||
~ResourceImporterScene();
|
||||
|
||||
template <typename M>
|
||||
|
|
@ -317,7 +320,6 @@ class EditorSceneFormatImporterESCN : public EditorSceneFormatImporter {
|
|||
GDCLASS(EditorSceneFormatImporterESCN, EditorSceneFormatImporter);
|
||||
|
||||
public:
|
||||
virtual uint32_t get_import_flags() const override;
|
||||
virtual void get_extensions(List<String> *r_extensions) const override;
|
||||
virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err = nullptr) override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,9 +37,11 @@
|
|||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/plugins/skeleton_3d_editor_plugin.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/3d/importer_mesh_instance_3d.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/gui/subviewport_container.h"
|
||||
#include "scene/resources/3d/importer_mesh.h"
|
||||
#include "scene/resources/surface_tool.h"
|
||||
|
||||
|
|
@ -117,7 +119,9 @@ class SceneImportSettingsData : public Object {
|
|||
ERR_FAIL_NULL(settings);
|
||||
if (r_option.name == "rest_pose/load_pose") {
|
||||
if (!settings->has("rest_pose/load_pose") || int((*settings)["rest_pose/load_pose"]) != 2) {
|
||||
(*settings)["rest_pose/external_animation_library"] = Variant();
|
||||
if (settings->has("rest_pose/external_animation_library")) {
|
||||
(*settings)["rest_pose/external_animation_library"] = Variant();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (r_option.name == "rest_pose/selected_animation") {
|
||||
|
|
@ -134,7 +138,10 @@ class SceneImportSettingsData : public Object {
|
|||
}
|
||||
} break;
|
||||
case 2: {
|
||||
Object *res = (*settings)["rest_pose/external_animation_library"];
|
||||
Object *res = nullptr;
|
||||
if (settings->has("rest_pose/external_animation_library")) {
|
||||
res = (*settings)["rest_pose/external_animation_library"];
|
||||
}
|
||||
Ref<Animation> anim(res);
|
||||
Ref<AnimationLibrary> library(res);
|
||||
if (anim.is_valid()) {
|
||||
|
|
@ -362,6 +369,7 @@ void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_ite
|
|||
mesh_node->set_transform(src_mesh_node->get_transform());
|
||||
mesh_node->set_skin(src_mesh_node->get_skin());
|
||||
mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path());
|
||||
mesh_node->set_visible(src_mesh_node->is_visible());
|
||||
if (src_mesh_node->get_mesh().is_valid()) {
|
||||
Ref<ImporterMesh> editor_mesh = src_mesh_node->get_mesh();
|
||||
mesh_node->set_mesh(editor_mesh->get_mesh());
|
||||
|
|
@ -414,7 +422,9 @@ void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_ite
|
|||
animation_player->connect(SceneStringName(animation_finished), callable_mp(this, &SceneImportSettingsDialog::_animation_finished));
|
||||
} else if (Object::cast_to<Skeleton3D>(p_node)) {
|
||||
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE;
|
||||
skeletons.push_back(Object::cast_to<Skeleton3D>(p_node));
|
||||
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node);
|
||||
skeleton->connect(SceneStringName(tree_entered), callable_mp(this, &SceneImportSettingsDialog::_skeleton_tree_entered).bind(skeleton));
|
||||
skeletons.push_back(skeleton);
|
||||
} else {
|
||||
category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
|
||||
}
|
||||
|
|
@ -467,6 +477,32 @@ void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_ite
|
|||
}
|
||||
|
||||
AABB aabb = accum_xform.xform(mesh_node->get_mesh()->get_aabb());
|
||||
|
||||
if (first_aabb) {
|
||||
contents_aabb = aabb;
|
||||
first_aabb = false;
|
||||
} else {
|
||||
contents_aabb.merge_with(aabb);
|
||||
}
|
||||
}
|
||||
|
||||
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node);
|
||||
if (skeleton) {
|
||||
Ref<ArrayMesh> bones_mesh = Skeleton3DGizmoPlugin::get_bones_mesh(skeleton, -1, true);
|
||||
|
||||
bones_mesh_preview->set_mesh(bones_mesh);
|
||||
|
||||
Transform3D accum_xform;
|
||||
Node3D *base = skeleton;
|
||||
while (base) {
|
||||
accum_xform = base->get_transform() * accum_xform;
|
||||
base = Object::cast_to<Node3D>(base->get_parent());
|
||||
}
|
||||
|
||||
bones_mesh_preview->set_transform(accum_xform * skeleton->get_transform());
|
||||
|
||||
AABB aabb = accum_xform.xform(bones_mesh->get_aabb());
|
||||
|
||||
if (first_aabb) {
|
||||
contents_aabb = aabb;
|
||||
first_aabb = false;
|
||||
|
|
@ -672,26 +708,26 @@ void SceneImportSettingsDialog::update_view() {
|
|||
update_view_timer->start();
|
||||
}
|
||||
|
||||
void SceneImportSettingsDialog::open_settings(const String &p_path, bool p_for_animation) {
|
||||
void SceneImportSettingsDialog::open_settings(const String &p_path, const String &p_scene_import_type) {
|
||||
if (scene) {
|
||||
_cleanup();
|
||||
memdelete(scene);
|
||||
scene = nullptr;
|
||||
}
|
||||
|
||||
editing_animation = p_for_animation;
|
||||
editing_animation = p_scene_import_type == "AnimationLibrary";
|
||||
scene_import_settings_data->settings = nullptr;
|
||||
scene_import_settings_data->path = p_path;
|
||||
|
||||
// Visibility.
|
||||
data_mode->set_tab_hidden(1, p_for_animation);
|
||||
data_mode->set_tab_hidden(2, p_for_animation);
|
||||
if (p_for_animation) {
|
||||
data_mode->set_tab_hidden(1, editing_animation);
|
||||
data_mode->set_tab_hidden(2, editing_animation);
|
||||
if (editing_animation) {
|
||||
data_mode->set_current_tab(0);
|
||||
}
|
||||
|
||||
action_menu->get_popup()->set_item_disabled(action_menu->get_popup()->get_item_id(ACTION_EXTRACT_MATERIALS), p_for_animation);
|
||||
action_menu->get_popup()->set_item_disabled(action_menu->get_popup()->get_item_id(ACTION_CHOOSE_MESH_SAVE_PATHS), p_for_animation);
|
||||
action_menu->get_popup()->set_item_disabled(action_menu->get_popup()->get_item_id(ACTION_EXTRACT_MATERIALS), editing_animation);
|
||||
action_menu->get_popup()->set_item_disabled(action_menu->get_popup()->get_item_id(ACTION_CHOOSE_MESH_SAVE_PATHS), editing_animation);
|
||||
|
||||
base_path = p_path;
|
||||
|
||||
|
|
@ -765,7 +801,7 @@ void SceneImportSettingsDialog::open_settings(const String &p_path, bool p_for_a
|
|||
// Start with the root item (Scene) selected.
|
||||
scene_tree->get_root()->select(0);
|
||||
|
||||
if (p_for_animation) {
|
||||
if (editing_animation) {
|
||||
set_title(vformat(TTR("Advanced Import Settings for AnimationLibrary '%s'"), base_path.get_file()));
|
||||
} else {
|
||||
set_title(vformat(TTR("Advanced Import Settings for Scene '%s'"), base_path.get_file()));
|
||||
|
|
@ -789,6 +825,7 @@ void SceneImportSettingsDialog::_select(Tree *p_from, const String &p_type, cons
|
|||
selecting = true;
|
||||
scene_import_settings_data->hide_options = false;
|
||||
|
||||
bones_mesh_preview->hide();
|
||||
if (p_type == "Node") {
|
||||
node_selected->hide(); // Always hide just in case.
|
||||
mesh_preview->hide();
|
||||
|
|
@ -828,6 +865,7 @@ void SceneImportSettingsDialog::_select(Tree *p_from, const String &p_type, cons
|
|||
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE;
|
||||
} else if (Object::cast_to<Skeleton3D>(nd.node)) {
|
||||
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE;
|
||||
bones_mesh_preview->show();
|
||||
} else {
|
||||
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
|
||||
scene_import_settings_data->hide_options = editing_animation;
|
||||
|
|
@ -847,6 +885,8 @@ void SceneImportSettingsDialog::_select(Tree *p_from, const String &p_type, cons
|
|||
|
||||
scene_import_settings_data->settings = &ad.settings;
|
||||
scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION;
|
||||
|
||||
_animation_update_skeleton_visibility();
|
||||
} else if (p_type == "Mesh") {
|
||||
node_selected->hide();
|
||||
if (Object::cast_to<Node3D>(scene)) {
|
||||
|
|
@ -981,11 +1021,11 @@ void SceneImportSettingsDialog::_play_animation() {
|
|||
if (animation_player->has_animation(id)) {
|
||||
if (animation_player->is_playing()) {
|
||||
animation_player->pause();
|
||||
animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
set_process(false);
|
||||
} else {
|
||||
animation_player->play(id);
|
||||
animation_play_button->set_icon(get_editor_theme_icon(SNAME("Pause")));
|
||||
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
|
||||
set_process(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -994,7 +1034,7 @@ void SceneImportSettingsDialog::_play_animation() {
|
|||
void SceneImportSettingsDialog::_stop_current_animation() {
|
||||
animation_pingpong = false;
|
||||
animation_player->stop();
|
||||
animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
animation_slider->set_value_no_signal(0.0);
|
||||
set_process(false);
|
||||
}
|
||||
|
|
@ -1006,7 +1046,7 @@ void SceneImportSettingsDialog::_reset_animation(const String &p_animation_name)
|
|||
if (animation_player != nullptr && animation_player->is_playing()) {
|
||||
animation_player->stop();
|
||||
}
|
||||
animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
|
||||
_reset_bone_transforms();
|
||||
set_process(false);
|
||||
|
|
@ -1028,7 +1068,7 @@ void SceneImportSettingsDialog::_reset_animation(const String &p_animation_name)
|
|||
animation_player->play(p_animation_name);
|
||||
} else {
|
||||
animation_player->stop(true);
|
||||
animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
animation_player->set_assigned_animation(p_animation_name);
|
||||
animation_player->seek(0.0, true);
|
||||
animation_slider->set_value_no_signal(0.0);
|
||||
|
|
@ -1043,18 +1083,23 @@ void SceneImportSettingsDialog::_animation_slider_value_changed(double p_value)
|
|||
}
|
||||
if (animation_player->is_playing()) {
|
||||
animation_player->stop();
|
||||
animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
set_process(false);
|
||||
}
|
||||
animation_player->seek(p_value * animation_map[selected_id].animation->get_length(), true);
|
||||
}
|
||||
|
||||
void SceneImportSettingsDialog::_skeleton_tree_entered(Skeleton3D *p_skeleton) {
|
||||
bones_mesh_preview->set_skeleton_path(p_skeleton->get_path());
|
||||
bones_mesh_preview->set_skin(p_skeleton->register_skin(p_skeleton->create_skin_from_rest_transforms()));
|
||||
}
|
||||
|
||||
void SceneImportSettingsDialog::_animation_finished(const StringName &p_name) {
|
||||
Animation::LoopMode loop_mode = animation_loop_mode;
|
||||
|
||||
switch (loop_mode) {
|
||||
case Animation::LOOP_NONE: {
|
||||
animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
animation_slider->set_value_no_signal(1.0);
|
||||
set_process(false);
|
||||
} break;
|
||||
|
|
@ -1074,6 +1119,14 @@ void SceneImportSettingsDialog::_animation_finished(const StringName &p_name) {
|
|||
}
|
||||
}
|
||||
|
||||
void SceneImportSettingsDialog::_animation_update_skeleton_visibility() {
|
||||
if (animation_toggle_skeleton_visibility->is_pressed()) {
|
||||
bones_mesh_preview->show();
|
||||
} else {
|
||||
bones_mesh_preview->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneImportSettingsDialog::_material_tree_selected() {
|
||||
if (selecting) {
|
||||
return;
|
||||
|
|
@ -1172,6 +1225,20 @@ void SceneImportSettingsDialog::_viewport_input(const Ref<InputEvent> &p_input)
|
|||
}
|
||||
_update_camera();
|
||||
}
|
||||
Ref<InputEventMagnifyGesture> mg = p_input;
|
||||
if (mg.is_valid()) {
|
||||
real_t mg_factor = mg->get_factor();
|
||||
if (mg_factor == 0.0) {
|
||||
mg_factor = 1.0;
|
||||
}
|
||||
(*zoom) /= mg_factor;
|
||||
if ((*zoom) < 0.1) {
|
||||
(*zoom) = 0.1;
|
||||
} else if ((*zoom) > 10.0) {
|
||||
(*zoom) = 10.0;
|
||||
}
|
||||
_update_camera();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneImportSettingsDialog::_re_import() {
|
||||
|
|
@ -1262,20 +1329,22 @@ void SceneImportSettingsDialog::_notification(int p_what) {
|
|||
case NOTIFICATION_THEME_CHANGED: {
|
||||
action_menu->begin_bulk_theme_override();
|
||||
action_menu->add_theme_style_override(CoreStringName(normal), get_theme_stylebox(CoreStringName(normal), "Button"));
|
||||
action_menu->add_theme_style_override("hover", get_theme_stylebox("hover", "Button"));
|
||||
action_menu->add_theme_style_override(SceneStringName(hover), get_theme_stylebox(SceneStringName(hover), "Button"));
|
||||
action_menu->add_theme_style_override(SceneStringName(pressed), get_theme_stylebox(SceneStringName(pressed), "Button"));
|
||||
action_menu->end_bulk_theme_override();
|
||||
|
||||
if (animation_player != nullptr && animation_player->is_playing()) {
|
||||
animation_play_button->set_icon(get_editor_theme_icon(SNAME("Pause")));
|
||||
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
|
||||
} else {
|
||||
animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
}
|
||||
animation_stop_button->set_icon(get_editor_theme_icon(SNAME("Stop")));
|
||||
animation_stop_button->set_button_icon(get_editor_theme_icon(SNAME("Stop")));
|
||||
|
||||
light_1_switch->set_icon(theme_cache.light_1_icon);
|
||||
light_2_switch->set_icon(theme_cache.light_2_icon);
|
||||
light_rotate_switch->set_icon(theme_cache.rotate_icon);
|
||||
light_1_switch->set_button_icon(theme_cache.light_1_icon);
|
||||
light_2_switch->set_button_icon(theme_cache.light_2_icon);
|
||||
light_rotate_switch->set_button_icon(theme_cache.rotate_icon);
|
||||
|
||||
animation_toggle_skeleton_visibility->set_button_icon(get_editor_theme_icon(SNAME("SkeletonPreview")));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_PROCESS: {
|
||||
|
|
@ -1648,13 +1717,14 @@ SceneImportSettingsDialog::SceneImportSettingsDialog() {
|
|||
animation_hbox->add_child(animation_play_button);
|
||||
animation_play_button->set_flat(true);
|
||||
animation_play_button->set_focus_mode(Control::FOCUS_NONE);
|
||||
animation_play_button->set_shortcut(ED_SHORTCUT("scene_import_settings/play_selected_animation", TTR("Selected Animation Play/Pause"), Key::SPACE));
|
||||
animation_play_button->set_shortcut(ED_SHORTCUT("scene_import_settings/play_selected_animation", TTRC("Selected Animation Play/Pause"), Key::SPACE));
|
||||
animation_play_button->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_play_animation));
|
||||
|
||||
animation_stop_button = memnew(Button);
|
||||
animation_hbox->add_child(animation_stop_button);
|
||||
animation_stop_button->set_flat(true);
|
||||
animation_stop_button->set_focus_mode(Control::FOCUS_NONE);
|
||||
animation_stop_button->set_tooltip_text(TTR("Selected Animation Stop"));
|
||||
animation_stop_button->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_stop_current_animation));
|
||||
|
||||
animation_slider = memnew(HSlider);
|
||||
|
|
@ -1667,6 +1737,15 @@ SceneImportSettingsDialog::SceneImportSettingsDialog() {
|
|||
animation_slider->set_focus_mode(Control::FOCUS_NONE);
|
||||
animation_slider->connect(SceneStringName(value_changed), callable_mp(this, &SceneImportSettingsDialog::_animation_slider_value_changed));
|
||||
|
||||
animation_toggle_skeleton_visibility = memnew(Button);
|
||||
animation_hbox->add_child(animation_toggle_skeleton_visibility);
|
||||
animation_toggle_skeleton_visibility->set_toggle_mode(true);
|
||||
animation_toggle_skeleton_visibility->set_theme_type_variation("FlatButton");
|
||||
animation_toggle_skeleton_visibility->set_focus_mode(Control::FOCUS_NONE);
|
||||
animation_toggle_skeleton_visibility->set_tooltip_text(TTR("Toggle Animation Skeleton Visibility"));
|
||||
|
||||
animation_toggle_skeleton_visibility->connect(SceneStringName(pressed), callable_mp(this, &SceneImportSettingsDialog::_animation_update_skeleton_visibility));
|
||||
|
||||
base_viewport->set_use_own_world_3d(true);
|
||||
|
||||
HBoxContainer *viewport_hbox = memnew(HBoxContainer);
|
||||
|
|
@ -1794,6 +1873,13 @@ SceneImportSettingsDialog::SceneImportSettingsDialog() {
|
|||
collider_mat->set_albedo(Color(0.5, 0.5, 1.0));
|
||||
}
|
||||
|
||||
{
|
||||
bones_mesh_preview = memnew(MeshInstance3D);
|
||||
bones_mesh_preview->set_cast_shadows_setting(GeometryInstance3D::SHADOW_CASTING_SETTING_OFF);
|
||||
bones_mesh_preview->set_skeleton_path(NodePath());
|
||||
base_viewport->add_child(bones_mesh_preview);
|
||||
}
|
||||
|
||||
inspector = memnew(EditorInspector);
|
||||
inspector->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
|
||||
inspector->connect(SNAME("property_edited"), callable_mp(this, &SceneImportSettingsDialog::_inspector_property_edited));
|
||||
|
|
|
|||
|
|
@ -37,13 +37,11 @@
|
|||
#include "scene/3d/mesh_instance_3d.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/gui/slider.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/subviewport_container.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
#include "scene/gui/tree.h"
|
||||
#include "scene/resources/3d/primitive_meshes.h"
|
||||
|
|
@ -109,10 +107,12 @@ class SceneImportSettingsDialog : public ConfirmationDialog {
|
|||
HSlider *animation_slider = nullptr;
|
||||
Button *animation_play_button = nullptr;
|
||||
Button *animation_stop_button = nullptr;
|
||||
Button *animation_toggle_skeleton_visibility = nullptr;
|
||||
Animation::LoopMode animation_loop_mode = Animation::LOOP_NONE;
|
||||
bool animation_pingpong = false;
|
||||
bool previous_import_as_skeleton = false;
|
||||
bool previous_rest_as_reset = false;
|
||||
MeshInstance3D *bones_mesh_preview = nullptr;
|
||||
|
||||
Ref<StandardMaterial3D> collider_mat;
|
||||
|
||||
|
|
@ -187,9 +187,11 @@ class SceneImportSettingsDialog : public ConfirmationDialog {
|
|||
void _reset_animation(const String &p_animation_name = "");
|
||||
void _animation_slider_value_changed(double p_value);
|
||||
void _animation_finished(const StringName &p_name);
|
||||
void _animation_update_skeleton_visibility();
|
||||
void _material_tree_selected();
|
||||
void _mesh_tree_selected();
|
||||
void _scene_tree_selected();
|
||||
void _skeleton_tree_entered(Skeleton3D *p_skeleton);
|
||||
void _cleanup();
|
||||
void _on_light_1_switch_pressed();
|
||||
void _on_light_2_switch_pressed();
|
||||
|
|
@ -243,7 +245,7 @@ public:
|
|||
bool is_editing_animation() const { return editing_animation; }
|
||||
void request_generate_collider();
|
||||
void update_view();
|
||||
void open_settings(const String &p_path, bool p_for_animation = false);
|
||||
void open_settings(const String &p_path, const String &p_scene_import_type = "PackedScene");
|
||||
static SceneImportSettingsDialog *get_singleton();
|
||||
Node *get_selected_node();
|
||||
SceneImportSettingsDialog();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ void AudioStreamImportSettingsDialog::_notification(int p_what) {
|
|||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
_stop_button->set_icon(get_editor_theme_icon(SNAME("Stop")));
|
||||
_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
_stop_button->set_button_icon(get_editor_theme_icon(SNAME("Stop")));
|
||||
|
||||
_preview->set_color(get_theme_color(SNAME("dark_color_2"), EditorStringName(Editor)));
|
||||
color_rect->set_color(get_theme_color(SNAME("dark_color_1"), EditorStringName(Editor)));
|
||||
|
|
@ -61,9 +61,9 @@ void AudioStreamImportSettingsDialog::_notification(int p_what) {
|
|||
_duration_label->add_theme_font_size_override(SceneStringName(font_size), get_theme_font_size(SNAME("status_source_size"), EditorStringName(EditorFonts)));
|
||||
_duration_label->end_bulk_theme_override();
|
||||
|
||||
zoom_in->set_icon(get_editor_theme_icon(SNAME("ZoomMore")));
|
||||
zoom_out->set_icon(get_editor_theme_icon(SNAME("ZoomLess")));
|
||||
zoom_reset->set_icon(get_editor_theme_icon(SNAME("ZoomReset")));
|
||||
zoom_in->set_button_icon(get_editor_theme_icon(SNAME("ZoomMore")));
|
||||
zoom_out->set_button_icon(get_editor_theme_icon(SNAME("ZoomLess")));
|
||||
zoom_reset->set_button_icon(get_editor_theme_icon(SNAME("ZoomReset")));
|
||||
|
||||
_indicator->queue_redraw();
|
||||
_preview->queue_redraw();
|
||||
|
|
@ -180,7 +180,7 @@ void AudioStreamImportSettingsDialog::_preview_changed(ObjectID p_which) {
|
|||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_preview_zoom_in() {
|
||||
if (!stream.is_valid()) {
|
||||
if (stream.is_null()) {
|
||||
return;
|
||||
}
|
||||
float page_size = zoom_bar->get_page();
|
||||
|
|
@ -192,7 +192,7 @@ void AudioStreamImportSettingsDialog::_preview_zoom_in() {
|
|||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_preview_zoom_out() {
|
||||
if (!stream.is_valid()) {
|
||||
if (stream.is_null()) {
|
||||
return;
|
||||
}
|
||||
float page_size = zoom_bar->get_page();
|
||||
|
|
@ -204,7 +204,7 @@ void AudioStreamImportSettingsDialog::_preview_zoom_out() {
|
|||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_preview_zoom_reset() {
|
||||
if (!stream.is_valid()) {
|
||||
if (stream.is_null()) {
|
||||
return;
|
||||
}
|
||||
zoom_bar->set_max(stream->get_length());
|
||||
|
|
@ -233,25 +233,25 @@ void AudioStreamImportSettingsDialog::_play() {
|
|||
// '_pausing' variable indicates that we want to pause the audio player, not stop it. See '_on_finished()'.
|
||||
_pausing = true;
|
||||
_player->stop();
|
||||
_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
set_process(false);
|
||||
} else {
|
||||
_player->play(_current);
|
||||
_play_button->set_icon(get_editor_theme_icon(SNAME("Pause")));
|
||||
_play_button->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
|
||||
set_process(true);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_stop() {
|
||||
_player->stop();
|
||||
_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
_current = 0;
|
||||
_indicator->queue_redraw();
|
||||
set_process(false);
|
||||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_on_finished() {
|
||||
_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
|
||||
if (!_pausing) {
|
||||
_current = 0;
|
||||
_indicator->queue_redraw();
|
||||
|
|
@ -262,7 +262,7 @@ void AudioStreamImportSettingsDialog::_on_finished() {
|
|||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::_draw_indicator() {
|
||||
if (!stream.is_valid()) {
|
||||
if (stream.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -401,7 +401,7 @@ void AudioStreamImportSettingsDialog::_seek_to(real_t p_x) {
|
|||
}
|
||||
|
||||
void AudioStreamImportSettingsDialog::edit(const String &p_path, const String &p_importer, const Ref<AudioStream> &p_stream) {
|
||||
if (!stream.is_null()) {
|
||||
if (stream.is_valid()) {
|
||||
stream->disconnect_changed(callable_mp(this, &AudioStreamImportSettingsDialog::_audio_changed));
|
||||
}
|
||||
|
||||
|
|
@ -414,7 +414,7 @@ void AudioStreamImportSettingsDialog::edit(const String &p_path, const String &p
|
|||
String text = String::num(stream->get_length(), 2).pad_decimals(2) + "s";
|
||||
_duration_label->set_text(text);
|
||||
|
||||
if (!stream.is_null()) {
|
||||
if (stream.is_valid()) {
|
||||
stream->connect_changed(callable_mp(this, &AudioStreamImportSettingsDialog::_audio_changed));
|
||||
_preview->queue_redraw();
|
||||
_indicator->queue_redraw();
|
||||
|
|
@ -537,7 +537,7 @@ AudioStreamImportSettingsDialog::AudioStreamImportSettingsDialog() {
|
|||
loop = memnew(CheckBox);
|
||||
loop->set_text(TTR("Enable"));
|
||||
loop->set_tooltip_text(TTR("Enable looping."));
|
||||
loop->connect("toggled", callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
loop->connect(SceneStringName(toggled), callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
loop_hb->add_child(loop);
|
||||
loop_hb->add_spacer();
|
||||
loop_hb->add_child(memnew(Label(TTR("Offset:"))));
|
||||
|
|
@ -554,7 +554,7 @@ AudioStreamImportSettingsDialog::AudioStreamImportSettingsDialog() {
|
|||
interactive_hb->add_theme_constant_override("separation", 4 * EDSCALE);
|
||||
bpm_enabled = memnew(CheckBox);
|
||||
bpm_enabled->set_text((TTR("BPM:")));
|
||||
bpm_enabled->connect("toggled", callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
bpm_enabled->connect(SceneStringName(toggled), callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
interactive_hb->add_child(bpm_enabled);
|
||||
bpm_edit = memnew(SpinBox);
|
||||
bpm_edit->set_max(400);
|
||||
|
|
@ -565,7 +565,7 @@ AudioStreamImportSettingsDialog::AudioStreamImportSettingsDialog() {
|
|||
interactive_hb->add_spacer();
|
||||
beats_enabled = memnew(CheckBox);
|
||||
beats_enabled->set_text(TTR("Beat Count:"));
|
||||
beats_enabled->connect("toggled", callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
beats_enabled->connect(SceneStringName(toggled), callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
interactive_hb->add_child(beats_enabled);
|
||||
beats_edit = memnew(SpinBox);
|
||||
beats_edit->set_tooltip_text(TTR("Configure the amount of Beats used for music-aware looping. If zero, it will be autodetected from the length.\nIt is recommended to set this value (either manually or by clicking on a beat number in the preview) to ensure looping works properly."));
|
||||
|
|
@ -580,12 +580,10 @@ AudioStreamImportSettingsDialog::AudioStreamImportSettingsDialog() {
|
|||
bar_beats_edit->set_max(32);
|
||||
bar_beats_edit->connect(SceneStringName(value_changed), callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
|
||||
interactive_hb->add_child(bar_beats_edit);
|
||||
interactive_hb->add_spacer();
|
||||
main_vbox->add_margin_child(TTR("Music Playback:"), interactive_hb);
|
||||
|
||||
color_rect = memnew(ColorRect);
|
||||
main_vbox->add_margin_child(TTR("Preview:"), color_rect);
|
||||
|
||||
main_vbox->add_margin_child(TTR("Preview:"), color_rect, true);
|
||||
color_rect->set_custom_minimum_size(Size2(600, 200) * EDSCALE);
|
||||
color_rect->set_v_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,15 +31,15 @@
|
|||
#include "dynamic_font_import_settings.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/string/translation.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/editor_locale_dialog.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_property_name_processor.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/editor_string_names.h"
|
||||
#include "editor/gui/editor_file_dialog.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
|
||||
/*************************************************************************/
|
||||
/* Settings data */
|
||||
|
|
@ -95,7 +95,7 @@ struct UniRange {
|
|||
};
|
||||
|
||||
// Unicode Character Blocks
|
||||
// Source: https://www.unicode.org/Public/14.0.0/ucd/Blocks.txt
|
||||
// Source: https://www.unicode.org/Public/16.0.0/ucd/Blocks.txt
|
||||
static UniRange unicode_ranges[] = {
|
||||
{ 0x0000, 0x007F, U"Basic Latin" },
|
||||
{ 0x0080, 0x00FF, U"Latin-1 Supplement" },
|
||||
|
|
@ -282,6 +282,7 @@ static UniRange unicode_ranges[] = {
|
|||
{ 0x10500, 0x1052F, U"Elbasan" },
|
||||
{ 0x10530, 0x1056F, U"Caucasian Albanian" },
|
||||
{ 0x10570, 0x105BF, U"Vithkuqi" },
|
||||
{ 0x105C0, 0x105FF, U"Todhri" },
|
||||
{ 0x10600, 0x1077F, U"Linear A" },
|
||||
{ 0x10780, 0x107BF, U"Latin Extended-F" },
|
||||
{ 0x10800, 0x1083F, U"Cypriot Syllabary" },
|
||||
|
|
@ -304,6 +305,7 @@ static UniRange unicode_ranges[] = {
|
|||
{ 0x10C00, 0x10C4F, U"Old Turkic" },
|
||||
{ 0x10C80, 0x10CFF, U"Old Hungarian" },
|
||||
{ 0x10D00, 0x10D3F, U"Hanifi Rohingya" },
|
||||
{ 0x10D40, 0x10D8F, U"Garay" },
|
||||
{ 0x10E60, 0x10E7F, U"Rumi Numeral Symbols" },
|
||||
{ 0x10E80, 0x10EBF, U"Yezidi" },
|
||||
{ 0x10EC0, 0x10EFF, U"Arabic Extended-C" },
|
||||
|
|
@ -323,12 +325,14 @@ static UniRange unicode_ranges[] = {
|
|||
{ 0x11280, 0x112AF, U"Multani" },
|
||||
{ 0x112B0, 0x112FF, U"Khudawadi" },
|
||||
{ 0x11300, 0x1137F, U"Grantha" },
|
||||
{ 0x11380, 0x113FF, U"Tulu-Tigalari" },
|
||||
{ 0x11400, 0x1147F, U"Newa" },
|
||||
{ 0x11480, 0x114DF, U"Tirhuta" },
|
||||
{ 0x11580, 0x115FF, U"Siddham" },
|
||||
{ 0x11600, 0x1165F, U"Modi" },
|
||||
{ 0x11660, 0x1167F, U"Mongolian Supplement" },
|
||||
{ 0x11680, 0x116CF, U"Takri" },
|
||||
{ 0x116D0, 0x116FF, U"Myanmar Extended-C" },
|
||||
{ 0x11700, 0x1174F, U"Ahom" },
|
||||
{ 0x11800, 0x1184F, U"Dogra" },
|
||||
{ 0x118A0, 0x118FF, U"Warang Citi" },
|
||||
|
|
@ -339,6 +343,7 @@ static UniRange unicode_ranges[] = {
|
|||
{ 0x11AB0, 0x11ABF, U"Unified Canadian Aboriginal Syllabics Extended-A" },
|
||||
{ 0x11AC0, 0x11AFF, U"Pau Cin Hau" },
|
||||
{ 0x11B00, 0x11B5F, U"Devanagari Extended-A" },
|
||||
{ 0x11BC0, 0x11BFF, U"Sunuwar" },
|
||||
{ 0x11C00, 0x11C6F, U"Bhaiksuki" },
|
||||
{ 0x11C70, 0x11CBF, U"Marchen" },
|
||||
{ 0x11D00, 0x11D5F, U"Masaram Gondi" },
|
||||
|
|
@ -353,12 +358,15 @@ static UniRange unicode_ranges[] = {
|
|||
{ 0x12F90, 0x12FFF, U"Cypro-Minoan" },
|
||||
{ 0x13000, 0x1342F, U"Egyptian Hieroglyphs" },
|
||||
{ 0x13430, 0x1343F, U"Egyptian Hieroglyph Format Controls" },
|
||||
{ 0x13460, 0x143FF, U"Egyptian Hieroglyphs Extended-A" },
|
||||
{ 0x14400, 0x1467F, U"Anatolian Hieroglyphs" },
|
||||
{ 0x16100, 0x1613F, U"Gurung Khema" },
|
||||
{ 0x16800, 0x16A3F, U"Bamum Supplement" },
|
||||
{ 0x16A40, 0x16A6F, U"Mro" },
|
||||
{ 0x16A70, 0x16ACF, U"Tangsa" },
|
||||
{ 0x16AD0, 0x16AFF, U"Bassa Vah" },
|
||||
{ 0x16B00, 0x16B8F, U"Pahawh Hmong" },
|
||||
{ 0x16D40, 0x16D7F, U"Kirat Rai" },
|
||||
{ 0x16E40, 0x16E9F, U"Medefaidrin" },
|
||||
{ 0x16F00, 0x16F9F, U"Miao" },
|
||||
{ 0x16FE0, 0x16FFF, U"Ideographic Symbols and Punctuation" },
|
||||
|
|
@ -373,6 +381,7 @@ static UniRange unicode_ranges[] = {
|
|||
{ 0x1B170, 0x1B2FF, U"Nushu" },
|
||||
{ 0x1BC00, 0x1BC9F, U"Duployan" },
|
||||
{ 0x1BCA0, 0x1BCAF, U"Shorthand Format Controls" },
|
||||
{ 0x1CC00, 0x1CEBF, U"Symbols for Legacy Computing Supplement" },
|
||||
{ 0x1CF00, 0x1CFCF, U"Znamenny Musical Notation" },
|
||||
{ 0x1D000, 0x1D0FF, U"Byzantine Musical Symbols" },
|
||||
{ 0x1D100, 0x1D1FF, U"Musical Symbols" },
|
||||
|
|
@ -390,6 +399,7 @@ static UniRange unicode_ranges[] = {
|
|||
{ 0x1E290, 0x1E2BF, U"Toto" },
|
||||
{ 0x1E2C0, 0x1E2FF, U"Wancho" },
|
||||
{ 0x1E4D0, 0x1E4FF, U"Nag Mundari" },
|
||||
{ 0x1E5D0, 0x1E5FF, U"Ol Onal" },
|
||||
{ 0x1E7E0, 0x1E7FF, U"Ethiopic Extended-B" },
|
||||
{ 0x1E800, 0x1E8DF, U"Mende Kikakui" },
|
||||
{ 0x1E900, 0x1E95F, U"Adlam" },
|
||||
|
|
@ -417,6 +427,7 @@ static UniRange unicode_ranges[] = {
|
|||
{ 0x2B740, 0x2B81F, U"CJK Unified Ideographs Extension D" },
|
||||
{ 0x2B820, 0x2CEAF, U"CJK Unified Ideographs Extension E" },
|
||||
{ 0x2CEB0, 0x2EBEF, U"CJK Unified Ideographs Extension F" },
|
||||
{ 0x2EBF0, 0x2EE5F, U"CJK Unified Ideographs Extension I" },
|
||||
{ 0x2F800, 0x2FA1F, U"CJK Compatibility Ideographs Supplement" },
|
||||
{ 0x30000, 0x3134F, U"CJK Unified Ideographs Extension G" },
|
||||
{ 0x31350, 0x323AF, U"CJK Unified Ideographs Extension H" },
|
||||
|
|
@ -462,6 +473,7 @@ void DynamicFontImportSettingsDialog::_main_prop_changed(const String &p_edited_
|
|||
if (font_preview.is_valid()) {
|
||||
if (p_edited_property == "antialiasing") {
|
||||
font_preview->set_antialiasing((TextServer::FontAntialiasing)import_settings_data->get("antialiasing").operator int());
|
||||
_variations_validate();
|
||||
} else if (p_edited_property == "generate_mipmaps") {
|
||||
font_preview->set_generate_mipmaps(import_settings_data->get("generate_mipmaps"));
|
||||
} else if (p_edited_property == "disable_embedded_bitmaps") {
|
||||
|
|
@ -481,7 +493,18 @@ void DynamicFontImportSettingsDialog::_main_prop_changed(const String &p_edited_
|
|||
} else if (p_edited_property == "hinting") {
|
||||
font_preview->set_hinting((TextServer::Hinting)import_settings_data->get("hinting").operator int());
|
||||
} else if (p_edited_property == "subpixel_positioning") {
|
||||
font_preview->set_subpixel_positioning((TextServer::SubpixelPositioning)import_settings_data->get("subpixel_positioning").operator int());
|
||||
int font_subpixel_positioning = import_settings_data->get("subpixel_positioning").operator int();
|
||||
if (font_subpixel_positioning == 4 /* Auto (Except Pixel Fonts) */) {
|
||||
if (is_pixel) {
|
||||
font_subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
|
||||
} else {
|
||||
font_subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
|
||||
}
|
||||
}
|
||||
font_preview->set_subpixel_positioning((TextServer::SubpixelPositioning)font_subpixel_positioning);
|
||||
_variations_validate();
|
||||
} else if (p_edited_property == "keep_rounding_remainders") {
|
||||
font_preview->set_keep_rounding_remainders(import_settings_data->get("keep_rounding_remainders"));
|
||||
} else if (p_edited_property == "oversampling") {
|
||||
font_preview->set_oversampling(import_settings_data->get("oversampling"));
|
||||
}
|
||||
|
|
@ -508,7 +531,7 @@ void DynamicFontImportSettingsDialog::_variation_add() {
|
|||
Ref<DynamicFontImportSettingsData> import_variation_data;
|
||||
import_variation_data.instantiate();
|
||||
import_variation_data->owner = this;
|
||||
ERR_FAIL_NULL(import_variation_data);
|
||||
ERR_FAIL_COND(import_variation_data.is_null());
|
||||
|
||||
for (List<ResourceImporter::ImportOption>::Element *E = options_variations.front(); E; E = E->next()) {
|
||||
import_variation_data->defaults[E->get().option.name] = E->get().default_value;
|
||||
|
|
@ -528,7 +551,7 @@ void DynamicFontImportSettingsDialog::_variation_selected() {
|
|||
TreeItem *vars_item = vars_list->get_selected();
|
||||
if (vars_item) {
|
||||
Ref<DynamicFontImportSettingsData> import_variation_data = vars_item->get_metadata(0);
|
||||
ERR_FAIL_NULL(import_variation_data);
|
||||
ERR_FAIL_COND(import_variation_data.is_null());
|
||||
|
||||
inspector_vars->edit(import_variation_data.ptr());
|
||||
import_variation_data->notify_property_list_changed();
|
||||
|
|
@ -587,14 +610,14 @@ void DynamicFontImportSettingsDialog::_variations_validate() {
|
|||
}
|
||||
for (TreeItem *vars_item_a = vars_list_root->get_first_child(); vars_item_a; vars_item_a = vars_item_a->get_next()) {
|
||||
Ref<DynamicFontImportSettingsData> import_variation_data_a = vars_item_a->get_metadata(0);
|
||||
ERR_FAIL_NULL(import_variation_data_a);
|
||||
ERR_FAIL_COND(import_variation_data_a.is_null());
|
||||
|
||||
for (TreeItem *vars_item_b = vars_list_root->get_first_child(); vars_item_b; vars_item_b = vars_item_b->get_next()) {
|
||||
if (vars_item_b != vars_item_a) {
|
||||
bool match = true;
|
||||
for (const KeyValue<StringName, Variant> &E : import_variation_data_a->settings) {
|
||||
Ref<DynamicFontImportSettingsData> import_variation_data_b = vars_item_b->get_metadata(0);
|
||||
ERR_FAIL_NULL(import_variation_data_b);
|
||||
ERR_FAIL_COND(import_variation_data_b.is_null());
|
||||
match = match && (import_variation_data_b->settings[E.key] == E.value);
|
||||
}
|
||||
if (match) {
|
||||
|
|
@ -607,7 +630,15 @@ void DynamicFontImportSettingsDialog::_variations_validate() {
|
|||
if ((TextServer::FontAntialiasing)(int)import_settings_data->get("antialiasing") == TextServer::FONT_ANTIALIASING_LCD) {
|
||||
warn += "\n" + TTR("Note: LCD Subpixel antialiasing is selected, each of the glyphs will be pre-rendered for all supported subpixel layouts (5x).");
|
||||
}
|
||||
if ((TextServer::SubpixelPositioning)(int)import_settings_data->get("subpixel_positioning") != TextServer::SUBPIXEL_POSITIONING_DISABLED) {
|
||||
int font_subpixel_positioning = import_settings_data->get("subpixel_positioning").operator int();
|
||||
if (font_subpixel_positioning == 4 /* Auto (Except Pixel Fonts) */) {
|
||||
if (is_pixel) {
|
||||
font_subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
|
||||
} else {
|
||||
font_subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
|
||||
}
|
||||
}
|
||||
if ((TextServer::SubpixelPositioning)font_subpixel_positioning != TextServer::SUBPIXEL_POSITIONING_DISABLED) {
|
||||
warn += "\n" + TTR("Note: Subpixel positioning is selected, each of the glyphs might be pre-rendered for multiple subpixel offsets (up to 4x).");
|
||||
}
|
||||
if (warn.is_empty()) {
|
||||
|
|
@ -928,7 +959,7 @@ void DynamicFontImportSettingsDialog::_notification(int p_what) {
|
|||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
add_var->set_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
add_var->set_button_icon(get_editor_theme_icon(SNAME("Add")));
|
||||
label_warn->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
|
||||
} break;
|
||||
}
|
||||
|
|
@ -948,6 +979,7 @@ void DynamicFontImportSettingsDialog::_re_import() {
|
|||
main_settings["force_autohinter"] = import_settings_data->get("force_autohinter");
|
||||
main_settings["hinting"] = import_settings_data->get("hinting");
|
||||
main_settings["subpixel_positioning"] = import_settings_data->get("subpixel_positioning");
|
||||
main_settings["keep_rounding_remainders"] = import_settings_data->get("keep_rounding_remainders");
|
||||
main_settings["oversampling"] = import_settings_data->get("oversampling");
|
||||
main_settings["fallbacks"] = import_settings_data->get("fallbacks");
|
||||
main_settings["compress"] = import_settings_data->get("compress");
|
||||
|
|
@ -955,7 +987,7 @@ void DynamicFontImportSettingsDialog::_re_import() {
|
|||
Array configurations;
|
||||
for (TreeItem *vars_item = vars_list_root->get_first_child(); vars_item; vars_item = vars_item->get_next()) {
|
||||
Ref<DynamicFontImportSettingsData> import_variation_data = vars_item->get_metadata(0);
|
||||
ERR_FAIL_NULL(import_variation_data);
|
||||
ERR_FAIL_COND(import_variation_data.is_null());
|
||||
|
||||
Dictionary preload_config;
|
||||
preload_config["name"] = vars_item->get_text(0);
|
||||
|
|
@ -1073,6 +1105,34 @@ void DynamicFontImportSettingsDialog::open_settings(const String &p_path) {
|
|||
font_preview.instantiate();
|
||||
font_preview->set_data(font_data);
|
||||
|
||||
Array rids = font_preview->get_rids();
|
||||
if (!rids.is_empty()) {
|
||||
PackedInt32Array glyphs = TS->font_get_supported_glyphs(rids[0]);
|
||||
is_pixel = true;
|
||||
for (int32_t gl : glyphs) {
|
||||
Dictionary ct = TS->font_get_glyph_contours(rids[0], 16, gl);
|
||||
PackedInt32Array contours = ct["contours"];
|
||||
PackedVector3Array points = ct["points"];
|
||||
int prev_start = 0;
|
||||
for (int i = 0; i < contours.size(); i++) {
|
||||
for (int j = prev_start; j <= contours[i]; j++) {
|
||||
int next_point = (j < contours[i]) ? (j + 1) : prev_start;
|
||||
if ((points[j].z != TextServer::CONTOUR_CURVE_TAG_ON) || (!Math::is_equal_approx(points[j].x, points[next_point].x) && !Math::is_equal_approx(points[j].y, points[next_point].y))) {
|
||||
is_pixel = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
prev_start = contours[i] + 1;
|
||||
if (!is_pixel) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!is_pixel) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String font_name = vformat("%s (%s)", font_preview->get_font_name(), font_preview->get_font_style_name());
|
||||
String sample;
|
||||
static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
|
||||
|
|
@ -1106,7 +1166,7 @@ void DynamicFontImportSettingsDialog::open_settings(const String &p_path) {
|
|||
inspector_general->edit(nullptr);
|
||||
|
||||
text_settings_data.instantiate();
|
||||
ERR_FAIL_NULL(text_settings_data);
|
||||
ERR_FAIL_COND(text_settings_data.is_null());
|
||||
|
||||
text_settings_data->owner = this;
|
||||
|
||||
|
|
@ -1136,7 +1196,7 @@ void DynamicFontImportSettingsDialog::open_settings(const String &p_path) {
|
|||
|
||||
Ref<ConfigFile> config;
|
||||
config.instantiate();
|
||||
ERR_FAIL_NULL(config);
|
||||
ERR_FAIL_COND(config.is_null());
|
||||
|
||||
Error err = config->load(p_path + ".import");
|
||||
print_verbose("Loading import settings:");
|
||||
|
|
@ -1168,7 +1228,7 @@ void DynamicFontImportSettingsDialog::open_settings(const String &p_path) {
|
|||
|
||||
Ref<DynamicFontImportSettingsData> import_variation_data_custom;
|
||||
import_variation_data_custom.instantiate();
|
||||
ERR_FAIL_NULL(import_variation_data_custom);
|
||||
ERR_FAIL_COND(import_variation_data_custom.is_null());
|
||||
|
||||
import_variation_data_custom->owner = this;
|
||||
for (List<ResourceImporter::ImportOption>::Element *F = options_variations.front(); F; F = F->next()) {
|
||||
|
|
@ -1223,7 +1283,16 @@ void DynamicFontImportSettingsDialog::open_settings(const String &p_path) {
|
|||
font_preview->set_allow_system_fallback(import_settings_data->get("allow_system_fallback"));
|
||||
font_preview->set_force_autohinter(import_settings_data->get("force_autohinter"));
|
||||
font_preview->set_hinting((TextServer::Hinting)import_settings_data->get("hinting").operator int());
|
||||
font_preview->set_subpixel_positioning((TextServer::SubpixelPositioning)import_settings_data->get("subpixel_positioning").operator int());
|
||||
int font_subpixel_positioning = import_settings_data->get("subpixel_positioning").operator int();
|
||||
if (font_subpixel_positioning == 4 /* Auto (Except Pixel Fonts) */) {
|
||||
if (is_pixel) {
|
||||
font_subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
|
||||
} else {
|
||||
font_subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
|
||||
}
|
||||
}
|
||||
font_preview->set_subpixel_positioning((TextServer::SubpixelPositioning)font_subpixel_positioning);
|
||||
font_preview->set_keep_rounding_remainders(import_settings_data->get("keep_rounding_remainders"));
|
||||
font_preview->set_oversampling(import_settings_data->get("oversampling"));
|
||||
}
|
||||
font_preview_label->add_theme_font_override(SceneStringName(font), font_preview);
|
||||
|
|
@ -1255,7 +1324,8 @@ DynamicFontImportSettingsDialog::DynamicFontImportSettingsDialog() {
|
|||
options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "allow_system_fallback"), true));
|
||||
options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false));
|
||||
options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1));
|
||||
options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), 1));
|
||||
options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel,Auto (Except Pixel Fonts)"), 4));
|
||||
options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "keep_rounding_remainders"), true));
|
||||
options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
|
||||
|
||||
options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::NIL, "Metadata Overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP), Variant()));
|
||||
|
|
|
|||
|
|
@ -31,19 +31,13 @@
|
|||
#ifndef DYNAMIC_FONT_IMPORT_SETTINGS_H
|
||||
#define DYNAMIC_FONT_IMPORT_SETTINGS_H
|
||||
|
||||
#include "editor/import/resource_importer_dynamic_font.h"
|
||||
#include "core/io/resource_importer.h"
|
||||
|
||||
#include "core/templates/rb_set.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
#include "scene/gui/item_list.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/split_container.h"
|
||||
#include "scene/gui/subviewport_container.h"
|
||||
#include "scene/gui/tab_container.h"
|
||||
#include "scene/gui/text_edit.h"
|
||||
#include "scene/gui/tree.h"
|
||||
#include "scene/resources/font.h"
|
||||
#include "servers/text_server.h"
|
||||
|
||||
class DynamicFontImportSettingsDialog;
|
||||
|
||||
|
|
@ -90,6 +84,8 @@ class DynamicFontImportSettingsDialog : public ConfirmationDialog {
|
|||
List<ResourceImporter::ImportOption> options_variations;
|
||||
List<ResourceImporter::ImportOption> options_general;
|
||||
|
||||
bool is_pixel = false;
|
||||
|
||||
// Root layout
|
||||
Label *label_warn = nullptr;
|
||||
TabContainer *main_pages = nullptr;
|
||||
|
|
|
|||
|
|
@ -112,6 +112,14 @@ int EditorImportPlugin::get_import_order() const {
|
|||
ERR_FAIL_V_MSG(-1, "Unimplemented _get_import_order in add-on.");
|
||||
}
|
||||
|
||||
int EditorImportPlugin::get_format_version() const {
|
||||
int ret = 0;
|
||||
if (GDVIRTUAL_CALL(_get_format_version, ret)) {
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EditorImportPlugin::get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options, int p_preset) const {
|
||||
Array needed;
|
||||
needed.push_back("name");
|
||||
|
|
@ -163,7 +171,7 @@ bool EditorImportPlugin::get_option_visibility(const String &p_path, const Strin
|
|||
ERR_FAIL_V_MSG(false, "Unimplemented _get_option_visibility in add-on.");
|
||||
}
|
||||
|
||||
Error EditorImportPlugin::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Error EditorImportPlugin::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Dictionary options;
|
||||
TypedArray<String> platform_variants, gen_files;
|
||||
|
||||
|
|
@ -206,6 +214,7 @@ Error EditorImportPlugin::_append_import_external_resource(const String &p_file,
|
|||
}
|
||||
|
||||
Error EditorImportPlugin::append_import_external_resource(const String &p_file, const HashMap<StringName, Variant> &p_custom_options, const String &p_custom_importer, Variant p_generator_parameters) {
|
||||
ERR_FAIL_COND_V_MSG(!EditorFileSystem::get_singleton()->is_importing(), ERR_INVALID_PARAMETER, "Can only append files to import during a current reimport process.");
|
||||
return EditorFileSystem::get_singleton()->reimport_append(p_file, p_custom_options, p_custom_importer, p_generator_parameters);
|
||||
}
|
||||
|
||||
|
|
@ -220,6 +229,7 @@ void EditorImportPlugin::_bind_methods() {
|
|||
GDVIRTUAL_BIND(_get_resource_type)
|
||||
GDVIRTUAL_BIND(_get_priority)
|
||||
GDVIRTUAL_BIND(_get_import_order)
|
||||
GDVIRTUAL_BIND(_get_format_version)
|
||||
GDVIRTUAL_BIND(_get_option_visibility, "path", "option_name", "options")
|
||||
GDVIRTUAL_BIND(_import, "source_file", "save_path", "options", "platform_variants", "gen_files");
|
||||
GDVIRTUAL_BIND(_can_import_threaded);
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ protected:
|
|||
GDVIRTUAL0RC(String, _get_resource_type)
|
||||
GDVIRTUAL0RC(float, _get_priority)
|
||||
GDVIRTUAL0RC(int, _get_import_order)
|
||||
GDVIRTUAL0RC(int, _get_format_version)
|
||||
GDVIRTUAL3RC(bool, _get_option_visibility, String, StringName, Dictionary)
|
||||
GDVIRTUAL5RC(Error, _import, String, String, Dictionary, TypedArray<String>, TypedArray<String>)
|
||||
GDVIRTUAL0RC(bool, _can_import_threaded)
|
||||
|
|
@ -67,9 +68,10 @@ public:
|
|||
virtual String get_resource_type() const override;
|
||||
virtual float get_priority() const override;
|
||||
virtual int get_import_order() const override;
|
||||
virtual int get_format_version() const override;
|
||||
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const override;
|
||||
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata = nullptr) override;
|
||||
virtual bool can_import_threaded() const override;
|
||||
Error append_import_external_resource(const String &p_file, const HashMap<StringName, Variant> &p_custom_options = HashMap<StringName, Variant>(), const String &p_custom_importer = String(), Variant p_generator_parameters = Variant());
|
||||
};
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ void ResourceImporterBitMap::get_import_options(const String &p_path, List<Impor
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.5));
|
||||
}
|
||||
|
||||
Error ResourceImporterBitMap::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Error ResourceImporterBitMap::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
int create_from = p_options["create_from"];
|
||||
float threshold = p_options["threshold"];
|
||||
Ref<Image> image;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ public:
|
|||
|
||||
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
|
||||
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
virtual bool can_import_threaded() const override { return true; }
|
||||
|
||||
ResourceImporterBitMap();
|
||||
~ResourceImporterBitMap();
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ void ResourceImporterBMFont::get_import_options(const String &p_path, List<Impor
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "scaling_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled (Integer),Enabled (Fractional)"), TextServer::FIXED_SIZE_SCALE_ENABLED));
|
||||
}
|
||||
|
||||
Error ResourceImporterBMFont::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Error ResourceImporterBMFont::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
print_verbose("Importing BMFont font from: " + p_source_file);
|
||||
|
||||
Array fallbacks = p_options["fallbacks"];
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ public:
|
|||
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
|
||||
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
virtual bool can_import_threaded() const override { return true; }
|
||||
|
||||
ResourceImporterBMFont();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
#include "core/io/file_access.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/string/optimized_translation.h"
|
||||
#include "core/string/translation.h"
|
||||
#include "core/string/translation_server.h"
|
||||
|
||||
String ResourceImporterCSVTranslation::get_importer_name() const {
|
||||
return "csv_translation";
|
||||
|
|
@ -72,7 +72,7 @@ void ResourceImporterCSVTranslation::get_import_options(const String &p_path, Li
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "delimiter", PROPERTY_HINT_ENUM, "Comma,Semicolon,Tab"), 0));
|
||||
}
|
||||
|
||||
Error ResourceImporterCSVTranslation::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Error ResourceImporterCSVTranslation::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
bool compress = p_options["compress"];
|
||||
|
||||
String delimiter;
|
||||
|
|
@ -147,6 +147,9 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const
|
|||
if (r_gen_files) {
|
||||
r_gen_files->push_back(save_path);
|
||||
}
|
||||
|
||||
ResourceUID::ID save_id = hash64_murmur3_64(translations[i]->get_locale().hash64(), p_source_id);
|
||||
ResourceSaver::set_uid(save_path, save_id);
|
||||
}
|
||||
|
||||
return OK;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,9 @@ public:
|
|||
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
|
||||
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
virtual bool can_import_threaded() const override { return true; }
|
||||
|
||||
ResourceImporterCSVTranslation();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,8 +36,6 @@
|
|||
#include "scene/resources/font.h"
|
||||
#include "servers/text_server.h"
|
||||
|
||||
#include "modules/modules_enabled.gen.h" // For freetype.
|
||||
|
||||
String ResourceImporterDynamicFont::get_importer_name() const {
|
||||
return "font_data_dynamic";
|
||||
}
|
||||
|
|
@ -48,7 +46,6 @@ String ResourceImporterDynamicFont::get_visible_name() const {
|
|||
|
||||
void ResourceImporterDynamicFont::get_recognized_extensions(List<String> *p_extensions) const {
|
||||
if (p_extensions) {
|
||||
#ifdef MODULE_FREETYPE_ENABLED
|
||||
p_extensions->push_back("ttf");
|
||||
p_extensions->push_back("ttc");
|
||||
p_extensions->push_back("otf");
|
||||
|
|
@ -57,7 +54,6 @@ void ResourceImporterDynamicFont::get_recognized_extensions(List<String> *p_exte
|
|||
p_extensions->push_back("woff2");
|
||||
p_extensions->push_back("pfb");
|
||||
p_extensions->push_back("pfm");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,6 +81,9 @@ bool ResourceImporterDynamicFont::get_option_visibility(const String &p_path, co
|
|||
if (p_option == "subpixel_positioning" && bool(p_options["multichannel_signed_distance_field"])) {
|
||||
return false;
|
||||
}
|
||||
if (p_option == "keep_rounding_remainders" && bool(p_options["multichannel_signed_distance_field"])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +117,8 @@ void ResourceImporterDynamicFont::get_import_options(const String &p_path, List<
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "allow_system_fallback"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), 1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel,Auto (Except Pixel Fonts)"), 4));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "keep_rounding_remainders"), true));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
|
||||
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::NIL, "Fallbacks", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP), Variant()));
|
||||
|
|
@ -141,7 +141,7 @@ void ResourceImporterDynamicFont::show_advanced_options(const String &p_path) {
|
|||
DynamicFontImportSettingsDialog::get_singleton()->open_settings(p_path);
|
||||
}
|
||||
|
||||
Error ResourceImporterDynamicFont::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Error ResourceImporterDynamicFont::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
print_verbose("Importing dynamic font from: " + p_source_file);
|
||||
|
||||
int antialiasing = p_options["antialiasing"];
|
||||
|
|
@ -156,6 +156,7 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str
|
|||
bool allow_system_fallback = p_options["allow_system_fallback"];
|
||||
int hinting = p_options["hinting"];
|
||||
int subpixel_positioning = p_options["subpixel_positioning"];
|
||||
bool keep_rounding_remainders = p_options["keep_rounding_remainders"];
|
||||
real_t oversampling = p_options["oversampling"];
|
||||
Array fallbacks = p_options["fallbacks"];
|
||||
|
||||
|
|
@ -176,11 +177,48 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str
|
|||
font->set_fixed_size(0);
|
||||
font->set_force_autohinter(autohinter);
|
||||
font->set_allow_system_fallback(allow_system_fallback);
|
||||
font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning);
|
||||
font->set_hinting((TextServer::Hinting)hinting);
|
||||
font->set_oversampling(oversampling);
|
||||
font->set_fallbacks(fallbacks);
|
||||
|
||||
if (subpixel_positioning == 4 /* Auto (Except Pixel Fonts) */) {
|
||||
Array rids = font->get_rids();
|
||||
if (!rids.is_empty()) {
|
||||
PackedInt32Array glyphs = TS->font_get_supported_glyphs(rids[0]);
|
||||
bool is_pixel = true;
|
||||
for (int32_t gl : glyphs) {
|
||||
Dictionary ct = TS->font_get_glyph_contours(rids[0], 16, gl);
|
||||
PackedInt32Array contours = ct["contours"];
|
||||
PackedVector3Array points = ct["points"];
|
||||
int prev_start = 0;
|
||||
for (int i = 0; i < contours.size(); i++) {
|
||||
for (int j = prev_start; j <= contours[i]; j++) {
|
||||
int next_point = (j < contours[i]) ? (j + 1) : prev_start;
|
||||
if ((points[j].z != TextServer::CONTOUR_CURVE_TAG_ON) || (!Math::is_equal_approx(points[j].x, points[next_point].x) && !Math::is_equal_approx(points[j].y, points[next_point].y))) {
|
||||
is_pixel = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
prev_start = contours[i] + 1;
|
||||
if (!is_pixel) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!is_pixel) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_pixel && !glyphs.is_empty()) {
|
||||
print_line(vformat("%s: Pixel font detected, disabling subpixel positioning.", p_source_file));
|
||||
subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
|
||||
} else {
|
||||
subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
|
||||
}
|
||||
}
|
||||
}
|
||||
font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning);
|
||||
font->set_keep_rounding_remainders(keep_rounding_remainders);
|
||||
|
||||
Dictionary langs = p_options["language_support"];
|
||||
for (int i = 0; i < langs.size(); i++) {
|
||||
String key = langs.get_key_at_index(i);
|
||||
|
|
|
|||
|
|
@ -58,7 +58,9 @@ public:
|
|||
bool has_advanced_options() const override;
|
||||
void show_advanced_options(const String &p_path) override;
|
||||
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
virtual bool can_import_threaded() const override { return true; }
|
||||
|
||||
ResourceImporterDynamicFont();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@
|
|||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
String ResourceImporterImage::get_importer_name() const {
|
||||
return "image";
|
||||
|
|
@ -70,7 +68,7 @@ String ResourceImporterImage::get_preset_name(int p_idx) const {
|
|||
void ResourceImporterImage::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
|
||||
}
|
||||
|
||||
Error ResourceImporterImage::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Error ResourceImporterImage::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file from path '" + p_source_file + "'.");
|
||||
|
|
|
|||
|
|
@ -50,7 +50,9 @@ public:
|
|||
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
|
||||
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
virtual bool can_import_threaded() const override { return true; }
|
||||
|
||||
ResourceImporterImage();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ void ResourceImporterImageFont::get_import_options(const String &p_path, List<Im
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "scaling_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled (Integer),Enabled (Fractional)"), TextServer::FIXED_SIZE_SCALE_ENABLED));
|
||||
}
|
||||
|
||||
Error ResourceImporterImageFont::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Error ResourceImporterImageFont::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
print_verbose("Importing image font from: " + p_source_file);
|
||||
|
||||
int columns = p_options["columns"];
|
||||
|
|
@ -112,6 +112,7 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
|
|||
font->set_multichannel_signed_distance_field(false);
|
||||
font->set_fixed_size(chr_height);
|
||||
font->set_subpixel_positioning(TextServer::SUBPIXEL_POSITIONING_DISABLED);
|
||||
font->set_keep_rounding_remainders(true);
|
||||
font->set_force_autohinter(false);
|
||||
font->set_allow_system_fallback(false);
|
||||
font->set_hinting(TextServer::HINTING_NONE);
|
||||
|
|
@ -158,7 +159,7 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
|
|||
c++; // Skip "+".
|
||||
continue;
|
||||
}
|
||||
} else if (range[c] == '0' && (c <= range.length() - 2) && range[c + 1] == 'x') {
|
||||
} else if (range[c] == '0' && (c <= range.length() - 2) && (range[c + 1] == 'x' || range[c + 1] == 'X')) {
|
||||
// Read hexadecimal value, start.
|
||||
token = String();
|
||||
if (step == STEP_START_BEGIN) {
|
||||
|
|
@ -199,7 +200,7 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
|
|||
case STEP_OFF_Y_BEGIN: {
|
||||
// Read advance and offset.
|
||||
if (range[c] == ' ') {
|
||||
int next = range.find(" ", c + 1);
|
||||
int next = range.find_char(' ', c + 1);
|
||||
if (next < c) {
|
||||
next = range.length();
|
||||
}
|
||||
|
|
@ -293,18 +294,20 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
|
|||
}
|
||||
String from_tokens;
|
||||
for (int i = 0; i < kp_tokens[0].length(); i++) {
|
||||
if (i <= kp_tokens[0].length() - 6 && kp_tokens[0][i] == '\\' && kp_tokens[0][i + 1] == 'u') {
|
||||
if (i <= kp_tokens[0].length() - 6 && kp_tokens[0][i] == '\\' && kp_tokens[0][i + 1] == 'u' && is_hex_digit(kp_tokens[0][i + 2]) && is_hex_digit(kp_tokens[0][i + 3]) && is_hex_digit(kp_tokens[0][i + 4]) && is_hex_digit(kp_tokens[0][i + 5])) {
|
||||
char32_t charcode = kp_tokens[0].substr(i + 2, 4).hex_to_int();
|
||||
from_tokens += charcode;
|
||||
i += 5;
|
||||
} else {
|
||||
from_tokens += kp_tokens[0][i];
|
||||
}
|
||||
}
|
||||
String to_tokens;
|
||||
for (int i = 0; i < kp_tokens[1].length(); i++) {
|
||||
if (i <= kp_tokens[1].length() - 6 && kp_tokens[1][i] == '\\' && kp_tokens[1][i + 1] == 'u') {
|
||||
if (i <= kp_tokens[1].length() - 6 && kp_tokens[1][i] == '\\' && kp_tokens[1][i + 1] == 'u' && is_hex_digit(kp_tokens[1][i + 2]) && is_hex_digit(kp_tokens[1][i + 3]) && is_hex_digit(kp_tokens[1][i + 4]) && is_hex_digit(kp_tokens[1][i + 5])) {
|
||||
char32_t charcode = kp_tokens[1].substr(i + 2, 4).hex_to_int();
|
||||
to_tokens += charcode;
|
||||
i += 5;
|
||||
} else {
|
||||
to_tokens += kp_tokens[1][i];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ public:
|
|||
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
|
||||
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
virtual bool can_import_threaded() const override { return true; }
|
||||
|
||||
ResourceImporterImageFont();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,15 +32,12 @@
|
|||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/io/config_file.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/import/resource_importer_texture.h"
|
||||
#include "editor/import/resource_importer_texture_settings.h"
|
||||
#include "scene/resources/compressed_texture.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
String ResourceImporterLayeredTexture::get_importer_name() const {
|
||||
switch (mode) {
|
||||
|
|
@ -276,6 +273,10 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons
|
|||
f->store_32(0);
|
||||
f->store_32(0);
|
||||
|
||||
if ((p_compress_mode == COMPRESS_LOSSLESS || p_compress_mode == COMPRESS_LOSSY) && p_images[0]->get_format() >= Image::FORMAT_RF) {
|
||||
p_compress_mode = COMPRESS_VRAM_UNCOMPRESSED; // These can't go as lossy.
|
||||
}
|
||||
|
||||
for (int i = 0; i < p_images.size(); i++) {
|
||||
ResourceImporterTexture::save_to_ctex_format(f, p_images[i], ResourceImporterTexture::CompressMode(p_compress_mode), used_channels, p_vram_compression, p_lossy);
|
||||
}
|
||||
|
|
@ -285,7 +286,7 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons
|
|||
}
|
||||
}
|
||||
|
||||
Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Error ResourceImporterLayeredTexture::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
int compress_mode = p_options["compress/mode"];
|
||||
float lossy = p_options["compress/lossy_quality"];
|
||||
bool high_quality = p_options["compress/high_quality"];
|
||||
|
|
@ -335,14 +336,7 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
|
|||
return err;
|
||||
}
|
||||
|
||||
if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) {
|
||||
//basis universal does not support float formats, fall back
|
||||
compress_mode = COMPRESS_VRAM_COMPRESSED;
|
||||
}
|
||||
|
||||
if (compress_mode == COMPRESS_VRAM_COMPRESSED) {
|
||||
mipmaps = true;
|
||||
|
||||
//if using video ram, optimize
|
||||
if (channel_pack == 0) {
|
||||
//remove alpha if not needed, so compression is more efficient
|
||||
|
|
@ -433,22 +427,20 @@ String ResourceImporterLayeredTexture::get_import_settings_string() const {
|
|||
return s;
|
||||
}
|
||||
|
||||
bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_path) const {
|
||||
bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const {
|
||||
//will become invalid if formats are missing to import
|
||||
Dictionary meta = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path);
|
||||
|
||||
if (!meta.has("vram_texture")) {
|
||||
if (!p_meta.has("vram_texture")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vram = meta["vram_texture"];
|
||||
bool vram = p_meta["vram_texture"];
|
||||
if (!vram) {
|
||||
return true; //do not care about non vram
|
||||
}
|
||||
|
||||
Vector<String> formats_imported;
|
||||
if (meta.has("imported_formats")) {
|
||||
formats_imported = meta["imported_formats"];
|
||||
if (p_meta.has("imported_formats")) {
|
||||
formats_imported = p_meta["imported_formats"];
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
|
|
@ -497,7 +489,7 @@ void ResourceImporterLayeredTexture::_check_compress_ctex(const String &p_source
|
|||
_save_tex(*r_texture_import->slices, r_texture_import->save_path + "." + extension, r_texture_import->compress_mode, r_texture_import->lossy, Image::COMPRESS_S3TC /* IGNORED */, *r_texture_import->csource, r_texture_import->used_channels, r_texture_import->mipmaps, false);
|
||||
return;
|
||||
}
|
||||
// Must import in all formats, in order of priority (so platform choses the best supported one. IE, etc2 over etc).
|
||||
// Must import in all formats, in order of priority (so platform chooses the best supported one. IE, etc2 over etc).
|
||||
// Android, GLES 2.x
|
||||
|
||||
const bool can_s3tc_bptc = ResourceImporterTextureSettings::should_import_s3tc_bptc();
|
||||
|
|
@ -512,7 +504,7 @@ void ResourceImporterLayeredTexture::_check_compress_ctex(const String &p_source
|
|||
}
|
||||
|
||||
bool can_compress_hdr = r_texture_import->hdr_compression > 0;
|
||||
ERR_FAIL_NULL(r_texture_import->image);
|
||||
ERR_FAIL_COND(r_texture_import->image.is_null());
|
||||
bool is_hdr = (r_texture_import->image->get_format() >= Image::FORMAT_RF && r_texture_import->image->get_format() <= Image::FORMAT_RGBE9995);
|
||||
ERR_FAIL_NULL(r_texture_import->slices);
|
||||
// Can compress hdr, but hdr with alpha is not compressible.
|
||||
|
|
|
|||
|
|
@ -112,11 +112,13 @@ public:
|
|||
|
||||
void _save_tex(Vector<Ref<Image>> p_images, const String &p_to_path, int p_compress_mode, float p_lossy, Image::CompressMode p_vram_compression, Image::CompressSource p_csource, Image::UsedChannels used_channels, bool p_mipmaps, bool p_force_po2);
|
||||
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
virtual bool are_import_settings_valid(const String &p_path) const override;
|
||||
virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const override;
|
||||
virtual String get_import_settings_string() const override;
|
||||
|
||||
virtual bool can_import_threaded() const override { return true; }
|
||||
|
||||
void set_mode(Mode p_mode) { mode = p_mode; }
|
||||
|
||||
ResourceImporterLayeredTexture(bool p_singleton = false);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#include "resource_importer_shader_file.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/plugins/shader_file_editor_plugin.h"
|
||||
|
|
@ -89,15 +88,15 @@ static String _include_function(const String &p_path, void *userpointer) {
|
|||
return file_inc->get_as_utf8_string();
|
||||
}
|
||||
|
||||
Error ResourceImporterShaderFile::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Error ResourceImporterShaderFile::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
/* STEP 1, Read shader code */
|
||||
ERR_FAIL_COND_V_EDMSG((OS::get_singleton()->get_current_rendering_method() == "gl_compatibility"), ERR_UNAVAILABLE, "Cannot import custom .glsl shaders when using the gl_compatibility rendering_method. Please switch to the forward_plus or mobile rendering methods to use custom shaders.");
|
||||
ERR_FAIL_COND_V_EDMSG((OS::get_singleton()->get_current_rendering_method() == "gl_compatibility"), ERR_UNAVAILABLE, "Cannot import custom .glsl shaders when using the Compatibility renderer. Please switch to the Forward+ or Mobile renderer to use custom shaders.");
|
||||
ERR_FAIL_COND_V_EDMSG((DisplayServer::get_singleton()->get_name() == "headless"), ERR_UNAVAILABLE, "Cannot import custom .glsl shaders when running in headless mode.");
|
||||
|
||||
Error err;
|
||||
Ref<FileAccess> file = FileAccess::open(p_source_file, FileAccess::READ, &err);
|
||||
ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN);
|
||||
ERR_FAIL_COND_V(!file.is_valid(), ERR_CANT_OPEN);
|
||||
ERR_FAIL_COND_V(file.is_null(), ERR_CANT_OPEN);
|
||||
|
||||
String file_txt = file->get_as_utf8_string();
|
||||
Ref<RDShaderFile> shader_file;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,9 @@ public:
|
|||
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
|
||||
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
virtual bool can_import_threaded() const override { return true; }
|
||||
|
||||
ResourceImporterShaderFile();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
#include "core/io/image_loader.h"
|
||||
#include "core/version.h"
|
||||
#include "editor/editor_file_system.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/gui/editor_toaster.h"
|
||||
#include "editor/import/resource_importer_texture_settings.h"
|
||||
#include "editor/themes/editor_scale.h"
|
||||
|
|
@ -46,7 +45,6 @@ void ResourceImporterTexture::_texture_reimport_roughness(const Ref<CompressedTe
|
|||
ERR_FAIL_COND(p_tex.is_null());
|
||||
|
||||
MutexLock lock(singleton->mutex);
|
||||
|
||||
StringName path = p_tex->get_path();
|
||||
|
||||
if (!singleton->make_flags.has(path)) {
|
||||
|
|
@ -62,7 +60,6 @@ void ResourceImporterTexture::_texture_reimport_3d(const Ref<CompressedTexture2D
|
|||
ERR_FAIL_COND(p_tex.is_null());
|
||||
|
||||
MutexLock lock(singleton->mutex);
|
||||
|
||||
StringName path = p_tex->get_path();
|
||||
|
||||
if (!singleton->make_flags.has(path)) {
|
||||
|
|
@ -76,7 +73,6 @@ void ResourceImporterTexture::_texture_reimport_normal(const Ref<CompressedTextu
|
|||
ERR_FAIL_COND(p_tex.is_null());
|
||||
|
||||
MutexLock lock(singleton->mutex);
|
||||
|
||||
StringName path = p_tex->get_path();
|
||||
|
||||
if (!singleton->make_flags.has(path)) {
|
||||
|
|
@ -86,79 +82,87 @@ void ResourceImporterTexture::_texture_reimport_normal(const Ref<CompressedTextu
|
|||
singleton->make_flags[path].flags |= MAKE_NORMAL_FLAG;
|
||||
}
|
||||
|
||||
inline void ResourceImporterTexture::_print_callback_message(const String &p_message) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorToaster::get_singleton()->popup_str(p_message);
|
||||
#endif
|
||||
print_line(p_message);
|
||||
}
|
||||
|
||||
void ResourceImporterTexture::update_imports() {
|
||||
if (EditorFileSystem::get_singleton()->is_scanning() || EditorFileSystem::get_singleton()->is_importing()) {
|
||||
return; // do nothing for now
|
||||
return; // Don't update when EditorFileSystem is doing something else.
|
||||
}
|
||||
|
||||
MutexLock lock(mutex);
|
||||
Vector<String> to_reimport;
|
||||
{
|
||||
if (make_flags.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const KeyValue<StringName, MakeInfo> &E : make_flags) {
|
||||
Ref<ConfigFile> cf;
|
||||
cf.instantiate();
|
||||
String src_path = String(E.key) + ".import";
|
||||
|
||||
Error err = cf->load(src_path);
|
||||
ERR_CONTINUE(err != OK);
|
||||
|
||||
bool changed = false;
|
||||
|
||||
if (E.value.flags & MAKE_NORMAL_FLAG && int(cf->get_value("params", "compress/normal_map")) == 0) {
|
||||
String message = vformat(TTR("%s: Texture detected as used as a normal map in 3D. Enabling red-green texture compression to reduce memory usage (blue channel is discarded)."), String(E.key));
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorToaster::get_singleton()->popup_str(message);
|
||||
#endif
|
||||
print_line(message);
|
||||
cf->set_value("params", "compress/normal_map", 1);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (E.value.flags & MAKE_ROUGHNESS_FLAG && int(cf->get_value("params", "roughness/mode")) == 0) {
|
||||
String message = vformat(TTR("%s: Texture detected as used as a roughness map in 3D. Enabling roughness limiter based on the detected associated normal map at %s."), String(E.key), E.value.normal_path_for_roughness);
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorToaster::get_singleton()->popup_str(message);
|
||||
#endif
|
||||
print_line(message);
|
||||
cf->set_value("params", "roughness/mode", E.value.channel_for_roughness + 2);
|
||||
cf->set_value("params", "roughness/src_normal", E.value.normal_path_for_roughness);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (E.value.flags & MAKE_3D_FLAG && bool(cf->get_value("params", "detect_3d/compress_to"))) {
|
||||
const int compress_to = cf->get_value("params", "detect_3d/compress_to");
|
||||
String compress_string;
|
||||
cf->set_value("params", "detect_3d/compress_to", 0);
|
||||
if (compress_to == 1) {
|
||||
cf->set_value("params", "compress/mode", COMPRESS_VRAM_COMPRESSED);
|
||||
compress_string = "VRAM Compressed (S3TC/ETC/BPTC)";
|
||||
} else if (compress_to == 2) {
|
||||
cf->set_value("params", "compress/mode", COMPRESS_BASIS_UNIVERSAL);
|
||||
compress_string = "Basis Universal";
|
||||
}
|
||||
String message = vformat(TTR("%s: Texture detected as used in 3D. Enabling mipmap generation and setting the texture compression mode to %s."), String(E.key), compress_string);
|
||||
#ifdef TOOLS_ENABLED
|
||||
EditorToaster::get_singleton()->popup_str(message);
|
||||
#endif
|
||||
print_line(message);
|
||||
cf->set_value("params", "mipmaps/generate", true);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
cf->save(src_path);
|
||||
to_reimport.push_back(E.key);
|
||||
}
|
||||
}
|
||||
|
||||
make_flags.clear();
|
||||
if (make_flags.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (to_reimport.size()) {
|
||||
for (const KeyValue<StringName, MakeInfo> &E : make_flags) {
|
||||
Ref<ConfigFile> cf;
|
||||
cf.instantiate();
|
||||
String src_path = String(E.key) + ".import";
|
||||
|
||||
Error err = cf->load(src_path);
|
||||
ERR_CONTINUE(err != OK);
|
||||
|
||||
bool changed = false;
|
||||
|
||||
if (E.value.flags & MAKE_NORMAL_FLAG && int(cf->get_value("params", "compress/normal_map")) == 0) {
|
||||
_print_callback_message(
|
||||
vformat(TTR("%s: Texture detected as used as a normal map in 3D. Enabling red-green texture compression to reduce memory usage (blue channel is discarded)."),
|
||||
String(E.key)));
|
||||
|
||||
cf->set_value("params", "compress/normal_map", 1);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (E.value.flags & MAKE_ROUGHNESS_FLAG && int(cf->get_value("params", "roughness/mode")) == 0) {
|
||||
_print_callback_message(
|
||||
vformat(TTR("%s: Texture detected as used as a roughness map in 3D. Enabling roughness limiter based on the detected associated normal map at %s."),
|
||||
String(E.key), E.value.normal_path_for_roughness));
|
||||
|
||||
cf->set_value("params", "roughness/mode", E.value.channel_for_roughness + 2);
|
||||
cf->set_value("params", "roughness/src_normal", E.value.normal_path_for_roughness);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (E.value.flags & MAKE_3D_FLAG && bool(cf->get_value("params", "detect_3d/compress_to"))) {
|
||||
const int compress_to = cf->get_value("params", "detect_3d/compress_to");
|
||||
|
||||
// 3D detected, disable the callback.
|
||||
cf->set_value("params", "detect_3d/compress_to", 0);
|
||||
|
||||
String compress_string;
|
||||
if (compress_to == 1) {
|
||||
cf->set_value("params", "compress/mode", COMPRESS_VRAM_COMPRESSED);
|
||||
compress_string = "VRAM Compressed (S3TC/ETC/BPTC)";
|
||||
|
||||
} else if (compress_to == 2) {
|
||||
cf->set_value("params", "compress/mode", COMPRESS_BASIS_UNIVERSAL);
|
||||
compress_string = "Basis Universal";
|
||||
}
|
||||
|
||||
_print_callback_message(
|
||||
vformat(TTR("%s: Texture detected as used in 3D. Enabling mipmap generation and setting the texture compression mode to %s."),
|
||||
String(E.key), compress_string));
|
||||
|
||||
cf->set_value("params", "mipmaps/generate", true);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
cf->save(src_path);
|
||||
to_reimport.push_back(E.key);
|
||||
}
|
||||
}
|
||||
|
||||
make_flags.clear();
|
||||
|
||||
if (!to_reimport.is_empty()) {
|
||||
EditorFileSystem::get_singleton()->reimport_files(to_reimport);
|
||||
}
|
||||
}
|
||||
|
|
@ -189,21 +193,25 @@ bool ResourceImporterTexture::get_option_visibility(const String &p_path, const
|
|||
if (compress_mode != COMPRESS_VRAM_COMPRESSED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (p_option == "compress/lossy_quality") {
|
||||
int compress_mode = int(p_options["compress/mode"]);
|
||||
if (compress_mode != COMPRESS_LOSSY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (p_option == "compress/hdr_mode") {
|
||||
int compress_mode = int(p_options["compress/mode"]);
|
||||
if (compress_mode < COMPRESS_VRAM_COMPRESSED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (p_option == "compress/normal_map") {
|
||||
int compress_mode = int(p_options["compress/mode"]);
|
||||
if (compress_mode == COMPRESS_LOSSLESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (p_option == "mipmaps/limit") {
|
||||
return p_options["mipmaps/generate"];
|
||||
}
|
||||
|
|
@ -241,7 +249,10 @@ void ResourceImporterTexture::get_import_options(const String &p_path, List<Impo
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/normal_map_invert_y"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/hdr_as_srgb"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/hdr_clamp_exposure"), false));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "process/size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0));
|
||||
|
||||
// Maximum bound is the highest allowed value for lossy compression (the lowest common denominator).
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "process/size_limit", PROPERTY_HINT_RANGE, "0,16383,1"), 0));
|
||||
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "detect_3d/compress_to", PROPERTY_HINT_ENUM, "Disabled,VRAM Compressed,Basis Universal"), (p_preset == PRESET_DETECT) ? 1 : 0));
|
||||
|
||||
// Do path based customization only if a path was passed.
|
||||
|
|
@ -257,9 +268,9 @@ void ResourceImporterTexture::get_import_options(const String &p_path, List<Impo
|
|||
void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality) {
|
||||
switch (p_compress_mode) {
|
||||
case COMPRESS_LOSSLESS: {
|
||||
bool lossless_force_png = GLOBAL_GET("rendering/textures/lossless_compression/force_png") ||
|
||||
!Image::_webp_mem_loader_func; // WebP module disabled.
|
||||
bool use_webp = !lossless_force_png && p_image->get_width() <= 16383 && p_image->get_height() <= 16383; // WebP has a size limit
|
||||
bool lossless_force_png = GLOBAL_GET("rendering/textures/lossless_compression/force_png") || !Image::_webp_mem_loader_func; // WebP module disabled or png is forced.
|
||||
bool use_webp = !lossless_force_png && p_image->get_width() <= 16383 && p_image->get_height() <= 16383; // WebP has a size limit.
|
||||
|
||||
f->store_32(use_webp ? CompressedTexture2D::DATA_FORMAT_WEBP : CompressedTexture2D::DATA_FORMAT_PNG);
|
||||
f->store_16(p_image->get_width());
|
||||
f->store_16(p_image->get_height());
|
||||
|
|
@ -273,11 +284,11 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I
|
|||
} else {
|
||||
data = Image::png_packer(i ? p_image->get_image_from_mipmap(i) : p_image);
|
||||
}
|
||||
int data_len = data.size();
|
||||
f->store_32(data_len);
|
||||
|
||||
const uint8_t *r = data.ptr();
|
||||
f->store_buffer(r, data_len);
|
||||
const uint64_t data_size = data.size();
|
||||
|
||||
f->store_32(data_size);
|
||||
f->store_buffer(data.ptr(), data_size);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
|
@ -290,16 +301,15 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I
|
|||
|
||||
for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
|
||||
Vector<uint8_t> data = Image::webp_lossy_packer(i ? p_image->get_image_from_mipmap(i) : p_image, p_lossy_quality);
|
||||
int data_len = data.size();
|
||||
f->store_32(data_len);
|
||||
const uint64_t data_size = data.size();
|
||||
|
||||
const uint8_t *r = data.ptr();
|
||||
f->store_buffer(r, data_len);
|
||||
f->store_32(data_size);
|
||||
f->store_buffer(data.ptr(), data_size);
|
||||
}
|
||||
|
||||
} break;
|
||||
case COMPRESS_VRAM_COMPRESSED: {
|
||||
Ref<Image> image = p_image->duplicate();
|
||||
|
||||
image->compress_from_channels(p_compress_format, p_channels);
|
||||
|
||||
f->store_32(CompressedTexture2D::DATA_FORMAT_IMAGE);
|
||||
|
|
@ -307,11 +317,8 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I
|
|||
f->store_16(image->get_height());
|
||||
f->store_32(image->get_mipmap_count());
|
||||
f->store_32(image->get_format());
|
||||
f->store_buffer(image->get_data());
|
||||
|
||||
Vector<uint8_t> data = image->get_data();
|
||||
int dl = data.size();
|
||||
const uint8_t *r = data.ptr();
|
||||
f->store_buffer(r, dl);
|
||||
} break;
|
||||
case COMPRESS_VRAM_UNCOMPRESSED: {
|
||||
f->store_32(CompressedTexture2D::DATA_FORMAT_IMAGE);
|
||||
|
|
@ -319,12 +326,7 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I
|
|||
f->store_16(p_image->get_height());
|
||||
f->store_32(p_image->get_mipmap_count());
|
||||
f->store_32(p_image->get_format());
|
||||
|
||||
Vector<uint8_t> data = p_image->get_data();
|
||||
int dl = data.size();
|
||||
const uint8_t *r = data.ptr();
|
||||
|
||||
f->store_buffer(r, dl);
|
||||
f->store_buffer(p_image->get_data());
|
||||
|
||||
} break;
|
||||
case COMPRESS_BASIS_UNIVERSAL: {
|
||||
|
|
@ -333,11 +335,12 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I
|
|||
f->store_16(p_image->get_height());
|
||||
f->store_32(p_image->get_mipmap_count());
|
||||
f->store_32(p_image->get_format());
|
||||
|
||||
Vector<uint8_t> data = Image::basis_universal_packer(p_image, p_channels);
|
||||
int data_len = data.size();
|
||||
f->store_32(data_len);
|
||||
const uint8_t *r = data.ptr();
|
||||
f->store_buffer(r, data_len);
|
||||
const uint64_t data_size = data.size();
|
||||
|
||||
f->store_32(data_size);
|
||||
f->store_buffer(data.ptr(), data_size);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -345,14 +348,17 @@ void ResourceImporterTexture::save_to_ctex_format(Ref<FileAccess> f, const Ref<I
|
|||
void ResourceImporterTexture::_save_ctex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_roughness, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_to_path, FileAccess::WRITE);
|
||||
ERR_FAIL_COND(f.is_null());
|
||||
|
||||
// Godot Streamable Texture 2D.
|
||||
f->store_8('G');
|
||||
f->store_8('S');
|
||||
f->store_8('T');
|
||||
f->store_8('2'); //godot streamable texture 2D
|
||||
f->store_8('2');
|
||||
|
||||
//format version
|
||||
// Current format version.
|
||||
f->store_32(CompressedTexture2D::FORMAT_VERSION);
|
||||
//texture may be resized later, so original size must be saved first
|
||||
|
||||
// Texture may be resized later, so original size must be saved first.
|
||||
f->store_32(p_image->get_width());
|
||||
f->store_32(p_image->get_height());
|
||||
|
||||
|
|
@ -361,7 +367,7 @@ void ResourceImporterTexture::_save_ctex(const Ref<Image> &p_image, const String
|
|||
flags |= CompressedTexture2D::FORMAT_BIT_STREAM;
|
||||
}
|
||||
if (p_mipmaps) {
|
||||
flags |= CompressedTexture2D::FORMAT_BIT_HAS_MIPMAPS; //mipmaps bit
|
||||
flags |= CompressedTexture2D::FORMAT_BIT_HAS_MIPMAPS;
|
||||
}
|
||||
if (p_detect_3d) {
|
||||
flags |= CompressedTexture2D::FORMAT_BIT_DETECT_3D;
|
||||
|
|
@ -375,41 +381,49 @@ void ResourceImporterTexture::_save_ctex(const Ref<Image> &p_image, const String
|
|||
|
||||
f->store_32(flags);
|
||||
f->store_32(p_limit_mipmap);
|
||||
//reserved for future use
|
||||
|
||||
// Reserved.
|
||||
f->store_32(0);
|
||||
f->store_32(0);
|
||||
f->store_32(0);
|
||||
|
||||
if ((p_compress_mode == COMPRESS_LOSSLESS || p_compress_mode == COMPRESS_LOSSY) && p_image->get_format() > Image::FORMAT_RGBA8) {
|
||||
if ((p_compress_mode == COMPRESS_LOSSLESS || p_compress_mode == COMPRESS_LOSSY) && p_image->get_format() >= Image::FORMAT_RF) {
|
||||
p_compress_mode = COMPRESS_VRAM_UNCOMPRESSED; //these can't go as lossy
|
||||
}
|
||||
|
||||
Ref<Image> image = p_image->duplicate();
|
||||
|
||||
if (p_force_po2_for_compressed && p_mipmaps && ((p_compress_mode == COMPRESS_BASIS_UNIVERSAL) || (p_compress_mode == COMPRESS_VRAM_COMPRESSED))) {
|
||||
image->resize_to_po2();
|
||||
}
|
||||
if (p_mipmaps) {
|
||||
if (p_force_po2_for_compressed && (p_compress_mode == COMPRESS_BASIS_UNIVERSAL || p_compress_mode == COMPRESS_VRAM_COMPRESSED)) {
|
||||
image->resize_to_po2();
|
||||
}
|
||||
|
||||
if (p_mipmaps && (!image->has_mipmaps() || p_force_normal)) {
|
||||
image->generate_mipmaps(p_force_normal);
|
||||
}
|
||||
if (!image->has_mipmaps() || p_force_normal) {
|
||||
image->generate_mipmaps(p_force_normal);
|
||||
}
|
||||
|
||||
if (!p_mipmaps) {
|
||||
} else {
|
||||
image->clear_mipmaps();
|
||||
}
|
||||
|
||||
// Generate roughness mipmaps from normal texture.
|
||||
if (image->has_mipmaps() && p_normal.is_valid()) {
|
||||
image->generate_mipmap_roughness(p_roughness_channel, p_normal);
|
||||
}
|
||||
|
||||
Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC;
|
||||
if (p_force_normal) {
|
||||
csource = Image::COMPRESS_SOURCE_NORMAL;
|
||||
} else if (p_srgb_friendly) {
|
||||
csource = Image::COMPRESS_SOURCE_SRGB;
|
||||
}
|
||||
// Optimization: Only check for color channels when compressing as BasisU or VRAM.
|
||||
Image::UsedChannels used_channels = Image::USED_CHANNELS_RGBA;
|
||||
|
||||
Image::UsedChannels used_channels = image->detect_used_channels(csource);
|
||||
if (p_compress_mode == COMPRESS_BASIS_UNIVERSAL || p_compress_mode == COMPRESS_VRAM_COMPRESSED) {
|
||||
Image::CompressSource comp_source = Image::COMPRESS_SOURCE_GENERIC;
|
||||
if (p_force_normal) {
|
||||
comp_source = Image::COMPRESS_SOURCE_NORMAL;
|
||||
} else if (p_srgb_friendly) {
|
||||
comp_source = Image::COMPRESS_SOURCE_SRGB;
|
||||
}
|
||||
|
||||
used_channels = image->detect_used_channels(comp_source);
|
||||
}
|
||||
|
||||
save_to_ctex_format(f, image, p_compress_mode, used_channels, p_vram_compression, p_lossy_quality);
|
||||
}
|
||||
|
|
@ -428,7 +442,51 @@ Dictionary ResourceImporterTexture::_load_editor_meta(const String &p_path) cons
|
|||
return f->get_var();
|
||||
}
|
||||
|
||||
Error ResourceImporterTexture::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
void ResourceImporterTexture::_invert_y_channel(Ref<Image> &r_image) {
|
||||
// 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 = r_image->get_height();
|
||||
const int width = r_image->get_width();
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
for (int j = 0; j < height; j++) {
|
||||
const Color color = r_image->get_pixel(i, j);
|
||||
r_image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b, color.a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceImporterTexture::_clamp_hdr_exposure(Ref<Image> &r_image) {
|
||||
// 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 = r_image->get_height();
|
||||
const int width = r_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 = r_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));
|
||||
}
|
||||
|
||||
r_image->set_pixel(i, j, clamped_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Error ResourceImporterTexture::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
// Parse import options.
|
||||
int32_t loader_flags = ImageFormatLoader::FLAG_NONE;
|
||||
|
||||
|
|
@ -452,31 +510,52 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
|
|||
const bool fix_alpha_border = p_options["process/fix_alpha_border"];
|
||||
const bool premult_alpha = p_options["process/premult_alpha"];
|
||||
const bool normal_map_invert_y = p_options["process/normal_map_invert_y"];
|
||||
|
||||
const bool hdr_as_srgb = p_options["process/hdr_as_srgb"];
|
||||
const bool hdr_clamp_exposure = p_options["process/hdr_clamp_exposure"];
|
||||
int size_limit = p_options["process/size_limit"];
|
||||
|
||||
bool using_fallback_size_limit = false;
|
||||
if (size_limit == 0) {
|
||||
using_fallback_size_limit = true;
|
||||
// If no size limit is defined, use a fallback size limit to prevent textures from looking incorrect or failing to import.
|
||||
switch (compress_mode) {
|
||||
case COMPRESS_LOSSY:
|
||||
// Maximum WebP size on either axis.
|
||||
size_limit = 16383;
|
||||
break;
|
||||
case COMPRESS_BASIS_UNIVERSAL:
|
||||
// Maximum Basis Universal size on either axis.
|
||||
size_limit = 16384;
|
||||
break;
|
||||
default:
|
||||
// As of June 2024, no GPU can correctly display a texture larger than 32768 pixels on either axis.
|
||||
size_limit = 32768;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Support for texture streaming is not implemented yet.
|
||||
const bool stream = false;
|
||||
const int size_limit = p_options["process/size_limit"];
|
||||
const bool hdr_as_srgb = p_options["process/hdr_as_srgb"];
|
||||
if (hdr_as_srgb) {
|
||||
loader_flags |= ImageFormatLoader::FLAG_FORCE_LINEAR;
|
||||
}
|
||||
const bool hdr_clamp_exposure = p_options["process/hdr_clamp_exposure"];
|
||||
|
||||
float scale = 1.0;
|
||||
// SVG-specific options.
|
||||
if (p_options.has("svg/scale")) {
|
||||
scale = p_options["svg/scale"];
|
||||
}
|
||||
float scale = p_options.has("svg/scale") ? float(p_options["svg/scale"]) : 1.0f;
|
||||
|
||||
// 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"];
|
||||
|
||||
if (hdr_as_srgb) {
|
||||
loader_flags |= ImageFormatLoader::FLAG_FORCE_LINEAR;
|
||||
}
|
||||
|
||||
// Start importing images.
|
||||
List<Ref<Image>> images_imported;
|
||||
LocalVector<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)) {
|
||||
normal_image.instantiate();
|
||||
if (ImageLoader::load_image(normal_map, normal_image) == OK) {
|
||||
|
|
@ -496,11 +575,9 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
|
|||
// 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;
|
||||
}
|
||||
float editor_scale = use_editor_scale ? scale * EDSCALE : scale;
|
||||
|
||||
int32_t editor_loader_flags = loader_flags;
|
||||
if (convert_editor_colors) {
|
||||
|
|
@ -509,8 +586,9 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
|
|||
|
||||
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 + "'");
|
||||
WARN_PRINT(vformat("Failed to import an image resource for editor use from '%s'.", p_source_file));
|
||||
} else {
|
||||
images_imported.push_back(editor_image);
|
||||
}
|
||||
|
|
@ -523,11 +601,19 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
|
|||
int new_width = size_limit;
|
||||
int new_height = target_image->get_height() * new_width / target_image->get_width();
|
||||
|
||||
if (using_fallback_size_limit) {
|
||||
// Only warn if downsizing occurred when the user did not explicitly request it.
|
||||
WARN_PRINT(vformat("%s: Texture was downsized on import as its width (%d pixels) exceeded the importable size limit (%d pixels).", p_source_file, target_image->get_width(), size_limit));
|
||||
}
|
||||
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 (using_fallback_size_limit) {
|
||||
// Only warn if downsizing occurred when the user did not explicitly request it.
|
||||
WARN_PRINT(vformat("%s: Texture was downsized on import as its height (%d pixels) exceeded the importable size limit (%d pixels).", p_source_file, target_image->get_height(), size_limit));
|
||||
}
|
||||
target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC);
|
||||
}
|
||||
|
||||
|
|
@ -548,56 +634,15 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
|
|||
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
_invert_y_channel(target_image);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
_clamp_hdr_exposure(target_image);
|
||||
}
|
||||
}
|
||||
|
||||
if (compress_mode == COMPRESS_BASIS_UNIVERSAL && image->get_format() >= Image::FORMAT_RF) {
|
||||
// Basis universal does not support float formats, fallback.
|
||||
compress_mode = COMPRESS_VRAM_COMPRESSED;
|
||||
}
|
||||
|
||||
bool detect_3d = int(p_options["detect_3d/compress_to"]) > 0;
|
||||
bool detect_roughness = roughness == 0;
|
||||
bool detect_normal = normal == 0;
|
||||
|
|
@ -607,14 +652,12 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
|
|||
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 desktop and mobile formats in order of priority, so platform chooses the best supported one (e.g. s3tc over etc2 on desktop).
|
||||
const bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995);
|
||||
const bool can_s3tc_bptc = ResourceImporterTextureSettings::should_import_s3tc_bptc();
|
||||
const bool can_etc2_astc = ResourceImporterTextureSettings::should_import_etc2_astc();
|
||||
|
||||
// Add list of formats imported
|
||||
// Add list of formats imported.
|
||||
if (can_s3tc_bptc) {
|
||||
formats_imported.push_back("s3tc_bptc");
|
||||
}
|
||||
|
|
@ -624,7 +667,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
|
|||
|
||||
bool can_compress_hdr = hdr_compression > 0;
|
||||
bool has_alpha = image->detect_alpha() != Image::ALPHA_NONE;
|
||||
bool use_uncompressed = false;
|
||||
bool force_uncompressed = false;
|
||||
|
||||
if (is_hdr) {
|
||||
if (has_alpha) {
|
||||
|
|
@ -641,17 +684,16 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
|
|||
}
|
||||
}
|
||||
|
||||
if (!can_compress_hdr) {
|
||||
// Fallback to RGBE99995.
|
||||
if (image->get_format() != Image::FORMAT_RGBE9995) {
|
||||
image->convert(Image::FORMAT_RGBE9995);
|
||||
use_uncompressed = true;
|
||||
}
|
||||
// Fall back to RGBE99995.
|
||||
if (!can_compress_hdr && image->get_format() != Image::FORMAT_RGBE9995) {
|
||||
image->convert(Image::FORMAT_RGBE9995);
|
||||
force_uncompressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_uncompressed) {
|
||||
_save_ctex(image, p_save_path + ".ctex", COMPRESS_VRAM_UNCOMPRESSED, 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 (force_uncompressed) {
|
||||
_save_ctex(image, p_save_path + ".ctex", COMPRESS_VRAM_UNCOMPRESSED, 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);
|
||||
} else {
|
||||
if (can_s3tc_bptc) {
|
||||
Image::CompressMode image_compress_mode;
|
||||
|
|
@ -663,7 +705,9 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
|
|||
image_compress_mode = Image::COMPRESS_S3TC;
|
||||
image_compress_format = "s3tc";
|
||||
}
|
||||
_save_ctex(image, p_save_path + "." + image_compress_format + ".ctex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
|
||||
|
||||
_save_ctex(image, p_save_path + "." + image_compress_format + ".ctex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d,
|
||||
detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
|
||||
r_platform_variants->push_back(image_compress_format);
|
||||
}
|
||||
|
||||
|
|
@ -677,17 +721,21 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
|
|||
image_compress_mode = Image::COMPRESS_ETC2;
|
||||
image_compress_format = "etc2";
|
||||
}
|
||||
_save_ctex(image, p_save_path + "." + image_compress_format + ".ctex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
|
||||
|
||||
_save_ctex(image, p_save_path + "." + image_compress_format + ".ctex", compress_mode, lossy, image_compress_mode, mipmaps, stream, detect_3d,
|
||||
detect_roughness, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel);
|
||||
r_platform_variants->push_back(image_compress_format);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 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);
|
||||
_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);
|
||||
_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);
|
||||
|
||||
// Generate and save editor-specific metadata, which we cannot save to the .import file.
|
||||
Dictionary editor_meta;
|
||||
|
|
@ -695,6 +743,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
|
|||
if (use_editor_scale) {
|
||||
editor_meta["editor_scale"] = EDSCALE;
|
||||
}
|
||||
|
||||
if (convert_editor_colors) {
|
||||
editor_meta["editor_dark_theme"] = EditorThemeManager::is_dark_theme();
|
||||
}
|
||||
|
|
@ -705,6 +754,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
|
|||
if (r_metadata) {
|
||||
Dictionary meta;
|
||||
meta["vram_texture"] = compress_mode == COMPRESS_VRAM_COMPRESSED;
|
||||
|
||||
if (formats_imported.size()) {
|
||||
meta["imported_formats"] = formats_imported;
|
||||
}
|
||||
|
|
@ -724,26 +774,25 @@ const char *ResourceImporterTexture::compression_formats[] = {
|
|||
"etc2_astc",
|
||||
nullptr
|
||||
};
|
||||
|
||||
String ResourceImporterTexture::get_import_settings_string() const {
|
||||
String s;
|
||||
|
||||
int index = 0;
|
||||
while (compression_formats[index]) {
|
||||
String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
|
||||
bool test = GLOBAL_GET(setting_path);
|
||||
if (test) {
|
||||
const String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
|
||||
if (bool(GLOBAL_GET(setting_path))) {
|
||||
s += String(compression_formats[index]);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) const {
|
||||
Dictionary meta = ResourceFormatImporter::get_singleton()->get_resource_metadata(p_path);
|
||||
|
||||
if (meta.has("has_editor_variant")) {
|
||||
bool ResourceImporterTexture::are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const {
|
||||
if (p_meta.has("has_editor_variant")) {
|
||||
String imported_path = ResourceFormatImporter::get_singleton()->get_internal_resource_path(p_path);
|
||||
if (!FileAccess::exists(imported_path)) {
|
||||
return false;
|
||||
|
|
@ -755,41 +804,39 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co
|
|||
if (editor_meta.has("editor_scale") && (float)editor_meta["editor_scale"] != EDSCALE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (editor_meta.has("editor_dark_theme") && (bool)editor_meta["editor_dark_theme"] != EditorThemeManager::is_dark_theme()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!meta.has("vram_texture")) {
|
||||
if (!p_meta.has("vram_texture")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vram = meta["vram_texture"];
|
||||
if (!vram) {
|
||||
if (!bool(p_meta["vram_texture"])) {
|
||||
return true; // Do not care about non-VRAM.
|
||||
}
|
||||
|
||||
// Will become invalid if formats are missing to import.
|
||||
Vector<String> formats_imported;
|
||||
if (meta.has("imported_formats")) {
|
||||
formats_imported = meta["imported_formats"];
|
||||
if (p_meta.has("imported_formats")) {
|
||||
formats_imported = p_meta["imported_formats"];
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
bool valid = true;
|
||||
while (compression_formats[index]) {
|
||||
String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
|
||||
const String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
|
||||
if (ProjectSettings::get_singleton()->has_setting(setting_path)) {
|
||||
bool test = GLOBAL_GET(setting_path);
|
||||
if (test) {
|
||||
if (!formats_imported.has(compression_formats[index])) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
if (bool(GLOBAL_GET(setting_path)) && !formats_imported.has(compression_formats[index])) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
WARN_PRINT("Setting for imported format not found: " + setting_path);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
#include "core/io/file_access.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/io/resource_importer.h"
|
||||
#include "scene/resources/texture.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
class CompressedTexture2D;
|
||||
|
|
@ -78,6 +77,10 @@ protected:
|
|||
void _save_editor_meta(const Dictionary &p_metadata, const String &p_to_path);
|
||||
Dictionary _load_editor_meta(const String &p_to_path) const;
|
||||
|
||||
static inline void _clamp_hdr_exposure(Ref<Image> &r_image);
|
||||
static inline void _invert_y_channel(Ref<Image> &r_image);
|
||||
static inline void _print_callback_message(const String &p_message);
|
||||
|
||||
public:
|
||||
static void save_to_ctex_format(Ref<FileAccess> f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality);
|
||||
|
||||
|
|
@ -100,11 +103,13 @@ public:
|
|||
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
|
||||
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
virtual bool can_import_threaded() const override { return true; }
|
||||
|
||||
void update_imports();
|
||||
|
||||
virtual bool are_import_settings_valid(const String &p_path) const override;
|
||||
virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const override;
|
||||
virtual String get_import_settings_string() const override;
|
||||
|
||||
ResourceImporterTexture(bool p_singleton = false);
|
||||
|
|
|
|||
|
|
@ -32,12 +32,12 @@
|
|||
|
||||
#include "atlas_import_failed.xpm"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/image_loader.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/math/geometry_2d.h"
|
||||
#include "editor/editor_atlas_packer.h"
|
||||
#include "scene/resources/atlas_texture.h"
|
||||
#include "scene/resources/bit_map.h"
|
||||
#include "scene/resources/image_texture.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
#include "scene/resources/mesh_texture.h"
|
||||
|
|
@ -91,7 +91,7 @@ String ResourceImporterTextureAtlas::get_option_group_file() const {
|
|||
return "atlas_file";
|
||||
}
|
||||
|
||||
Error ResourceImporterTextureAtlas::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Error ResourceImporterTextureAtlas::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
/* If this happens, it's because the atlas_file field was not filled, so just import a broken texture */
|
||||
|
||||
//use an xpm because it's size independent, the editor images are vector and size dependent
|
||||
|
|
@ -314,7 +314,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file
|
|||
//update cache if existing, else create
|
||||
Ref<Texture2D> cache;
|
||||
cache = ResourceCache::get_ref(p_group_file);
|
||||
if (!cache.is_valid()) {
|
||||
if (cache.is_null()) {
|
||||
Ref<ImageTexture> res_cache = ImageTexture::create_from_image(new_atlas);
|
||||
res_cache->set_path(p_group_file);
|
||||
cache = res_cache;
|
||||
|
|
|
|||
|
|
@ -64,9 +64,11 @@ public:
|
|||
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
virtual String get_option_group_file() const override;
|
||||
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) override;
|
||||
|
||||
virtual bool can_import_threaded() const override { return true; }
|
||||
|
||||
ResourceImporterTextureAtlas();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,13 +30,7 @@
|
|||
|
||||
#include "resource_importer_wav.h"
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "scene/resources/audio_stream_wav.h"
|
||||
|
||||
const float TRIM_DB_LIMIT = -50;
|
||||
const int TRIM_FADE_OUT_FRAMES = 500;
|
||||
|
||||
String ResourceImporterWAV::get_importer_name() const {
|
||||
return "wav";
|
||||
|
|
@ -90,459 +84,18 @@ void ResourceImporterWAV::get_import_options(const String &p_path, List<ImportOp
|
|||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "edit/loop_mode", PROPERTY_HINT_ENUM, "Detect From WAV,Disabled,Forward,Ping-Pong,Backward", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "edit/loop_begin"), 0));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "edit/loop_end"), -1));
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Disabled,RAM (Ima-ADPCM),QOA (Quite OK Audio)"), 0));
|
||||
// Quite OK Audio is lightweight enough and supports virtually every significant AudioStreamWAV feature.
|
||||
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "PCM (Uncompressed),IMA ADPCM,Quite OK Audio"), 2));
|
||||
}
|
||||
|
||||
Error ResourceImporterWAV::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
/* STEP 1, READ WAVE FILE */
|
||||
|
||||
Error err;
|
||||
Ref<FileAccess> file = FileAccess::open(p_source_file, FileAccess::READ, &err);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_OPEN, "Cannot open file '" + p_source_file + "'.");
|
||||
|
||||
/* CHECK RIFF */
|
||||
char riff[5];
|
||||
riff[4] = 0;
|
||||
file->get_buffer((uint8_t *)&riff, 4); //RIFF
|
||||
|
||||
if (riff[0] != 'R' || riff[1] != 'I' || riff[2] != 'F' || riff[3] != 'F') {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, vformat("Not a WAV file. File should start with 'RIFF', but found '%s', in file of size %d bytes", riff, file->get_length()));
|
||||
Error ResourceImporterWAV::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
|
||||
Dictionary options;
|
||||
for (const KeyValue<StringName, Variant> &pair : p_options) {
|
||||
options[pair.key] = pair.value;
|
||||
}
|
||||
|
||||
/* GET FILESIZE */
|
||||
file->get_32(); // filesize
|
||||
|
||||
/* CHECK WAVE */
|
||||
|
||||
char wave[5];
|
||||
wave[4] = 0;
|
||||
file->get_buffer((uint8_t *)&wave, 4); //WAVE
|
||||
|
||||
if (wave[0] != 'W' || wave[1] != 'A' || wave[2] != 'V' || wave[3] != 'E') {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, vformat("Not a WAV file. Header should contain 'WAVE', but found '%s', in file of size %d bytes", wave, file->get_length()));
|
||||
}
|
||||
|
||||
// Let users override potential loop points from the WAV.
|
||||
// We parse the WAV loop points only with "Detect From WAV" (0).
|
||||
int import_loop_mode = p_options["edit/loop_mode"];
|
||||
|
||||
int format_bits = 0;
|
||||
int format_channels = 0;
|
||||
|
||||
AudioStreamWAV::LoopMode loop_mode = AudioStreamWAV::LOOP_DISABLED;
|
||||
uint16_t compression_code = 1;
|
||||
bool format_found = false;
|
||||
bool data_found = false;
|
||||
int format_freq = 0;
|
||||
int loop_begin = 0;
|
||||
int loop_end = 0;
|
||||
int frames = 0;
|
||||
|
||||
Vector<float> data;
|
||||
|
||||
while (!file->eof_reached()) {
|
||||
/* chunk */
|
||||
char chunkID[4];
|
||||
file->get_buffer((uint8_t *)&chunkID, 4); //RIFF
|
||||
|
||||
/* chunk size */
|
||||
uint32_t chunksize = file->get_32();
|
||||
uint32_t file_pos = file->get_position(); //save file pos, so we can skip to next chunk safely
|
||||
|
||||
if (file->eof_reached()) {
|
||||
//ERR_PRINT("EOF REACH");
|
||||
break;
|
||||
}
|
||||
|
||||
if (chunkID[0] == 'f' && chunkID[1] == 'm' && chunkID[2] == 't' && chunkID[3] == ' ' && !format_found) {
|
||||
/* IS FORMAT CHUNK */
|
||||
|
||||
//Issue: #7755 : Not a bug - usage of other formats (format codes) are unsupported in current importer version.
|
||||
//Consider revision for engine version 3.0
|
||||
compression_code = file->get_16();
|
||||
if (compression_code != 1 && compression_code != 3) {
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM or IEEE float instead.");
|
||||
}
|
||||
|
||||
format_channels = file->get_16();
|
||||
if (format_channels != 1 && format_channels != 2) {
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Format not supported for WAVE file (not stereo or mono).");
|
||||
}
|
||||
|
||||
format_freq = file->get_32(); //sampling rate
|
||||
|
||||
file->get_32(); // average bits/second (unused)
|
||||
file->get_16(); // block align (unused)
|
||||
format_bits = file->get_16(); // bits per sample
|
||||
|
||||
if (format_bits % 8 || format_bits == 0) {
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Invalid amount of bits in the sample (should be one of 8, 16, 24 or 32).");
|
||||
}
|
||||
|
||||
if (compression_code == 3 && format_bits % 32) {
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Invalid amount of bits in the IEEE float sample (should be 32 or 64).");
|
||||
}
|
||||
|
||||
/* Don't need anything else, continue */
|
||||
format_found = true;
|
||||
}
|
||||
|
||||
if (chunkID[0] == 'd' && chunkID[1] == 'a' && chunkID[2] == 't' && chunkID[3] == 'a' && !data_found) {
|
||||
/* IS DATA CHUNK */
|
||||
data_found = true;
|
||||
|
||||
if (!format_found) {
|
||||
ERR_PRINT("'data' chunk before 'format' chunk found.");
|
||||
break;
|
||||
}
|
||||
|
||||
frames = chunksize;
|
||||
|
||||
if (format_channels == 0) {
|
||||
ERR_FAIL_COND_V(format_channels == 0, ERR_INVALID_DATA);
|
||||
}
|
||||
frames /= format_channels;
|
||||
frames /= (format_bits >> 3);
|
||||
|
||||
/*print_line("chunksize: "+itos(chunksize));
|
||||
print_line("channels: "+itos(format_channels));
|
||||
print_line("bits: "+itos(format_bits));
|
||||
*/
|
||||
|
||||
data.resize(frames * format_channels);
|
||||
|
||||
if (compression_code == 1) {
|
||||
if (format_bits == 8) {
|
||||
for (int i = 0; i < frames * format_channels; i++) {
|
||||
// 8 bit samples are UNSIGNED
|
||||
|
||||
data.write[i] = int8_t(file->get_8() - 128) / 128.f;
|
||||
}
|
||||
} else if (format_bits == 16) {
|
||||
for (int i = 0; i < frames * format_channels; i++) {
|
||||
//16 bit SIGNED
|
||||
|
||||
data.write[i] = int16_t(file->get_16()) / 32768.f;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < frames * format_channels; i++) {
|
||||
//16+ bits samples are SIGNED
|
||||
// if sample is > 16 bits, just read extra bytes
|
||||
|
||||
uint32_t s = 0;
|
||||
for (int b = 0; b < (format_bits >> 3); b++) {
|
||||
s |= ((uint32_t)file->get_8()) << (b * 8);
|
||||
}
|
||||
s <<= (32 - format_bits);
|
||||
|
||||
data.write[i] = (int32_t(s) >> 16) / 32768.f;
|
||||
}
|
||||
}
|
||||
} else if (compression_code == 3) {
|
||||
if (format_bits == 32) {
|
||||
for (int i = 0; i < frames * format_channels; i++) {
|
||||
//32 bit IEEE Float
|
||||
|
||||
data.write[i] = file->get_float();
|
||||
}
|
||||
} else if (format_bits == 64) {
|
||||
for (int i = 0; i < frames * format_channels; i++) {
|
||||
//64 bit IEEE Float
|
||||
|
||||
data.write[i] = file->get_double();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file->eof_reached()) {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Premature end of file.");
|
||||
}
|
||||
}
|
||||
|
||||
if (import_loop_mode == 0 && chunkID[0] == 's' && chunkID[1] == 'm' && chunkID[2] == 'p' && chunkID[3] == 'l') {
|
||||
// Loop point info!
|
||||
|
||||
/**
|
||||
* Consider exploring next document:
|
||||
* http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/RIFFNEW.pdf
|
||||
* Especially on page:
|
||||
* 16 - 17
|
||||
* Timestamp:
|
||||
* 22:38 06.07.2017 GMT
|
||||
**/
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
file->get_32(); // i wish to know why should i do this... no doc!
|
||||
}
|
||||
|
||||
// only read 0x00 (loop forward), 0x01 (loop ping-pong) and 0x02 (loop backward)
|
||||
// Skip anything else because it's not supported, reserved for future uses or sampler specific
|
||||
// from https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#smpl (loop type values table)
|
||||
int loop_type = file->get_32();
|
||||
if (loop_type == 0x00 || loop_type == 0x01 || loop_type == 0x02) {
|
||||
if (loop_type == 0x00) {
|
||||
loop_mode = AudioStreamWAV::LOOP_FORWARD;
|
||||
} else if (loop_type == 0x01) {
|
||||
loop_mode = AudioStreamWAV::LOOP_PINGPONG;
|
||||
} else if (loop_type == 0x02) {
|
||||
loop_mode = AudioStreamWAV::LOOP_BACKWARD;
|
||||
}
|
||||
loop_begin = file->get_32();
|
||||
loop_end = file->get_32();
|
||||
}
|
||||
}
|
||||
// Move to the start of the next chunk. Note that RIFF requires a padding byte for odd
|
||||
// chunk sizes.
|
||||
file->seek(file_pos + chunksize + (chunksize & 1));
|
||||
}
|
||||
|
||||
// STEP 2, APPLY CONVERSIONS
|
||||
|
||||
bool is16 = format_bits != 8;
|
||||
int rate = format_freq;
|
||||
|
||||
/*
|
||||
print_line("Input Sample: ");
|
||||
print_line("\tframes: " + itos(frames));
|
||||
print_line("\tformat_channels: " + itos(format_channels));
|
||||
print_line("\t16bits: " + itos(is16));
|
||||
print_line("\trate: " + itos(rate));
|
||||
print_line("\tloop: " + itos(loop));
|
||||
print_line("\tloop begin: " + itos(loop_begin));
|
||||
print_line("\tloop end: " + itos(loop_end));
|
||||
*/
|
||||
|
||||
//apply frequency limit
|
||||
|
||||
bool limit_rate = p_options["force/max_rate"];
|
||||
int limit_rate_hz = p_options["force/max_rate_hz"];
|
||||
if (limit_rate && rate > limit_rate_hz && rate > 0 && frames > 0) {
|
||||
// resample!
|
||||
int new_data_frames = (int)(frames * (float)limit_rate_hz / (float)rate);
|
||||
|
||||
Vector<float> new_data;
|
||||
new_data.resize(new_data_frames * format_channels);
|
||||
for (int c = 0; c < format_channels; c++) {
|
||||
float frac = .0f;
|
||||
int ipos = 0;
|
||||
|
||||
for (int i = 0; i < new_data_frames; i++) {
|
||||
// Cubic interpolation should be enough.
|
||||
|
||||
float y0 = data[MAX(0, ipos - 1) * format_channels + c];
|
||||
float y1 = data[ipos * format_channels + c];
|
||||
float y2 = data[MIN(frames - 1, ipos + 1) * format_channels + c];
|
||||
float y3 = data[MIN(frames - 1, ipos + 2) * format_channels + c];
|
||||
|
||||
new_data.write[i * format_channels + c] = Math::cubic_interpolate(y1, y2, y0, y3, frac);
|
||||
|
||||
// update position and always keep fractional part within ]0...1]
|
||||
// in order to avoid 32bit floating point precision errors
|
||||
|
||||
frac += (float)rate / (float)limit_rate_hz;
|
||||
int tpos = (int)Math::floor(frac);
|
||||
ipos += tpos;
|
||||
frac -= tpos;
|
||||
}
|
||||
}
|
||||
|
||||
if (loop_mode) {
|
||||
loop_begin = (int)(loop_begin * (float)new_data_frames / (float)frames);
|
||||
loop_end = (int)(loop_end * (float)new_data_frames / (float)frames);
|
||||
}
|
||||
|
||||
data = new_data;
|
||||
rate = limit_rate_hz;
|
||||
frames = new_data_frames;
|
||||
}
|
||||
|
||||
bool normalize = p_options["edit/normalize"];
|
||||
|
||||
if (normalize) {
|
||||
float max = 0;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
float amp = Math::abs(data[i]);
|
||||
if (amp > max) {
|
||||
max = amp;
|
||||
}
|
||||
}
|
||||
|
||||
if (max > 0) {
|
||||
float mult = 1.0 / max;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
data.write[i] *= mult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool trim = p_options["edit/trim"];
|
||||
|
||||
if (trim && (loop_mode == AudioStreamWAV::LOOP_DISABLED) && format_channels > 0) {
|
||||
int first = 0;
|
||||
int last = (frames / format_channels) - 1;
|
||||
bool found = false;
|
||||
float limit = Math::db_to_linear(TRIM_DB_LIMIT);
|
||||
|
||||
for (int i = 0; i < data.size() / format_channels; i++) {
|
||||
float ampChannelSum = 0;
|
||||
for (int j = 0; j < format_channels; j++) {
|
||||
ampChannelSum += Math::abs(data[(i * format_channels) + j]);
|
||||
}
|
||||
|
||||
float amp = Math::abs(ampChannelSum / (float)format_channels);
|
||||
|
||||
if (!found && amp > limit) {
|
||||
first = i;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found && amp > limit) {
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (first < last) {
|
||||
Vector<float> new_data;
|
||||
new_data.resize((last - first) * format_channels);
|
||||
for (int i = first; i < last; i++) {
|
||||
float fadeOutMult = 1;
|
||||
|
||||
if (last - i < TRIM_FADE_OUT_FRAMES) {
|
||||
fadeOutMult = ((float)(last - i - 1) / (float)TRIM_FADE_OUT_FRAMES);
|
||||
}
|
||||
|
||||
for (int j = 0; j < format_channels; j++) {
|
||||
new_data.write[((i - first) * format_channels) + j] = data[(i * format_channels) + j] * fadeOutMult;
|
||||
}
|
||||
}
|
||||
|
||||
data = new_data;
|
||||
frames = data.size() / format_channels;
|
||||
}
|
||||
}
|
||||
|
||||
if (import_loop_mode >= 2) {
|
||||
loop_mode = (AudioStreamWAV::LoopMode)(import_loop_mode - 1);
|
||||
loop_begin = p_options["edit/loop_begin"];
|
||||
loop_end = p_options["edit/loop_end"];
|
||||
// Wrap around to max frames, so `-1` can be used to select the end, etc.
|
||||
if (loop_begin < 0) {
|
||||
loop_begin = CLAMP(loop_begin + frames + 1, 0, frames);
|
||||
}
|
||||
if (loop_end < 0) {
|
||||
loop_end = CLAMP(loop_end + frames + 1, 0, frames);
|
||||
}
|
||||
}
|
||||
|
||||
int compression = p_options["compress/mode"];
|
||||
bool force_mono = p_options["force/mono"];
|
||||
|
||||
if (force_mono && format_channels == 2) {
|
||||
Vector<float> new_data;
|
||||
new_data.resize(data.size() / 2);
|
||||
for (int i = 0; i < frames; i++) {
|
||||
new_data.write[i] = (data[i * 2 + 0] + data[i * 2 + 1]) / 2.0;
|
||||
}
|
||||
|
||||
data = new_data;
|
||||
format_channels = 1;
|
||||
}
|
||||
|
||||
bool force_8_bit = p_options["force/8_bit"];
|
||||
if (force_8_bit) {
|
||||
is16 = false;
|
||||
}
|
||||
|
||||
Vector<uint8_t> pcm_data;
|
||||
AudioStreamWAV::Format dst_format;
|
||||
|
||||
if (compression == 1) {
|
||||
dst_format = AudioStreamWAV::FORMAT_IMA_ADPCM;
|
||||
if (format_channels == 1) {
|
||||
_compress_ima_adpcm(data, pcm_data);
|
||||
} else {
|
||||
//byte interleave
|
||||
Vector<float> left;
|
||||
Vector<float> right;
|
||||
|
||||
int tframes = data.size() / 2;
|
||||
left.resize(tframes);
|
||||
right.resize(tframes);
|
||||
|
||||
for (int i = 0; i < tframes; i++) {
|
||||
left.write[i] = data[i * 2 + 0];
|
||||
right.write[i] = data[i * 2 + 1];
|
||||
}
|
||||
|
||||
Vector<uint8_t> bleft;
|
||||
Vector<uint8_t> bright;
|
||||
|
||||
_compress_ima_adpcm(left, bleft);
|
||||
_compress_ima_adpcm(right, bright);
|
||||
|
||||
int dl = bleft.size();
|
||||
pcm_data.resize(dl * 2);
|
||||
|
||||
uint8_t *w = pcm_data.ptrw();
|
||||
const uint8_t *rl = bleft.ptr();
|
||||
const uint8_t *rr = bright.ptr();
|
||||
|
||||
for (int i = 0; i < dl; i++) {
|
||||
w[i * 2 + 0] = rl[i];
|
||||
w[i * 2 + 1] = rr[i];
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
dst_format = is16 ? AudioStreamWAV::FORMAT_16_BITS : AudioStreamWAV::FORMAT_8_BITS;
|
||||
bool enforce16 = is16 || compression == 2;
|
||||
pcm_data.resize(data.size() * (enforce16 ? 2 : 1));
|
||||
{
|
||||
uint8_t *w = pcm_data.ptrw();
|
||||
|
||||
int ds = data.size();
|
||||
for (int i = 0; i < ds; i++) {
|
||||
if (enforce16) {
|
||||
int16_t v = CLAMP(data[i] * 32768, -32768, 32767);
|
||||
encode_uint16(v, &w[i * 2]);
|
||||
} else {
|
||||
int8_t v = CLAMP(data[i] * 128, -128, 127);
|
||||
w[i] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector<uint8_t> dst_data;
|
||||
if (compression == 2) {
|
||||
dst_format = AudioStreamWAV::FORMAT_QOA;
|
||||
qoa_desc desc = { 0, 0, 0, { { { 0 }, { 0 } } } };
|
||||
uint32_t qoa_len = 0;
|
||||
|
||||
desc.samplerate = rate;
|
||||
desc.samples = frames;
|
||||
desc.channels = format_channels;
|
||||
|
||||
void *encoded = qoa_encode((short *)pcm_data.ptrw(), &desc, &qoa_len);
|
||||
dst_data.resize(qoa_len);
|
||||
memcpy(dst_data.ptrw(), encoded, qoa_len);
|
||||
} else {
|
||||
dst_data = pcm_data;
|
||||
}
|
||||
|
||||
Ref<AudioStreamWAV> sample;
|
||||
sample.instantiate();
|
||||
sample->set_data(dst_data);
|
||||
sample->set_format(dst_format);
|
||||
sample->set_mix_rate(rate);
|
||||
sample->set_loop_mode(loop_mode);
|
||||
sample->set_loop_begin(loop_begin);
|
||||
sample->set_loop_end(loop_end);
|
||||
sample->set_stereo(format_channels == 2);
|
||||
|
||||
Ref<AudioStreamWAV> sample = AudioStreamWAV::load_from_file(p_source_file, options);
|
||||
ResourceSaver::save(sample, p_save_path + ".sample");
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#define RESOURCE_IMPORTER_WAV_H
|
||||
|
||||
#include "core/io/resource_importer.h"
|
||||
#include "scene/resources/audio_stream_wav.h"
|
||||
|
||||
class ResourceImporterWAV : public ResourceImporter {
|
||||
GDCLASS(ResourceImporterWAV, ResourceImporter);
|
||||
|
|
@ -49,98 +50,9 @@ public:
|
|||
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
|
||||
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
|
||||
|
||||
static void _compress_ima_adpcm(const Vector<float> &p_data, Vector<uint8_t> &dst_data) {
|
||||
static const int16_t _ima_adpcm_step_table[89] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
||||
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
||||
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
||||
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
||||
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
||||
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
||||
};
|
||||
virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
|
||||
static const int8_t _ima_adpcm_index_table[16] = {
|
||||
-1, -1, -1, -1, 2, 4, 6, 8,
|
||||
-1, -1, -1, -1, 2, 4, 6, 8
|
||||
};
|
||||
|
||||
int datalen = p_data.size();
|
||||
int datamax = datalen;
|
||||
if (datalen & 1) {
|
||||
datalen++;
|
||||
}
|
||||
|
||||
dst_data.resize(datalen / 2 + 4);
|
||||
uint8_t *w = dst_data.ptrw();
|
||||
|
||||
int i, step_idx = 0, prev = 0;
|
||||
uint8_t *out = w;
|
||||
const float *in = p_data.ptr();
|
||||
|
||||
// Initial value is zero.
|
||||
*(out++) = 0;
|
||||
*(out++) = 0;
|
||||
// Table index initial value.
|
||||
*(out++) = 0;
|
||||
// Unused.
|
||||
*(out++) = 0;
|
||||
|
||||
for (i = 0; i < datalen; i++) {
|
||||
int step, diff, vpdiff, mask;
|
||||
uint8_t nibble;
|
||||
int16_t xm_sample;
|
||||
|
||||
if (i >= datamax) {
|
||||
xm_sample = 0;
|
||||
} else {
|
||||
xm_sample = CLAMP(in[i] * 32767.0, -32768, 32767);
|
||||
}
|
||||
|
||||
diff = (int)xm_sample - prev;
|
||||
|
||||
nibble = 0;
|
||||
step = _ima_adpcm_step_table[step_idx];
|
||||
vpdiff = step >> 3;
|
||||
if (diff < 0) {
|
||||
nibble = 8;
|
||||
diff = -diff;
|
||||
}
|
||||
mask = 4;
|
||||
while (mask) {
|
||||
if (diff >= step) {
|
||||
nibble |= mask;
|
||||
diff -= step;
|
||||
vpdiff += step;
|
||||
}
|
||||
|
||||
step >>= 1;
|
||||
mask >>= 1;
|
||||
}
|
||||
|
||||
if (nibble & 8) {
|
||||
prev -= vpdiff;
|
||||
} else {
|
||||
prev += vpdiff;
|
||||
}
|
||||
|
||||
prev = CLAMP(prev, -32768, 32767);
|
||||
|
||||
step_idx += _ima_adpcm_index_table[nibble];
|
||||
step_idx = CLAMP(step_idx, 0, 88);
|
||||
|
||||
if (i & 1) {
|
||||
*out |= nibble << 4;
|
||||
out++;
|
||||
} else {
|
||||
*out = nibble;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
|
||||
virtual bool can_import_threaded() const override { return true; }
|
||||
|
||||
ResourceImporterWAV();
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue