summaryrefslogtreecommitdiff
path: root/editor/import
diff options
context:
space:
mode:
Diffstat (limited to 'editor/import')
-rw-r--r--editor/import/collada.cpp20
-rw-r--r--editor/import/dynamicfont_import_settings.cpp33
-rw-r--r--editor/import/editor_import_collada.cpp20
-rw-r--r--editor/import/editor_importer_bake_reset.cpp8
-rw-r--r--editor/import/resource_importer_scene.cpp15
-rw-r--r--editor/import/resource_importer_texture.cpp16
-rw-r--r--editor/import/resource_importer_texture_atlas.cpp2
-rw-r--r--editor/import/scene_import_settings.cpp60
-rw-r--r--editor/import/scene_importer_mesh.cpp503
-rw-r--r--editor/import/scene_importer_mesh.h14
10 files changed, 539 insertions, 152 deletions
diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp
index b6d0927ce6..4cd9066350 100644
--- a/editor/import/collada.cpp
+++ b/editor/import/collada.cpp
@@ -2095,19 +2095,19 @@ void Collada::_merge_skeletons(VisualScene *p_vscene, Node *p_node) {
}
void Collada::_merge_skeletons2(VisualScene *p_vscene) {
- for (Map<String, SkinControllerData>::Element *E = state.skin_controller_data_map.front(); E; E = E->next()) {
- SkinControllerData &cd = E->get();
+ for (KeyValue<String, SkinControllerData> &E : state.skin_controller_data_map) {
+ SkinControllerData &cd = E.value;
NodeSkeleton *skeleton = nullptr;
- for (Map<String, Transform3D>::Element *F = cd.bone_rest_map.front(); F; F = F->next()) {
+ for (const KeyValue<String, Transform3D> &F : cd.bone_rest_map) {
String name;
- if (!state.sid_to_node_map.has(F->key())) {
+ if (!state.sid_to_node_map.has(F.key)) {
continue;
}
- name = state.sid_to_node_map[F->key()];
+ name = state.sid_to_node_map[F.key];
ERR_CONTINUE(!state.scene_map.has(name));
@@ -2248,9 +2248,9 @@ bool Collada::_move_geometry_to_skeletons(VisualScene *p_vscene, Node *p_node, L
p_node->default_transform = skel_inv * (skin.bind_shape /* p_node->get_global_transform()*/); // i honestly have no idea what to do with a previous model xform.. most exporters ignore it
//make rests relative to the skeleton (they seem to be always relative to world)
- for (Map<String, Transform3D>::Element *E = skin.bone_rest_map.front(); E; E = E->next()) {
- E->get() = skel_inv * E->get(); //make the bone rest local to the skeleton
- state.bone_rest_map[E->key()] = E->get(); // make it remember where the bone is globally, now that it's relative
+ for (KeyValue<String, Transform3D> &E : skin.bone_rest_map) {
+ E.value = skel_inv * E.value; //make the bone rest local to the skeleton
+ state.bone_rest_map[E.key] = E.value; // make it remember where the bone is globally, now that it's relative
}
//but most exporters seem to work only if i do this..
@@ -2302,8 +2302,8 @@ void Collada::_find_morph_nodes(VisualScene *p_vscene, Node *p_node) {
}
void Collada::_optimize() {
- for (Map<String, VisualScene>::Element *E = state.visual_scene_map.front(); E; E = E->next()) {
- VisualScene &vs = E->get();
+ for (KeyValue<String, VisualScene> &E : state.visual_scene_map) {
+ VisualScene &vs = E.value;
for (int i = 0; i < vs.root_nodes.size(); i++) {
_create_skeletons(&vs.root_nodes.write[i]);
}
diff --git a/editor/import/dynamicfont_import_settings.cpp b/editor/import/dynamicfont_import_settings.cpp
index 37ca40287f..9a8abfa5c6 100644
--- a/editor/import/dynamicfont_import_settings.cpp
+++ b/editor/import/dynamicfont_import_settings.cpp
@@ -92,6 +92,8 @@ struct UniRange {
String name;
};
+// Unicode Character Blocks
+// Source: https://www.unicode.org/Public/14.0.0/ucd/Blocks.txt
static UniRange unicode_ranges[] = {
{ 0x0000, 0x007F, U"Basic Latin" },
{ 0x0080, 0x00FF, U"Latin-1 Supplement" },
@@ -109,10 +111,11 @@ static UniRange unicode_ranges[] = {
{ 0x0700, 0x074F, U"Syriac" },
{ 0x0750, 0x077F, U"Arabic Supplement" },
{ 0x0780, 0x07BF, U"Thaana" },
- { 0x07C0, 0x07FF, U"N'Ko" },
+ { 0x07C0, 0x07FF, U"NKo" },
{ 0x0800, 0x083F, U"Samaritan" },
{ 0x0840, 0x085F, U"Mandaic" },
{ 0x0860, 0x086F, U"Syriac Supplement" },
+ { 0x0870, 0x089F, U"Arabic Extended-B" },
{ 0x08A0, 0x08FF, U"Arabic Extended-A" },
{ 0x0900, 0x097F, U"Devanagari" },
{ 0x0980, 0x09FF, U"Bengali" },
@@ -239,9 +242,12 @@ static UniRange unicode_ranges[] = {
{ 0xAB30, 0xAB6F, U"Latin Extended-E" },
{ 0xAB70, 0xABBF, U"Cherokee Supplement" },
{ 0xABC0, 0xABFF, U"Meetei Mayek" },
+ { 0xAC00, 0xD7AF, U"Hangul Syllables" },
{ 0xD7B0, 0xD7FF, U"Hangul Jamo Extended-B" },
- //{ 0xF800, 0xDFFF, U"Surrogates" },
- { 0xE000, 0xE2FE, U"Private Use Area" },
+ //{ 0xD800, 0xDB7F, U"High Surrogates" },
+ //{ 0xDB80, 0xDBFF, U"High Private Use Surrogates" },
+ //{ 0xDC00, 0xDFFF, U"Low Surrogates" },
+ { 0xE000, 0xF8FF, U"Private Use Area" },
{ 0xF900, 0xFAFF, U"CJK Compatibility Ideographs" },
{ 0xFB00, 0xFB4F, U"Alphabetic Presentation Forms" },
{ 0xFB50, 0xFDFF, U"Arabic Presentation Forms-A" },
@@ -273,7 +279,9 @@ static UniRange unicode_ranges[] = {
{ 0x104B0, 0x104FF, U"Osage" },
{ 0x10500, 0x1052F, U"Elbasan" },
{ 0x10530, 0x1056F, U"Caucasian Albanian" },
+ { 0x10570, 0x105BF, U"Vithkuqi" },
{ 0x10600, 0x1077F, U"Linear A" },
+ { 0x10780, 0x107BF, U"Latin Extended-F" },
{ 0x10800, 0x1083F, U"Cypriot Syllabary" },
{ 0x10840, 0x1085F, U"Imperial Aramaic" },
{ 0x10860, 0x1087F, U"Palmyrene" },
@@ -298,6 +306,7 @@ static UniRange unicode_ranges[] = {
{ 0x10E80, 0x10EBF, U"Yezidi" },
{ 0x10F00, 0x10F2F, U"Old Sogdian" },
{ 0x10F30, 0x10F6F, U"Sogdian" },
+ { 0x10F70, 0x10FAF, U"Old Uyghur" },
{ 0x10FB0, 0x10FDF, U"Chorasmian" },
{ 0x10FE0, 0x10FFF, U"Elymaic" },
{ 0x11000, 0x1107F, U"Brahmi" },
@@ -317,13 +326,14 @@ static UniRange unicode_ranges[] = {
{ 0x11600, 0x1165F, U"Modi" },
{ 0x11660, 0x1167F, U"Mongolian Supplement" },
{ 0x11680, 0x116CF, U"Takri" },
- { 0x11700, 0x1173F, U"Ahom" },
+ { 0x11700, 0x1174F, U"Ahom" },
{ 0x11800, 0x1184F, U"Dogra" },
{ 0x118A0, 0x118FF, U"Warang Citi" },
{ 0x11900, 0x1195F, U"Dives Akuru" },
{ 0x119A0, 0x119FF, U"Nandinagari" },
{ 0x11A00, 0x11A4F, U"Zanabazar Square" },
{ 0x11A50, 0x11AAF, U"Soyombo" },
+ { 0x11AB0, 0x11ABF, U"Unified Canadian Aboriginal Syllabics Extended-A" },
{ 0x11AC0, 0x11AFF, U"Pau Cin Hau" },
{ 0x11C00, 0x11C6F, U"Bhaiksuki" },
{ 0x11C70, 0x11CBF, U"Marchen" },
@@ -335,11 +345,13 @@ static UniRange unicode_ranges[] = {
{ 0x12000, 0x123FF, U"Cuneiform" },
{ 0x12400, 0x1247F, U"Cuneiform Numbers and Punctuation" },
{ 0x12480, 0x1254F, U"Early Dynastic Cuneiform" },
+ { 0x12F90, 0x12FFF, U"Cypro-Minoan" },
{ 0x13000, 0x1342F, U"Egyptian Hieroglyphs" },
{ 0x13430, 0x1343F, U"Egyptian Hieroglyph Format Controls" },
{ 0x14400, 0x1467F, U"Anatolian Hieroglyphs" },
{ 0x16800, 0x16A3F, U"Bamum Supplement" },
{ 0x16A40, 0x16A6F, U"Mro" },
+ { 0x16A70, 0x16ACF, U"Tangsa" },
{ 0x16AD0, 0x16AFF, U"Bassa Vah" },
{ 0x16B00, 0x16B8F, U"Pahawh Hmong" },
{ 0x16E40, 0x16E9F, U"Medefaidrin" },
@@ -348,13 +360,15 @@ static UniRange unicode_ranges[] = {
{ 0x17000, 0x187FF, U"Tangut" },
{ 0x18800, 0x18AFF, U"Tangut Components" },
{ 0x18B00, 0x18CFF, U"Khitan Small Script" },
- { 0x18D00, 0x18D8F, U"Tangut Supplement" },
+ { 0x18D00, 0x18D7F, U"Tangut Supplement" },
+ { 0x1AFF0, 0x1AFFF, U"Kana Extended-B" },
{ 0x1B000, 0x1B0FF, U"Kana Supplement" },
{ 0x1B100, 0x1B12F, U"Kana Extended-A" },
{ 0x1B130, 0x1B16F, U"Small Kana Extension" },
{ 0x1B170, 0x1B2FF, U"Nushu" },
{ 0x1BC00, 0x1BC9F, U"Duployan" },
{ 0x1BCA0, 0x1BCAF, U"Shorthand Format Controls" },
+ { 0x1CF00, 0x1CFCF, U"Znamenny Musical Notation" },
{ 0x1D000, 0x1D0FF, U"Byzantine Musical Symbols" },
{ 0x1D100, 0x1D1FF, U"Musical Symbols" },
{ 0x1D200, 0x1D24F, U"Ancient Greek Musical Notation" },
@@ -363,9 +377,12 @@ static UniRange unicode_ranges[] = {
{ 0x1D360, 0x1D37F, U"Counting Rod Numerals" },
{ 0x1D400, 0x1D7FF, U"Mathematical Alphanumeric Symbols" },
{ 0x1D800, 0x1DAAF, U"Sutton SignWriting" },
+ { 0x1DF00, 0x1DFFF, U"Latin Extended-G" },
{ 0x1E000, 0x1E02F, U"Glagolitic Supplement" },
{ 0x1E100, 0x1E14F, U"Nyiakeng Puachue Hmong" },
+ { 0x1E290, 0x1E2BF, U"Toto" },
{ 0x1E2C0, 0x1E2FF, U"Wancho" },
+ { 0x1E7E0, 0x1E7FF, U"Ethiopic Extended-B" },
{ 0x1E800, 0x1E8DF, U"Mende Kikakui" },
{ 0x1E900, 0x1E95F, U"Adlam" },
{ 0x1EC70, 0x1ECBF, U"Indic Siyaq Numbers" },
@@ -396,8 +413,8 @@ static UniRange unicode_ranges[] = {
{ 0x30000, 0x3134F, U"CJK Unified Ideographs Extension G" },
//{ 0xE0000, 0xE007F, U"Tags" },
//{ 0xE0100, 0xE01EF, U"Variation Selectors Supplement" },
- { 0xF0000, 0xFFFFD, U"Supplementary Private Use Area-A" },
- { 0x100000, 0x10FFFD, U"Supplementary Private Use Area-B" },
+ { 0xF0000, 0xFFFFF, U"Supplementary Private Use Area-A" },
+ { 0x100000, 0x10FFFF, U"Supplementary Private Use Area-B" },
{ 0x10FFFF, 0x10FFFF, String() }
};
@@ -1799,7 +1816,7 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
glyph_tree = memnew(Tree);
glyphs_split->add_child(glyph_tree);
glyph_tree->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
- glyph_tree->set_columns(3);
+ glyph_tree->set_columns(2);
glyph_tree->set_hide_root(true);
glyph_tree->set_column_expand(0, false);
glyph_tree->set_column_expand(1, true);
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 7ab80ac3b4..3de7426302 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -1314,8 +1314,8 @@ Error ColladaImport::load(const String &p_path, int p_flags, bool p_force_make_t
}
void ColladaImport::_fix_param_animation_tracks() {
- for (Map<String, Collada::Node *>::Element *E = collada.state.scene_map.front(); E; E = E->next()) {
- Collada::Node *n = E->get();
+ for (KeyValue<String, Collada::Node *> &E : collada.state.scene_map) {
+ Collada::Node *n = E.value;
switch (n->type) {
case Collada::Node::TYPE_NODE: {
// ? do nothing
@@ -1363,7 +1363,7 @@ void ColladaImport::_fix_param_animation_tracks() {
for (int rti = 0; rti < rt.size(); rti++) {
Collada::AnimationTrack *at = &collada.state.animation_tracks.write[rt[rti]];
- at->target = E->key();
+ at->target = E.key;
at->param = "morph/" + collada.state.mesh_name_map[mesh_name];
at->property = true;
//at->param
@@ -1431,11 +1431,11 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
animation->set_name(collada.state.animation_clips[p_clip].name);
}
- for (Map<String, NodeMap>::Element *E = node_map.front(); E; E = E->next()) {
- if (E->get().bone < 0) {
+ for (const KeyValue<String, NodeMap> &E : node_map) {
+ if (E.value.bone < 0) {
continue;
}
- bones_with_animation[E->key()] = false;
+ bones_with_animation[E.key] = false;
}
//store and validate tracks
@@ -1626,19 +1626,19 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
if (p_make_tracks_in_all_bones) {
//some bones may lack animation, but since we don't store pose as a property, we must add keyframes!
- for (Map<String, bool>::Element *E = bones_with_animation.front(); E; E = E->next()) {
- if (E->get()) {
+ for (const KeyValue<String, bool> &E : bones_with_animation) {
+ if (E.value) {
continue;
}
- NodeMap &nm = node_map[E->key()];
+ NodeMap &nm = node_map[E.key];
String path = scene->get_path_to(nm.node);
ERR_CONTINUE(nm.bone < 0);
Skeleton3D *sk = static_cast<Skeleton3D *>(nm.node);
String name = sk->get_bone_name(nm.bone);
path = path + ":" + name;
- Collada::Node *cn = collada.state.scene_map[E->key()];
+ Collada::Node *cn = collada.state.scene_map[E.key];
if (cn->ignore_anim) {
WARN_PRINT("Collada: Ignoring animation on node: " + path);
continue;
diff --git a/editor/import/editor_importer_bake_reset.cpp b/editor/import/editor_importer_bake_reset.cpp
index 00dce6850e..fb5de941ae 100644
--- a/editor/import/editor_importer_bake_reset.cpp
+++ b/editor/import/editor_importer_bake_reset.cpp
@@ -122,14 +122,14 @@ void BakeReset::_align_animations(AnimationPlayer *p_ap, const Map<StringName, B
for (List<StringName>::Element *anim_i = anim_names.front(); anim_i; anim_i = anim_i->next()) {
Ref<Animation> a = p_ap->get_animation(anim_i->get());
ERR_CONTINUE(a.is_null());
- for (Map<StringName, BakeResetRestBone>::Element *rest_bone_i = r_rest_bones.front(); rest_bone_i; rest_bone_i = rest_bone_i->next()) {
- int track = a->find_track(NodePath(rest_bone_i->key()));
+ for (const KeyValue<StringName, BakeResetRestBone> &rest_bone_i : r_rest_bones) {
+ int track = a->find_track(NodePath(rest_bone_i.key));
if (track == -1) {
continue;
}
int new_track = a->add_track(Animation::TYPE_TRANSFORM3D);
- NodePath new_path = NodePath(rest_bone_i->key());
- BakeResetRestBone rest_bone = rest_bone_i->get();
+ NodePath new_path = NodePath(rest_bone_i.key);
+ const BakeResetRestBone rest_bone = rest_bone_i.value;
a->track_set_path(new_track, new_path);
for (int key_i = 0; key_i < a->track_get_key_count(track); key_i++) {
Vector3 loc;
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index c48d9bb117..2c9bc7dadf 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -980,6 +980,8 @@ 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: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "use_external/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
@@ -1259,6 +1261,8 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
//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;
String save_to_file;
@@ -1301,6 +1305,14 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
}
}
+ 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"];
+ }
+
if (mesh_settings.has("save_to_file/enabled") && bool(mesh_settings["save_to_file/enabled"]) && mesh_settings.has("save_to_file/path")) {
save_to_file = mesh_settings["save_to_file/path"];
if (!save_to_file.is_resource_file()) {
@@ -1310,8 +1322,9 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
}
if (generate_lods) {
- src_mesh_node->get_mesh()->generate_lods();
+ src_mesh_node->get_mesh()->generate_lods(merge_angle, split_angle);
}
+
if (create_shadow_meshes) {
src_mesh_node->get_mesh()->create_shadow_mesh();
}
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index 61745cb6ee..96a53b3257 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -86,28 +86,28 @@ void ResourceImporterTexture::update_imports() {
return;
}
- for (Map<StringName, MakeInfo>::Element *E = make_flags.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, MakeInfo> &E : make_flags) {
Ref<ConfigFile> cf;
cf.instantiate();
- String src_path = String(E->key()) + ".import";
+ String src_path = String(E.key) + ".import";
Error err = cf->load(src_path);
ERR_CONTINUE(err != OK);
bool changed = false;
- if (E->get().flags & MAKE_NORMAL_FLAG && int(cf->get_value("params", "compress/normal_map")) == 0) {
+ if (E.value.flags & MAKE_NORMAL_FLAG && int(cf->get_value("params", "compress/normal_map")) == 0) {
cf->set_value("params", "compress/normal_map", 1);
changed = true;
}
- if (E->get().flags & MAKE_ROUGHNESS_FLAG && int(cf->get_value("params", "roughness/mode")) == 0) {
- cf->set_value("params", "roughness/mode", E->get().channel_for_roughness + 2);
- cf->set_value("params", "roughness/src_normal", E->get().normal_path_for_roughness);
+ if (E.value.flags & MAKE_ROUGHNESS_FLAG && int(cf->get_value("params", "roughness/mode")) == 0) {
+ 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->get().flags & MAKE_3D_FLAG && bool(cf->get_value("params", "detect_3d/compress_to"))) {
+ if (E.value.flags & MAKE_3D_FLAG && bool(cf->get_value("params", "detect_3d/compress_to"))) {
int compress_to = cf->get_value("params", "detect_3d/compress_to");
cf->set_value("params", "detect_3d/compress_to", 0);
if (compress_to == 1) {
@@ -121,7 +121,7 @@ void ResourceImporterTexture::update_imports() {
if (changed) {
cf->save(src_path);
- to_reimport.push_back(E->key());
+ to_reimport.push_back(E.key);
}
}
diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp
index 869af209d3..36fd161c35 100644
--- a/editor/import/resource_importer_texture_atlas.cpp
+++ b/editor/import/resource_importer_texture_atlas.cpp
@@ -324,7 +324,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file
atlas_texture.instantiate();
atlas_texture->set_atlas(cache);
atlas_texture->set_region(Rect2(offset, pack_data.region.size));
- atlas_texture->set_margin(Rect2(pack_data.region.position, Size2(pack_data.image->get_width(), pack_data.image->get_height()) - pack_data.region.size));
+ atlas_texture->set_margin(Rect2(pack_data.region.position, pack_data.image->get_size() - pack_data.region.size));
texture = atlas_texture;
} else {
diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp
index 4bcb6863fb..7ab5308a47 100644
--- a/editor/import/scene_import_settings.cpp
+++ b/editor/import/scene_import_settings.cpp
@@ -434,7 +434,7 @@ void SceneImportSettings::_update_camera() {
}
}
- Vector3 center = camera_aabb.position + camera_aabb.size * 0.5;
+ Vector3 center = camera_aabb.get_center();
float camera_size = camera_aabb.get_longest_axis_size();
camera->set_orthogonal(camera_size * zoom, 0.0001, camera_size * 2);
@@ -771,52 +771,52 @@ void SceneImportSettings::_re_import() {
Dictionary subresources;
- for (Map<String, NodeData>::Element *E = node_map.front(); E; E = E->next()) {
- if (E->get().settings.size()) {
+ for (KeyValue<String, NodeData> &E : node_map) {
+ if (E.value.settings.size()) {
Dictionary d;
- for (Map<StringName, Variant>::Element *F = E->get().settings.front(); F; F = F->next()) {
- d[String(F->key())] = F->get();
+ for (const KeyValue<StringName, Variant> &F : E.value.settings) {
+ d[String(F.key)] = F.value;
}
- nodes[E->key()] = d;
+ nodes[E.key] = d;
}
}
if (nodes.size()) {
subresources["nodes"] = nodes;
}
- for (Map<String, MaterialData>::Element *E = material_map.front(); E; E = E->next()) {
- if (E->get().settings.size()) {
+ for (KeyValue<String, MaterialData> &E : material_map) {
+ if (E.value.settings.size()) {
Dictionary d;
- for (Map<StringName, Variant>::Element *F = E->get().settings.front(); F; F = F->next()) {
- d[String(F->key())] = F->get();
+ for (const KeyValue<StringName, Variant> &F : E.value.settings) {
+ d[String(F.key)] = F.value;
}
- materials[E->key()] = d;
+ materials[E.key] = d;
}
}
if (materials.size()) {
subresources["materials"] = materials;
}
- for (Map<String, MeshData>::Element *E = mesh_map.front(); E; E = E->next()) {
- if (E->get().settings.size()) {
+ for (KeyValue<String, MeshData> &E : mesh_map) {
+ if (E.value.settings.size()) {
Dictionary d;
- for (Map<StringName, Variant>::Element *F = E->get().settings.front(); F; F = F->next()) {
- d[String(F->key())] = F->get();
+ for (const KeyValue<StringName, Variant> &F : E.value.settings) {
+ d[String(F.key)] = F.value;
}
- meshes[E->key()] = d;
+ meshes[E.key] = d;
}
}
if (meshes.size()) {
subresources["meshes"] = meshes;
}
- for (Map<String, AnimationData>::Element *E = animation_map.front(); E; E = E->next()) {
- if (E->get().settings.size()) {
+ for (KeyValue<String, AnimationData> &E : animation_map) {
+ if (E.value.settings.size()) {
Dictionary d;
- for (Map<StringName, Variant>::Element *F = E->get().settings.front(); F; F = F->next()) {
- d[String(F->key())] = F->get();
+ for (const KeyValue<StringName, Variant> &F : E.value.settings) {
+ d[String(F.key)] = F.value;
}
- animations[E->key()] = d;
+ animations[E.key] = d;
}
}
if (animations.size()) {
@@ -889,8 +889,8 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
switch (current_action) {
case ACTION_EXTRACT_MATERIALS: {
- for (Map<String, MaterialData>::Element *E = material_map.front(); E; E = E->next()) {
- MaterialData &md = material_map[E->key()];
+ for (const KeyValue<String, MaterialData> &E : material_map) {
+ MaterialData &md = material_map[E.key];
TreeItem *item = external_path_tree->create_item(root);
@@ -905,7 +905,7 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
item->set_text(2, "Already External");
item->set_tooltip(2, TTR("This material already references an external file, no action will be taken.\nDisable the external property for it to be extracted again."));
} else {
- item->set_metadata(0, E->key());
+ item->set_metadata(0, E.key);
item->set_editable(0, true);
item->set_checked(0, true);
String path = p_path.plus_file(name);
@@ -942,8 +942,8 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
external_paths->get_ok_button()->set_text(TTR("Extract"));
} break;
case ACTION_CHOOSE_MESH_SAVE_PATHS: {
- for (Map<String, MeshData>::Element *E = mesh_map.front(); E; E = E->next()) {
- MeshData &md = mesh_map[E->key()];
+ for (const KeyValue<String, MeshData> &E : mesh_map) {
+ MeshData &md = mesh_map[E.key];
TreeItem *item = external_path_tree->create_item(root);
@@ -958,7 +958,7 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
item->set_text(2, "Already Saving");
item->set_tooltip(2, TTR("This mesh already saves to an external resource, no action will be taken."));
} else {
- item->set_metadata(0, E->key());
+ item->set_metadata(0, E.key);
item->set_editable(0, true);
item->set_checked(0, true);
String path = p_path.plus_file(name);
@@ -995,8 +995,8 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
external_paths->get_ok_button()->set_text(TTR("Set Paths"));
} break;
case ACTION_CHOOSE_ANIMATION_SAVE_PATHS: {
- for (Map<String, AnimationData>::Element *E = animation_map.front(); E; E = E->next()) {
- AnimationData &ad = animation_map[E->key()];
+ for (const KeyValue<String, AnimationData> &E : animation_map) {
+ AnimationData &ad = animation_map[E.key];
TreeItem *item = external_path_tree->create_item(root);
@@ -1010,7 +1010,7 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
item->set_text(2, "Already Saving");
item->set_tooltip(2, TTR("This animation already saves to an external resource, no action will be taken."));
} else {
- item->set_metadata(0, E->key());
+ item->set_metadata(0, E.key);
item->set_editable(0, true);
item->set_checked(0, true);
String path = p_path.plus_file(name);
diff --git a/editor/import/scene_importer_mesh.cpp b/editor/import/scene_importer_mesh.cpp
index 5e6dd08e79..370394b475 100644
--- a/editor/import/scene_importer_mesh.cpp
+++ b/editor/import/scene_importer_mesh.cpp
@@ -30,11 +30,99 @@
#include "scene_importer_mesh.h"
-#include "core/math/math_defs.h"
+#include "core/math/random_pcg.h"
+#include "core/math/static_raycaster.h"
#include "scene/resources/surface_tool.h"
#include <cstdint>
+void EditorSceneImporterMesh::Surface::split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) {
+ ERR_FAIL_COND(arrays.size() != RS::ARRAY_MAX);
+
+ const PackedVector3Array &vertices = arrays[RS::ARRAY_VERTEX];
+ int current_vertex_count = vertices.size();
+ int new_vertex_count = p_indices.size();
+ int final_vertex_count = current_vertex_count + new_vertex_count;
+ const int *indices_ptr = p_indices.ptr();
+
+ for (int i = 0; i < arrays.size(); i++) {
+ if (i == RS::ARRAY_INDEX) {
+ continue;
+ }
+
+ if (arrays[i].get_type() == Variant::NIL) {
+ continue;
+ }
+
+ switch (arrays[i].get_type()) {
+ case Variant::PACKED_VECTOR3_ARRAY: {
+ PackedVector3Array data = arrays[i];
+ data.resize(final_vertex_count);
+ Vector3 *data_ptr = data.ptrw();
+ if (i == RS::ARRAY_NORMAL) {
+ const Vector3 *normals_ptr = p_normals.ptr();
+ memcpy(&data_ptr[current_vertex_count], normals_ptr, sizeof(Vector3) * new_vertex_count);
+ } else {
+ for (int j = 0; j < new_vertex_count; j++) {
+ data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
+ }
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_VECTOR2_ARRAY: {
+ PackedVector2Array data = arrays[i];
+ data.resize(final_vertex_count);
+ Vector2 *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+ PackedFloat32Array data = arrays[i];
+ int elements = data.size() / current_vertex_count;
+ data.resize(final_vertex_count * elements);
+ float *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(float) * elements);
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ PackedInt32Array data = arrays[i];
+ int elements = data.size() / current_vertex_count;
+ data.resize(final_vertex_count * elements);
+ int32_t *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(int32_t) * elements);
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ PackedByteArray data = arrays[i];
+ int elements = data.size() / current_vertex_count;
+ data.resize(final_vertex_count * elements);
+ uint8_t *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(uint8_t) * elements);
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_COLOR_ARRAY: {
+ PackedColorArray data = arrays[i];
+ data.resize(final_vertex_count);
+ Color *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
+ }
+ } break;
+ default: {
+ ERR_FAIL_MSG("Uhandled array type.");
+ } break;
+ }
+ }
+}
+
void EditorSceneImporterMesh::add_blend_shape(const String &p_name) {
ERR_FAIL_COND(surfaces.size() > 0);
blend_shapes.push_back(p_name);
@@ -157,29 +245,14 @@ void EditorSceneImporterMesh::set_surface_material(int p_surface, const Ref<Mate
mesh.unref();
}
-Basis EditorSceneImporterMesh::compute_rotation_matrix_from_ortho_6d(Vector3 p_x_raw, Vector3 p_y_raw) {
- Vector3 x = p_x_raw.normalized();
- Vector3 z = x.cross(p_y_raw);
- z = z.normalized();
- Vector3 y = z.cross(x);
- Basis basis;
- basis.set_axis(Vector3::AXIS_X, x);
- basis.set_axis(Vector3::AXIS_Y, y);
- basis.set_axis(Vector3::AXIS_Z, z);
- return basis;
-}
-
-void EditorSceneImporterMesh::generate_lods() {
- if (!SurfaceTool::simplify_func) {
- return;
- }
+void EditorSceneImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle) {
if (!SurfaceTool::simplify_scale_func) {
return;
}
- if (!SurfaceTool::simplify_sloppy_func) {
+ if (!SurfaceTool::simplify_with_attrib_func) {
return;
}
- if (!SurfaceTool::simplify_with_attrib_func) {
+ if (!SurfaceTool::optimize_vertex_cache_func) {
return;
}
@@ -190,67 +263,343 @@ void EditorSceneImporterMesh::generate_lods() {
surfaces.write[i].lods.clear();
Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
- Vector<int> indices = surfaces[i].arrays[RS::ARRAY_INDEX];
- if (indices.size() == 0) {
+ PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX];
+ Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL];
+ Vector<Vector2> uvs = surfaces[i].arrays[RS::ARRAY_TEX_UV];
+
+ unsigned int index_count = indices.size();
+ unsigned int vertex_count = vertices.size();
+
+ if (index_count == 0) {
continue; //no lods if no indices
}
- Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL];
- uint32_t vertex_count = vertices.size();
+
const Vector3 *vertices_ptr = vertices.ptr();
- Vector<float> attributes;
- Vector<float> normal_weights;
- int32_t attribute_count = 6;
- if (normals.size()) {
- attributes.resize(normals.size() * attribute_count);
- for (int32_t normal_i = 0; normal_i < normals.size(); normal_i++) {
- Basis basis;
- basis.set_euler(normals[normal_i]);
- Vector3 basis_x = basis.get_axis(0);
- Vector3 basis_y = basis.get_axis(1);
- basis = compute_rotation_matrix_from_ortho_6d(basis_x, basis_y);
- basis_x = basis.get_axis(0);
- basis_y = basis.get_axis(1);
- attributes.write[normal_i * attribute_count + 0] = basis_x.x;
- attributes.write[normal_i * attribute_count + 1] = basis_x.y;
- attributes.write[normal_i * attribute_count + 2] = basis_x.z;
- attributes.write[normal_i * attribute_count + 3] = basis_y.x;
- attributes.write[normal_i * attribute_count + 4] = basis_y.y;
- attributes.write[normal_i * attribute_count + 5] = basis_y.z;
- }
- normal_weights.resize(vertex_count);
- for (int32_t weight_i = 0; weight_i < normal_weights.size(); weight_i++) {
- normal_weights.write[weight_i] = 1.0;
+ const int *indices_ptr = indices.ptr();
+
+ if (normals.is_empty()) {
+ normals.resize(vertices.size());
+ Vector3 *n_ptr = normals.ptrw();
+ for (unsigned int j = 0; j < index_count; j += 3) {
+ const Vector3 &v0 = vertices_ptr[indices_ptr[j + 0]];
+ const Vector3 &v1 = vertices_ptr[indices_ptr[j + 1]];
+ const Vector3 &v2 = vertices_ptr[indices_ptr[j + 2]];
+ Vector3 n = vec3_cross(v0 - v2, v0 - v1).normalized();
+ n_ptr[j + 0] = n;
+ n_ptr[j + 1] = n;
+ n_ptr[j + 2] = n;
}
- } else {
- attribute_count = 0;
}
- const int min_indices = 10;
- const float error_tolerance = 1.44224'95703; // Cube root of 3
- const float threshold = 1.0 / error_tolerance;
- int index_target = indices.size() * threshold;
- float max_mesh_error_percentage = 1e0f;
+
+ float normal_merge_threshold = Math::cos(Math::deg2rad(p_normal_merge_angle));
+ float normal_pre_split_threshold = Math::cos(Math::deg2rad(MIN(180.0f, p_normal_split_angle * 2.0f)));
+ float normal_split_threshold = Math::cos(Math::deg2rad(p_normal_split_angle));
+ const Vector3 *normals_ptr = normals.ptr();
+
+ Map<Vector3, LocalVector<Pair<int, int>>> unique_vertices;
+
+ LocalVector<int> vertex_remap;
+ LocalVector<int> vertex_inverse_remap;
+ LocalVector<Vector3> merged_vertices;
+ LocalVector<Vector3> merged_normals;
+ LocalVector<int> merged_normals_counts;
+ const Vector2 *uvs_ptr = uvs.ptr();
+
+ for (unsigned int j = 0; j < vertex_count; j++) {
+ const Vector3 &v = vertices_ptr[j];
+ const Vector3 &n = normals_ptr[j];
+
+ Map<Vector3, LocalVector<Pair<int, int>>>::Element *E = unique_vertices.find(v);
+
+ if (E) {
+ const LocalVector<Pair<int, int>> &close_verts = E->get();
+
+ bool found = false;
+ for (unsigned int k = 0; k < close_verts.size(); k++) {
+ const Pair<int, int> &idx = close_verts[k];
+
+ // TODO check more attributes?
+ if ((!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2) && normals[idx.second].dot(n) > normal_merge_threshold) {
+ vertex_remap.push_back(idx.first);
+ merged_normals[idx.first] += normals[idx.second];
+ merged_normals_counts[idx.first]++;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ int vcount = merged_vertices.size();
+ unique_vertices[v].push_back(Pair<int, int>(vcount, j));
+ vertex_inverse_remap.push_back(j);
+ merged_vertices.push_back(v);
+ vertex_remap.push_back(vcount);
+ merged_normals.push_back(normals_ptr[j]);
+ merged_normals_counts.push_back(1);
+ }
+ } else {
+ int vcount = merged_vertices.size();
+ unique_vertices[v] = LocalVector<Pair<int, int>>();
+ unique_vertices[v].push_back(Pair<int, int>(vcount, j));
+ vertex_inverse_remap.push_back(j);
+ merged_vertices.push_back(v);
+ vertex_remap.push_back(vcount);
+ merged_normals.push_back(normals_ptr[j]);
+ merged_normals_counts.push_back(1);
+ }
+ }
+
+ LocalVector<int> merged_indices;
+ merged_indices.resize(index_count);
+ for (unsigned int j = 0; j < index_count; j++) {
+ merged_indices[j] = vertex_remap[indices[j]];
+ }
+
+ unsigned int merged_vertex_count = merged_vertices.size();
+ const Vector3 *merged_vertices_ptr = merged_vertices.ptr();
+ const int32_t *merged_indices_ptr = merged_indices.ptr();
+
+ {
+ const int *counts_ptr = merged_normals_counts.ptr();
+ Vector3 *merged_normals_ptrw = merged_normals.ptr();
+ for (unsigned int j = 0; j < merged_vertex_count; j++) {
+ merged_normals_ptrw[j] /= counts_ptr[j];
+ }
+ }
+
+ LocalVector<float> normal_weights;
+ normal_weights.resize(merged_vertex_count);
+ for (unsigned int j = 0; j < merged_vertex_count; j++) {
+ normal_weights[j] = 2.0; // Give some weight to normal preservation, may be worth exposing as an import setting
+ }
+
+ const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target
+ float scale = SurfaceTool::simplify_scale_func((const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3));
float mesh_error = 0.0f;
- float scale = SurfaceTool::simplify_scale_func((const float *)vertices_ptr, vertex_count, sizeof(Vector3));
- while (index_target > min_indices) {
- Vector<int> new_indices;
- new_indices.resize(indices.size());
- size_t new_len = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const unsigned int *)indices.ptr(), indices.size(), (const float *)vertices_ptr, vertex_count, sizeof(Vector3), index_target, max_mesh_error_percentage, &mesh_error, (float *)attributes.ptrw(), normal_weights.ptrw(), attribute_count);
- if ((int)new_len > (index_target * error_tolerance)) {
- break;
+
+ unsigned int index_target = 12; // Start with the smallest target, 4 triangles
+ unsigned int last_index_count = 0;
+
+ int split_vertex_count = vertex_count;
+ LocalVector<Vector3> split_vertex_normals;
+ LocalVector<int> split_vertex_indices;
+ split_vertex_normals.reserve(index_count / 3);
+ split_vertex_indices.reserve(index_count / 3);
+
+ RandomPCG pcg;
+ pcg.seed(123456789); // Keep seed constant across imports
+
+ Ref<StaticRaycaster> raycaster = StaticRaycaster::create();
+ if (raycaster.is_valid()) {
+ raycaster->add_mesh(vertices, indices, 0);
+ raycaster->commit();
+ }
+
+ while (index_target < index_count) {
+ PackedInt32Array new_indices;
+ new_indices.resize(index_count);
+
+ size_t new_index_count = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const uint32_t *)merged_indices_ptr, index_count, (const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3), index_target, max_mesh_error, &mesh_error, (float *)merged_normals.ptr(), normal_weights.ptr(), 3);
+
+ if (new_index_count < last_index_count * 1.5f) {
+ index_target = index_target * 1.5f;
+ continue;
}
- Surface::LOD lod;
- lod.distance = mesh_error * scale;
- if (Math::is_zero_approx(mesh_error)) {
+
+ if (new_index_count <= 0 || (new_index_count >= (index_count * 0.75f))) {
break;
}
- if (new_len <= 0) {
- break;
+
+ new_indices.resize(new_index_count);
+
+ LocalVector<LocalVector<int>> vertex_corners;
+ vertex_corners.resize(vertex_count);
+ {
+ int *ptrw = new_indices.ptrw();
+ for (unsigned int j = 0; j < new_index_count; j++) {
+ const int &remapped = vertex_inverse_remap[ptrw[j]];
+ vertex_corners[remapped].push_back(j);
+ ptrw[j] = remapped;
+ }
}
- new_indices.resize(new_len);
+
+ if (raycaster.is_valid()) {
+ float error_factor = 1.0f / (scale * MAX(mesh_error, 0.15));
+ const float ray_bias = 0.05;
+ float ray_length = ray_bias + mesh_error * scale * 3.0f;
+
+ Vector<StaticRaycaster::Ray> rays;
+ LocalVector<Vector2> ray_uvs;
+
+ int32_t *new_indices_ptr = new_indices.ptrw();
+
+ int current_ray_count = 0;
+ for (unsigned int j = 0; j < new_index_count; j += 3) {
+ const Vector3 &v0 = vertices_ptr[new_indices_ptr[j + 0]];
+ const Vector3 &v1 = vertices_ptr[new_indices_ptr[j + 1]];
+ const Vector3 &v2 = vertices_ptr[new_indices_ptr[j + 2]];
+ Vector3 face_normal = vec3_cross(v0 - v2, v0 - v1);
+ float face_area = face_normal.length(); // Actually twice the face area, since it's the same error_factor on all faces, we don't care
+
+ Vector3 dir = face_normal / face_area;
+ int ray_count = CLAMP(5.0 * face_area * error_factor, 16, 64);
+
+ rays.resize(current_ray_count + ray_count);
+ StaticRaycaster::Ray *rays_ptr = rays.ptrw();
+
+ ray_uvs.resize(current_ray_count + ray_count);
+ Vector2 *ray_uvs_ptr = ray_uvs.ptr();
+
+ for (int k = 0; k < ray_count; k++) {
+ float u = pcg.randf();
+ float v = pcg.randf();
+
+ if (u + v >= 1.0f) {
+ u = 1.0f - u;
+ v = 1.0f - v;
+ }
+
+ u = 0.9f * u + 0.05f / 3.0f; // Give barycentric coordinates some padding, we don't want to sample right on the edge
+ v = 0.9f * v + 0.05f / 3.0f; // v = (v - one_third) * 0.95f + one_third;
+ float w = 1.0f - u - v;
+
+ Vector3 org = v0 * w + v1 * u + v2 * v;
+ org -= dir * ray_bias;
+ rays_ptr[current_ray_count + k] = StaticRaycaster::Ray(org, dir, 0.0f, ray_length);
+ rays_ptr[current_ray_count + k].id = j / 3;
+ ray_uvs_ptr[current_ray_count + k] = Vector2(u, v);
+ }
+
+ current_ray_count += ray_count;
+ }
+
+ raycaster->intersect(rays);
+
+ LocalVector<Vector3> ray_normals;
+ LocalVector<float> ray_normal_weights;
+
+ ray_normals.resize(new_index_count);
+ ray_normal_weights.resize(new_index_count);
+
+ for (unsigned int j = 0; j < new_index_count; j++) {
+ ray_normal_weights[j] = 0.0f;
+ }
+
+ const StaticRaycaster::Ray *rp = rays.ptr();
+ for (int j = 0; j < rays.size(); j++) {
+ if (rp[j].geomID != 0) { // Ray missed
+ continue;
+ }
+
+ if (rp[j].normal.normalized().dot(rp[j].dir) > 0.0f) { // Hit a back face.
+ continue;
+ }
+
+ const float &u = rp[j].u;
+ const float &v = rp[j].v;
+ const float w = 1.0f - u - v;
+
+ const unsigned int &hit_tri_id = rp[j].primID;
+ const unsigned int &orig_tri_id = rp[j].id;
+
+ const Vector3 &n0 = normals_ptr[indices_ptr[hit_tri_id * 3 + 0]];
+ const Vector3 &n1 = normals_ptr[indices_ptr[hit_tri_id * 3 + 1]];
+ const Vector3 &n2 = normals_ptr[indices_ptr[hit_tri_id * 3 + 2]];
+ Vector3 normal = n0 * w + n1 * u + n2 * v;
+
+ Vector2 orig_uv = ray_uvs[j];
+ float orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y };
+ for (int k = 0; k < 3; k++) {
+ int idx = orig_tri_id * 3 + k;
+ float weight = orig_bary[k];
+ ray_normals[idx] += normal * weight;
+ ray_normal_weights[idx] += weight;
+ }
+ }
+
+ for (unsigned int j = 0; j < new_index_count; j++) {
+ if (ray_normal_weights[j] < 1.0f) { // Not enough data, the new normal would be just a bad guess
+ ray_normals[j] = Vector3();
+ } else {
+ ray_normals[j] /= ray_normal_weights[j];
+ }
+ }
+
+ LocalVector<LocalVector<int>> normal_group_indices;
+ LocalVector<Vector3> normal_group_averages;
+ normal_group_indices.reserve(24);
+ normal_group_averages.reserve(24);
+
+ for (unsigned int j = 0; j < vertex_count; j++) {
+ const LocalVector<int> &corners = vertex_corners[j];
+ const Vector3 &vertex_normal = normals_ptr[j];
+
+ for (unsigned int k = 0; k < corners.size(); k++) {
+ const int &corner_idx = corners[k];
+ const Vector3 &ray_normal = ray_normals[corner_idx];
+
+ if (ray_normal.length_squared() < CMP_EPSILON2) {
+ continue;
+ }
+
+ bool found = false;
+ for (unsigned int l = 0; l < normal_group_indices.size(); l++) {
+ LocalVector<int> &group_indices = normal_group_indices[l];
+ Vector3 n = normal_group_averages[l] / group_indices.size();
+ if (n.dot(ray_normal) > normal_pre_split_threshold) {
+ found = true;
+ group_indices.push_back(corner_idx);
+ normal_group_averages[l] += ray_normal;
+ break;
+ }
+ }
+
+ if (!found) {
+ LocalVector<int> new_group;
+ new_group.push_back(corner_idx);
+ normal_group_indices.push_back(new_group);
+ normal_group_averages.push_back(ray_normal);
+ }
+ }
+
+ for (unsigned int k = 0; k < normal_group_indices.size(); k++) {
+ LocalVector<int> &group_indices = normal_group_indices[k];
+ Vector3 n = normal_group_averages[k] / group_indices.size();
+
+ if (vertex_normal.dot(n) < normal_split_threshold) {
+ split_vertex_indices.push_back(j);
+ split_vertex_normals.push_back(n);
+ int new_idx = split_vertex_count++;
+ for (unsigned int l = 0; l < group_indices.size(); l++) {
+ new_indices_ptr[group_indices[l]] = new_idx;
+ }
+ }
+ }
+
+ normal_group_indices.clear();
+ normal_group_averages.clear();
+ }
+ }
+
+ Surface::LOD lod;
+ lod.distance = MAX(mesh_error * scale, CMP_EPSILON2);
lod.indices = new_indices;
- print_line("Lod " + itos(surfaces.write[i].lods.size()) + " begin with " + itos(indices.size() / 3) + " triangles and shoot for " + itos(index_target / 3) + " triangles. Got " + itos(new_len / 3) + " triangles. Lod screen ratio " + rtos(lod.distance));
surfaces.write[i].lods.push_back(lod);
- index_target *= threshold;
+ index_target = MAX(new_index_count, index_target) * 2;
+ last_index_count = new_index_count;
+
+ if (mesh_error == 0.0f) {
+ break;
+ }
+ }
+
+ surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals);
+ surfaces.write[i].lods.sort_custom<Surface::LODComparator>();
+
+ for (int j = 0; j < surfaces.write[i].lods.size(); j++) {
+ Surface::LOD &lod = surfaces.write[i].lods.write[j];
+ unsigned int *lod_indices_ptr = (unsigned int *)lod.indices.ptrw();
+ SurfaceTool::optimize_vertex_cache_func(lod_indices_ptr, lod_indices_ptr, lod.indices.size(), split_vertex_count);
}
}
}
@@ -347,7 +696,7 @@ void EditorSceneImporterMesh::create_shadow_mesh() {
Map<Vector3, int> unique_vertices;
const Vector3 *vptr = vertices.ptr();
for (int j = 0; j < vertex_count; j++) {
- Vector3 v = vptr[j];
+ const Vector3 &v = vptr[j];
Map<Vector3, int>::Element *E = unique_vertices.find(v);
@@ -397,9 +746,9 @@ void EditorSceneImporterMesh::create_shadow_mesh() {
index_wptr = new_indices.ptrw();
for (int k = 0; k < index_count; k++) {
- int index = index_rptr[j];
+ int index = index_rptr[k];
ERR_FAIL_INDEX(index, vertex_count);
- index_wptr[j] = vertex_remap[index];
+ index_wptr[k] = vertex_remap[index];
}
lods[surfaces[i].lods[j].distance] = new_indices;
@@ -436,9 +785,9 @@ void EditorSceneImporterMesh::_set_data(const Dictionary &p_data) {
if (s.has("lods")) {
lods = s["lods"];
}
- Array blend_shapes;
- if (s.has("blend_shapes")) {
- blend_shapes = s["blend_shapes"];
+ Array b_shapes;
+ if (s.has("b_shapes")) {
+ b_shapes = s["b_shapes"];
}
Ref<Material> material;
if (s.has("material")) {
@@ -448,7 +797,7 @@ void EditorSceneImporterMesh::_set_data(const Dictionary &p_data) {
if (s.has("flags")) {
flags = s["flags"];
}
- add_surface(prim, arr, blend_shapes, lods, material, name, flags);
+ add_surface(prim, arr, b_shapes, lods, material, name, flags);
}
}
}
@@ -617,8 +966,8 @@ Ref<NavigationMesh> EditorSceneImporterMesh::create_navigation_mesh() {
Vector<Vector3> vertices;
vertices.resize(unique_vertices.size());
- for (Map<Vector3, int>::Element *E = unique_vertices.front(); E; E = E->next()) {
- vertices.write[E->get()] = E->key();
+ for (const KeyValue<Vector3, int> &E : unique_vertices) {
+ vertices.write[E.value] = E.key;
}
Ref<NavigationMesh> nm;
diff --git a/editor/import/scene_importer_mesh.h b/editor/import/scene_importer_mesh.h
index d32b1fdf74..111b191cae 100644
--- a/editor/import/scene_importer_mesh.h
+++ b/editor/import/scene_importer_mesh.h
@@ -32,6 +32,7 @@
#define EDITOR_SCENE_IMPORTER_MESH_H
#include "core/io/resource.h"
+#include "core/templates/local_vector.h"
#include "scene/resources/concave_polygon_shape_3d.h"
#include "scene/resources/convex_polygon_shape_3d.h"
#include "scene/resources/mesh.h"
@@ -55,12 +56,20 @@ class EditorSceneImporterMesh : public Resource {
Vector<BlendShape> blend_shape_data;
struct LOD {
Vector<int> indices;
- float distance;
+ float distance = 0.0f;
};
Vector<LOD> lods;
Ref<Material> material;
String name;
uint32_t flags = 0;
+
+ struct LODComparator {
+ _FORCE_INLINE_ bool operator()(const LOD &l, const LOD &r) const {
+ return l.distance < r.distance;
+ }
+ };
+
+ void split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals);
};
Vector<Surface> surfaces;
Vector<String> blend_shapes;
@@ -71,7 +80,6 @@ class EditorSceneImporterMesh : public Resource {
Ref<EditorSceneImporterMesh> shadow_mesh;
Size2i lightmap_size_hint;
- Basis compute_rotation_matrix_from_ortho_6d(Vector3 p_x_raw, Vector3 y_raw);
protected:
void _set_data(const Dictionary &p_data);
@@ -103,7 +111,7 @@ public:
void set_surface_material(int p_surface, const Ref<Material> &p_material);
- void generate_lods();
+ void generate_lods(float p_normal_merge_angle, float p_normal_split_angle);
void create_shadow_mesh();
Ref<EditorSceneImporterMesh> get_shadow_mesh() const;