summaryrefslogtreecommitdiff
path: root/editor/import
diff options
context:
space:
mode:
Diffstat (limited to 'editor/import')
-rw-r--r--editor/import/collada.cpp60
-rw-r--r--editor/import/collada.h22
-rw-r--r--editor/import/dynamicfont_import_settings.cpp1889
-rw-r--r--editor/import/dynamicfont_import_settings.h167
-rw-r--r--editor/import/editor_import_collada.cpp160
-rw-r--r--editor/import/editor_import_plugin.cpp170
-rw-r--r--editor/import/editor_import_plugin.h13
-rw-r--r--editor/import/editor_importer_bake_reset.cpp234
-rw-r--r--editor/import/editor_importer_bake_reset.h54
-rw-r--r--editor/import/resource_importer_bitmask.cpp4
-rw-r--r--editor/import/resource_importer_bmfont.cpp797
-rw-r--r--editor/import/resource_importer_bmfont.h56
-rw-r--r--editor/import/resource_importer_csv_translation.cpp4
-rw-r--r--editor/import/resource_importer_dynamicfont.cpp304
-rw-r--r--editor/import/resource_importer_dynamicfont.h71
-rw-r--r--editor/import/resource_importer_image.cpp2
-rw-r--r--editor/import/resource_importer_imagefont.cpp162
-rw-r--r--editor/import/resource_importer_imagefont.h58
-rw-r--r--editor/import/resource_importer_layered_texture.cpp7
-rw-r--r--editor/import/resource_importer_obj.cpp21
-rw-r--r--editor/import/resource_importer_scene.cpp359
-rw-r--r--editor/import/resource_importer_scene.h220
-rw-r--r--editor/import/resource_importer_shader_file.cpp6
-rw-r--r--editor/import/resource_importer_texture.cpp41
-rw-r--r--editor/import/resource_importer_texture.h2
-rw-r--r--editor/import/resource_importer_texture_atlas.cpp18
-rw-r--r--editor/import/resource_importer_wav.cpp6
-rw-r--r--editor/import/scene_import_settings.cpp174
-rw-r--r--editor/import/scene_import_settings.h4
-rw-r--r--editor/import/scene_importer_mesh.cpp121
-rw-r--r--editor/import/scene_importer_mesh.h14
31 files changed, 4785 insertions, 435 deletions
diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp
index e38034dd8c..b6d0927ce6 100644
--- a/editor/import/collada.cpp
+++ b/editor/import/collada.cpp
@@ -50,8 +50,8 @@ String Collada::Effect::get_texture_path(const String &p_source, Collada &state)
return state.state.image_map[image].path;
}
-Transform Collada::get_root_transform() const {
- Transform unit_scale_transform;
+Transform3D Collada::get_root_transform() const {
+ Transform3D unit_scale_transform;
#ifndef COLLADA_IMPORT_SCALE_SCENE
unit_scale_transform.scale(Vector3(state.unit_scale, state.unit_scale, state.unit_scale));
#endif
@@ -74,8 +74,8 @@ static String _uri_to_id(const String &p_uri) {
/** HELPER FUNCTIONS **/
-Transform Collada::fix_transform(const Transform &p_transform) {
- Transform tr = p_transform;
+Transform3D Collada::fix_transform(const Transform3D &p_transform) {
+ Transform3D tr = p_transform;
#ifndef NO_UP_AXIS_SWAP
@@ -102,8 +102,8 @@ Transform Collada::fix_transform(const Transform &p_transform) {
//return state.matrix_fix * p_transform;
}
-static Transform _read_transform_from_array(const Vector<float> &array, int ofs = 0) {
- Transform tr;
+static Transform3D _read_transform_from_array(const Vector<float> &array, int ofs = 0) {
+ Transform3D tr;
// i wonder why collada matrices are transposed, given that's opposed to opengl..
tr.basis.elements[0][0] = array[0 + ofs];
tr.basis.elements[0][1] = array[1 + ofs];
@@ -122,11 +122,11 @@ static Transform _read_transform_from_array(const Vector<float> &array, int ofs
/* STRUCTURES */
-Transform Collada::Node::compute_transform(Collada &state) const {
- Transform xform;
+Transform3D Collada::Node::compute_transform(Collada &state) const {
+ Transform3D xform;
for (int i = 0; i < xform_list.size(); i++) {
- Transform xform_step;
+ Transform3D xform_step;
const XForm &xf = xform_list[i];
switch (xf.op) {
case XForm::OP_ROTATE: {
@@ -165,11 +165,11 @@ Transform Collada::Node::compute_transform(Collada &state) const {
return xform;
}
-Transform Collada::Node::get_transform() const {
+Transform3D Collada::Node::get_transform() const {
return default_transform;
}
-Transform Collada::Node::get_global_transform() const {
+Transform3D Collada::Node::get_global_transform() const {
if (parent) {
return parent->get_global_transform() * default_transform;
} else {
@@ -201,14 +201,14 @@ Vector<float> Collada::AnimationTrack::get_value_at_time(float p_time) const {
if (keys[i].data.size() == 16) {
//interpolate a matrix
- Transform src = _read_transform_from_array(keys[i - 1].data);
- Transform dst = _read_transform_from_array(keys[i].data);
+ Transform3D src = _read_transform_from_array(keys[i - 1].data);
+ Transform3D dst = _read_transform_from_array(keys[i].data);
- Transform interp = c < 0.001 ? src : src.interpolate_with(dst, c);
+ Transform3D interp = c < 0.001 ? src : src.interpolate_with(dst, c);
Vector<float> ret;
ret.resize(16);
- Transform tr;
+ Transform3D tr;
// i wonder why collada matrices are transposed, given that's opposed to opengl..
ret.write[0] = interp.basis.elements[0][0];
ret.write[1] = interp.basis.elements[0][1];
@@ -287,7 +287,7 @@ void Collada::_parse_image(XMLParser &parser) {
if (state.version < State::Version(1, 4, 0)) {
/* <1.4 */
String path = parser.get_attribute_value("source").strip_edges();
- if (path.find("://") == -1 && path.is_rel_path()) {
+ if (path.find("://") == -1 && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
image.path = ProjectSettings::get_singleton()->localize_path(state.local_path.get_base_dir().plus_file(path.uri_decode()));
}
@@ -300,7 +300,7 @@ void Collada::_parse_image(XMLParser &parser) {
parser.read();
String path = parser.get_node_data().strip_edges().uri_decode();
- if (path.find("://") == -1 && path.is_rel_path()) {
+ if (path.find("://") == -1 && path.is_relative_path()) {
// path is relative to file being loaded, so convert to a resource path
path = ProjectSettings::get_singleton()->localize_path(state.local_path.get_base_dir().plus_file(path));
@@ -410,10 +410,9 @@ Vector<String> Collada::_read_string_array(XMLParser &parser) {
return array;
}
-Transform Collada::_read_transform(XMLParser &parser) {
- if (parser.is_empty()) {
- return Transform();
- }
+Transform3D Collada::_read_transform(XMLParser &parser) {
+ if (parser.is_empty())
+ return Transform3D();
Vector<String> array;
while (parser.read() == OK) {
@@ -429,7 +428,7 @@ Transform Collada::_read_transform(XMLParser &parser) {
}
}
- ERR_FAIL_COND_V(array.size() != 16, Transform());
+ ERR_FAIL_COND_V(array.size() != 16, Transform3D());
Vector<float> farr;
farr.resize(16);
for (int i = 0; i < 16; i++) {
@@ -961,6 +960,7 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name
} else if (section == "vertices") {
MeshData::Vertices vert;
String id = parser.get_attribute_value("id");
+ int last_ref = 0;
while (parser.read() == OK) {
if (parser.get_node_type() == XMLParser::NODE_ELEMENT) {
@@ -968,6 +968,10 @@ void Collada::_parse_mesh_geometry(XMLParser &parser, String p_id, String p_name
String semantic = parser.get_attribute_value("semantic");
String source = _uri_to_id(parser.get_attribute_value("source"));
+ if (semantic == "TEXCOORD") {
+ semantic = "TEXCOORD" + itos(last_ref++);
+ }
+
vert.sources[semantic] = source;
COLLADA_PRINT(section + " input semantic: " + semantic + " source: " + source);
@@ -1197,7 +1201,7 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
/* STORE REST MATRICES */
- Vector<Transform> rests;
+ Vector<Transform3D> rests;
ERR_FAIL_COND(!skindata.joints.sources.has("JOINT"));
ERR_FAIL_COND(!skindata.joints.sources.has("INV_BIND_MATRIX"));
@@ -1214,7 +1218,7 @@ void Collada::_parse_skin_controller(XMLParser &parser, String p_id) {
for (int i = 0; i < joint_source.sarray.size(); i++) {
String name = joint_source.sarray[i];
- Transform xform = _read_transform_from_array(ibm_source.array, i * 16); //<- this is a mistake, it must be applied to vertices
+ Transform3D xform = _read_transform_from_array(ibm_source.array, i * 16); //<- this is a mistake, it must be applied to vertices
xform.affine_invert(); // inverse for rest, because it's an inverse
#ifdef COLLADA_IMPORT_SCALE_SCENE
xform.origin *= state.unit_scale;
@@ -2096,7 +2100,7 @@ void Collada::_merge_skeletons2(VisualScene *p_vscene) {
NodeSkeleton *skeleton = nullptr;
- for (Map<String, Transform>::Element *F = cd.bone_rest_map.front(); F; F = F->next()) {
+ for (Map<String, Transform3D>::Element *F = cd.bone_rest_map.front(); F; F = F->next()) {
String name;
if (!state.sid_to_node_map.has(F->key())) {
@@ -2240,11 +2244,11 @@ bool Collada::_move_geometry_to_skeletons(VisualScene *p_vscene, Node *p_node, L
//this should be correct
ERR_FAIL_COND_V(!state.skin_controller_data_map.has(ng->source), false);
SkinControllerData &skin = state.skin_controller_data_map[ng->source];
- Transform skel_inv = sk->get_global_transform().affine_inverse();
+ Transform3D skel_inv = sk->get_global_transform().affine_inverse();
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, Transform>::Element *E = skin.bone_rest_map.front(); E; E = E->next()) {
+ 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
}
@@ -2252,7 +2256,7 @@ bool Collada::_move_geometry_to_skeletons(VisualScene *p_vscene, Node *p_node, L
//but most exporters seem to work only if i do this..
//p_node->default_transform = p_node->get_global_transform();
- //p_node->default_transform=Transform(); //this seems to be correct, because bind shape makes the object local to the skeleton
+ //p_node->default_transform=Transform3D(); //this seems to be correct, because bind shape makes the object local to the skeleton
p_node->ignore_anim = true; // collada may animate this later, if it does, then this is not supported (redo your original asset and don't animate the base mesh)
p_node->parent = sk;
//sk->children.push_back(0,p_node); //avoid INFINITE loop
diff --git a/editor/import/collada.h b/editor/import/collada.h
index 2c3f0a3006..5e38637504 100644
--- a/editor/import/collada.h
+++ b/editor/import/collada.h
@@ -182,7 +182,7 @@ public:
String base;
bool use_idrefs = false;
- Transform bind_shape;
+ Transform3D bind_shape;
struct Source {
Vector<String> sarray; //maybe for names
@@ -210,7 +210,7 @@ public:
int count = 0;
} weights;
- Map<String, Transform> bone_rest_map;
+ Map<String, Transform3D> bone_rest_map;
SkinControllerData() {}
};
@@ -342,15 +342,15 @@ public:
String empty_draw_type;
bool noname = false;
Vector<XForm> xform_list;
- Transform default_transform;
- Transform post_transform;
+ Transform3D default_transform;
+ Transform3D post_transform;
Vector<Node *> children;
Node *parent = nullptr;
- Transform compute_transform(Collada &state) const;
- Transform get_global_transform() const;
- Transform get_transform() const;
+ Transform3D compute_transform(Collada &state) const;
+ Transform3D get_global_transform() const;
+ Transform3D get_transform() const;
bool ignore_anim = false;
@@ -497,7 +497,7 @@ public:
Map<String, String> sid_to_node_map;
//Map<String,NodeJoint*> bone_map;
- Map<String, Transform> bone_rest_map;
+ Map<String, Transform3D> bone_rest_map;
String local_path;
String root_visual_scene;
@@ -517,9 +517,9 @@ public:
Collada();
- Transform fix_transform(const Transform &p_transform);
+ Transform3D fix_transform(const Transform3D &p_transform);
- Transform get_root_transform() const;
+ Transform3D get_root_transform() const;
int get_uv_channel(String p_name);
@@ -557,7 +557,7 @@ private: // private stuff
Variant _parse_param(XMLParser &parser);
Vector<float> _read_float_array(XMLParser &parser);
Vector<String> _read_string_array(XMLParser &parser);
- Transform _read_transform(XMLParser &parser);
+ Transform3D _read_transform(XMLParser &parser);
String _read_empty_draw_type(XMLParser &parser);
void _joint_set_owner(Collada::Node *p_node, NodeSkeleton *p_owner);
diff --git a/editor/import/dynamicfont_import_settings.cpp b/editor/import/dynamicfont_import_settings.cpp
new file mode 100644
index 0000000000..37ca40287f
--- /dev/null
+++ b/editor/import/dynamicfont_import_settings.cpp
@@ -0,0 +1,1889 @@
+/*************************************************************************/
+/* dynamicfont_import_settings.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "dynamicfont_import_settings.h"
+
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+
+/*************************************************************************/
+/* Settings data */
+/*************************************************************************/
+
+class DynamicFontImportSettingsData : public RefCounted {
+ GDCLASS(DynamicFontImportSettingsData, RefCounted)
+ friend class DynamicFontImportSettings;
+
+ Map<StringName, Variant> settings;
+ Map<StringName, Variant> defaults;
+ List<ResourceImporter::ImportOption> options;
+ DynamicFontImportSettings *owner = nullptr;
+
+ bool _set(const StringName &p_name, const Variant &p_value) {
+ if (defaults.has(p_name) && defaults[p_name] == p_value) {
+ settings.erase(p_name);
+ } else {
+ settings[p_name] = p_value;
+ }
+ return true;
+ }
+
+ bool _get(const StringName &p_name, Variant &r_ret) const {
+ if (settings.has(p_name)) {
+ r_ret = settings[p_name];
+ return true;
+ }
+ if (defaults.has(p_name)) {
+ r_ret = defaults[p_name];
+ return true;
+ }
+ return false;
+ }
+
+ void _get_property_list(List<PropertyInfo> *p_list) const {
+ for (const List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) {
+ if (owner && owner->import_settings_data.is_valid()) {
+ if (owner->import_settings_data->get("multichannel_signed_distance_field") && (E->get().option.name == "size" || E->get().option.name == "outline_size" || E->get().option.name == "oversampling")) {
+ continue;
+ }
+ if (!owner->import_settings_data->get("multichannel_signed_distance_field") && (E->get().option.name == "msdf_pixel_range" || E->get().option.name == "msdf_size")) {
+ continue;
+ }
+ }
+ p_list->push_back(E->get().option);
+ }
+ }
+};
+
+/*************************************************************************/
+/* Glyph ranges */
+/*************************************************************************/
+
+struct UniRange {
+ int32_t start;
+ int32_t end;
+ String name;
+};
+
+static UniRange unicode_ranges[] = {
+ { 0x0000, 0x007F, U"Basic Latin" },
+ { 0x0080, 0x00FF, U"Latin-1 Supplement" },
+ { 0x0100, 0x017F, U"Latin Extended-A" },
+ { 0x0180, 0x024F, U"Latin Extended-B" },
+ { 0x0250, 0x02AF, U"IPA Extensions" },
+ { 0x02B0, 0x02FF, U"Spacing Modifier Letters" },
+ { 0x0300, 0x036F, U"Combining Diacritical Marks" },
+ { 0x0370, 0x03FF, U"Greek and Coptic" },
+ { 0x0400, 0x04FF, U"Cyrillic" },
+ { 0x0500, 0x052F, U"Cyrillic Supplement" },
+ { 0x0530, 0x058F, U"Armenian" },
+ { 0x0590, 0x05FF, U"Hebrew" },
+ { 0x0600, 0x06FF, U"Arabic" },
+ { 0x0700, 0x074F, U"Syriac" },
+ { 0x0750, 0x077F, U"Arabic Supplement" },
+ { 0x0780, 0x07BF, U"Thaana" },
+ { 0x07C0, 0x07FF, U"N'Ko" },
+ { 0x0800, 0x083F, U"Samaritan" },
+ { 0x0840, 0x085F, U"Mandaic" },
+ { 0x0860, 0x086F, U"Syriac Supplement" },
+ { 0x08A0, 0x08FF, U"Arabic Extended-A" },
+ { 0x0900, 0x097F, U"Devanagari" },
+ { 0x0980, 0x09FF, U"Bengali" },
+ { 0x0A00, 0x0A7F, U"Gurmukhi" },
+ { 0x0A80, 0x0AFF, U"Gujarati" },
+ { 0x0B00, 0x0B7F, U"Oriya" },
+ { 0x0B80, 0x0BFF, U"Tamil" },
+ { 0x0C00, 0x0C7F, U"Telugu" },
+ { 0x0C80, 0x0CFF, U"Kannada" },
+ { 0x0D00, 0x0D7F, U"Malayalam" },
+ { 0x0D80, 0x0DFF, U"Sinhala" },
+ { 0x0E00, 0x0E7F, U"Thai" },
+ { 0x0E80, 0x0EFF, U"Lao" },
+ { 0x0F00, 0x0FFF, U"Tibetan" },
+ { 0x1000, 0x109F, U"Myanmar" },
+ { 0x10A0, 0x10FF, U"Georgian" },
+ { 0x1100, 0x11FF, U"Hangul Jamo" },
+ { 0x1200, 0x137F, U"Ethiopic" },
+ { 0x1380, 0x139F, U"Ethiopic Supplement" },
+ { 0x13A0, 0x13FF, U"Cherokee" },
+ { 0x1400, 0x167F, U"Unified Canadian Aboriginal Syllabics" },
+ { 0x1680, 0x169F, U"Ogham" },
+ { 0x16A0, 0x16FF, U"Runic" },
+ { 0x1700, 0x171F, U"Tagalog" },
+ { 0x1720, 0x173F, U"Hanunoo" },
+ { 0x1740, 0x175F, U"Buhid" },
+ { 0x1760, 0x177F, U"Tagbanwa" },
+ { 0x1780, 0x17FF, U"Khmer" },
+ { 0x1800, 0x18AF, U"Mongolian" },
+ { 0x18B0, 0x18FF, U"Unified Canadian Aboriginal Syllabics Extended" },
+ { 0x1900, 0x194F, U"Limbu" },
+ { 0x1950, 0x197F, U"Tai Le" },
+ { 0x1980, 0x19DF, U"New Tai Lue" },
+ { 0x19E0, 0x19FF, U"Khmer Symbols" },
+ { 0x1A00, 0x1A1F, U"Buginese" },
+ { 0x1A20, 0x1AAF, U"Tai Tham" },
+ { 0x1AB0, 0x1AFF, U"Combining Diacritical Marks Extended" },
+ { 0x1B00, 0x1B7F, U"Balinese" },
+ { 0x1B80, 0x1BBF, U"Sundanese" },
+ { 0x1BC0, 0x1BFF, U"Batak" },
+ { 0x1C00, 0x1C4F, U"Lepcha" },
+ { 0x1C50, 0x1C7F, U"Ol Chiki" },
+ { 0x1C80, 0x1C8F, U"Cyrillic Extended-C" },
+ { 0x1C90, 0x1CBF, U"Georgian Extended" },
+ { 0x1CC0, 0x1CCF, U"Sundanese Supplement" },
+ { 0x1CD0, 0x1CFF, U"Vedic Extensions" },
+ { 0x1D00, 0x1D7F, U"Phonetic Extensions" },
+ { 0x1D80, 0x1DBF, U"Phonetic Extensions Supplement" },
+ { 0x1DC0, 0x1DFF, U"Combining Diacritical Marks Supplement" },
+ { 0x1E00, 0x1EFF, U"Latin Extended Additional" },
+ { 0x1F00, 0x1FFF, U"Greek Extended" },
+ { 0x2000, 0x206F, U"General Punctuation" },
+ { 0x2070, 0x209F, U"Superscripts and Subscripts" },
+ { 0x20A0, 0x20CF, U"Currency Symbols" },
+ { 0x20D0, 0x20FF, U"Combining Diacritical Marks for Symbols" },
+ { 0x2100, 0x214F, U"Letterlike Symbols" },
+ { 0x2150, 0x218F, U"Number Forms" },
+ { 0x2190, 0x21FF, U"Arrows" },
+ { 0x2200, 0x22FF, U"Mathematical Operators" },
+ { 0x2300, 0x23FF, U"Miscellaneous Technical" },
+ { 0x2400, 0x243F, U"Control Pictures" },
+ { 0x2440, 0x245F, U"Optical Character Recognition" },
+ { 0x2460, 0x24FF, U"Enclosed Alphanumerics" },
+ { 0x2500, 0x257F, U"Box Drawing" },
+ { 0x2580, 0x259F, U"Block Elements" },
+ { 0x25A0, 0x25FF, U"Geometric Shapes" },
+ { 0x2600, 0x26FF, U"Miscellaneous Symbols" },
+ { 0x2700, 0x27BF, U"Dingbats" },
+ { 0x27C0, 0x27EF, U"Miscellaneous Mathematical Symbols-A" },
+ { 0x27F0, 0x27FF, U"Supplemental Arrows-A" },
+ { 0x2800, 0x28FF, U"Braille Patterns" },
+ { 0x2900, 0x297F, U"Supplemental Arrows-B" },
+ { 0x2980, 0x29FF, U"Miscellaneous Mathematical Symbols-B" },
+ { 0x2A00, 0x2AFF, U"Supplemental Mathematical Operators" },
+ { 0x2B00, 0x2BFF, U"Miscellaneous Symbols and Arrows" },
+ { 0x2C00, 0x2C5F, U"Glagolitic" },
+ { 0x2C60, 0x2C7F, U"Latin Extended-C" },
+ { 0x2C80, 0x2CFF, U"Coptic" },
+ { 0x2D00, 0x2D2F, U"Georgian Supplement" },
+ { 0x2D30, 0x2D7F, U"Tifinagh" },
+ { 0x2D80, 0x2DDF, U"Ethiopic Extended" },
+ { 0x2DE0, 0x2DFF, U"Cyrillic Extended-A" },
+ { 0x2E00, 0x2E7F, U"Supplemental Punctuation" },
+ { 0x2E80, 0x2EFF, U"CJK Radicals Supplement" },
+ { 0x2F00, 0x2FDF, U"Kangxi Radicals" },
+ { 0x2FF0, 0x2FFF, U"Ideographic Description Characters" },
+ { 0x3000, 0x303F, U"CJK Symbols and Punctuation" },
+ { 0x3040, 0x309F, U"Hiragana" },
+ { 0x30A0, 0x30FF, U"Katakana" },
+ { 0x3100, 0x312F, U"Bopomofo" },
+ { 0x3130, 0x318F, U"Hangul Compatibility Jamo" },
+ { 0x3190, 0x319F, U"Kanbun" },
+ { 0x31A0, 0x31BF, U"Bopomofo Extended" },
+ { 0x31C0, 0x31EF, U"CJK Strokes" },
+ { 0x31F0, 0x31FF, U"Katakana Phonetic Extensions" },
+ { 0x3200, 0x32FF, U"Enclosed CJK Letters and Months" },
+ { 0x3300, 0x33FF, U"CJK Compatibility" },
+ { 0x3400, 0x4DBF, U"CJK Unified Ideographs Extension A" },
+ { 0x4DC0, 0x4DFF, U"Yijing Hexagram Symbols" },
+ { 0x4E00, 0x9FFF, U"CJK Unified Ideographs" },
+ { 0xA000, 0xA48F, U"Yi Syllables" },
+ { 0xA490, 0xA4CF, U"Yi Radicals" },
+ { 0xA4D0, 0xA4FF, U"Lisu" },
+ { 0xA500, 0xA63F, U"Vai" },
+ { 0xA640, 0xA69F, U"Cyrillic Extended-B" },
+ { 0xA6A0, 0xA6FF, U"Bamum" },
+ { 0xA700, 0xA71F, U"Modifier Tone Letters" },
+ { 0xA720, 0xA7FF, U"Latin Extended-D" },
+ { 0xA800, 0xA82F, U"Syloti Nagri" },
+ { 0xA830, 0xA83F, U"Common Indic Number Forms" },
+ { 0xA840, 0xA87F, U"Phags-pa" },
+ { 0xA880, 0xA8DF, U"Saurashtra" },
+ { 0xA8E0, 0xA8FF, U"Devanagari Extended" },
+ { 0xA900, 0xA92F, U"Kayah Li" },
+ { 0xA930, 0xA95F, U"Rejang" },
+ { 0xA960, 0xA97F, U"Hangul Jamo Extended-A" },
+ { 0xA980, 0xA9DF, U"Javanese" },
+ { 0xA9E0, 0xA9FF, U"Myanmar Extended-B" },
+ { 0xAA00, 0xAA5F, U"Cham" },
+ { 0xAA60, 0xAA7F, U"Myanmar Extended-A" },
+ { 0xAA80, 0xAADF, U"Tai Viet" },
+ { 0xAAE0, 0xAAFF, U"Meetei Mayek Extensions" },
+ { 0xAB00, 0xAB2F, U"Ethiopic Extended-A" },
+ { 0xAB30, 0xAB6F, U"Latin Extended-E" },
+ { 0xAB70, 0xABBF, U"Cherokee Supplement" },
+ { 0xABC0, 0xABFF, U"Meetei Mayek" },
+ { 0xD7B0, 0xD7FF, U"Hangul Jamo Extended-B" },
+ //{ 0xF800, 0xDFFF, U"Surrogates" },
+ { 0xE000, 0xE2FE, U"Private Use Area" },
+ { 0xF900, 0xFAFF, U"CJK Compatibility Ideographs" },
+ { 0xFB00, 0xFB4F, U"Alphabetic Presentation Forms" },
+ { 0xFB50, 0xFDFF, U"Arabic Presentation Forms-A" },
+ //{ 0xFE00, 0xFE0F, U"Variation Selectors" },
+ { 0xFE10, 0xFE1F, U"Vertical Forms" },
+ { 0xFE20, 0xFE2F, U"Combining Half Marks" },
+ { 0xFE30, 0xFE4F, U"CJK Compatibility Forms" },
+ { 0xFE50, 0xFE6F, U"Small Form Variants" },
+ { 0xFE70, 0xFEFF, U"Arabic Presentation Forms-B" },
+ { 0xFF00, 0xFFEF, U"Halfwidth and Fullwidth Forms" },
+ //{ 0xFFF0, 0xFFFF, U"Specials" },
+ { 0x10000, 0x1007F, U"Linear B Syllabary" },
+ { 0x10080, 0x100FF, U"Linear B Ideograms" },
+ { 0x10100, 0x1013F, U"Aegean Numbers" },
+ { 0x10140, 0x1018F, U"Ancient Greek Numbers" },
+ { 0x10190, 0x101CF, U"Ancient Symbols" },
+ { 0x101D0, 0x101FF, U"Phaistos Disc" },
+ { 0x10280, 0x1029F, U"Lycian" },
+ { 0x102A0, 0x102DF, U"Carian" },
+ { 0x102E0, 0x102FF, U"Coptic Epact Numbers" },
+ { 0x10300, 0x1032F, U"Old Italic" },
+ { 0x10330, 0x1034F, U"Gothic" },
+ { 0x10350, 0x1037F, U"Old Permic" },
+ { 0x10380, 0x1039F, U"Ugaritic" },
+ { 0x103A0, 0x103DF, U"Old Persian" },
+ { 0x10400, 0x1044F, U"Deseret" },
+ { 0x10450, 0x1047F, U"Shavian" },
+ { 0x10480, 0x104AF, U"Osmanya" },
+ { 0x104B0, 0x104FF, U"Osage" },
+ { 0x10500, 0x1052F, U"Elbasan" },
+ { 0x10530, 0x1056F, U"Caucasian Albanian" },
+ { 0x10600, 0x1077F, U"Linear A" },
+ { 0x10800, 0x1083F, U"Cypriot Syllabary" },
+ { 0x10840, 0x1085F, U"Imperial Aramaic" },
+ { 0x10860, 0x1087F, U"Palmyrene" },
+ { 0x10880, 0x108AF, U"Nabataean" },
+ { 0x108E0, 0x108FF, U"Hatran" },
+ { 0x10900, 0x1091F, U"Phoenician" },
+ { 0x10920, 0x1093F, U"Lydian" },
+ { 0x10980, 0x1099F, U"Meroitic Hieroglyphs" },
+ { 0x109A0, 0x109FF, U"Meroitic Cursive" },
+ { 0x10A00, 0x10A5F, U"Kharoshthi" },
+ { 0x10A60, 0x10A7F, U"Old South Arabian" },
+ { 0x10A80, 0x10A9F, U"Old North Arabian" },
+ { 0x10AC0, 0x10AFF, U"Manichaean" },
+ { 0x10B00, 0x10B3F, U"Avestan" },
+ { 0x10B40, 0x10B5F, U"Inscriptional Parthian" },
+ { 0x10B60, 0x10B7F, U"Inscriptional Pahlavi" },
+ { 0x10B80, 0x10BAF, U"Psalter Pahlavi" },
+ { 0x10C00, 0x10C4F, U"Old Turkic" },
+ { 0x10C80, 0x10CFF, U"Old Hungarian" },
+ { 0x10D00, 0x10D3F, U"Hanifi Rohingya" },
+ { 0x10E60, 0x10E7F, U"Rumi Numeral Symbols" },
+ { 0x10E80, 0x10EBF, U"Yezidi" },
+ { 0x10F00, 0x10F2F, U"Old Sogdian" },
+ { 0x10F30, 0x10F6F, U"Sogdian" },
+ { 0x10FB0, 0x10FDF, U"Chorasmian" },
+ { 0x10FE0, 0x10FFF, U"Elymaic" },
+ { 0x11000, 0x1107F, U"Brahmi" },
+ { 0x11080, 0x110CF, U"Kaithi" },
+ { 0x110D0, 0x110FF, U"Sora Sompeng" },
+ { 0x11100, 0x1114F, U"Chakma" },
+ { 0x11150, 0x1117F, U"Mahajani" },
+ { 0x11180, 0x111DF, U"Sharada" },
+ { 0x111E0, 0x111FF, U"Sinhala Archaic Numbers" },
+ { 0x11200, 0x1124F, U"Khojki" },
+ { 0x11280, 0x112AF, U"Multani" },
+ { 0x112B0, 0x112FF, U"Khudawadi" },
+ { 0x11300, 0x1137F, U"Grantha" },
+ { 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" },
+ { 0x11700, 0x1173F, 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" },
+ { 0x11AC0, 0x11AFF, U"Pau Cin Hau" },
+ { 0x11C00, 0x11C6F, U"Bhaiksuki" },
+ { 0x11C70, 0x11CBF, U"Marchen" },
+ { 0x11D00, 0x11D5F, U"Masaram Gondi" },
+ { 0x11D60, 0x11DAF, U"Gunjala Gondi" },
+ { 0x11EE0, 0x11EFF, U"Makasar" },
+ { 0x11FB0, 0x11FBF, U"Lisu Supplement" },
+ { 0x11FC0, 0x11FFF, U"Tamil Supplement" },
+ { 0x12000, 0x123FF, U"Cuneiform" },
+ { 0x12400, 0x1247F, U"Cuneiform Numbers and Punctuation" },
+ { 0x12480, 0x1254F, U"Early Dynastic Cuneiform" },
+ { 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" },
+ { 0x16AD0, 0x16AFF, U"Bassa Vah" },
+ { 0x16B00, 0x16B8F, U"Pahawh Hmong" },
+ { 0x16E40, 0x16E9F, U"Medefaidrin" },
+ { 0x16F00, 0x16F9F, U"Miao" },
+ { 0x16FE0, 0x16FFF, U"Ideographic Symbols and Punctuation" },
+ { 0x17000, 0x187FF, U"Tangut" },
+ { 0x18800, 0x18AFF, U"Tangut Components" },
+ { 0x18B00, 0x18CFF, U"Khitan Small Script" },
+ { 0x18D00, 0x18D8F, U"Tangut Supplement" },
+ { 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" },
+ { 0x1D000, 0x1D0FF, U"Byzantine Musical Symbols" },
+ { 0x1D100, 0x1D1FF, U"Musical Symbols" },
+ { 0x1D200, 0x1D24F, U"Ancient Greek Musical Notation" },
+ { 0x1D2E0, 0x1D2FF, U"Mayan Numerals" },
+ { 0x1D300, 0x1D35F, U"Tai Xuan Jing Symbols" },
+ { 0x1D360, 0x1D37F, U"Counting Rod Numerals" },
+ { 0x1D400, 0x1D7FF, U"Mathematical Alphanumeric Symbols" },
+ { 0x1D800, 0x1DAAF, U"Sutton SignWriting" },
+ { 0x1E000, 0x1E02F, U"Glagolitic Supplement" },
+ { 0x1E100, 0x1E14F, U"Nyiakeng Puachue Hmong" },
+ { 0x1E2C0, 0x1E2FF, U"Wancho" },
+ { 0x1E800, 0x1E8DF, U"Mende Kikakui" },
+ { 0x1E900, 0x1E95F, U"Adlam" },
+ { 0x1EC70, 0x1ECBF, U"Indic Siyaq Numbers" },
+ { 0x1ED00, 0x1ED4F, U"Ottoman Siyaq Numbers" },
+ { 0x1EE00, 0x1EEFF, U"Arabic Mathematical Alphabetic Symbols" },
+ { 0x1F000, 0x1F02F, U"Mahjong Tiles" },
+ { 0x1F030, 0x1F09F, U"Domino Tiles" },
+ { 0x1F0A0, 0x1F0FF, U"Playing Cards" },
+ { 0x1F100, 0x1F1FF, U"Enclosed Alphanumeric Supplement" },
+ { 0x1F200, 0x1F2FF, U"Enclosed Ideographic Supplement" },
+ { 0x1F300, 0x1F5FF, U"Miscellaneous Symbols and Pictographs" },
+ { 0x1F600, 0x1F64F, U"Emoticons" },
+ { 0x1F650, 0x1F67F, U"Ornamental Dingbats" },
+ { 0x1F680, 0x1F6FF, U"Transport and Map Symbols" },
+ { 0x1F700, 0x1F77F, U"Alchemical Symbols" },
+ { 0x1F780, 0x1F7FF, U"Geometric Shapes Extended" },
+ { 0x1F800, 0x1F8FF, U"Supplemental Arrows-C" },
+ { 0x1F900, 0x1F9FF, U"Supplemental Symbols and Pictographs" },
+ { 0x1FA00, 0x1FA6F, U"Chess Symbols" },
+ { 0x1FA70, 0x1FAFF, U"Symbols and Pictographs Extended-A" },
+ { 0x1FB00, 0x1FBFF, U"Symbols for Legacy Computing" },
+ { 0x20000, 0x2A6DF, U"CJK Unified Ideographs Extension B" },
+ { 0x2A700, 0x2B73F, U"CJK Unified Ideographs Extension C" },
+ { 0x2B740, 0x2B81F, U"CJK Unified Ideographs Extension D" },
+ { 0x2B820, 0x2CEAF, U"CJK Unified Ideographs Extension E" },
+ { 0x2CEB0, 0x2EBEF, U"CJK Unified Ideographs Extension F" },
+ { 0x2F800, 0x2FA1F, U"CJK Compatibility Ideographs Supplement" },
+ { 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" },
+ { 0x10FFFF, 0x10FFFF, String() }
+};
+
+void DynamicFontImportSettings::_add_glyph_range_item(int32_t p_start, int32_t p_end, const String &p_name) {
+ const int page_size = 512;
+ int pages = (p_end - p_start) / page_size;
+ int remain = (p_end - p_start) % page_size;
+
+ int32_t start = p_start;
+ for (int i = 0; i < pages; i++) {
+ TreeItem *item = glyph_tree->create_item(glyph_root);
+ ERR_FAIL_NULL(item);
+ item->set_text(0, _pad_zeros(String::num_int64(start, 16)) + " - " + _pad_zeros(String::num_int64(start + page_size, 16)));
+ item->set_text(1, p_name);
+ item->set_metadata(0, Vector2i(start, start + page_size));
+ start += page_size;
+ }
+ if (remain > 0) {
+ TreeItem *item = glyph_tree->create_item(glyph_root);
+ ERR_FAIL_NULL(item);
+ item->set_text(0, _pad_zeros(String::num_int64(start, 16)) + " - " + _pad_zeros(String::num_int64(p_end, 16)));
+ item->set_text(1, p_name);
+ item->set_metadata(0, Vector2i(start, p_end));
+ }
+}
+
+/*************************************************************************/
+/* Languages and scripts */
+/*************************************************************************/
+
+struct CodeInfo {
+ String name;
+ String code;
+};
+
+static CodeInfo langs[] = {
+ { U"Custom", U"xx" },
+ { U"-", U"-" },
+ { U"Abkhazian", U"ab" },
+ { U"Afar", U"aa" },
+ { U"Afrikaans", U"af" },
+ { U"Akan", U"ak" },
+ { U"Albanian", U"sq" },
+ { U"Amharic", U"am" },
+ { U"Arabic", U"ar" },
+ { U"Aragonese", U"an" },
+ { U"Armenian", U"hy" },
+ { U"Assamese", U"as" },
+ { U"Avaric", U"av" },
+ { U"Avestan", U"ae" },
+ { U"Aymara", U"ay" },
+ { U"Azerbaijani", U"az" },
+ { U"Bambara", U"bm" },
+ { U"Bashkir", U"ba" },
+ { U"Basque", U"eu" },
+ { U"Belarusian", U"be" },
+ { U"Bengali", U"bn" },
+ { U"Bihari", U"bh" },
+ { U"Bislama", U"bi" },
+ { U"Bosnian", U"bs" },
+ { U"Breton", U"br" },
+ { U"Bulgarian", U"bg" },
+ { U"Burmese", U"my" },
+ { U"Catalan", U"ca" },
+ { U"Chamorro", U"ch" },
+ { U"Chechen", U"ce" },
+ { U"Chichewa", U"ny" },
+ { U"Chinese", U"zh" },
+ { U"Chuvash", U"cv" },
+ { U"Cornish", U"kw" },
+ { U"Corsican", U"co" },
+ { U"Cree", U"cr" },
+ { U"Croatian", U"hr" },
+ { U"Czech", U"cs" },
+ { U"Danish", U"da" },
+ { U"Divehi", U"dv" },
+ { U"Dutch", U"nl" },
+ { U"Dzongkha", U"dz" },
+ { U"English", U"en" },
+ { U"Esperanto", U"eo" },
+ { U"Estonian", U"et" },
+ { U"Ewe", U"ee" },
+ { U"Faroese", U"fo" },
+ { U"Fijian", U"fj" },
+ { U"Finnish", U"fi" },
+ { U"French", U"fr" },
+ { U"Fulah", U"ff" },
+ { U"Galician", U"gl" },
+ { U"Georgian", U"ka" },
+ { U"German", U"de" },
+ { U"Greek", U"el" },
+ { U"Guarani", U"gn" },
+ { U"Gujarati", U"gu" },
+ { U"Haitian", U"ht" },
+ { U"Hausa", U"ha" },
+ { U"Hebrew", U"he" },
+ { U"Herero", U"hz" },
+ { U"Hindi", U"hi" },
+ { U"Hiri Motu", U"ho" },
+ { U"Hungarian", U"hu" },
+ { U"Interlingua", U"ia" },
+ { U"Indonesian", U"id" },
+ { U"Interlingue", U"ie" },
+ { U"Irish", U"ga" },
+ { U"Igbo", U"ig" },
+ { U"Inupiaq", U"ik" },
+ { U"Ido", U"io" },
+ { U"Icelandic", U"is" },
+ { U"Italian", U"it" },
+ { U"Inuktitut", U"iu" },
+ { U"Japanese", U"ja" },
+ { U"Javanese", U"jv" },
+ { U"Kalaallisut", U"kl" },
+ { U"Kannada", U"kn" },
+ { U"Kanuri", U"kr" },
+ { U"Kashmiri", U"ks" },
+ { U"Kazakh", U"kk" },
+ { U"Central Khmer", U"km" },
+ { U"Kikuyu", U"ki" },
+ { U"Kinyarwanda", U"rw" },
+ { U"Kirghiz", U"ky" },
+ { U"Komi", U"kv" },
+ { U"Kongo", U"kg" },
+ { U"Korean", U"ko" },
+ { U"Kurdish", U"ku" },
+ { U"Kuanyama", U"kj" },
+ { U"Latin", U"la" },
+ { U"Luxembourgish", U"lb" },
+ { U"Ganda", U"lg" },
+ { U"Limburgan", U"li" },
+ { U"Lingala", U"ln" },
+ { U"Lao", U"lo" },
+ { U"Lithuanian", U"lt" },
+ { U"Luba-Katanga", U"lu" },
+ { U"Latvian", U"lv" },
+ { U"Man", U"gv" },
+ { U"Macedonian", U"mk" },
+ { U"Malagasy", U"mg" },
+ { U"Malay", U"ms" },
+ { U"Malayalam", U"ml" },
+ { U"Maltese", U"mt" },
+ { U"Maori", U"mi" },
+ { U"Marathi", U"mr" },
+ { U"Marshallese", U"mh" },
+ { U"Mongolian", U"mn" },
+ { U"Nauru", U"na" },
+ { U"Navajo", U"nv" },
+ { U"North Ndebele", U"nd" },
+ { U"Nepali", U"ne" },
+ { U"Ndonga", U"ng" },
+ { U"Norwegian Bokmål", U"nb" },
+ { U"Norwegian Nynorsk", U"nn" },
+ { U"Norwegian", U"no" },
+ { U"Sichuan Yi, Nuosu", U"ii" },
+ { U"South Ndebele", U"nr" },
+ { U"Occitan", U"oc" },
+ { U"Ojibwa", U"oj" },
+ { U"Church Slavic", U"cu" },
+ { U"Oromo", U"om" },
+ { U"Oriya", U"or" },
+ { U"Ossetian", U"os" },
+ { U"Punjabi", U"pa" },
+ { U"Pali", U"pi" },
+ { U"Persian", U"fa" },
+ { U"Polish", U"pl" },
+ { U"Pashto", U"ps" },
+ { U"Portuguese", U"pt" },
+ { U"Quechua", U"qu" },
+ { U"Romansh", U"rm" },
+ { U"Rundi", U"rn" },
+ { U"Romanian", U"ro" },
+ { U"Russian", U"ru" },
+ { U"Sanskrit", U"sa" },
+ { U"Sardinian", U"sc" },
+ { U"Sindhi", U"sd" },
+ { U"Northern Sami", U"se" },
+ { U"Samoan", U"sm" },
+ { U"Sango", U"sg" },
+ { U"Serbian", U"sr" },
+ { U"Gaelic", U"gd" },
+ { U"Shona", U"sn" },
+ { U"Sinhala", U"si" },
+ { U"Slovak", U"sk" },
+ { U"Slovenian", U"sl" },
+ { U"Somali", U"so" },
+ { U"Southern Sotho", U"st" },
+ { U"Spanish", U"es" },
+ { U"Sundanese", U"su" },
+ { U"Swahili", U"sw" },
+ { U"Swati", U"ss" },
+ { U"Swedish", U"sv" },
+ { U"Tamil", U"ta" },
+ { U"Telugu", U"te" },
+ { U"Tajik", U"tg" },
+ { U"Thai", U"th" },
+ { U"Tigrinya", U"ti" },
+ { U"Tibetan", U"bo" },
+ { U"Turkmen", U"tk" },
+ { U"Tagalog", U"tl" },
+ { U"Tswana", U"tn" },
+ { U"Tonga", U"to" },
+ { U"Turkish", U"tr" },
+ { U"Tsonga", U"ts" },
+ { U"Tatar", U"tt" },
+ { U"Twi", U"tw" },
+ { U"Tahitian", U"ty" },
+ { U"Uighur", U"ug" },
+ { U"Ukrainian", U"uk" },
+ { U"Urdu", U"ur" },
+ { U"Uzbek", U"uz" },
+ { U"Venda", U"ve" },
+ { U"Vietnamese", U"vi" },
+ { U"Volapük", U"vo" },
+ { U"Walloon", U"wa" },
+ { U"Welsh", U"cy" },
+ { U"Wolof", U"wo" },
+ { U"Western Frisian", U"fy" },
+ { U"Xhosa", U"xh" },
+ { U"Yiddish", U"yi" },
+ { U"Yoruba", U"yo" },
+ { U"Zhuang", U"za" },
+ { U"Zulu", U"zu" },
+ { String(), String() }
+};
+
+static CodeInfo scripts[] = {
+ { U"Custom", U"Qaaa" },
+ { U"-", U"-" },
+ { U"Adlam", U"Adlm" },
+ { U"Afaka", U"Afak" },
+ { U"Caucasian Albanian", U"Aghb" },
+ { U"Ahom", U"Ahom" },
+ { U"Arabic", U"Arab" },
+ { U"Imperial Aramaic", U"Armi" },
+ { U"Armenian", U"Armn" },
+ { U"Avestan", U"Avst" },
+ { U"Balinese", U"Bali" },
+ { U"Bamum", U"Bamu" },
+ { U"Bassa Vah", U"Bass" },
+ { U"Batak", U"Batk" },
+ { U"Bengali", U"Beng" },
+ { U"Bhaiksuki", U"Bhks" },
+ { U"Blissymbols", U"Blis" },
+ { U"Bopomofo", U"Bopo" },
+ { U"Brahmi", U"Brah" },
+ { U"Braille", U"Brai" },
+ { U"Buginese", U"Bugi" },
+ { U"Buhid", U"Buhd" },
+ { U"Chakma", U"Cakm" },
+ { U"Unified Canadian Aboriginal", U"Cans" },
+ { U"Carian", U"Cari" },
+ { U"Cham", U"Cham" },
+ { U"Cherokee", U"Cher" },
+ { U"Chorasmian", U"Chrs" },
+ { U"Cirth", U"Cirt" },
+ { U"Coptic", U"Copt" },
+ { U"Cypro-Minoan", U"Cpmn" },
+ { U"Cypriot", U"Cprt" },
+ { U"Cyrillic", U"Cyrl" },
+ { U"Devanagari", U"Deva" },
+ { U"Dives Akuru", U"Diak" },
+ { U"Dogra", U"Dogr" },
+ { U"Deseret", U"Dsrt" },
+ { U"Duployan", U"Dupl" },
+ { U"Egyptian demotic", U"Egyd" },
+ { U"Egyptian hieratic", U"Egyh" },
+ { U"Egyptian hieroglyphs", U"Egyp" },
+ { U"Elbasan", U"Elba" },
+ { U"Elymaic", U"Elym" },
+ { U"Ethiopic", U"Ethi" },
+ { U"Khutsuri", U"Geok" },
+ { U"Georgian", U"Geor" },
+ { U"Glagolitic", U"Glag" },
+ { U"Gunjala Gondi", U"Gong" },
+ { U"Masaram Gondi", U"Gonm" },
+ { U"Gothic", U"Goth" },
+ { U"Grantha", U"Gran" },
+ { U"Greek", U"Grek" },
+ { U"Gujarati", U"Gujr" },
+ { U"Gurmukhi", U"Guru" },
+ { U"Hangul", U"Hang" },
+ { U"Han", U"Hani" },
+ { U"Hanunoo", U"Hano" },
+ { U"Hatran", U"Hatr" },
+ { U"Hebrew", U"Hebr" },
+ { U"Hiragana", U"Hira" },
+ { U"Anatolian Hieroglyphs", U"Hluw" },
+ { U"Pahawh Hmong", U"Hmng" },
+ { U"Nyiakeng Puachue Hmong", U"Hmnp" },
+ { U"Old Hungarian", U"Hung" },
+ { U"Indus", U"Inds" },
+ { U"Old Italic", U"Ital" },
+ { U"Javanese", U"Java" },
+ { U"Jurchen", U"Jurc" },
+ { U"Kayah Li", U"Kali" },
+ { U"Katakana", U"Kana" },
+ { U"Kharoshthi", U"Khar" },
+ { U"Khmer", U"Khmr" },
+ { U"Khojki", U"Khoj" },
+ { U"Khitan large script", U"Kitl" },
+ { U"Khitan small script", U"Kits" },
+ { U"Kannada", U"Knda" },
+ { U"Kpelle", U"Kpel" },
+ { U"Kaithi", U"Kthi" },
+ { U"Tai Tham", U"Lana" },
+ { U"Lao", U"Laoo" },
+ { U"Latin", U"Latn" },
+ { U"Leke", U"Leke" },
+ { U"Lepcha", U"Lepc" },
+ { U"Limbu", U"Limb" },
+ { U"Linear A", U"Lina" },
+ { U"Linear B", U"Linb" },
+ { U"Lisu", U"Lisu" },
+ { U"Loma", U"Loma" },
+ { U"Lycian", U"Lyci" },
+ { U"Lydian", U"Lydi" },
+ { U"Mahajani", U"Mahj" },
+ { U"Makasar", U"Maka" },
+ { U"Mandaic", U"Mand" },
+ { U"Manichaean", U"Mani" },
+ { U"Marchen", U"Marc" },
+ { U"Mayan Hieroglyphs", U"Maya" },
+ { U"Medefaidrin", U"Medf" },
+ { U"Mende Kikakui", U"Mend" },
+ { U"Meroitic Cursive", U"Merc" },
+ { U"Meroitic Hieroglyphs", U"Mero" },
+ { U"Malayalam", U"Mlym" },
+ { U"Modi", U"Modi" },
+ { U"Mongolian", U"Mong" },
+ { U"Moon", U"Moon" },
+ { U"Mro", U"Mroo" },
+ { U"Meitei Mayek", U"Mtei" },
+ { U"Multani", U"Mult" },
+ { U"Myanmar (Burmese)", U"Mymr" },
+ { U"Nandinagari", U"Nand" },
+ { U"Old North Arabian", U"Narb" },
+ { U"Nabataean", U"Nbat" },
+ { U"Newa", U"Newa" },
+ { U"Naxi Dongba", U"Nkdb" },
+ { U"Nakhi Geba", U"Nkgb" },
+ { U"N’Ko", U"Nkoo" },
+ { U"Nüshu", U"Nshu" },
+ { U"Ogham", U"Ogam" },
+ { U"Ol Chiki", U"Olck" },
+ { U"Old Turkic", U"Orkh" },
+ { U"Oriya", U"Orya" },
+ { U"Osage", U"Osge" },
+ { U"Osmanya", U"Osma" },
+ { U"Old Uyghur", U"Ougr" },
+ { U"Palmyrene", U"Palm" },
+ { U"Pau Cin Hau", U"Pauc" },
+ { U"Proto-Cuneiform", U"Pcun" },
+ { U"Proto-Elamite", U"Pelm" },
+ { U"Old Permic", U"Perm" },
+ { U"Phags-pa", U"Phag" },
+ { U"Inscriptional Pahlavi", U"Phli" },
+ { U"Psalter Pahlavi", U"Phlp" },
+ { U"Book Pahlavi", U"Phlv" },
+ { U"Phoenician", U"Phnx" },
+ { U"Klingon", U"Piqd" },
+ { U"Miao", U"Plrd" },
+ { U"Inscriptional Parthian", U"Prti" },
+ { U"Proto-Sinaitic", U"Psin" },
+ { U"Ranjana", U"Ranj" },
+ { U"Rejang", U"Rjng" },
+ { U"Hanifi Rohingya", U"Rohg" },
+ { U"Rongorongo", U"Roro" },
+ { U"Runic", U"Runr" },
+ { U"Samaritan", U"Samr" },
+ { U"Sarati", U"Sara" },
+ { U"Old South Arabian", U"Sarb" },
+ { U"Saurashtra", U"Saur" },
+ { U"SignWriting", U"Sgnw" },
+ { U"Shavian", U"Shaw" },
+ { U"Sharada", U"Shrd" },
+ { U"Shuishu", U"Shui" },
+ { U"Siddham", U"Sidd" },
+ { U"Khudawadi", U"Sind" },
+ { U"Sinhala", U"Sinh" },
+ { U"Sogdian", U"Sogd" },
+ { U"Old Sogdian", U"Sogo" },
+ { U"Sora Sompeng", U"Sora" },
+ { U"Soyombo", U"Soyo" },
+ { U"Sundanese", U"Sund" },
+ { U"Syloti Nagri", U"Sylo" },
+ { U"Syriac", U"Syrc" },
+ { U"Tagbanwa", U"Tagb" },
+ { U"Takri", U"Takr" },
+ { U"Tai Le", U"Tale" },
+ { U"New Tai Lue", U"Talu" },
+ { U"Tamil", U"Taml" },
+ { U"Tangut", U"Tang" },
+ { U"Tai Viet", U"Tavt" },
+ { U"Telugu", U"Telu" },
+ { U"Tengwar", U"Teng" },
+ { U"Tifinagh", U"Tfng" },
+ { U"Tagalog", U"Tglg" },
+ { U"Thaana", U"Thaa" },
+ { U"Thai", U"Thai" },
+ { U"Tibetan", U"Tibt" },
+ { U"Tirhuta", U"Tirh" },
+ { U"Tangsa", U"Tnsa" },
+ { U"Toto", U"Toto" },
+ { U"Ugaritic", U"Ugar" },
+ { U"Vai", U"Vaii" },
+ { U"Visible Speech", U"Visp" },
+ { U"Vithkuqi", U"Vith" },
+ { U"Warang Citi", U"Wara" },
+ { U"Wancho", U"Wcho" },
+ { U"Woleai", U"Wole" },
+ { U"Old Persian", U"Xpeo" },
+ { U"Cuneiform", U"Xsux" },
+ { U"Yezidi", U"Yezi" },
+ { U"Yi", U"Yiii" },
+ { U"Zanabazar Square", U"Zanb" },
+ { String(), String() }
+};
+
+/*************************************************************************/
+/* Page 1 callbacks: Rendering Options */
+/*************************************************************************/
+
+void DynamicFontImportSettings::_main_prop_changed(const String &p_edited_property) {
+ // Update font preview.
+
+ if (p_edited_property == "antialiased") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_antialiased(import_settings_data->get("antialiased"));
+ }
+ } else if (p_edited_property == "multichannel_signed_distance_field") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_multichannel_signed_distance_field(import_settings_data->get("multichannel_signed_distance_field"));
+ }
+ _variation_selected();
+ _variations_validate();
+ } else if (p_edited_property == "msdf_pixel_range") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_msdf_pixel_range(import_settings_data->get("msdf_pixel_range"));
+ }
+ } else if (p_edited_property == "msdf_size") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_msdf_size(import_settings_data->get("msdf_size"));
+ }
+ } else if (p_edited_property == "force_autohinter") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_force_autohinter(import_settings_data->get("force_autohinter"));
+ }
+ } else if (p_edited_property == "hinting") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_hinting((TextServer::Hinting)import_settings_data->get("hinting").operator int());
+ }
+ } else if (p_edited_property == "oversampling") {
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_oversampling(import_settings_data->get("oversampling"));
+ }
+ }
+ font_preview_label->add_theme_font_override("font", font_preview);
+ font_preview_label->update();
+}
+
+/*************************************************************************/
+/* Page 2 callbacks: Configurations */
+/*************************************************************************/
+
+void DynamicFontImportSettings::_variation_add() {
+ TreeItem *vars_item = vars_list->create_item(vars_list_root);
+ ERR_FAIL_NULL(vars_item);
+
+ vars_item->set_text(0, TTR("New configuration"));
+ vars_item->set_editable(0, true);
+ vars_item->add_button(1, vars_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove Variation"));
+ vars_item->set_button_color(1, 0, Color(1, 1, 1, 0.75));
+
+ Ref<DynamicFontImportSettingsData> import_variation_data;
+ import_variation_data.instantiate();
+ import_variation_data->owner = this;
+ ERR_FAIL_NULL(import_variation_data);
+
+ 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;
+ }
+
+ import_variation_data->options = options_variations;
+ inspector_vars->edit(import_variation_data.ptr());
+ import_variation_data->notify_property_list_changed();
+
+ vars_item->set_metadata(0, import_variation_data);
+
+ _variations_validate();
+}
+
+void DynamicFontImportSettings::_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);
+
+ inspector_vars->edit(import_variation_data.ptr());
+ import_variation_data->notify_property_list_changed();
+ }
+}
+
+void DynamicFontImportSettings::_variation_remove(Object *p_item, int p_column, int p_id) {
+ TreeItem *vars_item = (TreeItem *)p_item;
+ ERR_FAIL_NULL(vars_item);
+
+ inspector_vars->edit(nullptr);
+
+ vars_list_root->remove_child(vars_item);
+ memdelete(vars_item);
+
+ if (vars_list_root->get_first_child()) {
+ Ref<DynamicFontImportSettingsData> import_variation_data = vars_list_root->get_first_child()->get_metadata(0);
+ inspector_vars->edit(import_variation_data.ptr());
+ import_variation_data->notify_property_list_changed();
+ }
+
+ _variations_validate();
+}
+
+void DynamicFontImportSettings::_variation_changed(const String &p_edited_property) {
+ _variations_validate();
+}
+
+void DynamicFontImportSettings::_variations_validate() {
+ String warn;
+ if (!vars_list_root->get_first_child()) {
+ warn = TTR("Warinig: There are no configurations specified, no glyphs will be pre-rendered.");
+ }
+ 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);
+
+ 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 (Map<StringName, Variant>::Element *E = import_variation_data_a->settings.front(); E; E = E->next()) {
+ Ref<DynamicFontImportSettingsData> import_variation_data_b = vars_item_b->get_metadata(0);
+ ERR_FAIL_NULL(import_variation_data_b);
+ match = match && (import_variation_data_b->settings[E->key()] == E->get());
+ }
+ if (match) {
+ warn = TTR("Warinig: Multiple configurations have identical settings. Duplicates will be ignored.");
+ break;
+ }
+ }
+ }
+ }
+ if (warn.is_empty()) {
+ label_warn->set_text("");
+ label_warn->hide();
+ } else {
+ label_warn->set_text(warn);
+ label_warn->show();
+ }
+}
+
+/*************************************************************************/
+/* Page 3 callbacks: Text to select glyphs */
+/*************************************************************************/
+
+void DynamicFontImportSettings::_change_text_opts() {
+ Vector<String> ftr = ftr_edit->get_text().split(",");
+ for (int i = 0; i < ftr.size(); i++) {
+ Vector<String> tokens = ftr[i].split("=");
+ if (tokens.size() == 2) {
+ text_edit->set_opentype_feature(tokens[0], tokens[1].to_int());
+ } else if (tokens.size() == 1) {
+ text_edit->set_opentype_feature(tokens[0], 1);
+ }
+ }
+ text_edit->set_language(lang_edit->get_text());
+}
+
+void DynamicFontImportSettings::_glyph_clear() {
+ selected_glyphs.clear();
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+ _range_selected();
+}
+
+void DynamicFontImportSettings::_glyph_text_selected() {
+ Dictionary ftrs;
+ Vector<String> ftr = ftr_edit->get_text().split(",");
+ for (int i = 0; i < ftr.size(); i++) {
+ Vector<String> tokens = ftr[i].split("=");
+ if (tokens.size() == 2) {
+ ftrs[tokens[0]] = tokens[1].to_int();
+ } else if (tokens.size() == 1) {
+ ftrs[tokens[0]] = 1;
+ }
+ }
+
+ RID text_rid = TS->create_shaped_text();
+ if (text_rid.is_valid()) {
+ TS->shaped_text_add_string(text_rid, text_edit->get_text(), font_main->get_rids(), 16, ftrs, text_edit->get_language());
+ TS->shaped_text_shape(text_rid);
+ const Vector<TextServer::Glyph> &gl = TS->shaped_text_get_glyphs(text_rid);
+
+ for (int i = 0; i < gl.size(); i++) {
+ if (gl[i].font_rid.is_valid() && gl[i].index != 0) {
+ selected_glyphs.insert(gl[i].index);
+ }
+ }
+ TS->free(text_rid);
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+ }
+ _range_selected();
+}
+
+/*************************************************************************/
+/* Page 4 callbacks: Character map */
+/*************************************************************************/
+
+void DynamicFontImportSettings::_glyph_selected() {
+ TreeItem *item = glyph_table->get_selected();
+ ERR_FAIL_NULL(item);
+
+ Color scol = glyph_table->get_theme_color("box_selection_fill_color", "Editor");
+ Color fcol = glyph_table->get_theme_color("font_selected_color", "Editor");
+ scol.a = 1.f;
+
+ int32_t c = item->get_metadata(glyph_table->get_selected_column());
+ if (font_main->has_char(c)) {
+ if (_char_update(c)) {
+ item->set_custom_color(glyph_table->get_selected_column(), fcol);
+ item->set_custom_bg_color(glyph_table->get_selected_column(), scol);
+ } else {
+ item->clear_custom_color(glyph_table->get_selected_column());
+ item->clear_custom_bg_color(glyph_table->get_selected_column());
+ }
+ }
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+}
+
+void DynamicFontImportSettings::_range_edited() {
+ TreeItem *item = glyph_tree->get_selected();
+ ERR_FAIL_NULL(item);
+ Vector2i range = item->get_metadata(0);
+ _range_update(range.x, range.y);
+}
+
+void DynamicFontImportSettings::_range_selected() {
+ TreeItem *item = glyph_tree->get_selected();
+ if (item) {
+ Vector2i range = item->get_metadata(0);
+ _edit_range(range.x, range.y);
+ }
+}
+
+void DynamicFontImportSettings::_edit_range(int32_t p_start, int32_t p_end) {
+ glyph_table->clear();
+
+ TreeItem *root = glyph_table->create_item();
+ ERR_FAIL_NULL(root);
+
+ Color scol = glyph_table->get_theme_color("box_selection_fill_color", "Editor");
+ Color fcol = glyph_table->get_theme_color("font_selected_color", "Editor");
+ scol.a = 1.f;
+
+ TreeItem *item = nullptr;
+ int col = 0;
+
+ for (int32_t c = p_start; c <= p_end; c++) {
+ if (col == 0) {
+ item = glyph_table->create_item(root);
+ ERR_FAIL_NULL(item);
+ item->set_text(0, _pad_zeros(String::num_int64(c, 16)));
+ item->set_text_align(0, TreeItem::ALIGN_LEFT);
+ item->set_selectable(0, false);
+ item->set_custom_bg_color(0, glyph_table->get_theme_color("dark_color_3", "Editor"));
+ }
+ if (font_main->has_char(c)) {
+ item->set_text(col + 1, String::chr(c));
+ item->set_custom_color(col + 1, Color(1, 1, 1));
+ if (selected_chars.has(c) || (font_main->get_data(0).is_valid() && selected_glyphs.has(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, c)))) {
+ item->set_custom_color(col + 1, fcol);
+ item->set_custom_bg_color(col + 1, scol);
+ } else {
+ item->clear_custom_color(col + 1);
+ item->clear_custom_bg_color(col + 1);
+ }
+ } else {
+ item->set_custom_bg_color(col + 1, glyph_table->get_theme_color("dark_color_2", "Editor"));
+ }
+ item->set_metadata(col + 1, c);
+ item->set_text_align(col + 1, TreeItem::ALIGN_CENTER);
+ item->set_selectable(col + 1, true);
+ item->set_custom_font(col + 1, font_main);
+ item->set_custom_font_size(col + 1, get_theme_font_size("font_size") * 2);
+
+ col++;
+ if (col == 16) {
+ col = 0;
+ }
+ }
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+}
+
+bool DynamicFontImportSettings::_char_update(int32_t p_char) {
+ if (selected_chars.has(p_char)) {
+ selected_chars.erase(p_char);
+ return false;
+ } else if (font_main->get_data(0).is_valid() && selected_glyphs.has(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, p_char))) {
+ selected_glyphs.erase(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, p_char));
+ return false;
+ } else {
+ selected_chars.insert(p_char);
+ return true;
+ }
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+}
+
+void DynamicFontImportSettings::_range_update(int32_t p_start, int32_t p_end) {
+ bool all_selected = true;
+ for (int32_t i = p_start; i <= p_end; i++) {
+ if (font_main->has_char(i)) {
+ if (font_main->get_data(0).is_valid()) {
+ all_selected = all_selected && (selected_chars.has(i) || (font_main->get_data(0).is_valid() && selected_glyphs.has(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, i))));
+ } else {
+ all_selected = all_selected && selected_chars.has(i);
+ }
+ }
+ }
+ for (int32_t i = p_start; i <= p_end; i++) {
+ if (font_main->has_char(i)) {
+ if (!all_selected) {
+ selected_chars.insert(i);
+ } else {
+ selected_chars.erase(i);
+ if (font_main->get_data(0).is_valid()) {
+ selected_glyphs.erase(font_main->get_data(0)->get_glyph_index(get_theme_font_size("font_size") * 2, i));
+ }
+ }
+ }
+ }
+ _edit_range(p_start, p_end);
+}
+
+/*************************************************************************/
+/* Page 5 callbacks: CMetadata override */
+/*************************************************************************/
+
+void DynamicFontImportSettings::_lang_add() {
+ menu_langs->set_position(lang_list->get_screen_transform().xform(lang_list->get_local_mouse_position()));
+ menu_langs->set_size(Vector2(1, 1));
+ menu_langs->popup();
+}
+
+void DynamicFontImportSettings::_lang_add_item(int p_option) {
+ TreeItem *lang_item = lang_list->create_item(lang_list_root);
+ ERR_FAIL_NULL(lang_item);
+
+ lang_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ lang_item->set_editable(0, true);
+ lang_item->set_checked(0, false);
+ lang_item->set_text(1, langs[p_option].code);
+ lang_item->set_editable(1, true);
+ lang_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ lang_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
+}
+
+void DynamicFontImportSettings::_lang_remove(Object *p_item, int p_column, int p_id) {
+ TreeItem *lang_item = (TreeItem *)p_item;
+ ERR_FAIL_NULL(lang_item);
+
+ lang_list_root->remove_child(lang_item);
+ memdelete(lang_item);
+}
+
+void DynamicFontImportSettings::_script_add() {
+ menu_scripts->set_position(script_list->get_screen_transform().xform(script_list->get_local_mouse_position()));
+ menu_scripts->set_size(Vector2(1, 1));
+ menu_scripts->popup();
+}
+
+void DynamicFontImportSettings::_script_add_item(int p_option) {
+ TreeItem *script_item = script_list->create_item(script_list_root);
+ ERR_FAIL_NULL(script_item);
+
+ script_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ script_item->set_editable(0, true);
+ script_item->set_checked(0, false);
+ script_item->set_text(1, scripts[p_option].code);
+ script_item->set_editable(1, true);
+ script_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ script_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
+}
+
+void DynamicFontImportSettings::_script_remove(Object *p_item, int p_column, int p_id) {
+ TreeItem *script_item = (TreeItem *)p_item;
+ ERR_FAIL_NULL(script_item);
+
+ script_list_root->remove_child(script_item);
+ memdelete(script_item);
+}
+
+/*************************************************************************/
+/* Common */
+/*************************************************************************/
+
+DynamicFontImportSettings *DynamicFontImportSettings::singleton = nullptr;
+
+String DynamicFontImportSettings::_pad_zeros(const String &p_hex) const {
+ int len = CLAMP(5 - p_hex.length(), 0, 5);
+ return String("0").repeat(len) + p_hex;
+}
+
+void DynamicFontImportSettings::_notification(int p_what) {
+ if (p_what == NOTIFICATION_READY) {
+ connect("confirmed", callable_mp(this, &DynamicFontImportSettings::_re_import));
+ } else if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ add_lang->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_script->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_var->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ }
+}
+
+void DynamicFontImportSettings::_re_import() {
+ Map<StringName, Variant> main_settings;
+
+ main_settings["antialiased"] = import_settings_data->get("antialiased");
+ main_settings["multichannel_signed_distance_field"] = import_settings_data->get("multichannel_signed_distance_field");
+ main_settings["msdf_pixel_range"] = import_settings_data->get("msdf_pixel_range");
+ main_settings["msdf_size"] = import_settings_data->get("msdf_size");
+ main_settings["force_autohinter"] = import_settings_data->get("force_autohinter");
+ main_settings["hinting"] = import_settings_data->get("hinting");
+ main_settings["oversampling"] = import_settings_data->get("oversampling");
+ main_settings["compress"] = import_settings_data->get("compress");
+
+ Vector<String> variations;
+ for (TreeItem *vars_item = vars_list_root->get_first_child(); vars_item; vars_item = vars_item->get_next()) {
+ String variation;
+ Ref<DynamicFontImportSettingsData> import_variation_data = vars_item->get_metadata(0);
+ ERR_FAIL_NULL(import_variation_data);
+
+ String name = vars_item->get_text(0);
+ variation += ("name=" + name);
+ for (Map<StringName, Variant>::Element *E = import_variation_data->settings.front(); E; E = E->next()) {
+ if (!variation.is_empty()) {
+ variation += ",";
+ }
+ variation += (String(E->key()) + "=" + String(E->get()));
+ }
+ variations.push_back(variation);
+ }
+ main_settings["preload/configurations"] = variations;
+
+ Vector<String> langs_enabled;
+ Vector<String> langs_disabled;
+ for (TreeItem *lang_item = lang_list_root->get_first_child(); lang_item; lang_item = lang_item->get_next()) {
+ bool selected = lang_item->is_checked(0);
+ String name = lang_item->get_text(1);
+ if (selected) {
+ langs_enabled.push_back(name);
+ } else {
+ langs_disabled.push_back(name);
+ }
+ }
+ main_settings["support_overrides/language_enabled"] = langs_enabled;
+ main_settings["support_overrides/language_disabled"] = langs_disabled;
+
+ Vector<String> scripts_enabled;
+ Vector<String> scripts_disabled;
+ for (TreeItem *script_item = script_list_root->get_first_child(); script_item; script_item = script_item->get_next()) {
+ bool selected = script_item->is_checked(0);
+ String name = script_item->get_text(1);
+ if (selected) {
+ scripts_enabled.push_back(name);
+ } else {
+ scripts_disabled.push_back(name);
+ }
+ }
+ main_settings["support_overrides/script_enabled"] = scripts_enabled;
+ main_settings["support_overrides/script_disabled"] = scripts_disabled;
+
+ if (!selected_chars.is_empty()) {
+ Vector<String> ranges;
+ char32_t start = selected_chars.front()->get();
+ for (Set<char32_t>::Element *E = selected_chars.front()->next(); E; E = E->next()) {
+ if (E->prev() && ((E->prev()->get() + 1) != E->get())) {
+ ranges.push_back(String("0x") + String::num_int64(start, 16) + String("-0x") + String::num_int64(E->prev()->get(), 16));
+ start = E->get();
+ }
+ }
+ ranges.push_back(String("0x") + String::num_int64(start, 16) + String("-0x") + String::num_int64(selected_chars.back()->get(), 16));
+ main_settings["preload/char_ranges"] = ranges;
+ }
+
+ if (!selected_glyphs.is_empty()) {
+ Vector<String> ranges;
+ int32_t start = selected_glyphs.front()->get();
+ for (Set<int32_t>::Element *E = selected_glyphs.front()->next(); E; E = E->next()) {
+ if (E->prev() && ((E->prev()->get() + 1) != E->get())) {
+ ranges.push_back(String("0x") + String::num_int64(start, 16) + String("-0x") + String::num_int64(E->prev()->get(), 16));
+ start = E->get();
+ }
+ }
+ ranges.push_back(String("0x") + String::num_int64(start, 16) + String("-0x") + String::num_int64(selected_glyphs.back()->get(), 16));
+ main_settings["preload/glyph_ranges"] = ranges;
+ }
+
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ print_line("Import settings:");
+ for (Map<StringName, Variant>::Element *E = main_settings.front(); E; E = E->next()) {
+ print_line(String(" ") + String(E->key()).utf8().get_data() + " == " + String(E->get()).utf8().get_data());
+ }
+ }
+
+ EditorFileSystem::get_singleton()->reimport_file_with_custom_parameters(base_path, "font_data_dynamic", main_settings);
+}
+
+void DynamicFontImportSettings::open_settings(const String &p_path) {
+ // Load base font data.
+ Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
+
+ // Load font for preview.
+ Ref<FontData> dfont_prev;
+ dfont_prev.instantiate();
+ dfont_prev->set_data(data);
+
+ font_preview.instantiate();
+ font_preview->add_data(dfont_prev);
+
+ String sample;
+ static const String sample_base = U"12漢字ԱբΑαАбΑαאבابܐܒހށआআਆઆଆஆఆಆആආกิກິༀကႠა한글ሀᎣᐁᚁᚠᜀᜠᝀᝠកᠠᤁᥐAb😀";
+ for (int i = 0; i < sample_base.length(); i++) {
+ if (dfont_prev->has_char(sample_base[i])) {
+ sample += sample_base[i];
+ }
+ }
+ if (sample.is_empty()) {
+ sample = dfont_prev->get_supported_chars().substr(0, 6);
+ }
+ font_preview_label->set_text(sample);
+
+ // Load second copy of font with MSDF disabled for the glyph table and metadata extraction.
+ Ref<FontData> dfont_main;
+ dfont_main.instantiate();
+ dfont_main->set_data(data);
+ dfont_main->set_multichannel_signed_distance_field(false);
+
+ font_main.instantiate();
+ font_main->add_data(dfont_main);
+ text_edit->add_theme_font_override("font", font_main);
+
+ base_path = p_path;
+
+ inspector_vars->edit(nullptr);
+ inspector_general->edit(nullptr);
+
+ int gww = get_theme_font("font")->get_string_size("00000", get_theme_font_size("font_size")).x + 50;
+ glyph_table->set_column_custom_minimum_width(0, gww);
+
+ glyph_table->clear();
+ vars_list->clear();
+ lang_list->clear();
+ script_list->clear();
+
+ selected_chars.clear();
+ selected_glyphs.clear();
+ text_edit->set_text(String());
+
+ vars_list_root = vars_list->create_item();
+ lang_list_root = lang_list->create_item();
+ script_list_root = script_list->create_item();
+
+ options_variations.clear();
+ Dictionary var_list = dfont_main->get_supported_variation_list();
+ for (int i = 0; i < var_list.size(); i++) {
+ int32_t tag = var_list.get_key_at_index(i);
+ Vector3i value = var_list.get_value_at_index(i);
+ options_variations.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, TS->tag_to_name(tag), PROPERTY_HINT_RANGE, itos(value.x) + "," + itos(value.y) + ",1"), value.z));
+ }
+ options_variations.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "size", PROPERTY_HINT_RANGE, "0,127,1"), 16));
+ options_variations.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_RANGE, "0,127,1"), 0));
+ options_variations.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "extra_spacing_glyph"), 0));
+ options_variations.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "extra_spacing_space"), 0));
+
+ import_settings_data->defaults.clear();
+ for (List<ResourceImporter::ImportOption>::Element *E = options_general.front(); E; E = E->next()) {
+ import_settings_data->defaults[E->get().option.name] = E->get().default_value;
+ }
+
+ Ref<ConfigFile> config;
+ config.instantiate();
+ ERR_FAIL_NULL(config);
+
+ Error err = config->load(p_path + ".import");
+ print_verbose("Loading import settings:");
+ if (err == OK) {
+ List<String> keys;
+ config->get_section_keys("params", &keys);
+ for (List<String>::Element *E = keys.front(); E; E = E->next()) {
+ String key = E->get();
+ print_verbose(String(" ") + key + " == " + String(config->get_value("params", key)));
+ if (key == "preload/char_ranges") {
+ Vector<String> ranges = config->get_value("params", key);
+ for (int i = 0; i < ranges.size(); i++) {
+ int32_t start, end;
+ Vector<String> tokens = ranges[i].split("-");
+ if (tokens.size() == 2) {
+ if (!ResourceImporterDynamicFont::_decode_range(tokens[0], start) || !ResourceImporterDynamicFont::_decode_range(tokens[1], end)) {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ } else if (tokens.size() == 1) {
+ if (!ResourceImporterDynamicFont::_decode_range(tokens[0], start)) {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ end = start;
+ } else {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ for (int32_t j = start; j <= end; j++) {
+ selected_chars.insert(j);
+ }
+ }
+ } else if (key == "preload/glyph_ranges") {
+ Vector<String> ranges = config->get_value("params", key);
+ for (int i = 0; i < ranges.size(); i++) {
+ int32_t start, end;
+ Vector<String> tokens = ranges[i].split("-");
+ if (tokens.size() == 2) {
+ if (!ResourceImporterDynamicFont::_decode_range(tokens[0], start) || !ResourceImporterDynamicFont::_decode_range(tokens[1], end)) {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ } else if (tokens.size() == 1) {
+ if (!ResourceImporterDynamicFont::_decode_range(tokens[0], start)) {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ end = start;
+ } else {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ for (int32_t j = start; j <= end; j++) {
+ selected_glyphs.insert(j);
+ }
+ }
+ } else if (key == "preload/configurations") {
+ Vector<String> variations = config->get_value("params", key);
+ for (int i = 0; i < variations.size(); i++) {
+ TreeItem *vars_item = vars_list->create_item(vars_list_root);
+ ERR_FAIL_NULL(vars_item);
+
+ vars_item->set_text(0, TTR("Configuration") + " " + itos(i));
+ vars_item->set_editable(0, true);
+ vars_item->add_button(1, vars_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove Variation"));
+ vars_item->set_button_color(1, 0, Color(1, 1, 1, 0.75));
+
+ Ref<DynamicFontImportSettingsData> import_variation_data_custom;
+ import_variation_data_custom.instantiate();
+ import_variation_data_custom->owner = this;
+ ERR_FAIL_NULL(import_variation_data_custom);
+
+ for (List<ResourceImporter::ImportOption>::Element *F = options_variations.front(); F; F = F->next()) {
+ import_variation_data_custom->defaults[F->get().option.name] = F->get().default_value;
+ }
+
+ import_variation_data_custom->options = options_variations;
+
+ vars_item->set_metadata(0, import_variation_data_custom);
+ Vector<String> variation_tags = variations[i].split(",");
+ for (int j = 0; j < variation_tags.size(); j++) {
+ Vector<String> tokens = variation_tags[j].split("=");
+ if (tokens[0] == "name") {
+ vars_item->set_text(0, tokens[1]);
+ } else if (tokens[0] == "size" || tokens[0] == "outline_size" || tokens[0] == "extra_spacing_space" || tokens[0] == "extra_spacing_glyph") {
+ import_variation_data_custom->set(tokens[0], tokens[1].to_int());
+ } else {
+ import_variation_data_custom->set(tokens[0], tokens[1].to_float());
+ }
+ }
+ }
+ } else if (key == "support_overrides/language_enabled") {
+ PackedStringArray _langs = config->get_value("params", key);
+ for (int i = 0; i < _langs.size(); i++) {
+ TreeItem *lang_item = lang_list->create_item(lang_list_root);
+ ERR_FAIL_NULL(lang_item);
+
+ lang_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ lang_item->set_editable(0, true);
+ lang_item->set_checked(0, true);
+ lang_item->set_text(1, _langs[i]);
+ lang_item->set_editable(1, true);
+ lang_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ }
+ } else if (key == "support_overrides/language_disabled") {
+ PackedStringArray _langs = config->get_value("params", key);
+ for (int i = 0; i < _langs.size(); i++) {
+ TreeItem *lang_item = lang_list->create_item(lang_list_root);
+ ERR_FAIL_NULL(lang_item);
+
+ lang_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ lang_item->set_editable(0, true);
+ lang_item->set_checked(0, false);
+ lang_item->set_text(1, _langs[i]);
+ lang_item->set_editable(1, true);
+ lang_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ }
+ } else if (key == "support_overrides/script_enabled") {
+ PackedStringArray _scripts = config->get_value("params", key);
+ for (int i = 0; i < _scripts.size(); i++) {
+ TreeItem *script_item = script_list->create_item(script_list_root);
+ ERR_FAIL_NULL(script_item);
+
+ script_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ script_item->set_editable(0, true);
+ script_item->set_checked(0, true);
+ script_item->set_text(1, _scripts[i]);
+ script_item->set_editable(1, true);
+ script_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ }
+ } else if (key == "support_overrides/script_disabled") {
+ PackedStringArray _scripts = config->get_value("params", key);
+ for (int i = 0; i < _scripts.size(); i++) {
+ TreeItem *script_item = script_list->create_item(script_list_root);
+ ERR_FAIL_NULL(script_item);
+
+ script_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ script_item->set_editable(0, true);
+ script_item->set_checked(0, false);
+ script_item->set_text(1, _scripts[i]);
+ script_item->set_editable(1, true);
+ script_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
+ }
+ } else {
+ Variant value = config->get_value("params", key);
+ import_settings_data->defaults[key] = value;
+ }
+ }
+ }
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(selected_glyphs.size()));
+
+ import_settings_data->options = options_general;
+ inspector_general->edit(import_settings_data.ptr());
+ import_settings_data->notify_property_list_changed();
+
+ if (font_preview->get_data_count() > 0) {
+ font_preview->get_data(0)->set_antialiased(import_settings_data->get("antialiased"));
+ font_preview->get_data(0)->set_multichannel_signed_distance_field(import_settings_data->get("multichannel_signed_distance_field"));
+ font_preview->get_data(0)->set_msdf_pixel_range(import_settings_data->get("msdf_pixel_range"));
+ font_preview->get_data(0)->set_msdf_size(import_settings_data->get("msdf_size"));
+ font_preview->get_data(0)->set_force_autohinter(import_settings_data->get("force_autohinter"));
+ font_preview->get_data(0)->set_hinting((TextServer::Hinting)import_settings_data->get("hinting").operator int());
+ font_preview->get_data(0)->set_oversampling(import_settings_data->get("oversampling"));
+ }
+ font_preview_label->add_theme_font_override("font", font_preview);
+ font_preview_label->update();
+
+ _variations_validate();
+
+ popup_centered_ratio();
+
+ set_title(vformat(TTR("Advanced Import Settings for '%s'"), base_path.get_file()));
+}
+
+DynamicFontImportSettings *DynamicFontImportSettings::get_singleton() {
+ return singleton;
+}
+
+DynamicFontImportSettings::DynamicFontImportSettings() {
+ singleton = this;
+
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "antialiased"), true));
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), 8));
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_RANGE, "1,250,1"), 48));
+ 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::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
+ options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "compress", PROPERTY_HINT_NONE, ""), false));
+
+ // Popup menus
+
+ menu_langs = memnew(PopupMenu);
+ menu_langs->set_name("Language");
+ for (int i = 0; langs[i].name != String(); i++) {
+ if (langs[i].name == "-") {
+ menu_langs->add_separator();
+ } else {
+ menu_langs->add_item(langs[i].name + " (" + langs[i].code + ")", i);
+ }
+ }
+ add_child(menu_langs);
+ menu_langs->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_lang_add_item));
+
+ menu_scripts = memnew(PopupMenu);
+ menu_scripts->set_name("Script");
+ for (int i = 0; scripts[i].name != String(); i++) {
+ if (scripts[i].name == "-") {
+ menu_scripts->add_separator();
+ } else {
+ menu_scripts->add_item(scripts[i].name + " (" + scripts[i].code + ")", i);
+ }
+ }
+ add_child(menu_scripts);
+ menu_scripts->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_script_add_item));
+
+ Color warn_color = (EditorNode::get_singleton()) ? EditorNode::get_singleton()->get_gui_base()->get_theme_color("warning_color", "Editor") : Color(1, 1, 0);
+
+ // Root layout
+
+ VBoxContainer *root_vb = memnew(VBoxContainer);
+ add_child(root_vb);
+
+ main_pages = memnew(TabContainer);
+ main_pages->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ main_pages->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ root_vb->add_child(main_pages);
+
+ label_warn = memnew(Label);
+ label_warn->set_align(Label::ALIGN_CENTER);
+ label_warn->set_text("");
+ root_vb->add_child(label_warn);
+ label_warn->add_theme_color_override("font_color", warn_color);
+ label_warn->hide();
+
+ // Page 1 layout: Rendering Options
+
+ VBoxContainer *page1_vb = memnew(VBoxContainer);
+ page1_vb->set_meta("_tab_name", TTR("Rendering options"));
+ main_pages->add_child(page1_vb);
+
+ page1_description = memnew(Label);
+ page1_description->set_text(TTR("Select font rendering options:"));
+ page1_description->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page1_vb->add_child(page1_description);
+
+ HSplitContainer *page1_hb = memnew(HSplitContainer);
+ page1_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ page1_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page1_vb->add_child(page1_hb);
+
+ font_preview_label = memnew(Label);
+ font_preview_label->add_theme_font_size_override("font_size", 200 * EDSCALE);
+ font_preview_label->set_align(Label::ALIGN_CENTER);
+ font_preview_label->set_valign(Label::VALIGN_CENTER);
+ font_preview_label->set_autowrap_mode(Label::AUTOWRAP_ARBITRARY);
+ font_preview_label->set_clip_text(true);
+ font_preview_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ font_preview_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page1_hb->add_child(font_preview_label);
+
+ inspector_general = memnew(EditorInspector);
+ inspector_general->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ inspector_general->set_custom_minimum_size(Size2(300 * EDSCALE, 250 * EDSCALE));
+ inspector_general->connect("property_edited", callable_mp(this, &DynamicFontImportSettings::_main_prop_changed));
+ page1_hb->add_child(inspector_general);
+
+ // Page 2 layout: Configurations
+ VBoxContainer *page2_vb = memnew(VBoxContainer);
+ page2_vb->set_meta("_tab_name", TTR("Sizes and variations"));
+ main_pages->add_child(page2_vb);
+
+ page2_description = memnew(Label);
+ page2_description->set_text(TTR("Add font size, variation coordinates, and extra spacing combinations to pre-render:"));
+ page2_description->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page2_description->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
+ page2_vb->add_child(page2_description);
+
+ HSplitContainer *page2_hb = memnew(HSplitContainer);
+ page2_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ page2_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page2_vb->add_child(page2_hb);
+
+ VBoxContainer *page2_side_vb = memnew(VBoxContainer);
+ page2_hb->add_child(page2_side_vb);
+
+ HBoxContainer *page2_hb_vars = memnew(HBoxContainer);
+ page2_side_vb->add_child(page2_hb_vars);
+
+ label_vars = memnew(Label);
+ page2_hb_vars->add_child(label_vars);
+ label_vars->set_align(Label::ALIGN_CENTER);
+ label_vars->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ label_vars->set_text(TTR("Configuration:"));
+
+ add_var = memnew(Button);
+ page2_hb_vars->add_child(add_var);
+ add_var->set_tooltip(TTR("Add configuration"));
+ add_var->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_var->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_variation_add));
+
+ vars_list = memnew(Tree);
+ page2_side_vb->add_child(vars_list);
+ vars_list->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
+ vars_list->set_hide_root(true);
+ vars_list->set_columns(2);
+ vars_list->set_column_expand(0, true);
+ vars_list->set_column_custom_minimum_width(0, 80 * EDSCALE);
+ vars_list->set_column_expand(1, false);
+ vars_list->set_column_custom_minimum_width(1, 50 * EDSCALE);
+ vars_list->connect("item_selected", callable_mp(this, &DynamicFontImportSettings::_variation_selected));
+ vars_list->connect("button_pressed", callable_mp(this, &DynamicFontImportSettings::_variation_remove));
+ vars_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+ inspector_vars = memnew(EditorInspector);
+ inspector_vars->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ inspector_vars->connect("property_edited", callable_mp(this, &DynamicFontImportSettings::_variation_changed));
+ page2_hb->add_child(inspector_vars);
+
+ // Page 3 layout: Text to select glyphs
+ VBoxContainer *page3_vb = memnew(VBoxContainer);
+ page3_vb->set_meta("_tab_name", TTR("Glyphs from the text"));
+ main_pages->add_child(page3_vb);
+
+ page3_description = memnew(Label);
+ page3_description->set_text(TTR("Enter a text to shape and add all required glyphs to pre-render list:"));
+ page3_description->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page3_description->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
+ page3_vb->add_child(page3_description);
+
+ HBoxContainer *ot_hb = memnew(HBoxContainer);
+ page3_vb->add_child(ot_hb);
+ ot_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ Label *label_ed_ftr = memnew(Label);
+ ot_hb->add_child(label_ed_ftr);
+ label_ed_ftr->set_text(TTR("OpenType features:"));
+
+ ftr_edit = memnew(LineEdit);
+ ot_hb->add_child(ftr_edit);
+ ftr_edit->connect("text_changed", callable_mp(this, &DynamicFontImportSettings::_change_text_opts));
+ ftr_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ Label *label_ed_lang = memnew(Label);
+ ot_hb->add_child(label_ed_lang);
+ label_ed_lang->set_text(TTR("Text language:"));
+
+ lang_edit = memnew(LineEdit);
+ ot_hb->add_child(lang_edit);
+ lang_edit->connect("text_changed", callable_mp(this, &DynamicFontImportSettings::_change_text_opts));
+ lang_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ text_edit = memnew(TextEdit);
+ page3_vb->add_child(text_edit);
+ text_edit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ text_edit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ HBoxContainer *text_hb = memnew(HBoxContainer);
+ page3_vb->add_child(text_hb);
+ text_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ label_glyphs = memnew(Label);
+ text_hb->add_child(label_glyphs);
+ label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(0));
+ label_glyphs->set_custom_minimum_size(Size2(50 * EDSCALE, 0));
+
+ Button *btn_fill = memnew(Button);
+ text_hb->add_child(btn_fill);
+ btn_fill->set_text(TTR("Shape text and add glyphs"));
+ btn_fill->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_glyph_text_selected));
+
+ Button *btn_clear = memnew(Button);
+ text_hb->add_child(btn_clear);
+ btn_clear->set_text(TTR("Clear glyph list"));
+ btn_clear->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_glyph_clear));
+
+ // Page 4 layout: Character map
+ VBoxContainer *page4_vb = memnew(VBoxContainer);
+ page4_vb->set_meta("_tab_name", TTR("Glyphs from the character map"));
+ main_pages->add_child(page4_vb);
+
+ page4_description = memnew(Label);
+ page4_description->set_text(TTR("Add or remove additional glyphs from the character map to pre-render list:\nNote: Some stylistic alternatives and glyph variants do not have one-to-one correspondence to character, and not shown in this map, use \"Glyphs from the text\" to add these."));
+ page4_description->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page4_description->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
+ page4_vb->add_child(page4_description);
+
+ HSplitContainer *glyphs_split = memnew(HSplitContainer);
+ glyphs_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ glyphs_split->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page4_vb->add_child(glyphs_split);
+
+ glyph_table = memnew(Tree);
+ glyphs_split->add_child(glyph_table);
+ glyph_table->set_custom_minimum_size(Size2((30 * 16 + 100) * EDSCALE, 0));
+ glyph_table->set_columns(17);
+ glyph_table->set_column_expand(0, false);
+ glyph_table->set_hide_root(true);
+ glyph_table->set_allow_reselect(true);
+ glyph_table->set_select_mode(Tree::SELECT_SINGLE);
+ glyph_table->connect("item_activated", callable_mp(this, &DynamicFontImportSettings::_glyph_selected));
+ glyph_table->set_column_titles_visible(true);
+ for (int i = 0; i < 16; i++) {
+ glyph_table->set_column_title(i + 1, String::num_int64(i, 16));
+ }
+ glyph_table->add_theme_style_override("selected", glyph_table->get_theme_stylebox("bg"));
+ glyph_table->add_theme_style_override("selected_focus", glyph_table->get_theme_stylebox("bg"));
+ glyph_table->add_theme_constant_override("hseparation", 0);
+ glyph_table->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ glyph_table->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+ 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_hide_root(true);
+ glyph_tree->set_column_expand(0, false);
+ glyph_tree->set_column_expand(1, true);
+ glyph_tree->set_column_custom_minimum_width(0, 120 * EDSCALE);
+ glyph_tree->connect("item_activated", callable_mp(this, &DynamicFontImportSettings::_range_edited));
+ glyph_tree->connect("item_selected", callable_mp(this, &DynamicFontImportSettings::_range_selected));
+ glyph_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ glyph_root = glyph_tree->create_item();
+ for (int i = 0; unicode_ranges[i].name != String(); i++) {
+ _add_glyph_range_item(unicode_ranges[i].start, unicode_ranges[i].end, unicode_ranges[i].name);
+ }
+
+ // Page 4 layout: Metadata override
+ VBoxContainer *page5_vb = memnew(VBoxContainer);
+ page5_vb->set_meta("_tab_name", TTR("Metadata override"));
+ main_pages->add_child(page5_vb);
+
+ page5_description = memnew(Label);
+ page5_description->set_text(TTR("Add or remove language and script support overrides, to control fallback font selection order:"));
+ page5_description->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ page5_description->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
+ page5_vb->add_child(page5_description);
+
+ HBoxContainer *hb_lang = memnew(HBoxContainer);
+ page5_vb->add_child(hb_lang);
+
+ label_langs = memnew(Label);
+ label_langs->set_align(Label::ALIGN_CENTER);
+ label_langs->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ label_langs->set_text(TTR("Language support overrides"));
+ hb_lang->add_child(label_langs);
+
+ add_lang = memnew(Button);
+ hb_lang->add_child(add_lang);
+ add_lang->set_tooltip(TTR("Add language override"));
+ add_lang->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_lang->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_lang_add));
+
+ lang_list = memnew(Tree);
+ page5_vb->add_child(lang_list);
+ lang_list->set_hide_root(true);
+ lang_list->set_columns(3);
+ lang_list->set_column_expand(0, false); // Check
+ lang_list->set_column_custom_minimum_width(0, 50 * EDSCALE);
+ lang_list->set_column_expand(1, true);
+ lang_list->set_column_custom_minimum_width(1, 80 * EDSCALE);
+ lang_list->set_column_expand(2, false);
+ lang_list->set_column_custom_minimum_width(2, 50 * EDSCALE);
+ lang_list->connect("button_pressed", callable_mp(this, &DynamicFontImportSettings::_lang_remove));
+ lang_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+ HBoxContainer *hb_script = memnew(HBoxContainer);
+ page5_vb->add_child(hb_script);
+
+ label_script = memnew(Label);
+ label_script->set_align(Label::ALIGN_CENTER);
+ label_script->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ label_script->set_text(TTR("Script support overrides"));
+ hb_script->add_child(label_script);
+
+ add_script = memnew(Button);
+ hb_script->add_child(add_script);
+ add_script->set_tooltip(TTR("Add script override"));
+ add_script->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
+ add_script->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_script_add));
+
+ script_list = memnew(Tree);
+ page5_vb->add_child(script_list);
+ script_list->set_hide_root(true);
+ script_list->set_columns(3);
+ script_list->set_column_expand(0, false);
+ script_list->set_column_custom_minimum_width(0, 50 * EDSCALE);
+ script_list->set_column_expand(1, true);
+ script_list->set_column_custom_minimum_width(1, 80 * EDSCALE);
+ script_list->set_column_expand(2, false);
+ script_list->set_column_custom_minimum_width(2, 50 * EDSCALE);
+ script_list->connect("button_pressed", callable_mp(this, &DynamicFontImportSettings::_script_remove));
+ script_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+ // Common
+
+ import_settings_data.instantiate();
+ import_settings_data->owner = this;
+
+ get_ok_button()->set_text(TTR("Reimport"));
+ get_cancel_button()->set_text(TTR("Close"));
+}
diff --git a/editor/import/dynamicfont_import_settings.h b/editor/import/dynamicfont_import_settings.h
new file mode 100644
index 0000000000..05f5e8e00b
--- /dev/null
+++ b/editor/import/dynamicfont_import_settings.h
@@ -0,0 +1,167 @@
+/*************************************************************************/
+/* dynamicfont_import_settings.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef FONTDATA_IMPORT_SETTINGS_H
+#define FONTDATA_IMPORT_SETTINGS_H
+
+#include "editor/editor_file_dialog.h"
+#include "editor/editor_inspector.h"
+
+#include "editor/import/resource_importer_dynamicfont.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 DynamicFontImportSettingsData;
+
+class DynamicFontImportSettings : public ConfirmationDialog {
+ GDCLASS(DynamicFontImportSettings, ConfirmationDialog)
+ friend class DynamicFontImportSettingsData;
+
+ enum ItemButton {
+ BUTTON_ADD_VAR,
+ BUTTON_REMOVE_VAR,
+ };
+
+ static DynamicFontImportSettings *singleton;
+
+ String base_path;
+
+ Ref<DynamicFontImportSettingsData> import_settings_data;
+ List<ResourceImporter::ImportOption> options_variations;
+ List<ResourceImporter::ImportOption> options_general;
+
+ // Root layout
+ Label *label_warn = nullptr;
+ TabContainer *main_pages = nullptr;
+
+ // Page 1 layout: Rendering Options
+ Label *page1_description = nullptr;
+ Label *font_preview_label = nullptr;
+ EditorInspector *inspector_general = nullptr;
+
+ void _main_prop_changed(const String &p_edited_property);
+
+ // Page 2 layout: Configurations
+ Label *page2_description = nullptr;
+ Label *label_vars = nullptr;
+ Button *add_var = nullptr;
+ Tree *vars_list = nullptr;
+ TreeItem *vars_list_root = nullptr;
+ EditorInspector *inspector_vars = nullptr;
+
+ void _variation_add();
+ void _variation_selected();
+ void _variation_remove(Object *p_item, int p_column, int p_id);
+ void _variation_changed(const String &p_edited_property);
+ void _variations_validate();
+
+ // Page 3 layout: Text to select glyphs
+ Label *page3_description = nullptr;
+ Label *label_glyphs = nullptr;
+ TextEdit *text_edit = nullptr;
+ LineEdit *ftr_edit = nullptr;
+ LineEdit *lang_edit = nullptr;
+
+ void _change_text_opts();
+ void _glyph_text_selected();
+ void _glyph_clear();
+
+ // Page 4 layout: Character map
+ Label *page4_description = nullptr;
+ Tree *glyph_table = nullptr;
+ Tree *glyph_tree = nullptr;
+ TreeItem *glyph_root = nullptr;
+
+ void _glyph_selected();
+ void _range_edited();
+ void _range_selected();
+ void _edit_range(int32_t p_start, int32_t p_end);
+ bool _char_update(int32_t p_char);
+ void _range_update(int32_t p_start, int32_t p_end);
+
+ // Page 5 layout: Metadata override
+ Label *page5_description = nullptr;
+ Button *add_lang = nullptr;
+ Button *add_script = nullptr;
+
+ PopupMenu *menu_langs = nullptr;
+ PopupMenu *menu_scripts = nullptr;
+
+ Tree *lang_list = nullptr;
+ TreeItem *lang_list_root = nullptr;
+
+ Tree *script_list = nullptr;
+ TreeItem *script_list_root = nullptr;
+ Label *label_langs = nullptr;
+ Label *label_script = nullptr;
+
+ void _lang_add();
+ void _lang_add_item(int p_option);
+ void _lang_remove(Object *p_item, int p_column, int p_id);
+
+ void _script_add();
+ void _script_add_item(int p_option);
+ void _script_remove(Object *p_item, int p_column, int p_id);
+
+ // Common
+
+ void _add_glyph_range_item(int32_t p_start, int32_t p_end, const String &p_name);
+
+ Ref<Font> font_preview;
+ Ref<Font> font_main;
+
+ Set<char32_t> selected_chars;
+ Set<int32_t> selected_glyphs;
+
+ void _re_import();
+
+ String _pad_zeros(const String &p_hex) const;
+
+protected:
+ void _notification(int p_what);
+
+public:
+ void open_settings(const String &p_path);
+ static DynamicFontImportSettings *get_singleton();
+
+ DynamicFontImportSettings();
+};
+
+#endif // FONTDATA_IMPORT_SETTINGS_H
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index d3183e5a8d..7ab80ac3b4 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -87,7 +87,7 @@ struct ColladaImport {
Error _create_scene(Collada::Node *p_node, Node3D *p_parent);
Error _create_resources(Collada::Node *p_node, bool p_use_compression);
Error _create_material(const String &p_target);
- Error _create_mesh_surfaces(bool p_optimize, Ref<EditorSceneImporterMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<EditorSceneImporterMesh>> p_morph_meshes = Vector<Ref<EditorSceneImporterMesh>>(), bool p_use_compression = false, bool p_use_mesh_material = false);
+ Error _create_mesh_surfaces(bool p_optimize, Ref<EditorSceneImporterMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform3D &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<EditorSceneImporterMesh>> p_morph_meshes = Vector<Ref<EditorSceneImporterMesh>>(), bool p_use_compression = false, bool p_use_mesh_material = false);
Error load(const String &p_path, int p_flags, bool p_force_make_tangents = false, bool p_use_compression = false);
void _fix_param_animation_tracks();
void create_animation(int p_clip, bool p_make_tracks_in_all_bones, bool p_import_value_tracks);
@@ -300,7 +300,7 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Node3D *p_parent) {
nm.node = node;
node_map[p_node->id] = nm;
node_name_map[node->get_name()] = p_node->id;
- Transform xf = p_node->default_transform;
+ Transform3D xf = p_node->default_transform;
xf = collada.fix_transform(xf) * p_node->post_transform;
node->set_transform(xf);
@@ -457,7 +457,7 @@ Error ColladaImport::_create_material(const String &p_target) {
return OK;
}
-Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<EditorSceneImporterMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<EditorSceneImporterMesh>> p_morph_meshes, bool p_use_compression, bool p_use_mesh_material) {
+Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<EditorSceneImporterMesh> &p_mesh, const Map<String, Collada::NodeGeometry::Material> &p_material_map, const Collada::MeshData &meshdata, const Transform3D &p_local_xform, const Vector<int> &bone_remap, const Collada::SkinControllerData *p_skin_controller, const Collada::MorphControllerData *p_morph_data, Vector<Ref<EditorSceneImporterMesh>> p_morph_meshes, bool p_use_compression, bool p_use_mesh_material) {
bool local_xform_mirror = p_local_xform.basis.determinant() < 0;
if (p_morph_data) {
@@ -504,61 +504,121 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<EditorSceneImpor
const Collada::MeshData::Source *normal_src = nullptr;
int normal_ofs = 0;
- if (p.sources.has("NORMAL")) {
- String normal_source_id = p.sources["NORMAL"].source;
- normal_ofs = p.sources["NORMAL"].offset;
- ERR_FAIL_COND_V(!meshdata.sources.has(normal_source_id), ERR_INVALID_DATA);
- normal_src = &meshdata.sources[normal_source_id];
+ {
+ String normal_source_id = "";
+
+ if (p.sources.has("NORMAL")) {
+ normal_source_id = p.sources["NORMAL"].source;
+ normal_ofs = p.sources["NORMAL"].offset;
+ } else if (meshdata.vertices[vertex_src_id].sources.has("NORMAL")) {
+ normal_source_id = meshdata.vertices[vertex_src_id].sources["NORMAL"];
+ normal_ofs = vertex_ofs;
+ }
+
+ if (normal_source_id != "") {
+ ERR_FAIL_COND_V(!meshdata.sources.has(normal_source_id), ERR_INVALID_DATA);
+ normal_src = &meshdata.sources[normal_source_id];
+ }
}
const Collada::MeshData::Source *binormal_src = nullptr;
int binormal_ofs = 0;
- if (p.sources.has("TEXBINORMAL")) {
- String binormal_source_id = p.sources["TEXBINORMAL"].source;
- binormal_ofs = p.sources["TEXBINORMAL"].offset;
- ERR_FAIL_COND_V(!meshdata.sources.has(binormal_source_id), ERR_INVALID_DATA);
- binormal_src = &meshdata.sources[binormal_source_id];
+ {
+ String binormal_source_id = "";
+
+ if (p.sources.has("TEXBINORMAL")) {
+ binormal_source_id = p.sources["TEXBINORMAL"].source;
+ binormal_ofs = p.sources["TEXBINORMAL"].offset;
+ } else if (meshdata.vertices[vertex_src_id].sources.has("TEXBINORMAL")) {
+ binormal_source_id = meshdata.vertices[vertex_src_id].sources["TEXBINORMAL"];
+ binormal_ofs = vertex_ofs;
+ }
+
+ if (binormal_source_id != "") {
+ ERR_FAIL_COND_V(!meshdata.sources.has(binormal_source_id), ERR_INVALID_DATA);
+ binormal_src = &meshdata.sources[binormal_source_id];
+ }
}
const Collada::MeshData::Source *tangent_src = nullptr;
int tangent_ofs = 0;
- if (p.sources.has("TEXTANGENT")) {
- String tangent_source_id = p.sources["TEXTANGENT"].source;
- tangent_ofs = p.sources["TEXTANGENT"].offset;
- ERR_FAIL_COND_V(!meshdata.sources.has(tangent_source_id), ERR_INVALID_DATA);
- tangent_src = &meshdata.sources[tangent_source_id];
+ {
+ String tangent_source_id = "";
+
+ if (p.sources.has("TEXTANGENT")) {
+ tangent_source_id = p.sources["TEXTANGENT"].source;
+ tangent_ofs = p.sources["TEXTANGENT"].offset;
+ } else if (meshdata.vertices[vertex_src_id].sources.has("TEXTANGENT")) {
+ tangent_source_id = meshdata.vertices[vertex_src_id].sources["TEXTANGENT"];
+ tangent_ofs = vertex_ofs;
+ }
+
+ if (tangent_source_id != "") {
+ ERR_FAIL_COND_V(!meshdata.sources.has(tangent_source_id), ERR_INVALID_DATA);
+ tangent_src = &meshdata.sources[tangent_source_id];
+ }
}
const Collada::MeshData::Source *uv_src = nullptr;
int uv_ofs = 0;
- if (p.sources.has("TEXCOORD0")) {
- String uv_source_id = p.sources["TEXCOORD0"].source;
- uv_ofs = p.sources["TEXCOORD0"].offset;
- ERR_FAIL_COND_V(!meshdata.sources.has(uv_source_id), ERR_INVALID_DATA);
- uv_src = &meshdata.sources[uv_source_id];
+ {
+ String uv_source_id = "";
+
+ if (p.sources.has("TEXCOORD0")) {
+ uv_source_id = p.sources["TEXCOORD0"].source;
+ uv_ofs = p.sources["TEXCOORD0"].offset;
+ } else if (meshdata.vertices[vertex_src_id].sources.has("TEXCOORD0")) {
+ uv_source_id = meshdata.vertices[vertex_src_id].sources["TEXCOORD0"];
+ uv_ofs = vertex_ofs;
+ }
+
+ if (uv_source_id != "") {
+ ERR_FAIL_COND_V(!meshdata.sources.has(uv_source_id), ERR_INVALID_DATA);
+ uv_src = &meshdata.sources[uv_source_id];
+ }
}
const Collada::MeshData::Source *uv2_src = nullptr;
int uv2_ofs = 0;
- if (p.sources.has("TEXCOORD1")) {
- String uv2_source_id = p.sources["TEXCOORD1"].source;
- uv2_ofs = p.sources["TEXCOORD1"].offset;
- ERR_FAIL_COND_V(!meshdata.sources.has(uv2_source_id), ERR_INVALID_DATA);
- uv2_src = &meshdata.sources[uv2_source_id];
+ {
+ String uv2_source_id = "";
+
+ if (p.sources.has("TEXCOORD1")) {
+ uv2_source_id = p.sources["TEXCOORD1"].source;
+ uv2_ofs = p.sources["TEXCOORD1"].offset;
+ } else if (meshdata.vertices[vertex_src_id].sources.has("TEXCOORD1")) {
+ uv2_source_id = meshdata.vertices[vertex_src_id].sources["TEXCOORD1"];
+ uv2_ofs = vertex_ofs;
+ }
+
+ if (uv2_source_id != "") {
+ ERR_FAIL_COND_V(!meshdata.sources.has(uv2_source_id), ERR_INVALID_DATA);
+ uv2_src = &meshdata.sources[uv2_source_id];
+ }
}
const Collada::MeshData::Source *color_src = nullptr;
int color_ofs = 0;
- if (p.sources.has("COLOR")) {
- String color_source_id = p.sources["COLOR"].source;
- color_ofs = p.sources["COLOR"].offset;
- ERR_FAIL_COND_V(!meshdata.sources.has(color_source_id), ERR_INVALID_DATA);
- color_src = &meshdata.sources[color_source_id];
+ {
+ String color_source_id = "";
+
+ if (p.sources.has("COLOR")) {
+ color_source_id = p.sources["COLOR"].source;
+ color_ofs = p.sources["COLOR"].offset;
+ } else if (meshdata.vertices[vertex_src_id].sources.has("COLOR")) {
+ color_source_id = meshdata.vertices[vertex_src_id].sources["COLOR"];
+ color_ofs = vertex_ofs;
+ }
+
+ if (color_source_id != "") {
+ ERR_FAIL_COND_V(!meshdata.sources.has(color_source_id), ERR_INVALID_DATA);
+ color_src = &meshdata.sources[color_source_id];
+ }
}
//find largest source..
@@ -811,7 +871,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<EditorSceneImpor
if (has_weights) {
//if skeleton, localize
- Transform local_xform = p_local_xform;
+ Transform3D local_xform = p_local_xform;
for (int i = 0; i < vertex_array.size(); i++) {
vertex_array.write[i].vertex = local_xform.xform(vertex_array[i].vertex);
vertex_array.write[i].normal = local_xform.basis.xform(vertex_array[i].normal).normalized();
@@ -850,7 +910,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<EditorSceneImpor
}
Ref<SurfaceTool> surftool;
- surftool.instance();
+ surftool.instantiate();
surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
for (int k = 0; k < vertex_array.size(); k++) {
@@ -894,8 +954,8 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<EditorSceneImpor
surftool->add_vertex(vertex_array[k].vertex);
}
- for (List<int>::Element *E = indices_list.front(); E; E = E->next()) {
- surftool->add_index(E->get());
+ for (int &E : indices_list) {
+ surftool->add_index(E);
}
if (!normal_src) {
@@ -1037,7 +1097,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
Collada::SkinControllerData *skin = nullptr;
Collada::MorphControllerData *morph = nullptr;
String meshid;
- Transform apply_xform;
+ Transform3D apply_xform;
Vector<int> bone_remap;
Vector<Ref<EditorSceneImporterMesh>> morphs;
@@ -1073,9 +1133,9 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
if (apply_mesh_xform_to_vertices) {
apply_xform = collada.fix_transform(p_node->default_transform);
- node->set_transform(Transform());
+ node->set_transform(Transform3D());
} else {
- apply_xform = Transform();
+ apply_xform = Transform3D();
}
ERR_FAIL_COND_V(!skin->weights.sources.has("JOINT"), ERR_INVALID_DATA);
@@ -1414,7 +1474,7 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
//animation->set_loop(true);
//create animation tracks
- Vector<float> base_snapshots;
+ Vector<real_t> base_snapshots;
float f = 0;
float snapshot_interval = 1.0 / bake_fps; //should be customizable somewhere...
@@ -1461,12 +1521,12 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
continue;
}
- animation->add_track(Animation::TYPE_TRANSFORM);
+ animation->add_track(Animation::TYPE_TRANSFORM3D);
int track = animation->get_track_count() - 1;
animation->track_set_path(track, path);
animation->track_set_imported(track, true); //helps merging later
- Vector<float> snapshots = base_snapshots;
+ Vector<real_t> snapshots = base_snapshots;
if (nm.anim_tracks.size() == 1) {
//use snapshot keys from anim track instead, because this was most likely exported baked
@@ -1530,7 +1590,7 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
}
}
- Transform xform = cn->compute_transform(collada);
+ Transform3D xform = cn->compute_transform(collada);
xform = collada.fix_transform(xform) * cn->post_transform;
if (nm.bone >= 0) {
@@ -1544,8 +1604,8 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
}
Vector3 s = xform.basis.get_scale();
- bool singular_matrix = Math::is_equal_approx(s.x, 0.0f) || Math::is_equal_approx(s.y, 0.0f) || Math::is_equal_approx(s.z, 0.0f);
- Quat q = singular_matrix ? Quat() : xform.basis.get_rotation_quat();
+ bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z);
+ Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion();
Vector3 l = xform.origin;
animation->transform_track_insert_key(track, snapshots[i], l, q, s);
@@ -1584,19 +1644,19 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
continue;
}
- animation->add_track(Animation::TYPE_TRANSFORM);
+ animation->add_track(Animation::TYPE_TRANSFORM3D);
int track = animation->get_track_count() - 1;
animation->track_set_path(track, path);
animation->track_set_imported(track, true); //helps merging later
- Transform xform = cn->compute_transform(collada);
+ Transform3D xform = cn->compute_transform(collada);
xform = collada.fix_transform(xform) * cn->post_transform;
xform = sk->get_bone_rest(nm.bone).affine_inverse() * xform;
Vector3 s = xform.basis.get_scale();
- bool singular_matrix = Math::is_equal_approx(s.x, 0.0f) || Math::is_equal_approx(s.y, 0.0f) || Math::is_equal_approx(s.z, 0.0f);
- Quat q = singular_matrix ? Quat() : xform.basis.get_rotation_quat();
+ bool singular_matrix = Math::is_zero_approx(s.x) || Math::is_zero_approx(s.y) || Math::is_zero_approx(s.z);
+ Quaternion q = singular_matrix ? Quaternion() : xform.basis.get_rotation_quaternion();
Vector3 l = xform.origin;
animation->transform_track_insert_key(track, 0, l, q, s);
diff --git a/editor/import/editor_import_plugin.cpp b/editor/import/editor_import_plugin.cpp
index 44aff874eb..d219f6e325 100644
--- a/editor/import/editor_import_plugin.cpp
+++ b/editor/import/editor_import_plugin.cpp
@@ -35,102 +35,131 @@ EditorImportPlugin::EditorImportPlugin() {
}
String EditorImportPlugin::get_importer_name() const {
- ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("get_importer_name")), "");
- return get_script_instance()->call("get_importer_name");
+ String ret;
+ if (GDVIRTUAL_CALL(_get_importer_name, ret)) {
+ return ret;
+ }
+ ERR_FAIL_V_MSG(String(), "Unimplemented _get_importer_name in add-on.");
}
String EditorImportPlugin::get_visible_name() const {
- ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("get_visible_name")), "");
- return get_script_instance()->call("get_visible_name");
+ String ret;
+ if (GDVIRTUAL_CALL(_get_visible_name, ret)) {
+ return ret;
+ }
+ ERR_FAIL_V_MSG(String(), "Unimplemented _get_visible_name in add-on.");
}
void EditorImportPlugin::get_recognized_extensions(List<String> *p_extensions) const {
- ERR_FAIL_COND(!(get_script_instance() && get_script_instance()->has_method("get_recognized_extensions")));
- Array extensions = get_script_instance()->call("get_recognized_extensions");
- for (int i = 0; i < extensions.size(); i++) {
- p_extensions->push_back(extensions[i]);
+ Vector<String> extensions;
+
+ if (GDVIRTUAL_CALL(_get_recognized_extensions, extensions)) {
+ for (int i = 0; i < extensions.size(); i++) {
+ p_extensions->push_back(extensions[i]);
+ }
}
+ ERR_FAIL_MSG("Unimplemented _get_recognized_extensions in add-on.");
}
String EditorImportPlugin::get_preset_name(int p_idx) const {
- ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("get_preset_name")), "");
- return get_script_instance()->call("get_preset_name", p_idx);
+ String ret;
+ if (GDVIRTUAL_CALL(_get_preset_name, p_idx, ret)) {
+ return ret;
+ }
+ ERR_FAIL_V_MSG(String(), "Unimplemented _get_preset_name in add-on.");
}
int EditorImportPlugin::get_preset_count() const {
- ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("get_preset_count")), 0);
- return get_script_instance()->call("get_preset_count");
+ int ret;
+ if (GDVIRTUAL_CALL(_get_preset_count, ret)) {
+ return ret;
+ }
+ ERR_FAIL_V_MSG(-1, "Unimplemented _get_preset_count in add-on.");
}
String EditorImportPlugin::get_save_extension() const {
- ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("get_save_extension")), "");
- return get_script_instance()->call("get_save_extension");
+ String ret;
+ if (GDVIRTUAL_CALL(_get_save_extension, ret)) {
+ return ret;
+ }
+ ERR_FAIL_V_MSG(String(), "Unimplemented _get_save_extension in add-on.");
}
String EditorImportPlugin::get_resource_type() const {
- ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("get_resource_type")), "");
- return get_script_instance()->call("get_resource_type");
+ String ret;
+ if (GDVIRTUAL_CALL(_get_resource_type, ret)) {
+ return ret;
+ }
+ ERR_FAIL_V_MSG(String(), "Unimplemented _get_resource_type in add-on.");
}
float EditorImportPlugin::get_priority() const {
- if (!(get_script_instance() && get_script_instance()->has_method("get_priority"))) {
- return ResourceImporter::get_priority();
+ float ret;
+ if (GDVIRTUAL_CALL(_get_priority, ret)) {
+ return ret;
}
- return get_script_instance()->call("get_priority");
+ ERR_FAIL_V_MSG(-1, "Unimplemented _get_priority in add-on.");
}
int EditorImportPlugin::get_import_order() const {
- if (!(get_script_instance() && get_script_instance()->has_method("get_import_order"))) {
- return ResourceImporter::get_import_order();
+ int ret;
+ if (GDVIRTUAL_CALL(_get_import_order, ret)) {
+ return ret;
}
- return get_script_instance()->call("get_import_order");
+ ERR_FAIL_V_MSG(-1, "Unimplemented _get_import_order in add-on.");
}
void EditorImportPlugin::get_import_options(List<ResourceImporter::ImportOption> *r_options, int p_preset) const {
- ERR_FAIL_COND(!(get_script_instance() && get_script_instance()->has_method("get_import_options")));
Array needed;
needed.push_back("name");
needed.push_back("default_value");
- Array options = get_script_instance()->call("get_import_options", p_preset);
- for (int i = 0; i < options.size(); i++) {
- Dictionary d = options[i];
- ERR_FAIL_COND(!d.has_all(needed));
- String name = d["name"];
- Variant default_value = d["default_value"];
-
- PropertyHint hint = PROPERTY_HINT_NONE;
- if (d.has("property_hint")) {
- hint = (PropertyHint)d["property_hint"].operator int64_t();
- }
-
- String hint_string;
- if (d.has("hint_string")) {
- hint_string = d["hint_string"];
+ Array options;
+ if (GDVIRTUAL_CALL(_get_import_options, p_preset, options)) {
+ for (int i = 0; i < options.size(); i++) {
+ Dictionary d = options[i];
+ ERR_FAIL_COND(!d.has_all(needed));
+ String name = d["name"];
+ Variant default_value = d["default_value"];
+
+ PropertyHint hint = PROPERTY_HINT_NONE;
+ if (d.has("property_hint")) {
+ hint = (PropertyHint)d["property_hint"].operator int64_t();
+ }
+
+ String hint_string;
+ if (d.has("hint_string")) {
+ hint_string = d["hint_string"];
+ }
+
+ uint32_t usage = PROPERTY_USAGE_DEFAULT;
+ if (d.has("usage")) {
+ usage = d["usage"];
+ }
+
+ ImportOption option(PropertyInfo(default_value.get_type(), name, hint, hint_string, usage), default_value);
+ r_options->push_back(option);
}
-
- uint32_t usage = PROPERTY_USAGE_DEFAULT;
- if (d.has("usage")) {
- usage = d["usage"];
- }
-
- ImportOption option(PropertyInfo(default_value.get_type(), name, hint, hint_string, usage), default_value);
- r_options->push_back(option);
}
+
+ ERR_FAIL_MSG("Unimplemented _get_import_options in add-on.");
}
bool EditorImportPlugin::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
- ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("get_option_visibility")), true);
Dictionary d;
Map<StringName, Variant>::Element *E = p_options.front();
while (E) {
d[E->key()] = E->get();
E = E->next();
}
- return get_script_instance()->call("get_option_visibility", p_option, d);
+ bool visible;
+ if (GDVIRTUAL_CALL(_get_option_visibility, p_option, d, visible)) {
+ return visible;
+ }
+
+ 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 Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
- ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("import")), ERR_UNAVAILABLE);
Dictionary options;
Array platform_variants, gen_files;
@@ -139,28 +168,33 @@ Error EditorImportPlugin::import(const String &p_source_file, const String &p_sa
options[E->key()] = E->get();
E = E->next();
}
- Error err = (Error)get_script_instance()->call("import", p_source_file, p_save_path, options, platform_variants, gen_files).operator int64_t();
- for (int i = 0; i < platform_variants.size(); i++) {
- r_platform_variants->push_back(platform_variants[i]);
- }
- for (int i = 0; i < gen_files.size(); i++) {
- r_gen_files->push_back(gen_files[i]);
+ int err;
+ if (GDVIRTUAL_CALL(_import, p_source_file, p_save_path, options, platform_variants, gen_files, err)) {
+ Error ret_err = Error(err);
+
+ for (int i = 0; i < platform_variants.size(); i++) {
+ r_platform_variants->push_back(platform_variants[i]);
+ }
+ for (int i = 0; i < gen_files.size(); i++) {
+ r_gen_files->push_back(gen_files[i]);
+ }
+ return ret_err;
}
- return err;
+ ERR_FAIL_V_MSG(ERR_METHOD_NOT_FOUND, "Unimplemented _import in add-on.");
}
void EditorImportPlugin::_bind_methods() {
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_importer_name"));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_visible_name"));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::INT, "get_preset_count"));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_preset_name", PropertyInfo(Variant::INT, "preset")));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::ARRAY, "get_recognized_extensions"));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::ARRAY, "get_import_options", PropertyInfo(Variant::INT, "preset")));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_save_extension"));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::STRING, "get_resource_type"));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::FLOAT, "get_priority"));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::INT, "get_import_order"));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "get_option_visibility", PropertyInfo(Variant::STRING, "option"), PropertyInfo(Variant::DICTIONARY, "options")));
- ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::INT, "import", PropertyInfo(Variant::STRING, "source_file"), PropertyInfo(Variant::STRING, "save_path"), PropertyInfo(Variant::DICTIONARY, "options"), PropertyInfo(Variant::ARRAY, "platform_variants"), PropertyInfo(Variant::ARRAY, "gen_files")));
+ GDVIRTUAL_BIND(_get_importer_name)
+ GDVIRTUAL_BIND(_get_visible_name)
+ GDVIRTUAL_BIND(_get_preset_count)
+ GDVIRTUAL_BIND(_get_preset_name, "preset_index")
+ GDVIRTUAL_BIND(_get_recognized_extensions)
+ GDVIRTUAL_BIND(_get_import_options, "preset_index")
+ GDVIRTUAL_BIND(_get_save_extension)
+ GDVIRTUAL_BIND(_get_resource_type)
+ GDVIRTUAL_BIND(_get_priority)
+ GDVIRTUAL_BIND(_get_import_order)
+ GDVIRTUAL_BIND(_get_option_visibility, "option_name", "options")
+ GDVIRTUAL_BIND(_import, "source_file", "save_path", "options", "platform_variants", "gen_files");
}
diff --git a/editor/import/editor_import_plugin.h b/editor/import/editor_import_plugin.h
index 345a40e96d..49c959ab44 100644
--- a/editor/import/editor_import_plugin.h
+++ b/editor/import/editor_import_plugin.h
@@ -39,6 +39,19 @@ class EditorImportPlugin : public ResourceImporter {
protected:
static void _bind_methods();
+ GDVIRTUAL0RC(String, _get_importer_name)
+ GDVIRTUAL0RC(String, _get_visible_name)
+ GDVIRTUAL0RC(int, _get_preset_count)
+ GDVIRTUAL1RC(String, _get_preset_name, int)
+ GDVIRTUAL0RC(Vector<String>, _get_recognized_extensions)
+ GDVIRTUAL1RC(Array, _get_import_options, int)
+ GDVIRTUAL0RC(String, _get_save_extension)
+ GDVIRTUAL0RC(String, _get_resource_type)
+ GDVIRTUAL0RC(float, _get_priority)
+ GDVIRTUAL0RC(int, _get_import_order)
+ GDVIRTUAL2RC(bool, _get_option_visibility, StringName, Dictionary)
+ GDVIRTUAL5RC(int, _import, String, String, Dictionary, Array, Array)
+
public:
EditorImportPlugin();
virtual String get_importer_name() const override;
diff --git a/editor/import/editor_importer_bake_reset.cpp b/editor/import/editor_importer_bake_reset.cpp
new file mode 100644
index 0000000000..00dce6850e
--- /dev/null
+++ b/editor/import/editor_importer_bake_reset.cpp
@@ -0,0 +1,234 @@
+/*************************************************************************/
+/* editor_importer_bake_reset.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "editor/import/editor_importer_bake_reset.h"
+
+#include "core/error/error_list.h"
+#include "core/error/error_macros.h"
+#include "core/math/transform_3d.h"
+#include "editor/import/scene_importer_mesh_node_3d.h"
+#include "resource_importer_scene.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/node_3d.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/animation/animation_player.h"
+
+// Given that an engineering team has made a reference character, one wants ten animators to create animations.
+// Currently, a tech artist needs to combine the ten files into one exported gltf2 to import into Godot Engine.
+// We bake the RESET animation and then set it to identity,
+// so that rigs with corresponding RESET animation can have their animations transferred with ease.
+//
+// The original algorithm for the code was used to change skeleton bone rolls to be parent to child.
+//
+// Reference https://github.com/godotengine/godot-proposals/issues/2961
+void BakeReset::_bake_animation_pose(Node *scene, const String &p_bake_anim) {
+ Map<StringName, BakeResetRestBone> r_rest_bones;
+ Vector<Node3D *> r_meshes;
+ List<Node *> queue;
+ queue.push_back(scene);
+ while (!queue.is_empty()) {
+ List<Node *>::Element *E = queue.front();
+ Node *node = E->get();
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(node);
+ // Step 1: import scene with animations into the rest bones data structure.
+ _fetch_reset_animation(ap, r_rest_bones, p_bake_anim);
+
+ int child_count = node->get_child_count();
+ for (int i = 0; i < child_count; i++) {
+ queue.push_back(node->get_child(i));
+ }
+ queue.pop_front();
+ }
+
+ queue.push_back(scene);
+ while (!queue.is_empty()) {
+ List<Node *>::Element *E = queue.front();
+ Node *node = E->get();
+ EditorSceneImporterMeshNode3D *editor_mesh_3d = scene->cast_to<EditorSceneImporterMeshNode3D>(node);
+ MeshInstance3D *mesh_3d = scene->cast_to<MeshInstance3D>(node);
+ if (scene->cast_to<Skeleton3D>(node)) {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node);
+
+ // Step 2: Bake the RESET animation from the RestBone to the skeleton.
+ _fix_skeleton(skeleton, r_rest_bones);
+ }
+ if (editor_mesh_3d) {
+ NodePath path = editor_mesh_3d->get_skeleton_path();
+ if (!path.is_empty() && editor_mesh_3d->get_node_or_null(path) && Object::cast_to<Skeleton3D>(editor_mesh_3d->get_node_or_null(path))) {
+ r_meshes.push_back(editor_mesh_3d);
+ }
+ } else if (mesh_3d) {
+ NodePath path = mesh_3d->get_skeleton_path();
+ if (!path.is_empty() && mesh_3d->get_node_or_null(path) && Object::cast_to<Skeleton3D>(mesh_3d->get_node_or_null(path))) {
+ r_meshes.push_back(mesh_3d);
+ }
+ }
+ int child_count = node->get_child_count();
+ for (int i = 0; i < child_count; i++) {
+ queue.push_back(node->get_child(i));
+ }
+ queue.pop_front();
+ }
+
+ queue.push_back(scene);
+ while (!queue.is_empty()) {
+ List<Node *>::Element *E = queue.front();
+ Node *node = E->get();
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(node);
+ if (ap) {
+ // Step 3: Key all RESET animation frames to identity.
+ _align_animations(ap, r_rest_bones);
+ }
+
+ int child_count = node->get_child_count();
+ for (int i = 0; i < child_count; i++) {
+ queue.push_back(node->get_child(i));
+ }
+ queue.pop_front();
+ }
+}
+
+void BakeReset::_align_animations(AnimationPlayer *p_ap, const Map<StringName, BakeResetRestBone> &r_rest_bones) {
+ ERR_FAIL_NULL(p_ap);
+ List<StringName> anim_names;
+ p_ap->get_animation_list(&anim_names);
+ 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()));
+ 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();
+ 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;
+ Quaternion rot;
+ Vector3 scale;
+ Error err = a->transform_track_get_key(track, key_i, &loc, &rot, &scale);
+ ERR_CONTINUE(err);
+ real_t time = a->track_get_key_time(track, key_i);
+ rot.normalize();
+ loc = loc - rest_bone.loc;
+ rot = rest_bone.rest_delta.get_rotation_quaternion().inverse() * rot;
+ rot.normalize();
+ scale = Vector3(1, 1, 1) - (rest_bone.rest_delta.get_scale() - scale);
+ // Apply the reverse of the rest changes to make the key be close to identity transform.
+ a->transform_track_insert_key(new_track, time, loc, rot, scale);
+ }
+ a->remove_track(track);
+ }
+ }
+}
+
+void BakeReset::_fetch_reset_animation(AnimationPlayer *p_ap, Map<StringName, BakeResetRestBone> &r_rest_bones, const String &p_bake_anim) {
+ if (!p_ap) {
+ return;
+ }
+ List<StringName> anim_names;
+ p_ap->get_animation_list(&anim_names);
+ Node *root = p_ap->get_owner();
+ ERR_FAIL_NULL(root);
+ if (!p_ap->has_animation(p_bake_anim)) {
+ return;
+ }
+ Ref<Animation> a = p_ap->get_animation(p_bake_anim);
+ if (a.is_null()) {
+ return;
+ }
+ for (int32_t track = 0; track < a->get_track_count(); track++) {
+ NodePath path = a->track_get_path(track);
+ String string_path = path;
+ Skeleton3D *skeleton = root->cast_to<Skeleton3D>(root->get_node(string_path.get_slice(":", 0)));
+ if (!skeleton) {
+ continue;
+ }
+ String bone_name = string_path.get_slice(":", 1);
+ for (int key_i = 0; key_i < a->track_get_key_count(track); key_i++) {
+ Vector3 loc;
+ Quaternion rot;
+ Vector3 scale;
+ Error err = a->transform_track_get_key(track, key_i, &loc, &rot, &scale);
+ if (err != OK) {
+ ERR_PRINT_ONCE("Reset animation baker can't get key.");
+ continue;
+ }
+ rot.normalize();
+ Basis rot_basis = Basis(rot, scale);
+ BakeResetRestBone rest_bone;
+ rest_bone.rest_delta = rot_basis;
+ rest_bone.loc = loc;
+ // Store the animation into the RestBone.
+ r_rest_bones[StringName(String(skeleton->get_owner()->get_path_to(skeleton)) + ":" + bone_name)] = rest_bone;
+ break;
+ }
+ }
+}
+
+void BakeReset::_fix_skeleton(Skeleton3D *p_skeleton, Map<StringName, BakeReset::BakeResetRestBone> &r_rest_bones) {
+ int bone_count = p_skeleton->get_bone_count();
+
+ // First iterate through all the bones and update the RestBone.
+ for (int j = 0; j < bone_count; j++) {
+ StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(j);
+ BakeResetRestBone &rest_bone = r_rest_bones[final_path];
+ rest_bone.rest_local = p_skeleton->get_bone_rest(j);
+ }
+ for (int i = 0; i < bone_count; i++) {
+ int parent_bone = p_skeleton->get_bone_parent(i);
+ String path = p_skeleton->get_owner()->get_path_to(p_skeleton);
+ StringName final_path = String(path) + String(":") + p_skeleton->get_bone_name(parent_bone);
+ if (parent_bone >= 0) {
+ r_rest_bones[path].children.push_back(i);
+ }
+ }
+
+ // When we apply transform to a bone, we also have to move all of its children in the opposite direction.
+ for (int i = 0; i < bone_count; i++) {
+ StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i);
+ r_rest_bones[final_path].rest_local = r_rest_bones[final_path].rest_local * Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc);
+ // Iterate through the children and move in the opposite direction.
+ for (int j = 0; j < r_rest_bones[final_path].children.size(); j++) {
+ int child_index = r_rest_bones[final_path].children[j];
+ StringName children_path = String(p_skeleton->get_name()) + String(":") + p_skeleton->get_bone_name(child_index);
+ r_rest_bones[children_path].rest_local = Transform3D(r_rest_bones[final_path].rest_delta, r_rest_bones[final_path].loc).affine_inverse() * r_rest_bones[children_path].rest_local;
+ }
+ }
+
+ for (int i = 0; i < bone_count; i++) {
+ StringName final_path = String(p_skeleton->get_owner()->get_path_to(p_skeleton)) + String(":") + p_skeleton->get_bone_name(i);
+ ERR_CONTINUE(!r_rest_bones.has(final_path));
+ Transform3D rest_transform = r_rest_bones[final_path].rest_local;
+ p_skeleton->set_bone_rest(i, rest_transform);
+ }
+}
diff --git a/editor/import/editor_importer_bake_reset.h b/editor/import/editor_importer_bake_reset.h
new file mode 100644
index 0000000000..e36ae86181
--- /dev/null
+++ b/editor/import/editor_importer_bake_reset.h
@@ -0,0 +1,54 @@
+/*************************************************************************/
+/* editor_importer_bake_reset.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RESOURCE_IMPORTER_BAKE_RESET_H
+#define RESOURCE_IMPORTER_BAKE_RESET_H
+
+#include "scene/main/node.h"
+
+class Skeleton3D;
+class AnimationPlayer;
+class BakeReset {
+ struct BakeResetRestBone {
+ Transform3D rest_local;
+ Basis rest_delta;
+ Vector3 loc;
+ Vector<int> children;
+ };
+
+public:
+ void _bake_animation_pose(Node *scene, const String &p_bake_anim);
+
+private:
+ void _fix_skeleton(Skeleton3D *p_skeleton, Map<StringName, BakeReset::BakeResetRestBone> &r_rest_bones);
+ void _align_animations(AnimationPlayer *p_ap, const Map<StringName, BakeResetRestBone> &r_rest_bones);
+ void _fetch_reset_animation(AnimationPlayer *p_ap, Map<StringName, BakeResetRestBone> &r_rest_bones, const String &p_bake_anim);
+};
+#endif
diff --git a/editor/import/resource_importer_bitmask.cpp b/editor/import/resource_importer_bitmask.cpp
index ffef759c07..7fd9230284 100644
--- a/editor/import/resource_importer_bitmask.cpp
+++ b/editor/import/resource_importer_bitmask.cpp
@@ -78,7 +78,7 @@ Error ResourceImporterBitMap::import(const String &p_source_file, const String &
int create_from = p_options["create_from"];
float threshold = p_options["threshold"];
Ref<Image> image;
- image.instance();
+ image.instantiate();
Error err = ImageLoader::load_image(p_source_file, image);
if (err != OK) {
return err;
@@ -88,7 +88,7 @@ Error ResourceImporterBitMap::import(const String &p_source_file, const String &
int h = image->get_height();
Ref<BitMap> bitmap;
- bitmap.instance();
+ bitmap.instantiate();
bitmap->create(Size2(w, h));
for (int i = 0; i < h; i++) {
diff --git a/editor/import/resource_importer_bmfont.cpp b/editor/import/resource_importer_bmfont.cpp
new file mode 100644
index 0000000000..2e7ef1402b
--- /dev/null
+++ b/editor/import/resource_importer_bmfont.cpp
@@ -0,0 +1,797 @@
+/*************************************************************************/
+/* resource_importer_bmfont.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "resource_importer_bmfont.h"
+
+#include "core/io/image_loader.h"
+#include "core/io/resource_saver.h"
+
+String ResourceImporterBMFont::get_importer_name() const {
+ return "font_data_bmfont";
+}
+
+String ResourceImporterBMFont::get_visible_name() const {
+ return "Font Data (AngelCode BMFont)";
+}
+
+void ResourceImporterBMFont::get_recognized_extensions(List<String> *p_extensions) const {
+ if (p_extensions) {
+ p_extensions->push_back("font");
+ p_extensions->push_back("fnt");
+ }
+}
+
+String ResourceImporterBMFont::get_save_extension() const {
+ return "fontdata";
+}
+
+String ResourceImporterBMFont::get_resource_type() const {
+ return "FontData";
+}
+
+bool ResourceImporterBMFont::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
+ return true;
+}
+
+void ResourceImporterBMFont::get_import_options(List<ImportOption> *r_options, int p_preset) const {
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
+}
+
+void _convert_packed_8bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_sz) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ PackedByteArray imgdata_r;
+ imgdata_r.resize(w * h * 2);
+ uint8_t *wr = imgdata_r.ptrw();
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_b;
+ imgdata_b.resize(w * h * 2);
+ uint8_t *wb = imgdata_b.ptrw();
+
+ PackedByteArray imgdata_a;
+ imgdata_a.resize(w * h * 2);
+ uint8_t *wa = imgdata_a.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * 4;
+ int ofs_dst = (i * w + j) * 2;
+ wr[ofs_dst + 0] = 255;
+ wr[ofs_dst + 1] = r[ofs_src + 0];
+ wg[ofs_dst + 0] = 255;
+ wg[ofs_dst + 1] = r[ofs_src + 1];
+ wb[ofs_dst + 0] = 255;
+ wb[ofs_dst + 1] = r[ofs_src + 2];
+ wa[ofs_dst + 0] = 255;
+ wa[ofs_dst + 1] = r[ofs_src + 3];
+ }
+ }
+ Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
+ Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
+ Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
+}
+
+void _convert_packed_4bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_sz) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ PackedByteArray imgdata_r;
+ imgdata_r.resize(w * h * 2);
+ uint8_t *wr = imgdata_r.ptrw();
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_b;
+ imgdata_b.resize(w * h * 2);
+ uint8_t *wb = imgdata_b.ptrw();
+
+ PackedByteArray imgdata_a;
+ imgdata_a.resize(w * h * 2);
+ uint8_t *wa = imgdata_a.ptrw();
+
+ PackedByteArray imgdata_ro;
+ imgdata_ro.resize(w * h * 2);
+ uint8_t *wro = imgdata_ro.ptrw();
+
+ PackedByteArray imgdata_go;
+ imgdata_go.resize(w * h * 2);
+ uint8_t *wgo = imgdata_go.ptrw();
+
+ PackedByteArray imgdata_bo;
+ imgdata_bo.resize(w * h * 2);
+ uint8_t *wbo = imgdata_bo.ptrw();
+
+ PackedByteArray imgdata_ao;
+ imgdata_ao.resize(w * h * 2);
+ uint8_t *wao = imgdata_ao.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * 4;
+ int ofs_dst = (i * w + j) * 2;
+ wr[ofs_dst + 0] = 255;
+ wro[ofs_dst + 0] = 255;
+ if (r[ofs_src + 0] > 0x0F) {
+ wr[ofs_dst + 1] = (r[ofs_src + 0] - 0x0F) * 2;
+ wro[ofs_dst + 1] = 0;
+ } else {
+ wr[ofs_dst + 1] = 0;
+ wro[ofs_dst + 1] = r[ofs_src + 0] * 2;
+ }
+ wg[ofs_dst + 0] = 255;
+ wgo[ofs_dst + 0] = 255;
+ if (r[ofs_src + 1] > 0x0F) {
+ wg[ofs_dst + 1] = (r[ofs_src + 1] - 0x0F) * 2;
+ wgo[ofs_dst + 1] = 0;
+ } else {
+ wg[ofs_dst + 1] = 0;
+ wgo[ofs_dst + 1] = r[ofs_src + 1] * 2;
+ }
+ wb[ofs_dst + 0] = 255;
+ wbo[ofs_dst + 0] = 255;
+ if (r[ofs_src + 2] > 0x0F) {
+ wb[ofs_dst + 1] = (r[ofs_src + 2] - 0x0F) * 2;
+ wbo[ofs_dst + 1] = 0;
+ } else {
+ wb[ofs_dst + 1] = 0;
+ wbo[ofs_dst + 1] = r[ofs_src + 2] * 2;
+ }
+ wa[ofs_dst + 0] = 255;
+ wao[ofs_dst + 0] = 255;
+ if (r[ofs_src + 3] > 0x0F) {
+ wa[ofs_dst + 1] = (r[ofs_src + 3] - 0x0F) * 2;
+ wao[ofs_dst + 1] = 0;
+ } else {
+ wa[ofs_dst + 1] = 0;
+ wao[ofs_dst + 1] = r[ofs_src + 3] * 2;
+ }
+ }
+ }
+ Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
+ Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
+ Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
+
+ Ref<Image> img_ro = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ro));
+ r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 0, img_ro);
+ Ref<Image> img_go = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_go));
+ r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 1, img_go);
+ Ref<Image> img_bo = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_bo));
+ r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 2, img_bo);
+ Ref<Image> img_ao = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ao));
+ r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 3, img_ao);
+}
+
+void _convert_rgba_4bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_sz) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 4);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_o;
+ imgdata_o.resize(w * h * 4);
+ uint8_t *wo = imgdata_o.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = (i * w + j) * 4;
+
+ if (r[ofs + 0] > 0x7F) {
+ wg[ofs + 0] = r[ofs + 0];
+ wo[ofs + 0] = 0;
+ } else {
+ wg[ofs + 0] = 0;
+ wo[ofs + 0] = r[ofs + 0] * 2;
+ }
+ if (r[ofs + 1] > 0x7F) {
+ wg[ofs + 1] = r[ofs + 1];
+ wo[ofs + 1] = 0;
+ } else {
+ wg[ofs + 1] = 0;
+ wo[ofs + 1] = r[ofs + 1] * 2;
+ }
+ if (r[ofs + 2] > 0x7F) {
+ wg[ofs + 2] = r[ofs + 2];
+ wo[ofs + 2] = 0;
+ } else {
+ wg[ofs + 2] = 0;
+ wo[ofs + 2] = r[ofs + 2] * 2;
+ }
+ if (r[ofs + 3] > 0x7F) {
+ wg[ofs + 3] = r[ofs + 3];
+ wo[ofs + 3] = 0;
+ } else {
+ wg[ofs + 3] = 0;
+ wo[ofs + 3] = r[ofs + 3] * 2;
+ }
+ }
+ }
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_g));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
+
+ Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_o));
+ r_font->set_texture_image(0, Vector2i(p_sz, 1), p_page, img_o);
+}
+
+void _convert_mono_8bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ int size = 4;
+ if (p_source->get_format() == Image::FORMAT_L8) {
+ size = 1;
+ p_ch = 0;
+ }
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * size;
+ int ofs_dst = (i * w + j) * 2;
+ wg[ofs_dst + 0] = 255;
+ wg[ofs_dst + 1] = r[ofs_src + p_ch];
+ }
+ }
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ r_font->set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_g);
+}
+
+void _convert_mono_4bit(Ref<FontData> &r_font, Ref<Image> &p_source, int p_page, int p_ch, int p_sz, int p_ol) {
+ int w = p_source->get_width();
+ int h = p_source->get_height();
+
+ PackedByteArray imgdata = p_source->get_data();
+ const uint8_t *r = imgdata.ptr();
+
+ int size = 4;
+ if (p_source->get_format() == Image::FORMAT_L8) {
+ size = 1;
+ p_ch = 0;
+ }
+
+ PackedByteArray imgdata_g;
+ imgdata_g.resize(w * h * 2);
+ uint8_t *wg = imgdata_g.ptrw();
+
+ PackedByteArray imgdata_o;
+ imgdata_o.resize(w * h * 2);
+ uint8_t *wo = imgdata_o.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs_src = (i * w + j) * size;
+ int ofs_dst = (i * w + j) * 2;
+ wg[ofs_dst + 0] = 255;
+ wo[ofs_dst + 0] = 255;
+ if (r[ofs_src + p_ch] > 0x7F) {
+ wg[ofs_dst + 1] = r[ofs_src + p_ch];
+ wo[ofs_dst + 1] = 0;
+ } else {
+ wg[ofs_dst + 1] = 0;
+ wo[ofs_dst + 1] = r[ofs_src + p_ch] * 2;
+ }
+ }
+ }
+ Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ r_font->set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
+
+ Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_o));
+ r_font->set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_o);
+}
+
+Error ResourceImporterBMFont::import(const String &p_source_file, const String &p_save_path, const Map<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);
+
+ Ref<FontData> font;
+ font.instantiate();
+ font->set_antialiased(false);
+ font->set_multichannel_signed_distance_field(false);
+ font->set_force_autohinter(false);
+ font->set_hinting(TextServer::HINTING_NONE);
+ font->set_oversampling(1.0f);
+
+ FileAccessRef f = FileAccess::open(p_source_file, FileAccess::READ);
+ if (f == nullptr) {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Cannot open font from file ") + "\"" + p_source_file + "\".");
+ }
+
+ int base_size = 16;
+ int height = 0;
+ int ascent = 0;
+ int outline = 0;
+
+ bool packed = false;
+ uint8_t ch[4] = { 0, 0, 0, 0 }; // RGBA
+ int first_gl_ch = -1;
+ int first_ol_ch = -1;
+ int first_cm_ch = -1;
+
+ unsigned char magic[4];
+ f->get_buffer((unsigned char *)&magic, 4);
+ if (magic[0] == 'B' && magic[1] == 'M' && magic[2] == 'F') {
+ // Binary BMFont file.
+ ERR_FAIL_COND_V_MSG(magic[3] != 3, ERR_CANT_CREATE, vformat(TTR("Version %d of BMFont is not supported."), (int)magic[3]));
+
+ uint8_t block_type = f->get_8();
+ uint32_t block_size = f->get_32();
+ while (!f->eof_reached()) {
+ uint64_t off = f->get_position();
+ switch (block_type) {
+ case 1: /* info */ {
+ ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, TTR("Invalid BMFont info block size."));
+ base_size = f->get_16();
+ uint8_t flags = f->get_8();
+ ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
+ f->get_8(); // non-unicode charset, skip
+ f->get_16(); // stretch_h, skip
+ f->get_8(); // aa, skip
+ f->get_32(); // padding, skip
+ f->get_16(); // spacing, skip
+ outline = f->get_8();
+ // font name, skip
+ font->set_fixed_size(base_size);
+ } break;
+ case 2: /* common */ {
+ ERR_FAIL_COND_V_MSG(block_size != 15, ERR_CANT_CREATE, TTR("Invalid BMFont common block size."));
+ height = f->get_16();
+ ascent = f->get_16();
+ f->get_32(); // scale, skip
+ f->get_16(); // pages, skip
+ uint8_t flags = f->get_8();
+ packed = (flags & 0x01);
+ ch[3] = f->get_8();
+ ch[0] = f->get_8();
+ ch[1] = f->get_8();
+ ch[2] = f->get_8();
+ for (int i = 0; i < 4; i++) {
+ if (ch[i] == 0 && first_gl_ch == -1) {
+ first_gl_ch = i;
+ }
+ if (ch[i] == 1 && first_ol_ch == -1) {
+ first_ol_ch = i;
+ }
+ if (ch[i] == 2 && first_cm_ch == -1) {
+ first_cm_ch = i;
+ }
+ }
+ } break;
+ case 3: /* pages */ {
+ int page = 0;
+ CharString cs;
+ char32_t c = f->get_8();
+ while (!f->eof_reached() && f->get_position() <= off + block_size) {
+ if (c == '\0') {
+ String base_dir = p_source_file.get_base_dir();
+ String file = base_dir.plus_file(String::utf8(cs.ptr(), cs.length()));
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img;
+ img.instantiate();
+ Error err = ImageLoader::load_image(file, img);
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + file + "\".");
+
+ if (packed) {
+ if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_packed_8bit(font, img, page, base_size);
+ } else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_packed_4bit(font, img, page, base_size);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ }
+ } else {
+ if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ font->set_texture_image(0, Vector2i(base_size, 0), page, img);
+ } else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_rgba_4bit(font, img, page, base_size);
+ } else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(font, img, page, first_gl_ch, base_size, 0);
+ _convert_mono_8bit(font, img, page, first_ol_ch, base_size, 1);
+ } else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_4bit(font, img, page, first_cm_ch, base_size, 1);
+ } else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(font, img, page, first_gl_ch, base_size, 0);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ }
+ }
+ }
+ page++;
+ cs = "";
+ } else {
+ cs += c;
+ }
+ c = f->get_8();
+ }
+ } break;
+ case 4: /* chars */ {
+ int char_count = block_size / 20;
+ for (int i = 0; i < char_count; i++) {
+ Vector2 advance;
+ Vector2 size;
+ Vector2 offset;
+ Rect2 uv_rect;
+
+ char32_t idx = f->get_32();
+ uv_rect.position.x = (int16_t)f->get_16();
+ uv_rect.position.y = (int16_t)f->get_16();
+ uv_rect.size.width = (int16_t)f->get_16();
+ size.width = uv_rect.size.width;
+ uv_rect.size.height = (int16_t)f->get_16();
+ size.height = uv_rect.size.height;
+ offset.x = (int16_t)f->get_16();
+ offset.y = (int16_t)f->get_16() - ascent;
+ advance.x = (int16_t)f->get_16();
+ if (advance.x < 0) {
+ advance.x = size.width + 1;
+ }
+
+ int texture_idx = f->get_8();
+ uint8_t channel = f->get_8();
+
+ ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, TTR("Invalid glyph channel."));
+ int ch_off = 0;
+ switch (channel) {
+ case 1:
+ ch_off = 2;
+ break; // B
+ case 2:
+ ch_off = 1;
+ break; // G
+ case 4:
+ ch_off = 0;
+ break; // R
+ case 8:
+ ch_off = 3;
+ break; // A
+ default:
+ ch_off = 0;
+ break;
+ }
+ font->set_glyph_advance(0, base_size, idx, advance);
+ font->set_glyph_offset(0, Vector2i(base_size, 0), idx, offset);
+ font->set_glyph_size(0, Vector2i(base_size, 0), idx, size);
+ font->set_glyph_uv_rect(0, Vector2i(base_size, 0), idx, uv_rect);
+ font->set_glyph_texture_idx(0, Vector2i(base_size, 0), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ if (outline > 0) {
+ font->set_glyph_offset(0, Vector2i(base_size, 1), idx, offset);
+ font->set_glyph_size(0, Vector2i(base_size, 1), idx, size);
+ font->set_glyph_uv_rect(0, Vector2i(base_size, 1), idx, uv_rect);
+ font->set_glyph_texture_idx(0, Vector2i(base_size, 1), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ }
+ }
+ } break;
+ case 5: /* kerning */ {
+ int pair_count = block_size / 10;
+ for (int i = 0; i < pair_count; i++) {
+ Vector2i kpk;
+ kpk.x = f->get_32();
+ kpk.y = f->get_32();
+ font->set_kerning(0, base_size, kpk, Vector2((int16_t)f->get_16(), 0));
+ }
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Invalid BMFont block type."));
+ } break;
+ }
+ f->seek(off + block_size);
+ block_type = f->get_8();
+ block_size = f->get_32();
+ }
+
+ } else {
+ // Text BMFont file.
+ f->seek(0);
+ while (true) {
+ String line = f->get_line();
+
+ int delimiter = line.find(" ");
+ String type = line.substr(0, delimiter);
+ int pos = delimiter + 1;
+ Map<String, String> keys;
+
+ while (pos < line.size() && line[pos] == ' ') {
+ pos++;
+ }
+
+ while (pos < line.size()) {
+ int eq = line.find("=", pos);
+ if (eq == -1) {
+ break;
+ }
+ String key = line.substr(pos, eq - pos);
+ int end = -1;
+ String value;
+ if (line[eq + 1] == '"') {
+ end = line.find("\"", eq + 2);
+ if (end == -1) {
+ break;
+ }
+ value = line.substr(eq + 2, end - 1 - eq - 1);
+ pos = end + 1;
+ } else {
+ end = line.find(" ", eq + 1);
+ if (end == -1) {
+ end = line.size();
+ }
+ value = line.substr(eq + 1, end - eq);
+ pos = end;
+ }
+
+ while (pos < line.size() && line[pos] == ' ') {
+ pos++;
+ }
+
+ keys[key] = value;
+ }
+
+ if (type == "info") {
+ if (keys.has("size")) {
+ base_size = keys["size"].to_int();
+ font->set_fixed_size(base_size);
+ }
+ if (keys.has("outline")) {
+ outline = keys["outline"].to_int();
+ }
+ ERR_FAIL_COND_V_MSG((!keys.has("unicode") || keys["unicode"].to_int() != 1), ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
+ } else if (type == "common") {
+ if (keys.has("lineHeight")) {
+ height = keys["lineHeight"].to_int();
+ }
+ if (keys.has("base")) {
+ ascent = keys["base"].to_int();
+ }
+ if (keys.has("packed")) {
+ packed = (keys["packed"].to_int() == 1);
+ }
+ if (keys.has("alphaChnl")) {
+ ch[3] = keys["alphaChnl"].to_int();
+ }
+ if (keys.has("redChnl")) {
+ ch[0] = keys["redChnl"].to_int();
+ }
+ if (keys.has("greenChnl")) {
+ ch[1] = keys["greenChnl"].to_int();
+ }
+ if (keys.has("blueChnl")) {
+ ch[2] = keys["blueChnl"].to_int();
+ }
+ for (int i = 0; i < 4; i++) {
+ if (ch[i] == 0 && first_gl_ch == -1) {
+ first_gl_ch = i;
+ }
+ if (ch[i] == 1 && first_ol_ch == -1) {
+ first_ol_ch = i;
+ }
+ if (ch[i] == 2 && first_cm_ch == -1) {
+ first_cm_ch = i;
+ }
+ }
+ } else if (type == "page") {
+ int page = 0;
+ if (keys.has("id")) {
+ page = keys["id"].to_int();
+ }
+ if (keys.has("file")) {
+ String base_dir = p_source_file.get_base_dir();
+ String file = base_dir.plus_file(keys["file"]);
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img;
+ img.instantiate();
+ Error err = ImageLoader::load_image(file, img);
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + file + "\".");
+ if (packed) {
+ if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_packed_8bit(font, img, page, base_size);
+ } else if ((ch[3] == 2) && (outline > 0)) { // 4 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_packed_4bit(font, img, page, base_size);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ }
+ } else {
+ if ((ch[0] == 0) && (ch[1] == 0) && (ch[2] == 0) && (ch[3] == 0)) { // RGBA8 color, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ font->set_texture_image(0, Vector2i(base_size, 0), page, img);
+ } else if ((ch[0] == 2) && (ch[1] == 2) && (ch[2] == 2) && (ch[3] == 2) && (outline > 0)) { // RGBA4 color, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_rgba_4bit(font, img, page, base_size);
+ } else if ((first_gl_ch >= 0) && (first_ol_ch >= 0) && (outline > 0)) { // 1 x 8 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(font, img, page, first_gl_ch, base_size, 0);
+ _convert_mono_8bit(font, img, page, first_ol_ch, base_size, 1);
+ } else if ((first_cm_ch >= 0) && (outline > 0)) { // 1 x 4 bit monochrome, gl + outline
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_4bit(font, img, page, first_cm_ch, base_size, 1);
+ } else if (first_gl_ch >= 0) { // 1 x 8 bit monochrome, no outline
+ outline = 0;
+ ERR_FAIL_COND_V_MSG(img->get_format() != Image::FORMAT_RGBA8 && img->get_format() != Image::FORMAT_L8, ERR_FILE_CANT_READ, TTR("Unsupported BMFont texture format."));
+ _convert_mono_8bit(font, img, page, first_gl_ch, base_size, 0);
+ } else {
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, TTR("Unsupported BMFont texture format."));
+ }
+ }
+ }
+ }
+ } else if (type == "char") {
+ char32_t idx = 0;
+ Vector2 advance;
+ Vector2 size;
+ Vector2 offset;
+ Rect2 uv_rect;
+ int texture_idx = -1;
+ uint8_t channel = 15;
+
+ if (keys.has("id")) {
+ idx = keys["id"].to_int();
+ }
+ if (keys.has("x")) {
+ uv_rect.position.x = keys["x"].to_int();
+ }
+ if (keys.has("y")) {
+ uv_rect.position.y = keys["y"].to_int();
+ }
+ if (keys.has("width")) {
+ uv_rect.size.width = keys["width"].to_int();
+ size.width = keys["width"].to_int();
+ }
+ if (keys.has("height")) {
+ uv_rect.size.height = keys["height"].to_int();
+ size.height = keys["height"].to_int();
+ }
+ if (keys.has("xoffset")) {
+ offset.x = keys["xoffset"].to_int();
+ }
+ if (keys.has("yoffset")) {
+ offset.y = keys["yoffset"].to_int() - ascent;
+ }
+ if (keys.has("page")) {
+ texture_idx = keys["page"].to_int();
+ }
+ if (keys.has("xadvance")) {
+ advance.x = keys["xadvance"].to_int();
+ }
+ if (advance.x < 0) {
+ advance.x = size.width + 1;
+ }
+ if (keys.has("chnl")) {
+ channel = keys["chnl"].to_int();
+ }
+
+ ERR_FAIL_COND_V_MSG(!packed && channel != 15, ERR_CANT_CREATE, TTR("Invalid glyph channel."));
+ int ch_off = 0;
+ switch (channel) {
+ case 1:
+ ch_off = 2;
+ break; // B
+ case 2:
+ ch_off = 1;
+ break; // G
+ case 4:
+ ch_off = 0;
+ break; // R
+ case 8:
+ ch_off = 3;
+ break; // A
+ default:
+ ch_off = 0;
+ break;
+ }
+ font->set_glyph_advance(0, base_size, idx, advance);
+ font->set_glyph_offset(0, Vector2i(base_size, 0), idx, offset);
+ font->set_glyph_size(0, Vector2i(base_size, 0), idx, size);
+ font->set_glyph_uv_rect(0, Vector2i(base_size, 0), idx, uv_rect);
+ font->set_glyph_texture_idx(0, Vector2i(base_size, 0), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ if (outline > 0) {
+ font->set_glyph_offset(0, Vector2i(base_size, 1), idx, offset);
+ font->set_glyph_size(0, Vector2i(base_size, 1), idx, size);
+ font->set_glyph_uv_rect(0, Vector2i(base_size, 1), idx, uv_rect);
+ font->set_glyph_texture_idx(0, Vector2i(base_size, 1), idx, texture_idx * (packed ? 4 : 1) + ch_off);
+ }
+ } else if (type == "kerning") {
+ Vector2i kpk;
+ if (keys.has("first")) {
+ kpk.x = keys["first"].to_int();
+ }
+ if (keys.has("second")) {
+ kpk.y = keys["second"].to_int();
+ }
+ if (keys.has("amount")) {
+ font->set_kerning(0, base_size, kpk, Vector2(keys["amount"].to_int(), 0));
+ }
+ }
+
+ if (f->eof_reached()) {
+ break;
+ }
+ }
+ }
+
+ font->set_ascent(0, base_size, ascent);
+ font->set_descent(0, base_size, height - ascent);
+
+ int flg = ResourceSaver::SaverFlags::FLAG_BUNDLE_RESOURCES | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
+ if ((bool)p_options["compress"]) {
+ flg |= ResourceSaver::SaverFlags::FLAG_COMPRESS;
+ }
+
+ print_verbose("Saving to: " + p_save_path + ".fontdata");
+ Error err = ResourceSaver::save(p_save_path + ".fontdata", font, flg);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save font to file \"" + p_save_path + ".res\".");
+ print_verbose("Done saving to: " + p_save_path + ".fontdata");
+ return OK;
+}
+
+ResourceImporterBMFont::ResourceImporterBMFont() {
+}
diff --git a/editor/import/resource_importer_bmfont.h b/editor/import/resource_importer_bmfont.h
new file mode 100644
index 0000000000..065703132a
--- /dev/null
+++ b/editor/import/resource_importer_bmfont.h
@@ -0,0 +1,56 @@
+/*************************************************************************/
+/* resource_importer_bmfont.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RESOURCE_IMPORTER_BMFONT_H
+#define RESOURCE_IMPORTER_BMFONT_H
+
+#include "core/io/resource_importer.h"
+#include "scene/resources/font.h"
+#include "servers/text_server.h"
+
+class ResourceImporterBMFont : public ResourceImporter {
+ GDCLASS(ResourceImporterBMFont, ResourceImporter);
+
+public:
+ 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;
+ virtual String get_save_extension() const override;
+ virtual String get_resource_type() const override;
+
+ virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
+ virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
+
+ virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+
+ ResourceImporterBMFont();
+};
+
+#endif // RESOURCE_IMPORTER_BMFONT_H
diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp
index 4a4d9d8f06..07647d8b6a 100644
--- a/editor/import/resource_importer_csv_translation.cpp
+++ b/editor/import/resource_importer_csv_translation.cpp
@@ -30,8 +30,8 @@
#include "resource_importer_csv_translation.h"
+#include "core/io/file_access.h"
#include "core/io/resource_saver.h"
-#include "core/os/file_access.h"
#include "core/string/optimized_translation.h"
#include "core/string/translation.h"
@@ -104,7 +104,7 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const
locales.push_back(locale);
Ref<Translation> translation;
- translation.instance();
+ translation.instantiate();
translation->set_locale(locale);
translations.push_back(translation);
}
diff --git a/editor/import/resource_importer_dynamicfont.cpp b/editor/import/resource_importer_dynamicfont.cpp
new file mode 100644
index 0000000000..8e01adbd56
--- /dev/null
+++ b/editor/import/resource_importer_dynamicfont.cpp
@@ -0,0 +1,304 @@
+/*************************************************************************/
+/* resource_importer_dynamicfont.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "resource_importer_dynamicfont.h"
+
+#include "dynamicfont_import_settings.h"
+
+#include "core/io/file_access.h"
+#include "core/io/resource_saver.h"
+#include "editor/editor_node.h"
+#include "modules/modules_enabled.gen.h"
+
+String ResourceImporterDynamicFont::get_importer_name() const {
+ return "font_data_dynamic";
+}
+
+String ResourceImporterDynamicFont::get_visible_name() const {
+ return "Font Data (Dynamic Font)";
+}
+
+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("otf");
+ p_extensions->push_back("woff");
+ //p_extensions->push_back("woff2");
+ p_extensions->push_back("pfb");
+ p_extensions->push_back("pfm");
+#endif
+ }
+}
+
+String ResourceImporterDynamicFont::get_save_extension() const {
+ return "fontdata";
+}
+
+String ResourceImporterDynamicFont::get_resource_type() const {
+ return "FontData";
+}
+
+bool ResourceImporterDynamicFont::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
+ if (p_option == "msdf_pixel_range" && !bool(p_options["multichannel_signed_distance_field"])) {
+ return false;
+ }
+ if (p_option == "msdf_size" && !bool(p_options["multichannel_signed_distance_field"])) {
+ return false;
+ }
+ if (p_option == "oversampling" && bool(p_options["multichannel_signed_distance_field"])) {
+ return false;
+ }
+ return true;
+}
+
+int ResourceImporterDynamicFont::get_preset_count() const {
+ return PRESET_MAX;
+}
+
+String ResourceImporterDynamicFont::get_preset_name(int p_idx) const {
+ switch (p_idx) {
+ case PRESET_DYNAMIC:
+ return TTR("Dynamically rendered TrueType/OpenType font");
+ case PRESET_MSDF:
+ return TTR("Prerendered multichannel(+true) signed distance field");
+ default:
+ return String();
+ }
+}
+
+void ResourceImporterDynamicFont::get_import_options(List<ImportOption> *r_options, int p_preset) const {
+ bool msdf = p_preset == PRESET_MSDF;
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "antialiased"), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), (msdf) ? true : false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), 8));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_RANGE, "1,250,1"), 48));
+
+ 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::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "preload/char_ranges"), Vector<String>()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "preload/glyph_ranges"), Vector<String>()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "preload/configurations"), Vector<String>()));
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "support_overrides/language_enabled"), Vector<String>()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "support_overrides/language_disabled"), Vector<String>()));
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "support_overrides/script_enabled"), Vector<String>()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "support_overrides/script_disabled"), Vector<String>()));
+}
+
+bool ResourceImporterDynamicFont::_decode_variation(const String &p_token, Dictionary &r_variations, Vector2i &r_size, String &r_name, Vector2i &r_spacing) {
+ Vector<String> tokens = p_token.split("=");
+ if (tokens.size() == 2) {
+ if (tokens[0] == "name") {
+ r_name = tokens[1];
+ } else if (tokens[0] == "size") {
+ r_size.x = tokens[1].to_int();
+ } else if (tokens[0] == "outline_size") {
+ r_size.y = tokens[1].to_int();
+ } else if (tokens[0] == "spacing_space") {
+ r_spacing.x = tokens[1].to_int();
+ } else if (tokens[0] == "spacing_glyph") {
+ r_spacing.y = tokens[1].to_int();
+ } else {
+ r_variations[tokens[0]] = tokens[1].to_float();
+ }
+ return true;
+ } else {
+ WARN_PRINT("Invalid variation: '" + p_token + "'.");
+ return false;
+ }
+}
+
+bool ResourceImporterDynamicFont::_decode_range(const String &p_token, int32_t &r_pos) {
+ if (p_token.begins_with("U+") || p_token.begins_with("u+") || p_token.begins_with("0x")) {
+ // Unicode character hex index.
+ r_pos = p_token.substr(2).hex_to_int();
+ return true;
+ } else if (p_token.length() == 3 && p_token[0] == '\'' && p_token[2] == '\'') {
+ // Unicode character.
+ r_pos = p_token.unicode_at(1);
+ return true;
+ } else if (p_token.is_numeric()) {
+ // Unicode character decimal index.
+ r_pos = p_token.to_int();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool ResourceImporterDynamicFont::has_advanced_options() const {
+ return true;
+}
+void ResourceImporterDynamicFont::show_advanced_options(const String &p_path) {
+ DynamicFontImportSettings::get_singleton()->open_settings(p_path);
+}
+
+Error ResourceImporterDynamicFont::import(const String &p_source_file, const String &p_save_path, const Map<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);
+
+ bool antialiased = p_options["antialiased"];
+ bool msdf = p_options["multichannel_signed_distance_field"];
+ int px_range = p_options["msdf_pixel_range"];
+ int px_size = p_options["msdf_size"];
+
+ bool autohinter = p_options["force_autohinter"];
+ int hinting = p_options["hinting"];
+ real_t oversampling = p_options["oversampling"];
+
+ // Load base font data.
+ Vector<uint8_t> data = FileAccess::get_file_as_array(p_source_file);
+
+ // Create font.
+ Ref<FontData> font;
+ font.instantiate();
+ font->set_data(data);
+ font->set_antialiased(antialiased);
+ font->set_multichannel_signed_distance_field(msdf);
+ font->set_msdf_pixel_range(px_range);
+ font->set_msdf_size(px_size);
+ font->set_fixed_size(0);
+ font->set_force_autohinter(autohinter);
+ font->set_hinting((TextServer::Hinting)hinting);
+ font->set_oversampling(oversampling);
+
+ Vector<String> lang_en = p_options["support_overrides/language_enabled"];
+ for (int i = 0; i < lang_en.size(); i++) {
+ font->set_language_support_override(lang_en[i], true);
+ }
+
+ Vector<String> lang_dis = p_options["support_overrides/language_disabled"];
+ for (int i = 0; i < lang_dis.size(); i++) {
+ font->set_language_support_override(lang_dis[i], false);
+ }
+
+ Vector<String> scr_en = p_options["support_overrides/script_enabled"];
+ for (int i = 0; i < scr_en.size(); i++) {
+ font->set_script_support_override(scr_en[i], true);
+ }
+
+ Vector<String> scr_dis = p_options["support_overrides/script_disabled"];
+ for (int i = 0; i < scr_dis.size(); i++) {
+ font->set_script_support_override(scr_dis[i], false);
+ }
+
+ Vector<String> variations = p_options["preload/configurations"];
+ Vector<String> char_ranges = p_options["preload/char_ranges"];
+ Vector<String> gl_ranges = p_options["preload/glyph_ranges"];
+
+ for (int i = 0; i < variations.size(); i++) {
+ String name;
+ Dictionary var;
+ Vector2i size = Vector2(16, 0);
+ Vector2i spacing;
+
+ Vector<String> variation_tags = variations[i].split(",");
+ for (int j = 0; j < variation_tags.size(); j++) {
+ if (!_decode_variation(variation_tags[j], var, size, name, spacing)) {
+ WARN_PRINT(vformat(TTR("Invalid variation: \"%s\""), variations[i]));
+ continue;
+ }
+ }
+ RID conf = font->find_cache(var);
+
+ for (int j = 0; j < char_ranges.size(); j++) {
+ int32_t start, end;
+ Vector<String> tokens = char_ranges[j].split("-");
+ if (tokens.size() == 2) {
+ if (!_decode_range(tokens[0], start) || !_decode_range(tokens[1], end)) {
+ WARN_PRINT(vformat(TTR("Invalid range: \"%s\""), char_ranges[j]));
+ continue;
+ }
+ } else if (tokens.size() == 1) {
+ if (!_decode_range(tokens[0], start)) {
+ WARN_PRINT(vformat(TTR("Invalid range: \"%s\""), char_ranges[j]));
+ continue;
+ }
+ end = start;
+ } else {
+ WARN_PRINT(vformat(TTR("Invalid range: \"%s\""), char_ranges[j]));
+ continue;
+ }
+
+ // Preload character ranges for each variations / sizes.
+ print_verbose(vformat(TTR("Pre-rendering range U+%s...%s from configuration \"%s\" (%d / %d)..."), String::num_int64(start, 16), String::num_int64(end, 16), name, i + 1, variations.size()));
+ TS->font_render_range(conf, size, start, end);
+ }
+
+ for (int j = 0; j < gl_ranges.size(); j++) {
+ int32_t start, end;
+ Vector<String> tokens = gl_ranges[j].split("-");
+ if (tokens.size() == 2) {
+ if (!_decode_range(tokens[0], start) || !_decode_range(tokens[1], end)) {
+ WARN_PRINT(vformat(TTR("Invalid range: \"%s\""), gl_ranges[j]));
+ continue;
+ }
+ } else if (tokens.size() == 1) {
+ if (!_decode_range(tokens[0], start)) {
+ WARN_PRINT(vformat(TTR("Invalid range: \"%s\""), gl_ranges[j]));
+ continue;
+ }
+ end = start;
+ } else {
+ WARN_PRINT(vformat(TTR("Invalid range: \"%s\""), gl_ranges[j]));
+ continue;
+ }
+
+ // Preload glyph range for each variations / sizes.
+ print_verbose(vformat(TTR("Pre-rendering glyph range 0x%s...%s from configuration \"%s\" (%d / %d)..."), String::num_int64(start, 16), String::num_int64(end, 16), name, i + 1, variations.size()));
+ for (int32_t k = start; k <= end; k++) {
+ TS->font_render_glyph(conf, size, k);
+ }
+ }
+
+ TS->font_set_spacing(conf, size.x, TextServer::SPACING_SPACE, spacing.x);
+ TS->font_set_spacing(conf, size.x, TextServer::SPACING_GLYPH, spacing.y);
+ }
+
+ int flg = ResourceSaver::SaverFlags::FLAG_BUNDLE_RESOURCES | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
+ if ((bool)p_options["compress"]) {
+ flg |= ResourceSaver::SaverFlags::FLAG_COMPRESS;
+ }
+
+ print_verbose("Saving to: " + p_save_path + ".fontdata");
+ Error err = ResourceSaver::save(p_save_path + ".fontdata", font, flg);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save font to file \"" + p_save_path + ".res\".");
+ print_verbose("Done saving to: " + p_save_path + ".fontdata");
+ return OK;
+}
+
+ResourceImporterDynamicFont::ResourceImporterDynamicFont() {
+}
diff --git a/editor/import/resource_importer_dynamicfont.h b/editor/import/resource_importer_dynamicfont.h
new file mode 100644
index 0000000000..52f256ab96
--- /dev/null
+++ b/editor/import/resource_importer_dynamicfont.h
@@ -0,0 +1,71 @@
+/*************************************************************************/
+/* resource_importer_dynamicfont.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RESOURCE_IMPORTER_FONT_DATA_H
+#define RESOURCE_IMPORTER_FONT_DATA_H
+
+#include "core/io/resource_importer.h"
+#include "scene/resources/font.h"
+#include "servers/text_server.h"
+
+class ResourceImporterDynamicFont : public ResourceImporter {
+ GDCLASS(ResourceImporterDynamicFont, ResourceImporter);
+
+ enum Presets {
+ PRESET_DYNAMIC,
+ PRESET_MSDF,
+ PRESET_MAX
+ };
+
+public:
+ static bool _decode_range(const String &p_token, int32_t &r_pos);
+ static bool _decode_variation(const String &p_token, Dictionary &r_variations, Vector2i &r_size, String &r_name, Vector2i &r_spacing);
+
+ 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;
+ virtual String get_save_extension() const override;
+ virtual String get_resource_type() const override;
+
+ virtual int get_preset_count() const override;
+ virtual String get_preset_name(int p_idx) const override;
+
+ virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
+ virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
+
+ 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 Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+
+ ResourceImporterDynamicFont();
+};
+
+#endif // RESOURCE_IMPORTER_FONTDATA_H
diff --git a/editor/import/resource_importer_image.cpp b/editor/import/resource_importer_image.cpp
index c5b2a8dc3a..2dea359188 100644
--- a/editor/import/resource_importer_image.cpp
+++ b/editor/import/resource_importer_image.cpp
@@ -30,9 +30,9 @@
#include "resource_importer_image.h"
+#include "core/io/file_access.h"
#include "core/io/image_loader.h"
#include "core/io/resource_saver.h"
-#include "core/os/file_access.h"
#include "scene/resources/texture.h"
String ResourceImporterImage::get_importer_name() const {
diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp
new file mode 100644
index 0000000000..997280d1dd
--- /dev/null
+++ b/editor/import/resource_importer_imagefont.cpp
@@ -0,0 +1,162 @@
+/*************************************************************************/
+/* resource_importer_imagefont.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "resource_importer_imagefont.h"
+
+#include "core/io/image_loader.h"
+#include "core/io/resource_saver.h"
+
+String ResourceImporterImageFont::get_importer_name() const {
+ return "font_data_image";
+}
+
+String ResourceImporterImageFont::get_visible_name() const {
+ return "Font Data (Monospace Image Font)";
+}
+
+void ResourceImporterImageFont::get_recognized_extensions(List<String> *p_extensions) const {
+ if (p_extensions) {
+ ImageLoader::get_recognized_extensions(p_extensions);
+ }
+}
+
+String ResourceImporterImageFont::get_save_extension() const {
+ return "fontdata";
+}
+
+String ResourceImporterImageFont::get_resource_type() const {
+ return "FontData";
+}
+
+bool ResourceImporterImageFont::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
+ return true;
+}
+
+void ResourceImporterImageFont::get_import_options(List<ImportOption> *r_options, int p_preset) const {
+ r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "character_ranges"), Vector<String>()));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "columns"), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "rows"), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "font_size"), 14));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
+}
+
+bool ResourceImporterImageFont::_decode_range(const String &p_token, int32_t &r_pos) {
+ if (p_token.begins_with("U+") || p_token.begins_with("u+") || p_token.begins_with("0x")) {
+ // Unicode character hex index.
+ r_pos = p_token.substr(2).hex_to_int();
+ return true;
+ } else if (p_token.length() == 3 && p_token[0] == '\'' && p_token[2] == '\'') {
+ // Unicode character.
+ r_pos = p_token.unicode_at(1);
+ return true;
+ } else if (p_token.is_numeric()) {
+ // Unicode character decimal index.
+ r_pos = p_token.to_int();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+Error ResourceImporterImageFont::import(const String &p_source_file, const String &p_save_path, const Map<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"];
+ int rows = p_options["rows"];
+ int base_size = p_options["font_size"];
+ Vector<String> ranges = p_options["character_ranges"];
+
+ Ref<FontData> font;
+ font.instantiate();
+ font->set_antialiased(false);
+ font->set_multichannel_signed_distance_field(false);
+ font->set_fixed_size(base_size);
+ font->set_force_autohinter(false);
+ font->set_hinting(TextServer::HINTING_NONE);
+ font->set_oversampling(1.0f);
+
+ Ref<Image> img;
+ img.instantiate();
+ Error err = ImageLoader::load_image(p_source_file, img);
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, TTR("Can't load font texture: ") + "\"" + p_source_file + "\".");
+ font->set_texture_image(0, Vector2i(base_size, 0), 0, img);
+
+ int count = columns * rows;
+ int chr_width = img->get_width() / columns;
+ int chr_height = img->get_height() / rows;
+ int pos = 0;
+
+ for (int i = 0; i < ranges.size(); i++) {
+ int32_t start, end;
+ Vector<String> tokens = ranges[i].split("-");
+ if (tokens.size() == 2) {
+ if (!_decode_range(tokens[0], start) || !_decode_range(tokens[1], end)) {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ } else if (tokens.size() == 1) {
+ if (!_decode_range(tokens[0], start)) {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ end = start;
+ } else {
+ WARN_PRINT("Invalid range: \"" + ranges[i] + "\"");
+ continue;
+ }
+ for (int32_t idx = start; idx <= end; idx++) {
+ int x = pos % columns;
+ int y = pos / columns;
+ font->set_glyph_advance(0, base_size, idx, Vector2(chr_width, 0));
+ font->set_glyph_offset(0, Vector2i(base_size, 0), idx, Vector2(0, -0.5 * chr_height));
+ font->set_glyph_size(0, Vector2i(base_size, 0), idx, Vector2(chr_width, chr_height));
+ font->set_glyph_uv_rect(0, Vector2i(base_size, 0), idx, Rect2(chr_width * x, chr_height * y, chr_width, chr_height));
+ font->set_glyph_texture_idx(0, Vector2i(base_size, 0), idx, 0);
+ pos++;
+ ERR_FAIL_COND_V_MSG(pos >= count, ERR_CANT_CREATE, "Too many characters in range.");
+ }
+ }
+ font->set_ascent(0, base_size, 0.5 * chr_height);
+ font->set_descent(0, base_size, 0.5 * chr_height);
+
+ int flg = ResourceSaver::SaverFlags::FLAG_BUNDLE_RESOURCES | ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
+ if ((bool)p_options["compress"]) {
+ flg |= ResourceSaver::SaverFlags::FLAG_COMPRESS;
+ }
+
+ print_verbose("Saving to: " + p_save_path + ".fontdata");
+ err = ResourceSaver::save(p_save_path + ".fontdata", font, flg);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save font to file \"" + p_save_path + ".res\".");
+ print_verbose("Done saving to: " + p_save_path + ".fontdata");
+ return OK;
+}
+
+ResourceImporterImageFont::ResourceImporterImageFont() {
+}
diff --git a/editor/import/resource_importer_imagefont.h b/editor/import/resource_importer_imagefont.h
new file mode 100644
index 0000000000..9b2b38596f
--- /dev/null
+++ b/editor/import/resource_importer_imagefont.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* resource_importer_imagefont.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RESOURCE_IMPORTER_IMAGE_FONT_H
+#define RESOURCE_IMPORTER_IMAGE_FONT_H
+
+#include "core/io/resource_importer.h"
+#include "scene/resources/font.h"
+#include "servers/text_server.h"
+
+class ResourceImporterImageFont : public ResourceImporter {
+ GDCLASS(ResourceImporterImageFont, ResourceImporter);
+
+public:
+ static bool _decode_range(const String &p_token, int32_t &r_pos);
+
+ 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;
+ virtual String get_save_extension() const override;
+ virtual String get_resource_type() const override;
+
+ virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
+ virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
+
+ virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+
+ ResourceImporterImageFont();
+};
+
+#endif // RESOURCE_IMPORTER_IMAGE_FONT_H
diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp
index 6d2215c379..d5bb21443c 100644
--- a/editor/import/resource_importer_layered_texture.cpp
+++ b/editor/import/resource_importer_layered_texture.cpp
@@ -186,7 +186,7 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons
for (int i = 0; i < mm_d; i++) {
Ref<Image> mm;
- mm.instance();
+ mm.instantiate();
mm->create(mm_w, mm_h, false, p_images[0]->get_format());
Vector3 pos;
pos.z = float(i) * float(d) / float(mm_d) + 0.5;
@@ -328,7 +328,7 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
}
Ref<Image> image;
- image.instance();
+ image.instantiate();
Error err = ImageLoader::load_image(p_source_file, image, nullptr, false, 1.0);
if (err != OK) {
return err;
@@ -341,10 +341,7 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
if (compress_mode == COMPRESS_VRAM_COMPRESSED) {
mipmaps = true;
- }
- //optimize
- if (compress_mode == COMPRESS_VRAM_COMPRESSED) {
//if using video ram, optimize
if (channel_pack == 0) {
//remove alpha if not needed, so compression is more efficient
diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp
index dd62c72d8a..34bc0a7d8d 100644
--- a/editor/import/resource_importer_obj.cpp
+++ b/editor/import/resource_importer_obj.cpp
@@ -30,8 +30,8 @@
#include "resource_importer_obj.h"
+#include "core/io/file_access.h"
#include "core/io/resource_saver.h"
-#include "core/os/file_access.h"
#include "editor/import/scene_importer_mesh.h"
#include "editor/import/scene_importer_mesh_node_3d.h"
#include "scene/3d/mesh_instance_3d.h"
@@ -57,7 +57,7 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Stand
//vertex
current_name = l.replace("newmtl", "").strip_edges();
- current.instance();
+ current.instantiate();
current->set_name(current_name);
material_map[current_name] = current;
} else if (l.begins_with("Ka ")) {
@@ -126,7 +126,7 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Stand
String p = l.replace("map_Kd", "").replace("\\", "/").strip_edges();
String path;
- if (p.is_abs_path()) {
+ if (p.is_absolute_path()) {
path = p;
} else {
path = base_path.plus_file(p);
@@ -146,7 +146,7 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Stand
String p = l.replace("map_Ks", "").replace("\\", "/").strip_edges();
String path;
- if (p.is_abs_path()) {
+ if (p.is_absolute_path()) {
path = p;
} else {
path = base_path.plus_file(p);
@@ -166,7 +166,7 @@ static Error _parse_material_library(const String &p_path, Map<String, Ref<Stand
String p = l.replace("map_Ns", "").replace("\\", "/").strip_edges();
String path;
- if (p.is_abs_path()) {
+ if (p.is_absolute_path()) {
path = p;
} else {
path = base_path.plus_file(p);
@@ -207,7 +207,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path));
Ref<ArrayMesh> mesh;
- mesh.instance();
+ mesh.instantiate();
bool generate_tangents = p_generate_tangents;
Vector3 scale_mesh = p_scale_mesh;
@@ -378,7 +378,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
if (!p_single_mesh) {
mesh->set_name(name);
r_meshes.push_back(mesh);
- mesh.instance();
+ mesh.instantiate();
current_group = "";
current_material = "";
}
@@ -438,17 +438,16 @@ Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, in
Node3D *scene = memnew(Node3D);
- for (List<Ref<Mesh>>::Element *E = meshes.front(); E; E = E->next()) {
+ for (const Ref<Mesh> &m : meshes) {
Ref<EditorSceneImporterMesh> mesh;
- mesh.instance();
- Ref<Mesh> m = E->get();
+ mesh.instantiate();
for (int i = 0; i < m->get_surface_count(); i++) {
mesh->add_surface(m->surface_get_primitive_type(i), m->surface_get_arrays(i), Array(), Dictionary(), m->surface_get_material(i));
}
EditorSceneImporterMeshNode3D *mi = memnew(EditorSceneImporterMeshNode3D);
mi->set_mesh(mesh);
- mi->set_name(E->get()->get_name());
+ mi->set_name(m->get_name());
scene->add_child(mi);
mi->set_owner(scene);
}
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 96002400f3..753dcb1275 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -32,6 +32,7 @@
#include "core/io/resource_saver.h"
#include "editor/editor_node.h"
+#include "editor/import/editor_importer_bake_reset.h"
#include "editor/import/scene_import_settings.h"
#include "editor/import/scene_importer_mesh_node_3d.h"
#include "scene/3d/area_3d.h"
@@ -44,23 +45,24 @@
#include "scene/resources/animation.h"
#include "scene/resources/box_shape_3d.h"
#include "scene/resources/packed_scene.h"
-#include "scene/resources/ray_shape_3d.h"
#include "scene/resources/resource_format_text.h"
+#include "scene/resources/separation_ray_shape_3d.h"
#include "scene/resources/sphere_shape_3d.h"
#include "scene/resources/surface_tool.h"
-#include "scene/resources/world_margin_shape_3d.h"
+#include "scene/resources/world_boundary_shape_3d.h"
uint32_t EditorSceneImporter::get_import_flags() const {
- if (get_script_instance()) {
- return get_script_instance()->call("_get_import_flags");
+ int ret;
+ if (GDVIRTUAL_CALL(_get_import_flags, ret)) {
+ return ret;
}
ERR_FAIL_V(0);
}
void EditorSceneImporter::get_extensions(List<String> *r_extensions) const {
- if (get_script_instance()) {
- Array arr = get_script_instance()->call("_get_extensions");
+ Vector<String> arr;
+ if (GDVIRTUAL_CALL(_get_extensions, arr)) {
for (int i = 0; i < arr.size(); i++) {
r_extensions->push_back(arr[i]);
}
@@ -71,16 +73,18 @@ void EditorSceneImporter::get_extensions(List<String> *r_extensions) const {
}
Node *EditorSceneImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
- if (get_script_instance()) {
- return get_script_instance()->call("_import_scene", p_path, p_flags, p_bake_fps);
+ Object *ret;
+ if (GDVIRTUAL_CALL(_import_scene, p_path, p_flags, p_bake_fps, ret)) {
+ return Object::cast_to<Node>(ret);
}
ERR_FAIL_V(nullptr);
}
Ref<Animation> EditorSceneImporter::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
- if (get_script_instance()) {
- return get_script_instance()->call("_import_animation", p_path, p_flags);
+ Ref<Animation> ret;
+ if (GDVIRTUAL_CALL(_import_animation, p_path, p_flags, p_bake_fps, ret)) {
+ return ret;
}
ERR_FAIL_V(nullptr);
@@ -101,15 +105,10 @@ void EditorSceneImporter::_bind_methods() {
ClassDB::bind_method(D_METHOD("import_scene_from_other_importer", "path", "flags", "bake_fps"), &EditorSceneImporter::import_scene_from_other_importer);
ClassDB::bind_method(D_METHOD("import_animation_from_other_importer", "path", "flags", "bake_fps"), &EditorSceneImporter::import_animation_from_other_importer);
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_import_flags"));
- BIND_VMETHOD(MethodInfo(Variant::ARRAY, "_get_extensions"));
-
- MethodInfo mi = MethodInfo(Variant::OBJECT, "_import_scene", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "flags"), PropertyInfo(Variant::INT, "bake_fps"));
- mi.return_val.class_name = "Node";
- BIND_VMETHOD(mi);
- mi = MethodInfo(Variant::OBJECT, "_import_animation", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "flags"), PropertyInfo(Variant::INT, "bake_fps"));
- mi.return_val.class_name = "Animation";
- BIND_VMETHOD(mi);
+ GDVIRTUAL_BIND(_get_import_flags);
+ GDVIRTUAL_BIND(_get_extensions);
+ GDVIRTUAL_BIND(_import_scene, "path", "flags", "bake_fps");
+ GDVIRTUAL_BIND(_import_animation, "path", "flags", "bake_fps");
BIND_CONSTANT(IMPORT_SCENE);
BIND_CONSTANT(IMPORT_ANIMATION);
@@ -120,13 +119,14 @@ void EditorSceneImporter::_bind_methods() {
/////////////////////////////////
void EditorScenePostImport::_bind_methods() {
- BIND_VMETHOD(MethodInfo(Variant::OBJECT, "post_import", PropertyInfo(Variant::OBJECT, "scene")));
+ GDVIRTUAL_BIND(_post_import, "scene")
ClassDB::bind_method(D_METHOD("get_source_file"), &EditorScenePostImport::get_source_file);
}
Node *EditorScenePostImport::post_import(Node *p_scene) {
- if (get_script_instance()) {
- return get_script_instance()->call("post_import", p_scene);
+ Object *ret;
+ if (GDVIRTUAL_CALL(_post_import, p_scene, ret)) {
+ return Object::cast_to<Node>(ret);
}
return p_scene;
@@ -233,28 +233,14 @@ static String _fixstr(const String &p_what, const String &p_str) {
return what;
}
-static void _gen_shape_list(const Ref<Mesh> &mesh, List<Ref<Shape3D>> &r_shape_list, bool p_convex) {
- ERR_FAIL_NULL_MSG(mesh, "Cannot generate shape list with null mesh value");
- if (!p_convex) {
- Ref<Shape3D> shape = mesh->create_trimesh_shape();
- r_shape_list.push_back(shape);
- } else {
- Vector<Ref<Shape3D>> cd = mesh->convex_decompose();
- if (cd.size()) {
- for (int i = 0; i < cd.size(); i++) {
- r_shape_list.push_back(cd[i]);
- }
- }
- }
-}
-
-static void _pre_gen_shape_list(const Ref<EditorSceneImporterMesh> &mesh, List<Ref<Shape3D>> &r_shape_list, bool p_convex) {
+static void _pre_gen_shape_list(Ref<EditorSceneImporterMesh> &mesh, Vector<Ref<Shape3D>> &r_shape_list, bool p_convex) {
ERR_FAIL_NULL_MSG(mesh, "Cannot generate shape list with null mesh value");
if (!p_convex) {
Ref<Shape3D> shape = mesh->create_trimesh_shape();
r_shape_list.push_back(shape);
} else {
- Vector<Ref<Shape3D>> cd = mesh->convex_decompose();
+ Vector<Ref<Shape3D>> cd;
+ cd.push_back(mesh->get_mesh()->create_convex_shape(true, /*Passing false, otherwise VHACD will be used to simplify (Decompose) the Mesh.*/ false));
if (cd.size()) {
for (int i = 0; i < cd.size(); i++) {
r_shape_list.push_back(cd[i]);
@@ -263,7 +249,7 @@ static void _pre_gen_shape_list(const Ref<EditorSceneImporterMesh> &mesh, List<R
}
}
-Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map) {
+Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, Vector<Ref<Shape3D>>> &collision_map) {
// 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, collision_map);
@@ -312,8 +298,8 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
List<StringName> anims;
ap->get_animation_list(&anims);
- for (List<StringName>::Element *E = anims.front(); E; E = E->next()) {
- Ref<Animation> anim = ap->get_animation(E->get());
+ for (const StringName &E : anims) {
+ Ref<Animation> anim = ap->get_animation(E);
ERR_CONTINUE(anim.is_null());
for (int i = 0; i < anim->get_track_count(); i++) {
NodePath path = anim->track_get_path(i);
@@ -328,14 +314,14 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
}
}
- String animname = E->get();
+ String animname = E;
const int loop_string_count = 3;
static const char *loop_strings[loop_string_count] = { "loops", "loop", "cycle" };
for (int i = 0; i < loop_string_count; i++) {
if (_teststr(animname, loop_strings[i])) {
anim->set_loop(true);
animname = _fixstr(animname, loop_strings[i]);
- ap->rename_animation(E->get(), animname);
+ ap->rename_animation(E, animname);
}
}
}
@@ -350,7 +336,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
- List<Ref<Shape3D>> shapes;
+ Vector<Ref<Shape3D>> shapes;
String fixed_name;
if (collision_map.has(mesh)) {
shapes = collision_map[mesh];
@@ -396,13 +382,13 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
boxShape->set_size(Vector3(2, 2, 2));
colshape->set_shape(boxShape);
} else if (empty_draw_type == "SINGLE_ARROW") {
- RayShape3D *rayShape = memnew(RayShape3D);
+ SeparationRayShape3D *rayShape = memnew(SeparationRayShape3D);
rayShape->set_length(1);
colshape->set_shape(rayShape);
Object::cast_to<Node3D>(sb)->rotate_x(Math_PI / 2);
} else if (empty_draw_type == "IMAGE") {
- WorldMarginShape3D *world_margin_shape = memnew(WorldMarginShape3D);
- colshape->set_shape(world_margin_shape);
+ WorldBoundaryShape3D *world_boundary_shape = memnew(WorldBoundaryShape3D);
+ colshape->set_shape(world_boundary_shape);
} else {
SphereShape3D *sphereShape = memnew(SphereShape3D);
sphereShape->set_radius(1);
@@ -421,11 +407,11 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
- List<Ref<Shape3D>> shapes;
+ Vector<Ref<Shape3D>> shapes;
if (collision_map.has(mesh)) {
shapes = collision_map[mesh];
} else {
- _gen_shape_list(mesh, shapes, true);
+ _pre_gen_shape_list(mesh, shapes, true);
}
RigidBody3D *rigid_body = memnew(RigidBody3D);
@@ -433,7 +419,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
p_node->replace_by(rigid_body);
rigid_body->set_transform(mi->get_transform());
p_node = rigid_body;
- mi->set_transform(Transform());
+ mi->set_transform(Transform3D());
rigid_body->add_child(mi);
mi->set_owner(rigid_body->get_owner());
@@ -446,15 +432,15 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
- List<Ref<Shape3D>> shapes;
+ Vector<Ref<Shape3D>> shapes;
String fixed_name;
if (collision_map.has(mesh)) {
shapes = collision_map[mesh];
} else if (_teststr(name, "col")) {
- _gen_shape_list(mesh, shapes, false);
+ _pre_gen_shape_list(mesh, shapes, false);
collision_map[mesh] = shapes;
} else if (_teststr(name, "convcol")) {
- _gen_shape_list(mesh, shapes, true);
+ _pre_gen_shape_list(mesh, shapes, true);
collision_map[mesh] = shapes;
}
@@ -505,15 +491,15 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
if (!mesh.is_null()) {
- List<Ref<Shape3D>> shapes;
+ Vector<Ref<Shape3D>> shapes;
if (collision_map.has(mesh)) {
shapes = collision_map[mesh];
} else if (_teststr(mesh->get_name(), "col")) {
- _gen_shape_list(mesh, shapes, false);
+ _pre_gen_shape_list(mesh, shapes, false);
collision_map[mesh] = shapes;
mesh->set_name(_fixstr(mesh->get_name(), "col"));
} else if (_teststr(mesh->get_name(), "convcol")) {
- _gen_shape_list(mesh, shapes, true);
+ _pre_gen_shape_list(mesh, shapes, true);
collision_map[mesh] = shapes;
mesh->set_name(_fixstr(mesh->get_name(), "convcol"));
}
@@ -531,7 +517,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<E
return p_node;
}
-Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map, Set<Ref<EditorSceneImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps) {
+Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Set<Ref<EditorSceneImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps) {
// children first
for (int i = 0; i < p_node->get_child_count(); i++) {
Node *r = _post_fix_node(p_node->get_child(i), p_root, collision_map, r_scanned_meshes, p_node_data, p_material_data, p_animation_data, p_animation_fps);
@@ -594,28 +580,35 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
}
if (node_settings.has("generate/physics")) {
- int mesh_physics_mode = node_settings["generate/physics"];
-
- if (mesh_physics_mode != MESH_PHYSICS_DISABLED) {
- List<Ref<Shape3D>> shapes;
+ int mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_DISABLED;
+
+ const bool generate_collider = node_settings["generate/physics"];
+ if (generate_collider) {
+ mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_MESH_AND_STATIC_COLLIDER;
+ if (node_settings.has("physics/body_type")) {
+ const BodyType body_type = (BodyType)node_settings["physics/body_type"].operator int();
+ switch (body_type) {
+ case BODY_TYPE_STATIC:
+ mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_MESH_AND_STATIC_COLLIDER;
+ break;
+ case BODY_TYPE_DYNAMIC:
+ mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_RIGID_BODY_AND_MESH;
+ break;
+ case BODY_TYPE_AREA:
+ mesh_physics_mode = MeshPhysicsMode::MESH_PHYSICS_AREA_ONLY;
+ break;
+ }
+ }
+ }
+ if (mesh_physics_mode != MeshPhysicsMode::MESH_PHYSICS_DISABLED) {
+ Vector<Ref<Shape3D>> shapes;
if (collision_map.has(m)) {
shapes = collision_map[m];
} else {
- switch (mesh_physics_mode) {
- case MESH_PHYSICS_MESH_AND_STATIC_COLLIDER: {
- _pre_gen_shape_list(m, shapes, false);
- } break;
- case MESH_PHYSICS_RIGID_BODY_AND_MESH: {
- _pre_gen_shape_list(m, shapes, true);
- } break;
- case MESH_PHYSICS_STATIC_COLLIDER_ONLY: {
- _pre_gen_shape_list(m, shapes, false);
- } break;
- case MESH_PHYSICS_AREA_ONLY: {
- _pre_gen_shape_list(m, shapes, true);
- } break;
- }
+ shapes = get_collision_shapes(
+ m->get_mesh(),
+ node_settings);
}
if (shapes.size()) {
@@ -624,22 +617,24 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
case MESH_PHYSICS_MESH_AND_STATIC_COLLIDER: {
StaticBody3D *col = memnew(StaticBody3D);
p_node->add_child(col);
+ col->set_owner(p_node->get_owner());
+ col->set_transform(get_collision_shapes_transform(node_settings));
base = col;
} break;
case MESH_PHYSICS_RIGID_BODY_AND_MESH: {
RigidBody3D *rigid_body = memnew(RigidBody3D);
rigid_body->set_name(p_node->get_name());
p_node->replace_by(rigid_body);
- rigid_body->set_transform(mi->get_transform());
+ rigid_body->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));
p_node = rigid_body;
- mi->set_transform(Transform());
+ mi->set_transform(Transform3D());
rigid_body->add_child(mi);
mi->set_owner(rigid_body->get_owner());
base = rigid_body;
} break;
case MESH_PHYSICS_STATIC_COLLIDER_ONLY: {
StaticBody3D *col = memnew(StaticBody3D);
- col->set_transform(mi->get_transform());
+ col->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));
col->set_name(p_node->get_name());
p_node->replace_by(col);
memdelete(p_node);
@@ -648,7 +643,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
} break;
case MESH_PHYSICS_AREA_ONLY: {
Area3D *area = memnew(Area3D);
- area->set_transform(mi->get_transform());
+ area->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings));
area->set_name(p_node->get_name());
p_node->replace_by(area);
memdelete(p_node);
@@ -659,9 +654,9 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
}
int idx = 0;
- for (List<Ref<Shape3D>>::Element *E = shapes.front(); E; E = E->next()) {
+ for (const Ref<Shape3D> &E : shapes) {
CollisionShape3D *cshape = memnew(CollisionShape3D);
- cshape->set_shape(E->get());
+ cshape->set_shape(E);
base->add_child(cshape);
cshape->set_owner(base->get_owner());
@@ -712,9 +707,9 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
//fill node settings for this node with default values
List<ImportOption> iopts;
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts);
- for (List<ImportOption>::Element *E = iopts.front(); E; E = E->next()) {
- if (!node_settings.has(E->get().option.name)) {
- node_settings[E->get().option.name] = E->get().default_value;
+ for (const ImportOption &E : iopts) {
+ if (!node_settings.has(E.option.name)) {
+ node_settings[E.option.name] = E.default_value;
}
}
}
@@ -756,8 +751,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
} else {
List<StringName> anims;
ap->get_animation_list(&anims);
- for (List<StringName>::Element *E = anims.front(); E; E = E->next()) {
- String name = E->get();
+ for (const StringName &name : anims) {
Ref<Animation> anim = ap->get_animation(name);
if (p_animation_data.has(name)) {
Dictionary anim_settings = p_animation_data[name];
@@ -765,9 +759,9 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
//fill with default values
List<ImportOption> iopts;
get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION, &iopts);
- for (List<ImportOption>::Element *F = iopts.front(); F; F = F->next()) {
- if (!anim_settings.has(F->get().option.name)) {
- anim_settings[F->get().option.name] = F->get().default_value;
+ for (const ImportOption &F : iopts) {
+ if (!anim_settings.has(F.option.name)) {
+ anim_settings[F.option.name] = F.default_value;
}
}
}
@@ -856,8 +850,8 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
if (kt > (from + 0.01) && k > 0) {
- if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM) {
- Quat q;
+ if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
+ Quaternion q;
Vector3 p;
Vector3 s;
default_anim->transform_track_interpolate(j, from, &p, &q, &s);
@@ -870,8 +864,8 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
}
}
- if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM) {
- Quat q;
+ if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
+ Quaternion q;
Vector3 p;
Vector3 s;
default_anim->transform_track_get_key(j, k, &p, &q, &s);
@@ -884,8 +878,8 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
}
if (dtrack != -1 && kt >= to) {
- if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM) {
- Quat q;
+ if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
+ Quaternion q;
Vector3 p;
Vector3 s;
default_anim->transform_track_interpolate(j, to, &p, &q, &s);
@@ -902,8 +896,8 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
new_anim->add_track(default_anim->track_get_type(j));
dtrack = new_anim->get_track_count() - 1;
new_anim->track_set_path(dtrack, default_anim->track_get_path(j));
- if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM) {
- Quat q;
+ if (default_anim->track_get_type(j) == Animation::TYPE_TRANSFORM3D) {
+ Quaternion q;
Vector3 p;
Vector3 s;
default_anim->transform_track_interpolate(j, from, &p, &q, &s);
@@ -936,8 +930,8 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
void ResourceImporterScene::_optimize_animations(AnimationPlayer *anim, float p_max_lin_error, float p_max_ang_error, float p_max_angle) {
List<StringName> anim_names;
anim->get_animation_list(&anim_names);
- for (List<StringName>::Element *E = anim_names.front(); E; E = E->next()) {
- Ref<Animation> a = anim->get_animation(E->get());
+ for (const StringName &E : anim_names) {
+ Ref<Animation> a = anim->get_animation(E);
a->optimize(p_max_lin_error, p_max_ang_error, Math::deg2rad(p_max_angle));
}
}
@@ -949,8 +943,35 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
} break;
case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/physics", PROPERTY_HINT_ENUM, "Disabled,Mesh + Static Collider,Rigid Body + Mesh,Static Collider Only,Area Only"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate/physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/navmesh", PROPERTY_HINT_ENUM, "Disabled,Mesh + NavMesh,NavMesh Only"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/body_type", PROPERTY_HINT_ENUM, "Static,Dynamic,Area"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "physics/shape_type", PROPERTY_HINT_ENUM, "Decompose Convex,Simple Convex,Trimesh,Box,Sphere,Cylinder,Capsule", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
+
+ // Decomposition
+ Mesh::ConvexDecompositionSettings decomposition_default;
+ 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.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.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.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.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.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.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.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.convexhull_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.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.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.convexhull_approximation));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "decomposition/max_convex_hulls", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), decomposition_default.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.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()));
} 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));
@@ -1001,6 +1022,65 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
case INTERNAL_IMPORT_CATEGORY_NODE: {
} break;
case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
+ const bool generate_physics =
+ p_options.has("generate/physics") &&
+ p_options["generate/physics"].operator bool();
+
+ if (
+ p_option == "physics/body_type" ||
+ p_option == "physics/shape_type") {
+ // Show if need to generate collisions.
+ return generate_physics;
+ }
+
+ if (p_option.find("decomposition/") >= 0) {
+ // Show if need to generate collisions.
+ if (generate_physics &&
+ // Show if convex is enabled.
+ p_options["physics/shape_type"] == Variant(SHAPE_TYPE_DECOMPOSE_CONVEX)) {
+ if (p_option == "decomposition/advanced") {
+ return true;
+ }
+
+ const bool decomposition_advanced =
+ p_options.has("decomposition/advanced") &&
+ p_options["decomposition/advanced"].operator bool();
+
+ if (p_option == "decomposition/precision") {
+ return !decomposition_advanced;
+ } else {
+ return decomposition_advanced;
+ }
+ }
+
+ return false;
+ }
+
+ if (p_option == "primitive/position" || p_option == "primitive/rotation") {
+ const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
+ return generate_physics &&
+ physics_shape >= SHAPE_TYPE_BOX;
+ }
+
+ if (p_option == "primitive/size") {
+ const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
+ return generate_physics &&
+ physics_shape == SHAPE_TYPE_BOX;
+ }
+
+ if (p_option == "primitive/radius") {
+ const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
+ return generate_physics && (physics_shape == SHAPE_TYPE_SPHERE ||
+ physics_shape == SHAPE_TYPE_CYLINDER ||
+ physics_shape == SHAPE_TYPE_CAPSULE);
+ }
+
+ if (p_option == "primitive/height") {
+ const ShapeType physics_shape = (ShapeType)p_options["physics/shape_type"].operator int();
+ return generate_physics &&
+ (physics_shape == SHAPE_TYPE_CYLINDER ||
+ physics_shape == SHAPE_TYPE_CAPSULE);
+ }
} break;
case INTERNAL_IMPORT_CATEGORY_MESH: {
if (p_option == "save_to_file/path" || p_option == "save_to_file/make_streamable") {
@@ -1037,6 +1117,33 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
return true;
}
+bool ResourceImporterScene::get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const {
+ switch (p_category) {
+ case INTERNAL_IMPORT_CATEGORY_NODE: {
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
+ if (
+ p_option == "generate/physics" ||
+ p_option == "physics/shape_type" ||
+ p_option.find("decomposition/") >= 0 ||
+ p_option.find("primitive/") >= 0) {
+ return true;
+ }
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_MESH: {
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_MATERIAL: {
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_ANIMATION: {
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {
+ } break;
+ default: {
+ }
+ }
+ return false;
+}
+
void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_type", PROPERTY_HINT_TYPE_STRING, "Node"), "Node3D"));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/root_name"), "Scene Root"));
@@ -1046,11 +1153,11 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
String script_ext_hint;
- for (List<String>::Element *E = script_extentions.front(); E; E = E->next()) {
+ for (const String &E : script_extentions) {
if (script_ext_hint != "") {
script_ext_hint += ",";
}
- script_ext_hint += "*." + E->get();
+ script_ext_hint += "*." + E;
}
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0));
@@ -1061,6 +1168,7 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/bake_reset_animation"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
@@ -1089,9 +1197,9 @@ Node *ResourceImporterScene::import_scene_from_other_importer(EditorSceneImporte
List<String> extensions;
E->get()->get_extensions(&extensions);
- for (List<String>::Element *F = extensions.front(); F; F = F->next()) {
- if (F->get().to_lower() == ext) {
- importer = E->get();
+ for (const String &F : extensions) {
+ if (F.to_lower() == ext) {
+ importer = E;
break;
}
}
@@ -1119,9 +1227,9 @@ Ref<Animation> ResourceImporterScene::import_animation_from_other_importer(Edito
List<String> extensions;
E->get()->get_extensions(&extensions);
- for (List<String>::Element *F = extensions.front(); F; F = F->next()) {
- if (F->get().to_lower() == ext) {
- importer = E->get();
+ for (const String &F : extensions) {
+ if (F.to_lower() == ext) {
+ importer = E;
break;
}
}
@@ -1209,11 +1317,11 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
}
if (bake_lightmaps) {
- Transform xf;
+ Transform3D xf;
Node3D *n = src_mesh_node;
while (n) {
xf = n->get_transform() * xf;
- n = n->get_parent_spatial();
+ n = n->get_parent_node_3d();
}
Vector<uint8_t> lightmap_cache;
@@ -1290,10 +1398,10 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m
}
}
-void ResourceImporterScene::_add_shapes(Node *p_node, const List<Ref<Shape3D>> &p_shapes) {
- for (const List<Ref<Shape3D>>::Element *E = p_shapes.front(); E; E = E->next()) {
+void ResourceImporterScene::_add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes) {
+ for (const Ref<Shape3D> &E : p_shapes) {
CollisionShape3D *cshape = memnew(CollisionShape3D);
- cshape->set_shape(E->get());
+ cshape->set_shape(E);
p_node->add_child(cshape);
cshape->set_owner(p_node->get_owner());
@@ -1311,8 +1419,8 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file) {
List<String> extensions;
E->get()->get_extensions(&extensions);
- for (List<String>::Element *F = extensions.front(); F; F = F->next()) {
- if (F->get().to_lower() == ext) {
+ for (const String &F : extensions) {
+ if (F.to_lower() == ext) {
importer = E->get();
break;
}
@@ -1331,7 +1439,7 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file) {
return nullptr;
}
- Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> collision_map;
+ Map<Ref<EditorSceneImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
_pre_fix_node(scene, scene, collision_map);
@@ -1351,8 +1459,8 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
List<String> extensions;
E->get()->get_extensions(&extensions);
- for (List<String>::Element *F = extensions.front(); F; F = F->next()) {
- if (F->get().to_lower() == ext) {
+ for (const String &F : extensions) {
+ if (F.to_lower() == ext) {
importer = E->get();
break;
}
@@ -1407,10 +1515,15 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
}
Set<Ref<EditorSceneImporterMesh>> scanned_meshes;
- Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> collision_map;
+ Map<Ref<EditorSceneImporterMesh>, Vector<Ref<Shape3D>>> collision_map;
_pre_fix_node(scene, scene, collision_map);
_post_fix_node(scene, scene, collision_map, scanned_meshes, node_data, material_data, animation_data, fps);
+ bool use_bake_reset_animation = p_options["animation/bake_reset_animation"];
+ if (use_bake_reset_animation) {
+ BakeReset bake_reset;
+ bake_reset._bake_animation_pose(scene, "RESET");
+ }
String root_type = p_options["nodes/root_type"];
root_type = root_type.split(" ")[0]; // full root_type is "ClassName (filename.gd)" for a script global class.
@@ -1422,7 +1535,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
}
if (root_type != "Node3D") {
- Node *base_node = Object::cast_to<Node>(ClassDB::instance(root_type));
+ Node *base_node = Object::cast_to<Node>(ClassDB::instantiate(root_type));
if (base_node) {
scene->replace_by(base_node);
@@ -1508,7 +1621,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
if (!scene) {
EditorNode::add_io_error(
TTR("Error running post-import script:") + " " + post_import_script_path + "\n" +
- TTR("Did you return a Node-derived object in the `post_import()` method?"));
+ TTR("Did you return a Node-derived object in the `_post_import()` method?"));
return err;
}
}
@@ -1557,7 +1670,7 @@ Node *EditorSceneImporterESCN::import_scene(const String &p_path, uint32_t p_fla
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 + "'.");
- Node *scene = ps->instance();
+ Node *scene = ps->instantiate();
ERR_FAIL_COND_V(!scene, nullptr);
return scene;
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index 8cb84abce2..e232b715be 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -42,8 +42,8 @@ class Material;
class AnimationPlayer;
class EditorSceneImporterMesh;
-class EditorSceneImporter : public Reference {
- GDCLASS(EditorSceneImporter, Reference);
+class EditorSceneImporter : public RefCounted {
+ GDCLASS(EditorSceneImporter, RefCounted);
protected:
static void _bind_methods();
@@ -51,6 +51,11 @@ protected:
Node *import_scene_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps);
Ref<Animation> import_animation_from_other_importer(const String &p_path, uint32_t p_flags, int p_bake_fps);
+ GDVIRTUAL0RC(int, _get_import_flags)
+ GDVIRTUAL0RC(Vector<String>, _get_extensions)
+ GDVIRTUAL3R(Object *, _import_scene, String, uint32_t, uint32_t)
+ GDVIRTUAL3R(Ref<Animation>, _import_animation, String, uint32_t, uint32_t)
+
public:
enum ImportFlags {
IMPORT_SCENE = 1,
@@ -58,7 +63,6 @@ public:
IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 4,
IMPORT_GENERATE_TANGENT_ARRAYS = 8,
IMPORT_USE_NAMED_SKIN_BINDS = 16,
-
};
virtual uint32_t get_import_flags() const;
@@ -69,14 +73,16 @@ public:
EditorSceneImporter() {}
};
-class EditorScenePostImport : public Reference {
- GDCLASS(EditorScenePostImport, Reference);
+class EditorScenePostImport : public RefCounted {
+ GDCLASS(EditorScenePostImport, RefCounted);
String source_file;
protected:
static void _bind_methods();
+ GDVIRTUAL1R(Object *, _post_import, Node *)
+
public:
String get_source_file() const;
virtual Node *post_import(Node *p_scene);
@@ -118,9 +124,25 @@ class ResourceImporterScene : public ResourceImporter {
MESH_OVERRIDE_DISABLE,
};
+ enum BodyType {
+ BODY_TYPE_STATIC,
+ BODY_TYPE_DYNAMIC,
+ BODY_TYPE_AREA
+ };
+
+ enum ShapeType {
+ SHAPE_TYPE_DECOMPOSE_CONVEX,
+ SHAPE_TYPE_SIMPLE_CONVEX,
+ SHAPE_TYPE_TRIMESH,
+ SHAPE_TYPE_BOX,
+ SHAPE_TYPE_SPHERE,
+ SHAPE_TYPE_CYLINDER,
+ SHAPE_TYPE_CAPSULE,
+ };
+
void _replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner);
void _generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches);
- void _add_shapes(Node *p_node, const List<Ref<Shape3D>> &p_shapes);
+ void _add_shapes(Node *p_node, const Vector<Ref<Shape3D>> &p_shapes);
public:
static ResourceImporterScene *get_singleton() { return singleton; }
@@ -152,13 +174,15 @@ public:
void get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const;
bool get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const;
+ bool get_internal_option_update_view_required(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const;
virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const override;
- virtual int get_import_order() const override { return 100; } //after everything
+ // Import scenes *after* everything else (such as textures).
+ virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; }
- Node *_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map);
- Node *_post_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map, Set<Ref<EditorSceneImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps);
+ Node *_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, Vector<Ref<Shape3D>>> &collision_map);
+ Node *_post_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, Vector<Ref<Shape3D>>> &collision_map, Set<Ref<EditorSceneImporterMesh>> &r_scanned_meshes, const Dictionary &p_node_data, const Dictionary &p_material_data, const Dictionary &p_animation_data, float p_animation_fps);
Ref<Animation> _save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, String p_save_to_path, bool p_keep_custom_tracks);
void _create_clips(AnimationPlayer *anim, const Array &p_clips, bool p_bake_all);
@@ -176,6 +200,12 @@ public:
virtual bool can_import_threaded() const override { return false; }
ResourceImporterScene();
+
+ template <class M>
+ static Vector<Ref<Shape3D>> get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options);
+
+ template <class M>
+ static Transform3D get_collision_shapes_transform(const M &p_options);
};
class EditorSceneImporterESCN : public EditorSceneImporter {
@@ -188,4 +218,176 @@ public:
virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) override;
};
+#include "scene/resources/box_shape_3d.h"
+#include "scene/resources/capsule_shape_3d.h"
+#include "scene/resources/cylinder_shape_3d.h"
+#include "scene/resources/sphere_shape_3d.h"
+
+template <class M>
+Vector<Ref<Shape3D>> ResourceImporterScene::get_collision_shapes(const Ref<Mesh> &p_mesh, const M &p_options) {
+ ShapeType generate_shape_type = SHAPE_TYPE_DECOMPOSE_CONVEX;
+ if (p_options.has(SNAME("physics/shape_type"))) {
+ generate_shape_type = (ShapeType)p_options[SNAME("physics/shape_type")].operator int();
+ }
+
+ if (generate_shape_type == SHAPE_TYPE_DECOMPOSE_CONVEX) {
+ Mesh::ConvexDecompositionSettings decomposition_settings;
+ bool advanced = false;
+ if (p_options.has(SNAME("decomposition/advanced"))) {
+ advanced = p_options[SNAME("decomposition/advanced")];
+ }
+
+ if (advanced) {
+ if (p_options.has(SNAME("decomposition/max_concavity"))) {
+ decomposition_settings.max_concavity = p_options[SNAME("decomposition/max_concavity")];
+ }
+
+ if (p_options.has(SNAME("decomposition/symmetry_planes_clipping_bias"))) {
+ decomposition_settings.symmetry_planes_clipping_bias = p_options[SNAME("decomposition/symmetry_planes_clipping_bias")];
+ }
+
+ if (p_options.has(SNAME("decomposition/revolution_axes_clipping_bias"))) {
+ decomposition_settings.revolution_axes_clipping_bias = p_options[SNAME("decomposition/revolution_axes_clipping_bias")];
+ }
+
+ if (p_options.has(SNAME("decomposition/min_volume_per_convex_hull"))) {
+ decomposition_settings.min_volume_per_convex_hull = p_options[SNAME("decomposition/min_volume_per_convex_hull")];
+ }
+
+ if (p_options.has(SNAME("decomposition/resolution"))) {
+ decomposition_settings.resolution = p_options[SNAME("decomposition/resolution")];
+ }
+
+ if (p_options.has(SNAME("decomposition/max_num_vertices_per_convex_hull"))) {
+ decomposition_settings.max_num_vertices_per_convex_hull = p_options[SNAME("decomposition/max_num_vertices_per_convex_hull")];
+ }
+
+ if (p_options.has(SNAME("decomposition/plane_downsampling"))) {
+ decomposition_settings.plane_downsampling = p_options[SNAME("decomposition/plane_downsampling")];
+ }
+
+ if (p_options.has(SNAME("decomposition/convexhull_downsampling"))) {
+ decomposition_settings.convexhull_downsampling = p_options[SNAME("decomposition/convexhull_downsampling")];
+ }
+
+ if (p_options.has(SNAME("decomposition/normalize_mesh"))) {
+ decomposition_settings.normalize_mesh = p_options[SNAME("decomposition/normalize_mesh")];
+ }
+
+ if (p_options.has(SNAME("decomposition/mode"))) {
+ decomposition_settings.mode = (Mesh::ConvexDecompositionSettings::Mode)p_options[SNAME("decomposition/mode")].operator int();
+ }
+
+ if (p_options.has(SNAME("decomposition/convexhull_approximation"))) {
+ decomposition_settings.convexhull_approximation = p_options[SNAME("decomposition/convexhull_approximation")];
+ }
+
+ if (p_options.has(SNAME("decomposition/max_convex_hulls"))) {
+ decomposition_settings.max_convex_hulls = p_options[SNAME("decomposition/max_convex_hulls")];
+ }
+
+ if (p_options.has(SNAME("decomposition/project_hull_vertices"))) {
+ decomposition_settings.project_hull_vertices = p_options[SNAME("decomposition/project_hull_vertices")];
+ }
+ } else {
+ int precision_level = 5;
+ if (p_options.has(SNAME("decomposition/precision"))) {
+ precision_level = p_options[SNAME("decomposition/precision")];
+ }
+
+ const real_t precision = real_t(precision_level - 1) / 9.0;
+
+ decomposition_settings.max_concavity = Math::lerp(real_t(1.0), real_t(0.001), precision);
+ decomposition_settings.min_volume_per_convex_hull = Math::lerp(real_t(0.01), real_t(0.0001), precision);
+ decomposition_settings.resolution = Math::lerp(10'000, 100'000, precision);
+ decomposition_settings.max_num_vertices_per_convex_hull = Math::lerp(32, 64, precision);
+ decomposition_settings.plane_downsampling = Math::lerp(3, 16, precision);
+ decomposition_settings.convexhull_downsampling = Math::lerp(3, 16, precision);
+ decomposition_settings.max_convex_hulls = Math::lerp(1, 32, precision);
+ }
+
+ return p_mesh->convex_decompose(decomposition_settings);
+ } else if (generate_shape_type == SHAPE_TYPE_SIMPLE_CONVEX) {
+ Vector<Ref<Shape3D>> shapes;
+ shapes.push_back(p_mesh->create_convex_shape(true, /*Passing false, otherwise VHACD will be used to simplify (Decompose) the Mesh.*/ false));
+ return shapes;
+ } else if (generate_shape_type == SHAPE_TYPE_TRIMESH) {
+ Vector<Ref<Shape3D>> shapes;
+ shapes.push_back(p_mesh->create_trimesh_shape());
+ return shapes;
+ } else if (generate_shape_type == SHAPE_TYPE_BOX) {
+ Ref<BoxShape3D> box;
+ box.instantiate();
+ if (p_options.has(SNAME("primitive/size"))) {
+ box->set_size(p_options[SNAME("primitive/size")]);
+ }
+
+ Vector<Ref<Shape3D>> shapes;
+ shapes.push_back(box);
+ return shapes;
+
+ } else if (generate_shape_type == SHAPE_TYPE_SPHERE) {
+ Ref<SphereShape3D> sphere;
+ sphere.instantiate();
+ if (p_options.has(SNAME("primitive/radius"))) {
+ sphere->set_radius(p_options[SNAME("primitive/radius")]);
+ }
+
+ Vector<Ref<Shape3D>> shapes;
+ shapes.push_back(sphere);
+ return shapes;
+ } else if (generate_shape_type == SHAPE_TYPE_CYLINDER) {
+ Ref<CylinderShape3D> cylinder;
+ cylinder.instantiate();
+ if (p_options.has(SNAME("primitive/height"))) {
+ cylinder->set_height(p_options[SNAME("primitive/height")]);
+ }
+ if (p_options.has(SNAME("primitive/radius"))) {
+ cylinder->set_radius(p_options[SNAME("primitive/radius")]);
+ }
+
+ Vector<Ref<Shape3D>> shapes;
+ shapes.push_back(cylinder);
+ return shapes;
+ } else if (generate_shape_type == SHAPE_TYPE_CAPSULE) {
+ Ref<CapsuleShape3D> capsule;
+ capsule.instantiate();
+ if (p_options.has(SNAME("primitive/height"))) {
+ capsule->set_height(p_options[SNAME("primitive/height")]);
+ }
+ if (p_options.has(SNAME("primitive/radius"))) {
+ capsule->set_radius(p_options[SNAME("primitive/radius")]);
+ }
+
+ Vector<Ref<Shape3D>> shapes;
+ shapes.push_back(capsule);
+ return shapes;
+ }
+ return Vector<Ref<Shape3D>>();
+}
+
+template <class M>
+Transform3D ResourceImporterScene::get_collision_shapes_transform(const M &p_options) {
+ Transform3D transform;
+
+ ShapeType generate_shape_type = SHAPE_TYPE_DECOMPOSE_CONVEX;
+ if (p_options.has(SNAME("physics/shape_type"))) {
+ generate_shape_type = (ShapeType)p_options[SNAME("physics/shape_type")].operator int();
+ }
+
+ if (generate_shape_type == SHAPE_TYPE_BOX ||
+ generate_shape_type == SHAPE_TYPE_SPHERE ||
+ generate_shape_type == SHAPE_TYPE_CYLINDER ||
+ generate_shape_type == SHAPE_TYPE_CAPSULE) {
+ if (p_options.has(SNAME("primitive/position"))) {
+ transform.origin = p_options[SNAME("primitive/position")];
+ }
+
+ if (p_options.has(SNAME("primitive/rotation"))) {
+ transform.basis.set_euler((p_options[SNAME("primitive/rotation")].operator Vector3() / 180.0) * Math_PI);
+ }
+ }
+ return transform;
+}
+
#endif // RESOURCEIMPORTERSCENE_H
diff --git a/editor/import/resource_importer_shader_file.cpp b/editor/import/resource_importer_shader_file.cpp
index f4d20a6296..c01d8068da 100644
--- a/editor/import/resource_importer_shader_file.cpp
+++ b/editor/import/resource_importer_shader_file.cpp
@@ -30,9 +30,9 @@
#include "resource_importer_shader_file.h"
+#include "core/io/file_access.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
-#include "core/os/file_access.h"
#include "editor/editor_node.h"
#include "editor/plugins/shader_file_editor_plugin.h"
#include "servers/rendering/rendering_device_binds.h"
@@ -78,7 +78,7 @@ static String _include_function(const String &p_path, void *userpointer) {
String *base_path = (String *)userpointer;
String include = p_path;
- if (include.is_rel_path()) {
+ if (include.is_relative_path()) {
include = base_path->plus_file(include);
}
@@ -99,7 +99,7 @@ Error ResourceImporterShaderFile::import(const String &p_source_file, const Stri
String file_txt = file->get_as_utf8_string();
Ref<RDShaderFile> shader_file;
- shader_file.instance();
+ shader_file.instantiate();
String base_path = p_source_file.get_base_dir();
err = shader_file->parse_versions_from_text(file_txt, "", _include_function, &base_path);
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index de8031af35..61745cb6ee 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -88,7 +88,7 @@ void ResourceImporterTexture::update_imports() {
for (Map<StringName, MakeInfo>::Element *E = make_flags.front(); E; E = E->next()) {
Ref<ConfigFile> cf;
- cf.instance();
+ cf.instantiate();
String src_path = String(E->key()) + ".import";
Error err = cf->load(src_path);
@@ -208,7 +208,7 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options,
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "roughness/src_normal", PROPERTY_HINT_FILE, "*.bmp,*.dds,*.exr,*.jpeg,*.jpg,*.hdr,*.png,*.svg,*.svgz,*.tga,*.webp"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/fix_alpha_border"), p_preset != PRESET_3D));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/premult_alpha"), false));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/invert_color"), false));
+ 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::INT, "process/size_limit", PROPERTY_HINT_RANGE, "0,4096,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));
@@ -218,14 +218,21 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options,
void ResourceImporterTexture::save_to_stex_format(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: {
- f->store_32(StreamTexture2D::DATA_FORMAT_LOSSLESS);
+ bool lossless_force_png = ProjectSettings::get_singleton()->get("rendering/textures/lossless_compression/force_png");
+ 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 ? StreamTexture2D::DATA_FORMAT_WEBP : StreamTexture2D::DATA_FORMAT_PNG);
f->store_16(p_image->get_width());
f->store_16(p_image->get_height());
f->store_32(p_image->get_mipmap_count());
f->store_32(p_image->get_format());
for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
- Vector<uint8_t> data = Image::lossless_packer(p_image->get_image_from_mipmap(i));
+ Vector<uint8_t> data;
+ if (use_webp) {
+ data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i));
+ } else {
+ data = Image::png_packer(p_image->get_image_from_mipmap(i));
+ }
int data_len = data.size();
f->store_32(data_len);
@@ -235,14 +242,14 @@ void ResourceImporterTexture::save_to_stex_format(FileAccess *f, const Ref<Image
} break;
case COMPRESS_LOSSY: {
- f->store_32(StreamTexture2D::DATA_FORMAT_LOSSY);
+ f->store_32(StreamTexture2D::DATA_FORMAT_WEBP);
f->store_16(p_image->get_width());
f->store_16(p_image->get_height());
f->store_32(p_image->get_mipmap_count());
f->store_32(p_image->get_format());
for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) {
- Vector<uint8_t> data = Image::lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality);
+ Vector<uint8_t> data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality);
int data_len = data.size();
f->store_32(data_len);
@@ -301,6 +308,7 @@ void ResourceImporterTexture::save_to_stex_format(FileAccess *f, const Ref<Image
void ResourceImporterTexture::_save_stex(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) {
FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE);
+ ERR_FAIL_NULL(f);
f->store_8('G');
f->store_8('S');
f->store_8('T');
@@ -385,10 +393,10 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
float lossy = p_options["compress/lossy_quality"];
int pack_channels = p_options["compress/channel_pack"];
bool mipmaps = p_options["mipmaps/generate"];
- uint32_t mipmap_limit = int(mipmaps ? int(p_options["mipmaps/limit"]) : int(-1));
+ uint32_t mipmap_limit = mipmaps ? uint32_t(p_options["mipmaps/limit"]) : uint32_t(-1);
bool fix_alpha_border = p_options["process/fix_alpha_border"];
bool premult_alpha = p_options["process/premult_alpha"];
- bool invert_color = p_options["process/invert_color"];
+ bool normal_map_invert_y = p_options["process/normal_map_invert_y"];
bool stream = p_options["compress/streamed"];
int size_limit = p_options["process/size_limit"];
bool hdr_as_srgb = p_options["process/HDR_as_SRGB"];
@@ -403,13 +411,13 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
Image::RoughnessChannel roughness_channel = Image::ROUGHNESS_CHANNEL_R;
if (mipmaps && roughness > 1 && FileAccess::exists(normal_map)) {
- normal_image.instance();
+ normal_image.instantiate();
if (ImageLoader::load_image(normal_map, normal_image) == OK) {
roughness_channel = Image::RoughnessChannel(roughness - 2);
}
}
Ref<Image> image;
- image.instance();
+ image.instantiate();
Error err = ImageLoader::load_image(p_source_file, image, nullptr, hdr_as_srgb, scale);
if (err != OK) {
return err;
@@ -444,13 +452,18 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
image->premultiply_alpha();
}
- if (invert_color) {
- int height = image->get_height();
- int width = image->get_width();
+ if (normal_map_invert_y) {
+ // Inverting the green channel can be used to flip a normal map's direction.
+ // There's no standard when it comes to normal map Y direction, so this is
+ // sometimes needed when using a normal map exported from another program.
+ // See <http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates>.
+ const int height = image->get_height();
+ const int width = image->get_width();
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
- image->set_pixel(i, j, image->get_pixel(i, j).inverted());
+ const Color color = image->get_pixel(i, j);
+ image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b));
}
}
}
diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h
index 0d551a965c..41220009cd 100644
--- a/editor/import/resource_importer_texture.h
+++ b/editor/import/resource_importer_texture.h
@@ -31,9 +31,9 @@
#ifndef RESOURCEIMPORTTEXTURE_H
#define RESOURCEIMPORTTEXTURE_H
+#include "core/io/file_access.h"
#include "core/io/image.h"
#include "core/io/resource_importer.h"
-#include "core/os/file_access.h"
#include "scene/resources/texture.h"
#include "servers/rendering_server.h"
diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp
index 4c3ae59951..dec1466da1 100644
--- a/editor/import/resource_importer_texture_atlas.cpp
+++ b/editor/import/resource_importer_texture_atlas.cpp
@@ -31,10 +31,10 @@
#include "resource_importer_texture_atlas.h"
#include "atlas_import_failed.xpm"
+#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 "core/os/file_access.h"
#include "editor/editor_atlas_packer.h"
#include "scene/resources/mesh.h"
#include "scene/resources/texture.h"
@@ -87,7 +87,7 @@ Error ResourceImporterTextureAtlas::import(const String &p_source_file, const St
//it's a simple hack
Ref<Image> broken = memnew(Image((const char **)atlas_import_failed_xpm));
Ref<ImageTexture> broken_texture;
- broken_texture.instance();
+ broken_texture.instantiate();
broken_texture->create_from_image(broken);
String target_file = p_save_path + ".tex";
@@ -201,7 +201,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file
const Map<StringName, Variant> &options = E->get();
Ref<Image> image;
- image.instance();
+ image.instantiate();
Error err = ImageLoader::load_image(source, image);
ERR_CONTINUE(err != OK);
@@ -240,7 +240,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file
pack_data.is_mesh = true;
Ref<BitMap> bit_map;
- bit_map.instance();
+ bit_map.instantiate();
bit_map->create_from_image_alpha(image);
Vector<Vector<Vector2>> polygons = bit_map->clip_opaque_to_polygons(Rect2(0, 0, image->get_width(), image->get_height()));
@@ -272,7 +272,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file
//blit the atlas
Ref<Image> new_atlas;
- new_atlas.instance();
+ new_atlas.instantiate();
new_atlas->create(atlas_width, atlas_height, false, Image::FORMAT_RGBA8);
for (int i = 0; i < pack_data_files.size(); i++) {
@@ -303,7 +303,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file
cache.reference_ptr(resptr);
} else {
Ref<ImageTexture> res_cache;
- res_cache.instance();
+ res_cache.instantiate();
res_cache->create_from_image(new_atlas);
res_cache->set_path(p_group_file);
cache = res_cache;
@@ -321,7 +321,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file
//region
Ref<AtlasTexture> atlas_texture;
- atlas_texture.instance();
+ 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));
@@ -329,7 +329,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file
texture = atlas_texture;
} else {
Ref<ArrayMesh> mesh;
- mesh.instance();
+ mesh.instantiate();
for (int i = 0; i < pack_data.chart_pieces.size(); i++) {
const EditorAtlasPacker::Chart &chart = charts[pack_data.chart_pieces[i]];
@@ -375,7 +375,7 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file
}
Ref<MeshTexture> mesh_texture;
- mesh_texture.instance();
+ mesh_texture.instantiate();
mesh_texture->set_base_texture(cache);
mesh_texture->set_image_size(pack_data.image->get_size());
mesh_texture->set_mesh(mesh);
diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp
index bcc55b330b..2db1db9e51 100644
--- a/editor/import/resource_importer_wav.cpp
+++ b/editor/import/resource_importer_wav.cpp
@@ -30,9 +30,9 @@
#include "resource_importer_wav.h"
+#include "core/io/file_access.h"
#include "core/io/marshalls.h"
#include "core/io/resource_saver.h"
-#include "core/os/file_access.h"
#include "scene/resources/audio_stream_sample.h"
const float TRIM_DB_LIMIT = -50;
@@ -78,7 +78,7 @@ void ResourceImporterWAV::get_import_options(List<ImportOption> *r_options, int
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force/8_bit"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force/mono"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force/max_rate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
- r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "force/max_rate_hz", PROPERTY_HINT_EXP_RANGE, "11025,192000,1"), 44100));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "force/max_rate_hz", PROPERTY_HINT_RANGE, "11025,192000,1,exp"), 44100));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "edit/trim"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "edit/normalize"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "edit/loop"), false));
@@ -508,7 +508,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
}
Ref<AudioStreamSample> sample;
- sample.instance();
+ sample.instantiate();
sample->set_data(dst_data);
sample->set_format(dst_format);
sample->set_mix_rate(rate);
diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp
index 488c124c28..4bcb6863fb 100644
--- a/editor/import/scene_import_settings.cpp
+++ b/editor/import/scene_import_settings.cpp
@@ -53,6 +53,11 @@ class SceneImportSettingsData : public Object {
}
current[p_name] = p_value;
+
+ if (ResourceImporterScene::get_singleton()->get_internal_option_update_view_required(category, p_name, current)) {
+ SceneImportSettings::get_singleton()->update_view();
+ }
+
return true;
}
return false;
@@ -71,9 +76,9 @@ class SceneImportSettingsData : public Object {
return false;
}
void _get_property_list(List<PropertyInfo> *p_list) const {
- for (const List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) {
- if (ResourceImporterScene::get_singleton()->get_internal_option_visibility(category, E->get().option.name, current)) {
- p_list->push_back(E->get().option);
+ for (const ResourceImporter::ImportOption &E : options) {
+ if (ResourceImporterScene::get_singleton()->get_internal_option_visibility(category, E.option.name, current)) {
+ p_list->push_back(E.option);
}
}
}
@@ -105,7 +110,7 @@ void SceneImportSettings::_fill_material(Tree *p_tree, const Ref<Material> &p_ma
MaterialData &material_data = material_map[import_id];
- Ref<Texture2D> icon = get_theme_icon("StandardMaterial3D", "EditorIcons");
+ Ref<Texture2D> icon = get_theme_icon(SNAME("StandardMaterial3D"), SNAME("EditorIcons"));
TreeItem *item = p_tree->create_item(p_parent);
item->set_text(0, p_material->get_name());
@@ -161,7 +166,7 @@ void SceneImportSettings::_fill_mesh(Tree *p_tree, const Ref<Mesh> &p_mesh, Tree
MeshData &mesh_data = mesh_map[import_id];
- Ref<Texture2D> icon = get_theme_icon("Mesh", "EditorIcons");
+ Ref<Texture2D> icon = get_theme_icon(SNAME("Mesh"), SNAME("EditorIcons"));
TreeItem *item = p_tree->create_item(p_parent);
item->set_text(0, p_mesh->get_name());
@@ -211,7 +216,7 @@ void SceneImportSettings::_fill_animation(Tree *p_tree, const Ref<Animation> &p_
AnimationData &animation_data = animation_map[p_name];
- Ref<Texture2D> icon = get_theme_icon("Animation", "EditorIcons");
+ Ref<Texture2D> icon = get_theme_icon(SNAME("Animation"), SNAME("EditorIcons"));
TreeItem *item = p_tree->create_item(p_parent);
item->set_text(0, p_name);
@@ -255,17 +260,17 @@ void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) {
String type = p_node->get_class();
- if (!has_theme_icon(type, "EditorIcons")) {
+ if (!has_theme_icon(type, SNAME("EditorIcons"))) {
type = "Node3D";
}
- Ref<Texture2D> icon = get_theme_icon(type, "EditorIcons");
+ Ref<Texture2D> icon = get_theme_icon(type, SNAME("EditorIcons"));
TreeItem *item = scene_tree->create_item(p_parent_item);
item->set_text(0, p_node->get_name());
if (p_node == scene) {
- icon = get_theme_icon("PackedScene", "EditorIcons");
+ icon = get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
item->set_text(0, "Scene");
}
@@ -305,8 +310,8 @@ void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) {
if (anim_node) {
List<StringName> animations;
anim_node->get_animation_list(&animations);
- for (List<StringName>::Element *E = animations.front(); E; E = E->next()) {
- _fill_animation(scene_tree, anim_node->get_animation(E->get()), E->get(), item);
+ for (const StringName &E : animations) {
+ _fill_animation(scene_tree, anim_node->get_animation(E), E, item);
}
}
@@ -317,7 +322,14 @@ void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) {
if (mesh_node && mesh_node->get_mesh().is_valid()) {
_fill_mesh(scene_tree, mesh_node->get_mesh(), item);
- Transform accum_xform;
+ // Add the collider view.
+ MeshInstance3D *collider_view = memnew(MeshInstance3D);
+ collider_view->set_name("collider_view");
+ collider_view->set_visible(false);
+ mesh_node->add_child(collider_view);
+ collider_view->set_owner(mesh_node);
+
+ Transform3D accum_xform;
Node3D *base = mesh_node;
while (base) {
accum_xform = base->get_transform() * accum_xform;
@@ -346,6 +358,54 @@ void SceneImportSettings::_update_scene() {
_fill_scene(scene, nullptr);
}
+void SceneImportSettings::_update_view_gizmos() {
+ for (const KeyValue<String, NodeData> &e : node_map) {
+ bool generate_collider = false;
+ if (e.value.settings.has(SNAME("generate/physics"))) {
+ generate_collider = e.value.settings[SNAME("generate/physics")];
+ }
+
+ MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(e.value.node);
+ if (mesh_node == nullptr || mesh_node->get_mesh().is_null()) {
+ // Nothing to do
+ continue;
+ }
+
+ MeshInstance3D *collider_view = static_cast<MeshInstance3D *>(mesh_node->find_node("collider_view"));
+ CRASH_COND_MSG(collider_view == nullptr, "This is unreachable, since the collider view is always created even when the collision is not used! If this is triggered there is a bug on the function `_fill_scene`.");
+
+ collider_view->set_visible(generate_collider);
+ if (generate_collider) {
+ // This collider_view doesn't have a mesh so we need to generate a new one.
+
+ // Generate the mesh collider.
+ Vector<Ref<Shape3D>> shapes = ResourceImporterScene::get_collision_shapes(mesh_node->get_mesh(), e.value.settings);
+ const Transform3D transform = ResourceImporterScene::get_collision_shapes_transform(e.value.settings);
+
+ Ref<ArrayMesh> collider_view_mesh;
+ collider_view_mesh.instantiate();
+ for (Ref<Shape3D> shape : shapes) {
+ Ref<ArrayMesh> debug_shape_mesh;
+ if (shape.is_valid()) {
+ debug_shape_mesh = shape->get_debug_mesh();
+ }
+ if (debug_shape_mesh.is_valid()) {
+ collider_view_mesh->add_surface_from_arrays(
+ debug_shape_mesh->surface_get_primitive_type(0),
+ debug_shape_mesh->surface_get_arrays(0));
+
+ collider_view_mesh->surface_set_material(
+ collider_view_mesh->get_surface_count() - 1,
+ collider_mat);
+ }
+ }
+
+ collider_view->set_mesh(collider_view_mesh);
+ collider_view->set_transform(transform);
+ }
+ }
+}
+
void SceneImportSettings::_update_camera() {
AABB camera_aabb;
@@ -379,7 +439,7 @@ void SceneImportSettings::_update_camera() {
camera->set_orthogonal(camera_size * zoom, 0.0001, camera_size * 2);
- Transform xf;
+ Transform3D xf;
xf.basis = Basis(Vector3(0, 1, 0), rot_y) * Basis(Vector3(1, 0, 0), rot_x);
xf.origin = center;
xf.translate(0, 0, camera_size);
@@ -394,8 +454,8 @@ void SceneImportSettings::_load_default_subresource_settings(Map<StringName, Var
d = d[p_import_id];
List<ResourceImporterScene::ImportOption> options;
ResourceImporterScene::get_singleton()->get_internal_import_options(p_category, &options);
- for (List<ResourceImporterScene::ImportOption>::Element *E = options.front(); E; E = E->next()) {
- String key = E->get().option.name;
+ for (const ResourceImporterScene::ImportOption &E : options) {
+ String key = E.option.name;
if (d.has(key)) {
settings[key] = d[key];
}
@@ -404,11 +464,16 @@ void SceneImportSettings::_load_default_subresource_settings(Map<StringName, Var
}
}
+void SceneImportSettings::update_view() {
+ _update_view_gizmos();
+}
+
void SceneImportSettings::open_settings(const String &p_path) {
if (scene) {
memdelete(scene);
scene = nullptr;
}
+ scene_import_settings_data->settings = nullptr;
scene = ResourceImporterScene::get_singleton()->pre_import(p_path);
if (scene == nullptr) {
EditorNode::get_singleton()->show_warning(TTR("Error opening scene"));
@@ -435,17 +500,17 @@ void SceneImportSettings::open_settings(const String &p_path) {
base_subresource_settings.clear();
Ref<ConfigFile> config;
- config.instance();
+ config.instantiate();
Error err = config->load(p_path + ".import");
if (err == OK) {
List<String> keys;
config->get_section_keys("params", &keys);
- for (List<String>::Element *E = keys.front(); E; E = E->next()) {
- Variant value = config->get_value("params", E->get());
- if (E->get() == "_subresources") {
+ for (const String &E : keys) {
+ Variant value = config->get_value("params", E);
+ if (E == "_subresources") {
base_subresource_settings = value;
} else {
- defaults[E->get()] = value;
+ defaults[E] = value;
}
}
}
@@ -463,6 +528,7 @@ void SceneImportSettings::open_settings(const String &p_path) {
}
popup_centered_ratio();
+ _update_view_gizmos();
_update_camera();
set_title(vformat(TTR("Advanced Import Settings for '%s'"), base_path.get_file()));
@@ -493,7 +559,7 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
Ref<Mesh> base_mesh = mi->get_mesh();
if (base_mesh.is_valid()) {
AABB aabb = base_mesh->get_aabb();
- Transform aabb_xf;
+ Transform3D aabb_xf;
aabb_xf.basis.scale(aabb.size);
aabb_xf.origin = aabb.position;
@@ -605,13 +671,13 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
scene_import_settings_data->defaults.clear();
scene_import_settings_data->current.clear();
- for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) {
- scene_import_settings_data->defaults[E->get().option.name] = E->get().default_value;
+ for (const ResourceImporter::ImportOption &E : options) {
+ scene_import_settings_data->defaults[E.option.name] = E.default_value;
//needed for visibility toggling (fails if something is missing)
- if (scene_import_settings_data->settings->has(E->get().option.name)) {
- scene_import_settings_data->current[E->get().option.name] = (*scene_import_settings_data->settings)[E->get().option.name];
+ if (scene_import_settings_data->settings->has(E.option.name)) {
+ scene_import_settings_data->current[E.option.name] = (*scene_import_settings_data->settings)[E.option.name];
} else {
- scene_import_settings_data->current[E->get().option.name] = E->get().default_value;
+ scene_import_settings_data->current[E.option.name] = E.default_value;
}
}
scene_import_settings_data->options = options;
@@ -629,6 +695,7 @@ void SceneImportSettings::_material_tree_selected() {
_select(material_tree, type, import_id);
}
+
void SceneImportSettings::_mesh_tree_selected() {
if (selecting) {
return;
@@ -640,6 +707,7 @@ void SceneImportSettings::_mesh_tree_selected() {
_select(mesh_tree, type, import_id);
}
+
void SceneImportSettings::_scene_tree_selected() {
if (selecting) {
return;
@@ -795,11 +863,11 @@ void SceneImportSettings::_save_path_changed(const String &p_path) {
if (FileAccess::exists(p_path)) {
save_path_item->set_text(2, "Warning: File exists");
save_path_item->set_tooltip(2, TTR("Existing file with the same name will be replaced."));
- save_path_item->set_icon(2, get_theme_icon("StatusWarning", "EditorIcons"));
+ save_path_item->set_icon(2, get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
} else {
save_path_item->set_text(2, "Will create new File");
- save_path_item->set_icon(2, get_theme_icon("StatusSuccess", "EditorIcons"));
+ save_path_item->set_icon(2, get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")));
}
}
@@ -829,7 +897,7 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
String name = md.material_node->get_text(0);
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
- item->set_icon(0, get_theme_icon("StandardMaterial3D", "EditorIcons"));
+ item->set_icon(0, get_theme_icon(SNAME("StandardMaterial3D"), SNAME("EditorIcons")));
item->set_text(0, name);
if (md.has_import_id) {
@@ -851,20 +919,20 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
if (FileAccess::exists(path)) {
item->set_text(2, "Warning: File exists");
item->set_tooltip(2, TTR("Existing file with the same name will be replaced."));
- item->set_icon(2, get_theme_icon("StatusWarning", "EditorIcons"));
+ item->set_icon(2, get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
} else {
item->set_text(2, "Will create new File");
- item->set_icon(2, get_theme_icon("StatusSuccess", "EditorIcons"));
+ item->set_icon(2, get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")));
}
- item->add_button(1, get_theme_icon("Folder", "EditorIcons"));
+ item->add_button(1, get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
}
} else {
item->set_text(2, "No import ID");
item->set_tooltip(2, TTR("Material has no name nor any other way to identify on re-import.\nPlease name it or ensure it is exported with an unique ID."));
- item->set_icon(2, get_theme_icon("StatusError", "EditorIcons"));
+ item->set_icon(2, get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons")));
}
save_path_items.push_back(item);
@@ -882,7 +950,7 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
String name = md.mesh_node->get_text(0);
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
- item->set_icon(0, get_theme_icon("Mesh", "EditorIcons"));
+ item->set_icon(0, get_theme_icon(SNAME("Mesh"), SNAME("EditorIcons")));
item->set_text(0, name);
if (md.has_import_id) {
@@ -904,20 +972,20 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
if (FileAccess::exists(path)) {
item->set_text(2, "Warning: File exists");
item->set_tooltip(2, TTR("Existing file with the same name will be replaced on import."));
- item->set_icon(2, get_theme_icon("StatusWarning", "EditorIcons"));
+ item->set_icon(2, get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
} else {
item->set_text(2, "Will save to new File");
- item->set_icon(2, get_theme_icon("StatusSuccess", "EditorIcons"));
+ item->set_icon(2, get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")));
}
- item->add_button(1, get_theme_icon("Folder", "EditorIcons"));
+ item->add_button(1, get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
}
} else {
item->set_text(2, "No import ID");
item->set_tooltip(2, TTR("Mesh has no name nor any other way to identify on re-import.\nPlease name it or ensure it is exported with an unique ID."));
- item->set_icon(2, get_theme_icon("StatusError", "EditorIcons"));
+ item->set_icon(2, get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons")));
}
save_path_items.push_back(item);
@@ -935,7 +1003,7 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
String name = ad.scene_node->get_text(0);
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
- item->set_icon(0, get_theme_icon("Animation", "EditorIcons"));
+ item->set_icon(0, get_theme_icon(SNAME("Animation"), SNAME("EditorIcons")));
item->set_text(0, name);
if (ad.settings.has("save_to_file/enabled") && bool(ad.settings["save_to_file/enabled"])) {
@@ -956,14 +1024,14 @@ void SceneImportSettings::_save_dir_callback(const String &p_path) {
if (FileAccess::exists(path)) {
item->set_text(2, "Warning: File exists");
item->set_tooltip(2, TTR("Existing file with the same name will be replaced on import."));
- item->set_icon(2, get_theme_icon("StatusWarning", "EditorIcons"));
+ item->set_icon(2, get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")));
} else {
item->set_text(2, "Will save to new File");
- item->set_icon(2, get_theme_icon("StatusSuccess", "EditorIcons"));
+ item->set_icon(2, get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")));
}
- item->add_button(1, get_theme_icon("Folder", "EditorIcons"));
+ item->add_button(1, get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
}
save_path_items.push_back(item);
@@ -1099,18 +1167,18 @@ SceneImportSettings::SceneImportSettings() {
camera->make_current();
light = memnew(DirectionalLight3D);
- light->set_transform(Transform().looking_at(Vector3(-1, -2, -0.6), Vector3(0, 1, 0)));
+ light->set_transform(Transform3D().looking_at(Vector3(-1, -2, -0.6), Vector3(0, 1, 0)));
base_viewport->add_child(light);
light->set_shadow(true);
{
Ref<StandardMaterial3D> selection_mat;
- selection_mat.instance();
+ selection_mat.instantiate();
selection_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
selection_mat->set_albedo(Color(1, 0.8, 1.0));
Ref<SurfaceTool> st;
- st.instance();
+ st.instantiate();
st->begin(Mesh::PRIMITIVE_LINES);
AABB base_aabb;
@@ -1126,7 +1194,7 @@ SceneImportSettings::SceneImportSettings() {
st->add_vertex(b.lerp(a, 0.2));
}
- selection_mesh.instance();
+ selection_mesh.instantiate();
st->commit(selection_mesh);
selection_mesh->surface_set_material(0, selection_mat);
@@ -1141,7 +1209,13 @@ SceneImportSettings::SceneImportSettings() {
base_viewport->add_child(mesh_preview);
mesh_preview->hide();
- material_preview.instance();
+ material_preview.instantiate();
+ }
+
+ {
+ collider_mat.instantiate();
+ collider_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ collider_mat->set_albedo(Color(0.5, 0.5, 1.0));
}
inspector = memnew(EditorInspector);
@@ -1163,13 +1237,13 @@ SceneImportSettings::SceneImportSettings() {
external_path_tree->set_columns(3);
external_path_tree->set_column_titles_visible(true);
external_path_tree->set_column_expand(0, true);
- external_path_tree->set_column_min_width(0, 100 * EDSCALE);
+ external_path_tree->set_column_custom_minimum_width(0, 100 * EDSCALE);
external_path_tree->set_column_title(0, TTR("Resource"));
external_path_tree->set_column_expand(1, true);
- external_path_tree->set_column_min_width(1, 100 * EDSCALE);
+ external_path_tree->set_column_custom_minimum_width(1, 100 * EDSCALE);
external_path_tree->set_column_title(1, TTR("Path"));
external_path_tree->set_column_expand(2, false);
- external_path_tree->set_column_min_width(2, 200 * EDSCALE);
+ external_path_tree->set_column_custom_minimum_width(2, 200 * EDSCALE);
external_path_tree->set_column_title(2, TTR("Status"));
save_path = memnew(EditorFileDialog);
save_path->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
diff --git a/editor/import/scene_import_settings.h b/editor/import/scene_import_settings.h
index ddcf4a6d5d..c7c94af493 100644
--- a/editor/import/scene_import_settings.h
+++ b/editor/import/scene_import_settings.h
@@ -84,6 +84,8 @@ class SceneImportSettings : public ConfirmationDialog {
MeshInstance3D *mesh_preview;
Ref<SphereMesh> material_preview;
+ Ref<StandardMaterial3D> collider_mat;
+
float cam_rot_x;
float cam_rot_y;
float cam_zoom;
@@ -145,6 +147,7 @@ class SceneImportSettings : public ConfirmationDialog {
bool selecting = false;
+ void _update_view_gizmos();
void _update_camera();
void _select(Tree *p_from, String p_type, String p_id);
void _material_tree_selected();
@@ -190,6 +193,7 @@ protected:
void _notification(int p_what);
public:
+ void update_view();
void open_settings(const String &p_path);
static SceneImportSettings *get_singleton();
SceneImportSettings();
diff --git a/editor/import/scene_importer_mesh.cpp b/editor/import/scene_importer_mesh.cpp
index fa1a027a8d..5e6dd08e79 100644
--- a/editor/import/scene_importer_mesh.cpp
+++ b/editor/import/scene_importer_mesh.cpp
@@ -33,6 +33,8 @@
#include "core/math/math_defs.h"
#include "scene/resources/surface_tool.h"
+#include <cstdint>
+
void EditorSceneImporterMesh::add_blend_shape(const String &p_name) {
ERR_FAIL_COND(surfaces.size() > 0);
blend_shapes.push_back(p_name);
@@ -55,13 +57,14 @@ Mesh::BlendShapeMode EditorSceneImporterMesh::get_blend_shape_mode() const {
return blend_shape_mode;
}
-void EditorSceneImporterMesh::add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, const Dictionary &p_lods, const Ref<Material> &p_material, const String &p_name) {
+void EditorSceneImporterMesh::add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, const Dictionary &p_lods, const Ref<Material> &p_material, const String &p_name, const uint32_t p_flags) {
ERR_FAIL_COND(p_blend_shapes.size() != blend_shapes.size());
ERR_FAIL_COND(p_arrays.size() != Mesh::ARRAY_MAX);
Surface s;
s.primitive = p_primitive;
s.arrays = p_arrays;
s.name = p_name;
+ s.flags = p_flags;
Vector<Vector3> vertex_array = p_arrays[Mesh::ARRAY_VERTEX];
int vertex_count = vertex_array.size();
@@ -79,11 +82,11 @@ void EditorSceneImporterMesh::add_surface(Mesh::PrimitiveType p_primitive, const
List<Variant> lods;
p_lods.get_key_list(&lods);
- for (List<Variant>::Element *E = lods.front(); E; E = E->next()) {
- ERR_CONTINUE(!E->get().is_num());
+ for (const Variant &E : lods) {
+ ERR_CONTINUE(!E.is_num());
Surface::LOD lod;
- lod.distance = E->get();
- lod.indices = p_lods[E->get()];
+ lod.distance = E;
+ lod.indices = p_lods[E];
ERR_CONTINUE(lod.indices.size() == 0);
s.lods.push_back(lod);
}
@@ -110,6 +113,12 @@ String EditorSceneImporterMesh::get_surface_name(int p_surface) const {
ERR_FAIL_INDEX_V(p_surface, surfaces.size(), String());
return surfaces[p_surface].name;
}
+void EditorSceneImporterMesh::set_surface_name(int p_surface, const String &p_name) {
+ ERR_FAIL_INDEX(p_surface, surfaces.size());
+ surfaces.write[p_surface].name = p_name;
+ mesh.unref();
+}
+
Array EditorSceneImporterMesh::get_surface_blend_shape_arrays(int p_surface, int p_blend_shape) const {
ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Array());
ERR_FAIL_INDEX_V(p_blend_shape, surfaces[p_surface].blend_shape_data.size(), Array());
@@ -132,6 +141,11 @@ float EditorSceneImporterMesh::get_surface_lod_size(int p_surface, int p_lod) co
return surfaces[p_surface].lods[p_lod].distance;
}
+uint32_t EditorSceneImporterMesh::get_surface_format(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0);
+ return surfaces[p_surface].flags;
+}
+
Ref<Material> EditorSceneImporterMesh::get_surface_material(int p_surface) const {
ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Ref<Material>());
return surfaces[p_surface].material;
@@ -140,6 +154,7 @@ Ref<Material> EditorSceneImporterMesh::get_surface_material(int p_surface) const
void EditorSceneImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_material) {
ERR_FAIL_INDEX(p_surface, surfaces.size());
surfaces.write[p_surface].material = p_material;
+ mesh.unref();
}
Basis EditorSceneImporterMesh::compute_rotation_matrix_from_ortho_6d(Vector3 p_x_raw, Vector3 p_y_raw) {
@@ -215,6 +230,7 @@ void EditorSceneImporterMesh::generate_lods() {
int index_target = indices.size() * threshold;
float max_mesh_error_percentage = 1e0f;
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());
@@ -223,8 +239,8 @@ void EditorSceneImporterMesh::generate_lods() {
break;
}
Surface::LOD lod;
- lod.distance = mesh_error;
- if (Math::is_equal_approx(mesh_error, 0.0f)) {
+ lod.distance = mesh_error * scale;
+ if (Math::is_zero_approx(mesh_error)) {
break;
}
if (new_len <= 0) {
@@ -243,7 +259,7 @@ bool EditorSceneImporterMesh::has_mesh() const {
return mesh.is_valid();
}
-Ref<ArrayMesh> EditorSceneImporterMesh::get_mesh(const Ref<Mesh> &p_base) {
+Ref<ArrayMesh> EditorSceneImporterMesh::get_mesh(const Ref<ArrayMesh> &p_base) {
ERR_FAIL_COND_V(surfaces.size() == 0, Ref<ArrayMesh>());
if (mesh.is_null()) {
@@ -251,7 +267,7 @@ Ref<ArrayMesh> EditorSceneImporterMesh::get_mesh(const Ref<Mesh> &p_base) {
mesh = p_base;
}
if (mesh.is_null()) {
- mesh.instance();
+ mesh.instantiate();
}
mesh->set_name(get_name());
if (has_meta("import_id")) {
@@ -275,7 +291,7 @@ Ref<ArrayMesh> EditorSceneImporterMesh::get_mesh(const Ref<Mesh> &p_base) {
}
}
- mesh->add_surface_from_arrays(surfaces[i].primitive, surfaces[i].arrays, bs_data, lods);
+ mesh->add_surface_from_arrays(surfaces[i].primitive, surfaces[i].arrays, bs_data, lods, surfaces[i].flags);
if (surfaces[i].material.is_valid()) {
mesh->surface_set_material(mesh->get_surface_count() - 1, surfaces[i].material);
}
@@ -320,7 +336,7 @@ void EditorSceneImporterMesh::create_shadow_mesh() {
}
}
- shadow_mesh.instance();
+ shadow_mesh.instantiate();
for (int i = 0; i < surfaces.size(); i++) {
LocalVector<int> vertex_remap;
@@ -390,7 +406,7 @@ void EditorSceneImporterMesh::create_shadow_mesh() {
}
}
- shadow_mesh->add_surface(surfaces[i].primitive, new_surface, Array(), lods, Ref<Material>(), surfaces[i].name);
+ shadow_mesh->add_surface(surfaces[i].primitive, new_surface, Array(), lods, Ref<Material>(), surfaces[i].name, surfaces[i].flags);
}
}
@@ -428,7 +444,11 @@ void EditorSceneImporterMesh::_set_data(const Dictionary &p_data) {
if (s.has("material")) {
material = s["material"];
}
- add_surface(prim, arr, blend_shapes, lods, material, name);
+ uint32_t flags = 0;
+ if (s.has("flags")) {
+ flags = s["flags"];
+ }
+ add_surface(prim, arr, blend_shapes, lods, material, name, flags);
}
}
}
@@ -465,6 +485,10 @@ Dictionary EditorSceneImporterMesh::_get_data() const {
d["name"] = surfaces[i].name;
}
+ if (surfaces[i].flags != 0) {
+ d["flags"] = surfaces[i].flags;
+ }
+
surface_arr.push_back(d);
}
data["surfaces"] = surface_arr;
@@ -500,36 +524,47 @@ Vector<Face3> EditorSceneImporterMesh::get_faces() const {
return faces;
}
-Vector<Ref<Shape3D>> EditorSceneImporterMesh::convex_decompose() const {
- ERR_FAIL_COND_V(!Mesh::convex_composition_function, Vector<Ref<Shape3D>>());
+Vector<Ref<Shape3D>> EditorSceneImporterMesh::convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const {
+ ERR_FAIL_COND_V(!Mesh::convex_decomposition_function, Vector<Ref<Shape3D>>());
const Vector<Face3> faces = get_faces();
+ int face_count = faces.size();
+
+ Vector<Vector3> vertices;
+ uint32_t vertex_count = 0;
+ vertices.resize(face_count * 3);
+ Vector<uint32_t> indices;
+ indices.resize(face_count * 3);
+ {
+ Map<Vector3, uint32_t> vertex_map;
+ Vector3 *vertex_w = vertices.ptrw();
+ uint32_t *index_w = indices.ptrw();
+ for (int i = 0; i < face_count; i++) {
+ for (int j = 0; j < 3; j++) {
+ const Vector3 &vertex = faces[i].vertex[j];
+ Map<Vector3, uint32_t>::Element *found_vertex = vertex_map.find(vertex);
+ uint32_t index;
+ if (found_vertex) {
+ index = found_vertex->get();
+ } else {
+ index = ++vertex_count;
+ vertex_map[vertex] = index;
+ vertex_w[index] = vertex;
+ }
+ index_w[i * 3 + j] = index;
+ }
+ }
+ }
+ vertices.resize(vertex_count);
- Vector<Vector<Face3>> decomposed = Mesh::convex_composition_function(faces);
+ Vector<Vector<Vector3>> decomposed = Mesh::convex_decomposition_function((real_t *)vertices.ptr(), vertex_count, indices.ptr(), face_count, p_settings, nullptr);
Vector<Ref<Shape3D>> ret;
for (int i = 0; i < decomposed.size(); i++) {
- Set<Vector3> points;
- for (int j = 0; j < decomposed[i].size(); j++) {
- points.insert(decomposed[i][j].vertex[0]);
- points.insert(decomposed[i][j].vertex[1]);
- points.insert(decomposed[i][j].vertex[2]);
- }
-
- Vector<Vector3> convex_points;
- convex_points.resize(points.size());
- {
- Vector3 *w = convex_points.ptrw();
- int idx = 0;
- for (Set<Vector3>::Element *E = points.front(); E; E = E->next()) {
- w[idx++] = E->get();
- }
- }
-
Ref<ConvexPolygonShape3D> shape;
- shape.instance();
- shape->set_points(convex_points);
+ shape.instantiate();
+ shape->set_points(decomposed[i]);
ret.push_back(shape);
}
@@ -587,7 +622,7 @@ Ref<NavigationMesh> EditorSceneImporterMesh::create_navigation_mesh() {
}
Ref<NavigationMesh> nm;
- nm.instance();
+ nm.instantiate();
nm->set_vertices(vertices);
Vector<int> v3;
@@ -612,7 +647,7 @@ struct EditorSceneImporterMeshLightmapSurface {
String name;
};
-Error EditorSceneImporterMesh::lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache) {
+Error EditorSceneImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache) {
ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED);
ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes.");
@@ -628,7 +663,7 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(const Transform &p_base_tr
Basis basis = p_base_transform.get_basis();
Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length());
- Transform transform;
+ Transform3D transform;
transform.scale(scale);
Basis normal_basis = transform.basis.inverse().transposed();
@@ -733,7 +768,7 @@ Error EditorSceneImporterMesh::lightmap_unwrap_cached(const Transform &p_base_tr
for (int i = 0; i < lightmap_surfaces.size(); i++) {
Ref<SurfaceTool> st;
- st.instance();
+ st.instantiate();
st->begin(Mesh::PRIMITIVE_TRIANGLES);
st->set_material(lightmap_surfaces[i].material);
st->set_meta("name", lightmap_surfaces[i].name);
@@ -825,7 +860,7 @@ void EditorSceneImporterMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &EditorSceneImporterMesh::set_blend_shape_mode);
ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &EditorSceneImporterMesh::get_blend_shape_mode);
- ClassDB::bind_method(D_METHOD("add_surface", "primitive", "arrays", "blend_shapes", "lods", "material", "name"), &EditorSceneImporterMesh::add_surface, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(Ref<Material>()), DEFVAL(String()));
+ ClassDB::bind_method(D_METHOD("add_surface", "primitive", "arrays", "blend_shapes", "lods", "material", "name", "flags"), &EditorSceneImporterMesh::add_surface, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(Ref<Material>()), DEFVAL(String()), DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_surface_count"), &EditorSceneImporterMesh::get_surface_count);
ClassDB::bind_method(D_METHOD("get_surface_primitive_type", "surface_idx"), &EditorSceneImporterMesh::get_surface_primitive_type);
@@ -836,8 +871,12 @@ void EditorSceneImporterMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_surface_lod_size", "surface_idx", "lod_idx"), &EditorSceneImporterMesh::get_surface_lod_size);
ClassDB::bind_method(D_METHOD("get_surface_lod_indices", "surface_idx", "lod_idx"), &EditorSceneImporterMesh::get_surface_lod_indices);
ClassDB::bind_method(D_METHOD("get_surface_material", "surface_idx"), &EditorSceneImporterMesh::get_surface_material);
+ ClassDB::bind_method(D_METHOD("get_surface_format", "surface_idx"), &EditorSceneImporterMesh::get_surface_format);
+
+ ClassDB::bind_method(D_METHOD("set_surface_name", "surface_idx", "name"), &EditorSceneImporterMesh::set_surface_name);
+ ClassDB::bind_method(D_METHOD("set_surface_material", "surface_idx", "material"), &EditorSceneImporterMesh::set_surface_material);
- ClassDB::bind_method(D_METHOD("get_mesh"), &EditorSceneImporterMesh::get_mesh);
+ ClassDB::bind_method(D_METHOD("get_mesh", "base_mesh"), &EditorSceneImporterMesh::get_mesh, DEFVAL(Ref<ArrayMesh>()));
ClassDB::bind_method(D_METHOD("clear"), &EditorSceneImporterMesh::clear);
ClassDB::bind_method(D_METHOD("_set_data", "data"), &EditorSceneImporterMesh::_set_data);
diff --git a/editor/import/scene_importer_mesh.h b/editor/import/scene_importer_mesh.h
index c00339a620..d32b1fdf74 100644
--- a/editor/import/scene_importer_mesh.h
+++ b/editor/import/scene_importer_mesh.h
@@ -36,6 +36,9 @@
#include "scene/resources/convex_polygon_shape_3d.h"
#include "scene/resources/mesh.h"
#include "scene/resources/navigation_mesh.h"
+
+#include <cstdint>
+
// The following classes are used by importers instead of ArrayMesh and MeshInstance3D
// so the data is not registered (hence, quality loss), importing happens faster and
// its easier to modify before saving
@@ -57,6 +60,7 @@ class EditorSceneImporterMesh : public Resource {
Vector<LOD> lods;
Ref<Material> material;
String name;
+ uint32_t flags = 0;
};
Vector<Surface> surfaces;
Vector<String> blend_shapes;
@@ -80,7 +84,7 @@ public:
int get_blend_shape_count() const;
String get_blend_shape_name(int p_blend_shape) const;
- void add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), const Ref<Material> &p_material = Ref<Material>(), const String &p_name = String());
+ void add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), const Ref<Material> &p_material = Ref<Material>(), const String &p_name = String(), const uint32_t p_flags = 0);
int get_surface_count() const;
void set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode);
@@ -88,12 +92,14 @@ public:
Mesh::PrimitiveType get_surface_primitive_type(int p_surface);
String get_surface_name(int p_surface) const;
+ void set_surface_name(int p_surface, const String &p_name);
Array get_surface_arrays(int p_surface) const;
Array get_surface_blend_shape_arrays(int p_surface, int p_blend_shape) const;
int get_surface_lod_count(int p_surface) const;
Vector<int> get_surface_lod_indices(int p_surface, int p_lod) const;
float get_surface_lod_size(int p_surface, int p_lod) const;
Ref<Material> get_surface_material(int p_surface) const;
+ uint32_t get_surface_format(int p_surface) const;
void set_surface_material(int p_surface, const Ref<Material> &p_material);
@@ -103,16 +109,16 @@ public:
Ref<EditorSceneImporterMesh> get_shadow_mesh() const;
Vector<Face3> get_faces() const;
- Vector<Ref<Shape3D>> convex_decompose() const;
+ Vector<Ref<Shape3D>> convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const;
Ref<Shape3D> create_trimesh_shape() const;
Ref<NavigationMesh> create_navigation_mesh();
- Error lightmap_unwrap_cached(const Transform &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache);
+ Error lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache);
void set_lightmap_size_hint(const Size2i &p_size);
Size2i get_lightmap_size_hint() const;
bool has_mesh() const;
- Ref<ArrayMesh> get_mesh(const Ref<Mesh> &p_base = Ref<Mesh>());
+ Ref<ArrayMesh> get_mesh(const Ref<ArrayMesh> &p_base = Ref<ArrayMesh>());
void clear();
};
#endif // EDITOR_SCENE_IMPORTER_MESH_H