summaryrefslogtreecommitdiff
path: root/editor/import
diff options
context:
space:
mode:
Diffstat (limited to 'editor/import')
-rw-r--r--editor/import/collada.cpp68
-rw-r--r--editor/import/collada.h65
-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.cpp330
-rw-r--r--editor/import/editor_import_collada.h4
-rw-r--r--editor/import/editor_import_plugin.cpp176
-rw-r--r--editor/import/editor_import_plugin.h17
-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/editor_scene_importer_gltf.cpp3199
-rw-r--r--editor/import/editor_scene_importer_gltf.h398
-rw-r--r--editor/import/resource_importer_bitmask.cpp10
-rw-r--r--editor/import/resource_importer_bitmask.h6
-rw-r--r--editor/import/resource_importer_bmfont.cpp797
-rw-r--r--editor/import/resource_importer_bmfont.h (renamed from editor/import/resource_importer_csv.h)23
-rw-r--r--editor/import/resource_importer_csv_translation.cpp14
-rw-r--r--editor/import/resource_importer_csv_translation.h4
-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.cpp8
-rw-r--r--editor/import/resource_importer_image.h6
-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.cpp27
-rw-r--r--editor/import/resource_importer_layered_texture.h36
-rw-r--r--editor/import/resource_importer_obj.cpp61
-rw-r--r--editor/import/resource_importer_obj.h5
-rw-r--r--editor/import/resource_importer_scene.cpp1565
-rw-r--r--editor/import/resource_importer_scene.h116
-rw-r--r--editor/import/resource_importer_shader_file.cpp8
-rw-r--r--editor/import/resource_importer_shader_file.h4
-rw-r--r--editor/import/resource_importer_texture.cpp67
-rw-r--r--editor/import/resource_importer_texture.h16
-rw-r--r--editor/import/resource_importer_texture_atlas.cpp30
-rw-r--r--editor/import/resource_importer_texture_atlas.h8
-rw-r--r--editor/import/resource_importer_wav.cpp10
-rw-r--r--editor/import/resource_importer_wav.h4
-rw-r--r--editor/import/scene_import_settings.cpp1199
-rw-r--r--editor/import/scene_import_settings.h199
-rw-r--r--editor/import/scene_importer_mesh.cpp861
-rw-r--r--editor/import/scene_importer_mesh.h119
-rw-r--r--editor/import/scene_importer_mesh_node_3d.cpp83
-rw-r--r--editor/import/scene_importer_mesh_node_3d.h (renamed from editor/import/resource_importer_csv.cpp)68
44 files changed, 7628 insertions, 4922 deletions
diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp
index 8eb68ecdcf..71930e1e59 100644
--- a/editor/import/collada.cpp
+++ b/editor/import/collada.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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];
@@ -289,7 +289,7 @@ void Collada::_parse_image(XMLParser &parser) {
String path = parser.get_attribute_value("source").strip_edges();
if (path.find("://") == -1 && path.is_rel_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.percent_decode()));
+ image.path = ProjectSettings::get_singleton()->localize_path(state.local_path.get_base_dir().plus_file(path.uri_decode()));
}
} else {
while (parser.read() == OK) {
@@ -298,7 +298,7 @@ void Collada::_parse_image(XMLParser &parser) {
if (name == "init_from") {
parser.read();
- String path = parser.get_node_data().strip_edges().percent_decode();
+ String path = parser.get_node_data().strip_edges().uri_decode();
if (path.find("://") == -1 && path.is_rel_path()) {
// path is relative to file being loaded, so convert to a resource 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;
@@ -1365,7 +1369,7 @@ Collada::Node *Collada::_parse_visual_instance_geometry(XMLParser &parser) {
}
if (geom->controller) {
- if (geom->skeletons.empty()) {
+ if (geom->skeletons.is_empty()) {
//XSI style
if (state.skin_controller_data_map.has(geom->source)) {
@@ -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
@@ -2321,7 +2325,7 @@ void Collada::_optimize() {
i--;
}
- while (!mgeom.empty()) {
+ while (!mgeom.is_empty()) {
Node *n = mgeom.front()->get();
n->parent->children.push_back(n);
mgeom.pop_front();
diff --git a/editor/import/collada.h b/editor/import/collada.h
index 90c6c47e0b..5e38637504 100644
--- a/editor/import/collada.h
+++ b/editor/import/collada.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,9 +31,9 @@
#ifndef COLLADA_H
#define COLLADA_H
+#include "core/config/project_settings.h"
#include "core/io/xml_parser.h"
-#include "core/map.h"
-#include "core/project_settings.h"
+#include "core/templates/map.h"
#include "scene/resources/material.h"
class Collada {
@@ -96,8 +96,8 @@ public:
};
float aspect = 1;
- float z_near = 0.1;
- float z_far = 100;
+ float z_near = 0.05;
+ float z_far = 4000;
CameraData() {}
};
@@ -128,7 +128,7 @@ public:
String name;
struct Source {
Vector<float> array;
- int stride;
+ int stride = 0;
};
Map<String, Source> sources;
@@ -142,15 +142,15 @@ public:
struct Primitives {
struct SourceRef {
String source;
- int offset;
+ int offset = 0;
};
String material;
Map<String, SourceRef> sources;
Vector<float> polygons;
Vector<float> indices;
- int count;
- int vertex_size;
+ int count = 0;
+ int vertex_size = 0;
};
Vector<Primitives> primitives;
@@ -168,7 +168,7 @@ public:
struct Source {
Vector<String> sarray;
Vector<float> array;
- int stride;
+ int stride = 0;
};
Map<String, Source> sources;
@@ -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
@@ -200,17 +200,17 @@ public:
struct Weights {
struct SourceRef {
String source;
- int offset;
+ int offset = 0;
};
String material;
Map<String, SourceRef> sources;
Vector<float> sets;
Vector<float> indices;
- int count;
+ int count = 0;
} weights;
- Map<String, Transform> bone_rest_map;
+ Map<String, Transform3D> bone_rest_map;
SkinControllerData() {}
};
@@ -242,8 +242,8 @@ public:
Color color;
int uid = 0;
struct Weight {
- int bone_idx;
- float weight;
+ int bone_idx = 0;
+ float weight = 0;
bool operator<(const Weight w) const { return weight > w.weight; } //heaviest first
};
@@ -274,7 +274,7 @@ public:
if (normal == p_vert.normal) {
if (uv == p_vert.uv) {
if (uv2 == p_vert.uv2) {
- if (!weights.empty() || !p_vert.weights.empty()) {
+ if (!weights.is_empty() || !p_vert.weights.is_empty()) {
if (weights.size() == p_vert.weights.size()) {
for (int i = 0; i < weights.size(); i++) {
if (weights[i].bone_idx != p_vert.weights[i].bone_idx) {
@@ -313,7 +313,6 @@ public:
struct Node {
enum Type {
-
TYPE_NODE,
TYPE_JOINT,
TYPE_SKELETON, //this bone is not collada, it's added afterwards as optimization
@@ -332,7 +331,7 @@ public:
};
String id;
- Op op;
+ Op op = OP_ROTATE;
Vector<float> data;
};
@@ -343,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;
@@ -376,7 +375,7 @@ public:
};
struct NodeGeometry : public Node {
- bool controller;
+ bool controller = false;
String source;
struct Material {
@@ -439,7 +438,7 @@ public:
TYPE_MATRIX
};
- float time;
+ float time = 0;
Vector<float> data;
Point2 in_tangent;
Point2 out_tangent;
@@ -464,10 +463,10 @@ public:
float unit_scale = 1.0;
Vector3::Axis up_axis = Vector3::AXIS_Y;
- bool z_up;
+ bool z_up = false;
struct Version {
- int major, minor, rev;
+ int major = 0, minor = 0, rev = 0;
bool operator<(const Version &p_ver) const { return (major == p_ver.major) ? ((minor == p_ver.minor) ? (rev < p_ver.rev) : minor < p_ver.minor) : major < p_ver.major; }
Version(int p_major = 0, int p_minor = 0, int p_rev = 0) {
@@ -498,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;
@@ -518,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);
@@ -558,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 12cbaaa885..7ab80ac3b4 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,7 @@
#include "core/os/os.h"
#include "editor/editor_node.h"
#include "editor/import/collada.h"
+#include "editor/import/scene_importer_mesh_node_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
@@ -46,33 +47,28 @@
struct ColladaImport {
Collada collada;
- Node3D *scene;
+ Node3D *scene = nullptr;
Vector<Ref<Animation>> animations;
struct NodeMap {
//String path;
- Node3D *node;
- int bone;
+ Node3D *node = nullptr;
+ int bone = -1;
List<int> anim_tracks;
-
- NodeMap() {
- node = nullptr;
- bone = -1;
- }
};
- bool found_ambient;
+ bool found_ambient = false;
Color ambient;
- bool found_directional;
- bool force_make_tangents;
- bool apply_mesh_xform_to_vertices;
- bool use_mesh_builtin_materials;
- float bake_fps;
+ bool found_directional = false;
+ bool force_make_tangents = false;
+ bool apply_mesh_xform_to_vertices = true;
+ bool use_mesh_builtin_materials = false;
+ float bake_fps = 15;
Map<String, NodeMap> node_map; //map from collada node to engine node
Map<String, String> node_name_map; //map from collada node to engine node
- Map<String, Ref<ArrayMesh>> mesh_cache;
+ Map<String, Ref<EditorSceneImporterMesh>> mesh_cache;
Map<String, Ref<Curve3D>> curve_cache;
Map<String, Ref<Material>> material_cache;
Map<Collada::Node *, Skeleton3D *> skeleton_map;
@@ -83,12 +79,15 @@ struct ColladaImport {
Vector<int> valid_animated_properties;
Map<String, bool> bones_with_animation;
+ Set<String> mesh_unique_names;
+ Set<String> material_unique_names;
+
Error _populate_skeleton(Skeleton3D *p_skeleton, Collada::Node *p_node, int &r_bone, int p_parent);
Error _create_scene_skeletons(Collada::Node *p_node);
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<ArrayMesh> &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<ArrayMesh>> p_morph_meshes = Vector<Ref<ArrayMesh>>(), 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);
@@ -98,14 +97,6 @@ struct ColladaImport {
Vector<String> missing_textures;
void _pre_process_lights(Collada::Node *p_node);
-
- ColladaImport() {
- found_ambient = false;
- found_directional = false;
- force_make_tangents = false;
- apply_mesh_xform_to_vertices = true;
- bake_fps = 15;
- }
};
Error ColladaImport::_populate_skeleton(Skeleton3D *p_skeleton, Collada::Node *p_node, int &r_bone, int p_parent) {
@@ -291,8 +282,8 @@ Error ColladaImport::_create_scene(Collada::Node *p_node, Node3D *p_parent) {
node = memnew(Path3D);
} else {
//mesh since nothing else
- node = memnew(MeshInstance3D);
- //Object::cast_to<MeshInstance3D>(node)->set_flag(GeometryInstance3D::FLAG_USE_BAKED_LIGHT, true);
+ node = memnew(EditorSceneImporterMeshNode3D);
+ //Object::cast_to<EditorSceneImporterMeshNode3D>(node)->set_flag(GeometryInstance3D::FLAG_USE_BAKED_LIGHT, true);
}
} break;
case Collada::Node::TYPE_SKELETON: {
@@ -309,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);
@@ -338,12 +329,25 @@ Error ColladaImport::_create_material(const String &p_target) {
Ref<StandardMaterial3D> material = memnew(StandardMaterial3D);
+ String base_name;
if (src_mat.name != "") {
- material->set_name(src_mat.name);
+ base_name = src_mat.name;
} else if (effect.name != "") {
- material->set_name(effect.name);
+ base_name = effect.name;
+ } else {
+ base_name = "Material";
}
+ String name = base_name;
+ int counter = 2;
+ while (material_unique_names.has(name)) {
+ name = base_name + itos(counter++);
+ }
+
+ material_unique_names.insert(name);
+
+ material->set_name(name);
+
// DIFFUSE
if (effect.diffuse.texture != "") {
@@ -453,7 +457,7 @@ Error ColladaImport::_create_material(const String &p_target) {
return OK;
}
-Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &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<ArrayMesh>> 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) {
@@ -500,61 +504,121 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me
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..
@@ -693,7 +757,8 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me
int vertex_index = p.indices[src + vertex_ofs]; //used for index field (later used by controllers)
int vertex_pos = (vertex_src->stride ? vertex_src->stride : 3) * vertex_index;
- ERR_FAIL_INDEX_V(vertex_pos, vertex_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(vertex_pos + 0, vertex_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(vertex_pos + 2, vertex_src->array.size(), ERR_INVALID_DATA);
vertex.vertex = Vector3(vertex_src->array[vertex_pos + 0], vertex_src->array[vertex_pos + 1], vertex_src->array[vertex_pos + 2]);
if (pre_weights.has(vertex_index)) {
@@ -702,16 +767,19 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me
if (normal_src) {
int normal_pos = (normal_src->stride ? normal_src->stride : 3) * p.indices[src + normal_ofs];
- ERR_FAIL_INDEX_V(normal_pos, normal_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(normal_pos + 0, normal_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(normal_pos + 2, normal_src->array.size(), ERR_INVALID_DATA);
vertex.normal = Vector3(normal_src->array[normal_pos + 0], normal_src->array[normal_pos + 1], normal_src->array[normal_pos + 2]);
if (tangent_src && binormal_src) {
int binormal_pos = (binormal_src->stride ? binormal_src->stride : 3) * p.indices[src + binormal_ofs];
- ERR_FAIL_INDEX_V(binormal_pos, binormal_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(binormal_pos + 0, binormal_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(binormal_pos + 2, binormal_src->array.size(), ERR_INVALID_DATA);
Vector3 binormal = Vector3(binormal_src->array[binormal_pos + 0], binormal_src->array[binormal_pos + 1], binormal_src->array[binormal_pos + 2]);
int tangent_pos = (tangent_src->stride ? tangent_src->stride : 3) * p.indices[src + tangent_ofs];
- ERR_FAIL_INDEX_V(tangent_pos, tangent_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(tangent_pos + 0, tangent_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(tangent_pos + 2, tangent_src->array.size(), ERR_INVALID_DATA);
Vector3 tangent = Vector3(tangent_src->array[tangent_pos + 0], tangent_src->array[tangent_pos + 1], tangent_src->array[tangent_pos + 2]);
vertex.tangent.normal = tangent;
@@ -721,19 +789,22 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me
if (uv_src) {
int uv_pos = (uv_src->stride ? uv_src->stride : 2) * p.indices[src + uv_ofs];
- ERR_FAIL_INDEX_V(uv_pos, uv_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(uv_pos + 0, uv_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(uv_pos + 1, uv_src->array.size(), ERR_INVALID_DATA);
vertex.uv = Vector3(uv_src->array[uv_pos + 0], 1.0 - uv_src->array[uv_pos + 1], 0);
}
if (uv2_src) {
int uv2_pos = (uv2_src->stride ? uv2_src->stride : 2) * p.indices[src + uv2_ofs];
- ERR_FAIL_INDEX_V(uv2_pos, uv2_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(uv2_pos + 0, uv2_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(uv2_pos + 1, uv2_src->array.size(), ERR_INVALID_DATA);
vertex.uv2 = Vector3(uv2_src->array[uv2_pos + 0], 1.0 - uv2_src->array[uv2_pos + 1], 0);
}
if (color_src) {
int color_pos = (color_src->stride ? color_src->stride : 3) * p.indices[src + color_ofs]; // colors are RGB in collada..
- ERR_FAIL_INDEX_V(color_pos, color_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(color_pos + 0, color_src->array.size(), ERR_INVALID_DATA);
+ ERR_FAIL_INDEX_V(color_pos + ((color_src->stride > 3) ? 3 : 2), color_src->array.size(), ERR_INVALID_DATA);
vertex.color = Color(color_src->array[color_pos + 0], color_src->array[color_pos + 1], color_src->array[color_pos + 2], (color_src->stride > 3) ? color_src->array[color_pos + 3] : 1.0);
}
@@ -800,7 +871,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me
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();
@@ -839,24 +910,24 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me
}
Ref<SurfaceTool> surftool;
- surftool.instance();
+ surftool.instantiate();
surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
for (int k = 0; k < vertex_array.size(); k++) {
if (normal_src) {
- surftool->add_normal(vertex_array[k].normal);
+ surftool->set_normal(vertex_array[k].normal);
if (binormal_src && tangent_src) {
- surftool->add_tangent(vertex_array[k].tangent);
+ surftool->set_tangent(vertex_array[k].tangent);
}
}
if (uv_src) {
- surftool->add_uv(Vector2(vertex_array[k].uv.x, vertex_array[k].uv.y));
+ surftool->set_uv(Vector2(vertex_array[k].uv.x, vertex_array[k].uv.y));
}
if (uv2_src) {
- surftool->add_uv2(Vector2(vertex_array[k].uv2.x, vertex_array[k].uv2.y));
+ surftool->set_uv2(Vector2(vertex_array[k].uv2.x, vertex_array[k].uv2.y));
}
if (color_src) {
- surftool->add_color(vertex_array[k].color);
+ surftool->set_color(vertex_array[k].color);
}
if (has_weights) {
@@ -876,15 +947,15 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me
}
}
- surftool->add_bones(bones);
- surftool->add_weights(weights);
+ surftool->set_bones(bones);
+ surftool->set_weights(weights);
}
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) {
@@ -910,7 +981,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me
////////////////////////////
for (int mi = 0; mi < p_morph_meshes.size(); mi++) {
- Array a = p_morph_meshes[mi]->surface_get_arrays(surface);
+ Array a = p_morph_meshes[mi]->get_surface_arrays(surface);
//add valid weight and bone arrays if they exist, TODO check if they are unique to shape (generally not)
if (has_weights) {
@@ -923,14 +994,15 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ArrayMesh> &p_me
mr.push_back(a);
}
- p_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, d, mr, Dictionary(), p_use_compression ? Mesh::ARRAY_COMPRESS_DEFAULT : 0);
-
+ String surface_name;
+ Ref<Material> mat;
if (material.is_valid()) {
if (p_use_mesh_material) {
- p_mesh->surface_set_material(surface, material);
+ mat = material;
}
- p_mesh->surface_set_name(surface, material->get_name());
+ surface_name = material->get_name();
}
+ p_mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, d, mr, Dictionary(), mat, surface_name);
}
/*****************/
@@ -1015,19 +1087,19 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
}
}
- if (Object::cast_to<MeshInstance3D>(node)) {
+ if (Object::cast_to<EditorSceneImporterMeshNode3D>(node)) {
Collada::NodeGeometry *ng2 = static_cast<Collada::NodeGeometry *>(p_node);
- MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(node);
+ EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(node);
ERR_FAIL_COND_V(!mi, ERR_BUG);
Collada::SkinControllerData *skin = nullptr;
Collada::MorphControllerData *morph = nullptr;
String meshid;
- Transform apply_xform;
+ Transform3D apply_xform;
Vector<int> bone_remap;
- Vector<Ref<ArrayMesh>> morphs;
+ Vector<Ref<EditorSceneImporterMesh>> morphs;
if (ng2->controller) {
String ngsource = ng2->source;
@@ -1038,7 +1110,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
Vector<String> skeletons = ng2->skeletons;
- ERR_FAIL_COND_V(skeletons.empty(), ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(skeletons.is_empty(), ERR_INVALID_DATA);
String skname = skeletons[0];
ERR_FAIL_COND_V(!node_map.has(skname), ERR_INVALID_DATA);
@@ -1061,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);
@@ -1096,10 +1168,10 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
for (int i = 0; i < names.size(); i++) {
String meshid2 = names[i];
if (collada.state.mesh_data_map.has(meshid2)) {
- Ref<ArrayMesh> mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ Ref<EditorSceneImporterMesh> mesh = Ref<EditorSceneImporterMesh>(memnew(EditorSceneImporterMesh));
const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid2];
mesh->set_name(meshdata.name);
- Error err = _create_mesh_surfaces(false, mesh, ng2->material_map, meshdata, apply_xform, bone_remap, skin, nullptr, Vector<Ref<ArrayMesh>>(), false);
+ Error err = _create_mesh_surfaces(false, mesh, ng2->material_map, meshdata, apply_xform, bone_remap, skin, nullptr, Vector<Ref<EditorSceneImporterMesh>>(), false);
ERR_FAIL_COND_V(err, err);
morphs.push_back(mesh);
@@ -1122,7 +1194,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
meshid = ng2->source;
}
- Ref<ArrayMesh> mesh;
+ Ref<EditorSceneImporterMesh> mesh;
if (mesh_cache.has(meshid)) {
mesh = mesh_cache[meshid];
} else {
@@ -1130,9 +1202,24 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
//bleh, must ignore invalid
ERR_FAIL_COND_V(!collada.state.mesh_data_map.has(meshid), ERR_INVALID_DATA);
- mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ mesh = Ref<EditorSceneImporterMesh>(memnew(EditorSceneImporterMesh));
const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid];
- mesh->set_name(meshdata.name);
+ String name = meshdata.name;
+ if (name == "") {
+ name = "Mesh";
+ }
+ int counter = 2;
+ while (mesh_unique_names.has(name)) {
+ name = meshdata.name;
+ if (name == "") {
+ name = "Mesh";
+ }
+ name += itos(counter++);
+ }
+
+ mesh_unique_names.insert(name);
+
+ mesh->set_name(name);
Error err = _create_mesh_surfaces(morphs.size() == 0, mesh, ng2->material_map, meshdata, apply_xform, bone_remap, skin, morph, morphs, p_use_compression, use_mesh_builtin_materials);
ERR_FAIL_COND_V_MSG(err, err, "Cannot create mesh surface.");
@@ -1387,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...
@@ -1434,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
@@ -1482,7 +1569,7 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
}
Vector<float> data = at.get_value_at_time(snapshots[i]);
- ERR_CONTINUE(data.empty());
+ ERR_CONTINUE(data.is_empty());
Collada::Node::XForm &xf = cn->xform_list.write[xform_idx];
@@ -1503,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) {
@@ -1517,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);
@@ -1557,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);
@@ -1649,16 +1736,23 @@ void EditorSceneImporterCollada::get_extensions(List<String> *r_extensions) cons
}
Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
+ if (r_err) {
+ *r_err = OK;
+ }
ColladaImport state;
uint32_t flags = Collada::IMPORT_FLAG_SCENE;
if (p_flags & IMPORT_ANIMATION) {
flags |= Collada::IMPORT_FLAG_ANIMATION;
}
- state.use_mesh_builtin_materials = !(p_flags & IMPORT_MATERIALS_IN_INSTANCES);
+ state.use_mesh_builtin_materials = true;
state.bake_fps = p_bake_fps;
- Error err = state.load(p_path, flags, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS, p_flags & EditorSceneImporter::IMPORT_USE_COMPRESSION);
+ Error err = state.load(p_path, flags, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS, false);
+
+ if (r_err) {
+ *r_err = err;
+ }
ERR_FAIL_COND_V_MSG(err != OK, nullptr, "Cannot load scene from file '" + p_path + "'.");
@@ -1678,7 +1772,7 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
}
if (p_flags & IMPORT_ANIMATION) {
- state.create_animations(p_flags & IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS, p_flags & EditorSceneImporter::IMPORT_ANIMATION_KEEP_VALUE_TRACKS);
+ state.create_animations(true, true);
AnimationPlayer *ap = memnew(AnimationPlayer);
for (int i = 0; i < state.animations.size(); i++) {
String name;
@@ -1688,12 +1782,6 @@ Node *EditorSceneImporterCollada::import_scene(const String &p_path, uint32_t p_
name = state.animations[i]->get_name();
}
- if (p_flags & IMPORT_ANIMATION_DETECT_LOOP) {
- if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
- state.animations.write[i]->set_loop(true);
- }
- }
-
ap->add_animation(name, state.animations[i]);
}
state.scene->add_child(ap);
@@ -1711,7 +1799,7 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path
Error err = state.load(p_path, Collada::IMPORT_FLAG_ANIMATION, p_flags & EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS);
ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load animation from file '" + p_path + "'.");
- state.create_animations(p_flags & EditorSceneImporter::IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS, p_flags & EditorSceneImporter::IMPORT_ANIMATION_KEEP_VALUE_TRACKS);
+ state.create_animations(true, true);
if (state.scene) {
memdelete(state.scene);
}
@@ -1720,12 +1808,6 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String &p_path
return Ref<Animation>();
}
Ref<Animation> anim = state.animations[0];
- String base = p_path.get_basename().to_lower();
- if (p_flags & IMPORT_ANIMATION_DETECT_LOOP) {
- if (base.begins_with("loop") || base.ends_with("loop") || base.begins_with("cycle") || base.ends_with("cycle")) {
- anim->set_loop(true);
- }
- }
return anim;
}
diff --git a/editor/import/editor_import_collada.h b/editor/import/editor_import_collada.h
index 5fa17ebd02..bf45322765 100644
--- a/editor/import/editor_import_collada.h
+++ b/editor/import/editor_import_collada.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/editor/import/editor_import_plugin.cpp b/editor/import/editor_import_plugin.cpp
index 6d46d4d2e9..d219f6e325 100644
--- a/editor/import/editor_import_plugin.cpp
+++ b/editor/import/editor_import_plugin.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,108 +29,137 @@
/*************************************************************************/
#include "editor_import_plugin.h"
-#include "core/script_language.h"
+#include "core/object/script_language.h"
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 00a7d9efba..49c959ab44 100644
--- a/editor/import/editor_import_plugin.h
+++ b/editor/import/editor_import_plugin.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp
deleted file mode 100644
index 8caa4aeeaf..0000000000
--- a/editor/import/editor_scene_importer_gltf.cpp
+++ /dev/null
@@ -1,3199 +0,0 @@
-/*************************************************************************/
-/* editor_scene_importer_gltf.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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_scene_importer_gltf.h"
-
-#include "core/crypto/crypto_core.h"
-#include "core/io/json.h"
-#include "core/math/disjoint_set.h"
-#include "core/math/math_defs.h"
-#include "core/os/file_access.h"
-#include "core/os/os.h"
-#include "modules/regex/regex.h"
-#include "scene/3d/bone_attachment_3d.h"
-#include "scene/3d/camera_3d.h"
-#include "scene/3d/mesh_instance_3d.h"
-#include "scene/animation/animation_player.h"
-#include "scene/resources/surface_tool.h"
-
-uint32_t EditorSceneImporterGLTF::get_import_flags() const {
- return IMPORT_SCENE | IMPORT_ANIMATION;
-}
-
-void EditorSceneImporterGLTF::get_extensions(List<String> *r_extensions) const {
- r_extensions->push_back("gltf");
- r_extensions->push_back("glb");
-}
-
-Error EditorSceneImporterGLTF::_parse_json(const String &p_path, GLTFState &state) {
- Error err;
- FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
- if (!f) {
- return err;
- }
-
- Vector<uint8_t> array;
- array.resize(f->get_len());
- f->get_buffer(array.ptrw(), array.size());
- String text;
- text.parse_utf8((const char *)array.ptr(), array.size());
-
- String err_txt;
- int err_line;
- Variant v;
- err = JSON::parse(text, v, err_txt, err_line);
- if (err != OK) {
- _err_print_error("", p_path.utf8().get_data(), err_line, err_txt.utf8().get_data(), ERR_HANDLER_SCRIPT);
- return err;
- }
- state.json = v;
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_parse_glb(const String &p_path, GLTFState &state) {
- Error err;
- FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
- if (!f) {
- return err;
- }
-
- uint32_t magic = f->get_32();
- ERR_FAIL_COND_V(magic != 0x46546C67, ERR_FILE_UNRECOGNIZED); //glTF
- f->get_32(); // version
- f->get_32(); // length
-
- uint32_t chunk_length = f->get_32();
- uint32_t chunk_type = f->get_32();
-
- ERR_FAIL_COND_V(chunk_type != 0x4E4F534A, ERR_PARSE_ERROR); //JSON
- Vector<uint8_t> json_data;
- json_data.resize(chunk_length);
- uint32_t len = f->get_buffer(json_data.ptrw(), chunk_length);
- ERR_FAIL_COND_V(len != chunk_length, ERR_FILE_CORRUPT);
-
- String text;
- text.parse_utf8((const char *)json_data.ptr(), json_data.size());
-
- String err_txt;
- int err_line;
- Variant v;
- err = JSON::parse(text, v, err_txt, err_line);
- if (err != OK) {
- _err_print_error("", p_path.utf8().get_data(), err_line, err_txt.utf8().get_data(), ERR_HANDLER_SCRIPT);
- return err;
- }
-
- state.json = v;
-
- //data?
-
- chunk_length = f->get_32();
- chunk_type = f->get_32();
-
- if (f->eof_reached()) {
- return OK; //all good
- }
-
- ERR_FAIL_COND_V(chunk_type != 0x004E4942, ERR_PARSE_ERROR); //BIN
-
- state.glb_data.resize(chunk_length);
- len = f->get_buffer(state.glb_data.ptrw(), chunk_length);
- ERR_FAIL_COND_V(len != chunk_length, ERR_FILE_CORRUPT);
-
- return OK;
-}
-
-static Vector3 _arr_to_vec3(const Array &p_array) {
- ERR_FAIL_COND_V(p_array.size() != 3, Vector3());
- return Vector3(p_array[0], p_array[1], p_array[2]);
-}
-
-static Quat _arr_to_quat(const Array &p_array) {
- ERR_FAIL_COND_V(p_array.size() != 4, Quat());
- return Quat(p_array[0], p_array[1], p_array[2], p_array[3]);
-}
-
-static Transform _arr_to_xform(const Array &p_array) {
- ERR_FAIL_COND_V(p_array.size() != 16, Transform());
-
- Transform xform;
- xform.basis.set_axis(Vector3::AXIS_X, Vector3(p_array[0], p_array[1], p_array[2]));
- xform.basis.set_axis(Vector3::AXIS_Y, Vector3(p_array[4], p_array[5], p_array[6]));
- xform.basis.set_axis(Vector3::AXIS_Z, Vector3(p_array[8], p_array[9], p_array[10]));
- xform.set_origin(Vector3(p_array[12], p_array[13], p_array[14]));
-
- return xform;
-}
-
-String EditorSceneImporterGLTF::_sanitize_scene_name(const String &name) {
- RegEx regex("([^a-zA-Z0-9_ -]+)");
- String p_name = regex.sub(name, "", true);
- return p_name;
-}
-
-String EditorSceneImporterGLTF::_gen_unique_name(GLTFState &state, const String &p_name) {
- const String s_name = _sanitize_scene_name(p_name);
-
- String name;
- int index = 1;
- while (true) {
- name = s_name;
-
- if (index > 1) {
- name += " " + itos(index);
- }
- if (!state.unique_names.has(name)) {
- break;
- }
- index++;
- }
-
- state.unique_names.insert(name);
-
- return name;
-}
-
-String EditorSceneImporterGLTF::_sanitize_bone_name(const String &name) {
- String p_name = name.camelcase_to_underscore(true);
-
- RegEx pattern_del("([^a-zA-Z0-9_ ])+");
- p_name = pattern_del.sub(p_name, "", true);
-
- RegEx pattern_nospace(" +");
- p_name = pattern_nospace.sub(p_name, "_", true);
-
- RegEx pattern_multiple("_+");
- p_name = pattern_multiple.sub(p_name, "_", true);
-
- RegEx pattern_padded("0+(\\d+)");
- p_name = pattern_padded.sub(p_name, "$1", true);
-
- return p_name;
-}
-
-String EditorSceneImporterGLTF::_gen_unique_bone_name(GLTFState &state, const GLTFSkeletonIndex skel_i, const String &p_name) {
- const String s_name = _sanitize_bone_name(p_name);
-
- String name;
- int index = 1;
- while (true) {
- name = s_name;
-
- if (index > 1) {
- name += "_" + itos(index);
- }
- if (!state.skeletons[skel_i].unique_names.has(name)) {
- break;
- }
- index++;
- }
-
- state.skeletons.write[skel_i].unique_names.insert(name);
-
- return name;
-}
-
-Error EditorSceneImporterGLTF::_parse_scenes(GLTFState &state) {
- ERR_FAIL_COND_V(!state.json.has("scenes"), ERR_FILE_CORRUPT);
- const Array &scenes = state.json["scenes"];
- int loaded_scene = 0;
- if (state.json.has("scene")) {
- loaded_scene = state.json["scene"];
- } else {
- WARN_PRINT("The load-time scene is not defined in the glTF2 file. Picking the first scene.");
- }
-
- if (scenes.size()) {
- ERR_FAIL_COND_V(loaded_scene >= scenes.size(), ERR_FILE_CORRUPT);
- const Dictionary &s = scenes[loaded_scene];
- ERR_FAIL_COND_V(!s.has("nodes"), ERR_UNAVAILABLE);
- const Array &nodes = s["nodes"];
- for (int j = 0; j < nodes.size(); j++) {
- state.root_nodes.push_back(nodes[j]);
- }
-
- if (s.has("name") && s["name"] != "") {
- state.scene_name = _gen_unique_name(state, s["name"]);
- } else {
- state.scene_name = _gen_unique_name(state, "Scene");
- }
- }
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_parse_nodes(GLTFState &state) {
- ERR_FAIL_COND_V(!state.json.has("nodes"), ERR_FILE_CORRUPT);
- const Array &nodes = state.json["nodes"];
- for (int i = 0; i < nodes.size(); i++) {
- GLTFNode *node = memnew(GLTFNode);
- const Dictionary &n = nodes[i];
-
- if (n.has("name")) {
- node->name = n["name"];
- }
- if (n.has("camera")) {
- node->camera = n["camera"];
- }
- if (n.has("mesh")) {
- node->mesh = n["mesh"];
- }
- if (n.has("skin")) {
- node->skin = n["skin"];
- }
- if (n.has("matrix")) {
- node->xform = _arr_to_xform(n["matrix"]);
-
- } else {
- if (n.has("translation")) {
- node->translation = _arr_to_vec3(n["translation"]);
- }
- if (n.has("rotation")) {
- node->rotation = _arr_to_quat(n["rotation"]);
- }
- if (n.has("scale")) {
- node->scale = _arr_to_vec3(n["scale"]);
- }
-
- node->xform.basis.set_quat_scale(node->rotation, node->scale);
- node->xform.origin = node->translation;
- }
- if (n.has("extensions")) {
- Dictionary extensions = n["extensions"];
- if (extensions.has("KHR_lights_punctual")) {
- Dictionary lights_punctual = extensions["KHR_lights_punctual"];
- if (lights_punctual.has("light")) {
- GLTFLightIndex light = lights_punctual["light"];
- node->light = light;
- }
- }
- }
- if (n.has("children")) {
- const Array &children = n["children"];
- for (int j = 0; j < children.size(); j++) {
- node->children.push_back(children[j]);
- }
- }
-
- state.nodes.push_back(node);
- }
-
- // build the hierarchy
- for (GLTFNodeIndex node_i = 0; node_i < state.nodes.size(); node_i++) {
- for (int j = 0; j < state.nodes[node_i]->children.size(); j++) {
- GLTFNodeIndex child_i = state.nodes[node_i]->children[j];
-
- ERR_FAIL_INDEX_V(child_i, state.nodes.size(), ERR_FILE_CORRUPT);
- ERR_CONTINUE(state.nodes[child_i]->parent != -1); //node already has a parent, wtf.
-
- state.nodes[child_i]->parent = node_i;
- }
- }
-
- _compute_node_heights(state);
-
- return OK;
-}
-
-void EditorSceneImporterGLTF::_compute_node_heights(GLTFState &state) {
- state.root_nodes.clear();
- for (GLTFNodeIndex node_i = 0; node_i < state.nodes.size(); ++node_i) {
- GLTFNode *node = state.nodes[node_i];
- node->height = 0;
-
- GLTFNodeIndex current_i = node_i;
- while (current_i >= 0) {
- const GLTFNodeIndex parent_i = state.nodes[current_i]->parent;
- if (parent_i >= 0) {
- ++node->height;
- }
- current_i = parent_i;
- }
-
- if (node->height == 0) {
- state.root_nodes.push_back(node_i);
- }
- }
-}
-
-static Vector<uint8_t> _parse_base64_uri(const String &uri) {
- int start = uri.find(",");
- ERR_FAIL_COND_V(start == -1, Vector<uint8_t>());
-
- CharString substr = uri.right(start + 1).ascii();
-
- int strlen = substr.length();
-
- Vector<uint8_t> buf;
- buf.resize(strlen / 4 * 3 + 1 + 1);
-
- size_t len = 0;
- ERR_FAIL_COND_V(CryptoCore::b64_decode(buf.ptrw(), buf.size(), &len, (unsigned char *)substr.get_data(), strlen) != OK, Vector<uint8_t>());
-
- buf.resize(len);
-
- return buf;
-}
-
-Error EditorSceneImporterGLTF::_parse_buffers(GLTFState &state, const String &p_base_path) {
- if (!state.json.has("buffers")) {
- return OK;
- }
-
- const Array &buffers = state.json["buffers"];
- for (GLTFBufferIndex i = 0; i < buffers.size(); i++) {
- if (i == 0 && state.glb_data.size()) {
- state.buffers.push_back(state.glb_data);
-
- } else {
- const Dictionary &buffer = buffers[i];
- if (buffer.has("uri")) {
- Vector<uint8_t> buffer_data;
- String uri = buffer["uri"];
-
- if (uri.findn("data:application/octet-stream;base64") == 0) {
- //embedded data
- buffer_data = _parse_base64_uri(uri);
- } else {
- uri = p_base_path.plus_file(uri).replace("\\", "/"); //fix for windows
- buffer_data = FileAccess::get_file_as_array(uri);
- ERR_FAIL_COND_V(buffer.size() == 0, ERR_PARSE_ERROR);
- }
-
- ERR_FAIL_COND_V(!buffer.has("byteLength"), ERR_PARSE_ERROR);
- int byteLength = buffer["byteLength"];
- ERR_FAIL_COND_V(byteLength < buffer_data.size(), ERR_PARSE_ERROR);
- state.buffers.push_back(buffer_data);
- }
- }
- }
-
- print_verbose("glTF: Total buffers: " + itos(state.buffers.size()));
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_parse_buffer_views(GLTFState &state) {
- ERR_FAIL_COND_V(!state.json.has("bufferViews"), ERR_FILE_CORRUPT);
- const Array &buffers = state.json["bufferViews"];
- for (GLTFBufferViewIndex i = 0; i < buffers.size(); i++) {
- const Dictionary &d = buffers[i];
-
- GLTFBufferView buffer_view;
-
- ERR_FAIL_COND_V(!d.has("buffer"), ERR_PARSE_ERROR);
- buffer_view.buffer = d["buffer"];
- ERR_FAIL_COND_V(!d.has("byteLength"), ERR_PARSE_ERROR);
- buffer_view.byte_length = d["byteLength"];
-
- if (d.has("byteOffset")) {
- buffer_view.byte_offset = d["byteOffset"];
- }
-
- if (d.has("byteStride")) {
- buffer_view.byte_stride = d["byteStride"];
- }
-
- if (d.has("target")) {
- const int target = d["target"];
- buffer_view.indices = target == ELEMENT_ARRAY_BUFFER;
- }
-
- state.buffer_views.push_back(buffer_view);
- }
-
- print_verbose("glTF: Total buffer views: " + itos(state.buffer_views.size()));
-
- return OK;
-}
-
-EditorSceneImporterGLTF::GLTFType EditorSceneImporterGLTF::_get_type_from_str(const String &p_string) {
- if (p_string == "SCALAR") {
- return TYPE_SCALAR;
- }
-
- if (p_string == "VEC2") {
- return TYPE_VEC2;
- }
- if (p_string == "VEC3") {
- return TYPE_VEC3;
- }
- if (p_string == "VEC4") {
- return TYPE_VEC4;
- }
-
- if (p_string == "MAT2") {
- return TYPE_MAT2;
- }
- if (p_string == "MAT3") {
- return TYPE_MAT3;
- }
- if (p_string == "MAT4") {
- return TYPE_MAT4;
- }
-
- ERR_FAIL_V(TYPE_SCALAR);
-}
-
-Error EditorSceneImporterGLTF::_parse_accessors(GLTFState &state) {
- ERR_FAIL_COND_V(!state.json.has("accessors"), ERR_FILE_CORRUPT);
- const Array &accessors = state.json["accessors"];
- for (GLTFAccessorIndex i = 0; i < accessors.size(); i++) {
- const Dictionary &d = accessors[i];
-
- GLTFAccessor accessor;
-
- ERR_FAIL_COND_V(!d.has("componentType"), ERR_PARSE_ERROR);
- accessor.component_type = d["componentType"];
- ERR_FAIL_COND_V(!d.has("count"), ERR_PARSE_ERROR);
- accessor.count = d["count"];
- ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
- accessor.type = _get_type_from_str(d["type"]);
-
- if (d.has("bufferView")) {
- accessor.buffer_view = d["bufferView"]; //optional because it may be sparse...
- }
-
- if (d.has("byteOffset")) {
- accessor.byte_offset = d["byteOffset"];
- }
-
- if (d.has("max")) {
- accessor.max = d["max"];
- }
-
- if (d.has("min")) {
- accessor.min = d["min"];
- }
-
- if (d.has("sparse")) {
- //eeh..
-
- const Dictionary &s = d["sparse"];
-
- ERR_FAIL_COND_V(!s.has("count"), ERR_PARSE_ERROR);
- accessor.sparse_count = s["count"];
- ERR_FAIL_COND_V(!s.has("indices"), ERR_PARSE_ERROR);
- const Dictionary &si = s["indices"];
-
- ERR_FAIL_COND_V(!si.has("bufferView"), ERR_PARSE_ERROR);
- accessor.sparse_indices_buffer_view = si["bufferView"];
- ERR_FAIL_COND_V(!si.has("componentType"), ERR_PARSE_ERROR);
- accessor.sparse_indices_component_type = si["componentType"];
-
- if (si.has("byteOffset")) {
- accessor.sparse_indices_byte_offset = si["byteOffset"];
- }
-
- ERR_FAIL_COND_V(!s.has("values"), ERR_PARSE_ERROR);
- const Dictionary &sv = s["values"];
-
- ERR_FAIL_COND_V(!sv.has("bufferView"), ERR_PARSE_ERROR);
- accessor.sparse_values_buffer_view = sv["bufferView"];
- if (sv.has("byteOffset")) {
- accessor.sparse_values_byte_offset = sv["byteOffset"];
- }
- }
-
- state.accessors.push_back(accessor);
- }
-
- print_verbose("glTF: Total accessors: " + itos(state.accessors.size()));
-
- return OK;
-}
-
-String EditorSceneImporterGLTF::_get_component_type_name(const uint32_t p_component) {
- switch (p_component) {
- case COMPONENT_TYPE_BYTE:
- return "Byte";
- case COMPONENT_TYPE_UNSIGNED_BYTE:
- return "UByte";
- case COMPONENT_TYPE_SHORT:
- return "Short";
- case COMPONENT_TYPE_UNSIGNED_SHORT:
- return "UShort";
- case COMPONENT_TYPE_INT:
- return "Int";
- case COMPONENT_TYPE_FLOAT:
- return "Float";
- }
-
- return "<Error>";
-}
-
-String EditorSceneImporterGLTF::_get_type_name(const GLTFType p_component) {
- static const char *names[] = {
- "float",
- "vec2",
- "vec3",
- "vec4",
- "mat2",
- "mat3",
- "mat4"
- };
-
- return names[p_component];
-}
-
-Error EditorSceneImporterGLTF::_decode_buffer_view(GLTFState &state, double *dst, const GLTFBufferViewIndex p_buffer_view, const int skip_every, const int skip_bytes, const int element_size, const int count, const GLTFType type, const int component_count, const int component_type, const int component_size, const bool normalized, const int byte_offset, const bool for_vertex) {
- const GLTFBufferView &bv = state.buffer_views[p_buffer_view];
-
- int stride = bv.byte_stride ? bv.byte_stride : element_size;
- if (for_vertex && stride % 4) {
- stride += 4 - (stride % 4); //according to spec must be multiple of 4
- }
-
- ERR_FAIL_INDEX_V(bv.buffer, state.buffers.size(), ERR_PARSE_ERROR);
-
- const uint32_t offset = bv.byte_offset + byte_offset;
- Vector<uint8_t> buffer = state.buffers[bv.buffer]; //copy on write, so no performance hit
- const uint8_t *bufptr = buffer.ptr();
-
- //use to debug
- print_verbose("glTF: type " + _get_type_name(type) + " component type: " + _get_component_type_name(component_type) + " stride: " + itos(stride) + " amount " + itos(count));
- print_verbose("glTF: accessor offset" + itos(byte_offset) + " view offset: " + itos(bv.byte_offset) + " total buffer len: " + itos(buffer.size()) + " view len " + itos(bv.byte_length));
-
- const int buffer_end = (stride * (count - 1)) + element_size;
- ERR_FAIL_COND_V(buffer_end > bv.byte_length, ERR_PARSE_ERROR);
-
- ERR_FAIL_COND_V((int)(offset + buffer_end) > buffer.size(), ERR_PARSE_ERROR);
-
- //fill everything as doubles
-
- for (int i = 0; i < count; i++) {
- const uint8_t *src = &bufptr[offset + i * stride];
-
- for (int j = 0; j < component_count; j++) {
- if (skip_every && j > 0 && (j % skip_every) == 0) {
- src += skip_bytes;
- }
-
- double d = 0;
-
- switch (component_type) {
- case COMPONENT_TYPE_BYTE: {
- int8_t b = int8_t(*src);
- if (normalized) {
- d = (double(b) / 128.0);
- } else {
- d = double(b);
- }
- } break;
- case COMPONENT_TYPE_UNSIGNED_BYTE: {
- uint8_t b = *src;
- if (normalized) {
- d = (double(b) / 255.0);
- } else {
- d = double(b);
- }
- } break;
- case COMPONENT_TYPE_SHORT: {
- int16_t s = *(int16_t *)src;
- if (normalized) {
- d = (double(s) / 32768.0);
- } else {
- d = double(s);
- }
- } break;
- case COMPONENT_TYPE_UNSIGNED_SHORT: {
- uint16_t s = *(uint16_t *)src;
- if (normalized) {
- d = (double(s) / 65535.0);
- } else {
- d = double(s);
- }
-
- } break;
- case COMPONENT_TYPE_INT: {
- d = *(int *)src;
- } break;
- case COMPONENT_TYPE_FLOAT: {
- d = *(float *)src;
- } break;
- }
-
- *dst++ = d;
- src += component_size;
- }
- }
-
- return OK;
-}
-
-int EditorSceneImporterGLTF::_get_component_type_size(const int component_type) {
- switch (component_type) {
- case COMPONENT_TYPE_BYTE:
- return 1;
- break;
- case COMPONENT_TYPE_UNSIGNED_BYTE:
- return 1;
- break;
- case COMPONENT_TYPE_SHORT:
- return 2;
- break;
- case COMPONENT_TYPE_UNSIGNED_SHORT:
- return 2;
- break;
- case COMPONENT_TYPE_INT:
- return 4;
- break;
- case COMPONENT_TYPE_FLOAT:
- return 4;
- break;
- default: {
- ERR_FAIL_V(0);
- }
- }
- return 0;
-}
-
-Vector<double> EditorSceneImporterGLTF::_decode_accessor(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- //spec, for reference:
- //https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment
-
- ERR_FAIL_INDEX_V(p_accessor, state.accessors.size(), Vector<double>());
-
- const GLTFAccessor &a = state.accessors[p_accessor];
-
- const int component_count_for_type[7] = {
- 1, 2, 3, 4, 4, 9, 16
- };
-
- const int component_count = component_count_for_type[a.type];
- const int component_size = _get_component_type_size(a.component_type);
- ERR_FAIL_COND_V(component_size == 0, Vector<double>());
- int element_size = component_count * component_size;
-
- int skip_every = 0;
- int skip_bytes = 0;
- //special case of alignments, as described in spec
- switch (a.component_type) {
- case COMPONENT_TYPE_BYTE:
- case COMPONENT_TYPE_UNSIGNED_BYTE: {
- if (a.type == TYPE_MAT2) {
- skip_every = 2;
- skip_bytes = 2;
- element_size = 8; //override for this case
- }
- if (a.type == TYPE_MAT3) {
- skip_every = 3;
- skip_bytes = 1;
- element_size = 12; //override for this case
- }
-
- } break;
- case COMPONENT_TYPE_SHORT:
- case COMPONENT_TYPE_UNSIGNED_SHORT: {
- if (a.type == TYPE_MAT3) {
- skip_every = 6;
- skip_bytes = 4;
- element_size = 16; //override for this case
- }
- } break;
- default: {
- }
- }
-
- Vector<double> dst_buffer;
- dst_buffer.resize(component_count * a.count);
- double *dst = dst_buffer.ptrw();
-
- if (a.buffer_view >= 0) {
- ERR_FAIL_INDEX_V(a.buffer_view, state.buffer_views.size(), Vector<double>());
-
- const Error err = _decode_buffer_view(state, dst, a.buffer_view, skip_every, skip_bytes, element_size, a.count, a.type, component_count, a.component_type, component_size, a.normalized, a.byte_offset, p_for_vertex);
- if (err != OK) {
- return Vector<double>();
- }
-
- } else {
- //fill with zeros, as bufferview is not defined.
- for (int i = 0; i < (a.count * component_count); i++) {
- dst_buffer.write[i] = 0;
- }
- }
-
- if (a.sparse_count > 0) {
- // I could not find any file using this, so this code is so far untested
- Vector<double> indices;
- indices.resize(a.sparse_count);
- const int indices_component_size = _get_component_type_size(a.sparse_indices_component_type);
-
- Error err = _decode_buffer_view(state, indices.ptrw(), a.sparse_indices_buffer_view, 0, 0, indices_component_size, a.sparse_count, TYPE_SCALAR, 1, a.sparse_indices_component_type, indices_component_size, false, a.sparse_indices_byte_offset, false);
- if (err != OK) {
- return Vector<double>();
- }
-
- Vector<double> data;
- data.resize(component_count * a.sparse_count);
- err = _decode_buffer_view(state, data.ptrw(), a.sparse_values_buffer_view, skip_every, skip_bytes, element_size, a.sparse_count, a.type, component_count, a.component_type, component_size, a.normalized, a.sparse_values_byte_offset, p_for_vertex);
- if (err != OK) {
- return Vector<double>();
- }
-
- for (int i = 0; i < indices.size(); i++) {
- const int write_offset = int(indices[i]) * component_count;
-
- for (int j = 0; j < component_count; j++) {
- dst[write_offset + j] = data[i * component_count + j];
- }
- }
- }
-
- return dst_buffer;
-}
-
-Vector<int> EditorSceneImporterGLTF::_decode_accessor_as_ints(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
- Vector<int> ret;
-
- if (attribs.size() == 0) {
- return ret;
- }
-
- const double *attribs_ptr = attribs.ptr();
- const int ret_size = attribs.size();
- ret.resize(ret_size);
- {
- int *w = ret.ptrw();
- for (int i = 0; i < ret_size; i++) {
- w[i] = int(attribs_ptr[i]);
- }
- }
- return ret;
-}
-
-Vector<float> EditorSceneImporterGLTF::_decode_accessor_as_floats(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
- Vector<float> ret;
-
- if (attribs.size() == 0) {
- return ret;
- }
-
- const double *attribs_ptr = attribs.ptr();
- const int ret_size = attribs.size();
- ret.resize(ret_size);
- {
- float *w = ret.ptrw();
- for (int i = 0; i < ret_size; i++) {
- w[i] = float(attribs_ptr[i]);
- }
- }
- return ret;
-}
-
-Vector<Vector2> EditorSceneImporterGLTF::_decode_accessor_as_vec2(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
- Vector<Vector2> ret;
-
- if (attribs.size() == 0) {
- return ret;
- }
-
- ERR_FAIL_COND_V(attribs.size() % 2 != 0, ret);
- const double *attribs_ptr = attribs.ptr();
- const int ret_size = attribs.size() / 2;
- ret.resize(ret_size);
- {
- Vector2 *w = ret.ptrw();
- for (int i = 0; i < ret_size; i++) {
- w[i] = Vector2(attribs_ptr[i * 2 + 0], attribs_ptr[i * 2 + 1]);
- }
- }
- return ret;
-}
-
-Vector<Vector3> EditorSceneImporterGLTF::_decode_accessor_as_vec3(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
- Vector<Vector3> ret;
-
- if (attribs.size() == 0) {
- return ret;
- }
-
- ERR_FAIL_COND_V(attribs.size() % 3 != 0, ret);
- const double *attribs_ptr = attribs.ptr();
- const int ret_size = attribs.size() / 3;
- ret.resize(ret_size);
- {
- Vector3 *w = ret.ptrw();
- for (int i = 0; i < ret_size; i++) {
- w[i] = Vector3(attribs_ptr[i * 3 + 0], attribs_ptr[i * 3 + 1], attribs_ptr[i * 3 + 2]);
- }
- }
- return ret;
-}
-
-Vector<Color> EditorSceneImporterGLTF::_decode_accessor_as_color(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
- Vector<Color> ret;
-
- if (attribs.size() == 0) {
- return ret;
- }
-
- const int type = state.accessors[p_accessor].type;
- ERR_FAIL_COND_V(!(type == TYPE_VEC3 || type == TYPE_VEC4), ret);
- int vec_len = 3;
- if (type == TYPE_VEC4) {
- vec_len = 4;
- }
-
- ERR_FAIL_COND_V(attribs.size() % vec_len != 0, ret);
- const double *attribs_ptr = attribs.ptr();
- const int ret_size = attribs.size() / vec_len;
- ret.resize(ret_size);
- {
- Color *w = ret.ptrw();
- for (int i = 0; i < ret_size; i++) {
- w[i] = Color(attribs_ptr[i * vec_len + 0], attribs_ptr[i * vec_len + 1], attribs_ptr[i * vec_len + 2], vec_len == 4 ? attribs_ptr[i * 4 + 3] : 1.0);
- }
- }
- return ret;
-}
-
-Vector<Quat> EditorSceneImporterGLTF::_decode_accessor_as_quat(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
- Vector<Quat> ret;
-
- if (attribs.size() == 0) {
- return ret;
- }
-
- ERR_FAIL_COND_V(attribs.size() % 4 != 0, ret);
- const double *attribs_ptr = attribs.ptr();
- const int ret_size = attribs.size() / 4;
- ret.resize(ret_size);
- {
- for (int i = 0; i < ret_size; i++) {
- ret.write[i] = Quat(attribs_ptr[i * 4 + 0], attribs_ptr[i * 4 + 1], attribs_ptr[i * 4 + 2], attribs_ptr[i * 4 + 3]).normalized();
- }
- }
- return ret;
-}
-
-Vector<Transform2D> EditorSceneImporterGLTF::_decode_accessor_as_xform2d(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
- Vector<Transform2D> ret;
-
- if (attribs.size() == 0) {
- return ret;
- }
-
- ERR_FAIL_COND_V(attribs.size() % 4 != 0, ret);
- ret.resize(attribs.size() / 4);
- for (int i = 0; i < ret.size(); i++) {
- ret.write[i][0] = Vector2(attribs[i * 4 + 0], attribs[i * 4 + 1]);
- ret.write[i][1] = Vector2(attribs[i * 4 + 2], attribs[i * 4 + 3]);
- }
- return ret;
-}
-
-Vector<Basis> EditorSceneImporterGLTF::_decode_accessor_as_basis(GLTFState &state, const GLTFAccessorIndex p_accessor, bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
- Vector<Basis> ret;
-
- if (attribs.size() == 0) {
- return ret;
- }
-
- ERR_FAIL_COND_V(attribs.size() % 9 != 0, ret);
- ret.resize(attribs.size() / 9);
- for (int i = 0; i < ret.size(); i++) {
- ret.write[i].set_axis(0, Vector3(attribs[i * 9 + 0], attribs[i * 9 + 1], attribs[i * 9 + 2]));
- ret.write[i].set_axis(1, Vector3(attribs[i * 9 + 3], attribs[i * 9 + 4], attribs[i * 9 + 5]));
- ret.write[i].set_axis(2, Vector3(attribs[i * 9 + 6], attribs[i * 9 + 7], attribs[i * 9 + 8]));
- }
- return ret;
-}
-
-Vector<Transform> EditorSceneImporterGLTF::_decode_accessor_as_xform(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
- const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
- Vector<Transform> ret;
-
- if (attribs.size() == 0) {
- return ret;
- }
-
- ERR_FAIL_COND_V(attribs.size() % 16 != 0, ret);
- ret.resize(attribs.size() / 16);
- for (int i = 0; i < ret.size(); i++) {
- ret.write[i].basis.set_axis(0, Vector3(attribs[i * 16 + 0], attribs[i * 16 + 1], attribs[i * 16 + 2]));
- ret.write[i].basis.set_axis(1, Vector3(attribs[i * 16 + 4], attribs[i * 16 + 5], attribs[i * 16 + 6]));
- ret.write[i].basis.set_axis(2, Vector3(attribs[i * 16 + 8], attribs[i * 16 + 9], attribs[i * 16 + 10]));
- ret.write[i].set_origin(Vector3(attribs[i * 16 + 12], attribs[i * 16 + 13], attribs[i * 16 + 14]));
- }
- return ret;
-}
-
-Error EditorSceneImporterGLTF::_parse_meshes(GLTFState &state) {
- if (!state.json.has("meshes")) {
- return OK;
- }
-
- bool compress_vert_data = state.import_flags & IMPORT_USE_COMPRESSION;
- uint32_t mesh_flags = compress_vert_data ? Mesh::ARRAY_COMPRESS_DEFAULT : 0;
-
- Array meshes = state.json["meshes"];
- for (GLTFMeshIndex i = 0; i < meshes.size(); i++) {
- print_verbose("glTF: Parsing mesh: " + itos(i));
- Dictionary d = meshes[i];
-
- GLTFMesh mesh;
- mesh.mesh.instance();
-
- ERR_FAIL_COND_V(!d.has("primitives"), ERR_PARSE_ERROR);
-
- Array primitives = d["primitives"];
- const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : Dictionary();
-
- for (int j = 0; j < primitives.size(); j++) {
- Dictionary p = primitives[j];
-
- Array array;
- array.resize(Mesh::ARRAY_MAX);
-
- ERR_FAIL_COND_V(!p.has("attributes"), ERR_PARSE_ERROR);
-
- Dictionary a = p["attributes"];
-
- Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_TRIANGLES;
- if (p.has("mode")) {
- const int mode = p["mode"];
- ERR_FAIL_INDEX_V(mode, 7, ERR_FILE_CORRUPT);
- static const Mesh::PrimitiveType primitives2[7] = {
- Mesh::PRIMITIVE_POINTS,
- Mesh::PRIMITIVE_LINES,
- Mesh::PRIMITIVE_LINES, //loop not supported, should ce converted
- Mesh::PRIMITIVE_LINES,
- Mesh::PRIMITIVE_TRIANGLES,
- Mesh::PRIMITIVE_TRIANGLE_STRIP,
- Mesh::PRIMITIVE_TRIANGLES, //fan not supported, should be converted
-#ifndef _MSC_VER
-#warning line loop and triangle fan are not supported and need to be converted to lines and triangles
-#endif
-
- };
-
- primitive = primitives2[mode];
- }
-
- ERR_FAIL_COND_V(!a.has("POSITION"), ERR_PARSE_ERROR);
- if (a.has("POSITION")) {
- array[Mesh::ARRAY_VERTEX] = _decode_accessor_as_vec3(state, a["POSITION"], true);
- }
- if (a.has("NORMAL")) {
- array[Mesh::ARRAY_NORMAL] = _decode_accessor_as_vec3(state, a["NORMAL"], true);
- }
- if (a.has("TANGENT")) {
- array[Mesh::ARRAY_TANGENT] = _decode_accessor_as_floats(state, a["TANGENT"], true);
- }
- if (a.has("TEXCOORD_0")) {
- array[Mesh::ARRAY_TEX_UV] = _decode_accessor_as_vec2(state, a["TEXCOORD_0"], true);
- }
- if (a.has("TEXCOORD_1")) {
- array[Mesh::ARRAY_TEX_UV2] = _decode_accessor_as_vec2(state, a["TEXCOORD_1"], true);
- }
- if (a.has("COLOR_0")) {
- array[Mesh::ARRAY_COLOR] = _decode_accessor_as_color(state, a["COLOR_0"], true);
- }
- if (a.has("JOINTS_0")) {
- array[Mesh::ARRAY_BONES] = _decode_accessor_as_ints(state, a["JOINTS_0"], true);
- }
- if (a.has("WEIGHTS_0")) {
- Vector<float> weights = _decode_accessor_as_floats(state, a["WEIGHTS_0"], true);
- { //gltf does not seem to normalize the weights for some reason..
- int wc = weights.size();
- float *w = weights.ptrw();
-
- for (int k = 0; k < wc; k += 4) {
- float total = 0.0;
- total += w[k + 0];
- total += w[k + 1];
- total += w[k + 2];
- total += w[k + 3];
- if (total > 0.0) {
- w[k + 0] /= total;
- w[k + 1] /= total;
- w[k + 2] /= total;
- w[k + 3] /= total;
- }
- }
- }
- array[Mesh::ARRAY_WEIGHTS] = weights;
- }
-
- if (p.has("indices")) {
- Vector<int> indices = _decode_accessor_as_ints(state, p["indices"], false);
-
- if (primitive == Mesh::PRIMITIVE_TRIANGLES) {
- //swap around indices, convert ccw to cw for front face
-
- const int is = indices.size();
- int *w = indices.ptrw();
- for (int k = 0; k < is; k += 3) {
- SWAP(w[k + 1], w[k + 2]);
- }
- }
- array[Mesh::ARRAY_INDEX] = indices;
-
- } else if (primitive == Mesh::PRIMITIVE_TRIANGLES) {
- //generate indices because they need to be swapped for CW/CCW
- const Vector<Vector3> &vertices = array[Mesh::ARRAY_VERTEX];
- ERR_FAIL_COND_V(vertices.size() == 0, ERR_PARSE_ERROR);
- Vector<int> indices;
- const int vs = vertices.size();
- indices.resize(vs);
- {
- int *w = indices.ptrw();
- for (int k = 0; k < vs; k += 3) {
- w[k] = k;
- w[k + 1] = k + 2;
- w[k + 2] = k + 1;
- }
- }
- array[Mesh::ARRAY_INDEX] = indices;
- }
-
- bool generate_tangents = (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL"));
-
- if (generate_tangents) {
- //must generate mikktspace tangents.. ergh..
- Ref<SurfaceTool> st;
- st.instance();
- st->create_from_triangle_arrays(array);
- st->generate_tangents();
- array = st->commit_to_arrays();
- }
-
- Array morphs;
- //blend shapes
- if (p.has("targets")) {
- print_verbose("glTF: Mesh has targets");
- const Array &targets = p["targets"];
-
- //ideally BLEND_SHAPE_MODE_RELATIVE since gltf2 stores in displacement
- //but it could require a larger refactor?
- mesh.mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
-
- if (j == 0) {
- const Array &target_names = extras.has("targetNames") ? (Array)extras["targetNames"] : Array();
- for (int k = 0; k < targets.size(); k++) {
- const String name = k < target_names.size() ? (String)target_names[k] : String("morph_") + itos(k);
- mesh.mesh->add_blend_shape(name);
- }
- }
-
- for (int k = 0; k < targets.size(); k++) {
- const Dictionary &t = targets[k];
-
- Array array_copy;
- array_copy.resize(Mesh::ARRAY_MAX);
-
- for (int l = 0; l < Mesh::ARRAY_MAX; l++) {
- array_copy[l] = array[l];
- }
-
- array_copy[Mesh::ARRAY_INDEX] = Variant();
-
- if (t.has("POSITION")) {
- Vector<Vector3> varr = _decode_accessor_as_vec3(state, t["POSITION"], true);
- const Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX];
- const int size = src_varr.size();
- ERR_FAIL_COND_V(size == 0, ERR_PARSE_ERROR);
- {
- const int max_idx = varr.size();
- varr.resize(size);
-
- Vector3 *w_varr = varr.ptrw();
- const Vector3 *r_varr = varr.ptr();
- const Vector3 *r_src_varr = src_varr.ptr();
- for (int l = 0; l < size; l++) {
- if (l < max_idx) {
- w_varr[l] = r_varr[l] + r_src_varr[l];
- } else {
- w_varr[l] = r_src_varr[l];
- }
- }
- }
- array_copy[Mesh::ARRAY_VERTEX] = varr;
- }
- if (t.has("NORMAL")) {
- Vector<Vector3> narr = _decode_accessor_as_vec3(state, t["NORMAL"], true);
- const Vector<Vector3> src_narr = array[Mesh::ARRAY_NORMAL];
- int size = src_narr.size();
- ERR_FAIL_COND_V(size == 0, ERR_PARSE_ERROR);
- {
- int max_idx = narr.size();
- narr.resize(size);
-
- Vector3 *w_narr = narr.ptrw();
- const Vector3 *r_narr = narr.ptr();
- const Vector3 *r_src_narr = src_narr.ptr();
- for (int l = 0; l < size; l++) {
- if (l < max_idx) {
- w_narr[l] = r_narr[l] + r_src_narr[l];
- } else {
- w_narr[l] = r_src_narr[l];
- }
- }
- }
- array_copy[Mesh::ARRAY_NORMAL] = narr;
- }
- if (t.has("TANGENT")) {
- const Vector<Vector3> tangents_v3 = _decode_accessor_as_vec3(state, t["TANGENT"], true);
- const Vector<float> src_tangents = array[Mesh::ARRAY_TANGENT];
- ERR_FAIL_COND_V(src_tangents.size() == 0, ERR_PARSE_ERROR);
-
- Vector<float> tangents_v4;
-
- {
- int max_idx = tangents_v3.size();
-
- int size4 = src_tangents.size();
- tangents_v4.resize(size4);
- float *w4 = tangents_v4.ptrw();
-
- const Vector3 *r3 = tangents_v3.ptr();
- const float *r4 = src_tangents.ptr();
-
- for (int l = 0; l < size4 / 4; l++) {
- if (l < max_idx) {
- w4[l * 4 + 0] = r3[l].x + r4[l * 4 + 0];
- w4[l * 4 + 1] = r3[l].y + r4[l * 4 + 1];
- w4[l * 4 + 2] = r3[l].z + r4[l * 4 + 2];
- } else {
- w4[l * 4 + 0] = r4[l * 4 + 0];
- w4[l * 4 + 1] = r4[l * 4 + 1];
- w4[l * 4 + 2] = r4[l * 4 + 2];
- }
- w4[l * 4 + 3] = r4[l * 4 + 3]; //copy flip value
- }
- }
-
- array_copy[Mesh::ARRAY_TANGENT] = tangents_v4;
- }
-
- if (generate_tangents) {
- Ref<SurfaceTool> st;
- st.instance();
- st->create_from_triangle_arrays(array_copy);
- st->deindex();
- st->generate_tangents();
- array_copy = st->commit_to_arrays();
- }
-
- morphs.push_back(array_copy);
- }
- }
-
- //just add it
- mesh.mesh->add_surface_from_arrays(primitive, array, morphs, Dictionary(), mesh_flags);
-
- if (p.has("material")) {
- const int material = p["material"];
- ERR_FAIL_INDEX_V(material, state.materials.size(), ERR_FILE_CORRUPT);
- const Ref<Material> &mat = state.materials[material];
-
- mesh.mesh->surface_set_material(mesh.mesh->get_surface_count() - 1, mat);
- } else {
- Ref<StandardMaterial3D> mat;
- mat.instance();
- mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
-
- mesh.mesh->surface_set_material(mesh.mesh->get_surface_count() - 1, mat);
- }
- }
-
- mesh.blend_weights.resize(mesh.mesh->get_blend_shape_count());
- for (int32_t weight_i = 0; weight_i < mesh.blend_weights.size(); weight_i++) {
- mesh.blend_weights.write[weight_i] = 0.0f;
- }
-
- if (d.has("weights")) {
- const Array &weights = d["weights"];
- ERR_FAIL_COND_V(mesh.blend_weights.size() != weights.size(), ERR_PARSE_ERROR);
- for (int j = 0; j < weights.size(); j++) {
- mesh.blend_weights.write[j] = weights[j];
- }
- }
-
- state.meshes.push_back(mesh);
- }
-
- print_verbose("glTF: Total meshes: " + itos(state.meshes.size()));
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_parse_images(GLTFState &state, const String &p_base_path) {
- if (!state.json.has("images")) {
- return OK;
- }
-
- const Array &images = state.json["images"];
- for (int i = 0; i < images.size(); i++) {
- const Dictionary &d = images[i];
-
- String mimetype;
- if (d.has("mimeType")) {
- mimetype = d["mimeType"];
- }
-
- Vector<uint8_t> data;
- const uint8_t *data_ptr = nullptr;
- int data_size = 0;
-
- if (d.has("uri")) {
- String uri = d["uri"];
-
- if (uri.findn("data:application/octet-stream;base64") == 0 ||
- uri.findn("data:" + mimetype + ";base64") == 0) {
- //embedded data
- data = _parse_base64_uri(uri);
- data_ptr = data.ptr();
- data_size = data.size();
- } else {
- uri = p_base_path.plus_file(uri).replace("\\", "/"); //fix for windows
- Ref<Texture2D> texture = ResourceLoader::load(uri);
- state.images.push_back(texture);
- continue;
- }
- }
-
- if (d.has("bufferView")) {
- const GLTFBufferViewIndex bvi = d["bufferView"];
-
- ERR_FAIL_INDEX_V(bvi, state.buffer_views.size(), ERR_PARAMETER_RANGE_ERROR);
-
- const GLTFBufferView &bv = state.buffer_views[bvi];
-
- const GLTFBufferIndex bi = bv.buffer;
- ERR_FAIL_INDEX_V(bi, state.buffers.size(), ERR_PARAMETER_RANGE_ERROR);
-
- ERR_FAIL_COND_V(bv.byte_offset + bv.byte_length > state.buffers[bi].size(), ERR_FILE_CORRUPT);
-
- data_ptr = &state.buffers[bi][bv.byte_offset];
- data_size = bv.byte_length;
- }
-
- ERR_FAIL_COND_V(mimetype == "", ERR_FILE_CORRUPT);
-
- if (mimetype.findn("png") != -1) {
- //is a png
- ERR_FAIL_COND_V(Image::_png_mem_loader_func == nullptr, ERR_UNAVAILABLE);
-
- const Ref<Image> img = Image::_png_mem_loader_func(data_ptr, data_size);
-
- ERR_FAIL_COND_V(img.is_null(), ERR_FILE_CORRUPT);
-
- Ref<ImageTexture> t;
- t.instance();
- t->create_from_image(img);
-
- state.images.push_back(t);
- continue;
- }
-
- if (mimetype.findn("jpeg") != -1) {
- //is a jpg
- ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == nullptr, ERR_UNAVAILABLE);
-
- const Ref<Image> img = Image::_jpg_mem_loader_func(data_ptr, data_size);
-
- ERR_FAIL_COND_V(img.is_null(), ERR_FILE_CORRUPT);
-
- Ref<ImageTexture> t;
- t.instance();
- t->create_from_image(img);
-
- state.images.push_back(t);
-
- continue;
- }
-
- ERR_FAIL_V(ERR_FILE_CORRUPT);
- }
-
- print_verbose("Total images: " + itos(state.images.size()));
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_parse_textures(GLTFState &state) {
- if (!state.json.has("textures")) {
- return OK;
- }
-
- const Array &textures = state.json["textures"];
- for (GLTFTextureIndex i = 0; i < textures.size(); i++) {
- const Dictionary &d = textures[i];
-
- ERR_FAIL_COND_V(!d.has("source"), ERR_PARSE_ERROR);
-
- GLTFTexture t;
- t.src_image = d["source"];
- state.textures.push_back(t);
- }
-
- return OK;
-}
-
-Ref<Texture2D> EditorSceneImporterGLTF::_get_texture(GLTFState &state, const GLTFTextureIndex p_texture) {
- ERR_FAIL_INDEX_V(p_texture, state.textures.size(), Ref<Texture2D>());
- const GLTFImageIndex image = state.textures[p_texture].src_image;
-
- ERR_FAIL_INDEX_V(image, state.images.size(), Ref<Texture2D>());
-
- return state.images[image];
-}
-
-Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) {
- if (!state.json.has("materials")) {
- return OK;
- }
-
- const Array &materials = state.json["materials"];
- for (GLTFMaterialIndex i = 0; i < materials.size(); i++) {
- const Dictionary &d = materials[i];
-
- Ref<StandardMaterial3D> material;
- material.instance();
- if (d.has("name")) {
- material->set_name(d["name"]);
- }
- material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
-
- if (d.has("pbrMetallicRoughness")) {
- const Dictionary &mr = d["pbrMetallicRoughness"];
- if (mr.has("baseColorFactor")) {
- const Array &arr = mr["baseColorFactor"];
- ERR_FAIL_COND_V(arr.size() != 4, ERR_PARSE_ERROR);
- const Color c = Color(arr[0], arr[1], arr[2], arr[3]).to_srgb();
-
- material->set_albedo(c);
- }
-
- if (mr.has("baseColorTexture")) {
- const Dictionary &bct = mr["baseColorTexture"];
- if (bct.has("index")) {
- material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, _get_texture(state, bct["index"]));
- }
- if (!mr.has("baseColorFactor")) {
- material->set_albedo(Color(1, 1, 1));
- }
- }
-
- if (mr.has("metallicFactor")) {
- material->set_metallic(mr["metallicFactor"]);
- } else {
- material->set_metallic(1.0);
- }
-
- if (mr.has("roughnessFactor")) {
- material->set_roughness(mr["roughnessFactor"]);
- } else {
- material->set_roughness(1.0);
- }
-
- if (mr.has("metallicRoughnessTexture")) {
- const Dictionary &bct = mr["metallicRoughnessTexture"];
- if (bct.has("index")) {
- const Ref<Texture2D> t = _get_texture(state, bct["index"]);
- material->set_texture(StandardMaterial3D::TEXTURE_METALLIC, t);
- material->set_metallic_texture_channel(StandardMaterial3D::TEXTURE_CHANNEL_BLUE);
- material->set_texture(StandardMaterial3D::TEXTURE_ROUGHNESS, t);
- material->set_roughness_texture_channel(StandardMaterial3D::TEXTURE_CHANNEL_GREEN);
- if (!mr.has("metallicFactor")) {
- material->set_metallic(1);
- }
- if (!mr.has("roughnessFactor")) {
- material->set_roughness(1);
- }
- }
- }
- }
-
- if (d.has("normalTexture")) {
- const Dictionary &bct = d["normalTexture"];
- if (bct.has("index")) {
- material->set_texture(StandardMaterial3D::TEXTURE_NORMAL, _get_texture(state, bct["index"]));
- material->set_feature(StandardMaterial3D::FEATURE_NORMAL_MAPPING, true);
- }
- if (bct.has("scale")) {
- material->set_normal_scale(bct["scale"]);
- }
- }
- if (d.has("occlusionTexture")) {
- const Dictionary &bct = d["occlusionTexture"];
- if (bct.has("index")) {
- material->set_texture(StandardMaterial3D::TEXTURE_AMBIENT_OCCLUSION, _get_texture(state, bct["index"]));
- material->set_ao_texture_channel(StandardMaterial3D::TEXTURE_CHANNEL_RED);
- material->set_feature(StandardMaterial3D::FEATURE_AMBIENT_OCCLUSION, true);
- }
- }
-
- if (d.has("emissiveFactor")) {
- const Array &arr = d["emissiveFactor"];
- ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR);
- const Color c = Color(arr[0], arr[1], arr[2]).to_srgb();
- material->set_feature(StandardMaterial3D::FEATURE_EMISSION, true);
-
- material->set_emission(c);
- }
-
- if (d.has("emissiveTexture")) {
- const Dictionary &bct = d["emissiveTexture"];
- if (bct.has("index")) {
- material->set_texture(StandardMaterial3D::TEXTURE_EMISSION, _get_texture(state, bct["index"]));
- material->set_feature(StandardMaterial3D::FEATURE_EMISSION, true);
- material->set_emission(Color(0, 0, 0));
- }
- }
-
- if (d.has("doubleSided")) {
- const bool ds = d["doubleSided"];
- if (ds) {
- material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
- }
- }
-
- if (d.has("alphaMode")) {
- const String &am = d["alphaMode"];
- if (am == "BLEND") {
- material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS);
- } else if (am == "MASK") {
- material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA_SCISSOR);
- if (d.has("alphaCutoff")) {
- material->set_alpha_scissor_threshold(d["alphaCutoff"]);
- } else {
- material->set_alpha_scissor_threshold(0.5f);
- }
- }
- }
-
- state.materials.push_back(material);
- }
-
- print_verbose("Total materials: " + itos(state.materials.size()));
-
- return OK;
-}
-
-EditorSceneImporterGLTF::GLTFNodeIndex EditorSceneImporterGLTF::_find_highest_node(GLTFState &state, const Vector<GLTFNodeIndex> &subset) {
- int highest = -1;
- GLTFNodeIndex best_node = -1;
-
- for (int i = 0; i < subset.size(); ++i) {
- const GLTFNodeIndex node_i = subset[i];
- const GLTFNode *node = state.nodes[node_i];
-
- if (highest == -1 || node->height < highest) {
- highest = node->height;
- best_node = node_i;
- }
- }
-
- return best_node;
-}
-
-bool EditorSceneImporterGLTF::_capture_nodes_in_skin(GLTFState &state, GLTFSkin &skin, const GLTFNodeIndex node_index) {
- bool found_joint = false;
-
- for (int i = 0; i < state.nodes[node_index]->children.size(); ++i) {
- found_joint |= _capture_nodes_in_skin(state, skin, state.nodes[node_index]->children[i]);
- }
-
- if (found_joint) {
- // Mark it if we happen to find another skins joint...
- if (state.nodes[node_index]->joint && skin.joints.find(node_index) < 0) {
- skin.joints.push_back(node_index);
- } else if (skin.non_joints.find(node_index) < 0) {
- skin.non_joints.push_back(node_index);
- }
- }
-
- if (skin.joints.find(node_index) > 0) {
- return true;
- }
-
- return false;
-}
-
-void EditorSceneImporterGLTF::_capture_nodes_for_multirooted_skin(GLTFState &state, GLTFSkin &skin) {
- DisjointSet<GLTFNodeIndex> disjoint_set;
-
- for (int i = 0; i < skin.joints.size(); ++i) {
- const GLTFNodeIndex node_index = skin.joints[i];
- const GLTFNodeIndex parent = state.nodes[node_index]->parent;
- disjoint_set.insert(node_index);
-
- if (skin.joints.find(parent) >= 0) {
- disjoint_set.create_union(parent, node_index);
- }
- }
-
- Vector<GLTFNodeIndex> roots;
- disjoint_set.get_representatives(roots);
-
- if (roots.size() <= 1) {
- return;
- }
-
- int maxHeight = -1;
-
- // Determine the max height rooted tree
- for (int i = 0; i < roots.size(); ++i) {
- const GLTFNodeIndex root = roots[i];
-
- if (maxHeight == -1 || state.nodes[root]->height < maxHeight) {
- maxHeight = state.nodes[root]->height;
- }
- }
-
- // Go up the tree till all of the multiple roots of the skin are at the same hierarchy level.
- // This sucks, but 99% of all game engines (not just Godot) would have this same issue.
- for (int i = 0; i < roots.size(); ++i) {
- GLTFNodeIndex current_node = roots[i];
- while (state.nodes[current_node]->height > maxHeight) {
- GLTFNodeIndex parent = state.nodes[current_node]->parent;
-
- if (state.nodes[parent]->joint && skin.joints.find(parent) < 0) {
- skin.joints.push_back(parent);
- } else if (skin.non_joints.find(parent) < 0) {
- skin.non_joints.push_back(parent);
- }
-
- current_node = parent;
- }
-
- // replace the roots
- roots.write[i] = current_node;
- }
-
- // Climb up the tree until they all have the same parent
- bool all_same;
-
- do {
- all_same = true;
- const GLTFNodeIndex first_parent = state.nodes[roots[0]]->parent;
-
- for (int i = 1; i < roots.size(); ++i) {
- all_same &= (first_parent == state.nodes[roots[i]]->parent);
- }
-
- if (!all_same) {
- for (int i = 0; i < roots.size(); ++i) {
- const GLTFNodeIndex current_node = roots[i];
- const GLTFNodeIndex parent = state.nodes[current_node]->parent;
-
- if (state.nodes[parent]->joint && skin.joints.find(parent) < 0) {
- skin.joints.push_back(parent);
- } else if (skin.non_joints.find(parent) < 0) {
- skin.non_joints.push_back(parent);
- }
-
- roots.write[i] = parent;
- }
- }
-
- } while (!all_same);
-}
-
-Error EditorSceneImporterGLTF::_expand_skin(GLTFState &state, GLTFSkin &skin) {
- _capture_nodes_for_multirooted_skin(state, skin);
-
- // Grab all nodes that lay in between skin joints/nodes
- DisjointSet<GLTFNodeIndex> disjoint_set;
-
- Vector<GLTFNodeIndex> all_skin_nodes;
- all_skin_nodes.append_array(skin.joints);
- all_skin_nodes.append_array(skin.non_joints);
-
- for (int i = 0; i < all_skin_nodes.size(); ++i) {
- const GLTFNodeIndex node_index = all_skin_nodes[i];
- const GLTFNodeIndex parent = state.nodes[node_index]->parent;
- disjoint_set.insert(node_index);
-
- if (all_skin_nodes.find(parent) >= 0) {
- disjoint_set.create_union(parent, node_index);
- }
- }
-
- Vector<GLTFNodeIndex> out_owners;
- disjoint_set.get_representatives(out_owners);
-
- Vector<GLTFNodeIndex> out_roots;
-
- for (int i = 0; i < out_owners.size(); ++i) {
- Vector<GLTFNodeIndex> set;
- disjoint_set.get_members(set, out_owners[i]);
-
- const GLTFNodeIndex root = _find_highest_node(state, set);
- ERR_FAIL_COND_V(root < 0, FAILED);
- out_roots.push_back(root);
- }
-
- out_roots.sort();
-
- for (int i = 0; i < out_roots.size(); ++i) {
- _capture_nodes_in_skin(state, skin, out_roots[i]);
- }
-
- skin.roots = out_roots;
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_verify_skin(GLTFState &state, GLTFSkin &skin) {
- // This may seem duplicated from expand_skins, but this is really a sanity check! (so it kinda is)
- // In case additional interpolating logic is added to the skins, this will help ensure that you
- // do not cause it to self implode into a fiery blaze
-
- // We are going to re-calculate the root nodes and compare them to the ones saved in the skin,
- // then ensure the multiple trees (if they exist) are on the same sublevel
-
- // Grab all nodes that lay in between skin joints/nodes
- DisjointSet<GLTFNodeIndex> disjoint_set;
-
- Vector<GLTFNodeIndex> all_skin_nodes;
- all_skin_nodes.append_array(skin.joints);
- all_skin_nodes.append_array(skin.non_joints);
-
- for (int i = 0; i < all_skin_nodes.size(); ++i) {
- const GLTFNodeIndex node_index = all_skin_nodes[i];
- const GLTFNodeIndex parent = state.nodes[node_index]->parent;
- disjoint_set.insert(node_index);
-
- if (all_skin_nodes.find(parent) >= 0) {
- disjoint_set.create_union(parent, node_index);
- }
- }
-
- Vector<GLTFNodeIndex> out_owners;
- disjoint_set.get_representatives(out_owners);
-
- Vector<GLTFNodeIndex> out_roots;
-
- for (int i = 0; i < out_owners.size(); ++i) {
- Vector<GLTFNodeIndex> set;
- disjoint_set.get_members(set, out_owners[i]);
-
- const GLTFNodeIndex root = _find_highest_node(state, set);
- ERR_FAIL_COND_V(root < 0, FAILED);
- out_roots.push_back(root);
- }
-
- out_roots.sort();
-
- ERR_FAIL_COND_V(out_roots.size() == 0, FAILED);
-
- // Make sure the roots are the exact same (they better be)
- ERR_FAIL_COND_V(out_roots.size() != skin.roots.size(), FAILED);
- for (int i = 0; i < out_roots.size(); ++i) {
- ERR_FAIL_COND_V(out_roots[i] != skin.roots[i], FAILED);
- }
-
- // Single rooted skin? Perfectly ok!
- if (out_roots.size() == 1) {
- return OK;
- }
-
- // Make sure all parents of a multi-rooted skin are the SAME
- const GLTFNodeIndex parent = state.nodes[out_roots[0]]->parent;
- for (int i = 1; i < out_roots.size(); ++i) {
- if (state.nodes[out_roots[i]]->parent != parent) {
- return FAILED;
- }
- }
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_parse_skins(GLTFState &state) {
- if (!state.json.has("skins")) {
- return OK;
- }
-
- const Array &skins = state.json["skins"];
-
- // Create the base skins, and mark nodes that are joints
- for (int i = 0; i < skins.size(); i++) {
- const Dictionary &d = skins[i];
-
- GLTFSkin skin;
-
- ERR_FAIL_COND_V(!d.has("joints"), ERR_PARSE_ERROR);
-
- const Array &joints = d["joints"];
-
- if (d.has("inverseBindMatrices")) {
- skin.inverse_binds = _decode_accessor_as_xform(state, d["inverseBindMatrices"], false);
- ERR_FAIL_COND_V(skin.inverse_binds.size() != joints.size(), ERR_PARSE_ERROR);
- }
-
- for (int j = 0; j < joints.size(); j++) {
- const GLTFNodeIndex node = joints[j];
- ERR_FAIL_INDEX_V(node, state.nodes.size(), ERR_PARSE_ERROR);
-
- skin.joints.push_back(node);
- skin.joints_original.push_back(node);
-
- state.nodes[node]->joint = true;
- }
-
- if (d.has("name")) {
- skin.name = d["name"];
- }
-
- if (d.has("skeleton")) {
- skin.skin_root = d["skeleton"];
- }
-
- state.skins.push_back(skin);
- }
-
- for (GLTFSkinIndex i = 0; i < state.skins.size(); ++i) {
- GLTFSkin &skin = state.skins.write[i];
-
- // Expand the skin to capture all the extra non-joints that lie in between the actual joints,
- // and expand the hierarchy to ensure multi-rooted trees lie on the same height level
- ERR_FAIL_COND_V(_expand_skin(state, skin), ERR_PARSE_ERROR);
- ERR_FAIL_COND_V(_verify_skin(state, skin), ERR_PARSE_ERROR);
- }
-
- print_verbose("glTF: Total skins: " + itos(state.skins.size()));
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_determine_skeletons(GLTFState &state) {
- // Using a disjoint set, we are going to potentially combine all skins that are actually branches
- // of a main skeleton, or treat skins defining the same set of nodes as ONE skeleton.
- // This is another unclear issue caused by the current glTF specification.
-
- DisjointSet<GLTFNodeIndex> skeleton_sets;
-
- for (GLTFSkinIndex skin_i = 0; skin_i < state.skins.size(); ++skin_i) {
- const GLTFSkin &skin = state.skins[skin_i];
-
- Vector<GLTFNodeIndex> all_skin_nodes;
- all_skin_nodes.append_array(skin.joints);
- all_skin_nodes.append_array(skin.non_joints);
-
- for (int i = 0; i < all_skin_nodes.size(); ++i) {
- const GLTFNodeIndex node_index = all_skin_nodes[i];
- const GLTFNodeIndex parent = state.nodes[node_index]->parent;
- skeleton_sets.insert(node_index);
-
- if (all_skin_nodes.find(parent) >= 0) {
- skeleton_sets.create_union(parent, node_index);
- }
- }
-
- // We are going to connect the separate skin subtrees in each skin together
- // so that the final roots are entire sets of valid skin trees
- for (int i = 1; i < skin.roots.size(); ++i) {
- skeleton_sets.create_union(skin.roots[0], skin.roots[i]);
- }
- }
-
- { // attempt to joint all touching subsets (siblings/parent are part of another skin)
- Vector<GLTFNodeIndex> groups_representatives;
- skeleton_sets.get_representatives(groups_representatives);
-
- Vector<GLTFNodeIndex> highest_group_members;
- Vector<Vector<GLTFNodeIndex>> groups;
- for (int i = 0; i < groups_representatives.size(); ++i) {
- Vector<GLTFNodeIndex> group;
- skeleton_sets.get_members(group, groups_representatives[i]);
- highest_group_members.push_back(_find_highest_node(state, group));
- groups.push_back(group);
- }
-
- for (int i = 0; i < highest_group_members.size(); ++i) {
- const GLTFNodeIndex node_i = highest_group_members[i];
-
- // Attach any siblings together (this needs to be done n^2/2 times)
- for (int j = i + 1; j < highest_group_members.size(); ++j) {
- const GLTFNodeIndex node_j = highest_group_members[j];
-
- // Even if they are siblings under the root! :)
- if (state.nodes[node_i]->parent == state.nodes[node_j]->parent) {
- skeleton_sets.create_union(node_i, node_j);
- }
- }
-
- // Attach any parenting going on together (we need to do this n^2 times)
- const GLTFNodeIndex node_i_parent = state.nodes[node_i]->parent;
- if (node_i_parent >= 0) {
- for (int j = 0; j < groups.size() && i != j; ++j) {
- const Vector<GLTFNodeIndex> &group = groups[j];
-
- if (group.find(node_i_parent) >= 0) {
- const GLTFNodeIndex node_j = highest_group_members[j];
- skeleton_sets.create_union(node_i, node_j);
- }
- }
- }
- }
- }
-
- // At this point, the skeleton groups should be finalized
- Vector<GLTFNodeIndex> skeleton_owners;
- skeleton_sets.get_representatives(skeleton_owners);
-
- // Mark all the skins actual skeletons, after we have merged them
- for (GLTFSkeletonIndex skel_i = 0; skel_i < skeleton_owners.size(); ++skel_i) {
- const GLTFNodeIndex skeleton_owner = skeleton_owners[skel_i];
- GLTFSkeleton skeleton;
-
- Vector<GLTFNodeIndex> skeleton_nodes;
- skeleton_sets.get_members(skeleton_nodes, skeleton_owner);
-
- for (GLTFSkinIndex skin_i = 0; skin_i < state.skins.size(); ++skin_i) {
- GLTFSkin &skin = state.skins.write[skin_i];
-
- // If any of the the skeletons nodes exist in a skin, that skin now maps to the skeleton
- for (int i = 0; i < skeleton_nodes.size(); ++i) {
- GLTFNodeIndex skel_node_i = skeleton_nodes[i];
- if (skin.joints.find(skel_node_i) >= 0 || skin.non_joints.find(skel_node_i) >= 0) {
- skin.skeleton = skel_i;
- continue;
- }
- }
- }
-
- Vector<GLTFNodeIndex> non_joints;
- for (int i = 0; i < skeleton_nodes.size(); ++i) {
- const GLTFNodeIndex node_i = skeleton_nodes[i];
-
- if (state.nodes[node_i]->joint) {
- skeleton.joints.push_back(node_i);
- } else {
- non_joints.push_back(node_i);
- }
- }
-
- state.skeletons.push_back(skeleton);
-
- _reparent_non_joint_skeleton_subtrees(state, state.skeletons.write[skel_i], non_joints);
- }
-
- for (GLTFSkeletonIndex skel_i = 0; skel_i < state.skeletons.size(); ++skel_i) {
- GLTFSkeleton &skeleton = state.skeletons.write[skel_i];
-
- for (int i = 0; i < skeleton.joints.size(); ++i) {
- const GLTFNodeIndex node_i = skeleton.joints[i];
- GLTFNode *node = state.nodes[node_i];
-
- ERR_FAIL_COND_V(!node->joint, ERR_PARSE_ERROR);
- ERR_FAIL_COND_V(node->skeleton >= 0, ERR_PARSE_ERROR);
- node->skeleton = skel_i;
- }
-
- ERR_FAIL_COND_V(_determine_skeleton_roots(state, skel_i), ERR_PARSE_ERROR);
- }
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_reparent_non_joint_skeleton_subtrees(GLTFState &state, GLTFSkeleton &skeleton, const Vector<GLTFNodeIndex> &non_joints) {
- DisjointSet<GLTFNodeIndex> subtree_set;
-
- // Populate the disjoint set with ONLY non joints that are in the skeleton hierarchy (non_joints vector)
- // This way we can find any joints that lie in between joints, as the current glTF specification
- // mentions nothing about non-joints being in between joints of the same skin. Hopefully one day we
- // can remove this code.
-
- // skinD depicted here explains this issue:
- // https://github.com/KhronosGroup/glTF-Asset-Generator/blob/master/Output/Positive/Animation_Skin
-
- for (int i = 0; i < non_joints.size(); ++i) {
- const GLTFNodeIndex node_i = non_joints[i];
-
- subtree_set.insert(node_i);
-
- const GLTFNodeIndex parent_i = state.nodes[node_i]->parent;
- if (parent_i >= 0 && non_joints.find(parent_i) >= 0 && !state.nodes[parent_i]->joint) {
- subtree_set.create_union(parent_i, node_i);
- }
- }
-
- // Find all the non joint subtrees and re-parent them to a new "fake" joint
-
- Vector<GLTFNodeIndex> non_joint_subtree_roots;
- subtree_set.get_representatives(non_joint_subtree_roots);
-
- for (int root_i = 0; root_i < non_joint_subtree_roots.size(); ++root_i) {
- const GLTFNodeIndex subtree_root = non_joint_subtree_roots[root_i];
-
- Vector<GLTFNodeIndex> subtree_nodes;
- subtree_set.get_members(subtree_nodes, subtree_root);
-
- for (int subtree_i = 0; subtree_i < subtree_nodes.size(); ++subtree_i) {
- ERR_FAIL_COND_V(_reparent_to_fake_joint(state, skeleton, subtree_nodes[subtree_i]), FAILED);
-
- // We modified the tree, recompute all the heights
- _compute_node_heights(state);
- }
- }
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_reparent_to_fake_joint(GLTFState &state, GLTFSkeleton &skeleton, const GLTFNodeIndex node_index) {
- GLTFNode *node = state.nodes[node_index];
-
- // Can we just "steal" this joint if it is just a spatial node?
- if (node->skin < 0 && node->mesh < 0 && node->camera < 0) {
- node->joint = true;
- // Add the joint to the skeletons joints
- skeleton.joints.push_back(node_index);
- return OK;
- }
-
- GLTFNode *fake_joint = memnew(GLTFNode);
- const GLTFNodeIndex fake_joint_index = state.nodes.size();
- state.nodes.push_back(fake_joint);
-
- // We better not be a joint, or we messed up in our logic
- if (node->joint) {
- return FAILED;
- }
-
- fake_joint->translation = node->translation;
- fake_joint->rotation = node->rotation;
- fake_joint->scale = node->scale;
- fake_joint->xform = node->xform;
- fake_joint->joint = true;
-
- // We can use the exact same name here, because the joint will be inside a skeleton and not the scene
- fake_joint->name = node->name;
-
- // Clear the nodes transforms, since it will be parented to the fake joint
- node->translation = Vector3(0, 0, 0);
- node->rotation = Quat();
- node->scale = Vector3(1, 1, 1);
- node->xform = Transform();
-
- // Transfer the node children to the fake joint
- for (int child_i = 0; child_i < node->children.size(); ++child_i) {
- GLTFNode *child = state.nodes[node->children[child_i]];
- child->parent = fake_joint_index;
- }
-
- fake_joint->children = node->children;
- node->children.clear();
-
- // add the fake joint to the parent and remove the original joint
- if (node->parent >= 0) {
- GLTFNode *parent = state.nodes[node->parent];
- parent->children.erase(node_index);
- parent->children.push_back(fake_joint_index);
- fake_joint->parent = node->parent;
- }
-
- // Add the node to the fake joint
- fake_joint->children.push_back(node_index);
- node->parent = fake_joint_index;
- node->fake_joint_parent = fake_joint_index;
-
- // Add the fake joint to the skeletons joints
- skeleton.joints.push_back(fake_joint_index);
-
- // Replace skin_skeletons with fake joints if we must.
- for (GLTFSkinIndex skin_i = 0; skin_i < state.skins.size(); ++skin_i) {
- GLTFSkin &skin = state.skins.write[skin_i];
- if (skin.skin_root == node_index) {
- skin.skin_root = fake_joint_index;
- }
- }
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_determine_skeleton_roots(GLTFState &state, const GLTFSkeletonIndex skel_i) {
- DisjointSet<GLTFNodeIndex> disjoint_set;
-
- for (GLTFNodeIndex i = 0; i < state.nodes.size(); ++i) {
- const GLTFNode *node = state.nodes[i];
-
- if (node->skeleton != skel_i) {
- continue;
- }
-
- disjoint_set.insert(i);
-
- if (node->parent >= 0 && state.nodes[node->parent]->skeleton == skel_i) {
- disjoint_set.create_union(node->parent, i);
- }
- }
-
- GLTFSkeleton &skeleton = state.skeletons.write[skel_i];
-
- Vector<GLTFNodeIndex> owners;
- disjoint_set.get_representatives(owners);
-
- Vector<GLTFNodeIndex> roots;
-
- for (int i = 0; i < owners.size(); ++i) {
- Vector<GLTFNodeIndex> set;
- disjoint_set.get_members(set, owners[i]);
- const GLTFNodeIndex root = _find_highest_node(state, set);
- ERR_FAIL_COND_V(root < 0, FAILED);
- roots.push_back(root);
- }
-
- roots.sort();
-
- skeleton.roots = roots;
-
- if (roots.size() == 0) {
- return FAILED;
- } else if (roots.size() == 1) {
- return OK;
- }
-
- // Check that the subtrees have the same parent root
- const GLTFNodeIndex parent = state.nodes[roots[0]]->parent;
- for (int i = 1; i < roots.size(); ++i) {
- if (state.nodes[roots[i]]->parent != parent) {
- return FAILED;
- }
- }
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_create_skeletons(GLTFState &state) {
- for (GLTFSkeletonIndex skel_i = 0; skel_i < state.skeletons.size(); ++skel_i) {
- GLTFSkeleton &gltf_skeleton = state.skeletons.write[skel_i];
-
- Skeleton3D *skeleton = memnew(Skeleton3D);
- gltf_skeleton.godot_skeleton = skeleton;
-
- // Make a unique name, no gltf node represents this skeleton
- skeleton->set_name(_gen_unique_name(state, "Skeleton"));
-
- List<GLTFNodeIndex> bones;
-
- for (int i = 0; i < gltf_skeleton.roots.size(); ++i) {
- bones.push_back(gltf_skeleton.roots[i]);
- }
-
- // Make the skeleton creation deterministic by going through the roots in
- // a sorted order, and DEPTH FIRST
- bones.sort();
-
- while (!bones.empty()) {
- const GLTFNodeIndex node_i = bones.front()->get();
- bones.pop_front();
-
- GLTFNode *node = state.nodes[node_i];
- ERR_FAIL_COND_V(node->skeleton != skel_i, FAILED);
-
- { // Add all child nodes to the stack (deterministically)
- Vector<GLTFNodeIndex> child_nodes;
- for (int i = 0; i < node->children.size(); ++i) {
- const GLTFNodeIndex child_i = node->children[i];
- if (state.nodes[child_i]->skeleton == skel_i) {
- child_nodes.push_back(child_i);
- }
- }
-
- // Depth first insertion
- child_nodes.sort();
- for (int i = child_nodes.size() - 1; i >= 0; --i) {
- bones.push_front(child_nodes[i]);
- }
- }
-
- const int bone_index = skeleton->get_bone_count();
-
- if (node->name.empty()) {
- node->name = "bone";
- }
-
- node->name = _gen_unique_bone_name(state, skel_i, node->name);
-
- skeleton->add_bone(node->name);
- skeleton->set_bone_rest(bone_index, node->xform);
-
- if (node->parent >= 0 && state.nodes[node->parent]->skeleton == skel_i) {
- const int bone_parent = skeleton->find_bone(state.nodes[node->parent]->name);
- ERR_FAIL_COND_V(bone_parent < 0, FAILED);
- skeleton->set_bone_parent(bone_index, skeleton->find_bone(state.nodes[node->parent]->name));
- }
-
- state.scene_nodes.insert(node_i, skeleton);
- }
- }
-
- ERR_FAIL_COND_V(_map_skin_joints_indices_to_skeleton_bone_indices(state), ERR_PARSE_ERROR);
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_map_skin_joints_indices_to_skeleton_bone_indices(GLTFState &state) {
- for (GLTFSkinIndex skin_i = 0; skin_i < state.skins.size(); ++skin_i) {
- GLTFSkin &skin = state.skins.write[skin_i];
-
- const GLTFSkeleton &skeleton = state.skeletons[skin.skeleton];
-
- for (int joint_index = 0; joint_index < skin.joints_original.size(); ++joint_index) {
- const GLTFNodeIndex node_i = skin.joints_original[joint_index];
- const GLTFNode *node = state.nodes[node_i];
-
- skin.joint_i_to_name.insert(joint_index, node->name);
-
- const int bone_index = skeleton.godot_skeleton->find_bone(node->name);
- ERR_FAIL_COND_V(bone_index < 0, FAILED);
-
- skin.joint_i_to_bone_i.insert(joint_index, bone_index);
- }
- }
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_create_skins(GLTFState &state) {
- for (GLTFSkinIndex skin_i = 0; skin_i < state.skins.size(); ++skin_i) {
- GLTFSkin &gltf_skin = state.skins.write[skin_i];
-
- Ref<Skin> skin;
- skin.instance();
-
- // Some skins don't have IBM's! What absolute monsters!
- const bool has_ibms = !gltf_skin.inverse_binds.empty();
-
- for (int joint_i = 0; joint_i < gltf_skin.joints_original.size(); ++joint_i) {
- Transform xform;
- if (has_ibms) {
- xform = gltf_skin.inverse_binds[joint_i];
- }
-
- if (state.use_named_skin_binds) {
- StringName name = gltf_skin.joint_i_to_name[joint_i];
- skin->add_named_bind(name, xform);
- } else {
- int bone_i = gltf_skin.joint_i_to_bone_i[joint_i];
- skin->add_bind(bone_i, xform);
- }
- }
-
- gltf_skin.godot_skin = skin;
- }
-
- // Purge the duplicates!
- _remove_duplicate_skins(state);
-
- // Create unique names now, after removing duplicates
- for (GLTFSkinIndex skin_i = 0; skin_i < state.skins.size(); ++skin_i) {
- Ref<Skin> skin = state.skins[skin_i].godot_skin;
- if (skin->get_name().empty()) {
- // Make a unique name, no gltf node represents this skin
- skin->set_name(_gen_unique_name(state, "Skin"));
- }
- }
-
- return OK;
-}
-
-bool EditorSceneImporterGLTF::_skins_are_same(const Ref<Skin> &skin_a, const Ref<Skin> &skin_b) {
- if (skin_a->get_bind_count() != skin_b->get_bind_count()) {
- return false;
- }
-
- for (int i = 0; i < skin_a->get_bind_count(); ++i) {
- if (skin_a->get_bind_bone(i) != skin_b->get_bind_bone(i)) {
- return false;
- }
-
- Transform a_xform = skin_a->get_bind_pose(i);
- Transform b_xform = skin_b->get_bind_pose(i);
-
- if (a_xform != b_xform) {
- return false;
- }
- }
-
- return true;
-}
-
-void EditorSceneImporterGLTF::_remove_duplicate_skins(GLTFState &state) {
- for (int i = 0; i < state.skins.size(); ++i) {
- for (int j = i + 1; j < state.skins.size(); ++j) {
- const Ref<Skin> &skin_i = state.skins[i].godot_skin;
- const Ref<Skin> &skin_j = state.skins[j].godot_skin;
-
- if (_skins_are_same(skin_i, skin_j)) {
- // replace it and delete the old
- state.skins.write[j].godot_skin = skin_i;
- }
- }
- }
-}
-
-Error EditorSceneImporterGLTF::_parse_lights(GLTFState &state) {
- if (!state.json.has("extensions")) {
- return OK;
- }
- Dictionary extensions = state.json["extensions"];
- if (!extensions.has("KHR_lights_punctual")) {
- return OK;
- }
- Dictionary lights_punctual = extensions["KHR_lights_punctual"];
- if (!lights_punctual.has("lights")) {
- return OK;
- }
-
- const Array &lights = lights_punctual["lights"];
-
- for (GLTFLightIndex light_i = 0; light_i < lights.size(); light_i++) {
- const Dictionary &d = lights[light_i];
-
- GLTFLight light;
- ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
- const String &type = d["type"];
- light.type = type;
-
- if (d.has("color")) {
- const Array &arr = d["color"];
- ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR);
- const Color c = Color(arr[0], arr[1], arr[2]).to_srgb();
- light.color = c;
- }
- if (d.has("intensity")) {
- light.intensity = d["intensity"];
- }
- if (d.has("range")) {
- light.range = d["range"];
- }
- if (type == "spot") {
- const Dictionary &spot = d["spot"];
- light.inner_cone_angle = spot["innerConeAngle"];
- light.outer_cone_angle = spot["outerConeAngle"];
- ERR_FAIL_COND_V_MSG(light.inner_cone_angle >= light.outer_cone_angle, ERR_PARSE_ERROR, "The inner angle must be smaller than the outer angle.");
- } else if (type != "point" && type != "directional") {
- ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "Light type is unknown.");
- }
-
- state.lights.push_back(light);
- }
-
- print_verbose("glTF: Total lights: " + itos(state.lights.size()));
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_parse_cameras(GLTFState &state) {
- if (!state.json.has("cameras")) {
- return OK;
- }
-
- const Array &cameras = state.json["cameras"];
-
- for (GLTFCameraIndex i = 0; i < cameras.size(); i++) {
- const Dictionary &d = cameras[i];
-
- GLTFCamera camera;
- ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
- const String &type = d["type"];
- if (type == "orthographic") {
- camera.perspective = false;
- if (d.has("orthographic")) {
- const Dictionary &og = d["orthographic"];
- camera.fov_size = og["ymag"];
- camera.zfar = og["zfar"];
- camera.znear = og["znear"];
- } else {
- camera.fov_size = 10;
- }
-
- } else if (type == "perspective") {
- camera.perspective = true;
- if (d.has("perspective")) {
- const Dictionary &ppt = d["perspective"];
- // GLTF spec is in radians, Godot's camera is in degrees.
- camera.fov_size = (double)ppt["yfov"] * 180.0 / Math_PI;
- camera.zfar = ppt["zfar"];
- camera.znear = ppt["znear"];
- } else {
- camera.fov_size = 10;
- }
- } else {
- ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "Camera should be in 'orthographic' or 'perspective'");
- }
-
- state.cameras.push_back(camera);
- }
-
- print_verbose("glTF: Total cameras: " + itos(state.cameras.size()));
-
- return OK;
-}
-
-Error EditorSceneImporterGLTF::_parse_animations(GLTFState &state) {
- if (!state.json.has("animations")) {
- return OK;
- }
-
- const Array &animations = state.json["animations"];
-
- for (GLTFAnimationIndex i = 0; i < animations.size(); i++) {
- const Dictionary &d = animations[i];
-
- GLTFAnimation animation;
-
- if (!d.has("channels") || !d.has("samplers")) {
- continue;
- }
-
- Array channels = d["channels"];
- Array samplers = d["samplers"];
-
- if (d.has("name")) {
- String name = d["name"];
- if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
- animation.loop = true;
- }
- animation.name = _sanitize_scene_name(name);
- }
-
- for (int j = 0; j < channels.size(); j++) {
- const Dictionary &c = channels[j];
- if (!c.has("target")) {
- continue;
- }
-
- const Dictionary &t = c["target"];
- if (!t.has("node") || !t.has("path")) {
- continue;
- }
-
- ERR_FAIL_COND_V(!c.has("sampler"), ERR_PARSE_ERROR);
- const int sampler = c["sampler"];
- ERR_FAIL_INDEX_V(sampler, samplers.size(), ERR_PARSE_ERROR);
-
- GLTFNodeIndex node = t["node"];
- String path = t["path"];
-
- ERR_FAIL_INDEX_V(node, state.nodes.size(), ERR_PARSE_ERROR);
-
- GLTFAnimation::Track *track = nullptr;
-
- if (!animation.tracks.has(node)) {
- animation.tracks[node] = GLTFAnimation::Track();
- }
-
- track = &animation.tracks[node];
-
- const Dictionary &s = samplers[sampler];
-
- ERR_FAIL_COND_V(!s.has("input"), ERR_PARSE_ERROR);
- ERR_FAIL_COND_V(!s.has("output"), ERR_PARSE_ERROR);
-
- const int input = s["input"];
- const int output = s["output"];
-
- GLTFAnimation::Interpolation interp = GLTFAnimation::INTERP_LINEAR;
- int output_count = 1;
- if (s.has("interpolation")) {
- const String &in = s["interpolation"];
- if (in == "STEP") {
- interp = GLTFAnimation::INTERP_STEP;
- } else if (in == "LINEAR") {
- interp = GLTFAnimation::INTERP_LINEAR;
- } else if (in == "CATMULLROMSPLINE") {
- interp = GLTFAnimation::INTERP_CATMULLROMSPLINE;
- output_count = 3;
- } else if (in == "CUBICSPLINE") {
- interp = GLTFAnimation::INTERP_CUBIC_SPLINE;
- output_count = 3;
- }
- }
-
- const Vector<float> times = _decode_accessor_as_floats(state, input, false);
- if (path == "translation") {
- const Vector<Vector3> translations = _decode_accessor_as_vec3(state, output, false);
- track->translation_track.interpolation = interp;
- track->translation_track.times = Variant(times); //convert via variant
- track->translation_track.values = Variant(translations); //convert via variant
- } else if (path == "rotation") {
- const Vector<Quat> rotations = _decode_accessor_as_quat(state, output, false);
- track->rotation_track.interpolation = interp;
- track->rotation_track.times = Variant(times); //convert via variant
- track->rotation_track.values = rotations; //convert via variant
- } else if (path == "scale") {
- const Vector<Vector3> scales = _decode_accessor_as_vec3(state, output, false);
- track->scale_track.interpolation = interp;
- track->scale_track.times = Variant(times); //convert via variant
- track->scale_track.values = Variant(scales); //convert via variant
- } else if (path == "weights") {
- const Vector<float> weights = _decode_accessor_as_floats(state, output, false);
-
- ERR_FAIL_INDEX_V(state.nodes[node]->mesh, state.meshes.size(), ERR_PARSE_ERROR);
- const GLTFMesh *mesh = &state.meshes[state.nodes[node]->mesh];
- ERR_FAIL_COND_V(mesh->blend_weights.size() == 0, ERR_PARSE_ERROR);
- const int wc = mesh->blend_weights.size();
-
- track->weight_tracks.resize(wc);
-
- const int expected_value_count = times.size() * output_count * wc;
- ERR_FAIL_COND_V_MSG(weights.size() != expected_value_count, ERR_PARSE_ERROR, "Invalid weight data, expected " + itos(expected_value_count) + " weight values, got " + itos(weights.size()) + " instead.");
-
- const int wlen = weights.size() / wc;
- const float *r = weights.ptr();
- for (int k = 0; k < wc; k++) { //separate tracks, having them together is not such a good idea
- GLTFAnimation::Channel<float> cf;
- cf.interpolation = interp;
- cf.times = Variant(times);
- Vector<float> wdata;
- wdata.resize(wlen);
- for (int l = 0; l < wlen; l++) {
- wdata.write[l] = r[l * wc + k];
- }
-
- cf.values = wdata;
- track->weight_tracks.write[k] = cf;
- }
- } else {
- WARN_PRINT("Invalid path '" + path + "'.");
- }
- }
-
- state.animations.push_back(animation);
- }
-
- print_verbose("glTF: Total animations '" + itos(state.animations.size()) + "'.");
-
- return OK;
-}
-
-void EditorSceneImporterGLTF::_assign_scene_names(GLTFState &state) {
- for (int i = 0; i < state.nodes.size(); i++) {
- GLTFNode *n = state.nodes[i];
-
- // Any joints get unique names generated when the skeleton is made, unique to the skeleton
- if (n->skeleton >= 0) {
- continue;
- }
-
- if (n->name.empty()) {
- if (n->mesh >= 0) {
- n->name = "Mesh";
- } else if (n->camera >= 0) {
- n->name = "Camera";
- } else {
- n->name = "Node";
- }
- }
-
- n->name = _gen_unique_name(state, n->name);
- }
-}
-
-BoneAttachment3D *EditorSceneImporterGLTF::_generate_bone_attachment(GLTFState &state, Skeleton3D *skeleton, const GLTFNodeIndex node_index) {
- const GLTFNode *gltf_node = state.nodes[node_index];
- const GLTFNode *bone_node = state.nodes[gltf_node->parent];
-
- BoneAttachment3D *bone_attachment = memnew(BoneAttachment3D);
- print_verbose("glTF: Creating bone attachment for: " + gltf_node->name);
-
- ERR_FAIL_COND_V(!bone_node->joint, nullptr);
-
- bone_attachment->set_bone_name(bone_node->name);
-
- return bone_attachment;
-}
-
-MeshInstance3D *EditorSceneImporterGLTF::_generate_mesh_instance(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) {
- const GLTFNode *gltf_node = state.nodes[node_index];
-
- ERR_FAIL_INDEX_V(gltf_node->mesh, state.meshes.size(), nullptr);
-
- MeshInstance3D *mi = memnew(MeshInstance3D);
- print_verbose("glTF: Creating mesh for: " + gltf_node->name);
-
- GLTFMesh &mesh = state.meshes.write[gltf_node->mesh];
- mi->set_mesh(mesh.mesh);
-
- if (mesh.mesh->get_name() == "") {
- mesh.mesh->set_name(gltf_node->name);
- }
-
- for (int i = 0; i < mesh.blend_weights.size(); i++) {
- mi->set("blend_shapes/" + mesh.mesh->get_blend_shape_name(i), mesh.blend_weights[i]);
- }
-
- return mi;
-}
-
-Light3D *EditorSceneImporterGLTF::_generate_light(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) {
- const GLTFNode *gltf_node = state.nodes[node_index];
-
- ERR_FAIL_INDEX_V(gltf_node->light, state.lights.size(), nullptr);
-
- print_verbose("glTF: Creating light for: " + gltf_node->name);
-
- const GLTFLight &l = state.lights[gltf_node->light];
-
- float intensity = l.intensity;
- if (intensity > 10) {
- // GLTF spec has the default around 1, but Blender defaults lights to 100.
- // The only sane way to handle this is to check where it came from and
- // handle it accordingly. If it's over 10, it probably came from Blender.
- intensity /= 100;
- }
-
- if (l.type == "directional") {
- DirectionalLight3D *light = memnew(DirectionalLight3D);
- light->set_param(Light3D::PARAM_ENERGY, intensity);
- light->set_color(l.color);
- return light;
- }
-
- const float range = CLAMP(l.range, 0, 4096);
- // Doubling the range will double the effective brightness, so we need double attenuation (half brightness).
- // We want to have double intensity give double brightness, so we need half the attenuation.
- const float attenuation = range / intensity;
- if (l.type == "point") {
- OmniLight3D *light = memnew(OmniLight3D);
- light->set_param(OmniLight3D::PARAM_ATTENUATION, attenuation);
- light->set_param(OmniLight3D::PARAM_RANGE, range);
- light->set_color(l.color);
- return light;
- }
- if (l.type == "spot") {
- SpotLight3D *light = memnew(SpotLight3D);
- light->set_param(SpotLight3D::PARAM_ATTENUATION, attenuation);
- light->set_param(SpotLight3D::PARAM_RANGE, range);
- light->set_param(SpotLight3D::PARAM_SPOT_ANGLE, Math::rad2deg(l.outer_cone_angle));
- light->set_color(l.color);
-
- // Line of best fit derived from guessing, see https://www.desmos.com/calculator/biiflubp8b
- // The points in desmos are not exact, except for (1, infinity).
- float angle_ratio = l.inner_cone_angle / l.outer_cone_angle;
- float angle_attenuation = 0.2 / (1 - angle_ratio) - 0.1;
- light->set_param(SpotLight3D::PARAM_SPOT_ATTENUATION, angle_attenuation);
- return light;
- }
- return nullptr;
-}
-
-Camera3D *EditorSceneImporterGLTF::_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) {
- const GLTFNode *gltf_node = state.nodes[node_index];
-
- ERR_FAIL_INDEX_V(gltf_node->camera, state.cameras.size(), nullptr);
-
- Camera3D *camera = memnew(Camera3D);
- print_verbose("glTF: Creating camera for: " + gltf_node->name);
-
- const GLTFCamera &c = state.cameras[gltf_node->camera];
- if (c.perspective) {
- camera->set_perspective(c.fov_size, c.znear, c.zfar);
- } else {
- camera->set_orthogonal(c.fov_size, c.znear, c.zfar);
- }
-
- return camera;
-}
-
-Node3D *EditorSceneImporterGLTF::_generate_spatial(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) {
- const GLTFNode *gltf_node = state.nodes[node_index];
-
- Node3D *spatial = memnew(Node3D);
- print_verbose("glTF: Creating spatial for: " + gltf_node->name);
-
- return spatial;
-}
-
-void EditorSceneImporterGLTF::_generate_scene_node(GLTFState &state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index) {
- const GLTFNode *gltf_node = state.nodes[node_index];
-
- Node3D *current_node = nullptr;
-
- // Is our parent a skeleton
- Skeleton3D *active_skeleton = Object::cast_to<Skeleton3D>(scene_parent);
-
- if (gltf_node->skeleton >= 0) {
- Skeleton3D *skeleton = state.skeletons[gltf_node->skeleton].godot_skeleton;
-
- if (active_skeleton != skeleton) {
- ERR_FAIL_COND_MSG(active_skeleton != nullptr, "glTF: Generating scene detected direct parented Skeletons");
-
- // Add it to the scene if it has not already been added
- if (skeleton->get_parent() == nullptr) {
- scene_parent->add_child(skeleton);
- skeleton->set_owner(scene_root);
- }
- }
-
- active_skeleton = skeleton;
- current_node = skeleton;
- }
-
- // If we have an active skeleton, and the node is node skinned, we need to create a bone attachment
- if (current_node == nullptr && active_skeleton != nullptr && gltf_node->skin < 0) {
- BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index);
-
- scene_parent->add_child(bone_attachment);
- bone_attachment->set_owner(scene_root);
-
- // There is no gltf_node that represent this, so just directly create a unique name
- bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment"));
-
- // We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
- // and attach it to the bone_attachment
- scene_parent = bone_attachment;
- }
-
- // We still have not managed to make a node
- if (current_node == nullptr) {
- if (gltf_node->mesh >= 0) {
- current_node = _generate_mesh_instance(state, scene_parent, node_index);
- } else if (gltf_node->camera >= 0) {
- current_node = _generate_camera(state, scene_parent, node_index);
- } else if (gltf_node->light >= 0) {
- current_node = _generate_light(state, scene_parent, node_index);
- } else {
- current_node = _generate_spatial(state, scene_parent, node_index);
- }
-
- scene_parent->add_child(current_node);
- current_node->set_owner(scene_root);
- current_node->set_transform(gltf_node->xform);
- current_node->set_name(gltf_node->name);
- }
-
- state.scene_nodes.insert(node_index, current_node);
-
- for (int i = 0; i < gltf_node->children.size(); ++i) {
- _generate_scene_node(state, current_node, scene_root, gltf_node->children[i]);
- }
-}
-
-template <class T>
-struct EditorSceneImporterGLTFInterpolate {
- T lerp(const T &a, const T &b, float c) const {
- return a + (b - a) * c;
- }
-
- T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) {
- const float t2 = t * t;
- const float t3 = t2 * t;
-
- return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
- }
-
- T bezier(T start, T control_1, T control_2, T end, float t) {
- /* Formula from Wikipedia article on Bezier curves. */
- const real_t omt = (1.0 - t);
- const real_t omt2 = omt * omt;
- const real_t omt3 = omt2 * omt;
- const real_t t2 = t * t;
- const real_t t3 = t2 * t;
-
- return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
- }
-};
-
-// thank you for existing, partial specialization
-template <>
-struct EditorSceneImporterGLTFInterpolate<Quat> {
- Quat lerp(const Quat &a, const Quat &b, const float c) const {
- ERR_FAIL_COND_V_MSG(!a.is_normalized(), Quat(), "The quaternion \"a\" must be normalized.");
- ERR_FAIL_COND_V_MSG(!b.is_normalized(), Quat(), "The quaternion \"b\" must be normalized.");
-
- return a.slerp(b, c).normalized();
- }
-
- Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, const float c) {
- ERR_FAIL_COND_V_MSG(!p1.is_normalized(), Quat(), "The quaternion \"p1\" must be normalized.");
- ERR_FAIL_COND_V_MSG(!p2.is_normalized(), Quat(), "The quaternion \"p2\" must be normalized.");
-
- return p1.slerp(p2, c).normalized();
- }
-
- Quat bezier(const Quat start, const Quat control_1, const Quat control_2, const Quat end, const float t) {
- ERR_FAIL_COND_V_MSG(!start.is_normalized(), Quat(), "The start quaternion must be normalized.");
- ERR_FAIL_COND_V_MSG(!end.is_normalized(), Quat(), "The end quaternion must be normalized.");
-
- return start.slerp(end, t).normalized();
- }
-};
-
-template <class T>
-T EditorSceneImporterGLTF::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) {
- //could use binary search, worth it?
- int idx = -1;
- for (int i = 0; i < p_times.size(); i++) {
- if (p_times[i] > p_time) {
- break;
- }
- idx++;
- }
-
- EditorSceneImporterGLTFInterpolate<T> interp;
-
- switch (p_interp) {
- case GLTFAnimation::INTERP_LINEAR: {
- if (idx == -1) {
- return p_values[0];
- } else if (idx >= p_times.size() - 1) {
- return p_values[p_times.size() - 1];
- }
-
- const float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
-
- return interp.lerp(p_values[idx], p_values[idx + 1], c);
-
- } break;
- case GLTFAnimation::INTERP_STEP: {
- if (idx == -1) {
- return p_values[0];
- } else if (idx >= p_times.size() - 1) {
- return p_values[p_times.size() - 1];
- }
-
- return p_values[idx];
-
- } break;
- case GLTFAnimation::INTERP_CATMULLROMSPLINE: {
- if (idx == -1) {
- return p_values[1];
- } else if (idx >= p_times.size() - 1) {
- return p_values[1 + p_times.size() - 1];
- }
-
- const float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
-
- return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c);
-
- } break;
- case GLTFAnimation::INTERP_CUBIC_SPLINE: {
- if (idx == -1) {
- return p_values[1];
- } else if (idx >= p_times.size() - 1) {
- return p_values[(p_times.size() - 1) * 3 + 1];
- }
-
- const float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
-
- const T from = p_values[idx * 3 + 1];
- const T c1 = from + p_values[idx * 3 + 2];
- const T to = p_values[idx * 3 + 4];
- const T c2 = to + p_values[idx * 3 + 3];
-
- return interp.bezier(from, c1, c2, to, c);
-
- } break;
- }
-
- ERR_FAIL_V(p_values[0]);
-}
-
-void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlayer *ap, const GLTFAnimationIndex index, const int bake_fps) {
- const GLTFAnimation &anim = state.animations[index];
-
- String name = anim.name;
- if (name.empty()) {
- // No node represent these, and they are not in the hierarchy, so just make a unique name
- name = _gen_unique_name(state, "Animation");
- }
-
- Ref<Animation> animation;
- animation.instance();
- animation->set_name(name);
-
- if (anim.loop) {
- animation->set_loop(true);
- }
-
- float length = 0;
-
- for (Map<int, GLTFAnimation::Track>::Element *E = anim.tracks.front(); E; E = E->next()) {
- const GLTFAnimation::Track &track = E->get();
- //need to find the path
- NodePath node_path;
-
- GLTFNodeIndex node_index = E->key();
- if (state.nodes[node_index]->fake_joint_parent >= 0) {
- // Should be same as parent
- node_index = state.nodes[node_index]->fake_joint_parent;
- }
-
- const GLTFNode *node = state.nodes[E->key()];
-
- if (node->skeleton >= 0) {
- const Skeleton3D *sk = Object::cast_to<Skeleton3D>(state.scene_nodes.find(node_index)->get());
- ERR_FAIL_COND(sk == nullptr);
-
- const String path = ap->get_parent()->get_path_to(sk);
- const String bone = node->name;
- node_path = path + ":" + bone;
- } else {
- node_path = ap->get_parent()->get_path_to(state.scene_nodes.find(node_index)->get());
- }
-
- for (int i = 0; i < track.rotation_track.times.size(); i++) {
- length = MAX(length, track.rotation_track.times[i]);
- }
- for (int i = 0; i < track.translation_track.times.size(); i++) {
- length = MAX(length, track.translation_track.times[i]);
- }
- for (int i = 0; i < track.scale_track.times.size(); i++) {
- length = MAX(length, track.scale_track.times[i]);
- }
-
- for (int i = 0; i < track.weight_tracks.size(); i++) {
- for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
- length = MAX(length, track.weight_tracks[i].times[j]);
- }
- }
-
- if (track.rotation_track.values.size() || track.translation_track.values.size() || track.scale_track.values.size()) {
- //make transform track
- int track_idx = animation->get_track_count();
- animation->add_track(Animation::TYPE_TRANSFORM);
- animation->track_set_path(track_idx, node_path);
- animation->track_set_imported(track_idx, true);
- //first determine animation length
-
- const float increment = 1.0 / float(bake_fps);
- float time = 0.0;
-
- Vector3 base_pos;
- Quat base_rot;
- Vector3 base_scale = Vector3(1, 1, 1);
-
- if (!track.rotation_track.values.size()) {
- base_rot = state.nodes[E->key()]->rotation.normalized();
- }
-
- if (!track.translation_track.values.size()) {
- base_pos = state.nodes[E->key()]->translation;
- }
-
- if (!track.scale_track.values.size()) {
- base_scale = state.nodes[E->key()]->scale;
- }
-
- bool last = false;
- while (true) {
- Vector3 pos = base_pos;
- Quat rot = base_rot;
- Vector3 scale = base_scale;
-
- if (track.translation_track.times.size()) {
- pos = _interpolate_track<Vector3>(track.translation_track.times, track.translation_track.values, time, track.translation_track.interpolation);
- }
-
- if (track.rotation_track.times.size()) {
- rot = _interpolate_track<Quat>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation);
- }
-
- if (track.scale_track.times.size()) {
- scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation);
- }
-
- if (node->skeleton >= 0) {
- Transform xform;
- xform.basis.set_quat_scale(rot, scale);
- xform.origin = pos;
-
- const Skeleton3D *skeleton = state.skeletons[node->skeleton].godot_skeleton;
- const int bone_idx = skeleton->find_bone(node->name);
- xform = skeleton->get_bone_rest(bone_idx).affine_inverse() * xform;
-
- rot = xform.basis.get_rotation_quat();
- rot.normalize();
- scale = xform.basis.get_scale();
- pos = xform.origin;
- }
-
- animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
-
- if (last) {
- break;
- }
- time += increment;
- if (time >= length) {
- last = true;
- time = length;
- }
- }
- }
-
- for (int i = 0; i < track.weight_tracks.size(); i++) {
- ERR_CONTINUE(node->mesh < 0 || node->mesh >= state.meshes.size());
- const GLTFMesh &mesh = state.meshes[node->mesh];
- const String prop = "blend_shapes/" + mesh.mesh->get_blend_shape_name(i);
-
- const String blend_path = String(node_path) + ":" + prop;
-
- const int track_idx = animation->get_track_count();
- animation->add_track(Animation::TYPE_VALUE);
- animation->track_set_path(track_idx, blend_path);
-
- // Only LINEAR and STEP (NEAREST) can be supported out of the box by Godot's Animation,
- // the other modes have to be baked.
- GLTFAnimation::Interpolation gltf_interp = track.weight_tracks[i].interpolation;
- if (gltf_interp == GLTFAnimation::INTERP_LINEAR || gltf_interp == GLTFAnimation::INTERP_STEP) {
- animation->track_set_interpolation_type(track_idx, gltf_interp == GLTFAnimation::INTERP_STEP ? Animation::INTERPOLATION_NEAREST : Animation::INTERPOLATION_LINEAR);
- for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
- const float t = track.weight_tracks[i].times[j];
- const float w = track.weight_tracks[i].values[j];
- animation->track_insert_key(track_idx, t, w);
- }
- } else {
- // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
- const float increment = 1.0 / float(bake_fps);
- float time = 0.0;
- bool last = false;
- while (true) {
- _interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp);
- if (last) {
- break;
- }
- time += increment;
- if (time >= length) {
- last = true;
- time = length;
- }
- }
- }
- }
- }
-
- animation->set_length(length);
-
- ap->add_animation(name, animation);
-}
-
-void EditorSceneImporterGLTF::_process_mesh_instances(GLTFState &state, Node3D *scene_root) {
- for (GLTFNodeIndex node_i = 0; node_i < state.nodes.size(); ++node_i) {
- const GLTFNode *node = state.nodes[node_i];
-
- if (node->skin >= 0 && node->mesh >= 0) {
- const GLTFSkinIndex skin_i = node->skin;
-
- Map<GLTFNodeIndex, Node *>::Element *mi_element = state.scene_nodes.find(node_i);
- MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(mi_element->get());
- ERR_FAIL_COND(mi == nullptr);
-
- const GLTFSkeletonIndex skel_i = state.skins[node->skin].skeleton;
- const GLTFSkeleton &gltf_skeleton = state.skeletons[skel_i];
- Skeleton3D *skeleton = gltf_skeleton.godot_skeleton;
- ERR_FAIL_COND(skeleton == nullptr);
-
- mi->get_parent()->remove_child(mi);
- skeleton->add_child(mi);
- mi->set_owner(scene_root);
-
- mi->set_skin(state.skins[skin_i].godot_skin);
- mi->set_skeleton_path(mi->get_path_to(skeleton));
- mi->set_transform(Transform());
- }
- }
-}
-
-Node3D *EditorSceneImporterGLTF::_generate_scene(GLTFState &state, const int p_bake_fps) {
- Node3D *root = memnew(Node3D);
-
- // scene_name is already unique
- root->set_name(state.scene_name);
-
- for (int i = 0; i < state.root_nodes.size(); ++i) {
- _generate_scene_node(state, root, root, state.root_nodes[i]);
- }
-
- _process_mesh_instances(state, root);
-
- if (state.animations.size()) {
- AnimationPlayer *ap = memnew(AnimationPlayer);
- ap->set_name("AnimationPlayer");
- root->add_child(ap);
- ap->set_owner(root);
-
- for (int i = 0; i < state.animations.size(); i++) {
- _import_animation(state, ap, i, p_bake_fps);
- }
- }
-
- return root;
-}
-
-Node *EditorSceneImporterGLTF::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
- GLTFState state;
-
- if (p_path.to_lower().ends_with("glb")) {
- //binary file
- //text file
- Error err = _parse_glb(p_path, state);
- if (err) {
- return nullptr;
- }
- } else {
- //text file
- Error err = _parse_json(p_path, state);
- if (err) {
- return nullptr;
- }
- }
-
- ERR_FAIL_COND_V(!state.json.has("asset"), nullptr);
-
- Dictionary asset = state.json["asset"];
-
- ERR_FAIL_COND_V(!asset.has("version"), nullptr);
-
- String version = asset["version"];
-
- state.import_flags = p_flags;
- state.major_version = version.get_slice(".", 0).to_int();
- state.minor_version = version.get_slice(".", 1).to_int();
- state.use_named_skin_binds = p_flags & IMPORT_USE_NAMED_SKIN_BINDS;
-
- /* STEP 0 PARSE SCENE */
- Error err = _parse_scenes(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 1 PARSE NODES */
- err = _parse_nodes(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 2 PARSE BUFFERS */
- err = _parse_buffers(state, p_path.get_base_dir());
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 3 PARSE BUFFER VIEWS */
- err = _parse_buffer_views(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 4 PARSE ACCESSORS */
- err = _parse_accessors(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 5 PARSE IMAGES */
- err = _parse_images(state, p_path.get_base_dir());
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 6 PARSE TEXTURES */
- err = _parse_textures(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 7 PARSE TEXTURES */
- err = _parse_materials(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 9 PARSE SKINS */
- err = _parse_skins(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 10 DETERMINE SKELETONS */
- err = _determine_skeletons(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 11 CREATE SKELETONS */
- err = _create_skeletons(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 12 CREATE SKINS */
- err = _create_skins(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 13 PARSE MESHES (we have enough info now) */
- err = _parse_meshes(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 14 PARSE LIGHTS */
- err = _parse_lights(state);
- if (err != OK) {
- return NULL;
- }
-
- /* STEP 15 PARSE CAMERAS */
- err = _parse_cameras(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 16 PARSE ANIMATIONS */
- err = _parse_animations(state);
- if (err != OK) {
- return nullptr;
- }
-
- /* STEP 17 ASSIGN SCENE NAMES */
- _assign_scene_names(state);
-
- /* STEP 18 MAKE SCENE! */
- Node3D *scene = _generate_scene(state, p_bake_fps);
-
- return scene;
-}
-
-Ref<Animation> EditorSceneImporterGLTF::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
- return Ref<Animation>();
-}
-
-EditorSceneImporterGLTF::EditorSceneImporterGLTF() {
-}
diff --git a/editor/import/editor_scene_importer_gltf.h b/editor/import/editor_scene_importer_gltf.h
deleted file mode 100644
index bd30f8f1dd..0000000000
--- a/editor/import/editor_scene_importer_gltf.h
+++ /dev/null
@@ -1,398 +0,0 @@
-/*************************************************************************/
-/* editor_scene_importer_gltf.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 EDITOR_SCENE_IMPORTER_GLTF_H
-#define EDITOR_SCENE_IMPORTER_GLTF_H
-
-#include "editor/import/resource_importer_scene.h"
-#include "scene/3d/light_3d.h"
-#include "scene/3d/node_3d.h"
-#include "scene/3d/skeleton_3d.h"
-
-class AnimationPlayer;
-class BoneAttachment3D;
-class MeshInstance3D;
-
-class EditorSceneImporterGLTF : public EditorSceneImporter {
- GDCLASS(EditorSceneImporterGLTF, EditorSceneImporter);
-
- typedef int GLTFAccessorIndex;
- typedef int GLTFAnimationIndex;
- typedef int GLTFBufferIndex;
- typedef int GLTFBufferViewIndex;
- typedef int GLTFCameraIndex;
- typedef int GLTFImageIndex;
- typedef int GLTFMaterialIndex;
- typedef int GLTFMeshIndex;
- typedef int GLTFLightIndex;
- typedef int GLTFNodeIndex;
- typedef int GLTFSkeletonIndex;
- typedef int GLTFSkinIndex;
- typedef int GLTFTextureIndex;
-
- enum {
- ARRAY_BUFFER = 34962,
- ELEMENT_ARRAY_BUFFER = 34963,
-
- TYPE_BYTE = 5120,
- TYPE_UNSIGNED_BYTE = 5121,
- TYPE_SHORT = 5122,
- TYPE_UNSIGNED_SHORT = 5123,
- TYPE_UNSIGNED_INT = 5125,
- TYPE_FLOAT = 5126,
-
- COMPONENT_TYPE_BYTE = 5120,
- COMPONENT_TYPE_UNSIGNED_BYTE = 5121,
- COMPONENT_TYPE_SHORT = 5122,
- COMPONENT_TYPE_UNSIGNED_SHORT = 5123,
- COMPONENT_TYPE_INT = 5125,
- COMPONENT_TYPE_FLOAT = 5126,
-
- };
-
- String _get_component_type_name(const uint32_t p_component);
- int _get_component_type_size(const int component_type);
-
- enum GLTFType {
- TYPE_SCALAR,
- TYPE_VEC2,
- TYPE_VEC3,
- TYPE_VEC4,
- TYPE_MAT2,
- TYPE_MAT3,
- TYPE_MAT4,
- };
-
- String _get_type_name(const GLTFType p_component);
-
- struct GLTFNode {
- //matrices need to be transformed to this
- GLTFNodeIndex parent = -1;
- int height = -1;
-
- Transform xform;
- String name;
-
- GLTFMeshIndex mesh = -1;
- GLTFCameraIndex camera = -1;
- GLTFSkinIndex skin = -1;
-
- GLTFSkeletonIndex skeleton = -1;
- bool joint = false;
-
- Vector3 translation;
- Quat rotation;
- Vector3 scale = Vector3(1, 1, 1);
-
- Vector<int> children;
-
- GLTFNodeIndex fake_joint_parent = -1;
-
- GLTFLightIndex light = -1;
-
- GLTFNode() {}
- };
-
- struct GLTFBufferView {
- GLTFBufferIndex buffer = -1;
- int byte_offset = 0;
- int byte_length = 0;
- int byte_stride = 0;
- bool indices = false;
- //matrices need to be transformed to this
-
- GLTFBufferView() {}
- };
-
- struct GLTFAccessor {
- GLTFBufferViewIndex buffer_view = 0;
- int byte_offset = 0;
- int component_type = 0;
- bool normalized = false;
- int count = 0;
- GLTFType type;
- float min = 0;
- float max = 0;
- int sparse_count = 0;
- int sparse_indices_buffer_view = 0;
- int sparse_indices_byte_offset = 0;
- int sparse_indices_component_type = 0;
- int sparse_values_buffer_view = 0;
- int sparse_values_byte_offset = 0;
-
- GLTFAccessor() {}
- };
- struct GLTFTexture {
- GLTFImageIndex src_image;
- };
-
- struct GLTFSkeleton {
- // The *synthesized* skeletons joints
- Vector<GLTFNodeIndex> joints;
-
- // The roots of the skeleton. If there are multiple, each root must have the same parent
- // (ie roots are siblings)
- Vector<GLTFNodeIndex> roots;
-
- // The created Skeleton for the scene
- Skeleton3D *godot_skeleton = nullptr;
-
- // Set of unique bone names for the skeleton
- Set<String> unique_names;
-
- GLTFSkeleton() {}
- };
-
- struct GLTFSkin {
- String name;
-
- // The "skeleton" property defined in the gltf spec. -1 = Scene Root
- GLTFNodeIndex skin_root = -1;
-
- Vector<GLTFNodeIndex> joints_original;
- Vector<Transform> inverse_binds;
-
- // Note: joints + non_joints should form a complete subtree, or subtrees with a common parent
-
- // All nodes that are skins that are caught in-between the original joints
- // (inclusive of joints_original)
- Vector<GLTFNodeIndex> joints;
-
- // All Nodes that are caught in-between skin joint nodes, and are not defined
- // as joints by any skin
- Vector<GLTFNodeIndex> non_joints;
-
- // The roots of the skin. In the case of multiple roots, their parent *must*
- // be the same (the roots must be siblings)
- Vector<GLTFNodeIndex> roots;
-
- // The GLTF Skeleton this Skin points to (after we determine skeletons)
- GLTFSkeletonIndex skeleton = -1;
-
- // A mapping from the joint indices (in the order of joints_original) to the
- // Godot Skeleton's bone_indices
- Map<int, int> joint_i_to_bone_i;
- Map<int, StringName> joint_i_to_name;
-
- // The Actual Skin that will be created as a mapping between the IBM's of this skin
- // to the generated skeleton for the mesh instances.
- Ref<Skin> godot_skin;
-
- GLTFSkin() {}
- };
-
- struct GLTFMesh {
- Ref<ArrayMesh> mesh;
- Vector<float> blend_weights;
- };
-
- struct GLTFCamera {
- bool perspective = true;
- float fov_size = 64;
- float zfar = 500;
- float znear = 0.1;
-
- GLTFCamera() {}
- };
-
- struct GLTFLight {
- Color color = Color(1.0f, 1.0f, 1.0f);
- float intensity = 1.0f;
- String type = "";
- float range = Math_INF;
- float inner_cone_angle = 0.0f;
- float outer_cone_angle = Math_PI / 4.0;
-
- GLTFLight() {}
- };
-
- struct GLTFAnimation {
- bool loop = false;
-
- enum Interpolation {
- INTERP_LINEAR,
- INTERP_STEP,
- INTERP_CATMULLROMSPLINE,
- INTERP_CUBIC_SPLINE
- };
-
- template <class T>
- struct Channel {
- Interpolation interpolation;
- Vector<float> times;
- Vector<T> values;
- };
-
- struct Track {
- Channel<Vector3> translation_track;
- Channel<Quat> rotation_track;
- Channel<Vector3> scale_track;
- Vector<Channel<float>> weight_tracks;
- };
-
- String name;
-
- Map<int, Track> tracks;
- };
-
- struct GLTFState {
- Dictionary json;
- int major_version;
- int minor_version;
- Vector<uint8_t> glb_data;
-
- bool use_named_skin_binds;
-
- Vector<GLTFNode *> nodes;
- Vector<Vector<uint8_t>> buffers;
- Vector<GLTFBufferView> buffer_views;
- Vector<GLTFAccessor> accessors;
-
- Vector<GLTFMesh> meshes; //meshes are loaded directly, no reason not to.
- Vector<Ref<Material>> materials;
-
- String scene_name;
- Vector<int> root_nodes;
-
- Vector<GLTFTexture> textures;
- Vector<Ref<Texture2D>> images;
-
- Vector<GLTFSkin> skins;
- Vector<GLTFCamera> cameras;
- Vector<GLTFLight> lights;
-
- Set<String> unique_names;
-
- Vector<GLTFSkeleton> skeletons;
- Vector<GLTFAnimation> animations;
-
- Map<GLTFNodeIndex, Node *> scene_nodes;
-
- // EditorSceneImporter::ImportFlags
- uint32_t import_flags;
-
- ~GLTFState() {
- for (int i = 0; i < nodes.size(); i++) {
- memdelete(nodes[i]);
- }
- }
- };
-
- String _sanitize_scene_name(const String &name);
- String _gen_unique_name(GLTFState &state, const String &p_name);
-
- String _sanitize_bone_name(const String &name);
- String _gen_unique_bone_name(GLTFState &state, const GLTFSkeletonIndex skel_i, const String &p_name);
-
- Ref<Texture2D> _get_texture(GLTFState &state, const GLTFTextureIndex p_texture);
-
- Error _parse_json(const String &p_path, GLTFState &state);
- Error _parse_glb(const String &p_path, GLTFState &state);
-
- Error _parse_scenes(GLTFState &state);
- Error _parse_nodes(GLTFState &state);
-
- void _compute_node_heights(GLTFState &state);
-
- Error _parse_buffers(GLTFState &state, const String &p_base_path);
- Error _parse_buffer_views(GLTFState &state);
- GLTFType _get_type_from_str(const String &p_string);
- Error _parse_accessors(GLTFState &state);
- Error _decode_buffer_view(GLTFState &state, double *dst, const GLTFBufferViewIndex p_buffer_view, const int skip_every, const int skip_bytes, const int element_size, const int count, const GLTFType type, const int component_count, const int component_type, const int component_size, const bool normalized, const int byte_offset, const bool for_vertex);
-
- Vector<double> _decode_accessor(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
- Vector<float> _decode_accessor_as_floats(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
- Vector<int> _decode_accessor_as_ints(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
- Vector<Vector2> _decode_accessor_as_vec2(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
- Vector<Vector3> _decode_accessor_as_vec3(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
- Vector<Color> _decode_accessor_as_color(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
- Vector<Quat> _decode_accessor_as_quat(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
- Vector<Transform2D> _decode_accessor_as_xform2d(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
- Vector<Basis> _decode_accessor_as_basis(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
- Vector<Transform> _decode_accessor_as_xform(GLTFState &state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex);
-
- Error _parse_meshes(GLTFState &state);
- Error _parse_images(GLTFState &state, const String &p_base_path);
- Error _parse_textures(GLTFState &state);
-
- Error _parse_materials(GLTFState &state);
-
- GLTFNodeIndex _find_highest_node(GLTFState &state, const Vector<GLTFNodeIndex> &subset);
-
- bool _capture_nodes_in_skin(GLTFState &state, GLTFSkin &skin, const GLTFNodeIndex node_index);
- void _capture_nodes_for_multirooted_skin(GLTFState &state, GLTFSkin &skin);
- Error _expand_skin(GLTFState &state, GLTFSkin &skin);
- Error _verify_skin(GLTFState &state, GLTFSkin &skin);
- Error _parse_skins(GLTFState &state);
-
- Error _determine_skeletons(GLTFState &state);
- Error _reparent_non_joint_skeleton_subtrees(GLTFState &state, GLTFSkeleton &skeleton, const Vector<GLTFNodeIndex> &non_joints);
- Error _reparent_to_fake_joint(GLTFState &state, GLTFSkeleton &skeleton, const GLTFNodeIndex node_index);
- Error _determine_skeleton_roots(GLTFState &state, const GLTFSkeletonIndex skel_i);
-
- Error _create_skeletons(GLTFState &state);
- Error _map_skin_joints_indices_to_skeleton_bone_indices(GLTFState &state);
-
- Error _create_skins(GLTFState &state);
- bool _skins_are_same(const Ref<Skin> &skin_a, const Ref<Skin> &skin_b);
- void _remove_duplicate_skins(GLTFState &state);
-
- Error _parse_cameras(GLTFState &state);
- Error _parse_lights(GLTFState &state);
- Error _parse_animations(GLTFState &state);
-
- BoneAttachment3D *_generate_bone_attachment(GLTFState &state, Skeleton3D *skeleton, const GLTFNodeIndex node_index);
- MeshInstance3D *_generate_mesh_instance(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
- Camera3D *_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
- Light3D *_generate_light(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
- Node3D *_generate_spatial(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
-
- void _generate_scene_node(GLTFState &state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index);
- Node3D *_generate_scene(GLTFState &state, const int p_bake_fps);
-
- void _process_mesh_instances(GLTFState &state, Node3D *scene_root);
-
- void _assign_scene_names(GLTFState &state);
-
- template <class T>
- T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp);
-
- void _import_animation(GLTFState &state, AnimationPlayer *ap, const GLTFAnimationIndex index, const int bake_fps);
-
-public:
- virtual uint32_t get_import_flags() const override;
- virtual void get_extensions(List<String> *r_extensions) const override;
- virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps = nullptr, Error *r_err = nullptr) override;
- virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) override;
-
- EditorSceneImporterGLTF();
-};
-
-#endif // EDITOR_SCENE_IMPORTER_GLTF_H
diff --git a/editor/import/resource_importer_bitmask.cpp b/editor/import/resource_importer_bitmask.cpp
index da2d1c9bdf..7fd9230284 100644
--- a/editor/import/resource_importer_bitmask.cpp
+++ b/editor/import/resource_importer_bitmask.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,8 +29,8 @@
/*************************************************************************/
#include "resource_importer_bitmask.h"
-#include "core/image.h"
#include "core/io/config_file.h"
+#include "core/io/image.h"
#include "core/io/image_loader.h"
#include "editor/editor_file_system.h"
#include "editor/editor_node.h"
@@ -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_bitmask.h b/editor/import/resource_importer_bitmask.h
index 0d3cb23697..d68693c54a 100644
--- a/editor/import/resource_importer_bitmask.h
+++ b/editor/import/resource_importer_bitmask.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef RESOURCE_IMPORTER_BITMASK_H
#define RESOURCE_IMPORTER_BITMASK_H
-#include "core/image.h"
+#include "core/io/image.h"
#include "core/io/resource_importer.h"
class StreamBitMap;
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_csv.h b/editor/import/resource_importer_bmfont.h
index c9fbe75dd2..065703132a 100644
--- a/editor/import/resource_importer_csv.h
+++ b/editor/import/resource_importer_bmfont.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* resource_importer_csv.h */
+/* resource_importer_bmfont.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,13 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RESOURCEIMPORTERCSV_H
-#define RESOURCEIMPORTERCSV_H
+#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 ResourceImporterCSV : public ResourceImporter {
- GDCLASS(ResourceImporterCSV, ResourceImporter);
+class ResourceImporterBMFont : public ResourceImporter {
+ GDCLASS(ResourceImporterBMFont, ResourceImporter);
public:
virtual String get_importer_name() const override;
@@ -43,15 +45,12 @@ public:
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;
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;
- ResourceImporterCSV();
+ ResourceImporterBMFont();
};
-#endif // RESOURCEIMPORTERCSV_H
+#endif // RESOURCE_IMPORTER_BMFONT_H
diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp
index 04e20dee86..07647d8b6a 100644
--- a/editor/import/resource_importer_csv_translation.cpp
+++ b/editor/import/resource_importer_csv_translation.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,10 +30,10 @@
#include "resource_importer_csv_translation.h"
-#include "core/compressed_translation.h"
+#include "core/io/file_access.h"
#include "core/io/resource_saver.h"
-#include "core/os/file_access.h"
-#include "core/translation.h"
+#include "core/string/optimized_translation.h"
+#include "core/string/translation.h"
String ResourceImporterCSVTranslation::get_importer_name() const {
return "csv_translation";
@@ -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);
}
@@ -126,7 +126,7 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const
Ref<Translation> xlt = translations[i];
if (compress) {
- Ref<PHashTranslation> cxl = memnew(PHashTranslation);
+ Ref<OptimizedTranslation> cxl = memnew(OptimizedTranslation);
cxl->generate(xlt);
xlt = cxl;
}
diff --git a/editor/import/resource_importer_csv_translation.h b/editor/import/resource_importer_csv_translation.h
index 7c7646b640..d53e91e38b 100644
--- a/editor/import/resource_importer_csv_translation.h
+++ b/editor/import/resource_importer_csv_translation.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
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 885b00865b..2dea359188 100644
--- a/editor/import/resource_importer_image.cpp
+++ b/editor/import/resource_importer_image.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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 {
@@ -75,7 +75,7 @@ Error ResourceImporterImage::import(const String &p_source_file, const String &p
ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file from path '" + p_source_file + "'.");
- size_t len = f->get_len();
+ uint64_t len = f->get_length();
Vector<uint8_t> data;
data.resize(len);
diff --git a/editor/import/resource_importer_image.h b/editor/import/resource_importer_image.h
index dc9c2c3014..7c8d5e228e 100644
--- a/editor/import/resource_importer_image.h
+++ b/editor/import/resource_importer_image.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef RESOURCE_IMPORTER_IMAGE_H
#define RESOURCE_IMPORTER_IMAGE_H
-#include "core/image.h"
+#include "core/io/image.h"
#include "core/io/resource_importer.h"
class ResourceImporterImage : public ResourceImporter {
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 bbf62596d0..d5bb21443c 100644
--- a/editor/import/resource_importer_layered_texture.cpp
+++ b/editor/import/resource_importer_layered_texture.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -51,7 +51,7 @@ String ResourceImporterLayeredTexture::get_importer_name() const {
return "cubemap_array_texture";
} break;
case MODE_3D: {
- return "cubemap_3d_texture";
+ return "3d_texture";
} break;
}
@@ -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
@@ -373,7 +370,7 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
int x = slice_w * j;
int y = slice_h * i;
Ref<Image> slice = image->get_rect(Rect2(x, y, slice_w, slice_h));
- ERR_CONTINUE(slice.is_null() || slice->empty());
+ ERR_CONTINUE(slice.is_null() || slice->is_empty());
if (slice->get_width() != slice_w || slice->get_height() != slice_h) {
slice->resize(slice_w, slice_h);
}
@@ -391,8 +388,8 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
bool ok_on_pc = false;
bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995);
bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565);
- bool can_bptc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc");
- bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc");
+ bool can_bptc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_bptc");
+ bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_s3tc");
if (can_bptc) {
formats_imported.push_back("bptc"); //needs to be aded anyway
@@ -447,13 +444,13 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
ok_on_pc = true;
}
- if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) {
+ if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2")) {
_save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, lossy, Image::COMPRESS_ETC2, csource, used_channels, mipmaps, true);
r_platform_variants->push_back("etc2");
formats_imported.push_back("etc2");
}
- if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) {
+ if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc")) {
_save_tex(slices, p_save_path + ".etc2." + extension, compress_mode, lossy, Image::COMPRESS_ETC2, csource, used_channels, mipmaps, true);
r_platform_variants->push_back("pvrtc");
formats_imported.push_back("pvrtc");
@@ -492,7 +489,7 @@ String ResourceImporterLayeredTexture::get_import_settings_string() const {
int index = 0;
while (compression_formats[index]) {
- String setting_path = "rendering/vram_compression/import_" + String(compression_formats[index]);
+ String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
bool test = ProjectSettings::get_singleton()->get(setting_path);
if (test) {
s += String(compression_formats[index]);
@@ -524,7 +521,7 @@ bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_p
int index = 0;
bool valid = true;
while (compression_formats[index]) {
- String setting_path = "rendering/vram_compression/import_" + String(compression_formats[index]);
+ String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
bool test = ProjectSettings::get_singleton()->get(setting_path);
if (test) {
if (formats_imported.find(compression_formats[index]) == -1) {
diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h
index b54923be00..86e9c5bde8 100644
--- a/editor/import/resource_importer_layered_texture.h
+++ b/editor/import/resource_importer_layered_texture.h
@@ -5,38 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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. */
-/*************************************************************************/
-
-/*************************************************************************/
-/* resource_importer_layered_texture.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -61,7 +31,7 @@
#ifndef RESOURCE_IMPORTER_LAYERED_TEXTURE_H
#define RESOURCE_IMPORTER_LAYERED_TEXTURE_H
-#include "core/image.h"
+#include "core/io/image.h"
#include "core/io/resource_importer.h"
class StreamTexture2D;
diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp
index 49b47bf4be..34bc0a7d8d 100644
--- a/editor/import/resource_importer_obj.cpp
+++ b/editor/import/resource_importer_obj.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,10 @@
#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"
#include "scene/3d/node_3d.h"
#include "scene/resources/mesh.h"
@@ -55,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 ")) {
@@ -124,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);
@@ -144,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);
@@ -164,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);
@@ -205,12 +207,12 @@ 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;
Vector3 offset_mesh = p_offset_mesh;
- int mesh_flags = p_optimize ? Mesh::ARRAY_COMPRESS_DEFAULT : 0;
+ int mesh_flags = 0;
Vector<Vector3> vertices;
Vector<Vector3> normals;
@@ -225,6 +227,8 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
String current_material_library;
String current_material;
String current_group;
+ uint32_t smooth_group = 0;
+ bool smoothing = true;
while (true) {
String l = f->get_line().strip_edges();
@@ -294,7 +298,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
norm += normals.size() + 1;
}
ERR_FAIL_INDEX_V(norm, normals.size(), ERR_FILE_CORRUPT);
- surf_tool->add_normal(normals[norm]);
+ surf_tool->set_normal(normals[norm]);
}
if (face[idx].size() >= 2 && face[idx][1] != String()) {
@@ -303,7 +307,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
uv += uvs.size() + 1;
}
ERR_FAIL_INDEX_V(uv, uvs.size(), ERR_FILE_CORRUPT);
- surf_tool->add_uv(uvs[uv]);
+ surf_tool->set_uv(uvs[uv]);
}
int vtx = face[idx][0].to_int() - 1;
@@ -315,6 +319,10 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
Vector3 vertex = vertices[vtx];
//if (weld_vertices)
// vertex.snap(Vector3(weld_tolerance, weld_tolerance, weld_tolerance));
+ if (!smoothing) {
+ smooth_group++;
+ }
+ surf_tool->set_smooth_group(smooth_group);
surf_tool->add_vertex(vertex);
}
@@ -322,10 +330,15 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
}
} else if (l.begins_with("s ")) { //smoothing
String what = l.substr(2, l.length()).strip_edges();
+ bool do_smooth;
if (what == "off") {
- surf_tool->add_smooth_group(false);
+ do_smooth = false;
} else {
- surf_tool->add_smooth_group(true);
+ do_smooth = true;
+ }
+ if (do_smooth != smoothing) {
+ smooth_group++;
+ smoothing = do_smooth;
}
} else if (/*l.begins_with("g ") ||*/ l.begins_with("usemtl ") || (l.begins_with("o ") || f->eof_reached())) { //commit group to mesh
//groups are too annoying
@@ -365,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 = "";
}
@@ -414,7 +427,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
List<Ref<Mesh>> meshes;
- Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, p_flags & IMPORT_USE_COMPRESSION, Vector3(1, 1, 1), Vector3(0, 0, 0), r_missing_deps);
+ Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, Vector3(1, 1, 1), Vector3(0, 0, 0), r_missing_deps);
if (err != OK) {
if (r_err) {
@@ -425,10 +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()) {
- MeshInstance3D *mi = memnew(MeshInstance3D);
- mi->set_mesh(E->get());
- mi->set_name(E->get()->get_name());
+ for (const Ref<Mesh> &m : meshes) {
+ Ref<EditorSceneImporterMesh> mesh;
+ 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(m->get_name());
scene->add_child(mi);
mi->set_owner(scene);
}
@@ -473,6 +492,10 @@ String ResourceImporterOBJ::get_resource_type() const {
return "Mesh";
}
+int ResourceImporterOBJ::get_format_version() const {
+ return 1;
+}
+
int ResourceImporterOBJ::get_preset_count() const {
return 0;
}
diff --git a/editor/import/resource_importer_obj.h b/editor/import/resource_importer_obj.h
index 4083bc7403..414e0c1fe6 100644
--- a/editor/import/resource_importer_obj.h
+++ b/editor/import/resource_importer_obj.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -54,6 +54,7 @@ public:
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_format_version() const override;
virtual int get_preset_count() const override;
virtual String get_preset_name(int p_idx) const override;
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 5dcdf6bec4..c2244befa1 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,31 +32,37 @@
#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"
#include "scene/3d/collision_shape_3d.h"
#include "scene/3d/mesh_instance_3d.h"
-#include "scene/3d/navigation_3d.h"
+#include "scene/3d/navigation_region_3d.h"
#include "scene/3d/physics_body_3d.h"
#include "scene/3d/vehicle_body_3d.h"
#include "scene/animation/animation_player.h"
#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"
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]);
}
@@ -67,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);
@@ -97,53 +105,38 @@ 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);
- BIND_CONSTANT(IMPORT_ANIMATION_DETECT_LOOP);
- BIND_CONSTANT(IMPORT_ANIMATION_OPTIMIZE);
- BIND_CONSTANT(IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS);
- BIND_CONSTANT(IMPORT_ANIMATION_KEEP_VALUE_TRACKS);
- BIND_CONSTANT(IMPORT_GENERATE_TANGENT_ARRAYS);
BIND_CONSTANT(IMPORT_FAIL_ON_MISSING_DEPENDENCIES);
- BIND_CONSTANT(IMPORT_MATERIALS_IN_INSTANCES);
- BIND_CONSTANT(IMPORT_USE_COMPRESSION);
+ BIND_CONSTANT(IMPORT_GENERATE_TANGENT_ARRAYS);
+ BIND_CONSTANT(IMPORT_USE_NAMED_SKIN_BINDS);
}
/////////////////////////////////
void EditorScenePostImport::_bind_methods() {
- BIND_VMETHOD(MethodInfo(Variant::OBJECT, "post_import", PropertyInfo(Variant::OBJECT, "scene")));
- ClassDB::bind_method(D_METHOD("get_source_folder"), &EditorScenePostImport::get_source_folder);
+ 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;
}
-String EditorScenePostImport::get_source_folder() const {
- return source_folder;
-}
-
String EditorScenePostImport::get_source_file() const {
return source_file;
}
-void EditorScenePostImport::init(const String &p_source_folder, const String &p_source_file) {
- source_folder = p_source_folder;
+void EditorScenePostImport::init(const String &p_source_file) {
source_file = p_source_file;
}
@@ -172,34 +165,18 @@ String ResourceImporterScene::get_resource_type() const {
return "PackedScene";
}
+int ResourceImporterScene::get_format_version() const {
+ return 1;
+}
+
bool ResourceImporterScene::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
if (p_option.begins_with("animation/")) {
if (p_option != "animation/import" && !bool(p_options["animation/import"])) {
return false;
}
-
- if (p_option == "animation/keep_custom_tracks" && int(p_options["animation/storage"]) == 0) {
- return false;
- }
-
- if (p_option.begins_with("animation/optimizer/") && p_option != "animation/optimizer/enabled" && !bool(p_options["animation/optimizer/enabled"])) {
- return false;
- }
-
- if (p_option.begins_with("animation/clip_")) {
- int max_clip = p_options["animation/clips/amount"];
- int clip = p_option.get_slice("/", 1).get_slice("_", 1).to_int() - 1;
- if (clip >= max_clip) {
- return false;
- }
- }
}
- if (p_option == "materials/keep_on_reimport" && int(p_options["materials/storage"]) == 0) {
- return false;
- }
-
- if (p_option == "meshes/lightmap_texel_size" && int(p_options["meshes/light_baking"]) < 2) {
+ if (p_option == "meshes/lightmap_texel_size" && int(p_options["meshes/light_baking"]) < 3) {
return false;
}
@@ -207,34 +184,11 @@ bool ResourceImporterScene::get_option_visibility(const String &p_option, const
}
int ResourceImporterScene::get_preset_count() const {
- return PRESET_MAX;
+ return 0;
}
String ResourceImporterScene::get_preset_name(int p_idx) const {
- switch (p_idx) {
- case PRESET_SINGLE_SCENE:
- return TTR("Import as Single Scene");
- case PRESET_SEPARATE_ANIMATIONS:
- return TTR("Import with Separate Animations");
- case PRESET_SEPARATE_MATERIALS:
- return TTR("Import with Separate Materials");
- case PRESET_SEPARATE_MESHES:
- return TTR("Import with Separate Objects");
- case PRESET_SEPARATE_MESHES_AND_MATERIALS:
- return TTR("Import with Separate Objects+Materials");
- case PRESET_SEPARATE_MESHES_AND_ANIMATIONS:
- return TTR("Import with Separate Objects+Animations");
- case PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS:
- return TTR("Import with Separate Materials+Animations");
- case PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS:
- return TTR("Import with Separate Objects+Materials+Animations");
- case PRESET_MULTIPLE_SCENES:
- return TTR("Import as Multiple Scenes");
- case PRESET_MULTIPLE_SCENES_AND_MATERIALS:
- return TTR("Import as Multiple Scenes+Materials");
- }
-
- return "";
+ return String();
}
static bool _teststr(const String &p_what, const String &p_str) {
@@ -279,7 +233,8 @@ 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) {
+static void _pre_gen_shape_list(const Ref<EditorSceneImporterMesh> &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);
@@ -293,10 +248,10 @@ static void _gen_shape_list(const Ref<Mesh> &mesh, List<Ref<Shape3D>> &r_shape_l
}
}
-Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>, List<Ref<Shape3D>>> &collision_map, LightBakeMode p_light_bake_mode) {
+Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> &collision_map) {
// children first
for (int i = 0; i < p_node->get_child_count(); i++) {
- Node *r = _fix_node(p_node->get_child(i), p_root, collision_map, p_light_bake_mode);
+ Node *r = _pre_fix_node(p_node->get_child(i), p_root, collision_map);
if (!r) {
i--; //was erased
}
@@ -311,33 +266,29 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>
return nullptr;
}
- if (Object::cast_to<MeshInstance3D>(p_node)) {
- MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
+ if (Object::cast_to<EditorSceneImporterMeshNode3D>(p_node)) {
+ EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
- Ref<ArrayMesh> m = mi->get_mesh();
+ Ref<EditorSceneImporterMesh> m = mi->get_mesh();
if (m.is_valid()) {
for (int i = 0; i < m->get_surface_count(); i++) {
- Ref<StandardMaterial3D> mat = m->surface_get_material(i);
+ Ref<BaseMaterial3D> mat = m->get_surface_material(i);
if (!mat.is_valid()) {
continue;
}
if (_teststr(mat->get_name(), "alpha")) {
- mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ mat->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA);
mat->set_name(_fixstr(mat->get_name(), "alpha"));
}
if (_teststr(mat->get_name(), "vcol")) {
- mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
- mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+ mat->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ mat->set_flag(BaseMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
mat->set_name(_fixstr(mat->get_name(), "vcol"));
}
}
}
-
- if (p_light_bake_mode != LIGHT_BAKE_DISABLED) {
- mi->set_gi_mode(GeometryInstance3D::GI_MODE_BAKED);
- }
}
if (Object::cast_to<AnimationPlayer>(p_node)) {
@@ -346,8 +297,8 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>
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);
@@ -361,6 +312,17 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>
}
}
}
+
+ 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, animname);
+ }
+ }
}
}
@@ -368,9 +330,9 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>
if (isroot) {
return p_node;
}
- MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
+ EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
if (mi) {
- Ref<Mesh> mesh = mi->get_mesh();
+ Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
List<Ref<Shape3D>> shapes;
@@ -378,10 +340,10 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>
if (collision_map.has(mesh)) {
shapes = collision_map[mesh];
} else if (_teststr(name, "colonly")) {
- _gen_shape_list(mesh, shapes, false);
+ _pre_gen_shape_list(mesh, shapes, false);
collision_map[mesh] = shapes;
} else if (_teststr(name, "convcolonly")) {
- _gen_shape_list(mesh, shapes, true);
+ _pre_gen_shape_list(mesh, shapes, true);
collision_map[mesh] = shapes;
}
@@ -401,16 +363,7 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>
memdelete(p_node);
p_node = col;
- int idx = 0;
- for (List<Ref<Shape3D>>::Element *E = shapes.front(); E; E = E->next()) {
- CollisionShape3D *cshape = memnew(CollisionShape3D);
- cshape->set_shape(E->get());
- col->add_child(cshape);
-
- cshape->set_name("shape" + itos(idx));
- cshape->set_owner(col->get_owner());
- idx++;
- }
+ _add_shapes(col, shapes);
}
}
@@ -425,43 +378,39 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>
CollisionShape3D *colshape = memnew(CollisionShape3D);
if (empty_draw_type == "CUBE") {
BoxShape3D *boxShape = memnew(BoxShape3D);
- boxShape->set_extents(Vector3(1, 1, 1));
+ boxShape->set_size(Vector3(2, 2, 2));
colshape->set_shape(boxShape);
- colshape->set_name("BoxShape3D");
} else if (empty_draw_type == "SINGLE_ARROW") {
- RayShape3D *rayShape = memnew(RayShape3D);
+ SeparationRayShape3D *rayShape = memnew(SeparationRayShape3D);
rayShape->set_length(1);
colshape->set_shape(rayShape);
- colshape->set_name("RayShape3D");
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);
- colshape->set_name("WorldMarginShape3D");
} else {
SphereShape3D *sphereShape = memnew(SphereShape3D);
sphereShape->set_radius(1);
colshape->set_shape(sphereShape);
- colshape->set_name("SphereShape3D");
}
sb->add_child(colshape);
colshape->set_owner(sb->get_owner());
}
- } else if (_teststr(name, "rigid") && Object::cast_to<MeshInstance3D>(p_node)) {
+ } else if (_teststr(name, "rigid") && Object::cast_to<EditorSceneImporterMeshNode3D>(p_node)) {
if (isroot) {
return p_node;
}
- MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
- Ref<Mesh> mesh = mi->get_mesh();
+ EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
+ Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
List<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);
@@ -469,27 +418,17 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>
p_node->replace_by(rigid_body);
rigid_body->set_transform(mi->get_transform());
p_node = rigid_body;
- mi->set_name("mesh");
- mi->set_transform(Transform());
+ mi->set_transform(Transform3D());
rigid_body->add_child(mi);
mi->set_owner(rigid_body->get_owner());
- int idx = 0;
- for (List<Ref<Shape3D>>::Element *E = shapes.front(); E; E = E->next()) {
- CollisionShape3D *cshape = memnew(CollisionShape3D);
- cshape->set_shape(E->get());
- rigid_body->add_child(cshape);
-
- cshape->set_name("shape" + itos(idx));
- cshape->set_owner(p_node->get_owner());
- idx++;
- }
+ _add_shapes(rigid_body, shapes);
}
- } else if ((_teststr(name, "col") || (_teststr(name, "convcol"))) && Object::cast_to<MeshInstance3D>(p_node)) {
- MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
+ } else if ((_teststr(name, "col") || (_teststr(name, "convcol"))) && Object::cast_to<EditorSceneImporterMeshNode3D>(p_node)) {
+ EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
- Ref<Mesh> mesh = mi->get_mesh();
+ Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
if (mesh.is_valid()) {
List<Ref<Shape3D>> shapes;
@@ -497,10 +436,10 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>
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;
}
@@ -518,118 +457,315 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>
if (shapes.size()) {
StaticBody3D *col = memnew(StaticBody3D);
- col->set_name("static_collision");
mi->add_child(col);
col->set_owner(mi->get_owner());
- int idx = 0;
- for (List<Ref<Shape3D>>::Element *E = shapes.front(); E; E = E->next()) {
- CollisionShape3D *cshape = memnew(CollisionShape3D);
- cshape->set_shape(E->get());
- col->add_child(cshape);
-
- cshape->set_name("shape" + itos(idx));
- cshape->set_owner(p_node->get_owner());
-
- idx++;
- }
+ _add_shapes(col, shapes);
}
}
- } else if (_teststr(name, "navmesh") && Object::cast_to<MeshInstance3D>(p_node)) {
+ } else if (_teststr(name, "navmesh") && Object::cast_to<EditorSceneImporterMeshNode3D>(p_node)) {
if (isroot) {
return p_node;
}
- MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
+ EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
- Ref<ArrayMesh> mesh = mi->get_mesh();
+ Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
ERR_FAIL_COND_V(mesh.is_null(), nullptr);
NavigationRegion3D *nmi = memnew(NavigationRegion3D);
nmi->set_name(_fixstr(name, "navmesh"));
- Ref<NavigationMesh> nmesh = memnew(NavigationMesh);
- nmesh->create_from_mesh(mesh);
+ Ref<NavigationMesh> nmesh = mesh->create_navigation_mesh();
nmi->set_navigation_mesh(nmesh);
Object::cast_to<Node3D>(nmi)->set_transform(mi->get_transform());
p_node->replace_by(nmi);
memdelete(p_node);
p_node = nmi;
- } else if (_teststr(name, "vehicle")) {
- if (isroot) {
- return p_node;
- }
-
- Node *owner = p_node->get_owner();
- Node3D *s = Object::cast_to<Node3D>(p_node);
- VehicleBody3D *bv = memnew(VehicleBody3D);
- String n = _fixstr(p_node->get_name(), "vehicle");
- bv->set_name(n);
- p_node->replace_by(bv);
- p_node->set_name(n);
- bv->add_child(p_node);
- bv->set_owner(owner);
- p_node->set_owner(owner);
- bv->set_transform(s->get_transform());
- s->set_transform(Transform());
-
- p_node = bv;
-
- } else if (_teststr(name, "wheel")) {
- if (isroot) {
- return p_node;
- }
- Node *owner = p_node->get_owner();
- Node3D *s = Object::cast_to<Node3D>(p_node);
- VehicleWheel3D *bv = memnew(VehicleWheel3D);
- String n = _fixstr(p_node->get_name(), "wheel");
- bv->set_name(n);
- p_node->replace_by(bv);
- p_node->set_name(n);
- bv->add_child(p_node);
- bv->set_owner(owner);
- p_node->set_owner(owner);
- bv->set_transform(s->get_transform());
- s->set_transform(Transform());
-
- p_node = bv;
-
- } else if (Object::cast_to<MeshInstance3D>(p_node)) {
+ } else if (Object::cast_to<EditorSceneImporterMeshNode3D>(p_node)) {
//last attempt, maybe collision inside the mesh data
- MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
+ EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
- Ref<ArrayMesh> mesh = mi->get_mesh();
+ Ref<EditorSceneImporterMesh> mesh = mi->get_mesh();
if (!mesh.is_null()) {
List<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"));
}
if (shapes.size()) {
StaticBody3D *col = memnew(StaticBody3D);
- col->set_name("static_collision");
p_node->add_child(col);
col->set_owner(p_node->get_owner());
- int idx = 0;
- for (List<Ref<Shape3D>>::Element *E = shapes.front(); E; E = E->next()) {
- CollisionShape3D *cshape = memnew(CollisionShape3D);
- cshape->set_shape(E->get());
- col->add_child(cshape);
+ _add_shapes(col, shapes);
+ }
+ }
+ }
+
+ 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) {
+ // 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);
+ if (!r) {
+ i--; //was erased
+ }
+ }
+
+ bool isroot = p_node == p_root;
+
+ String import_id;
+
+ if (p_node->has_meta("import_id")) {
+ import_id = p_node->get_meta("import_id");
+ } else {
+ import_id = "PATH:" + p_root->get_path_to(p_node);
+ }
+
+ Dictionary node_settings;
+ if (p_node_data.has(import_id)) {
+ node_settings = p_node_data[import_id];
+ }
+
+ if (!isroot && (node_settings.has("import/skip_import") && bool(node_settings["import/skip_import"]))) {
+ memdelete(p_node);
+ return nullptr;
+ }
+
+ if (Object::cast_to<EditorSceneImporterMeshNode3D>(p_node)) {
+ EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
+
+ Ref<EditorSceneImporterMesh> m = mi->get_mesh();
+
+ if (m.is_valid()) {
+ if (!r_scanned_meshes.has(m)) {
+ for (int i = 0; i < m->get_surface_count(); i++) {
+ Ref<Material> mat = m->get_surface_material(i);
+ if (mat.is_valid()) {
+ String mat_id;
+ if (mat->has_meta("import_id")) {
+ mat_id = mat->get_meta("import_id");
+ } else {
+ mat_id = mat->get_name();
+ }
+
+ if (mat_id != String() && p_material_data.has(mat_id)) {
+ Dictionary matdata = p_material_data[mat_id];
+ if (matdata.has("use_external/enabled") && bool(matdata["use_external/enabled"]) && matdata.has("use_external/path")) {
+ String path = matdata["use_external/path"];
+ Ref<Material> external_mat = ResourceLoader::load(path);
+ if (external_mat.is_valid()) {
+ m->set_surface_material(i, external_mat);
+ }
+ }
+ }
+ }
+ }
+
+ r_scanned_meshes.insert(m);
+ }
+
+ 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;
+
+ 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;
+ }
+ }
+
+ if (shapes.size()) {
+ CollisionObject3D *base = nullptr;
+ switch (mesh_physics_mode) {
+ case MESH_PHYSICS_MESH_AND_STATIC_COLLIDER: {
+ StaticBody3D *col = memnew(StaticBody3D);
+ p_node->add_child(col);
+ 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());
+ p_node = rigid_body;
+ 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_name(p_node->get_name());
+ p_node->replace_by(col);
+ memdelete(p_node);
+ p_node = col;
+ base = col;
+ } break;
+ case MESH_PHYSICS_AREA_ONLY: {
+ Area3D *area = memnew(Area3D);
+ area->set_transform(mi->get_transform());
+ area->set_name(p_node->get_name());
+ p_node->replace_by(area);
+ memdelete(p_node);
+ p_node = area;
+ base = area;
+
+ } break;
+ }
+
+ int idx = 0;
+ for (const Ref<Shape3D> &E : shapes) {
+ CollisionShape3D *cshape = memnew(CollisionShape3D);
+ cshape->set_shape(E);
+ base->add_child(cshape);
+
+ cshape->set_owner(base->get_owner());
+ idx++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ //navmesh (node may have changed type above)
+ if (Object::cast_to<EditorSceneImporterMeshNode3D>(p_node)) {
+ EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
+
+ Ref<EditorSceneImporterMesh> m = mi->get_mesh();
+
+ if (m.is_valid()) {
+ if (node_settings.has("generate/navmesh")) {
+ int navmesh_mode = node_settings["generate/navmesh"];
+
+ if (navmesh_mode != NAVMESH_DISABLED) {
+ NavigationRegion3D *nmi = memnew(NavigationRegion3D);
+
+ Ref<NavigationMesh> nmesh = m->create_navigation_mesh();
+ nmi->set_navigation_mesh(nmesh);
+
+ if (navmesh_mode == NAVMESH_NAVMESH_ONLY) {
+ nmi->set_transform(mi->get_transform());
+ p_node->replace_by(nmi);
+ memdelete(p_node);
+ p_node = nmi;
+ } else {
+ mi->add_child(nmi);
+ nmi->set_owner(mi->get_owner());
+ }
+ }
+ }
+ }
+ }
+
+ if (Object::cast_to<AnimationPlayer>(p_node)) {
+ AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
- cshape->set_name("shape" + itos(idx));
- cshape->set_owner(p_node->get_owner());
- idx++;
+ {
+ //make sure this is unique
+ node_settings = node_settings.duplicate(true);
+ //fill node settings for this node with default values
+ List<ImportOption> iopts;
+ get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts);
+ for (const ImportOption &E : iopts) {
+ if (!node_settings.has(E.option.name)) {
+ node_settings[E.option.name] = E.default_value;
+ }
+ }
+ }
+
+ bool use_optimizer = node_settings["optimizer/enabled"];
+ float anim_optimizer_linerr = node_settings["optimizer/max_linear_error"];
+ float anim_optimizer_angerr = node_settings["optimizer/max_angular_error"];
+ float anim_optimizer_maxang = node_settings["optimizer/max_angle"];
+
+ if (use_optimizer) {
+ _optimize_animations(ap, anim_optimizer_linerr, anim_optimizer_angerr, anim_optimizer_maxang);
+ }
+
+ Array animation_clips;
+ {
+ int clip_count = node_settings["clips/amount"];
+
+ for (int i = 0; i < clip_count; i++) {
+ String name = node_settings["clip_" + itos(i + 1) + "/name"];
+ int from_frame = node_settings["clip_" + itos(i + 1) + "/start_frame"];
+ int end_frame = node_settings["clip_" + itos(i + 1) + "/end_frame"];
+ bool loop = node_settings["clip_" + itos(i + 1) + "/loops"];
+ bool save_to_file = node_settings["clip_" + itos(i + 1) + "/save_to_file/enabled"];
+ bool save_to_path = node_settings["clip_" + itos(i + 1) + "/save_to_file/path"];
+ bool save_to_file_keep_custom = node_settings["clip_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"];
+
+ animation_clips.push_back(name);
+ animation_clips.push_back(from_frame / p_animation_fps);
+ animation_clips.push_back(end_frame / p_animation_fps);
+ animation_clips.push_back(loop);
+ animation_clips.push_back(save_to_file);
+ animation_clips.push_back(save_to_path);
+ animation_clips.push_back(save_to_file_keep_custom);
+ }
+ }
+
+ if (animation_clips.size()) {
+ _create_clips(ap, animation_clips, true);
+ } else {
+ List<StringName> anims;
+ ap->get_animation_list(&anims);
+ 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];
+ {
+ //fill with default values
+ List<ImportOption> iopts;
+ get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION, &iopts);
+ for (const ImportOption &F : iopts) {
+ if (!anim_settings.has(F.option.name)) {
+ anim_settings[F.option.name] = F.default_value;
+ }
+ }
+ }
+
+ anim->set_loop(anim_settings["settings/loops"]);
+ bool save = anim_settings["save_to_file/enabled"];
+ String path = anim_settings["save_to_file/path"];
+ bool keep_custom = anim_settings["save_to_file/keep_custom_tracks"];
+
+ Ref<Animation> saved_anim = _save_animation_to_file(anim, save, path, keep_custom);
+
+ if (saved_anim != anim) {
+ ap->add_animation(name, saved_anim); //replace
+ }
}
}
}
@@ -638,27 +774,52 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>
return p_node;
}
-void ResourceImporterScene::_create_clips(Node *scene, const Array &p_clips, bool p_bake_all) {
- if (!scene->has_node(String("AnimationPlayer"))) {
- return;
+Ref<Animation> ResourceImporterScene::_save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, String p_save_to_path, bool p_keep_custom_tracks) {
+ if (!p_save_to_file || !p_save_to_path.is_resource_file()) {
+ return anim;
}
- Node *n = scene->get_node(String("AnimationPlayer"));
- ERR_FAIL_COND(!n);
- AnimationPlayer *anim = Object::cast_to<AnimationPlayer>(n);
- ERR_FAIL_COND(!anim);
+ if (FileAccess::exists(p_save_to_path) && p_keep_custom_tracks) {
+ // Copy custom animation tracks from previously imported files.
+ Ref<Animation> old_anim = ResourceLoader::load(p_save_to_path, "Animation", ResourceFormatLoader::CACHE_MODE_IGNORE);
+ if (old_anim.is_valid()) {
+ for (int i = 0; i < old_anim->get_track_count(); i++) {
+ if (!old_anim->track_is_imported(i)) {
+ old_anim->copy_track(i, anim);
+ }
+ }
+ anim->set_loop(old_anim->has_loop());
+ }
+ }
+ if (ResourceCache::has(p_save_to_path)) {
+ Ref<Animation> old_anim = Ref<Resource>(ResourceCache::get(p_save_to_path));
+ if (old_anim.is_valid()) {
+ old_anim->copy_from(anim);
+ anim = old_anim;
+ }
+ }
+ anim->set_path(p_save_to_path, true); // Set path to save externally.
+ Error err = ResourceSaver::save(p_save_to_path, anim, ResourceSaver::FLAG_CHANGE_PATH);
+ ERR_FAIL_COND_V_MSG(err != OK, anim, "Saving of animation failed: " + p_save_to_path);
+ return anim;
+}
+
+void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_clips, bool p_bake_all) {
if (!anim->has_animation("default")) {
return;
}
Ref<Animation> default_anim = anim->get_animation("default");
- for (int i = 0; i < p_clips.size(); i += 4) {
+ for (int i = 0; i < p_clips.size(); i += 7) {
String name = p_clips[i];
float from = p_clips[i + 1];
float to = p_clips[i + 2];
bool loop = p_clips[i + 3];
+ bool save_to_file = p_clips[i + 4];
+ String save_to_path = p_clips[i + 5];
+ bool keep_current = p_clips[i + 6];
if (from >= to) {
continue;
}
@@ -679,8 +840,8 @@ void ResourceImporterScene::_create_clips(Node *scene, const Array &p_clips, boo
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);
@@ -693,8 +854,8 @@ void ResourceImporterScene::_create_clips(Node *scene, const Array &p_clips, boo
}
}
- 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);
@@ -707,8 +868,8 @@ void ResourceImporterScene::_create_clips(Node *scene, const Array &p_clips, boo
}
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);
@@ -725,8 +886,8 @@ void ResourceImporterScene::_create_clips(Node *scene, const Array &p_clips, boo
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);
@@ -746,351 +907,118 @@ void ResourceImporterScene::_create_clips(Node *scene, const Array &p_clips, boo
new_anim->set_loop(loop);
new_anim->set_length(to - from);
anim->add_animation(name, new_anim);
- }
- anim->remove_animation("default"); //remove default (no longer needed)
-}
-
-void ResourceImporterScene::_filter_anim_tracks(Ref<Animation> anim, Set<String> &keep) {
- Ref<Animation> a = anim;
- ERR_FAIL_COND(!a.is_valid());
-
- for (int j = 0; j < a->get_track_count(); j++) {
- String path = a->track_get_path(j);
-
- if (!keep.has(path)) {
- a->remove_track(j);
- j--;
+ Ref<Animation> saved_anim = _save_animation_to_file(new_anim, save_to_file, save_to_path, keep_current);
+ if (saved_anim != new_anim) {
+ anim->add_animation(name, saved_anim);
}
}
-}
-
-void ResourceImporterScene::_filter_tracks(Node *scene, const String &p_text) {
- if (!scene->has_node(String("AnimationPlayer"))) {
- return;
- }
- Node *n = scene->get_node(String("AnimationPlayer"));
- ERR_FAIL_COND(!n);
- AnimationPlayer *anim = Object::cast_to<AnimationPlayer>(n);
- ERR_FAIL_COND(!anim);
-
- Vector<String> strings = p_text.split("\n");
- for (int i = 0; i < strings.size(); i++) {
- strings.write[i] = strings[i].strip_edges();
- }
-
- List<StringName> anim_names;
- anim->get_animation_list(&anim_names);
- for (List<StringName>::Element *E = anim_names.front(); E; E = E->next()) {
- String name = E->get();
- bool valid_for_this = false;
- bool valid = false;
-
- Set<String> keep;
- Set<String> keep_local;
-
- for (int i = 0; i < strings.size(); i++) {
- if (strings[i].begins_with("@")) {
- valid_for_this = false;
- for (Set<String>::Element *F = keep_local.front(); F; F = F->next()) {
- keep.insert(F->get());
- }
- keep_local.clear();
-
- Vector<String> filters = strings[i].substr(1, strings[i].length()).split(",");
- for (int j = 0; j < filters.size(); j++) {
- String fname = filters[j].strip_edges();
- if (fname == "") {
- continue;
- }
- int fc = fname[0];
- bool plus;
- if (fc == '+') {
- plus = true;
- } else if (fc == '-') {
- plus = false;
- } else {
- continue;
- }
-
- String filter = fname.substr(1, fname.length()).strip_edges();
- if (!name.matchn(filter)) {
- continue;
- }
- valid_for_this = plus;
- }
-
- if (valid_for_this) {
- valid = true;
- }
-
- } else if (valid_for_this) {
- Ref<Animation> a = anim->get_animation(name);
- if (!a.is_valid()) {
- continue;
- }
-
- for (int j = 0; j < a->get_track_count(); j++) {
- String path = a->track_get_path(j);
-
- String tname = strings[i];
- if (tname == "") {
- continue;
- }
- int fc = tname[0];
- bool plus;
- if (fc == '+') {
- plus = true;
- } else if (fc == '-') {
- plus = false;
- } else {
- continue;
- }
-
- String filter = tname.substr(1, tname.length()).strip_edges();
-
- if (!path.matchn(filter)) {
- continue;
- }
-
- if (plus) {
- keep_local.insert(path);
- } else if (!keep.has(path)) {
- keep_local.erase(path);
- }
- }
- }
- }
-
- if (valid) {
- for (Set<String>::Element *F = keep_local.front(); F; F = F->next()) {
- keep.insert(F->get());
- }
- _filter_anim_tracks(anim->get_animation(name), keep);
- }
- }
+ anim->remove_animation("default"); //remove default (no longer needed)
}
-void ResourceImporterScene::_optimize_animations(Node *scene, float p_max_lin_error, float p_max_ang_error, float p_max_angle) {
- if (!scene->has_node(String("AnimationPlayer"))) {
- return;
- }
- Node *n = scene->get_node(String("AnimationPlayer"));
- ERR_FAIL_COND(!n);
- AnimationPlayer *anim = Object::cast_to<AnimationPlayer>(n);
- ERR_FAIL_COND(!anim);
-
+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));
}
}
-static String _make_extname(const String &p_str) {
- String ext_name = p_str.replace(".", "_");
- ext_name = ext_name.replace(":", "_");
- ext_name = ext_name.replace("\"", "_");
- ext_name = ext_name.replace("<", "_");
- ext_name = ext_name.replace(">", "_");
- ext_name = ext_name.replace("/", "_");
- ext_name = ext_name.replace("|", "_");
- ext_name = ext_name.replace("\\", "_");
- ext_name = ext_name.replace("?", "_");
- ext_name = ext_name.replace("*", "_");
-
- return ext_name;
-}
-
-void ResourceImporterScene::_find_meshes(Node *p_node, Map<Ref<ArrayMesh>, Transform> &meshes) {
- List<PropertyInfo> pi;
- p_node->get_property_list(&pi);
-
- MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node);
-
- if (mi) {
- Ref<ArrayMesh> mesh = mi->get_mesh();
-
- if (mesh.is_valid() && !meshes.has(mesh)) {
- Node3D *s = mi;
- Transform transform;
- while (s) {
- transform = transform * s->get_transform();
- s = Object::cast_to<Node3D>(s->get_parent());
+void ResourceImporterScene::get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const {
+ switch (p_category) {
+ case INTERNAL_IMPORT_CATEGORY_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));
+ } 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::INT, "generate/navmesh", PROPERTY_HINT_ENUM, "Disabled,Mesh + NavMesh,NavMesh Only"), 0));
+ } 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));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.tres"), ""));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/make_streamable"), ""));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/shadow_meshes", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lightmap_uv", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lods", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0));
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_MATERIAL: {
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "use_external/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "use_external/path", PROPERTY_HINT_FILE, "*.material,*.res,*.tres"), ""));
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_ANIMATION: {
+ r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "settings/loops"), false));
+ 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));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.tres"), ""));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/keep_custom_tracks"), ""));
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_ANIMATION_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::BOOL, "optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_linear_error"), 0.05));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error"), 0.01));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angle"), 22));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slices/amount", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
+
+ for (int i = 0; i < 256; i++) {
+ r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/name"), ""));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/start_frame"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/end_frame"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/loops"), false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/save_to_file/path", PROPERTY_HINT_SAVE_FILE, ".res,*.tres"), ""));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"), false));
}
-
- meshes[mesh] = transform;
+ } break;
+ default: {
}
}
- for (int i = 0; i < p_node->get_child_count(); i++) {
- _find_meshes(p_node->get_child(i), meshes);
- }
}
-void ResourceImporterScene::_make_external_resources(Node *p_node, const String &p_base_path, bool p_make_animations, bool p_animations_as_text, bool p_keep_animations, bool p_make_materials, bool p_materials_as_text, bool p_keep_materials, bool p_make_meshes, bool p_meshes_as_text, Map<Ref<Animation>, Ref<Animation>> &p_animations, Map<Ref<Material>, Ref<Material>> &p_materials, Map<Ref<ArrayMesh>, Ref<ArrayMesh>> &p_meshes) {
- List<PropertyInfo> pi;
-
- if (p_make_animations) {
- if (Object::cast_to<AnimationPlayer>(p_node)) {
- AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node);
-
- 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());
- ERR_CONTINUE(anim.is_null());
-
- if (!p_animations.has(anim)) {
- // Tracks from source file should be set as imported, anything else is a custom track.
- for (int i = 0; i < anim->get_track_count(); i++) {
- anim->track_set_imported(i, true);
- }
-
- String ext_name;
-
- if (p_animations_as_text) {
- ext_name = p_base_path.plus_file(_make_extname(E->get()) + ".tres");
- } else {
- ext_name = p_base_path.plus_file(_make_extname(E->get()) + ".anim");
- }
-
- if (FileAccess::exists(ext_name) && p_keep_animations) {
- // Copy custom animation tracks from previously imported files.
- Ref<Animation> old_anim = ResourceLoader::load(ext_name, "Animation", true);
- if (old_anim.is_valid()) {
- for (int i = 0; i < old_anim->get_track_count(); i++) {
- if (!old_anim->track_is_imported(i)) {
- old_anim->copy_track(i, anim);
- }
- }
- anim->set_loop(old_anim->has_loop());
- }
- }
-
- anim->set_path(ext_name, true); // Set path to save externally.
- ResourceSaver::save(ext_name, anim, ResourceSaver::FLAG_CHANGE_PATH);
- p_animations[anim] = anim;
- }
+bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategory p_category, const String &p_option, const Map<StringName, Variant> &p_options) const {
+ if (p_options.has("import/skip_import") && p_option != "import/skip_import" && bool(p_options["import/skip_import"])) {
+ return false; //if skip import
+ }
+ switch (p_category) {
+ case INTERNAL_IMPORT_CATEGORY_NODE: {
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE: {
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_MESH: {
+ if (p_option == "save_to_file/path" || p_option == "save_to_file/make_streamable") {
+ return p_options["save_to_file/enabled"];
+ }
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_MATERIAL: {
+ if (p_option == "use_external/path") {
+ return p_options["use_external/enabled"];
+ }
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_ANIMATION: {
+ if (p_option == "save_to_file/path" || p_option == "save_to_file/keep_custom_tracks") {
+ return p_options["save_to_file/enabled"];
+ }
+ } break;
+ case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {
+ if (p_option.begins_with("animation/optimizer/") && p_option != "animation/optimizer/enabled" && !bool(p_options["animation/optimizer/enabled"])) {
+ return false;
}
- }
- }
-
- p_node->get_property_list(&pi);
-
- for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) {
- if (E->get().type == Variant::OBJECT) {
- Ref<Material> mat = p_node->get(E->get().name);
-
- if (p_make_materials && mat.is_valid() && mat->get_name() != "") {
- if (!p_materials.has(mat)) {
- String ext_name;
-
- if (p_materials_as_text) {
- ext_name = p_base_path.plus_file(_make_extname(mat->get_name()) + ".tres");
- } else {
- ext_name = p_base_path.plus_file(_make_extname(mat->get_name()) + ".material");
- }
-
- if (p_keep_materials && FileAccess::exists(ext_name)) {
- //if exists, use it
- p_materials[mat] = ResourceLoader::load(ext_name);
- } else {
- ResourceSaver::save(ext_name, mat, ResourceSaver::FLAG_CHANGE_PATH);
- p_materials[mat] = ResourceLoader::load(ext_name, "", true); // disable loading from the cache.
- }
- }
-
- if (p_materials[mat] != mat) {
- p_node->set(E->get().name, p_materials[mat]);
- }
- } else {
- Ref<ArrayMesh> mesh = p_node->get(E->get().name);
-
- if (mesh.is_valid()) {
- bool mesh_just_added = false;
-
- if (p_make_meshes) {
- if (!p_meshes.has(mesh)) {
- //meshes are always overwritten, keeping them is not practical
- String ext_name;
-
- if (p_meshes_as_text) {
- ext_name = p_base_path.plus_file(_make_extname(mesh->get_name()) + ".tres");
- } else {
- ext_name = p_base_path.plus_file(_make_extname(mesh->get_name()) + ".mesh");
- }
-
- ResourceSaver::save(ext_name, mesh, ResourceSaver::FLAG_CHANGE_PATH);
- p_meshes[mesh] = ResourceLoader::load(ext_name);
- p_node->set(E->get().name, p_meshes[mesh]);
- mesh_just_added = true;
- }
- }
-
- if (p_make_materials) {
- if (mesh_just_added || !p_meshes.has(mesh)) {
- for (int i = 0; i < mesh->get_surface_count(); i++) {
- mat = mesh->surface_get_material(i);
-
- if (!mat.is_valid()) {
- continue;
- }
- if (mat->get_name() == "") {
- continue;
- }
-
- if (!p_materials.has(mat)) {
- String ext_name;
-
- if (p_materials_as_text) {
- ext_name = p_base_path.plus_file(_make_extname(mat->get_name()) + ".tres");
- } else {
- ext_name = p_base_path.plus_file(_make_extname(mat->get_name()) + ".material");
- }
-
- if (p_keep_materials && FileAccess::exists(ext_name)) {
- //if exists, use it
- p_materials[mat] = ResourceLoader::load(ext_name);
- } else {
- ResourceSaver::save(ext_name, mat, ResourceSaver::FLAG_CHANGE_PATH);
- p_materials[mat] = ResourceLoader::load(ext_name, "", true); // disable loading from the cache.
- }
- }
-
- if (p_materials[mat] != mat) {
- mesh->surface_set_material(i, p_materials[mat]);
-
- //re-save the mesh since a material is now assigned
- if (p_make_meshes) {
- String ext_name;
-
- if (p_meshes_as_text) {
- ext_name = p_base_path.plus_file(_make_extname(mesh->get_name()) + ".tres");
- } else {
- ext_name = p_base_path.plus_file(_make_extname(mesh->get_name()) + ".mesh");
- }
-
- ResourceSaver::save(ext_name, mesh, ResourceSaver::FLAG_CHANGE_PATH);
- p_meshes[mesh] = ResourceLoader::load(ext_name);
- }
- }
- }
- if (!p_make_meshes) {
- p_meshes[mesh] = Ref<ArrayMesh>(); //save it anyway, so it won't be checked again
- }
- }
- }
+ if (p_option.begins_with("animation/slice_")) {
+ int max_slice = p_options["animation/slices/amount"];
+ int slice = p_option.get_slice("/", 1).get_slice("_", 1).to_int() - 1;
+ if (slice >= max_slice) {
+ return false;
}
}
+ } break;
+ default: {
}
}
- for (int i = 0; i < p_node->get_child_count(); i++) {
- _make_external_resources(p_node->get_child(i), p_base_path, p_make_animations, p_animations_as_text, p_keep_animations, p_make_materials, p_materials_as_text, p_keep_materials, p_make_meshes, p_meshes_as_text, p_animations, p_materials, p_meshes);
- }
+ return true;
}
void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, int p_preset) const {
@@ -1102,48 +1030,26 @@ 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;
}
- bool materials_out = p_preset == PRESET_SEPARATE_MATERIALS || p_preset == PRESET_SEPARATE_MESHES_AND_MATERIALS || p_preset == PRESET_MULTIPLE_SCENES_AND_MATERIALS || p_preset == PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS || p_preset == PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS;
- bool meshes_out = p_preset == PRESET_SEPARATE_MESHES || p_preset == PRESET_SEPARATE_MESHES_AND_MATERIALS || p_preset == PRESET_SEPARATE_MESHES_AND_ANIMATIONS || p_preset == PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS;
- bool scenes_out = p_preset == PRESET_MULTIPLE_SCENES || p_preset == PRESET_MULTIPLE_SCENES_AND_MATERIALS;
- bool animations_out = p_preset == PRESET_SEPARATE_ANIMATIONS || p_preset == PRESET_SEPARATE_MESHES_AND_ANIMATIONS || p_preset == PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS || p_preset == PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS;
-
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "nodes/root_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001"), 1.0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "nodes/custom_script", PROPERTY_HINT_FILE, script_ext_hint), ""));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "nodes/storage", PROPERTY_HINT_ENUM, "Single Scene,Instanced Sub-Scenes"), scenes_out ? 1 : 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/location", PROPERTY_HINT_ENUM, "Node,Mesh"), (meshes_out || materials_out) ? 1 : 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "materials/storage", PROPERTY_HINT_ENUM, "Built-In,Files (.material),Files (.tres)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), materials_out ? 1 : 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "materials/keep_on_reimport"), materials_out));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/compress"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/ensure_tangents"), true));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/storage", PROPERTY_HINT_ENUM, "Built-In,Files (.mesh),Files (.tres)"), meshes_out ? 1 : 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Enable,Gen Lightmaps", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/generate_lods"), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "meshes/create_shadow_meshes"), true));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "meshes/light_baking", PROPERTY_HINT_ENUM, "Disabled,Dynamic,Static,Static Lightmaps", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 2));
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, "external_files/store_in_subdir"), false));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 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, "animation/filter_script", PROPERTY_HINT_MULTILINE_TEXT), ""));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "animation/storage", PROPERTY_HINT_ENUM, "Built-In,Files (.anim),Files (.tres)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), animations_out));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/keep_custom_tracks"), animations_out));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true));
- r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/optimizer/max_linear_error"), 0.05));
- r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/optimizer/max_angular_error"), 0.01));
- r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/optimizer/max_angle"), 22));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/optimizer/remove_unused_tracks"), true));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "animation/clips/amount", PROPERTY_HINT_RANGE, "0,256,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
- for (int i = 0; i < 256; i++) {
- r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "animation/clip_" + itos(i + 1) + "/name"), ""));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "animation/clip_" + itos(i + 1) + "/start_frame"), 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "animation/clip_" + itos(i + 1) + "/end_frame"), 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/clip_" + itos(i + 1) + "/loops"), false));
- }
+ r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "_subresources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), Dictionary()));
}
void ResourceImporterScene::_replace_owner(Node *p_node, Node *p_scene, Node *p_new_owner) {
@@ -1168,9 +1074,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;
}
}
@@ -1198,9 +1104,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;
}
}
@@ -1215,6 +1121,208 @@ Ref<Animation> ResourceImporterScene::import_animation_from_other_importer(Edito
return importer->import_animation(p_path, p_flags, p_bake_fps);
}
+void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_mesh_data, bool p_generate_lods, bool p_create_shadow_meshes, LightBakeMode p_light_bake_mode, float p_lightmap_texel_size, const Vector<uint8_t> &p_src_lightmap_cache, Vector<Vector<uint8_t>> &r_lightmap_caches) {
+ EditorSceneImporterMeshNode3D *src_mesh_node = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
+ if (src_mesh_node) {
+ //is mesh
+ MeshInstance3D *mesh_node = memnew(MeshInstance3D);
+ mesh_node->set_name(src_mesh_node->get_name());
+ mesh_node->set_transform(src_mesh_node->get_transform());
+ mesh_node->set_skin(src_mesh_node->get_skin());
+ mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path());
+ if (src_mesh_node->get_mesh().is_valid()) {
+ Ref<ArrayMesh> mesh;
+ if (!src_mesh_node->get_mesh()->has_mesh()) {
+ //do mesh processing
+
+ bool generate_lods = p_generate_lods;
+ bool create_shadow_meshes = p_create_shadow_meshes;
+ bool bake_lightmaps = p_light_bake_mode == LIGHT_BAKE_STATIC_LIGHTMAPS;
+ String save_to_file;
+
+ String mesh_id;
+
+ if (src_mesh_node->get_mesh()->has_meta("import_id")) {
+ mesh_id = src_mesh_node->get_mesh()->get_meta("import_id");
+ } else {
+ mesh_id = src_mesh_node->get_mesh()->get_name();
+ }
+
+ if (mesh_id != String() && p_mesh_data.has(mesh_id)) {
+ Dictionary mesh_settings = p_mesh_data[mesh_id];
+
+ if (mesh_settings.has("generate/shadow_meshes")) {
+ int shadow_meshes = mesh_settings["generate/shadow_meshes"];
+ if (shadow_meshes == MESH_OVERRIDE_ENABLE) {
+ create_shadow_meshes = true;
+ } else if (shadow_meshes == MESH_OVERRIDE_DISABLE) {
+ create_shadow_meshes = false;
+ }
+ }
+
+ if (mesh_settings.has("generate/lightmap_uv")) {
+ int lightmap_uv = mesh_settings["generate/lightmap_uv"];
+ if (lightmap_uv == MESH_OVERRIDE_ENABLE) {
+ bake_lightmaps = true;
+ } else if (lightmap_uv == MESH_OVERRIDE_DISABLE) {
+ bake_lightmaps = false;
+ }
+ }
+
+ if (mesh_settings.has("generate/lods")) {
+ int lods = mesh_settings["generate/lods"];
+ if (lods == MESH_OVERRIDE_ENABLE) {
+ generate_lods = true;
+ } else if (lods == MESH_OVERRIDE_DISABLE) {
+ generate_lods = false;
+ }
+ }
+
+ if (mesh_settings.has("save_to_file/enabled") && bool(mesh_settings["save_to_file/enabled"]) && mesh_settings.has("save_to_file/path")) {
+ save_to_file = mesh_settings["save_to_file/path"];
+ if (!save_to_file.is_resource_file()) {
+ save_to_file = "";
+ }
+ }
+ }
+
+ if (generate_lods) {
+ src_mesh_node->get_mesh()->generate_lods();
+ }
+ if (create_shadow_meshes) {
+ src_mesh_node->get_mesh()->create_shadow_mesh();
+ }
+
+ if (bake_lightmaps) {
+ Transform3D xf;
+ Node3D *n = src_mesh_node;
+ while (n) {
+ xf = n->get_transform() * xf;
+ n = n->get_parent_node_3d();
+ }
+
+ Vector<uint8_t> lightmap_cache;
+ src_mesh_node->get_mesh()->lightmap_unwrap_cached(xf, p_lightmap_texel_size, p_src_lightmap_cache, lightmap_cache);
+
+ if (!lightmap_cache.is_empty()) {
+ if (r_lightmap_caches.is_empty()) {
+ r_lightmap_caches.push_back(lightmap_cache);
+ } else {
+ String new_md5 = String::md5(lightmap_cache.ptr()); // MD5 is stored at the beginning of the cache data
+
+ for (int i = 0; i < r_lightmap_caches.size(); i++) {
+ String md5 = String::md5(r_lightmap_caches[i].ptr());
+ if (new_md5 < md5) {
+ r_lightmap_caches.insert(i, lightmap_cache);
+ break;
+ }
+
+ if (new_md5 == md5) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (save_to_file != String()) {
+ Ref<Mesh> existing = Ref<Resource>(ResourceCache::get(save_to_file));
+ if (existing.is_valid()) {
+ //if somehow an existing one is useful, create
+ existing->reset_state();
+ }
+ mesh = src_mesh_node->get_mesh()->get_mesh(existing);
+
+ ResourceSaver::save(save_to_file, mesh); //override
+
+ mesh->set_path(save_to_file, true); //takeover existing, if needed
+
+ } else {
+ mesh = src_mesh_node->get_mesh()->get_mesh();
+ }
+ } else {
+ mesh = src_mesh_node->get_mesh()->get_mesh();
+ }
+
+ if (mesh.is_valid()) {
+ mesh_node->set_mesh(mesh);
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+ mesh_node->set_surface_override_material(i, src_mesh_node->get_surface_material(i));
+ }
+ }
+ }
+
+ switch (p_light_bake_mode) {
+ case LIGHT_BAKE_DISABLED: {
+ mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_DISABLED);
+ } break;
+ case LIGHT_BAKE_DYNAMIC: {
+ mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_DYNAMIC);
+ } break;
+ case LIGHT_BAKE_STATIC:
+ case LIGHT_BAKE_STATIC_LIGHTMAPS: {
+ mesh_node->set_gi_mode(GeometryInstance3D::GI_MODE_BAKED);
+ } break;
+ }
+
+ p_node->replace_by(mesh_node);
+ memdelete(p_node);
+ p_node = mesh_node;
+ }
+
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ _generate_meshes(p_node->get_child(i), p_mesh_data, p_generate_lods, p_create_shadow_meshes, p_light_bake_mode, p_lightmap_texel_size, p_src_lightmap_cache, r_lightmap_caches);
+ }
+}
+
+void ResourceImporterScene::_add_shapes(Node *p_node, const List<Ref<Shape3D>> &p_shapes) {
+ for (const Ref<Shape3D> &E : p_shapes) {
+ CollisionShape3D *cshape = memnew(CollisionShape3D);
+ cshape->set_shape(E);
+ p_node->add_child(cshape);
+
+ cshape->set_owner(p_node->get_owner());
+ }
+}
+
+Node *ResourceImporterScene::pre_import(const String &p_source_file) {
+ Ref<EditorSceneImporter> importer;
+ String ext = p_source_file.get_extension().to_lower();
+
+ EditorProgress progress("pre-import", TTR("Pre-Import Scene"), 0);
+ progress.step(TTR("Importing Scene..."), 0);
+
+ for (Set<Ref<EditorSceneImporter>>::Element *E = importers.front(); E; E = E->next()) {
+ List<String> extensions;
+ E->get()->get_extensions(&extensions);
+
+ for (const String &F : extensions) {
+ if (F.to_lower() == ext) {
+ importer = E->get();
+ break;
+ }
+ }
+
+ if (importer.is_valid()) {
+ break;
+ }
+ }
+
+ ERR_FAIL_COND_V(!importer.is_valid(), nullptr);
+
+ Error err = OK;
+ Node *scene = importer->import_scene(p_source_file, EditorSceneImporter::IMPORT_ANIMATION | EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS, 15, nullptr, &err);
+ if (!scene || err != OK) {
+ return nullptr;
+ }
+
+ Map<Ref<EditorSceneImporterMesh>, List<Ref<Shape3D>>> collision_map;
+
+ _pre_fix_node(scene, scene, collision_map);
+
+ return scene;
+}
+
Error ResourceImporterScene::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) {
const String &src_path = p_source_file;
@@ -1228,8 +1336,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;
}
@@ -1244,31 +1352,21 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
float fps = p_options["animation/fps"];
- int import_flags = EditorSceneImporter::IMPORT_ANIMATION_DETECT_LOOP;
- if (!bool(p_options["animation/optimizer/remove_unused_tracks"])) {
- import_flags |= EditorSceneImporter::IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS;
- }
+ int import_flags = 0;
if (bool(p_options["animation/import"])) {
import_flags |= EditorSceneImporter::IMPORT_ANIMATION;
}
- if (int(p_options["meshes/compress"])) {
- import_flags |= EditorSceneImporter::IMPORT_USE_COMPRESSION;
+ if (bool(p_options["skins/use_named_skins"])) {
+ import_flags |= EditorSceneImporter::IMPORT_USE_NAMED_SKIN_BINDS;
}
- if (bool(p_options["meshes/ensure_tangents"])) {
+ bool ensure_tangents = p_options["meshes/ensure_tangents"];
+ if (ensure_tangents) {
import_flags |= EditorSceneImporter::IMPORT_GENERATE_TANGENT_ARRAYS;
}
- if (int(p_options["materials/location"]) == 0) {
- import_flags |= EditorSceneImporter::IMPORT_MATERIALS_IN_INSTANCES;
- }
-
- if (bool(p_options["skins/use_named_skins"])) {
- import_flags |= EditorSceneImporter::IMPORT_USE_NAMED_SKIN_BINDS;
- }
-
Error err = OK;
List<String> missing_deps; // for now, not much will be done with this
Node *scene = importer->import_scene(src_path, import_flags, fps, &missing_deps, &err);
@@ -1276,6 +1374,34 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
return err;
}
+ Dictionary subresources = p_options["_subresources"];
+
+ Dictionary node_data;
+ if (subresources.has("nodes")) {
+ node_data = subresources["nodes"];
+ }
+
+ Dictionary material_data;
+ if (subresources.has("materials")) {
+ material_data = subresources["materials"];
+ }
+
+ Dictionary animation_data;
+ if (subresources.has("animations")) {
+ animation_data = subresources["animations"];
+ }
+
+ Set<Ref<EditorSceneImporterMesh>> scanned_meshes;
+ Map<Ref<EditorSceneImporterMesh>, List<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.
@@ -1286,7 +1412,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);
@@ -1311,193 +1437,44 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
scene->set_name(p_save_path.get_file().get_basename());
}
- err = OK;
-
- String animation_filter = String(p_options["animation/filter_script"]).strip_edges();
-
- bool use_optimizer = p_options["animation/optimizer/enabled"];
- float anim_optimizer_linerr = p_options["animation/optimizer/max_linear_error"];
- float anim_optimizer_angerr = p_options["animation/optimizer/max_angular_error"];
- float anim_optimizer_maxang = p_options["animation/optimizer/max_angle"];
+ bool gen_lods = bool(p_options["meshes/generate_lods"]);
+ bool create_shadow_meshes = bool(p_options["meshes/create_shadow_meshes"]);
int light_bake_mode = p_options["meshes/light_baking"];
+ float texel_size = p_options["meshes/lightmap_texel_size"];
+ float lightmap_texel_size = MAX(0.001, texel_size);
- Map<Ref<Mesh>, List<Ref<Shape3D>>> collision_map;
-
- scene = _fix_node(scene, scene, collision_map, LightBakeMode(light_bake_mode));
-
- if (use_optimizer) {
- _optimize_animations(scene, anim_optimizer_linerr, anim_optimizer_angerr, anim_optimizer_maxang);
- }
+ Vector<uint8_t> src_lightmap_cache;
+ Vector<Vector<uint8_t>> mesh_lightmap_caches;
- Array animation_clips;
{
- int clip_count = p_options["animation/clips/amount"];
-
- for (int i = 0; i < clip_count; i++) {
- String name = p_options["animation/clip_" + itos(i + 1) + "/name"];
- int from_frame = p_options["animation/clip_" + itos(i + 1) + "/start_frame"];
- int end_frame = p_options["animation/clip_" + itos(i + 1) + "/end_frame"];
- bool loop = p_options["animation/clip_" + itos(i + 1) + "/loops"];
-
- animation_clips.push_back(name);
- animation_clips.push_back(from_frame / fps);
- animation_clips.push_back(end_frame / fps);
- animation_clips.push_back(loop);
+ src_lightmap_cache = FileAccess::get_file_as_array(p_source_file + ".unwrap_cache", &err);
+ if (err != OK) {
+ src_lightmap_cache.clear();
}
}
- if (animation_clips.size()) {
- _create_clips(scene, animation_clips, !bool(p_options["animation/optimizer/remove_unused_tracks"]));
- }
-
- if (animation_filter != "") {
- _filter_tracks(scene, animation_filter);
- }
- bool external_animations = int(p_options["animation/storage"]) == 1 || int(p_options["animation/storage"]) == 2;
- bool external_animations_as_text = int(p_options["animation/storage"]) == 2;
- bool keep_custom_tracks = p_options["animation/keep_custom_tracks"];
- bool external_materials = int(p_options["materials/storage"]) == 1 || int(p_options["materials/storage"]) == 2;
- bool external_materials_as_text = int(p_options["materials/storage"]) == 2;
- bool external_meshes = int(p_options["meshes/storage"]) == 1 || int(p_options["meshes/storage"]) == 2;
- bool external_meshes_as_text = int(p_options["meshes/storage"]) == 2;
- bool external_scenes = int(p_options["nodes/storage"]) == 1;
-
- String base_path = p_source_file.get_base_dir();
-
- if (external_animations || external_materials || external_meshes || external_scenes) {
- if (bool(p_options["external_files/store_in_subdir"])) {
- String subdir_name = p_source_file.get_file().get_basename();
- DirAccess *da = DirAccess::open(base_path);
- Error err2 = da->make_dir(subdir_name);
- memdelete(da);
- ERR_FAIL_COND_V_MSG(err2 != OK && err2 != ERR_ALREADY_EXISTS, err2, "Cannot make directory '" + subdir_name + "'.");
- base_path = base_path.plus_file(subdir_name);
- }
+ Dictionary mesh_data;
+ if (subresources.has("meshes")) {
+ mesh_data = subresources["meshes"];
}
+ _generate_meshes(scene, mesh_data, gen_lods, create_shadow_meshes, LightBakeMode(light_bake_mode), lightmap_texel_size, src_lightmap_cache, mesh_lightmap_caches);
- if (light_bake_mode == 2 /* || generate LOD */) {
- Map<Ref<ArrayMesh>, Transform> meshes;
- _find_meshes(scene, meshes);
-
- String file_id = src_path.get_file();
- String cache_file_path = base_path.plus_file(file_id + ".unwrap_cache");
-
- Vector<unsigned char> cache_data;
-
- if (FileAccess::exists(cache_file_path)) {
- Error err2;
- FileAccess *file = FileAccess::open(cache_file_path, FileAccess::READ, &err2);
-
- if (err2) {
- if (file) {
- memdelete(file);
- }
- } else {
- int cache_size = file->get_len();
- cache_data.resize(cache_size);
- file->get_buffer(cache_data.ptrw(), cache_size);
+ if (mesh_lightmap_caches.size()) {
+ FileAccessRef f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE);
+ if (f) {
+ f->store_32(mesh_lightmap_caches.size());
+ for (int i = 0; i < mesh_lightmap_caches.size(); i++) {
+ String md5 = String::md5(mesh_lightmap_caches[i].ptr());
+ f->store_buffer(mesh_lightmap_caches[i].ptr(), mesh_lightmap_caches[i].size());
}
+ f->close();
}
-
- float texel_size = p_options["meshes/lightmap_texel_size"];
- texel_size = MAX(0.001, texel_size);
-
- Map<String, unsigned int> used_unwraps;
-
- EditorProgress progress2("gen_lightmaps", TTR("Generating Lightmaps"), meshes.size());
- int step = 0;
- for (Map<Ref<ArrayMesh>, Transform>::Element *E = meshes.front(); E; E = E->next()) {
- Ref<ArrayMesh> mesh = E->key();
- String name = mesh->get_name();
- if (name == "") { //should not happen but..
- name = "Mesh " + itos(step);
- }
-
- progress2.step(TTR("Generating for Mesh: ") + name + " (" + itos(step) + "/" + itos(meshes.size()) + ")", step);
-
- int *ret_cache_data = (int *)cache_data.ptrw();
- unsigned int ret_cache_size = cache_data.size();
- bool ret_used_cache = true; // Tell the unwrapper to use the cache
- Error err2 = mesh->lightmap_unwrap_cached(ret_cache_data, ret_cache_size, ret_used_cache, E->get(), texel_size);
-
- if (err2 != OK) {
- EditorNode::add_io_error("Mesh '" + name + "' failed lightmap generation. Please fix geometry.");
- } else {
- String hash = String::md5((unsigned char *)ret_cache_data);
- used_unwraps.insert(hash, ret_cache_size);
-
- if (!ret_used_cache) {
- // Cache was not used, add the generated entry to the current cache
- if (cache_data.empty()) {
- cache_data.resize(4 + ret_cache_size);
- int *data = (int *)cache_data.ptrw();
- data[0] = 1;
- memcpy(&data[1], ret_cache_data, ret_cache_size);
- } else {
- int current_size = cache_data.size();
- cache_data.resize(cache_data.size() + ret_cache_size);
- unsigned char *ptrw = cache_data.ptrw();
- memcpy(&ptrw[current_size], ret_cache_data, ret_cache_size);
- int *data = (int *)ptrw;
- data[0] += 1;
- }
- }
- }
- step++;
- }
-
- Error err2;
- FileAccess *file = FileAccess::open(cache_file_path, FileAccess::WRITE, &err2);
-
- if (err2) {
- if (file) {
- memdelete(file);
- }
- } else {
- // Store number of entries
- file->store_32(used_unwraps.size());
-
- // Store cache entries
- const int *cache = (int *)cache_data.ptr();
- unsigned int r_idx = 1;
- for (int i = 0; i < cache[0]; ++i) {
- unsigned char *entry_start = (unsigned char *)&cache[r_idx];
- String entry_hash = String::md5(entry_start);
- if (used_unwraps.has(entry_hash)) {
- unsigned int entry_size = used_unwraps[entry_hash];
- file->store_buffer(entry_start, entry_size);
- }
-
- r_idx += 4; // hash
- r_idx += 2; // size hint
-
- int vertex_count = cache[r_idx];
- r_idx += 1; // vertex count
- r_idx += vertex_count; // vertex
- r_idx += vertex_count * 2; // uvs
-
- int index_count = cache[r_idx];
- r_idx += 1; // index count
- r_idx += index_count; // indices
- }
-
- file->close();
- }
- }
-
- if (external_animations || external_materials || external_meshes) {
- Map<Ref<Animation>, Ref<Animation>> anim_map;
- Map<Ref<Material>, Ref<Material>> mat_map;
- Map<Ref<ArrayMesh>, Ref<ArrayMesh>> mesh_map;
-
- bool keep_materials = bool(p_options["materials/keep_on_reimport"]);
-
- _make_external_resources(scene, base_path, external_animations, external_animations_as_text, keep_custom_tracks, external_materials, external_materials_as_text, keep_materials, external_meshes, external_meshes_as_text, anim_map, mat_map, mesh_map);
}
+ err = OK;
progress.step(TTR("Running Custom Script..."), 2);
- String post_import_script_path = p_options["nodes/custom_script"];
+ String post_import_script_path = p_options["import_script/path"];
Ref<EditorScenePostImport> post_import_script;
if (post_import_script_path != "") {
@@ -1516,41 +1493,18 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
}
if (post_import_script.is_valid()) {
- post_import_script->init(base_path, p_source_file);
+ post_import_script->init(p_source_file);
scene = post_import_script->post_import(scene);
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;
}
}
progress.step(TTR("Saving..."), 104);
- if (external_scenes) {
- //save sub-scenes as instances!
- for (int i = 0; i < scene->get_child_count(); i++) {
- Node *child = scene->get_child(i);
- if (child->get_owner() != scene) {
- continue; //not a real child probably created by scene type (ig, a scrollbar)
- }
- _replace_owner(child, scene, child);
-
- String cn = String(child->get_name()).strip_edges().replace(".", "_").replace(":", "_");
- if (cn == String()) {
- cn = "ChildNode" + itos(i);
- }
- String path = base_path.plus_file(cn + ".scn");
- child->set_filename(path);
-
- Ref<PackedScene> packer = memnew(PackedScene);
- packer->pack(child);
- err = ResourceSaver::save(path, packer); //do not take over, let the changed files reload themselves
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot save scene to file '" + path + "'.");
- }
- }
-
Ref<PackedScene> packer = memnew(PackedScene);
packer->pack(scene);
print_verbose("Saving scene to: " + p_save_path + ".scn");
@@ -1567,6 +1521,13 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
ResourceImporterScene *ResourceImporterScene::singleton = nullptr;
+bool ResourceImporterScene::ResourceImporterScene::has_advanced_options() const {
+ return true;
+}
+void ResourceImporterScene::ResourceImporterScene::show_advanced_options(const String &p_path) {
+ SceneImportSettings::get_singleton()->open_settings(p_path);
+}
+
ResourceImporterScene::ResourceImporterScene() {
singleton = this;
}
@@ -1586,7 +1547,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 465d11116b..542959be02 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,14 +32,18 @@
#define RESOURCEIMPORTERSCENE_H
#include "core/io/resource_importer.h"
+#include "scene/3d/node_3d.h"
#include "scene/resources/animation.h"
#include "scene/resources/mesh.h"
#include "scene/resources/shape_3d.h"
+#include "scene/resources/skin.h"
class Material;
+class AnimationPlayer;
-class EditorSceneImporter : public Reference {
- GDCLASS(EditorSceneImporter, Reference);
+class EditorSceneImporterMesh;
+class EditorSceneImporter : public RefCounted {
+ GDCLASS(EditorSceneImporter, RefCounted);
protected:
static void _bind_methods();
@@ -47,19 +51,18 @@ 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,
IMPORT_ANIMATION = 2,
- IMPORT_ANIMATION_DETECT_LOOP = 4,
- IMPORT_ANIMATION_OPTIMIZE = 8,
- IMPORT_ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS = 16,
- IMPORT_ANIMATION_KEEP_VALUE_TRACKS = 32,
- IMPORT_GENERATE_TANGENT_ARRAYS = 256,
- IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 512,
- IMPORT_MATERIALS_IN_INSTANCES = 1024,
- IMPORT_USE_COMPRESSION = 2048,
- IMPORT_USE_NAMED_SKIN_BINDS = 4096,
+ IMPORT_FAIL_ON_MISSING_DEPENDENCIES = 4,
+ IMPORT_GENERATE_TANGENT_ARRAYS = 8,
+ IMPORT_USE_NAMED_SKIN_BINDS = 16,
};
@@ -71,20 +74,20 @@ public:
EditorSceneImporter() {}
};
-class EditorScenePostImport : public Reference {
- GDCLASS(EditorScenePostImport, Reference);
+class EditorScenePostImport : public RefCounted {
+ GDCLASS(EditorScenePostImport, RefCounted);
- String source_folder;
String source_file;
protected:
static void _bind_methods();
+ GDVIRTUAL1R(Object *, _post_import, Node *)
+
public:
- String get_source_folder() const;
String get_source_file() const;
virtual Node *post_import(Node *p_scene);
- virtual void init(const String &p_source_folder, const String &p_source_file);
+ virtual void init(const String &p_source_file);
EditorScenePostImport();
};
@@ -95,30 +98,36 @@ class ResourceImporterScene : public ResourceImporter {
static ResourceImporterScene *singleton;
- enum Presets {
- PRESET_SEPARATE_MATERIALS,
- PRESET_SEPARATE_MESHES,
- PRESET_SEPARATE_ANIMATIONS,
-
- PRESET_SINGLE_SCENE,
+ enum LightBakeMode {
+ LIGHT_BAKE_DISABLED,
+ LIGHT_BAKE_DYNAMIC,
+ LIGHT_BAKE_STATIC,
+ LIGHT_BAKE_STATIC_LIGHTMAPS
+ };
- PRESET_SEPARATE_MESHES_AND_MATERIALS,
- PRESET_SEPARATE_MESHES_AND_ANIMATIONS,
- PRESET_SEPARATE_MATERIALS_AND_ANIMATIONS,
- PRESET_SEPARATE_MESHES_MATERIALS_AND_ANIMATIONS,
+ enum MeshPhysicsMode {
+ MESH_PHYSICS_DISABLED,
+ MESH_PHYSICS_MESH_AND_STATIC_COLLIDER,
+ MESH_PHYSICS_RIGID_BODY_AND_MESH,
+ MESH_PHYSICS_STATIC_COLLIDER_ONLY,
+ MESH_PHYSICS_AREA_ONLY,
+ };
- PRESET_MULTIPLE_SCENES,
- PRESET_MULTIPLE_SCENES_AND_MATERIALS,
- PRESET_MAX
+ enum NavMeshMode {
+ NAVMESH_DISABLED,
+ NAVMESH_MESH_AND_NAVMESH,
+ NAVMESH_NAVMESH_ONLY,
};
- enum LightBakeMode {
- LIGHT_BAKE_DISABLED,
- LIGHT_BAKE_ENABLE,
- LIGHT_BAKE_LIGHTMAPS
+ enum MeshOverride {
+ MESH_OVERRIDE_DEFAULT,
+ MESH_OVERRIDE_ENABLE,
+ MESH_OVERRIDE_DISABLE,
};
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);
public:
static ResourceImporterScene *get_singleton() { return singleton; }
@@ -133,30 +142,47 @@ public:
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_format_version() 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;
- virtual int get_import_order() const override { return 100; } //after everything
+ enum InternalImportCategory {
+ INTERNAL_IMPORT_CATEGORY_NODE,
+ INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE,
+ INTERNAL_IMPORT_CATEGORY_MESH,
+ INTERNAL_IMPORT_CATEGORY_MATERIAL,
+ INTERNAL_IMPORT_CATEGORY_ANIMATION,
+ INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE,
+ INTERNAL_IMPORT_CATEGORY_MAX
+ };
- void _find_meshes(Node *p_node, Map<Ref<ArrayMesh>, Transform> &meshes);
+ 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;
- void _make_external_resources(Node *p_node, const String &p_base_path, bool p_make_animations, bool p_animations_as_text, bool p_keep_animations, bool p_make_materials, bool p_materials_as_text, bool p_keep_materials, bool p_make_meshes, bool p_meshes_as_text, Map<Ref<Animation>, Ref<Animation>> &p_animations, Map<Ref<Material>, Ref<Material>> &p_materials, Map<Ref<ArrayMesh>, Ref<ArrayMesh>> &p_meshes);
+ 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;
+ // Import scenes *after* everything else (such as textures).
+ virtual int get_import_order() const override { return ResourceImporter::IMPORT_ORDER_SCENE; }
- Node *_fix_node(Node *p_node, Node *p_root, Map<Ref<Mesh>, List<Ref<Shape3D>>> &collision_map, LightBakeMode p_light_bake_mode);
+ 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);
- void _create_clips(Node *scene, const Array &p_clips, bool p_bake_all);
- void _filter_anim_tracks(Ref<Animation> anim, Set<String> &keep);
- void _filter_tracks(Node *scene, const String &p_text);
- void _optimize_animations(Node *scene, float p_max_lin_error, float p_max_ang_error, float p_max_angle);
+ 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);
+ void _optimize_animations(AnimationPlayer *anim, float p_max_lin_error, float p_max_ang_error, float p_max_angle);
+ Node *pre_import(const String &p_source_file);
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;
Node *import_scene_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps);
Ref<Animation> import_animation_from_other_importer(EditorSceneImporter *p_exception, const String &p_path, uint32_t p_flags, int p_bake_fps);
+ virtual bool has_advanced_options() const override;
+ virtual void show_advanced_options(const String &p_path) override;
+
+ virtual bool can_import_threaded() const override { return false; }
+
ResourceImporterScene();
};
diff --git a/editor/import/resource_importer_shader_file.cpp b/editor/import/resource_importer_shader_file.cpp
index a2e80dfa18..4d92490675 100644
--- a/editor/import/resource_importer_shader_file.cpp
+++ b/editor/import/resource_importer_shader_file.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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"
@@ -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_shader_file.h b/editor/import/resource_importer_shader_file.h
index 66ae626c51..c421132ec2 100644
--- a/editor/import/resource_importer_shader_file.h
+++ b/editor/import/resource_importer_shader_file.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index 3a0e624a8f..daf7b15794 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -82,13 +82,13 @@ void ResourceImporterTexture::update_imports() {
MutexLock lock(mutex);
Vector<String> to_reimport;
{
- if (make_flags.empty()) {
+ if (make_flags.is_empty()) {
return;
}
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);
@@ -172,7 +172,7 @@ bool ResourceImporterTexture::get_option_visibility(const String &p_option, cons
if (compress_mode < COMPRESS_VRAM_COMPRESSED) {
return false;
}
- if (!ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc")) {
+ if (!ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_bptc")) {
return false;
}
}
@@ -201,14 +201,14 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options,
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/normal_map", PROPERTY_HINT_ENUM, "Detect,Enable,Disabled"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized"), 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/streamed"), false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress/streamed"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), (p_preset == PRESET_3D ? true : false)));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mipmaps/limit", PROPERTY_HINT_RANGE, "-1,256"), -1));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "roughness/mode", PROPERTY_HINT_ENUM, "Detect,Disabled,Red,Green,Blue,Alpha,Gray"), 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "roughness/src_normal", PROPERTY_HINT_FILE, "*.png,*.jpg"), ""));
+ 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');
@@ -388,7 +396,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
uint32_t mipmap_limit = int(mipmaps ? int(p_options["mipmaps/limit"]) : int(-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));
}
}
}
@@ -473,8 +486,8 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
bool ok_on_pc = false;
bool is_hdr = (image->get_format() >= Image::FORMAT_RF && image->get_format() <= Image::FORMAT_RGBE9995);
bool is_ldr = (image->get_format() >= Image::FORMAT_L8 && image->get_format() <= Image::FORMAT_RGB565);
- bool can_bptc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_bptc");
- bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/vram_compression/import_s3tc");
+ bool can_bptc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_bptc");
+ bool can_s3tc = ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_s3tc");
if (can_bptc) {
//add to the list anyway
@@ -524,20 +537,20 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
ok_on_pc = true;
}
- if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) {
+ if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc2")) {
_save_stex(image, p_save_path + ".etc2.stex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel);
r_platform_variants->push_back("etc2");
formats_imported.push_back("etc2");
}
- if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) {
+ if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_etc")) {
_save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel);
r_platform_variants->push_back("etc");
formats_imported.push_back("etc");
}
- if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) {
- _save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC4, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel);
+ if (ProjectSettings::get_singleton()->get("rendering/textures/vram_compression/import_pvrtc")) {
+ _save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC1_4, mipmaps, stream, detect_3d, detect_roughness, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel);
r_platform_variants->push_back("pvrtc");
formats_imported.push_back("pvrtc");
}
@@ -574,7 +587,7 @@ String ResourceImporterTexture::get_import_settings_string() const {
int index = 0;
while (compression_formats[index]) {
- String setting_path = "rendering/vram_compression/import_" + String(compression_formats[index]);
+ String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
bool test = ProjectSettings::get_singleton()->get(setting_path);
if (test) {
s += String(compression_formats[index]);
@@ -606,7 +619,7 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co
int index = 0;
bool valid = true;
while (compression_formats[index]) {
- String setting_path = "rendering/vram_compression/import_" + String(compression_formats[index]);
+ String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]);
bool test = ProjectSettings::get_singleton()->get(setting_path);
if (test) {
if (formats_imported.find(compression_formats[index]) == -1) {
diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h
index bc41aacae5..41220009cd 100644
--- a/editor/import/resource_importer_texture.h
+++ b/editor/import/resource_importer_texture.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,9 +31,9 @@
#ifndef RESOURCEIMPORTTEXTURE_H
#define RESOURCEIMPORTTEXTURE_H
-#include "core/image.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"
@@ -60,13 +60,9 @@ protected:
Mutex mutex;
struct MakeInfo {
- int flags;
+ int flags = 0;
String normal_path_for_roughness;
- RS::TextureDetectRoughnessChannel channel_for_roughness;
- MakeInfo() {
- flags = 0;
- channel_for_roughness = RS::TEXTURE_DETECT_ROUGNHESS_R;
- }
+ RS::TextureDetectRoughnessChannel channel_for_roughness = RS::TEXTURE_DETECT_ROUGHNESS_R;
};
Map<StringName, MakeInfo> make_flags;
diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp
index 2423553d22..dec1466da1 100644
--- a/editor/import/resource_importer_texture_atlas.cpp
+++ b/editor/import/resource_importer_texture_atlas.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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";
@@ -97,7 +97,7 @@ Error ResourceImporterTextureAtlas::import(const String &p_source_file, const St
return OK;
}
-static void _plot_triangle(Vector2 *vertices, const Vector2 &p_offset, bool p_transposed, Ref<Image> p_image, const Ref<Image> &p_src_image) {
+static void _plot_triangle(Vector2i *vertices, const Vector2i &p_offset, bool p_transposed, Ref<Image> p_image, const Ref<Image> &p_src_image) {
int width = p_image->get_width();
int height = p_image->get_height();
int src_width = p_src_image->get_width();
@@ -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++) {
@@ -281,13 +281,13 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file
for (int j = 0; j < pack_data.chart_pieces.size(); j++) {
const EditorAtlasPacker::Chart &chart = charts[pack_data.chart_pieces[j]];
for (int k = 0; k < chart.faces.size(); k++) {
- Vector2 positions[3];
+ Vector2i positions[3];
for (int l = 0; l < 3; l++) {
int vertex_idx = chart.faces[k].vertex[l];
- positions[l] = chart.vertices[vertex_idx];
+ positions[l] = Vector2i(chart.vertices[vertex_idx]);
}
- _plot_triangle(positions, chart.final_offset, chart.transposed, new_atlas, pack_data.image);
+ _plot_triangle(positions, Vector2i(chart.final_offset), chart.transposed, new_atlas, pack_data.image);
}
}
}
@@ -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_texture_atlas.h b/editor/import/resource_importer_texture_atlas.h
index 25a662a333..b675d12477 100644
--- a/editor/import/resource_importer_texture_atlas.h
+++ b/editor/import/resource_importer_texture_atlas.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,14 +31,14 @@
#ifndef RESOURCE_IMPORTER_TEXTURE_ATLAS_H
#define RESOURCE_IMPORTER_TEXTURE_ATLAS_H
-#include "core/image.h"
+#include "core/io/image.h"
#include "core/io/resource_importer.h"
class ResourceImporterTextureAtlas : public ResourceImporter {
GDCLASS(ResourceImporterTextureAtlas, ResourceImporter);
struct PackData {
Rect2 region;
- bool is_mesh;
+ bool is_mesh = false;
Vector<int> chart_pieces; //one for region, many for mesh
Vector<Vector<Vector2>> chart_vertices; //for mesh
Ref<Image> image;
diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp
index cb669b4c89..2db1db9e51 100644
--- a/editor/import/resource_importer_wav.cpp
+++ b/editor/import/resource_importer_wav.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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/resource_importer_wav.h b/editor/import/resource_importer_wav.h
index 3c4a8757eb..7413dbd11c 100644
--- a/editor/import/resource_importer_wav.h
+++ b/editor/import/resource_importer_wav.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp
new file mode 100644
index 0000000000..19a8f209bb
--- /dev/null
+++ b/editor/import/scene_import_settings.cpp
@@ -0,0 +1,1199 @@
+/*************************************************************************/
+/* scene_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 "scene_import_settings.h"
+#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
+#include "editor/import/scene_importer_mesh_node_3d.h"
+#include "scene/resources/surface_tool.h"
+
+class SceneImportSettingsData : public Object {
+ GDCLASS(SceneImportSettingsData, Object)
+ friend class SceneImportSettings;
+ Map<StringName, Variant> *settings = nullptr;
+ Map<StringName, Variant> current;
+ Map<StringName, Variant> defaults;
+ List<ResourceImporter::ImportOption> options;
+
+ ResourceImporterScene::InternalImportCategory category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX;
+
+ bool _set(const StringName &p_name, const Variant &p_value) {
+ if (settings) {
+ if (defaults.has(p_name) && defaults[p_name] == p_value) {
+ settings->erase(p_name);
+ } else {
+ (*settings)[p_name] = p_value;
+ }
+
+ current[p_name] = p_value;
+ return true;
+ }
+ return false;
+ }
+ bool _get(const StringName &p_name, Variant &r_ret) const {
+ if (settings) {
+ 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 ResourceImporter::ImportOption &E : options) {
+ if (ResourceImporterScene::get_singleton()->get_internal_option_visibility(category, E.option.name, current)) {
+ p_list->push_back(E.option);
+ }
+ }
+ }
+};
+
+void SceneImportSettings::_fill_material(Tree *p_tree, const Ref<Material> &p_material, TreeItem *p_parent) {
+ String import_id;
+ bool has_import_id = false;
+
+ if (p_material->has_meta("import_id")) {
+ import_id = p_material->get_meta("import_id");
+ has_import_id = true;
+ } else if (p_material->get_name() != "") {
+ import_id = p_material->get_name();
+ has_import_id = true;
+ } else {
+ import_id = "@MATERIAL:" + itos(material_set.size());
+ }
+
+ if (!material_map.has(import_id)) {
+ MaterialData md;
+ md.has_import_id = has_import_id;
+ md.material = p_material;
+
+ _load_default_subresource_settings(md.settings, "materials", import_id, ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MATERIAL);
+
+ material_map[import_id] = md;
+ }
+
+ MaterialData &material_data = material_map[import_id];
+
+ 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());
+ item->set_icon(0, icon);
+
+ bool created = false;
+ if (!material_set.has(p_material)) {
+ material_set.insert(p_material);
+ created = true;
+ }
+
+ item->set_meta("type", "Material");
+ item->set_meta("import_id", import_id);
+ item->set_tooltip(0, vformat(TTR("Import ID: %s"), import_id));
+ item->set_selectable(0, true);
+
+ if (p_tree == scene_tree) {
+ material_data.scene_node = item;
+ } else if (p_tree == mesh_tree) {
+ material_data.mesh_node = item;
+ } else {
+ material_data.material_node = item;
+ }
+
+ if (created) {
+ _fill_material(material_tree, p_material, material_tree->get_root());
+ }
+}
+
+void SceneImportSettings::_fill_mesh(Tree *p_tree, const Ref<Mesh> &p_mesh, TreeItem *p_parent) {
+ String import_id;
+
+ bool has_import_id = false;
+ if (p_mesh->has_meta("import_id")) {
+ import_id = p_mesh->get_meta("import_id");
+ has_import_id = true;
+ } else if (p_mesh->get_name() != String()) {
+ import_id = p_mesh->get_name();
+ has_import_id = true;
+ } else {
+ import_id = "@MESH:" + itos(mesh_set.size());
+ }
+
+ if (!mesh_map.has(import_id)) {
+ MeshData md;
+ md.has_import_id = has_import_id;
+ md.mesh = p_mesh;
+
+ _load_default_subresource_settings(md.settings, "meshes", import_id, ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH);
+
+ mesh_map[import_id] = md;
+ }
+
+ MeshData &mesh_data = mesh_map[import_id];
+
+ 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());
+ item->set_icon(0, icon);
+
+ bool created = false;
+ if (!mesh_set.has(p_mesh)) {
+ mesh_set.insert(p_mesh);
+ created = true;
+ }
+
+ item->set_meta("type", "Mesh");
+ item->set_meta("import_id", import_id);
+ item->set_tooltip(0, vformat(TTR("Import ID: %s"), import_id));
+
+ item->set_selectable(0, true);
+
+ if (p_tree == scene_tree) {
+ mesh_data.scene_node = item;
+ } else {
+ mesh_data.mesh_node = item;
+ }
+
+ item->set_collapsed(true);
+
+ for (int i = 0; i < p_mesh->get_surface_count(); i++) {
+ Ref<Material> mat = p_mesh->surface_get_material(i);
+ if (mat.is_valid()) {
+ _fill_material(p_tree, mat, item);
+ }
+ }
+
+ if (created) {
+ _fill_mesh(mesh_tree, p_mesh, mesh_tree->get_root());
+ }
+}
+
+void SceneImportSettings::_fill_animation(Tree *p_tree, const Ref<Animation> &p_anim, const String &p_name, TreeItem *p_parent) {
+ if (!animation_map.has(p_name)) {
+ AnimationData ad;
+ ad.animation = p_anim;
+
+ _load_default_subresource_settings(ad.settings, "animations", p_name, ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION);
+
+ animation_map[p_name] = ad;
+ }
+
+ AnimationData &animation_data = animation_map[p_name];
+
+ Ref<Texture2D> icon = get_theme_icon(SNAME("Animation"), SNAME("EditorIcons"));
+
+ TreeItem *item = p_tree->create_item(p_parent);
+ item->set_text(0, p_name);
+ item->set_icon(0, icon);
+
+ item->set_meta("type", "Animation");
+ item->set_meta("import_id", p_name);
+
+ item->set_selectable(0, true);
+
+ animation_data.scene_node = item;
+}
+
+void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) {
+ String import_id;
+
+ if (p_node->has_meta("import_id")) {
+ import_id = p_node->get_meta("import_id");
+ } else {
+ import_id = "PATH:" + String(scene->get_path_to(p_node));
+ p_node->set_meta("import_id", import_id);
+ }
+
+ EditorSceneImporterMeshNode3D *src_mesh_node = Object::cast_to<EditorSceneImporterMeshNode3D>(p_node);
+
+ if (src_mesh_node) {
+ MeshInstance3D *mesh_node = memnew(MeshInstance3D);
+ mesh_node->set_name(src_mesh_node->get_name());
+ mesh_node->set_transform(src_mesh_node->get_transform());
+ mesh_node->set_skin(src_mesh_node->get_skin());
+ mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path());
+ if (src_mesh_node->get_mesh().is_valid()) {
+ Ref<EditorSceneImporterMesh> editor_mesh = src_mesh_node->get_mesh();
+ mesh_node->set_mesh(editor_mesh->get_mesh());
+ }
+
+ p_node->replace_by(mesh_node);
+ memdelete(p_node);
+ p_node = mesh_node;
+ }
+
+ String type = p_node->get_class();
+
+ if (!has_theme_icon(type, SNAME("EditorIcons"))) {
+ type = "Node3D";
+ }
+
+ 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(SNAME("PackedScene"), SNAME("EditorIcons"));
+ item->set_text(0, "Scene");
+ }
+
+ item->set_icon(0, icon);
+
+ item->set_meta("type", "Node");
+ item->set_meta("class", type);
+ item->set_meta("import_id", import_id);
+ item->set_tooltip(0, vformat(TTR("Type: %s\nImport ID: %s"), type, import_id));
+
+ item->set_selectable(0, true);
+
+ if (!node_map.has(import_id)) {
+ NodeData nd;
+
+ if (p_node != scene) {
+ ResourceImporterScene::InternalImportCategory category;
+ if (src_mesh_node) {
+ category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE;
+ } else if (Object::cast_to<AnimationPlayer>(p_node)) {
+ category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE;
+ } else {
+ category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
+ }
+
+ _load_default_subresource_settings(nd.settings, "nodes", import_id, category);
+ }
+
+ node_map[import_id] = nd;
+ }
+ NodeData &node_data = node_map[import_id];
+
+ node_data.node = p_node;
+ node_data.scene_node = item;
+
+ AnimationPlayer *anim_node = Object::cast_to<AnimationPlayer>(p_node);
+ if (anim_node) {
+ List<StringName> animations;
+ anim_node->get_animation_list(&animations);
+ for (const StringName &E : animations) {
+ _fill_animation(scene_tree, anim_node->get_animation(E), E, item);
+ }
+ }
+
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ _fill_scene(p_node->get_child(i), item);
+ }
+ MeshInstance3D *mesh_node = Object::cast_to<MeshInstance3D>(p_node);
+ if (mesh_node && mesh_node->get_mesh().is_valid()) {
+ _fill_mesh(scene_tree, mesh_node->get_mesh(), item);
+
+ Transform3D accum_xform;
+ Node3D *base = mesh_node;
+ while (base) {
+ accum_xform = base->get_transform() * accum_xform;
+ base = Object::cast_to<Node3D>(base->get_parent());
+ }
+
+ AABB aabb = accum_xform.xform(mesh_node->get_mesh()->get_aabb());
+ if (first_aabb) {
+ contents_aabb = aabb;
+ first_aabb = false;
+ } else {
+ contents_aabb.merge_with(aabb);
+ }
+ }
+}
+
+void SceneImportSettings::_update_scene() {
+ scene_tree->clear();
+ material_tree->clear();
+ mesh_tree->clear();
+
+ //hidden roots
+ material_tree->create_item();
+ mesh_tree->create_item();
+
+ _fill_scene(scene, nullptr);
+}
+
+void SceneImportSettings::_update_camera() {
+ AABB camera_aabb;
+
+ float rot_x = cam_rot_x;
+ float rot_y = cam_rot_y;
+ float zoom = cam_zoom;
+
+ if (selected_type == "Node" || selected_type == "") {
+ camera_aabb = contents_aabb;
+ } else {
+ if (mesh_preview->get_mesh().is_valid()) {
+ camera_aabb = mesh_preview->get_transform().xform(mesh_preview->get_mesh()->get_aabb());
+ } else {
+ camera_aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2));
+ }
+ if (selected_type == "Mesh" && mesh_map.has(selected_id)) {
+ const MeshData &md = mesh_map[selected_id];
+ rot_x = md.cam_rot_x;
+ rot_y = md.cam_rot_y;
+ zoom = md.cam_zoom;
+ } else if (selected_type == "Material" && material_map.has(selected_id)) {
+ const MaterialData &md = material_map[selected_id];
+ rot_x = md.cam_rot_x;
+ rot_y = md.cam_rot_y;
+ zoom = md.cam_zoom;
+ }
+ }
+
+ Vector3 center = camera_aabb.position + camera_aabb.size * 0.5;
+ float camera_size = camera_aabb.get_longest_axis_size();
+
+ camera->set_orthogonal(camera_size * zoom, 0.0001, camera_size * 2);
+
+ 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);
+
+ camera->set_transform(xf);
+}
+
+void SceneImportSettings::_load_default_subresource_settings(Map<StringName, Variant> &settings, const String &p_type, const String &p_import_id, ResourceImporterScene::InternalImportCategory p_category) {
+ if (base_subresource_settings.has(p_type)) {
+ Dictionary d = base_subresource_settings[p_type];
+ if (d.has(p_import_id)) {
+ d = d[p_import_id];
+ List<ResourceImporterScene::ImportOption> options;
+ ResourceImporterScene::get_singleton()->get_internal_import_options(p_category, &options);
+ for (const ResourceImporterScene::ImportOption &E : options) {
+ String key = E.option.name;
+ if (d.has(key)) {
+ settings[key] = d[key];
+ }
+ }
+ }
+ }
+}
+
+void SceneImportSettings::open_settings(const String &p_path) {
+ if (scene) {
+ memdelete(scene);
+ scene = nullptr;
+ }
+ scene = ResourceImporterScene::get_singleton()->pre_import(p_path);
+ if (scene == nullptr) {
+ EditorNode::get_singleton()->show_warning(TTR("Error opening scene"));
+ return;
+ }
+
+ base_path = p_path;
+
+ material_set.clear();
+ mesh_set.clear();
+ material_map.clear();
+ mesh_map.clear();
+ node_map.clear();
+ defaults.clear();
+
+ selected_id = "";
+ selected_type = "";
+
+ cam_rot_x = -Math_PI / 4;
+ cam_rot_y = -Math_PI / 4;
+ cam_zoom = 1;
+
+ {
+ base_subresource_settings.clear();
+
+ Ref<ConfigFile> config;
+ config.instantiate();
+ Error err = config->load(p_path + ".import");
+ if (err == OK) {
+ List<String> keys;
+ config->get_section_keys("params", &keys);
+ for (const String &E : keys) {
+ Variant value = config->get_value("params", E);
+ if (E == "_subresources") {
+ base_subresource_settings = value;
+ } else {
+ defaults[E] = value;
+ }
+ }
+ }
+ }
+
+ first_aabb = true;
+
+ _update_scene();
+
+ base_viewport->add_child(scene);
+
+ if (first_aabb) {
+ contents_aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2));
+ first_aabb = false;
+ }
+
+ popup_centered_ratio();
+ _update_camera();
+
+ set_title(vformat(TTR("Advanced Import Settings for '%s'"), base_path.get_file()));
+}
+
+SceneImportSettings *SceneImportSettings::singleton = nullptr;
+
+SceneImportSettings *SceneImportSettings::get_singleton() {
+ return singleton;
+}
+
+void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {
+ selecting = true;
+
+ if (p_type == "Node") {
+ node_selected->hide(); //always hide just in case
+ mesh_preview->hide();
+ if (Object::cast_to<Node3D>(scene)) {
+ Object::cast_to<Node3D>(scene)->show();
+ }
+ //NodeData &nd=node_map[p_id];
+ material_tree->deselect_all();
+ mesh_tree->deselect_all();
+ NodeData &nd = node_map[p_id];
+
+ MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(nd.node);
+ if (mi) {
+ Ref<Mesh> base_mesh = mi->get_mesh();
+ if (base_mesh.is_valid()) {
+ AABB aabb = base_mesh->get_aabb();
+ Transform3D aabb_xf;
+ aabb_xf.basis.scale(aabb.size);
+ aabb_xf.origin = aabb.position;
+
+ aabb_xf = mi->get_global_transform() * aabb_xf;
+ node_selected->set_transform(aabb_xf);
+ node_selected->show();
+ }
+ }
+
+ if (nd.node == scene) {
+ scene_import_settings_data->settings = &defaults;
+ scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX;
+ } else {
+ scene_import_settings_data->settings = &nd.settings;
+ if (mi) {
+ scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE;
+ } else if (Object::cast_to<AnimationPlayer>(nd.node)) {
+ scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE;
+ } else {
+ scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;
+ }
+ }
+ } else if (p_type == "Animation") {
+ node_selected->hide(); //always hide just in case
+ mesh_preview->hide();
+ if (Object::cast_to<Node3D>(scene)) {
+ Object::cast_to<Node3D>(scene)->show();
+ }
+ //NodeData &nd=node_map[p_id];
+ material_tree->deselect_all();
+ mesh_tree->deselect_all();
+ AnimationData &ad = animation_map[p_id];
+
+ scene_import_settings_data->settings = &ad.settings;
+ scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION;
+ } else if (p_type == "Mesh") {
+ node_selected->hide();
+ if (Object::cast_to<Node3D>(scene)) {
+ Object::cast_to<Node3D>(scene)->hide();
+ }
+
+ MeshData &md = mesh_map[p_id];
+ if (p_from != mesh_tree) {
+ md.mesh_node->uncollapse_tree();
+ md.mesh_node->select(0);
+ mesh_tree->ensure_cursor_is_visible();
+ }
+ if (p_from != scene_tree) {
+ md.scene_node->uncollapse_tree();
+ md.scene_node->select(0);
+ scene_tree->ensure_cursor_is_visible();
+ }
+
+ mesh_preview->set_mesh(md.mesh);
+ mesh_preview->show();
+
+ material_tree->deselect_all();
+
+ scene_import_settings_data->settings = &md.settings;
+ scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH;
+ } else if (p_type == "Material") {
+ node_selected->hide();
+ if (Object::cast_to<Node3D>(scene)) {
+ Object::cast_to<Node3D>(scene)->hide();
+ }
+
+ mesh_preview->show();
+
+ MaterialData &md = material_map[p_id];
+
+ material_preview->set_material(md.material);
+ mesh_preview->set_mesh(material_preview);
+
+ if (p_from != mesh_tree) {
+ md.mesh_node->uncollapse_tree();
+ md.mesh_node->select(0);
+ mesh_tree->ensure_cursor_is_visible();
+ }
+ if (p_from != scene_tree) {
+ md.scene_node->uncollapse_tree();
+ md.scene_node->select(0);
+ scene_tree->ensure_cursor_is_visible();
+ }
+ if (p_from != material_tree) {
+ md.material_node->uncollapse_tree();
+ md.material_node->select(0);
+ material_tree->ensure_cursor_is_visible();
+ }
+
+ scene_import_settings_data->settings = &md.settings;
+ scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MATERIAL;
+ }
+
+ selected_type = p_type;
+ selected_id = p_id;
+
+ selecting = false;
+
+ _update_camera();
+
+ List<ResourceImporter::ImportOption> options;
+
+ if (scene_import_settings_data->category == ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MAX) {
+ ResourceImporterScene::get_singleton()->get_import_options(&options);
+ } else {
+ ResourceImporterScene::get_singleton()->get_internal_import_options(scene_import_settings_data->category, &options);
+ }
+
+ scene_import_settings_data->defaults.clear();
+ scene_import_settings_data->current.clear();
+
+ 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.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.option.name] = E.default_value;
+ }
+ }
+ scene_import_settings_data->options = options;
+ inspector->edit(scene_import_settings_data);
+ scene_import_settings_data->notify_property_list_changed();
+}
+
+void SceneImportSettings::_material_tree_selected() {
+ if (selecting) {
+ return;
+ }
+ TreeItem *item = material_tree->get_selected();
+ String type = item->get_meta("type");
+ String import_id = item->get_meta("import_id");
+
+ _select(material_tree, type, import_id);
+}
+void SceneImportSettings::_mesh_tree_selected() {
+ if (selecting) {
+ return;
+ }
+
+ TreeItem *item = mesh_tree->get_selected();
+ String type = item->get_meta("type");
+ String import_id = item->get_meta("import_id");
+
+ _select(mesh_tree, type, import_id);
+}
+void SceneImportSettings::_scene_tree_selected() {
+ if (selecting) {
+ return;
+ }
+ TreeItem *item = scene_tree->get_selected();
+ String type = item->get_meta("type");
+ String import_id = item->get_meta("import_id");
+
+ _select(scene_tree, type, import_id);
+}
+
+void SceneImportSettings::_viewport_input(const Ref<InputEvent> &p_input) {
+ float *rot_x = &cam_rot_x;
+ float *rot_y = &cam_rot_y;
+ float *zoom = &cam_zoom;
+
+ if (selected_type == "Mesh" && mesh_map.has(selected_id)) {
+ MeshData &md = mesh_map[selected_id];
+ rot_x = &md.cam_rot_x;
+ rot_y = &md.cam_rot_y;
+ zoom = &md.cam_zoom;
+ } else if (selected_type == "Material" && material_map.has(selected_id)) {
+ MaterialData &md = material_map[selected_id];
+ rot_x = &md.cam_rot_x;
+ rot_y = &md.cam_rot_y;
+ zoom = &md.cam_zoom;
+ }
+ Ref<InputEventMouseMotion> mm = p_input;
+ if (mm.is_valid() && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
+ (*rot_x) -= mm->get_relative().y * 0.01 * EDSCALE;
+ (*rot_y) -= mm->get_relative().x * 0.01 * EDSCALE;
+ (*rot_x) = CLAMP((*rot_x), -Math_PI / 2, Math_PI / 2);
+ _update_camera();
+ }
+ Ref<InputEventMouseButton> mb = p_input;
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
+ (*zoom) *= 1.1;
+ if ((*zoom) > 10.0) {
+ (*zoom) = 10.0;
+ }
+ _update_camera();
+ }
+ if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
+ (*zoom) /= 1.1;
+ if ((*zoom) < 0.1) {
+ (*zoom) = 0.1;
+ }
+ _update_camera();
+ }
+}
+
+void SceneImportSettings::_re_import() {
+ Map<StringName, Variant> main_settings;
+
+ main_settings = defaults;
+ main_settings.erase("_subresources");
+ Dictionary nodes;
+ Dictionary materials;
+ Dictionary meshes;
+ Dictionary animations;
+
+ Dictionary subresources;
+
+ for (Map<String, NodeData>::Element *E = node_map.front(); E; E = E->next()) {
+ if (E->get().settings.size()) {
+ Dictionary d;
+ for (Map<StringName, Variant>::Element *F = E->get().settings.front(); F; F = F->next()) {
+ d[String(F->key())] = F->get();
+ }
+ nodes[E->key()] = d;
+ }
+ }
+ if (nodes.size()) {
+ subresources["nodes"] = nodes;
+ }
+
+ for (Map<String, MaterialData>::Element *E = material_map.front(); E; E = E->next()) {
+ if (E->get().settings.size()) {
+ Dictionary d;
+ for (Map<StringName, Variant>::Element *F = E->get().settings.front(); F; F = F->next()) {
+ d[String(F->key())] = F->get();
+ }
+ materials[E->key()] = d;
+ }
+ }
+ if (materials.size()) {
+ subresources["materials"] = materials;
+ }
+
+ for (Map<String, MeshData>::Element *E = mesh_map.front(); E; E = E->next()) {
+ if (E->get().settings.size()) {
+ Dictionary d;
+ for (Map<StringName, Variant>::Element *F = E->get().settings.front(); F; F = F->next()) {
+ d[String(F->key())] = F->get();
+ }
+ meshes[E->key()] = d;
+ }
+ }
+ if (meshes.size()) {
+ subresources["meshes"] = meshes;
+ }
+
+ for (Map<String, AnimationData>::Element *E = animation_map.front(); E; E = E->next()) {
+ if (E->get().settings.size()) {
+ Dictionary d;
+ for (Map<StringName, Variant>::Element *F = E->get().settings.front(); F; F = F->next()) {
+ d[String(F->key())] = F->get();
+ }
+ animations[E->key()] = d;
+ }
+ }
+ if (animations.size()) {
+ subresources["animations"] = animations;
+ }
+
+ if (subresources.size()) {
+ main_settings["_subresources"] = subresources;
+ }
+
+ EditorFileSystem::get_singleton()->reimport_file_with_custom_parameters(base_path, "scene", main_settings);
+}
+
+void SceneImportSettings::_notification(int p_what) {
+ if (p_what == NOTIFICATION_READY) {
+ connect("confirmed", callable_mp(this, &SceneImportSettings::_re_import));
+ }
+}
+
+void SceneImportSettings::_menu_callback(int p_id) {
+ switch (p_id) {
+ case ACTION_EXTRACT_MATERIALS: {
+ save_path->set_text(TTR("Select folder to extract material resources"));
+ external_extension_type->select(0);
+ } break;
+ case ACTION_CHOOSE_MESH_SAVE_PATHS: {
+ save_path->set_text(TTR("Select folder where mesh resources will save on import"));
+ external_extension_type->select(1);
+ } break;
+ case ACTION_CHOOSE_ANIMATION_SAVE_PATHS: {
+ save_path->set_text(TTR("Select folder where animations will save on import"));
+ external_extension_type->select(1);
+ } break;
+ }
+
+ save_path->set_current_dir(base_path.get_base_dir());
+ current_action = p_id;
+ save_path->popup_centered_ratio();
+}
+
+void SceneImportSettings::_save_path_changed(const String &p_path) {
+ save_path_item->set_text(1, 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(SNAME("StatusWarning"), SNAME("EditorIcons")));
+
+ } else {
+ save_path_item->set_text(2, "Will create new File");
+ save_path_item->set_icon(2, get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")));
+ }
+}
+
+void SceneImportSettings::_browse_save_callback(Object *p_item, int p_column, int p_id) {
+ TreeItem *item = Object::cast_to<TreeItem>(p_item);
+
+ String path = item->get_text(1);
+
+ item_save_path->set_current_file(path);
+ save_path_item = item;
+
+ item_save_path->popup_centered_ratio();
+}
+
+void SceneImportSettings::_save_dir_callback(const String &p_path) {
+ external_path_tree->clear();
+ TreeItem *root = external_path_tree->create_item();
+ save_path_items.clear();
+
+ switch (current_action) {
+ case ACTION_EXTRACT_MATERIALS: {
+ for (Map<String, MaterialData>::Element *E = material_map.front(); E; E = E->next()) {
+ MaterialData &md = material_map[E->key()];
+
+ TreeItem *item = external_path_tree->create_item(root);
+
+ String name = md.material_node->get_text(0);
+
+ item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ item->set_icon(0, get_theme_icon(SNAME("StandardMaterial3D"), SNAME("EditorIcons")));
+ item->set_text(0, name);
+
+ if (md.has_import_id) {
+ if (md.settings.has("use_external/enabled") && bool(md.settings["use_external/enabled"])) {
+ item->set_text(2, "Already External");
+ item->set_tooltip(2, TTR("This material already references an external file, no action will be taken.\nDisable the external property for it to be extracted again."));
+ } else {
+ item->set_metadata(0, E->key());
+ item->set_editable(0, true);
+ item->set_checked(0, true);
+ String path = p_path.plus_file(name);
+ if (external_extension_type->get_selected() == 0) {
+ path += ".tres";
+ } else {
+ path += ".res";
+ }
+
+ item->set_text(1, 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(SNAME("StatusWarning"), SNAME("EditorIcons")));
+
+ } else {
+ item->set_text(2, "Will create new File");
+ item->set_icon(2, get_theme_icon(SNAME("StatusSuccess"), SNAME("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(SNAME("StatusError"), SNAME("EditorIcons")));
+ }
+
+ save_path_items.push_back(item);
+ }
+
+ external_paths->set_title(TTR("Extract Materials to Resource Files"));
+ external_paths->get_ok_button()->set_text(TTR("Extract"));
+ } break;
+ case ACTION_CHOOSE_MESH_SAVE_PATHS: {
+ for (Map<String, MeshData>::Element *E = mesh_map.front(); E; E = E->next()) {
+ MeshData &md = mesh_map[E->key()];
+
+ TreeItem *item = external_path_tree->create_item(root);
+
+ String name = md.mesh_node->get_text(0);
+
+ item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ item->set_icon(0, get_theme_icon(SNAME("Mesh"), SNAME("EditorIcons")));
+ item->set_text(0, name);
+
+ if (md.has_import_id) {
+ if (md.settings.has("save_to_file/enabled") && bool(md.settings["save_to_file/enabled"])) {
+ item->set_text(2, "Already Saving");
+ item->set_tooltip(2, TTR("This mesh already saves to an external resource, no action will be taken."));
+ } else {
+ item->set_metadata(0, E->key());
+ item->set_editable(0, true);
+ item->set_checked(0, true);
+ String path = p_path.plus_file(name);
+ if (external_extension_type->get_selected() == 0) {
+ path += ".tres";
+ } else {
+ path += ".res";
+ }
+
+ item->set_text(1, 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(SNAME("StatusWarning"), SNAME("EditorIcons")));
+
+ } else {
+ item->set_text(2, "Will save to new File");
+ item->set_icon(2, get_theme_icon(SNAME("StatusSuccess"), SNAME("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(SNAME("StatusError"), SNAME("EditorIcons")));
+ }
+
+ save_path_items.push_back(item);
+ }
+
+ external_paths->set_title(TTR("Set paths to save meshes as resource files on Reimport"));
+ external_paths->get_ok_button()->set_text(TTR("Set Paths"));
+ } break;
+ case ACTION_CHOOSE_ANIMATION_SAVE_PATHS: {
+ for (Map<String, AnimationData>::Element *E = animation_map.front(); E; E = E->next()) {
+ AnimationData &ad = animation_map[E->key()];
+
+ TreeItem *item = external_path_tree->create_item(root);
+
+ String name = ad.scene_node->get_text(0);
+
+ item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ 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"])) {
+ item->set_text(2, "Already Saving");
+ item->set_tooltip(2, TTR("This animation already saves to an external resource, no action will be taken."));
+ } else {
+ item->set_metadata(0, E->key());
+ item->set_editable(0, true);
+ item->set_checked(0, true);
+ String path = p_path.plus_file(name);
+ if (external_extension_type->get_selected() == 0) {
+ path += ".tres";
+ } else {
+ path += ".res";
+ }
+
+ item->set_text(1, 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(SNAME("StatusWarning"), SNAME("EditorIcons")));
+
+ } else {
+ item->set_text(2, "Will save to new File");
+ item->set_icon(2, get_theme_icon(SNAME("StatusSuccess"), SNAME("EditorIcons")));
+ }
+
+ item->add_button(1, get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")));
+ }
+
+ save_path_items.push_back(item);
+ }
+
+ external_paths->set_title(TTR("Set paths to save animations as resource files on Reimport"));
+ external_paths->get_ok_button()->set_text(TTR("Set Paths"));
+
+ } break;
+ }
+
+ external_paths->popup_centered_ratio();
+}
+
+void SceneImportSettings::_save_dir_confirm() {
+ for (int i = 0; i < save_path_items.size(); i++) {
+ TreeItem *item = save_path_items[i];
+ if (!item->is_checked(0)) {
+ continue; //ignore
+ }
+ String path = item->get_text(1);
+ if (!path.is_resource_file()) {
+ continue;
+ }
+
+ String id = item->get_metadata(0);
+
+ switch (current_action) {
+ case ACTION_EXTRACT_MATERIALS: {
+ ERR_CONTINUE(!material_map.has(id));
+ MaterialData &md = material_map[id];
+
+ Error err = ResourceSaver::save(path, md.material);
+ if (err != OK) {
+ EditorNode::get_singleton()->add_io_error(TTR("Can't make material external to file, write error:") + "\n\t" + path);
+ continue;
+ }
+
+ md.settings["use_external/enabled"] = true;
+ md.settings["use_external/path"] = path;
+
+ } break;
+ case ACTION_CHOOSE_MESH_SAVE_PATHS: {
+ ERR_CONTINUE(!mesh_map.has(id));
+ MeshData &md = mesh_map[id];
+
+ md.settings["save_to_file/enabled"] = true;
+ md.settings["save_to_file/path"] = path;
+ } break;
+ case ACTION_CHOOSE_ANIMATION_SAVE_PATHS: {
+ ERR_CONTINUE(!animation_map.has(id));
+ AnimationData &ad = animation_map[id];
+
+ ad.settings["save_to_file/enabled"] = true;
+ ad.settings["save_to_file/path"] = path;
+
+ } break;
+ }
+ }
+
+ if (current_action == ACTION_EXTRACT_MATERIALS) {
+ //as this happens right now, the scene needs to be saved and reimported.
+ _re_import();
+ open_settings(base_path);
+ } else {
+ scene_import_settings_data->notify_property_list_changed();
+ }
+}
+
+SceneImportSettings::SceneImportSettings() {
+ singleton = this;
+
+ VBoxContainer *main_vb = memnew(VBoxContainer);
+ add_child(main_vb);
+ HBoxContainer *menu_hb = memnew(HBoxContainer);
+ main_vb->add_child(menu_hb);
+
+ action_menu = memnew(MenuButton);
+ action_menu->set_text(TTR("Actions..."));
+ menu_hb->add_child(action_menu);
+
+ action_menu->get_popup()->add_item(TTR("Extract Materials"), ACTION_EXTRACT_MATERIALS);
+ action_menu->get_popup()->add_separator();
+ action_menu->get_popup()->add_item(TTR("Set Animation Save Paths"), ACTION_CHOOSE_ANIMATION_SAVE_PATHS);
+ action_menu->get_popup()->add_item(TTR("Set Mesh Save Paths"), ACTION_CHOOSE_MESH_SAVE_PATHS);
+
+ action_menu->get_popup()->connect("id_pressed", callable_mp(this, &SceneImportSettings::_menu_callback));
+
+ tree_split = memnew(HSplitContainer);
+ main_vb->add_child(tree_split);
+ tree_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+
+ data_mode = memnew(TabContainer);
+ tree_split->add_child(data_mode);
+ data_mode->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
+
+ property_split = memnew(HSplitContainer);
+ tree_split->add_child(property_split);
+ property_split->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ scene_tree = memnew(Tree);
+ scene_tree->set_name(TTR("Scene"));
+ data_mode->add_child(scene_tree);
+ scene_tree->connect("cell_selected", callable_mp(this, &SceneImportSettings::_scene_tree_selected));
+
+ mesh_tree = memnew(Tree);
+ mesh_tree->set_name(TTR("Meshes"));
+ data_mode->add_child(mesh_tree);
+ mesh_tree->set_hide_root(true);
+ mesh_tree->connect("cell_selected", callable_mp(this, &SceneImportSettings::_mesh_tree_selected));
+
+ material_tree = memnew(Tree);
+ material_tree->set_name(TTR("Materials"));
+ data_mode->add_child(material_tree);
+ material_tree->connect("cell_selected", callable_mp(this, &SceneImportSettings::_material_tree_selected));
+
+ material_tree->set_hide_root(true);
+
+ SubViewportContainer *vp_container = memnew(SubViewportContainer);
+ vp_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ vp_container->set_custom_minimum_size(Size2(10, 10));
+ vp_container->set_stretch(true);
+ vp_container->connect("gui_input", callable_mp(this, &SceneImportSettings::_viewport_input));
+ property_split->add_child(vp_container);
+
+ base_viewport = memnew(SubViewport);
+ vp_container->add_child(base_viewport);
+
+ base_viewport->set_use_own_world_3d(true);
+
+ camera = memnew(Camera3D);
+ base_viewport->add_child(camera);
+ camera->make_current();
+
+ light = memnew(DirectionalLight3D);
+ 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.instantiate();
+ selection_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ selection_mat->set_albedo(Color(1, 0.8, 1.0));
+
+ Ref<SurfaceTool> st;
+ st.instantiate();
+ st->begin(Mesh::PRIMITIVE_LINES);
+
+ AABB base_aabb;
+ base_aabb.size = Vector3(1, 1, 1);
+
+ for (int i = 0; i < 12; i++) {
+ Vector3 a, b;
+ base_aabb.get_edge(i, a, b);
+
+ st->add_vertex(a);
+ st->add_vertex(a.lerp(b, 0.2));
+ st->add_vertex(b);
+ st->add_vertex(b.lerp(a, 0.2));
+ }
+
+ selection_mesh.instantiate();
+ st->commit(selection_mesh);
+ selection_mesh->surface_set_material(0, selection_mat);
+
+ node_selected = memnew(MeshInstance3D);
+ node_selected->set_mesh(selection_mesh);
+ base_viewport->add_child(node_selected);
+ node_selected->hide();
+ }
+
+ {
+ mesh_preview = memnew(MeshInstance3D);
+ base_viewport->add_child(mesh_preview);
+ mesh_preview->hide();
+
+ material_preview.instantiate();
+ }
+
+ inspector = memnew(EditorInspector);
+ inspector->set_custom_minimum_size(Size2(300 * EDSCALE, 0));
+
+ property_split->add_child(inspector);
+
+ scene_import_settings_data = memnew(SceneImportSettingsData);
+
+ get_ok_button()->set_text(TTR("Reimport"));
+ get_cancel_button()->set_text(TTR("Close"));
+
+ external_paths = memnew(ConfirmationDialog);
+ add_child(external_paths);
+ external_path_tree = memnew(Tree);
+ external_paths->add_child(external_path_tree);
+ external_path_tree->connect("button_pressed", callable_mp(this, &SceneImportSettings::_browse_save_callback));
+ external_paths->connect("confirmed", callable_mp(this, &SceneImportSettings::_save_dir_confirm));
+ 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_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_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_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);
+ HBoxContainer *extension_hb = memnew(HBoxContainer);
+ save_path->get_vbox()->add_child(extension_hb);
+ extension_hb->add_spacer();
+ extension_hb->add_child(memnew(Label(TTR("Save Extension: "))));
+ external_extension_type = memnew(OptionButton);
+ extension_hb->add_child(external_extension_type);
+ external_extension_type->add_item(TTR("Text: *.tres"));
+ external_extension_type->add_item(TTR("Binary: *.res"));
+ external_path_tree->set_hide_root(true);
+ add_child(save_path);
+
+ item_save_path = memnew(EditorFileDialog);
+ item_save_path->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
+ item_save_path->add_filter("*.tres;Text Resource");
+ item_save_path->add_filter("*.res;Binary Resource");
+ add_child(item_save_path);
+ item_save_path->connect("file_selected", callable_mp(this, &SceneImportSettings::_save_path_changed));
+
+ save_path->connect("dir_selected", callable_mp(this, &SceneImportSettings::_save_dir_callback));
+}
+
+SceneImportSettings::~SceneImportSettings() {
+ memdelete(scene_import_settings_data);
+}
diff --git a/editor/import/scene_import_settings.h b/editor/import/scene_import_settings.h
new file mode 100644
index 0000000000..ddcf4a6d5d
--- /dev/null
+++ b/editor/import/scene_import_settings.h
@@ -0,0 +1,199 @@
+/*************************************************************************/
+/* scene_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 SCENEIMPORTSETTINGS_H
+#define SCENEIMPORTSETTINGS_H
+
+#include "editor/editor_file_dialog.h"
+#include "editor/editor_inspector.h"
+#include "editor/import/resource_importer_scene.h"
+#include "scene/3d/camera_3d.h"
+#include "scene/3d/light_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/gui/dialogs.h"
+#include "scene/gui/item_list.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/split_container.h"
+#include "scene/gui/subviewport_container.h"
+#include "scene/gui/tab_container.h"
+#include "scene/gui/tree.h"
+#include "scene/resources/primitive_meshes.h"
+
+class SceneImportSettingsData;
+
+class SceneImportSettings : public ConfirmationDialog {
+ GDCLASS(SceneImportSettings, ConfirmationDialog)
+
+ static SceneImportSettings *singleton;
+
+ enum Actions {
+ ACTION_EXTRACT_MATERIALS,
+ ACTION_CHOOSE_MESH_SAVE_PATHS,
+ ACTION_CHOOSE_ANIMATION_SAVE_PATHS,
+ };
+
+ Node *scene = nullptr;
+
+ HSplitContainer *tree_split;
+ HSplitContainer *property_split;
+ TabContainer *data_mode;
+ Tree *scene_tree;
+ Tree *mesh_tree;
+ Tree *material_tree;
+
+ EditorInspector *inspector;
+
+ SubViewport *base_viewport;
+
+ Camera3D *camera;
+ bool first_aabb = false;
+ AABB contents_aabb;
+
+ DirectionalLight3D *light;
+ Ref<ArrayMesh> selection_mesh;
+ MeshInstance3D *node_selected;
+
+ MeshInstance3D *mesh_preview;
+ Ref<SphereMesh> material_preview;
+
+ float cam_rot_x;
+ float cam_rot_y;
+ float cam_zoom;
+
+ void _update_scene();
+
+ struct MaterialData {
+ bool has_import_id;
+ Ref<Material> material;
+ TreeItem *scene_node;
+ TreeItem *mesh_node;
+ TreeItem *material_node;
+
+ float cam_rot_x = -Math_PI / 4;
+ float cam_rot_y = -Math_PI / 4;
+ float cam_zoom = 1;
+
+ Map<StringName, Variant> settings;
+ };
+ Map<String, MaterialData> material_map;
+
+ struct MeshData {
+ bool has_import_id;
+ Ref<Mesh> mesh;
+ TreeItem *scene_node;
+ TreeItem *mesh_node;
+
+ float cam_rot_x = -Math_PI / 4;
+ float cam_rot_y = -Math_PI / 4;
+ float cam_zoom = 1;
+ Map<StringName, Variant> settings;
+ };
+ Map<String, MeshData> mesh_map;
+
+ struct AnimationData {
+ Ref<Animation> animation;
+ TreeItem *scene_node;
+ Map<StringName, Variant> settings;
+ };
+ Map<String, AnimationData> animation_map;
+
+ struct NodeData {
+ Node *node;
+ TreeItem *scene_node;
+ Map<StringName, Variant> settings;
+ };
+ Map<String, NodeData> node_map;
+
+ void _fill_material(Tree *p_tree, const Ref<Material> &p_material, TreeItem *p_parent);
+ void _fill_mesh(Tree *p_tree, const Ref<Mesh> &p_mesh, TreeItem *p_parent);
+ void _fill_animation(Tree *p_tree, const Ref<Animation> &p_anim, const String &p_name, TreeItem *p_parent);
+ void _fill_scene(Node *p_node, TreeItem *p_parent_item);
+
+ Set<Ref<Mesh>> mesh_set;
+ Set<Ref<Material>> material_set;
+
+ String selected_type;
+ String selected_id;
+
+ bool selecting = false;
+
+ void _update_camera();
+ void _select(Tree *p_from, String p_type, String p_id);
+ void _material_tree_selected();
+ void _mesh_tree_selected();
+ void _scene_tree_selected();
+
+ void _viewport_input(const Ref<InputEvent> &p_input);
+
+ Map<StringName, Variant> defaults;
+
+ SceneImportSettingsData *scene_import_settings_data;
+
+ void _re_import();
+
+ String base_path;
+
+ MenuButton *action_menu;
+
+ ConfirmationDialog *external_paths;
+ Tree *external_path_tree;
+ EditorFileDialog *save_path;
+ OptionButton *external_extension_type;
+
+ EditorFileDialog *item_save_path;
+
+ void _menu_callback(int p_id);
+ void _save_dir_callback(const String &p_path);
+
+ int current_action;
+
+ Vector<TreeItem *> save_path_items;
+
+ TreeItem *save_path_item = nullptr;
+ void _save_path_changed(const String &p_path);
+ void _browse_save_callback(Object *p_item, int p_column, int p_id);
+ void _save_dir_confirm();
+
+ Dictionary base_subresource_settings;
+
+ void _load_default_subresource_settings(Map<StringName, Variant> &settings, const String &p_type, const String &p_import_id, ResourceImporterScene::InternalImportCategory p_category);
+
+protected:
+ void _notification(int p_what);
+
+public:
+ void open_settings(const String &p_path);
+ static SceneImportSettings *get_singleton();
+ SceneImportSettings();
+ ~SceneImportSettings();
+};
+
+#endif // SCENEIMPORTSETTINGS_H
diff --git a/editor/import/scene_importer_mesh.cpp b/editor/import/scene_importer_mesh.cpp
new file mode 100644
index 0000000000..06f373c54f
--- /dev/null
+++ b/editor/import/scene_importer_mesh.cpp
@@ -0,0 +1,861 @@
+/*************************************************************************/
+/* scene_importer_mesh.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 "scene_importer_mesh.h"
+
+#include "core/math/math_defs.h"
+#include "scene/resources/surface_tool.h"
+
+void EditorSceneImporterMesh::add_blend_shape(const String &p_name) {
+ ERR_FAIL_COND(surfaces.size() > 0);
+ blend_shapes.push_back(p_name);
+}
+
+int EditorSceneImporterMesh::get_blend_shape_count() const {
+ return blend_shapes.size();
+}
+
+String EditorSceneImporterMesh::get_blend_shape_name(int p_blend_shape) const {
+ ERR_FAIL_INDEX_V(p_blend_shape, blend_shapes.size(), String());
+ return blend_shapes[p_blend_shape];
+}
+
+void EditorSceneImporterMesh::set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode) {
+ blend_shape_mode = p_blend_shape_mode;
+}
+
+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) {
+ 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;
+
+ Vector<Vector3> vertex_array = p_arrays[Mesh::ARRAY_VERTEX];
+ int vertex_count = vertex_array.size();
+ ERR_FAIL_COND(vertex_count == 0);
+
+ for (int i = 0; i < blend_shapes.size(); i++) {
+ Array bsdata = p_blend_shapes[i];
+ ERR_FAIL_COND(bsdata.size() != Mesh::ARRAY_MAX);
+ Vector<Vector3> vertex_data = bsdata[Mesh::ARRAY_VERTEX];
+ ERR_FAIL_COND(vertex_data.size() != vertex_count);
+ Surface::BlendShape bs;
+ bs.arrays = bsdata;
+ s.blend_shape_data.push_back(bs);
+ }
+
+ List<Variant> lods;
+ p_lods.get_key_list(&lods);
+ for (const Variant &E : lods) {
+ ERR_CONTINUE(!E.is_num());
+ Surface::LOD lod;
+ lod.distance = E;
+ lod.indices = p_lods[E];
+ ERR_CONTINUE(lod.indices.size() == 0);
+ s.lods.push_back(lod);
+ }
+
+ s.material = p_material;
+
+ surfaces.push_back(s);
+ mesh.unref();
+}
+
+int EditorSceneImporterMesh::get_surface_count() const {
+ return surfaces.size();
+}
+
+Mesh::PrimitiveType EditorSceneImporterMesh::get_surface_primitive_type(int p_surface) {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Mesh::PRIMITIVE_MAX);
+ return surfaces[p_surface].primitive;
+}
+Array EditorSceneImporterMesh::get_surface_arrays(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Array());
+ return surfaces[p_surface].arrays;
+}
+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());
+ return surfaces[p_surface].blend_shape_data[p_blend_shape].arrays;
+}
+int EditorSceneImporterMesh::get_surface_lod_count(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0);
+ return surfaces[p_surface].lods.size();
+}
+Vector<int> EditorSceneImporterMesh::get_surface_lod_indices(int p_surface, int p_lod) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Vector<int>());
+ ERR_FAIL_INDEX_V(p_lod, surfaces[p_surface].lods.size(), Vector<int>());
+
+ return surfaces[p_surface].lods[p_lod].indices;
+}
+
+float EditorSceneImporterMesh::get_surface_lod_size(int p_surface, int p_lod) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0);
+ ERR_FAIL_INDEX_V(p_lod, surfaces[p_surface].lods.size(), 0);
+ return surfaces[p_surface].lods[p_lod].distance;
+}
+
+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;
+}
+
+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) {
+ Vector3 x = p_x_raw.normalized();
+ Vector3 z = x.cross(p_y_raw);
+ z = z.normalized();
+ Vector3 y = z.cross(x);
+ Basis basis;
+ basis.set_axis(Vector3::AXIS_X, x);
+ basis.set_axis(Vector3::AXIS_Y, y);
+ basis.set_axis(Vector3::AXIS_Z, z);
+ return basis;
+}
+
+void EditorSceneImporterMesh::generate_lods() {
+ if (!SurfaceTool::simplify_func) {
+ return;
+ }
+ if (!SurfaceTool::simplify_scale_func) {
+ return;
+ }
+ if (!SurfaceTool::simplify_sloppy_func) {
+ return;
+ }
+ if (!SurfaceTool::simplify_with_attrib_func) {
+ return;
+ }
+
+ for (int i = 0; i < surfaces.size(); i++) {
+ if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) {
+ continue;
+ }
+
+ surfaces.write[i].lods.clear();
+ Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
+ Vector<int> indices = surfaces[i].arrays[RS::ARRAY_INDEX];
+ if (indices.size() == 0) {
+ continue; //no lods if no indices
+ }
+ Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL];
+ uint32_t vertex_count = vertices.size();
+ const Vector3 *vertices_ptr = vertices.ptr();
+ Vector<float> attributes;
+ Vector<float> normal_weights;
+ int32_t attribute_count = 6;
+ if (normals.size()) {
+ attributes.resize(normals.size() * attribute_count);
+ for (int32_t normal_i = 0; normal_i < normals.size(); normal_i++) {
+ Basis basis;
+ basis.set_euler(normals[normal_i]);
+ Vector3 basis_x = basis.get_axis(0);
+ Vector3 basis_y = basis.get_axis(1);
+ basis = compute_rotation_matrix_from_ortho_6d(basis_x, basis_y);
+ basis_x = basis.get_axis(0);
+ basis_y = basis.get_axis(1);
+ attributes.write[normal_i * attribute_count + 0] = basis_x.x;
+ attributes.write[normal_i * attribute_count + 1] = basis_x.y;
+ attributes.write[normal_i * attribute_count + 2] = basis_x.z;
+ attributes.write[normal_i * attribute_count + 3] = basis_y.x;
+ attributes.write[normal_i * attribute_count + 4] = basis_y.y;
+ attributes.write[normal_i * attribute_count + 5] = basis_y.z;
+ }
+ normal_weights.resize(vertex_count);
+ for (int32_t weight_i = 0; weight_i < normal_weights.size(); weight_i++) {
+ normal_weights.write[weight_i] = 1.0;
+ }
+ } else {
+ attribute_count = 0;
+ }
+ const int min_indices = 10;
+ const float error_tolerance = 1.44224'95703; // Cube root of 3
+ const float threshold = 1.0 / error_tolerance;
+ int index_target = indices.size() * threshold;
+ float max_mesh_error_percentage = 1e0f;
+ float mesh_error = 0.0f;
+ float scale = SurfaceTool::simplify_scale_func((const float *)vertices_ptr, vertex_count, sizeof(Vector3));
+ while (index_target > min_indices) {
+ Vector<int> new_indices;
+ new_indices.resize(indices.size());
+ size_t new_len = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const unsigned int *)indices.ptr(), indices.size(), (const float *)vertices_ptr, vertex_count, sizeof(Vector3), index_target, max_mesh_error_percentage, &mesh_error, (float *)attributes.ptrw(), normal_weights.ptrw(), attribute_count);
+ if ((int)new_len > (index_target * error_tolerance)) {
+ break;
+ }
+ Surface::LOD lod;
+ lod.distance = mesh_error * scale;
+ if (Math::is_zero_approx(mesh_error)) {
+ break;
+ }
+ if (new_len <= 0) {
+ break;
+ }
+ new_indices.resize(new_len);
+ lod.indices = new_indices;
+ print_line("Lod " + itos(surfaces.write[i].lods.size()) + " begin with " + itos(indices.size() / 3) + " triangles and shoot for " + itos(index_target / 3) + " triangles. Got " + itos(new_len / 3) + " triangles. Lod screen ratio " + rtos(lod.distance));
+ surfaces.write[i].lods.push_back(lod);
+ index_target *= threshold;
+ }
+ }
+}
+
+bool EditorSceneImporterMesh::has_mesh() const {
+ return mesh.is_valid();
+}
+
+Ref<ArrayMesh> EditorSceneImporterMesh::get_mesh(const Ref<ArrayMesh> &p_base) {
+ ERR_FAIL_COND_V(surfaces.size() == 0, Ref<ArrayMesh>());
+
+ if (mesh.is_null()) {
+ if (p_base.is_valid()) {
+ mesh = p_base;
+ }
+ if (mesh.is_null()) {
+ mesh.instantiate();
+ }
+ mesh->set_name(get_name());
+ if (has_meta("import_id")) {
+ mesh->set_meta("import_id", get_meta("import_id"));
+ }
+ for (int i = 0; i < blend_shapes.size(); i++) {
+ mesh->add_blend_shape(blend_shapes[i]);
+ }
+ mesh->set_blend_shape_mode(blend_shape_mode);
+ for (int i = 0; i < surfaces.size(); i++) {
+ Array bs_data;
+ if (surfaces[i].blend_shape_data.size()) {
+ for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) {
+ bs_data.push_back(surfaces[i].blend_shape_data[j].arrays);
+ }
+ }
+ Dictionary lods;
+ if (surfaces[i].lods.size()) {
+ for (int j = 0; j < surfaces[i].lods.size(); j++) {
+ lods[surfaces[i].lods[j].distance] = surfaces[i].lods[j].indices;
+ }
+ }
+
+ mesh->add_surface_from_arrays(surfaces[i].primitive, surfaces[i].arrays, bs_data, lods);
+ if (surfaces[i].material.is_valid()) {
+ mesh->surface_set_material(mesh->get_surface_count() - 1, surfaces[i].material);
+ }
+ if (surfaces[i].name != String()) {
+ mesh->surface_set_name(mesh->get_surface_count() - 1, surfaces[i].name);
+ }
+ }
+
+ mesh->set_lightmap_size_hint(lightmap_size_hint);
+
+ if (shadow_mesh.is_valid()) {
+ Ref<ArrayMesh> shadow = shadow_mesh->get_mesh();
+ mesh->set_shadow_mesh(shadow);
+ }
+ }
+
+ return mesh;
+}
+
+void EditorSceneImporterMesh::clear() {
+ surfaces.clear();
+ blend_shapes.clear();
+ mesh.unref();
+}
+
+void EditorSceneImporterMesh::create_shadow_mesh() {
+ if (shadow_mesh.is_valid()) {
+ shadow_mesh.unref();
+ }
+
+ //no shadow mesh for blendshapes
+ if (blend_shapes.size() > 0) {
+ return;
+ }
+ //no shadow mesh for skeletons
+ for (int i = 0; i < surfaces.size(); i++) {
+ if (surfaces[i].arrays[RS::ARRAY_BONES].get_type() != Variant::NIL) {
+ return;
+ }
+ if (surfaces[i].arrays[RS::ARRAY_WEIGHTS].get_type() != Variant::NIL) {
+ return;
+ }
+ }
+
+ shadow_mesh.instantiate();
+
+ for (int i = 0; i < surfaces.size(); i++) {
+ LocalVector<int> vertex_remap;
+ Vector<Vector3> new_vertices;
+ Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
+ int vertex_count = vertices.size();
+ {
+ Map<Vector3, int> unique_vertices;
+ const Vector3 *vptr = vertices.ptr();
+ for (int j = 0; j < vertex_count; j++) {
+ Vector3 v = vptr[j];
+
+ Map<Vector3, int>::Element *E = unique_vertices.find(v);
+
+ if (E) {
+ vertex_remap.push_back(E->get());
+ } else {
+ int vcount = unique_vertices.size();
+ unique_vertices[v] = vcount;
+ vertex_remap.push_back(vcount);
+ new_vertices.push_back(v);
+ }
+ }
+ }
+
+ Array new_surface;
+ new_surface.resize(RS::ARRAY_MAX);
+ Dictionary lods;
+
+ // print_line("original vertex count: " + itos(vertices.size()) + " new vertex count: " + itos(new_vertices.size()));
+
+ new_surface[RS::ARRAY_VERTEX] = new_vertices;
+
+ Vector<int> indices = surfaces[i].arrays[RS::ARRAY_INDEX];
+ if (indices.size()) {
+ int index_count = indices.size();
+ const int *index_rptr = indices.ptr();
+ Vector<int> new_indices;
+ new_indices.resize(indices.size());
+ int *index_wptr = new_indices.ptrw();
+
+ for (int j = 0; j < index_count; j++) {
+ int index = index_rptr[j];
+ ERR_FAIL_INDEX(index, vertex_count);
+ index_wptr[j] = vertex_remap[index];
+ }
+
+ new_surface[RS::ARRAY_INDEX] = new_indices;
+
+ // Make sure the same LODs as the full version are used.
+ // This makes it more coherent between rendered model and its shadows.
+ for (int j = 0; j < surfaces[i].lods.size(); j++) {
+ indices = surfaces[i].lods[j].indices;
+
+ index_count = indices.size();
+ index_rptr = indices.ptr();
+ new_indices.resize(indices.size());
+ index_wptr = new_indices.ptrw();
+
+ for (int k = 0; k < index_count; k++) {
+ int index = index_rptr[j];
+ ERR_FAIL_INDEX(index, vertex_count);
+ index_wptr[j] = vertex_remap[index];
+ }
+
+ lods[surfaces[i].lods[j].distance] = new_indices;
+ }
+ }
+
+ shadow_mesh->add_surface(surfaces[i].primitive, new_surface, Array(), lods, Ref<Material>(), surfaces[i].name);
+ }
+}
+
+Ref<EditorSceneImporterMesh> EditorSceneImporterMesh::get_shadow_mesh() const {
+ return shadow_mesh;
+}
+
+void EditorSceneImporterMesh::_set_data(const Dictionary &p_data) {
+ clear();
+ if (p_data.has("blend_shape_names")) {
+ blend_shapes = p_data["blend_shape_names"];
+ }
+ if (p_data.has("surfaces")) {
+ Array surface_arr = p_data["surfaces"];
+ for (int i = 0; i < surface_arr.size(); i++) {
+ Dictionary s = surface_arr[i];
+ ERR_CONTINUE(!s.has("primitive"));
+ ERR_CONTINUE(!s.has("arrays"));
+ Mesh::PrimitiveType prim = Mesh::PrimitiveType(int(s["primitive"]));
+ ERR_CONTINUE(prim >= Mesh::PRIMITIVE_MAX);
+ Array arr = s["arrays"];
+ Dictionary lods;
+ String name;
+ if (s.has("name")) {
+ name = s["name"];
+ }
+ if (s.has("lods")) {
+ lods = s["lods"];
+ }
+ Array blend_shapes;
+ if (s.has("blend_shapes")) {
+ blend_shapes = s["blend_shapes"];
+ }
+ Ref<Material> material;
+ if (s.has("material")) {
+ material = s["material"];
+ }
+ add_surface(prim, arr, blend_shapes, lods, material, name);
+ }
+ }
+}
+Dictionary EditorSceneImporterMesh::_get_data() const {
+ Dictionary data;
+ if (blend_shapes.size()) {
+ data["blend_shape_names"] = blend_shapes;
+ }
+ Array surface_arr;
+ for (int i = 0; i < surfaces.size(); i++) {
+ Dictionary d;
+ d["primitive"] = surfaces[i].primitive;
+ d["arrays"] = surfaces[i].arrays;
+ if (surfaces[i].blend_shape_data.size()) {
+ Array bs_data;
+ for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) {
+ bs_data.push_back(surfaces[i].blend_shape_data[j].arrays);
+ }
+ d["blend_shapes"] = bs_data;
+ }
+ if (surfaces[i].lods.size()) {
+ Dictionary lods;
+ for (int j = 0; j < surfaces[i].lods.size(); j++) {
+ lods[surfaces[i].lods[j].distance] = surfaces[i].lods[j].indices;
+ }
+ d["lods"] = lods;
+ }
+
+ if (surfaces[i].material.is_valid()) {
+ d["material"] = surfaces[i].material;
+ }
+
+ if (surfaces[i].name != String()) {
+ d["name"] = surfaces[i].name;
+ }
+
+ surface_arr.push_back(d);
+ }
+ data["surfaces"] = surface_arr;
+ return data;
+}
+
+Vector<Face3> EditorSceneImporterMesh::get_faces() const {
+ Vector<Face3> faces;
+ for (int i = 0; i < surfaces.size(); i++) {
+ if (surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) {
+ Vector<Vector3> vertices = surfaces[i].arrays[Mesh::ARRAY_VERTEX];
+ Vector<int> indices = surfaces[i].arrays[Mesh::ARRAY_INDEX];
+ if (indices.size()) {
+ for (int j = 0; j < indices.size(); j += 3) {
+ Face3 f;
+ f.vertex[0] = vertices[indices[j + 0]];
+ f.vertex[1] = vertices[indices[j + 1]];
+ f.vertex[2] = vertices[indices[j + 2]];
+ faces.push_back(f);
+ }
+ } else {
+ for (int j = 0; j < vertices.size(); j += 3) {
+ Face3 f;
+ f.vertex[0] = vertices[j + 0];
+ f.vertex[1] = vertices[j + 1];
+ f.vertex[2] = vertices[j + 2];
+ faces.push_back(f);
+ }
+ }
+ }
+ }
+
+ return faces;
+}
+
+Vector<Ref<Shape3D>> EditorSceneImporterMesh::convex_decompose() const {
+ ERR_FAIL_COND_V(!Mesh::convex_composition_function, Vector<Ref<Shape3D>>());
+
+ const Vector<Face3> faces = get_faces();
+
+ Vector<Vector<Face3>> decomposed = Mesh::convex_composition_function(faces, -1);
+
+ 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.instantiate();
+ shape->set_points(convex_points);
+ ret.push_back(shape);
+ }
+
+ return ret;
+}
+
+Ref<Shape3D> EditorSceneImporterMesh::create_trimesh_shape() const {
+ Vector<Face3> faces = get_faces();
+ if (faces.size() == 0) {
+ return Ref<Shape3D>();
+ }
+
+ Vector<Vector3> face_points;
+ face_points.resize(faces.size() * 3);
+
+ for (int i = 0; i < face_points.size(); i += 3) {
+ Face3 f = faces.get(i / 3);
+ face_points.set(i, f.vertex[0]);
+ face_points.set(i + 1, f.vertex[1]);
+ face_points.set(i + 2, f.vertex[2]);
+ }
+
+ Ref<ConcavePolygonShape3D> shape = memnew(ConcavePolygonShape3D);
+ shape->set_faces(face_points);
+ return shape;
+}
+
+Ref<NavigationMesh> EditorSceneImporterMesh::create_navigation_mesh() {
+ Vector<Face3> faces = get_faces();
+ if (faces.size() == 0) {
+ return Ref<NavigationMesh>();
+ }
+
+ Map<Vector3, int> unique_vertices;
+ LocalVector<int> face_indices;
+
+ for (int i = 0; i < faces.size(); i++) {
+ for (int j = 0; j < 3; j++) {
+ Vector3 v = faces[i].vertex[j];
+ int idx;
+ if (unique_vertices.has(v)) {
+ idx = unique_vertices[v];
+ } else {
+ idx = unique_vertices.size();
+ unique_vertices[v] = idx;
+ }
+ face_indices.push_back(idx);
+ }
+ }
+
+ Vector<Vector3> vertices;
+ vertices.resize(unique_vertices.size());
+ for (Map<Vector3, int>::Element *E = unique_vertices.front(); E; E = E->next()) {
+ vertices.write[E->get()] = E->key();
+ }
+
+ Ref<NavigationMesh> nm;
+ nm.instantiate();
+ nm->set_vertices(vertices);
+
+ Vector<int> v3;
+ v3.resize(3);
+ for (uint32_t i = 0; i < face_indices.size(); i += 3) {
+ v3.write[0] = face_indices[i + 0];
+ v3.write[1] = face_indices[i + 1];
+ v3.write[2] = face_indices[i + 2];
+ nm->add_polygon(v3);
+ }
+
+ return nm;
+}
+
+extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y);
+
+struct EditorSceneImporterMeshLightmapSurface {
+ Ref<Material> material;
+ LocalVector<SurfaceTool::Vertex> vertices;
+ Mesh::PrimitiveType primitive = Mesh::PrimitiveType::PRIMITIVE_MAX;
+ uint32_t format = 0;
+ String name;
+};
+
+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.");
+
+ LocalVector<float> vertices;
+ LocalVector<float> normals;
+ LocalVector<int> indices;
+ LocalVector<float> uv;
+ LocalVector<Pair<int, int>> uv_indices;
+
+ Vector<EditorSceneImporterMeshLightmapSurface> lightmap_surfaces;
+
+ // Keep only the scale
+ 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());
+
+ Transform3D transform;
+ transform.scale(scale);
+
+ Basis normal_basis = transform.basis.inverse().transposed();
+
+ for (int i = 0; i < get_surface_count(); i++) {
+ EditorSceneImporterMeshLightmapSurface s;
+ s.primitive = get_surface_primitive_type(i);
+
+ ERR_FAIL_COND_V_MSG(s.primitive != Mesh::PRIMITIVE_TRIANGLES, ERR_UNAVAILABLE, "Only triangles are supported for lightmap unwrap.");
+ Array arrays = get_surface_arrays(i);
+ s.material = get_surface_material(i);
+ s.name = get_surface_name(i);
+
+ SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format);
+
+ PackedVector3Array rvertices = arrays[Mesh::ARRAY_VERTEX];
+ int vc = rvertices.size();
+
+ PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL];
+
+ int vertex_ofs = vertices.size() / 3;
+
+ vertices.resize((vertex_ofs + vc) * 3);
+ normals.resize((vertex_ofs + vc) * 3);
+ uv_indices.resize(vertex_ofs + vc);
+
+ for (int j = 0; j < vc; j++) {
+ Vector3 v = transform.xform(rvertices[j]);
+ Vector3 n = normal_basis.xform(rnormals[j]).normalized();
+
+ vertices[(j + vertex_ofs) * 3 + 0] = v.x;
+ vertices[(j + vertex_ofs) * 3 + 1] = v.y;
+ vertices[(j + vertex_ofs) * 3 + 2] = v.z;
+ normals[(j + vertex_ofs) * 3 + 0] = n.x;
+ normals[(j + vertex_ofs) * 3 + 1] = n.y;
+ normals[(j + vertex_ofs) * 3 + 2] = n.z;
+ uv_indices[j + vertex_ofs] = Pair<int, int>(i, j);
+ }
+
+ PackedInt32Array rindices = arrays[Mesh::ARRAY_INDEX];
+ int ic = rindices.size();
+
+ float eps = 1.19209290e-7F; // Taken from xatlas.h
+ if (ic == 0) {
+ for (int j = 0; j < vc / 3; j++) {
+ Vector3 p0 = transform.xform(rvertices[j * 3 + 0]);
+ Vector3 p1 = transform.xform(rvertices[j * 3 + 1]);
+ Vector3 p2 = transform.xform(rvertices[j * 3 + 2]);
+
+ if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
+ continue;
+ }
+
+ indices.push_back(vertex_ofs + j * 3 + 0);
+ indices.push_back(vertex_ofs + j * 3 + 1);
+ indices.push_back(vertex_ofs + j * 3 + 2);
+ }
+
+ } else {
+ for (int j = 0; j < ic / 3; j++) {
+ Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]);
+ Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]);
+ Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]);
+
+ if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
+ continue;
+ }
+
+ indices.push_back(vertex_ofs + rindices[j * 3 + 0]);
+ indices.push_back(vertex_ofs + rindices[j * 3 + 1]);
+ indices.push_back(vertex_ofs + rindices[j * 3 + 2]);
+ }
+ }
+
+ lightmap_surfaces.push_back(s);
+ }
+
+ //unwrap
+
+ bool use_cache = true; // Used to request cache generation and to know if cache was used
+ uint8_t *gen_cache;
+ int gen_cache_size;
+ float *gen_uvs;
+ int *gen_vertices;
+ int *gen_indices;
+ int gen_vertex_count;
+ int gen_index_count;
+ int size_x;
+ int size_y;
+
+ bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), p_src_cache.ptr(), &use_cache, &gen_cache, &gen_cache_size, &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y);
+
+ if (!ok) {
+ return ERR_CANT_CREATE;
+ }
+
+ //remove surfaces
+ clear();
+
+ //create surfacetools for each surface..
+ LocalVector<Ref<SurfaceTool>> surfaces_tools;
+
+ for (int i = 0; i < lightmap_surfaces.size(); i++) {
+ Ref<SurfaceTool> st;
+ st.instantiate();
+ st->begin(Mesh::PRIMITIVE_TRIANGLES);
+ st->set_material(lightmap_surfaces[i].material);
+ st->set_meta("name", lightmap_surfaces[i].name);
+ surfaces_tools.push_back(st); //stay there
+ }
+
+ print_verbose("Mesh: Gen indices: " + itos(gen_index_count));
+
+ //go through all indices
+ for (int i = 0; i < gen_index_count; i += 3) {
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], (int)uv_indices.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], (int)uv_indices.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], (int)uv_indices.size(), ERR_BUG);
+
+ ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG);
+
+ int surface = uv_indices[gen_vertices[gen_indices[i + 0]]].first;
+
+ for (int j = 0; j < 3; j++) {
+ SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second];
+
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_COLOR) {
+ surfaces_tools[surface]->set_color(v.color);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TEX_UV) {
+ surfaces_tools[surface]->set_uv(v.uv);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_NORMAL) {
+ surfaces_tools[surface]->set_normal(v.normal);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TANGENT) {
+ Plane t;
+ t.normal = v.tangent;
+ t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1;
+ surfaces_tools[surface]->set_tangent(t);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_BONES) {
+ surfaces_tools[surface]->set_bones(v.bones);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_WEIGHTS) {
+ surfaces_tools[surface]->set_weights(v.weights);
+ }
+
+ Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]);
+ surfaces_tools[surface]->set_uv2(uv2);
+
+ surfaces_tools[surface]->add_vertex(v.vertex);
+ }
+ }
+
+ //generate surfaces
+ for (unsigned int i = 0; i < surfaces_tools.size(); i++) {
+ surfaces_tools[i]->index();
+ Array arrays = surfaces_tools[i]->commit_to_arrays();
+ add_surface(surfaces_tools[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools[i]->get_material(), surfaces_tools[i]->get_meta("name"));
+ }
+
+ set_lightmap_size_hint(Size2(size_x, size_y));
+
+ if (gen_cache_size > 0) {
+ r_dst_cache.resize(gen_cache_size);
+ memcpy(r_dst_cache.ptrw(), gen_cache, gen_cache_size);
+ memfree(gen_cache);
+ }
+
+ if (!use_cache) {
+ // Cache was not used, free the buffers
+ memfree(gen_vertices);
+ memfree(gen_indices);
+ memfree(gen_uvs);
+ }
+
+ return OK;
+}
+
+void EditorSceneImporterMesh::set_lightmap_size_hint(const Size2i &p_size) {
+ lightmap_size_hint = p_size;
+}
+
+Size2i EditorSceneImporterMesh::get_lightmap_size_hint() const {
+ return lightmap_size_hint;
+}
+
+void EditorSceneImporterMesh::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &EditorSceneImporterMesh::add_blend_shape);
+ ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &EditorSceneImporterMesh::get_blend_shape_count);
+ ClassDB::bind_method(D_METHOD("get_blend_shape_name", "blend_shape_idx"), &EditorSceneImporterMesh::get_blend_shape_name);
+
+ 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("get_surface_count"), &EditorSceneImporterMesh::get_surface_count);
+ ClassDB::bind_method(D_METHOD("get_surface_primitive_type", "surface_idx"), &EditorSceneImporterMesh::get_surface_primitive_type);
+ ClassDB::bind_method(D_METHOD("get_surface_name", "surface_idx"), &EditorSceneImporterMesh::get_surface_name);
+ ClassDB::bind_method(D_METHOD("get_surface_arrays", "surface_idx"), &EditorSceneImporterMesh::get_surface_arrays);
+ ClassDB::bind_method(D_METHOD("get_surface_blend_shape_arrays", "surface_idx", "blend_shape_idx"), &EditorSceneImporterMesh::get_surface_blend_shape_arrays);
+ ClassDB::bind_method(D_METHOD("get_surface_lod_count", "surface_idx"), &EditorSceneImporterMesh::get_surface_lod_count);
+ 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("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", "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);
+ ClassDB::bind_method(D_METHOD("_get_data"), &EditorSceneImporterMesh::_get_data);
+
+ ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &EditorSceneImporterMesh::set_lightmap_size_hint);
+ ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &EditorSceneImporterMesh::get_lightmap_size_hint);
+
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data");
+}
diff --git a/editor/import/scene_importer_mesh.h b/editor/import/scene_importer_mesh.h
new file mode 100644
index 0000000000..e57e479d8e
--- /dev/null
+++ b/editor/import/scene_importer_mesh.h
@@ -0,0 +1,119 @@
+/*************************************************************************/
+/* scene_importer_mesh.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 EDITOR_SCENE_IMPORTER_MESH_H
+#define EDITOR_SCENE_IMPORTER_MESH_H
+
+#include "core/io/resource.h"
+#include "scene/resources/concave_polygon_shape_3d.h"
+#include "scene/resources/convex_polygon_shape_3d.h"
+#include "scene/resources/mesh.h"
+#include "scene/resources/navigation_mesh.h"
+// 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
+
+class EditorSceneImporterMesh : public Resource {
+ GDCLASS(EditorSceneImporterMesh, Resource)
+
+ struct Surface {
+ Mesh::PrimitiveType primitive;
+ Array arrays;
+ struct BlendShape {
+ Array arrays;
+ };
+ Vector<BlendShape> blend_shape_data;
+ struct LOD {
+ Vector<int> indices;
+ float distance;
+ };
+ Vector<LOD> lods;
+ Ref<Material> material;
+ String name;
+ };
+ Vector<Surface> surfaces;
+ Vector<String> blend_shapes;
+ Mesh::BlendShapeMode blend_shape_mode = Mesh::BLEND_SHAPE_MODE_NORMALIZED;
+
+ Ref<ArrayMesh> mesh;
+
+ Ref<EditorSceneImporterMesh> shadow_mesh;
+
+ Size2i lightmap_size_hint;
+ Basis compute_rotation_matrix_from_ortho_6d(Vector3 p_x_raw, Vector3 y_raw);
+
+protected:
+ void _set_data(const Dictionary &p_data);
+ Dictionary _get_data() const;
+
+ static void _bind_methods();
+
+public:
+ void add_blend_shape(const String &p_name);
+ 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());
+ int get_surface_count() const;
+
+ void set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode);
+ Mesh::BlendShapeMode get_blend_shape_mode() const;
+
+ 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;
+
+ void set_surface_material(int p_surface, const Ref<Material> &p_material);
+
+ void generate_lods();
+
+ void create_shadow_mesh();
+ Ref<EditorSceneImporterMesh> get_shadow_mesh() const;
+
+ Vector<Face3> get_faces() const;
+ Vector<Ref<Shape3D>> convex_decompose() const;
+ Ref<Shape3D> create_trimesh_shape() const;
+ Ref<NavigationMesh> create_navigation_mesh();
+ 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<ArrayMesh> &p_base = Ref<ArrayMesh>());
+ void clear();
+};
+#endif // EDITOR_SCENE_IMPORTER_MESH_H
diff --git a/editor/import/scene_importer_mesh_node_3d.cpp b/editor/import/scene_importer_mesh_node_3d.cpp
new file mode 100644
index 0000000000..3c201cf674
--- /dev/null
+++ b/editor/import/scene_importer_mesh_node_3d.cpp
@@ -0,0 +1,83 @@
+/*************************************************************************/
+/* scene_importer_mesh_node_3d.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 "scene_importer_mesh_node_3d.h"
+
+void EditorSceneImporterMeshNode3D::set_mesh(const Ref<EditorSceneImporterMesh> &p_mesh) {
+ mesh = p_mesh;
+}
+Ref<EditorSceneImporterMesh> EditorSceneImporterMeshNode3D::get_mesh() const {
+ return mesh;
+}
+
+void EditorSceneImporterMeshNode3D::set_skin(const Ref<Skin> &p_skin) {
+ skin = p_skin;
+}
+Ref<Skin> EditorSceneImporterMeshNode3D::get_skin() const {
+ return skin;
+}
+
+void EditorSceneImporterMeshNode3D::set_surface_material(int p_idx, const Ref<Material> &p_material) {
+ ERR_FAIL_COND(p_idx < 0);
+ if (p_idx >= surface_materials.size()) {
+ surface_materials.resize(p_idx + 1);
+ }
+
+ surface_materials.write[p_idx] = p_material;
+}
+Ref<Material> EditorSceneImporterMeshNode3D::get_surface_material(int p_idx) const {
+ ERR_FAIL_COND_V(p_idx < 0, Ref<Material>());
+ if (p_idx >= surface_materials.size()) {
+ return Ref<Material>();
+ }
+ return surface_materials[p_idx];
+}
+
+void EditorSceneImporterMeshNode3D::set_skeleton_path(const NodePath &p_path) {
+ skeleton_path = p_path;
+}
+NodePath EditorSceneImporterMeshNode3D::get_skeleton_path() const {
+ return skeleton_path;
+}
+
+void EditorSceneImporterMeshNode3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &EditorSceneImporterMeshNode3D::set_mesh);
+ ClassDB::bind_method(D_METHOD("get_mesh"), &EditorSceneImporterMeshNode3D::get_mesh);
+
+ ClassDB::bind_method(D_METHOD("set_skin", "skin"), &EditorSceneImporterMeshNode3D::set_skin);
+ ClassDB::bind_method(D_METHOD("get_skin"), &EditorSceneImporterMeshNode3D::get_skin);
+
+ ClassDB::bind_method(D_METHOD("set_skeleton_path", "skeleton_path"), &EditorSceneImporterMeshNode3D::set_skeleton_path);
+ ClassDB::bind_method(D_METHOD("get_skeleton_path"), &EditorSceneImporterMeshNode3D::get_skeleton_path);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "EditorSceneImporterMesh"), "set_mesh", "get_mesh");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path");
+}
diff --git a/editor/import/resource_importer_csv.cpp b/editor/import/scene_importer_mesh_node_3d.h
index d29ba28a96..dec1717c99 100644
--- a/editor/import/resource_importer_csv.cpp
+++ b/editor/import/scene_importer_mesh_node_3d.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* resource_importer_csv.cpp */
+/* scene_importer_mesh_node_3d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,49 +28,37 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "resource_importer_csv.h"
+#ifndef EDITOR_SCENE_IMPORTER_MESH_NODE_3D_H
+#define EDITOR_SCENE_IMPORTER_MESH_NODE_3D_H
-#include "core/io/resource_saver.h"
-#include "core/os/file_access.h"
+#include "editor/import/scene_importer_mesh.h"
+#include "scene/3d/node_3d.h"
+#include "scene/resources/skin.h"
-String ResourceImporterCSV::get_importer_name() const {
- return "csv";
-}
+class EditorSceneImporterMesh;
-String ResourceImporterCSV::get_visible_name() const {
- return "CSV";
-}
+class EditorSceneImporterMeshNode3D : public Node3D {
+ GDCLASS(EditorSceneImporterMeshNode3D, Node3D)
-void ResourceImporterCSV::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("csv");
-}
+ Ref<EditorSceneImporterMesh> mesh;
+ Ref<Skin> skin;
+ NodePath skeleton_path;
+ Vector<Ref<Material>> surface_materials;
-String ResourceImporterCSV::get_save_extension() const {
- return ""; //does not save a single resource
-}
+protected:
+ static void _bind_methods();
-String ResourceImporterCSV::get_resource_type() const {
- return "TextFile";
-}
+public:
+ void set_mesh(const Ref<EditorSceneImporterMesh> &p_mesh);
+ Ref<EditorSceneImporterMesh> get_mesh() const;
-bool ResourceImporterCSV::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
- return true;
-}
+ void set_skin(const Ref<Skin> &p_skin);
+ Ref<Skin> get_skin() const;
-int ResourceImporterCSV::get_preset_count() const {
- return 0;
-}
+ void set_surface_material(int p_idx, const Ref<Material> &p_material);
+ Ref<Material> get_surface_material(int p_idx) const;
-String ResourceImporterCSV::get_preset_name(int p_idx) const {
- return "";
-}
-
-void ResourceImporterCSV::get_import_options(List<ImportOption> *r_options, int p_preset) const {
-}
-
-Error ResourceImporterCSV::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) {
- return OK;
-}
-
-ResourceImporterCSV::ResourceImporterCSV() {
-}
+ void set_skeleton_path(const NodePath &p_path);
+ NodePath get_skeleton_path() const;
+};
+#endif