diff options
89 files changed, 3338 insertions, 341 deletions
diff --git a/SConstruct b/SConstruct index 4085f713b7..45765976cd 100644 --- a/SConstruct +++ b/SConstruct @@ -461,7 +461,8 @@ screen = sys.stdout node_count = 0 node_count_max = 0 node_count_interval = 1 -node_count_fname = str(env.Dir('#')) + '/.scons_node_count' +if ('env' in locals()): + node_count_fname = str(env.Dir('#')) + '/.scons_node_count' def progress_function(node): global node_count, node_count_max, node_count_interval, node_count_fname @@ -481,7 +482,7 @@ def progress_finish(target, source, env): with open(node_count_fname, 'w') as f: f.write('%d\n' % node_count) -if (env["progress"] == "yes"): +if ('env' in locals() and env["progress"] == "yes"): try: with open(node_count_fname) as f: node_count_max = int(f.readline()) diff --git a/core/class_db.cpp b/core/class_db.cpp index 6b8c290a99..24d71f86b0 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -937,6 +937,28 @@ bool ClassDB::get_property(Object *p_object, const StringName &p_property, Varia return false; } +int ClassDB::get_property_index(const StringName &p_class, const StringName &p_property, bool *r_is_valid) { + + ClassInfo *type = classes.getptr(p_class); + ClassInfo *check = type; + while (check) { + const PropertySetGet *psg = check->property_setget.getptr(p_property); + if (psg) { + + if (r_is_valid) + *r_is_valid = true; + + return psg->index; + } + + check = check->inherits_ptr; + } + if (r_is_valid) + *r_is_valid = false; + + return -1; +} + Variant::Type ClassDB::get_property_type(const StringName &p_class, const StringName &p_property, bool *r_is_valid) { ClassInfo *type = classes.getptr(p_class); diff --git a/core/class_db.h b/core/class_db.h index 4f00a16e91..02eac0dbbc 100644 --- a/core/class_db.h +++ b/core/class_db.h @@ -480,6 +480,7 @@ public: static bool set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid = NULL); static bool get_property(Object *p_object, const StringName &p_property, Variant &r_value); static bool has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance = false); + static int get_property_index(const StringName &p_class, const StringName &p_property, bool *r_is_valid = NULL); static Variant::Type get_property_type(const StringName &p_class, const StringName &p_property, bool *r_is_valid = NULL); static StringName get_property_setter(StringName p_class, const StringName p_property); static StringName get_property_getter(StringName p_class, const StringName p_property); diff --git a/core/color.h b/core/color.h index c83dcda4b4..9074a0e6d6 100644 --- a/core/color.h +++ b/core/color.h @@ -140,8 +140,16 @@ struct Color { b < 0.04045 ? b * (1.0 / 12.92) : Math::pow((b + 0.055) * (1.0 / (1 + 0.055)), 2.4), a); } + _FORCE_INLINE_ Color to_srgb() const { - static Color hex(uint32_t p_hex); + return Color( + r < 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math::pow(r, 1.0f / 2.4f) - 0.055, + g < 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math::pow(g, 1.0f / 2.4f) - 0.055, + b < 0.0031308 ? 12.92 * b : (1.0 + 0.055) * Math::pow(b, 1.0f / 2.4f) - 0.055, a); + } + + static Color + hex(uint32_t p_hex); static Color html(const String &p_color); static bool html_is_valid(const String &p_color); static Color named(const String &p_name); diff --git a/core/global_constants.cpp b/core/global_constants.cpp index 18071d7748..9e745ecb98 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -67,8 +67,8 @@ static _GlobalConstant _global_constants[] = { BIND_GLOBAL_CONSTANT(KEY_TAB), BIND_GLOBAL_CONSTANT(KEY_BACKTAB), BIND_GLOBAL_CONSTANT(KEY_BACKSPACE), - BIND_GLOBAL_CONSTANT(KEY_RETURN), BIND_GLOBAL_CONSTANT(KEY_ENTER), + BIND_GLOBAL_CONSTANT(KEY_KP_ENTER), BIND_GLOBAL_CONSTANT(KEY_INSERT), BIND_GLOBAL_CONSTANT(KEY_DELETE), BIND_GLOBAL_CONSTANT(KEY_PAUSE), diff --git a/core/input_map.cpp b/core/input_map.cpp index 24d0624e98..85e627f352 100644 --- a/core/input_map.cpp +++ b/core/input_map.cpp @@ -219,11 +219,11 @@ void InputMap::load_default() { add_action("ui_accept"); key.instance(); - key->set_scancode(KEY_RETURN); + key->set_scancode(KEY_ENTER); action_add_event("ui_accept", key); key.instance(); - key->set_scancode(KEY_ENTER); + key->set_scancode(KEY_KP_ENTER); action_add_event("ui_accept", key); key.instance(); diff --git a/core/path_db.cpp b/core/node_path.cpp index d5c84a2457..11df9670f2 100644 --- a/core/path_db.cpp +++ b/core/node_path.cpp @@ -27,7 +27,7 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "path_db.h" +#include "node_path.h" #include "print_string.h" diff --git a/core/path_db.h b/core/node_path.h index 1aed7535ca..1aed7535ca 100644 --- a/core/path_db.h +++ b/core/node_path.h diff --git a/core/object.cpp b/core/object.cpp index 316c624268..5824084151 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -1182,10 +1182,10 @@ Variant Object::_emit_signal(const Variant **p_args, int p_argcount, Variant::Ca return Variant(); } -void Object::emit_signal(const StringName &p_name, const Variant **p_args, int p_argcount) { +Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int p_argcount) { if (_block_signals) - return; //no emit, signals blocked + return ERR_CANT_AQUIRE_RESOURCE; //no emit, signals blocked Signal *s = signal_map.getptr(p_name); if (!s) { @@ -1194,11 +1194,11 @@ void Object::emit_signal(const StringName &p_name, const Variant **p_args, int p //check in script if (!signal_is_valid && !script.is_null() && !Ref<Script>(script)->has_script_signal(p_name)) { ERR_EXPLAIN("Can't emit non-existing signal " + String("\"") + p_name + "\"."); - ERR_FAIL(); + ERR_FAIL_V(ERR_UNAVAILABLE); } #endif //not connected? just return - return; + return ERR_UNAVAILABLE; } List<_ObjectSignalDisconnectData> disconnect_data; @@ -1214,6 +1214,8 @@ void Object::emit_signal(const StringName &p_name, const Variant **p_args, int p Vector<const Variant *> bind_mem; + Error err = OK; + for (int i = 0; i < ssize; i++) { const Connection &c = slot_map.getv(i).conn; @@ -1249,12 +1251,14 @@ void Object::emit_signal(const StringName &p_name, const Variant **p_args, int p } else { Variant::CallError ce; target->call(c.method, args, argc, ce); + if (ce.error != Variant::CallError::CALL_OK) { if (ce.error == Variant::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) { //most likely object is not initialized yet, do not throw error. } else { ERR_PRINTS("Error calling method from signal '" + String(p_name) + "': " + Variant::get_call_error_text(target, c.method, args, argc, ce)); + err = ERR_METHOD_NOT_FOUND; } } } @@ -1274,21 +1278,24 @@ void Object::emit_signal(const StringName &p_name, const Variant **p_args, int p disconnect(dd.signal, dd.target, dd.method); disconnect_data.pop_front(); } + + return err; } -void Object::emit_signal(const StringName &p_name, VARIANT_ARG_DECLARE) { +Error Object::emit_signal(const StringName &p_name, VARIANT_ARG_DECLARE) { VARIANT_ARGPTRS; int argc = 0; for (int i = 0; i < VARIANT_ARG_MAX; i++) { + if (argptr[i]->get_type() == Variant::NIL) break; argc++; } - emit_signal(p_name, argptr, argc); + return emit_signal(p_name, argptr, argc); } void Object::_add_user_signal(const String &p_name, const Array &p_args) { @@ -2008,7 +2015,7 @@ void ObjectDB::cleanup() { String node_name; if (instances[*K]->is_class("Node")) node_name = " - Node Name: " + String(instances[*K]->call("get_name")); - if (instances[*K]->is_class("Resoucre")) + if (instances[*K]->is_class("Resource")) node_name = " - Resource Name: " + String(instances[*K]->call("get_name")) + " Path: " + String(instances[*K]->call("get_path")); print_line("Leaked Instance: " + String(instances[*K]->get_class()) + ":" + itos(*K) + node_name); } diff --git a/core/object.h b/core/object.h index 148a73fbc4..fd3bb624ec 100644 --- a/core/object.h +++ b/core/object.h @@ -105,6 +105,7 @@ enum PropertyUsageFlags { PROPERTY_USAGE_STORE_IF_NULL = 16384, PROPERTY_USAGE_ANIMATE_AS_TRIGGER = 32768, PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED = 65536, + PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 17, PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK, PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED, @@ -654,8 +655,8 @@ public: void set_script_and_instance(const RefPtr &p_script, ScriptInstance *p_instance); //some script languages can't control instance creation, so this function eases the process void add_user_signal(const MethodInfo &p_signal); - void emit_signal(const StringName &p_name, VARIANT_ARG_LIST); - void emit_signal(const StringName &p_name, const Variant **p_args, int p_argcount); + Error emit_signal(const StringName &p_name, VARIANT_ARG_LIST); + Error emit_signal(const StringName &p_name, const Variant **p_args, int p_argcount); void get_signal_list(List<MethodInfo> *p_signals) const; void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const; void get_all_signal_connections(List<Connection> *p_connections) const; diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp index e154b1934d..9b3e376ea6 100644 --- a/core/os/keyboard.cpp +++ b/core/os/keyboard.cpp @@ -42,8 +42,8 @@ static const _KeyCodeText _keycodes[] = { {KEY_TAB ,"Tab"}, {KEY_BACKTAB ,"BackTab"}, {KEY_BACKSPACE ,"BackSpace"}, - {KEY_RETURN ,"Return"}, {KEY_ENTER ,"Enter"}, + {KEY_KP_ENTER ,"Kp Enter"}, {KEY_INSERT ,"Insert"}, {KEY_DELETE ,"Delete"}, {KEY_PAUSE ,"Pause"}, @@ -294,8 +294,8 @@ bool keycode_has_unicode(uint32_t p_keycode) { case KEY_TAB: case KEY_BACKTAB: case KEY_BACKSPACE: - case KEY_RETURN: case KEY_ENTER: + case KEY_KP_ENTER: case KEY_INSERT: case KEY_DELETE: case KEY_PAUSE: diff --git a/core/os/keyboard.h b/core/os/keyboard.h index c6985c887d..1ed93e3540 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -57,8 +57,8 @@ enum KeyList { KEY_TAB = SPKEY | 0x02, KEY_BACKTAB = SPKEY | 0x03, KEY_BACKSPACE = SPKEY | 0x04, - KEY_RETURN = SPKEY | 0x05, - KEY_ENTER = SPKEY | 0x06, + KEY_ENTER = SPKEY | 0x05, + KEY_KP_ENTER = SPKEY | 0x06, KEY_INSERT = SPKEY | 0x07, KEY_DELETE = SPKEY | 0x08, KEY_PAUSE = SPKEY | 0x09, diff --git a/core/project_settings.cpp b/core/project_settings.cpp index b31f78ec20..f6e0d2e991 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -925,10 +925,10 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("application/config/use_shared_user_dir", true); key.instance(); - key->set_scancode(KEY_RETURN); + key->set_scancode(KEY_ENTER); va.push_back(key); key.instance(); - key->set_scancode(KEY_ENTER); + key->set_scancode(KEY_KP_ENTER); va.push_back(key); key.instance(); key->set_scancode(KEY_SPACE); diff --git a/core/resource.cpp b/core/resource.cpp index 5625784396..86069bf2e9 100644 --- a/core/resource.cpp +++ b/core/resource.cpp @@ -32,6 +32,7 @@ #include "core_string_names.h" #include "io/resource_loader.h" #include "os/file_access.h" +#include "scene/main/node.h" //only so casting works #include "script_language.h" #include <stdio.h> diff --git a/core/script_language.cpp b/core/script_language.cpp index aeb1573840..bb99e0abae 100644 --- a/core/script_language.cpp +++ b/core/script_language.cpp @@ -280,8 +280,23 @@ ScriptDebugger::ScriptDebugger() { bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_value) { if (values.has(p_name)) { + Variant defval; + if (script->get_property_default_value(p_name, defval)) { + if (defval == p_value) { + values.erase(p_name); + return true; + } + } values[p_name] = p_value; return true; + } else { + Variant defval; + if (script->get_property_default_value(p_name, defval)) { + if (defval != p_value) { + values[p_name] = p_value; + } + return true; + } } return false; } @@ -291,12 +306,22 @@ bool PlaceHolderScriptInstance::get(const StringName &p_name, Variant &r_ret) co r_ret = values[p_name]; return true; } + + Variant defval; + if (script->get_property_default_value(p_name, defval)) { + r_ret = defval; + return true; + } return false; } void PlaceHolderScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const { for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { + PropertyInfo pinfo = E->get(); + if (!values.has(pinfo.name)) { + pinfo.usage |= PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE; + } p_properties->push_back(E->get()); } } @@ -336,6 +361,14 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c if (!new_values.has(E->key())) to_remove.push_back(E->key()); + + Variant defval; + if (script->get_property_default_value(E->key(), defval)) { + //remove because it's the same as the default value + if (defval == E->get()) { + to_remove.push_back(E->key()); + } + } } while (to_remove.size()) { diff --git a/core/variant.h b/core/variant.h index 661d31cf16..95782d9619 100644 --- a/core/variant.h +++ b/core/variant.h @@ -42,8 +42,8 @@ #include "io/ip_address.h" #include "math_2d.h" #include "matrix3.h" +#include "node_path.h" #include "os/power.h" -#include "path_db.h" #include "plane.h" #include "quat.h" #include "rect3.h" diff --git a/doc/base/classes.xml b/doc/base/classes.xml index 268bfeca1a..cd5b950f57 100644 --- a/doc/base/classes.xml +++ b/doc/base/classes.xml @@ -846,10 +846,10 @@ <constant name="KEY_BACKSPACE" value="16777220"> Backspace Key </constant> - <constant name="KEY_RETURN" value="16777221"> + <constant name="KEY_ENTER" value="16777221"> Return Key (On Main Keyboard) </constant> - <constant name="KEY_ENTER" value="16777222"> + <constant name="KEY_KP_ENTER" value="16777222"> Enter Key (On Numpad) </constant> <constant name="KEY_INSERT" value="16777223"> diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 89bea1e8cc..60e537df20 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -6902,17 +6902,19 @@ void RasterizerStorageGLES3::initialize() { config.use_fast_texture_filter = int(ProjectSettings::get_singleton()->get("rendering/quality/filters/use_nearest_mipmap_filter")); config.use_anisotropic_filter = config.extensions.has("rendering/quality/filters/anisotropic_filter_level"); - config.s3tc_supported = config.extensions.has("GL_EXT_texture_compression_dxt1") || config.extensions.has("GL_EXT_texture_compression_s3tc") || config.extensions.has("WEBGL_compressed_texture_s3tc"); config.etc_supported = config.extensions.has("GL_OES_compressed_ETC1_RGB8_texture"); config.latc_supported = config.extensions.has("GL_EXT_texture_compression_latc"); - config.rgtc_supported = config.extensions.has("GL_EXT_texture_compression_rgtc"); config.bptc_supported = config.extensions.has("GL_ARB_texture_compression_bptc"); #ifdef GLES_OVER_GL config.hdr_supported = true; config.etc2_supported = false; + config.rgtc_supported = true; //supported by spec + config.s3tc_supported = true; //supported by spec #else config.etc2_supported = true; config.hdr_supported = false; + config.rgtc_supported = config.extensions.has("GL_EXT_texture_compression_rgtc"); + config.s3tc_supported = config.extensions.has("GL_EXT_texture_compression_dxt1") || config.extensions.has("GL_EXT_texture_compression_s3tc") || config.extensions.has("WEBGL_compressed_texture_s3tc"); #endif config.pvrtc_supported = config.extensions.has("GL_IMG_texture_compression_pvrtc"); diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 340a1f24d2..efb82441f4 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1929,7 +1929,7 @@ FRAGMENT_SHADER_CODE if (fog_depth_enabled) { - float fog_z = smoothstep(fog_depth_begin,z_far,-vertex.z); + float fog_z = smoothstep(fog_depth_begin,z_far,length(vertex)); fog_amount = pow(fog_z,fog_depth_curve); if (fog_transmit_enabled) { diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index ed58116304..0fd643d031 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -512,6 +512,8 @@ void EditorFileSystem::_scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess if (FileAccess::exists(cd.plus_file(f).plus_file("project.godot"))) // skip if another project inside this continue; + if (FileAccess::exists(cd.plus_file(f).plus_file(".gdignore"))) // skip if another project inside this + continue; dirs.push_back(f); @@ -691,6 +693,8 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, const if (FileAccess::exists(cd.plus_file(f).plus_file("project.godot"))) // skip if another project inside this continue; + if (FileAccess::exists(cd.plus_file(f).plus_file(".gdignore"))) // skip if another project inside this + continue; EditorFileSystemDirectory *efd = memnew(EditorFileSystemDirectory); diff --git a/editor/editor_name_dialog.cpp b/editor/editor_name_dialog.cpp index 7435e9a9f7..6ebfcbf313 100644 --- a/editor/editor_name_dialog.cpp +++ b/editor/editor_name_dialog.cpp @@ -42,8 +42,8 @@ void EditorNameDialog::_line_gui_input(const Ref<InputEvent> &p_event) { return; switch (k->get_scancode()) { - case KEY_ENTER: - case KEY_RETURN: { + case KEY_KP_ENTER: + case KEY_ENTER: { if (get_hide_on_ok()) hide(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 4e2687449e..4d5dd14172 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -110,6 +110,7 @@ // end #include "editor_settings.h" #include "import/editor_import_collada.h" +#include "import/editor_scene_importer_gltf.h" #include "io_plugins/editor_bitmask_import_plugin.h" #include "io_plugins/editor_export_scene.h" #include "io_plugins/editor_font_import_plugin.h" @@ -2803,9 +2804,9 @@ void EditorNode::_discard_changes(const String &p_str) { String exec = OS::get_singleton()->get_executable_path(); List<String> args; - args.push_back("--path"); + args.push_back("-path"); args.push_back(exec.get_base_dir()); - args.push_back("--project-manager"); + args.push_back("-pm"); OS::ProcessID pid = 0; Error err = OS::get_singleton()->execute(exec, args, false, &pid); @@ -5151,6 +5152,10 @@ EditorNode::EditorNode() { Ref<EditorOBJImporter> import_obj; import_obj.instance(); import_scene->add_importer(import_obj); + + Ref<EditorSceneImporterGLTF> import_gltf; + import_gltf.instance(); + import_scene->add_importer(import_gltf); } } diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index fee2d0ba4d..aa97dd237b 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -45,24 +45,24 @@ Error EditorRun::run(const String &p_scene, const String p_custom_args, const Li int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port"); if (resource_path != "") { - args.push_back("--path"); + args.push_back("-path"); args.push_back(resource_path.replace(" ", "%20")); } if (true) { - args.push_back("--remote-debug"); + args.push_back("-rdebug"); args.push_back(remote_host + ":" + String::num(remote_port)); } - args.push_back("--editor-pid"); + args.push_back("-epid"); args.push_back(String::num(OS::get_singleton()->get_process_ID())); if (debug_collisions) { - args.push_back("--debug-collision"); + args.push_back("-debugcol"); } if (debug_navigation) { - args.push_back("--debug-navigation"); + args.push_back("-debugnav"); } int screen = EditorSettings::get_singleton()->get("run/window_placement/screen"); @@ -101,33 +101,33 @@ Error EditorRun::run(const String &p_scene, const String p_custom_args, const Li case 1: { // centered Vector2 pos = screen_rect.position + ((screen_rect.size - desired_size) / 2).floor(); args.push_back("-p"); - args.push_back(itos(pos.x) + "," + itos(pos.y)); + args.push_back(itos(pos.x) + "x" + itos(pos.y)); } break; case 2: { // custom pos Vector2 pos = EditorSettings::get_singleton()->get("run/window_placement/rect_custom_position"); pos += screen_rect.position; args.push_back("-p"); - args.push_back(itos(pos.x) + "," + itos(pos.y)); + args.push_back(itos(pos.x) + "x" + itos(pos.y)); } break; case 3: { // force maximized Vector2 pos = screen_rect.position; args.push_back("-p"); - args.push_back(itos(pos.x) + "," + itos(pos.y)); - args.push_back("-m"); + args.push_back(itos(pos.x) + "x" + itos(pos.y)); + args.push_back("-mx"); } break; case 4: { // force fullscreen Vector2 pos = screen_rect.position; args.push_back("-p"); - args.push_back(itos(pos.x) + "," + itos(pos.y)); + args.push_back(itos(pos.x) + "x" + itos(pos.y)); args.push_back("-f"); } break; } if (p_breakpoints.size()) { - args.push_back("-b"); + args.push_back("-bp"); String bpoints; for (const List<String>::Element *E = p_breakpoints.front(); E; E = E->next()) { diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp new file mode 100644 index 0000000000..1c42bcef8a --- /dev/null +++ b/editor/import/editor_scene_importer_gltf.cpp @@ -0,0 +1,2108 @@ +#include "editor_scene_importer_gltf.h" +#include "io/json.h" +#include "os/file_access.h" +#include "os/os.h" +#include "scene/3d/camera.h" +#include "scene/3d/mesh_instance.h" +#include "scene/animation/animation_player.h" +#include "scene/resources/surface_tool.h" +#include "thirdparty/misc/base64.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.ptr(), 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 + uint32_t version = f->get_32(); + uint32_t length = f->get_32(); + + 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.ptr(), 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.ptr(), 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::_gen_unique_name(GLTFState &state, const String &p_name) { + + int index = 1; + + String name; + while (true) { + + name = p_name; + if (index > 1) { + name += " " + itos(index); + } + if (!state.unique_names.has(name)) { + break; + } + index++; + } + + state.unique_names.insert(name); + + return name; +} + +Error EditorSceneImporterGLTF::_parse_scenes(GLTFState &state) { + + ERR_FAIL_COND_V(!state.json.has("scenes"), ERR_FILE_CORRUPT); + Array scenes = state.json["scenes"]; + for (int i = 0; i < 1; i++) { //only first scene is imported + Dictionary s = scenes[i]; + ERR_FAIL_COND_V(!s.has("nodes"), ERR_UNAVAILABLE); + Array nodes = s["nodes"]; + for (int j = 0; j < nodes.size(); j++) { + state.root_nodes.push_back(nodes[j]); + } + + if (s.has("name")) { + state.scene_name = s["name"]; + } + } + + return OK; +} + +Error EditorSceneImporterGLTF::_parse_nodes(GLTFState &state) { + + ERR_FAIL_COND_V(!state.json.has("nodes"), ERR_FILE_CORRUPT); + Array nodes = state.json["nodes"]; + for (int i = 0; i < nodes.size(); i++) { + + GLTFNode *node = memnew(GLTFNode); + Dictionary n = nodes[i]; + + print_line("node " + itos(i) + ": " + String(Variant(n))); + 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 (!state.skin_users.has(node->skin)) { + state.skin_users[node->skin] = Vector<int>(); + } + + state.skin_users[node->skin].push_back(i); + } + 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 = Basis(node->rotation); + node->xform.basis.scale(node->scale); + node->xform.origin = node->translation; + } + + if (n.has("children")) { + Array children = n["children"]; + for (int i = 0; i < children.size(); i++) { + node->children.push_back(children[i]); + } + } + + state.nodes.push_back(node); + } + + //build the hierarchy + + for (int i = 0; i < state.nodes.size(); i++) { + + for (int j = 0; j < state.nodes[i]->children.size(); j++) { + int child = state.nodes[i]->children[j]; + ERR_FAIL_INDEX_V(child, state.nodes.size(), ERR_FILE_CORRUPT); + ERR_CONTINUE(state.nodes[child]->parent != -1); //node already has a parent, wtf. + + state.nodes[child]->parent = i; + } + } + + return OK; +} + +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); + + int len = base64_decode((char *)buf.ptr(), (char *)substr.get_data(), strlen); + + buf.resize(len); + + return buf; +} + +Error EditorSceneImporterGLTF::_parse_buffers(GLTFState &state, const String &p_base_path) { + + if (!state.json.has("buffers")) + return OK; + + Array buffers = state.json["buffers"]; + for (int i = 0; i < buffers.size(); i++) { + + if (i == 0 && state.glb_data.size()) { + state.buffers.push_back(state.glb_data); + + } else { + 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_line("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); + Array buffers = state.json["bufferViews"]; + for (int i = 0; i < buffers.size(); i++) { + + 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")) { + int target = d["target"]; + buffer_view.indices = target == ELEMENT_ARRAY_BUFFER; + } + + state.buffer_views.push_back(buffer_view); + } + + print_line("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); + Array accessors = state.json["accessors"]; + for (int i = 0; i < accessors.size(); i++) { + + 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.. + + Dictionary s = d["sparse"]; + + ERR_FAIL_COND_V(!d.has("count"), ERR_PARSE_ERROR); + accessor.sparse_count = d["count"]; + ERR_FAIL_COND_V(!d.has("indices"), ERR_PARSE_ERROR); + Dictionary si = d["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(!d.has("values"), ERR_PARSE_ERROR); + Dictionary sv = d["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_line("total accessors: " + itos(state.accessors.size())); + + return OK; +} + +String EditorSceneImporterGLTF::_get_component_type_name(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(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, int p_buffer_view, double *dst, int skip_every, int skip_bytes, int element_size, int count, GLTFType type, int component_count, int component_type, int component_size, bool normalized, int byte_offset, 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); + + uint32_t offset = bv.byte_offset + byte_offset; + Vector<uint8_t> buffer = state.buffers[bv.buffer]; //copy on write, so no performance hit + + //use to debug + print_line("type " + _get_type_name(type) + " component type: " + _get_component_type_name(component_type) + " stride: " + itos(stride) + " amount " + itos(count)); + print_line("accessor offset" + itos(byte_offset) + " view offset: " + itos(bv.byte_offset) + " total buffer len: " + itos(buffer.size()) + " view len " + itos(bv.byte_length)); + + int buffer_end = (stride * (count - 1)) + element_size; + ERR_FAIL_COND_V(buffer_end > bv.byte_length, ERR_PARSE_ERROR); + + ERR_FAIL_COND_V((offset + buffer_end) > buffer.size(), ERR_PARSE_ERROR); + + //fill everything as doubles + + for (int i = 0; i < count; i++) { + + const uint8_t *src = &buffer[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(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, int p_accessor, 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]; + + int component_count_for_type[7] = { + 1, 2, 3, 4, 4, 9, 16 + }; + + int component_count = component_count_for_type[a.type]; + 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.ptr(); + + if (a.buffer_view >= 0) { + + ERR_FAIL_INDEX_V(a.buffer_view, state.buffer_views.size(), Vector<double>()); + + Error err = _decode_buffer_view(state, a.buffer_view, dst, 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[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); + int indices_component_size = _get_component_type_size(a.sparse_indices_component_type); + + Error err = _decode_buffer_view(state, a.sparse_indices_buffer_view, indices.ptr(), 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, a.sparse_values_buffer_view, data.ptr(), 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++) { + 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; +} + +PoolVector<int> EditorSceneImporterGLTF::_decode_accessor_as_ints(GLTFState &state, int p_accessor, bool p_for_vertex) { + + Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + PoolVector<int> ret; + if (attribs.size() == 0) + return ret; + const double *attribs_ptr = attribs.ptr(); + int ret_size = attribs.size(); + ret.resize(ret_size); + { + PoolVector<int>::Write w = ret.write(); + for (int i = 0; i < ret_size; i++) { + w[i] = int(attribs_ptr[i]); + } + } + return ret; +} + +PoolVector<float> EditorSceneImporterGLTF::_decode_accessor_as_floats(GLTFState &state, int p_accessor, bool p_for_vertex) { + + Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + PoolVector<float> ret; + if (attribs.size() == 0) + return ret; + const double *attribs_ptr = attribs.ptr(); + int ret_size = attribs.size(); + ret.resize(ret_size); + { + PoolVector<float>::Write w = ret.write(); + for (int i = 0; i < ret_size; i++) { + w[i] = float(attribs_ptr[i]); + } + } + return ret; +} + +PoolVector<Vector2> EditorSceneImporterGLTF::_decode_accessor_as_vec2(GLTFState &state, int p_accessor, bool p_for_vertex) { + + Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + PoolVector<Vector2> ret; + if (attribs.size() == 0) + return ret; + ERR_FAIL_COND_V(attribs.size() % 2 != 0, ret); + const double *attribs_ptr = attribs.ptr(); + int ret_size = attribs.size() / 2; + ret.resize(ret_size); + { + PoolVector<Vector2>::Write w = ret.write(); + for (int i = 0; i < ret_size; i++) { + w[i] = Vector2(attribs_ptr[i * 2 + 0], attribs_ptr[i * 2 + 1]); + } + } + return ret; +} + +PoolVector<Vector3> EditorSceneImporterGLTF::_decode_accessor_as_vec3(GLTFState &state, int p_accessor, bool p_for_vertex) { + + Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + PoolVector<Vector3> ret; + if (attribs.size() == 0) + return ret; + ERR_FAIL_COND_V(attribs.size() % 3 != 0, ret); + const double *attribs_ptr = attribs.ptr(); + int ret_size = attribs.size() / 3; + ret.resize(ret_size); + { + PoolVector<Vector3>::Write w = ret.write(); + 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; +} +PoolVector<Color> EditorSceneImporterGLTF::_decode_accessor_as_color(GLTFState &state, int p_accessor, bool p_for_vertex) { + + Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex); + PoolVector<Color> ret; + if (attribs.size() == 0) + return ret; + ERR_FAIL_COND_V(attribs.size() % 4 != 0, ret); + const double *attribs_ptr = attribs.ptr(); + int ret_size = attribs.size() / 4; + ret.resize(ret_size); + { + PoolVector<Color>::Write w = ret.write(); + for (int i = 0; i < ret_size; i++) { + w[i] = Color(attribs_ptr[i * 4 + 0], attribs_ptr[i * 4 + 1], attribs_ptr[i * 4 + 2], attribs_ptr[i * 4 + 3]); + } + } + return ret; +} +Vector<Quat> EditorSceneImporterGLTF::_decode_accessor_as_quat(GLTFState &state, int p_accessor, bool p_for_vertex) { + + 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(); + int ret_size = attribs.size() / 4; + ret.resize(ret_size); + { + for (int i = 0; i < ret_size; i++) { + ret[i] = Quat(attribs_ptr[i * 4 + 0], attribs_ptr[i * 4 + 1], attribs_ptr[i * 4 + 2], attribs_ptr[i * 4 + 3]); + } + } + return ret; +} +Vector<Transform2D> EditorSceneImporterGLTF::_decode_accessor_as_xform2d(GLTFState &state, int p_accessor, bool p_for_vertex) { + + 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[i][0] = Vector2(attribs[i * 4 + 0], attribs[i * 4 + 1]); + ret[i][1] = Vector2(attribs[i * 4 + 2], attribs[i * 4 + 3]); + } + return ret; +} + +Vector<Basis> EditorSceneImporterGLTF::_decode_accessor_as_basis(GLTFState &state, int p_accessor, bool p_for_vertex) { + + 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[i].set_axis(0, Vector3(attribs[i * 9 + 0], attribs[i * 9 + 1], attribs[i * 9 + 2])); + ret[i].set_axis(1, Vector3(attribs[i * 9 + 3], attribs[i * 9 + 4], attribs[i * 9 + 5])); + ret[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, int p_accessor, bool p_for_vertex) { + + 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[i].basis.set_axis(0, Vector3(attribs[i * 16 + 0], attribs[i * 16 + 1], attribs[i * 16 + 2])); + ret[i].basis.set_axis(1, Vector3(attribs[i * 16 + 4], attribs[i * 16 + 5], attribs[i * 16 + 6])); + ret[i].basis.set_axis(2, Vector3(attribs[i * 16 + 8], attribs[i * 16 + 9], attribs[i * 16 + 10])); + ret[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; + + Array meshes = state.json["meshes"]; + for (int i = 0; i < meshes.size(); i++) { + + Dictionary d = meshes[i]; + + GLTFMesh mesh; + mesh.mesh.instance(); + + ERR_FAIL_COND_V(!d.has("primitives"), ERR_PARSE_ERROR); + + Array primitives = d["primitives"]; + + 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")) { + int mode = p["mode"]; + ERR_FAIL_INDEX_V(mode, 7, ERR_FILE_CORRUPT); + static const Mesh::PrimitiveType primitives[7] = { + Mesh::PRIMITIVE_POINTS, + Mesh::PRIMITIVE_LINES, + Mesh::PRIMITIVE_LINE_LOOP, + Mesh::PRIMITIVE_LINE_STRIP, + Mesh::PRIMITIVE_TRIANGLES, + Mesh::PRIMITIVE_TRIANGLE_STRIP, + Mesh::PRIMITIVE_TRIANGLE_FAN, + }; + + primitive = primitives[mode]; + } + + 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")) { + PoolVector<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(); + PoolVector<float>::Write w = weights.write(); + for (int i = 0; i < wc; i += 4) { + float total = 0.0; + total += w[i + 0]; + total += w[i + 1]; + total += w[i + 2]; + total += w[i + 3]; + if (total > 0.0) { + w[i + 0] /= total; + w[i + 1] /= total; + w[i + 2] /= total; + w[i + 3] /= total; + } + } + } + array[Mesh::ARRAY_WEIGHTS] = weights; + } + + if (p.has("indices")) { + + PoolVector<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 + + int is = indices.size(); + PoolVector<int>::Write w = indices.write(); + for (int i = 0; i < is; i += 3) { + SWAP(w[i + 1], w[i + 2]); + } + } + array[Mesh::ARRAY_INDEX] = indices; + } else if (primitive == Mesh::PRIMITIVE_TRIANGLES) { + //generate indices because they need to be swapped for CW/CCW + PoolVector<Vector3> vertices = array[Mesh::ARRAY_VERTEX]; + ERR_FAIL_COND_V(vertices.size() == 0, ERR_PARSE_ERROR); + PoolVector<int> indices; + int vs = vertices.size(); + indices.resize(vs); + { + PoolVector<int>::Write w = indices.write(); + for (int i = 0; i < vs; i += 3) { + w[i] = i; + w[i + 1] = i + 2; + w[i + 2] = i + 1; + } + } + array[Mesh::ARRAY_INDEX] = indices; + } + + bool generated_tangents = false; + Variant erased_indices; + + if (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL")) { + //must generate mikktspace tangents.. ergh.. + Ref<SurfaceTool> st; + st.instance(); + st->create_from_triangle_arrays(array); + if (p.has("targets")) { + //morph targets should not be reindexed, as array size might differ + //removing indices is the best bet here + st->deindex(); + erased_indices = a[Mesh::ARRAY_INDEX]; + a[Mesh::ARRAY_INDEX] = Variant(); + } + st->generate_tangents(); + array = st->commit_to_arrays(); + generated_tangents = true; + } + + Array morphs; + //blend shapes + if (p.has("targets")) { + print_line("has targets!"); + Array targets = p["targets"]; + + if (j == 0) { + for (int k = 0; k < targets.size(); k++) { + mesh.mesh->add_blend_shape(String("morph_") + itos(k)); + } + } + + for (int k = 0; k < targets.size(); k++) { + + 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")) { + array_copy[Mesh::ARRAY_VERTEX] = _decode_accessor_as_vec3(state, t["POSITION"], true); + } + if (t.has("NORMAL")) { + array_copy[Mesh::ARRAY_NORMAL] = _decode_accessor_as_vec3(state, t["NORMAL"], true); + } + if (t.has("TANGENT")) { + PoolVector<Vector3> tangents_v3 = _decode_accessor_as_vec3(state, t["TANGENT"], true); + PoolVector<float> tangents_v4; + PoolVector<float> src_tangents = array[Mesh::ARRAY_TANGENT]; + ERR_FAIL_COND_V(src_tangents.size() == 0, ERR_PARSE_ERROR); + + { + + int size4 = src_tangents.size(); + tangents_v4.resize(size4); + PoolVector<float>::Write w4 = tangents_v4.write(); + + PoolVector<Vector3>::Read r3 = tangents_v3.read(); + PoolVector<float>::Read r4 = src_tangents.read(); + + for (int l = 0; l < size4 / 4; l++) { + + w4[l * 4 + 0] = r3[l].x; + w4[l * 4 + 1] = r3[l].y; + w4[l * 4 + 2] = r3[l].z; + w4[l * 4 + 3] = r4[l * 4 + 3]; //copy flip value + } + } + + array_copy[Mesh::ARRAY_TANGENT] = tangents_v4; + } + + if (generated_tangents) { + Ref<SurfaceTool> st; + st.instance(); + array_copy[Mesh::ARRAY_INDEX] = erased_indices; //needed for tangent generation, erased by deindex + 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); + + if (p.has("material")) { + int material = p["material"]; + ERR_FAIL_INDEX_V(material, state.materials.size(), ERR_FILE_CORRUPT); + Ref<Material> mat = state.materials[material]; + + mesh.mesh->surface_set_material(mesh.mesh->get_surface_count() - 1, mat); + } + } + + if (d.has("weights")) { + Array weights = d["weights"]; + ERR_FAIL_COND_V(mesh.mesh->get_blend_shape_count() != weights.size(), ERR_PARSE_ERROR); + mesh.blend_weights.resize(weights.size()); + for (int j = 0; j < weights.size(); j++) { + mesh.blend_weights[j] = weights[j]; + } + } + + state.meshes.push_back(mesh); + } + + print_line("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; + + Array images = state.json["images"]; + for (int i = 0; i < images.size(); i++) { + + Dictionary d = images[i]; + + String mimetype; + if (d.has("mimeType")) { + mimetype = d["mimeType"]; + } + + Vector<uint8_t> data; + const uint8_t *data_ptr = NULL; + int data_size = 0; + + if (d.has("uri")) { + String uri = d["uri"]; + + if (uri.findn("data:application/octet-stream;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<Texture> texture = ResourceLoader::load(uri); + state.images.push_back(texture); + continue; + } + } + + if (d.has("bufferView")) { + int bvi = d["bufferView"]; + + ERR_FAIL_INDEX_V(bvi, state.buffer_views.size(), ERR_PARAMETER_RANGE_ERROR); + + GLTFBufferView &bv = state.buffer_views[bvi]; + + int 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 + 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("jpg") != -1) { + //is a jpg + 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_line("total images: " + itos(state.images.size())); + + return OK; +} + +Error EditorSceneImporterGLTF::_parse_textures(GLTFState &state) { + + if (!state.json.has("textures")) + return OK; + + Array textures = state.json["textures"]; + for (int i = 0; i < textures.size(); i++) { + + 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<Texture> EditorSceneImporterGLTF::_get_texture(GLTFState &state, int p_texture) { + ERR_FAIL_INDEX_V(p_texture, state.textures.size(), Ref<Texture>()); + int image = state.textures[p_texture].src_image; + + ERR_FAIL_INDEX_V(image, state.images.size(), Ref<Texture>()); + + return state.images[image]; +} + +Error EditorSceneImporterGLTF::_parse_materials(GLTFState &state) { + + if (!state.json.has("materials")) + return OK; + + Array materials = state.json["materials"]; + for (int i = 0; i < materials.size(); i++) { + + Dictionary d = materials[i]; + + Ref<SpatialMaterial> material; + material.instance(); + if (d.has("name")) { + material->set_name(d["name"]); + } + + if (d.has("pbrMetallicRoughness")) { + + Dictionary mr = d["pbrMetallicRoughness"]; + if (mr.has("baseColorFactor")) { + Array arr = mr["baseColorFactor"]; + ERR_FAIL_COND_V(arr.size() != 4, ERR_PARSE_ERROR); + Color c = Color(arr[0], arr[1], arr[2], arr[3]).to_srgb(); + + material->set_albedo(c); + } + + if (mr.has("baseColorTexture")) { + Dictionary bct = mr["baseColorTexture"]; + if (bct.has("index")) { + material->set_texture(SpatialMaterial::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"]); + } + if (mr.has("roughnessFactor")) { + + material->set_roughness(mr["roughnessFactor"]); + } + + if (mr.has("metallicRoughnessTexture")) { + Dictionary bct = mr["metallicRoughnessTexture"]; + if (bct.has("index")) { + Ref<Texture> t = _get_texture(state, bct["index"]); + material->set_texture(SpatialMaterial::TEXTURE_METALLIC, t); + material->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_RED); + material->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, t); + material->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GREEN); + if (!mr.has("metallicFactor")) { + material->set_metallic(1); + } + if (!mr.has("roughnessFactor")) { + material->set_roughness(1); + } + } + } + } + + if (d.has("normalTexture")) { + Dictionary bct = d["normalTexture"]; + if (bct.has("index")) { + material->set_texture(SpatialMaterial::TEXTURE_NORMAL, _get_texture(state, bct["index"])); + material->set_feature(SpatialMaterial::FEATURE_NORMAL_MAPPING, true); + } + if (bct.has("scale")) { + material->set_normal_scale(bct["scale"]); + } + } + if (d.has("occlusionTexture")) { + Dictionary bct = d["occlusionTexture"]; + if (bct.has("index")) { + material->set_texture(SpatialMaterial::TEXTURE_AMBIENT_OCCLUSION, _get_texture(state, bct["index"])); + material->set_feature(SpatialMaterial::FEATURE_AMBIENT_OCCLUSION, true); + } + } + + if (d.has("emissiveFactor")) { + Array arr = d["emissiveFactor"]; + ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR); + Color c = Color(arr[0], arr[1], arr[2]).to_srgb(); + material->set_feature(SpatialMaterial::FEATURE_EMISSION, true); + + material->set_emission(c); + } + + if (d.has("emissiveTexture")) { + Dictionary bct = d["emissiveTexture"]; + if (bct.has("index")) { + material->set_texture(SpatialMaterial::TEXTURE_EMISSION, _get_texture(state, bct["index"])); + material->set_feature(SpatialMaterial::FEATURE_EMISSION, true); + material->set_emission(Color(0, 0, 0)); + } + } + + if (d.has("doubleSided")) { + bool ds = d["doubleSided"]; + if (ds) { + material->set_cull_mode(SpatialMaterial::CULL_DISABLED); + } + } + + if (d.has("alphaMode")) { + String am = d["alphaMode"]; + if (am != "OPAQUE") { + material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); + } + } + + state.materials.push_back(material); + } + + print_line("total materials: " + itos(state.materials.size())); + + return OK; +} + +Error EditorSceneImporterGLTF::_parse_skins(GLTFState &state) { + + if (!state.json.has("skins")) + return OK; + + Array skins = state.json["skins"]; + for (int i = 0; i < skins.size(); i++) { + + Dictionary d = skins[i]; + + GLTFSkin skin; + + ERR_FAIL_COND_V(!d.has("joints"), ERR_PARSE_ERROR); + + Array joints = d["joints"]; + Vector<Transform> bind_matrices; + + if (d.has("inverseBindMatrices")) { + bind_matrices = _decode_accessor_as_xform(state, d["inverseBindMatrices"], false); + ERR_FAIL_COND_V(bind_matrices.size() != joints.size(), ERR_PARSE_ERROR); + } + + for (int j = 0; j < joints.size(); j++) { + int index = joints[j]; + ERR_FAIL_INDEX_V(index, state.nodes.size(), ERR_PARSE_ERROR); + state.nodes[index]->joint_skin = state.skins.size(); + state.nodes[index]->joint_bone = j; + GLTFSkin::Bone bone; + bone.node = index; + if (bind_matrices.size()) { + bone.inverse_bind = bind_matrices[j]; + } + + skin.bones.push_back(bone); + } + + print_line("skin has skeleton? " + itos(d.has("skeleton"))); + if (d.has("skeleton")) { + int skeleton = d["skeleton"]; + ERR_FAIL_INDEX_V(skeleton, state.nodes.size(), ERR_PARSE_ERROR); + state.nodes[skeleton]->skeleton_skin = state.skins.size(); + print_line("setting skeleton skin to" + itos(skeleton)); + skin.skeleton = skeleton; + } + + if (d.has("name")) { + skin.name = d["name"]; + } + + //locate the right place to put a Skeleton node + + if (state.skin_users.has(i)) { + Vector<int> users = state.skin_users[i]; + int skin_node = -1; + for (int j = 0; j < users.size(); j++) { + int user = state.nodes[users[j]]->parent; //always go from parent + if (j == 0) { + skin_node = user; + } else if (skin_node != -1) { + bool found = false; + while (skin_node >= 0) { + + int cuser = user; + while (cuser != -1) { + if (cuser == skin_node) { + found = true; + break; + } + cuser = state.nodes[skin_node]->parent; + } + if (found) + break; + skin_node = state.nodes[skin_node]->parent; + } + + if (!found) { + skin_node = -1; //just leave where it is + } + + //find a common parent + } + } + + if (skin_node != -1) { + for (int j = 0; j < users.size(); j++) { + state.nodes[users[j]]->child_of_skeleton = i; + } + + state.nodes[skin_node]->skeleton_children.push_back(i); + } + state.skins.push_back(skin); + } + } + print_line("total skins: " + itos(state.skins.size())); + + //now + + return OK; +} + +Error EditorSceneImporterGLTF::_parse_cameras(GLTFState &state) { + + if (!state.json.has("cameras")) + return OK; + + Array cameras = state.json["cameras"]; + + for (int i = 0; i < cameras.size(); i++) { + + Dictionary d = cameras[i]; + + GLTFCamera camera; + ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR); + String type = d["type"]; + if (type == "orthographic") { + + camera.perspective = false; + if (d.has("orthographic")) { + 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")) { + Dictionary ppt = d["perspective"]; + camera.fov_size = ppt["yfov"]; + camera.zfar = ppt["zfar"]; + camera.znear = ppt["znear"]; + } else { + camera.fov_size = 10; + } + } else { + ERR_EXPLAIN("Camera should be in 'orthographic' or 'perspective'"); + ERR_FAIL_V(ERR_PARSE_ERROR); + } + + state.cameras.push_back(camera); + } + + print_line("total cameras: " + itos(state.cameras.size())); +} + +Error EditorSceneImporterGLTF::_parse_animations(GLTFState &state) { + + if (!state.json.has("animations")) + return OK; + + Array animations = state.json["animations"]; + + for (int i = 0; i < animations.size(); i++) { + + 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")) { + animation.name = d["name"]; + } + + for (int j = 0; j < channels.size(); j++) { + + Dictionary c = channels[j]; + if (!c.has("target")) + continue; + + Dictionary t = c["target"]; + if (!t.has("node") || !t.has("path")) { + continue; + } + + ERR_FAIL_COND_V(!c.has("sampler"), ERR_PARSE_ERROR); + int sampler = c["sampler"]; + ERR_FAIL_INDEX_V(sampler, samplers.size(), ERR_PARSE_ERROR); + + int node = t["node"]; + String path = t["path"]; + + ERR_FAIL_INDEX_V(node, state.nodes.size(), ERR_PARSE_ERROR); + + GLTFAnimation::Track *track = NULL; + + if (!animation.tracks.has(node)) { + animation.tracks[node] = GLTFAnimation::Track(); + } + + track = &animation.tracks[node]; + + Dictionary s = samplers[sampler]; + + ERR_FAIL_COND_V(!s.has("input"), ERR_PARSE_ERROR); + ERR_FAIL_COND_V(!s.has("output"), ERR_PARSE_ERROR); + + int input = s["input"]; + int output = s["output"]; + + GLTFAnimation::Interpolation interp = GLTFAnimation::INTERP_LINEAR; + if (s.has("interpolation")) { + 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; + } else if (in == "CUBICSPLINE") { + interp = GLTFAnimation::INTERP_CUBIC_SPLINE; + } + } + + print_line("path: " + path); + PoolVector<float> times = _decode_accessor_as_floats(state, input, false); + if (path == "translation") { + PoolVector<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") { + 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") { + PoolVector<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") { + PoolVector<float> weights = _decode_accessor_as_floats(state, output, false); + + ERR_FAIL_INDEX_V(state.nodes[node]->mesh, state.meshes.size(), ERR_PARSE_ERROR); + GLTFMesh *mesh = &state.meshes[state.nodes[node]->mesh]; + ERR_FAIL_COND_V(mesh->blend_weights.size() == 0, ERR_PARSE_ERROR); + int wc = mesh->blend_weights.size(); + + track->weight_tracks.resize(wc); + + int wlen = weights.size() / wc; + PoolVector<float>::Read r = weights.read(); + 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[l] = r[l * wc + k]; + } + + cf.values = wdata; + track->weight_tracks[k] = cf; + } + } else { + WARN_PRINTS("Invalid path: " + path); + } + } + + state.animations.push_back(animation); + } + + print_line("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]; + if (n->name == "") { + if (n->mesh >= 0) { + n->name = "Mesh"; + } else if (n->joint_skin >= 0) { + n->name = "Bone"; + } else { + n->name = "Node"; + } + } + + n->name = _gen_unique_name(state, n->name); + } +} + +void EditorSceneImporterGLTF::_generate_node(GLTFState &state, int p_node, Node *p_parent, Node *p_owner, Vector<Skeleton *> &skeletons) { + ERR_FAIL_INDEX(p_node, state.nodes.size()); + + GLTFNode *n = state.nodes[p_node]; + Spatial *node; + + if (n->mesh >= 0) { + ERR_FAIL_INDEX(n->mesh, state.meshes.size()); + MeshInstance *mi = memnew(MeshInstance); + const GLTFMesh &mesh = state.meshes[n->mesh]; + mi->set_mesh(mesh.mesh); + 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]); + } + + node = mi; + } else if (n->camera >= 0) { + ERR_FAIL_INDEX(n->camera, state.cameras.size()); + Camera *camera = memnew(Camera); + + const GLTFCamera &c = state.cameras[n->camera]; + if (c.perspective) { + camera->set_perspective(c.fov_size, c.znear, c.znear); + } else { + camera->set_orthogonal(c.fov_size, c.znear, c.znear); + } + + node = camera; + } else { + node = memnew(Spatial); + } + + node->set_name(n->name); + + if (n->child_of_skeleton >= 0) { + //move skeleton around and place it on node, as the node _is_ a skeleton. + Skeleton *s = skeletons[n->child_of_skeleton]; + p_parent = s; + } + + p_parent->add_child(node); + node->set_owner(p_owner); + node->set_transform(n->xform); + + n->godot_node = node; + + for (int i = 0; i < n->skeleton_children.size(); i++) { + + Skeleton *s = skeletons[n->skeleton_children[i]]; + s->get_parent()->remove_child(s); + node->add_child(s); + s->set_owner(p_owner); + } + + for (int i = 0; i < n->children.size(); i++) { + if (state.nodes[n->children[i]]->joint_skin >= 0) { + _generate_bone(state, n->children[i], skeletons, -1); + } else { + _generate_node(state, n->children[i], node, p_owner, skeletons); + } + } +} + +void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, int p_parent_bone) { + ERR_FAIL_INDEX(p_node, state.nodes.size()); + + GLTFNode *n = state.nodes[p_node]; + + ERR_FAIL_COND(n->joint_skin < 0); + + int bone_index = skeletons[n->joint_skin]->get_bone_count(); + skeletons[n->joint_skin]->add_bone(n->name); + if (p_parent_bone >= 0) { + skeletons[n->joint_skin]->set_bone_parent(bone_index, p_parent_bone); + } + skeletons[n->joint_skin]->set_bone_rest(bone_index, state.skins[n->joint_skin].bones[n->joint_bone].inverse_bind.affine_inverse()); + + n->godot_node = skeletons[n->joint_skin]; + n->godot_bone_index = bone_index; + + for (int i = 0; i < n->children.size(); i++) { + ERR_CONTINUE(state.nodes[n->children[i]]->joint_skin < 0); + _generate_bone(state, n->children[i], skeletons, bone_index); + } +} + +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) { + + float t2 = t * t; + float t3 = t2 * t; + + return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * 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. */ + real_t omt = (1.0 - t); + real_t omt2 = omt * omt; + real_t omt3 = omt2 * omt; + real_t t2 = t * t; + 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, float c) const { + + return a.slerp(b, c); + } + + Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, float c) { + + return p1.slerp(p2, c); + } + + Quat bezier(Quat start, Quat control_1, Quat control_2, Quat end, float t) { + return start.slerp(end, t); + } +}; + +template <class T> +T EditorSceneImporterGLTF::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, 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]; + } + + 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]; + } + + 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]; + } + + float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); + + T from = p_values[idx * 3 + 1]; + T c1 = from + p_values[idx * 3 + 0]; + T to = p_values[idx * 3 + 3]; + T c2 = to + p_values[idx * 3 + 2]; + + return interp.bezier(from, c1, c2, to, c); + + } break; + } + + ERR_FAIL_V(p_values[0]); +} + +void EditorSceneImporterGLTF::_import_animation(GLTFState &state, AnimationPlayer *ap, int index, int bake_fps, Vector<Skeleton *> skeletons) { + + const GLTFAnimation &anim = state.animations[index]; + + String name = anim.name; + if (name == "") { + name = _gen_unique_name(state, "Animation"); + } + + Ref<Animation> animation; + animation.instance(); + animation->set_name(name); + + 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; + + GLTFNode *node = state.nodes[E->key()]; + ERR_CONTINUE(!node->godot_node); + + if (node->godot_bone_index >= 0) { + Skeleton *sk = (Skeleton *)node->godot_node; + String path = ap->get_parent()->get_path_to(sk); + String bone = sk->get_bone_name(node->godot_bone_index); + node_path = path + ":" + bone; + } else { + node_path = ap->get_parent()->get_path_to(node->godot_node); + } + + float length = 0; + + 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]); + } + } + + animation->set_length(length); + + 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); + //first determine animation length + + 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; + } + + 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->godot_bone_index >= 0) { + + Transform xform; + xform.basis = Basis(rot); + xform.basis.scale(scale); + xform.origin = pos; + + Skeleton *skeleton = skeletons[node->joint_skin]; + int bone = node->godot_bone_index; + xform = skeleton->get_bone_rest(bone).affine_inverse() * xform; + + rot = xform.basis; + 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]; + String prop = "blend_shapes/" + mesh.mesh->get_blend_shape_name(i); + node_path = String(node_path) + ":" + prop; + + int track_idx = animation->get_track_count(); + animation->add_track(Animation::TYPE_VALUE); + animation->track_set_path(track_idx, node_path); + + if (track.weight_tracks[i].interpolation <= GLTFAnimation::INTERP_STEP) { + animation->track_set_interpolation_type(track_idx, track.weight_tracks[i].interpolation == GLTFAnimation::INTERP_STEP ? Animation::INTERPOLATION_NEAREST : Animation::INTERPOLATION_NEAREST); + for (int j = 0; j < track.weight_tracks[i].times.size(); j++) { + float t = track.weight_tracks[i].times[j]; + float w = track.weight_tracks[i].values[j]; + animation->track_insert_key(track_idx, t, w); + } + } else { + //must bake, apologies. + float increment = 1.0 / float(bake_fps); + float time = 0.0; + + bool last = false; + while (true) { + + float value = _interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, track.weight_tracks[i].interpolation); + if (last) { + break; + } + time += increment; + if (time >= length) { + last = true; + time = length; + } + } + } + } + } + + ap->add_animation(name, animation); +} + +Spatial *EditorSceneImporterGLTF::_generate_scene(GLTFState &state, int p_bake_fps) { + + Spatial *root = memnew(Spatial); + root->set_name(state.scene_name); + //generate skeletons + Vector<Skeleton *> skeletons; + for (int i = 0; i < state.skins.size(); i++) { + Skeleton *s = memnew(Skeleton); + String name = state.skins[i].name; + if (name == "") { + name = _gen_unique_name(state, "Skeleton"); + } + s->set_name(name); + root->add_child(s); + s->set_owner(root); + skeletons.push_back(s); + } + for (int i = 0; i < state.root_nodes.size(); i++) { + if (state.nodes[state.root_nodes[i]]->joint_skin >= 0) { + _generate_bone(state, state.root_nodes[i], skeletons, -1); + } else { + _generate_node(state, state.root_nodes[i], root, root, skeletons); + } + } + + for (int i = 0; i < skeletons.size(); i++) { + skeletons[i]->localize_rests(); + } + + 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, skeletons); + } + } + + 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 NULL; + } else { + //text file + Error err = _parse_json(p_path, state); + if (err) + return NULL; + } + + ERR_FAIL_COND_V(!state.json.has("asset"), NULL); + + Dictionary asset = state.json["asset"]; + + ERR_FAIL_COND_V(!asset.has("version"), NULL); + + String version = asset["version"]; + + state.major_version = version.get_slice(".", 0).to_int(); + state.minor_version = version.get_slice(".", 1).to_int(); + + /* STEP 0 PARSE SCENE */ + Error err = _parse_scenes(state); + if (err != OK) + return NULL; + + /* STEP 1 PARSE NODES */ + err = _parse_nodes(state); + if (err != OK) + return NULL; + + /* STEP 2 PARSE BUFFERS */ + err = _parse_buffers(state, p_path.get_base_dir()); + if (err != OK) + return NULL; + + /* STEP 3 PARSE BUFFER VIEWS */ + err = _parse_buffer_views(state); + if (err != OK) + return NULL; + + /* STEP 4 PARSE ACCESSORS */ + err = _parse_accessors(state); + if (err != OK) + return NULL; + + /* STEP 5 PARSE IMAGES */ + err = _parse_images(state, p_path.get_base_dir()); + if (err != OK) + return NULL; + + /* STEP 6 PARSE TEXTURES */ + err = _parse_textures(state); + if (err != OK) + return NULL; + + /* STEP 7 PARSE TEXTURES */ + err = _parse_materials(state); + if (err != OK) + return NULL; + + /* STEP 8 PARSE MESHES (we have enough info now) */ + err = _parse_meshes(state); + if (err != OK) + return NULL; + + /* STEP 9 PARSE SKINS */ + err = _parse_skins(state); + if (err != OK) + return NULL; + + /* STEP 10 PARSE CAMERAS */ + err = _parse_cameras(state); + if (err != OK) + return NULL; + + /* STEP 11 PARSE ANIMATIONS */ + err = _parse_animations(state); + if (err != OK) + return NULL; + + /* STEP 12 ASSIGN SCENE NAMES */ + _assign_scene_names(state); + + /* STEP 13 MAKE SCENE! */ + Spatial *scene = _generate_scene(state, p_bake_fps); + + return scene; +} + +Ref<Animation> EditorSceneImporterGLTF::import_animation(const String &p_path, uint32_t p_flags) { + + return Ref<Animation>(); +} + +EditorSceneImporterGLTF::EditorSceneImporterGLTF() { +} diff --git a/editor/import/editor_scene_importer_gltf.h b/editor/import/editor_scene_importer_gltf.h new file mode 100644 index 0000000000..d9479fae6f --- /dev/null +++ b/editor/import/editor_scene_importer_gltf.h @@ -0,0 +1,304 @@ +#ifndef EDITOR_SCENE_IMPORTER_GLTF_H +#define EDITOR_SCENE_IMPORTER_GLTF_H + +#include "editor/import/resource_importer_scene.h" +#include "scene/3d/skeleton.h" +#include "scene/3d/spatial.h" + +class AnimationPlayer; + +class EditorSceneImporterGLTF : public EditorSceneImporter { + + GDCLASS(EditorSceneImporterGLTF, EditorSceneImporter); + + 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(uint32_t p_component); + int _get_component_type_size(int component_type); + + enum GLTFType { + TYPE_SCALAR, + TYPE_VEC2, + TYPE_VEC3, + TYPE_VEC4, + TYPE_MAT2, + TYPE_MAT3, + TYPE_MAT4, + }; + + String _get_type_name(GLTFType p_component); + + struct GLTFNode { + //matrices need to be transformed to this + int parent; + + Transform xform; + String name; + Node *godot_node; + int godot_bone_index; + + int mesh; + int camera; + int skin; + int skeleton_skin; + int child_of_skeleton; // put as children of skeleton + Vector<int> skeleton_children; //skeleton put as children of this + + int joint_skin; + int joint_bone; + + //keep them for animation + Vector3 translation; + Quat rotation; + Vector3 scale; + + Vector<int> children; + + GLTFNode() { + godot_node = NULL; + godot_bone_index = -1; + joint_skin = -1; + joint_bone = -1; + child_of_skeleton = -1; + skeleton_skin = -1; + mesh = -1; + camera = -1; + parent = -1; + scale = Vector3(1, 1, 1); + } + }; + + struct GLTFBufferView { + + int buffer; + int byte_offset; + int byte_length; + int byte_stride; + bool indices; + //matrices need to be transformed to this + + GLTFBufferView() { + buffer = 0; + byte_offset = 0; + byte_length = 0; + byte_stride = 0; + indices = false; + } + }; + + struct GLTFAccessor { + + int buffer_view; + int byte_offset; + int component_type; + bool normalized; + int count; + GLTFType type; + float min; + float max; + int sparse_count; + int sparse_indices_buffer_view; + int sparse_indices_byte_offset; + int sparse_indices_component_type; + int sparse_values_buffer_view; + int sparse_values_byte_offset; + + //matrices need to be transformed to this + + GLTFAccessor() { + buffer_view = 0; + byte_offset = 0; + component_type = 0; + normalized = false; + count = 0; + min = 0; + max = 0; + sparse_count = 0; + sparse_indices_byte_offset = 0; + sparse_values_byte_offset = 0; + } + }; + struct GLTFTexture { + int src_image; + }; + + struct GLTFSkin { + + String name; + struct Bone { + Transform inverse_bind; + int node; + }; + + int skeleton; + Vector<Bone> bones; + + //matrices need to be transformed to this + + GLTFSkin() { + skeleton = -1; + } + }; + + struct GLTFMesh { + Ref<ArrayMesh> mesh; + Vector<float> blend_weights; + }; + + struct GLTFCamera { + + bool perspective; + float fov_size; + float zfar; + float znear; + + GLTFCamera() { + perspective = true; + fov_size = 65; + zfar = 500; + znear = 0.1; + } + }; + + struct GLTFAnimation { + + 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; + + 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<Texture> > images; + + Vector<GLTFSkin> skins; + Vector<GLTFCamera> cameras; + + Set<String> unique_names; + + Vector<GLTFAnimation> animations; + + Map<int, Vector<int> > skin_users; //cache skin users + + ~GLTFState() { + for (int i = 0; i < nodes.size(); i++) { + memdelete(nodes[i]); + } + } + }; + + String _gen_unique_name(GLTFState &state, const String &p_name); + + Ref<Texture> _get_texture(GLTFState &state, int 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); + 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, int p_buffer_view, double *dst, int skip_every, int skip_bytes, int element_size, int count, GLTFType type, int component_count, int component_type, int component_size, bool normalized, int byte_offset, bool for_vertex); + Vector<double> _decode_accessor(GLTFState &state, int p_accessor, bool p_for_vertex); + PoolVector<float> _decode_accessor_as_floats(GLTFState &state, int p_accessor, bool p_for_vertex); + PoolVector<int> _decode_accessor_as_ints(GLTFState &state, int p_accessor, bool p_for_vertex); + PoolVector<Vector2> _decode_accessor_as_vec2(GLTFState &state, int p_accessor, bool p_for_vertex); + PoolVector<Vector3> _decode_accessor_as_vec3(GLTFState &state, int p_accessor, bool p_for_vertex); + PoolVector<Color> _decode_accessor_as_color(GLTFState &state, int p_accessor, bool p_for_vertex); + Vector<Quat> _decode_accessor_as_quat(GLTFState &state, int p_accessor, bool p_for_vertex); + Vector<Transform2D> _decode_accessor_as_xform2d(GLTFState &state, int p_accessor, bool p_for_vertex); + Vector<Basis> _decode_accessor_as_basis(GLTFState &state, int p_accessor, bool p_for_vertex); + Vector<Transform> _decode_accessor_as_xform(GLTFState &state, int p_accessor, bool p_for_vertex); + + void _generate_bone(GLTFState &state, int p_node, Vector<Skeleton *> &skeletons, int p_parent_bone); + void _generate_node(GLTFState &state, int p_node, Node *p_parent, Node *p_owner, Vector<Skeleton *> &skeletons); + void _import_animation(GLTFState &state, AnimationPlayer *ap, int index, int bake_fps, Vector<Skeleton *> skeletons); + + Spatial *_generate_scene(GLTFState &state, int p_bake_fps); + + 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); + + Error _parse_skins(GLTFState &state); + + Error _parse_cameras(GLTFState &state); + + Error _parse_animations(GLTFState &state); + + void _assign_scene_names(GLTFState &state); + + template <class T> + T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, GLTFAnimation::Interpolation p_interp); + +public: + virtual uint32_t get_import_flags() const; + virtual void get_extensions(List<String> *r_extensions) const; + virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps = NULL, Error *r_err = NULL); + virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags); + + EditorSceneImporterGLTF(); +}; + +#endif // EDITOR_SCENE_IMPORTER_GLTF_H diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index e260b1ea22..e3184a028e 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -634,7 +634,17 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c } ScriptLanguage::LookupResult result; - if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), p_symbol, script->get_path().get_base_dir(), base, result) == OK) { + if (p_symbol.is_resource_file()) { + List<String> scene_extensions; + ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions); + + if (scene_extensions.find(p_symbol.get_extension())) { + EditorNode::get_singleton()->load_scene(p_symbol); + } else { + EditorNode::get_singleton()->load_resource(p_symbol); + } + + } else if (script->get_language()->lookup_code(code_editor->get_text_edit()->get_text_for_lookup_completion(), p_symbol, script->get_path().get_base_dir(), base, result) == OK) { _goto_line(p_row); diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index d12b979121..e7bc8a4dab 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -330,6 +330,8 @@ PoolVector<Vector2> TileMapEditor::_bucket_fill(const Point2i &p_start, bool era if (id == TileMap::INVALID_CELL) return PoolVector<Vector2>(); + } else if (prev_id == TileMap::INVALID_CELL) { + return PoolVector<Vector2>(); } Rect2i r = node->get_item_rect(); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 87340b1fe8..d5a56f644f 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -573,7 +573,7 @@ void ProjectManager::_unhandled_input(const Ref<InputEvent> &p_ev) { switch (k->get_scancode()) { - case KEY_RETURN: { + case KEY_ENTER: { _open_project(); } break; @@ -935,10 +935,10 @@ void ProjectManager::_open_project_confirm() { List<String> args; - args.push_back("--path"); + args.push_back("-path"); args.push_back(path); - args.push_back("--editor"); + args.push_back("-editor"); String exec = OS::get_singleton()->get_executable_path(); @@ -977,7 +977,7 @@ void ProjectManager::_run_project_confirm() { List<String> args; - args.push_back("--path"); + args.push_back("-path"); args.push_back(path); String exec = OS::get_singleton()->get_executable_path(); diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index 42485317c1..6b02afe0c1 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -3858,7 +3858,7 @@ void PropertyEditor::_item_edited() { break; if (type == Variant::INT) - _edit_set(name, round(item->get_range(1)), refresh_all); + _edit_set(name, int64_t(round(item->get_range(1))), refresh_all); else _edit_set(name, item->get_range(1), refresh_all); } break; diff --git a/main/main.cpp b/main/main.cpp index c2058ee8d4..ed6ed019f4 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -127,62 +127,62 @@ static String unescape_cmdline(const String &p_str) { void Main::print_help(const char *p_binary) { OS::get_singleton()->print(VERSION_FULL_NAME " (c) 2008-2017 Juan Linietsky, Ariel Manzur.\n"); - OS::get_singleton()->print("Usage: %s [options] [path to scene or 'project.godot' file]\n", p_binary); + OS::get_singleton()->print("Usage: %s [options] [scene]\n", p_binary); OS::get_singleton()->print("Options:\n"); - OS::get_singleton()->print(" -h, --help Display this help message.\n"); - OS::get_singleton()->print(" --path <directory> Path to the project (<directory> must contain a 'project.godot' file).\n"); + OS::get_singleton()->print("\t-path [dir] : Path to a game, containing project.godot\n"); #ifdef TOOLS_ENABLED - OS::get_singleton()->print(" -e, --editor Bring up the editor instead of running the scene.\n"); + OS::get_singleton()->print("\t-e,-editor : Bring up the editor instead of running the scene.\n"); #endif - OS::get_singleton()->print(" --test <test> Run a test ("); + OS::get_singleton()->print("\t-test [test] : Run a test.\n"); + OS::get_singleton()->print("\t\t("); const char **test_names = tests_get_names(); const char *coma = ""; while (*test_names) { - OS::get_singleton()->print("%s'%s'", coma, *test_names); + OS::get_singleton()->print("%s%s", coma, *test_names); test_names++; coma = ", "; } - OS::get_singleton()->print(").\n"); - - OS::get_singleton()->print(" -r, --resolution <W>x<H> Request window resolution.\n"); - OS::get_singleton()->print(" -p, --position <X>,<Y> Request window position.\n"); - OS::get_singleton()->print(" -f, --fullscreen Request fullscreen mode.\n"); - OS::get_singleton()->print(" -m, --maximized Request a maximized window.\n"); - OS::get_singleton()->print(" -w, --windowed Request windowed mode.\n"); - OS::get_singleton()->print(" --video-driver <driver> Video driver ("); + OS::get_singleton()->print(")\n"); + + OS::get_singleton()->print("\t-r WIDTHxHEIGHT\t : Request Window Resolution\n"); + OS::get_singleton()->print("\t-p XxY\t : Request Window Position\n"); + OS::get_singleton()->print("\t-f\t\t : Request Fullscreen\n"); + OS::get_singleton()->print("\t-mx\t\t Request Maximized\n"); + OS::get_singleton()->print("\t-w\t\t Request Windowed\n"); + OS::get_singleton()->print("\t-vd DRIVER\t : Video Driver ("); for (int i = 0; i < OS::get_singleton()->get_video_driver_count(); i++) { if (i != 0) OS::get_singleton()->print(", "); - OS::get_singleton()->print("'%s'", OS::get_singleton()->get_video_driver_name(i)); + OS::get_singleton()->print("%s", OS::get_singleton()->get_video_driver_name(i)); } - OS::get_singleton()->print(").\n"); - OS::get_singleton()->print(" --low-dpi Force low-DPI mode (macOS only).\n"); + OS::get_singleton()->print(")\n"); + OS::get_singleton()->print("\t-ldpi\t : Force low-dpi mode (OSX Only)\n"); - OS::get_singleton()->print(" --audio-driver <driver> Audio driver ("); + OS::get_singleton()->print("\t-ad DRIVER\t : Audio Driver ("); for (int i = 0; i < OS::get_singleton()->get_audio_driver_count(); i++) { if (i != 0) OS::get_singleton()->print(", "); - OS::get_singleton()->print("'%s'", OS::get_singleton()->get_audio_driver_name(i)); + OS::get_singleton()->print("%s", OS::get_singleton()->get_audio_driver_name(i)); } - OS::get_singleton()->print(").\n"); - OS::get_singleton()->print(" --render-thread <mode> Render thread mode ('unsafe', 'safe', 'separate').\n"); - OS::get_singleton()->print(" -s, --script <script> Run a script.\n"); - OS::get_singleton()->print(" -d, --debug Debug (local stdout debugger).\n"); - OS::get_singleton()->print(" -r, --remote-debug <address> Remote debug (<ip>:<port> host address).\n"); - OS::get_singleton()->print(" --frame-delay <ms> Simulate high CPU load (delay each frame by <ms> milliseconds).\n"); - OS::get_singleton()->print(" --time-scale <scale> Force time scale (higher values are faster, 1.0 is normal speed).\n"); - OS::get_singleton()->print(" -b, --breakpoints Breakpoint list as source::line comma-separated pairs, no spaces (use %%20, %%2C, ... instead).\n"); - OS::get_singleton()->print(" -v, --verbose Use verbose stdout mode.\n"); - OS::get_singleton()->print(" -l, --language <locale> Use a specific locale (<locale> being a two-letter code).\n"); - OS::get_singleton()->print(" --remote-fs <host/IP>[:<port>] Remote filesystem.\n"); - OS::get_singleton()->print(" --remote-fs-password <password> Password for remote filesystem.\n"); + OS::get_singleton()->print(")\n"); + OS::get_singleton()->print("\t-rthread <mode>\t : Render Thread Mode ('unsafe', 'safe', 'separate').\n"); + OS::get_singleton()->print("\t-s,-script [script] : Run a script.\n"); + OS::get_singleton()->print("\t-d,-debug : Debug (local stdout debugger).\n"); + OS::get_singleton()->print("\t-rdebug ADDRESS : Remote debug (<ip>:<port> host address).\n"); + OS::get_singleton()->print("\t-fdelay [msec]: Simulate high CPU load (delay each frame by [msec]).\n"); + OS::get_singleton()->print("\t-timescale [msec]: Simulate high CPU load (delay each frame by [msec]).\n"); + OS::get_singleton()->print("\t-bp : breakpoint list as source::line comma separated pairs, no spaces (%%20,%%2C,etc instead).\n"); + OS::get_singleton()->print("\t-v : Verbose stdout mode\n"); + OS::get_singleton()->print("\t-lang [locale]: Use a specific locale\n"); + OS::get_singleton()->print("\t-rfs <host/ip>[:<port>] : Remote FileSystem.\n"); + OS::get_singleton()->print("\t-rfs_pass <password> : Password for Remote FileSystem.\n"); #ifdef TOOLS_ENABLED - OS::get_singleton()->print(" --doctool <file> Dump the whole engine API to <file> in XML format. If <file> exists, it will be merged.\n"); - OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n"); - OS::get_singleton()->print(" --export <target> Export the project using the given export target.\n"); + OS::get_singleton()->print("\t-doctool FILE: Dump the whole engine api to FILE in XML format. If FILE exists, it will be merged.\n"); + OS::get_singleton()->print("\t-nodocbase: Disallow dump the base types (used with -doctool).\n"); + OS::get_singleton()->print("\t-export [target] Export the project using given export target.\n"); #endif } @@ -281,14 +281,14 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph List<String>::Element *N = I->next(); - if (I->get() == "--noop") { + if (I->get() == "-noop") { // no op - } else if (I->get() == "-h" || I->get() == "--help" || I->get() == "/?") { // display help + } else if (I->get() == "-h" || I->get() == "--help" || I->get() == "/?") { // resolution goto error; - } else if (I->get() == "-r" || I->get() == "--resolution") { // force resolution + } else if (I->get() == "-r") { // resolution if (I->next()) { @@ -296,7 +296,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph if (vm.find("x") == -1) { // invalid parameter format - OS::get_singleton()->print("Invalid resolution argument: %s\n", vm.utf8().get_data()); + OS::get_singleton()->print("Invalid -r argument: %s\n", vm.utf8().get_data()); goto error; } @@ -305,7 +305,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph if (w == 0 || h == 0) { - OS::get_singleton()->print("Invalid resolution, width and height must be above 0\n"); + OS::get_singleton()->print("Invalid -r resolution, x and y must be >0\n"); goto error; } @@ -315,66 +315,66 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph N = I->next()->next(); } else { - OS::get_singleton()->print("Invalid resolution argument, needs resolution\n"); + OS::get_singleton()->print("Invalid -p argument, needs resolution\n"); goto error; } - } else if (I->get() == "-p" || I->get() == "--position") { // position + } else if (I->get() == "-p") { // position if (I->next()) { String vm = I->next()->get(); - if (vm.find(",") == -1) { // invalid parameter format + if (vm.find("x") == -1) { // invalid parameter format - OS::get_singleton()->print("Invalid position argument: %s\n", vm.utf8().get_data()); + OS::get_singleton()->print("Invalid -p argument: %s\n", vm.utf8().get_data()); goto error; } - int x = vm.get_slice(",", 0).to_int(); - int y = vm.get_slice(",", 1).to_int(); + int x = vm.get_slice("x", 0).to_int(); + int y = vm.get_slice("x", 1).to_int(); init_custom_pos = Point2(x, y); init_use_custom_pos = true; N = I->next()->next(); } else { - OS::get_singleton()->print("Invalid position argument, needs position\n"); + OS::get_singleton()->print("Invalid -r argument, needs position\n"); goto error; } - } else if (I->get() == "-m" || I->get() == "--maximized") { // force maximized window + } else if (I->get() == "-mx") { // video driver init_maximized = true; - } else if (I->get() == "-w" || I->get() == "--windowed") { // force windowed window + } else if (I->get() == "-w") { // video driver init_windowed = true; - } else if (I->get() == "--profile") { // enable profiler + } else if (I->get() == "-profile") { // video driver use_debug_profiler = true; - } else if (I->get() == "--video-driver") { // force video driver + } else if (I->get() == "-vd") { // video driver if (I->next()) { video_driver = I->next()->get(); N = I->next()->next(); } else { - OS::get_singleton()->print("Invalid --video-driver argument, needs driver name\n"); + OS::get_singleton()->print("Invalid -cd argument, needs driver name\n"); goto error; } - } else if (I->get() == "-l" || I->get() == "--language") { // language + } else if (I->get() == "-lang") { // language if (I->next()) { locale = I->next()->get(); N = I->next()->next(); } else { - OS::get_singleton()->print("Invalid language argument, needs language code\n"); + OS::get_singleton()->print("Invalid -lang argument, needs language code\n"); goto error; } - } else if (I->get() == "--low-dpi") { // force low DPI + } else if (I->get() == "-ldpi") { // language force_lowdpi = true; - } else if (I->get() == "--remote-fs") { // remote filesystem + } else if (I->get() == "-rfs") { // language if (I->next()) { @@ -383,7 +383,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else { goto error; } - } else if (I->get() == "--remote-fs-password") { // remote filesystem password + } else if (I->get() == "-rfs_pass") { // language if (I->next()) { @@ -392,7 +392,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else { goto error; } - } else if (I->get() == "--render-thread") { // rendering thread + } else if (I->get() == "-rthread") { // language if (I->next()) { @@ -408,7 +408,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph goto error; } - } else if (I->get() == "--audio-driver") { // audio driver + } else if (I->get() == "-ad") { // video driver if (I->next()) { @@ -418,22 +418,22 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph goto error; } - } else if (I->get() == "-f" || I->get() == "--fullscreen") { // force fullscreen + } else if (I->get() == "-f") { // fullscreen //video_mode.fullscreen=false; init_fullscreen = true; - } else if (I->get() == "-e" || I->get() == "--editor") { // starts editor + } else if (I->get() == "-e" || I->get() == "-editor") { // fonud editor editor = true; - } else if (I->get() == "--no-window") { // disable window creation + } else if (I->get() == "-nowindow") { // fullscreen OS::get_singleton()->set_no_window_mode(true); - } else if (I->get() == "--quiet") { // quieter output + } else if (I->get() == "-quiet") { // fullscreen quiet_stdout = true; - } else if (I->get() == "-v" || I->get() == "--verbose") { // verbose output + } else if (I->get() == "-v") { // fullscreen OS::get_singleton()->_verbose_stdout = true; - } else if (I->get() == "--path") { // set path of project to start or edit + } else if (I->get() == "-path") { // resolution if (I->next()) { @@ -464,7 +464,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph #ifdef TOOLS_ENABLED editor = true; #endif - } else if (I->get() == "-b" || I->get() == "--breakpoints") { // add breakpoints + } else if (I->get() == "-bp") { // /breakpoints if (I->next()) { @@ -475,7 +475,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph goto error; } - } else if (I->get() == "--frame-delay") { // force frame delay + } else if (I->get() == "-fdelay") { // resolution if (I->next()) { @@ -485,7 +485,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph goto error; } - } else if (I->get() == "--time-scale") { // force time scale + } else if (I->get() == "-timescale") { // resolution if (I->next()) { @@ -495,7 +495,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph goto error; } - } else if (I->get() == "--pack") { + } else if (I->get() == "-pack") { if (I->next()) { @@ -506,7 +506,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph goto error; }; - } else if (I->get() == "--main-pack") { + } else if (I->get() == "-main_pack") { if (I->next()) { @@ -517,15 +517,15 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph goto error; }; - } else if (I->get() == "--debug" || I->get() == "-d") { + } else if (I->get() == "-debug" || I->get() == "-d") { debug_mode = "local"; #ifdef DEBUG_ENABLED - } else if (I->get() == "--debug-collision") { + } else if (I->get() == "-debugcol" || I->get() == "-dc") { debug_collisions = true; - } else if (I->get() == "--debug-navigation") { + } else if (I->get() == "-debugnav" || I->get() == "-dn") { debug_navigation = true; #endif - } else if (I->get() == "--editor-scene") { + } else if (I->get() == "-editor_scene") { if (I->next()) { @@ -534,7 +534,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph goto error; } - } else if (I->get() == "-r" || I->get() == "--remote-debug") { + } else if (I->get() == "-rdebug") { if (I->next()) { debug_mode = "remote"; @@ -547,7 +547,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } else { goto error; } - } else if (I->get() == "--editor-pid") { + } else if (I->get() == "-epid") { if (I->next()) { int editor_pid = I->next()->get().to_int(); @@ -660,7 +660,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph } if (editor) { - main_args.push_back("--editor"); + main_args.push_back("-editor"); init_maximized = true; use_custom_res = false; } @@ -1057,13 +1057,13 @@ bool Main::start() { List<String> args = OS::get_singleton()->get_cmdline_args(); for (int i = 0; i < args.size(); i++) { //parameters that do not have an argument to the right - if (args[i] == "--no-docbase") { + if (args[i] == "-nodocbase") { doc_base = false; - } else if (args[i] == "--no-quit") { + } else if (args[i] == "-noquit") { noquit = true; - } else if (args[i] == "--editor" || args[i] == "-e") { + } else if (args[i] == "-editor" || args[i] == "-e") { editor = true; - } else if (args[i] == "-P" || args[i] == "--project-manager") { + } else if (args[i] == "-pm" || args[i] == "-project_manager") { project_manager_request = true; } else if (args[i].length() && args[i][0] != '-' && game_path == "") { game_path = args[i]; @@ -1071,27 +1071,27 @@ bool Main::start() { //parameters that have an argument to the right else if (i < (args.size() - 1)) { bool parsed_pair = true; - if (args[i] == "--doctool") { + if (args[i] == "-doctool") { doc_tool = args[i + 1]; for (int j = i + 2; j < args.size(); j++) removal_docs.push_back(args[j]); - } else if (args[i] == "--script" || args[i] == "-s") { + } else if (args[i] == "-script" || args[i] == "-s") { script = args[i + 1]; - } else if (args[i] == "--level" || args[i] == "-l") { + } else if (args[i] == "-level" || args[i] == "-l") { Engine::get_singleton()->_custom_level = args[i + 1]; - } else if (args[i] == "--test") { + } else if (args[i] == "-test") { test = args[i + 1]; - } else if (args[i] == "--export") { + } else if (args[i] == "-export") { editor = true; //needs editor _export_platform = args[i + 1]; - } else if (args[i] == "--export-debug") { + } else if (args[i] == "-export_debug") { editor = true; //needs editor _export_platform = args[i + 1]; export_debug = true; - } else if (args[i] == "--import") { + } else if (args[i] == "-import") { editor = true; //needs editor _import = args[i + 1]; - } else if (args[i] == "--import-script") { + } else if (args[i] == "-import_script") { editor = true; //needs editor _import_script = args[i + 1]; } else { @@ -1139,7 +1139,7 @@ bool Main::start() { if (_export_platform != "") { if (game_path == "") { String err = "Command line param "; - err += export_debug ? "--export-debug" : "--export"; + err += export_debug ? "-export_debug" : "-export"; err += " passed but no destination path given.\n"; err += "Please specify the binary's file path to export to. Aborting export."; ERR_PRINT(err.utf8().get_data()); diff --git a/methods.py b/methods.py index abd87c07d7..30a1f3caed 100644 --- a/methods.py +++ b/methods.py @@ -244,6 +244,7 @@ def build_glsl_header(filename): fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Color& p_color) { _FU GLfloat col[4]={p_color.r,p_color.g,p_color.b,p_color.a}; glUniform4fv(get_uniform(p_uniform),1,col); }\n\n") fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Vector2& p_vec2) { _FU GLfloat vec2[2]={p_vec2.x,p_vec2.y}; glUniform2fv(get_uniform(p_uniform),1,vec2); }\n\n") fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Vector3& p_vec3) { _FU GLfloat vec3[3]={p_vec3.x,p_vec3.y,p_vec3.z}; glUniform3fv(get_uniform(p_uniform),1,vec3); }\n\n") + fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Plane& p_plane) { _FU GLfloat plane[4]={p_plane.normal.x,p_plane.normal.y,p_plane.normal.z,p_plane.d}; glUniform4fv(get_uniform(p_uniform),1,plane); }\n\n") fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_a, float p_b) { _FU glUniform2f(get_uniform(p_uniform),p_a,p_b); }\n\n") fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_a, float p_b, float p_c) { _FU glUniform3f(get_uniform(p_uniform),p_a,p_b,p_c); }\n\n") fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, float p_a, float p_b, float p_c, float p_d) { _FU glUniform4f(get_uniform(p_uniform),p_a,p_b,p_c,p_d); }\n\n") diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index 158f7fd94d..07dba921b1 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -155,7 +155,6 @@ String GDNativeLibrary::get_active_library_path() const { } GDNative::GDNative() { - initialized = false; native_handle = NULL; } @@ -219,6 +218,9 @@ bool GDNative::initialize() { library_init); if (err || !library_init) { + OS::get_singleton()->close_dynamic_library(native_handle); + native_handle = NULL; + ERR_PRINT("Failed to obtain godot_gdnative_init symbol"); return false; } @@ -272,7 +274,11 @@ bool GDNative::terminate() { OS::get_singleton()->close_dynamic_library(native_handle); native_handle = NULL; - return false; + return true; +} + +bool GDNative::is_initialized() { + return (native_handle != NULL); } void GDNativeCallRegistry::register_native_call_type(StringName p_call_type, native_call_cb p_callback) { diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h index f02741f4e3..b03866f432 100644 --- a/modules/gdnative/gdnative.h +++ b/modules/gdnative/gdnative.h @@ -117,7 +117,6 @@ class GDNative : public Reference { GDCLASS(GDNative, Reference) Ref<GDNativeLibrary> library; - bool initialized; // TODO(karroffel): different platforms? WASM???? void *native_handle; diff --git a/modules/gdnative/godot/dictionary.cpp b/modules/gdnative/godot/dictionary.cpp index ce402fa008..b92c8125bb 100644 --- a/modules/gdnative/godot/dictionary.cpp +++ b/modules/gdnative/godot/dictionary.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include <godot/dictionary.h> +#include "core/variant.h" + #include "core/dictionary.h" #include "core/io/json.h" -#include "core/variant.h" #ifdef __cplusplus extern "C" { diff --git a/modules/gdnative/godot/node_path.cpp b/modules/gdnative/godot/node_path.cpp index e718a9e55f..f4179361be 100644 --- a/modules/gdnative/godot/node_path.cpp +++ b/modules/gdnative/godot/node_path.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include <godot/node_path.h> -#include "core/path_db.h" +#include "core/node_path.h" #include "core/variant.h" #ifdef __cplusplus diff --git a/modules/gdnative/godot/variant.cpp b/modules/gdnative/godot/variant.cpp index 506614583c..d814ef913c 100644 --- a/modules/gdnative/godot/variant.cpp +++ b/modules/gdnative/godot/variant.cpp @@ -433,7 +433,6 @@ godot_variant GDAPI godot_variant_call(godot_variant *p_self, const godot_string Variant *dest = (Variant *)&raw_dest; Variant::CallError error; memnew_placement_custom(dest, Variant, Variant(self->call(*method, args, p_argcount, error))); - *dest = self->call(StringName(*method), args, p_argcount, r_error); if (r_error) { r_error->error = (godot_variant_call_error_error)error.error; r_error->argument = error.argument; diff --git a/modules/nativescript/nativescript.cpp b/modules/nativescript/nativescript.cpp index ad746c9546..fb334e573c 100644 --- a/modules/nativescript/nativescript.cpp +++ b/modules/nativescript/nativescript.cpp @@ -1052,9 +1052,23 @@ void NativeScriptLanguage::unregister_script(NativeScript *script) { #endif } -#ifndef NO_THREADS +void NativeScriptLanguage::call_libraries_cb(const StringName &name) { + // library_gdnatives is modified only from the main thread, so it's safe not to use mutex here + for (Map<String, Ref<GDNative> >::Element *L = library_gdnatives.front(); L; L = L->next()) { + if (L->get()->is_initialized()) { + L->get()->call_native_raw( + _noarg_call_type, + name, + NULL, + 0, + NULL, + NULL); + } + } +} void NativeScriptLanguage::frame() { +#ifndef NO_THREADS if (has_objects_to_register) { MutexLock lock(mutex); for (Set<Ref<GDNativeLibrary> >::Element *L = libs_to_init.front(); L; L = L->next()) { @@ -1067,44 +1081,18 @@ void NativeScriptLanguage::frame() { scripts_to_register.clear(); has_objects_to_register = false; } +#endif + call_libraries_cb(_frame_call_name); } +#ifndef NO_THREADS + void NativeScriptLanguage::thread_enter() { - Vector<Ref<GDNative> > libs; - { - MutexLock lock(mutex); - for (Map<String, Ref<GDNative> >::Element *L = library_gdnatives.front(); L; L = L->next()) { - libs.push_back(L->get()); - } - } - for (int i = 0; i < libs.size(); ++i) { - libs[i]->call_native_raw( - _thread_cb_call_type, - _thread_enter_call_name, - NULL, - 0, - NULL, - NULL); - } + call_libraries_cb(_thread_enter_call_name); } void NativeScriptLanguage::thread_exit() { - Vector<Ref<GDNative> > libs; - { - MutexLock lock(mutex); - for (Map<String, Ref<GDNative> >::Element *L = library_gdnatives.front(); L; L = L->next()) { - libs.push_back(L->get()); - } - } - for (int i = 0; i < libs.size(); ++i) { - libs[i]->call_native_raw( - _thread_cb_call_type, - _thread_exit_call_name, - NULL, - 0, - NULL, - NULL); - } + call_libraries_cb(_thread_exit_call_name); } #endif // NO_THREADS diff --git a/modules/nativescript/nativescript.h b/modules/nativescript/nativescript.h index 95b4954171..c60effd0c1 100644 --- a/modules/nativescript/nativescript.h +++ b/modules/nativescript/nativescript.h @@ -220,7 +220,10 @@ private: void register_script(NativeScript *script); void unregister_script(NativeScript *script); + void call_libraries_cb(const StringName &name); + public: + // These two maps must only be touched on the main thread Map<String, Map<StringName, NativeScriptDesc> > library_classes; Map<String, Ref<GDNative> > library_gdnatives; @@ -229,9 +232,14 @@ public: const StringName _init_call_type = "nativescript_init"; const StringName _init_call_name = "godot_nativescript_init"; - const StringName _thread_cb_call_type = "godot_nativescript_thread_cb"; + const StringName _noarg_call_type = "nativescript_no_arg"; + + const StringName _frame_call_name = "godot_nativescript_frame"; + +#ifndef NO_THREADS const StringName _thread_enter_call_name = "godot_nativescript_thread_enter"; const StringName _thread_exit_call_name = "godot_nativescript_thread_exit"; +#endif NativeScriptLanguage(); ~NativeScriptLanguage(); @@ -245,9 +253,9 @@ public: #ifndef NO_THREADS virtual void thread_enter(); virtual void thread_exit(); +#endif virtual void frame(); -#endif virtual String get_name() const; virtual void init(); diff --git a/modules/nativescript/register_types.cpp b/modules/nativescript/register_types.cpp index dfa16d8a2a..c28b982884 100644 --- a/modules/nativescript/register_types.cpp +++ b/modules/nativescript/register_types.cpp @@ -62,13 +62,11 @@ void init_call_cb(void *p_handle, godot_string *p_proc_name, void *p_data, int p fn(args[0]); } -#ifndef NO_THREADS - typedef void (*native_script_empty_callback)(); -void thread_call_cb(void *p_handle, godot_string *p_proc_name, void *p_data, int p_num_args, void **args, void *r_ret) { +void noarg_call_cb(void *p_handle, godot_string *p_proc_name, void *p_data, int p_num_args, void **args, void *r_ret) { if (p_handle == NULL) { - ERR_PRINT("No valid library handle, can't call nativescript thread enter/exit callback"); + ERR_PRINT("No valid library handle, can't call nativescript callback"); return; } @@ -87,8 +85,6 @@ void thread_call_cb(void *p_handle, godot_string *p_proc_name, void *p_data, int fn(); } -#endif // NO_THREADS - ResourceFormatLoaderNativeScript *resource_loader_gdns = NULL; ResourceFormatSaverNativeScript *resource_saver_gdns = NULL; @@ -100,9 +96,7 @@ void register_nativescript_types() { ScriptServer::register_language(native_script_language); GDNativeCallRegistry::singleton->register_native_raw_call_type(native_script_language->_init_call_type, init_call_cb); -#ifndef NO_THREADS - GDNativeCallRegistry::singleton->register_native_raw_call_type(native_script_language->_thread_cb_call_type, thread_call_cb); -#endif + GDNativeCallRegistry::singleton->register_native_raw_call_type(native_script_language->_noarg_call_type, noarg_call_cb); resource_saver_gdns = memnew(ResourceFormatSaverNativeScript); ResourceSaver::add_resource_format_saver(resource_saver_gdns); diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 69aa10ebca..d5d8b8fe6e 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -2798,7 +2798,7 @@ public: r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD; return 0; } - *p_outputs[0] = subcall->call(VisualScriptLanguage::singleton->_subcall, p_inputs, input_args, r_error_str); + *p_outputs[0] = subcall->call(VisualScriptLanguage::singleton->_subcall, p_inputs, input_args, r_error); return 0; } }; diff --git a/platform/android/build.gradle.template b/platform/android/build.gradle.template index fd0907f820..1df56ce621 100644 --- a/platform/android/build.gradle.template +++ b/platform/android/build.gradle.template @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.1' + classpath 'com.android.tools.build:gradle:2.3.3' $$GRADLE_CLASSPATH$$ } } @@ -31,7 +31,7 @@ android { } compileSdkVersion 23 - buildToolsVersion "25.0.3" + buildToolsVersion "26.0.1" useLibrary 'org.apache.http.legacy' packagingOptions { diff --git a/platform/android/java/gradle.properties b/platform/android/java/gradle.properties new file mode 100644 index 0000000000..aac7c9b461 --- /dev/null +++ b/platform/android/java/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.jar b/platform/android/java/gradle/wrapper/gradle-wrapper.jar Binary files differindex 8c0fb64a86..13372aef5e 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.jar +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.jar diff --git a/platform/android/java/gradle/wrapper/gradle-wrapper.properties b/platform/android/java/gradle/wrapper/gradle-wrapper.properties index a11cc1b825..ee6901c9d7 100644 --- a/platform/android/java/gradle/wrapper/gradle-wrapper.properties +++ b/platform/android/java/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,6 @@ -#Fri May 12 08:50:03 KST 2017 +#Sat Jul 29 16:10:03 ICT 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip -org.gradle.jvmargs=-Xmx1536M +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip diff --git a/platform/android/java/gradlew b/platform/android/java/gradlew index 91a7e269e1..9d82f78915 100755 --- a/platform/android/java/gradlew +++ b/platform/android/java/gradlew @@ -42,11 +42,6 @@ case "`uname`" in ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" @@ -61,9 +56,9 @@ while [ -h "$PRG" ] ; do fi done SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- +cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" -cd "$SAVED" >&- +cd "$SAVED" >/dev/null CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -114,6 +109,7 @@ fi if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` diff --git a/platform/haiku/key_mapping_haiku.cpp b/platform/haiku/key_mapping_haiku.cpp index 9df7b2f047..3db31fa3e4 100644 --- a/platform/haiku/key_mapping_haiku.cpp +++ b/platform/haiku/key_mapping_haiku.cpp @@ -83,7 +83,7 @@ static _HaikuTranslatePair _fn_to_keycode[] = { static _HaikuTranslatePair _hb_to_keycode[] = { { KEY_BACKSPACE, B_BACKSPACE }, { KEY_TAB, B_TAB }, - { KEY_RETURN, B_RETURN }, + { KEY_ENTER, B_RETURN }, { KEY_CAPSLOCK, B_CAPS_LOCK }, { KEY_ESCAPE, B_ESCAPE }, { KEY_SPACE, B_SPACE }, diff --git a/platform/javascript/dom_keys.h b/platform/javascript/dom_keys.h index 979731d157..4b8b764c45 100644 --- a/platform/javascript/dom_keys.h +++ b/platform/javascript/dom_keys.h @@ -249,7 +249,7 @@ int dom2godot_scancode(int dom_keycode) { case DOM_VK_RETURN: case DOM_VK_ENTER: // unused according to MDN - return KEY_RETURN; + return KEY_ENTER; case DOM_VK_SHIFT: return KEY_SHIFT; case DOM_VK_CONTROL: return KEY_CONTROL; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 4a01532d89..cfa039c130 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -518,7 +518,7 @@ static int translateKey(unsigned int key) { /* 21 */ KEY_BRACELEFT, /* 22 */ KEY_I, /* 23 */ KEY_P, - /* 24 */ KEY_RETURN, + /* 24 */ KEY_ENTER, /* 25 */ KEY_L, /* 26 */ KEY_J, /* 27 */ KEY_APOSTROPHE, @@ -558,7 +558,7 @@ static int translateKey(unsigned int key) { /* 49 */ KEY_UNKNOWN, /* VolumeDown */ /* 4a */ KEY_UNKNOWN, /* Mute */ /* 4b */ KEY_KP_DIVIDE, - /* 4c */ KEY_ENTER, + /* 4c */ KEY_KP_ENTER, /* 4d */ KEY_UNKNOWN, /* 4e */ KEY_KP_SUBTRACT, /* 4f */ KEY_UNKNOWN, diff --git a/platform/uwp/joypad_uwp.cpp b/platform/uwp/joypad_uwp.cpp index 34e36f7b66..f3d4eb99c8 100644 --- a/platform/uwp/joypad_uwp.cpp +++ b/platform/uwp/joypad_uwp.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "joypad_uwp.h" +#include "core/os/os.h" using namespace Windows::Gaming::Input; using namespace Windows::Foundation; @@ -45,27 +46,44 @@ void JoypadUWP::process_controllers() { for (int i = 0; i < MAX_CONTROLLERS; i++) { - if (!controllers[i].connected) break; + ControllerDevice &joy = controllers[i]; - switch (controllers[i].type) { + if (!joy.connected) break; + + switch (joy.type) { case ControllerType::GAMEPAD_CONTROLLER: { - GamepadReading reading = ((Gamepad ^)controllers[i].controller_reference)->GetCurrentReading(); + GamepadReading reading = ((Gamepad ^) joy.controller_reference)->GetCurrentReading(); int button_mask = (int)GamepadButtons::Menu; for (int j = 0; j < 14; j++) { - input->joy_button(controllers[i].id, j, (int)reading.Buttons & button_mask); + input->joy_button(joy.id, j, (int)reading.Buttons & button_mask); button_mask *= 2; } - input->joy_axis(controllers[i].id, JOY_AXIS_0, axis_correct(reading.LeftThumbstickX)); - input->joy_axis(controllers[i].id, JOY_AXIS_1, axis_correct(reading.LeftThumbstickY, true)); - input->joy_axis(controllers[i].id, JOY_AXIS_2, axis_correct(reading.RightThumbstickX)); - input->joy_axis(controllers[i].id, JOY_AXIS_3, axis_correct(reading.RightThumbstickY, true)); - input->joy_axis(controllers[i].id, JOY_AXIS_4, axis_correct(reading.LeftTrigger, false, true)); - input->joy_axis(controllers[i].id, JOY_AXIS_5, axis_correct(reading.RightTrigger, false, true)); + input->joy_axis(joy.id, JOY_AXIS_0, axis_correct(reading.LeftThumbstickX)); + input->joy_axis(joy.id, JOY_AXIS_1, axis_correct(reading.LeftThumbstickY, true)); + input->joy_axis(joy.id, JOY_AXIS_2, axis_correct(reading.RightThumbstickX)); + input->joy_axis(joy.id, JOY_AXIS_3, axis_correct(reading.RightThumbstickY, true)); + input->joy_axis(joy.id, JOY_AXIS_4, axis_correct(reading.LeftTrigger, false, true)); + input->joy_axis(joy.id, JOY_AXIS_5, axis_correct(reading.RightTrigger, false, true)); + + uint64_t timestamp = input->get_joy_vibration_timestamp(joy.id); + if (timestamp > joy.ff_timestamp) { + Vector2 strength = input->get_joy_vibration_strength(joy.id); + float duration = input->get_joy_vibration_duration(joy.id); + if (strength.x == 0 && strength.y == 0) { + joypad_vibration_stop(i, timestamp); + } else { + joypad_vibration_start(i, strength.x, strength.y, duration, timestamp); + } + } else if (joy.vibrating && joy.ff_end_timestamp != 0) { + uint64_t current_time = OS::get_singleton()->get_ticks_usec(); + if (current_time >= joy.ff_end_timestamp) + joypad_vibration_stop(i, current_time); + } break; } @@ -122,15 +140,7 @@ void JoypadUWP::OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Inp ERR_FAIL_COND(idx == -1); - for (int i = idx + 1; i < MAX_CONTROLLERS - 1; i++) { - - if (!controllers[i].connected) { - break; - } - - controllers[i - 1] = controllers[i]; - } - controllers[MAX_CONTROLLERS - 1] = ControllerDevice(); + controllers[idx] = ControllerDevice(); input->joy_connection_changed(idx, false, "Xbox Controller"); } @@ -144,3 +154,30 @@ InputDefault::JoyAxis JoypadUWP::axis_correct(double p_val, bool p_negate, bool return jx; } + +void JoypadUWP::joypad_vibration_start(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) { + ControllerDevice &joy = controllers[p_device]; + if (joy.connected) { + GamepadVibration vibration; + vibration.LeftMotor = p_strong_magnitude; + vibration.RightMotor = p_weak_magnitude; + ((Gamepad ^) joy.controller_reference)->Vibration = vibration; + + joy.ff_timestamp = p_timestamp; + joy.ff_end_timestamp = p_duration == 0 ? 0 : p_timestamp + (uint64_t)(p_duration * 1000000.0); + joy.vibrating = true; + } +} + +void JoypadUWP::joypad_vibration_stop(int p_device, uint64_t p_timestamp) { + ControllerDevice &joy = controllers[p_device]; + if (joy.connected) { + GamepadVibration vibration; + vibration.LeftMotor = 0.0; + vibration.RightMotor = 0.0; + ((Gamepad ^) joy.controller_reference)->Vibration = vibration; + + joy.ff_timestamp = p_timestamp; + joy.vibrating = false; + } +} diff --git a/platform/uwp/joypad_uwp.h b/platform/uwp/joypad_uwp.h index 7337ffb3ce..c55e1e7ab7 100644 --- a/platform/uwp/joypad_uwp.h +++ b/platform/uwp/joypad_uwp.h @@ -62,11 +62,17 @@ private: int id; bool connected; ControllerType type; + float ff_timestamp; + float ff_end_timestamp; + bool vibrating; ControllerDevice() { id = -1; connected = false; type = ControllerType::GAMEPAD_CONTROLLER; + ff_timestamp = 0.0f; + ff_end_timestamp = 0.0f; + vibrating = false; } }; @@ -78,6 +84,8 @@ private: void OnGamepadRemoved(Platform::Object ^ sender, Windows::Gaming::Input::Gamepad ^ value); InputDefault::JoyAxis axis_correct(double p_val, bool p_negate = false, bool p_trigger = false) const; + void joypad_vibration_start(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp); + void joypad_vibration_stop(int p_device, uint64_t p_timestamp); }; #endif diff --git a/platform/windows/key_mapping_win.cpp b/platform/windows/key_mapping_win.cpp index bffacb3a82..83e2af72b2 100644 --- a/platform/windows/key_mapping_win.cpp +++ b/platform/windows/key_mapping_win.cpp @@ -44,7 +44,7 @@ static _WinTranslatePair _vk_to_keycode[] = { //VK_CLEAR (0x0C) - { KEY_RETURN, VK_RETURN }, //(0x0D) + { KEY_ENTER, VK_RETURN }, //(0x0D) { KEY_SHIFT, VK_SHIFT }, //(0x10) diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index a1a7e3c321..9cab19fffb 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -807,7 +807,7 @@ void OS_Windows::process_key_events() { if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) { // Special case for Numpad Enter key - k->set_scancode(KEY_ENTER); + k->set_scancode(KEY_KP_ENTER); } else { k->set_scancode(KeyMappingWindows::get_keysym(ke.wParam)); } diff --git a/platform/x11/key_mapping_x11.cpp b/platform/x11/key_mapping_x11.cpp index 1d7eb1692c..32a9806b22 100644 --- a/platform/x11/key_mapping_x11.cpp +++ b/platform/x11/key_mapping_x11.cpp @@ -44,7 +44,7 @@ static _XTranslatePair _xkeysym_to_keycode[] = { { XK_Tab, KEY_TAB }, { XK_ISO_Left_Tab, KEY_BACKTAB }, { XK_BackSpace, KEY_BACKSPACE }, - { XK_Return, KEY_RETURN }, + { XK_Return, KEY_ENTER }, { XK_Insert, KEY_INSERT }, { XK_Delete, KEY_DELETE }, { XK_Clear, KEY_DELETE }, @@ -78,7 +78,7 @@ static _XTranslatePair _xkeysym_to_keycode[] = { { XK_Help, KEY_HELP }, { XK_KP_Space, KEY_SPACE }, { XK_KP_Tab, KEY_TAB }, - { XK_KP_Enter, KEY_ENTER }, + { XK_KP_Enter, KEY_KP_ENTER }, { XK_Home, KEY_HOME }, { XK_Left, KEY_LEFT }, { XK_Up, KEY_UP }, diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index 22649cedd7..b10ee85da5 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -346,6 +346,7 @@ void AnimatedSprite::_notification(int p_what) { update(); _change_notify("frame"); + emit_signal(SceneStringNames::get_singleton()->frame_changed); } float to_process = MIN(timeout, remaining); diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index 0acc85681d..82efe1d7fb 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -243,7 +243,7 @@ void Node2D::global_translate(const Vector2 &p_amount) { set_global_position(get_global_position() + p_amount); } -void Node2D::scale(const Size2 &p_amount) { +void Node2D::apply_scale(const Size2 &p_amount) { set_scale(get_scale() * p_amount); } @@ -429,7 +429,7 @@ void Node2D::_bind_methods() { ClassDB::bind_method(D_METHOD("move_local_y", "delta", "scaled"), &Node2D::move_y, DEFVAL(false)); ClassDB::bind_method(D_METHOD("translate", "offset"), &Node2D::translate); ClassDB::bind_method(D_METHOD("global_translate", "offset"), &Node2D::global_translate); - ClassDB::bind_method(D_METHOD("scale", "ratio"), &Node2D::scale); + ClassDB::bind_method(D_METHOD("apply_scale", "ratio"), &Node2D::apply_scale); ClassDB::bind_method(D_METHOD("set_global_position", "pos"), &Node2D::set_global_position); ClassDB::bind_method(D_METHOD("get_global_position"), &Node2D::get_global_position); diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h index 5b3a28d5c3..df9a05ff79 100644 --- a/scene/2d/node_2d.h +++ b/scene/2d/node_2d.h @@ -78,7 +78,7 @@ public: void move_y(float p_delta, bool p_scaled = false); void translate(const Vector2 &p_amount); void global_translate(const Vector2 &p_amount); - void scale(const Size2 &p_amount); + void apply_scale(const Size2 &p_amount); Point2 get_position() const; float get_rotation() const; diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 8b2653f639..d5527fc9ca 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -798,6 +798,40 @@ bool RigidBody2D::is_contact_monitor_enabled() const { return contact_monitor != NULL; } +void RigidBody2D::_notification(int p_what) { + +#ifdef TOOLS_ENABLED + if (p_what == NOTIFICATION_ENTER_TREE) { + if (get_tree()->is_editor_hint()) { + set_notify_local_transform(true); //used for warnings and only in editor + } + } + + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { + if (get_tree()->is_editor_hint()) { + update_configuration_warning(); + } + } + +#endif +} + +String RigidBody2D::get_configuration_warning() const { + + Transform2D t = get_transform(); + + String warning = CollisionObject2D::get_configuration_warning(); + + if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05)) { + if (warning != String()) { + warning += "\n"; + } + warning += TTR("Size changes to RigidBody2D (in character or rigid modes) will be overriden by the physics engine when running.\nChange the size in children collision shapes instead."); + } + + return warning; +} + void RigidBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidBody2D::set_mode); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 8c8e4ebc77..54bd263b15 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -185,6 +185,7 @@ private: bool _test_motion(const Vector2 &p_motion, float p_margin = 0.08, const Ref<Physics2DTestMotionResult> &p_result = Ref<Physics2DTestMotionResult>()); protected: + void _notification(int p_what); static void _bind_methods(); public: @@ -253,6 +254,8 @@ public: Array get_colliding_bodies() const; //function for script + virtual String get_configuration_warning() const; + RigidBody2D(); ~RigidBody2D(); }; diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp index ad34dfd63a..01d101a89c 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -237,7 +237,7 @@ void Sprite::set_vframes(int p_amount) { vframes = p_amount; update(); item_rect_changed(); - _change_notify("frame"); + _change_notify(); } int Sprite::get_vframes() const { @@ -250,7 +250,7 @@ void Sprite::set_hframes(int p_amount) { hframes = p_amount; update(); item_rect_changed(); - _change_notify("frame"); + _change_notify(); } int Sprite::get_hframes() const { diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 9d70b75027..02dcc7d059 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -1144,6 +1144,20 @@ Array TileMap::get_used_cells() const { return a; } +Array TileMap::get_used_cells_by_id(int p_id) const { + + Array a; + for (Map<PosKey, Cell>::Element *E = tile_map.front(); E; E = E->next()) { + + if (E->value().id == p_id) { + Vector2 p(E->key().x, E->key().y); + a.push_back(p); + } + } + + return a; +} + Rect2 TileMap::get_used_rect() { // Not const because of cache if (used_size_cache_dirty) { @@ -1262,6 +1276,7 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear); ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMap::get_used_cells); + ClassDB::bind_method(D_METHOD("get_used_cells_by_id", "id"), &TileMap::get_used_cells_by_id); ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMap::get_used_rect); ClassDB::bind_method(D_METHOD("map_to_world", "mappos", "ignore_half_ofs"), &TileMap::map_to_world, DEFVAL(false)); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 3468854a61..082e9d1018 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -270,6 +270,7 @@ public: bool is_y_sort_mode_enabled() const; Array get_used_cells() const; + Array get_used_cells_by_id(int p_id) const; Rect2 get_used_rect(); // Not const because of cache void set_occluder_light_mask(int p_mask); diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp index a37c74cb07..fb71b61d45 100644 --- a/scene/2d/visibility_notifier_2d.cpp +++ b/scene/2d/visibility_notifier_2d.cpp @@ -33,6 +33,7 @@ #include "scene/2d/animated_sprite.h" #include "scene/2d/physics_body_2d.h" #include "scene/animation/animation_player.h" +#include "scene/main/viewport.h" #include "scene/scene_string_names.h" #include "scene/scene_string_names.h" diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp index 5f2a720748..3c99f7fb3a 100644 --- a/scene/3d/arvr_nodes.cpp +++ b/scene/3d/arvr_nodes.cpp @@ -98,6 +98,7 @@ void ARVRController::_notification(int p_what) { is_active = false; button_states = 0; } else { + is_active = true; set_transform(tracker->get_transform(true)); int joy_id = tracker->get_joy_id(); @@ -231,6 +232,118 @@ ARVRController::~ARVRController(){ //////////////////////////////////////////////////////////////////////////////////////////////////// +void ARVRAnchor::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + set_process_internal(true); + }; break; + case NOTIFICATION_EXIT_TREE: { + set_process_internal(false); + }; break; + case NOTIFICATION_INTERNAL_PROCESS: { + // get our ARVRServer + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL(arvr_server); + + // find the tracker for our anchor + ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_ANCHOR, anchor_id); + if (tracker == NULL) { + // this anchor is currently not available + is_active = false; + } else { + is_active = true; + Transform transform; + + // we'll need our world_scale + real_t world_scale = arvr_server->get_world_scale(); + + // get our info from our tracker + transform.basis = tracker->get_orientation(); + transform.origin = tracker->get_position(); // <-- already adjusted to world scale + + // our basis is scaled to the size of the plane the anchor is tracking + // extract the size from our basis and reset the scale + size = transform.basis.get_scale() * world_scale; + transform.basis.set_scale(Vector3(1.0, 1.0, 1.0)); + + // apply our reference frame and set our transform + set_transform(arvr_server->get_reference_frame() * transform); + }; + }; break; + default: + break; + }; +}; + +void ARVRAnchor::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_anchor_id", "anchor_id"), &ARVRAnchor::set_anchor_id); + ClassDB::bind_method(D_METHOD("get_anchor_id"), &ARVRAnchor::get_anchor_id); + ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_id"), "set_anchor_id", "get_anchor_id"); + ClassDB::bind_method(D_METHOD("get_anchor_name"), &ARVRAnchor::get_anchor_name); + + ClassDB::bind_method(D_METHOD("get_is_active"), &ARVRAnchor::get_is_active); + ClassDB::bind_method(D_METHOD("get_size"), &ARVRAnchor::get_size); +}; + +void ARVRAnchor::set_anchor_id(int p_anchor_id) { + // we don't check any bounds here, this anchor may not yet be active and just be a place holder until it is. + anchor_id = p_anchor_id; +}; + +int ARVRAnchor::get_anchor_id(void) const { + return anchor_id; +}; + +Vector3 ARVRAnchor::get_size() const { + return size; +}; + +String ARVRAnchor::get_anchor_name(void) const { + // get our ARVRServer + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, String()); + + ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_ANCHOR, anchor_id); + if (tracker == NULL) { + return String("Not connected"); + }; + + return tracker->get_name(); +}; + +bool ARVRAnchor::get_is_active() const { + return is_active; +}; + +String ARVRAnchor::get_configuration_warning() const { + if (!is_visible() || !is_inside_tree()) + return String(); + + // must be child node of ARVROrigin! + ARVROrigin *origin = get_parent()->cast_to<ARVROrigin>(); + if (origin == NULL) { + return TTR("ARVRAnchor must have an ARVROrigin node as its parent"); + }; + + if (anchor_id == 0) { + return TTR("The anchor id must not be 0 or this anchor will not be bound to an actual anchor"); + }; + + return String(); +}; + +ARVRAnchor::ARVRAnchor() { + anchor_id = 0; + is_active = true; +}; + +ARVRAnchor::~ARVRAnchor(){ + // nothing to do here yet for now.. +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + String ARVROrigin::get_configuration_warning() const { if (!is_visible() || !is_inside_tree()) return String(); diff --git a/scene/3d/arvr_nodes.h b/scene/3d/arvr_nodes.h index 3dab263317..936519126b 100644 --- a/scene/3d/arvr_nodes.h +++ b/scene/3d/arvr_nodes.h @@ -39,7 +39,7 @@ **/ /* - ARVRCamera is a subclass of camera which will register itself with its parent ARVROrigin and as a result is automatically positioned + ARVRCamera is a subclass of camera which will register itself with its parent ARVROrigin and as a result is automatically positioned */ class ARVRCamera : public Camera { @@ -56,9 +56,9 @@ public: }; /* - ARVRController is a helper node that automatically updates it's position based on tracker data. + ARVRController is a helper node that automatically updates it's position based on tracker data. - It must be a child node of our ARVROrigin node + It must be a child node of our ARVROrigin node */ class ARVRController : public Spatial { @@ -92,6 +92,37 @@ public: }; /* + ARVRAnchor is a helper node that automatically updates it's position based on anchor data, it represents a real world location. + It must be a child node of our ARVROrigin node +*/ + +class ARVRAnchor : public Spatial { + GDCLASS(ARVRAnchor, Spatial); + +private: + int anchor_id; + bool is_active; + Vector3 size; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_anchor_id(int p_anchor_id); + int get_anchor_id(void) const; + String get_anchor_name(void) const; + + bool get_is_active() const; + Vector3 get_size() const; + + String get_configuration_warning() const; + + ARVRAnchor(); + ~ARVRAnchor(); +}; + +/* ARVROrigin is special spatial node that acts as our origin point mapping our real world center of our tracking volume into our virtual world. It is this point that you will move around the world as the player 'moves while standing still', i.e. the player moves through teleporting or controller inputs as opposed to physically moving. diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index 7e599ce2f5..9feed2fe7b 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -473,6 +473,21 @@ void RigidBody::_direct_state_changed(Object *p_state) { } void RigidBody::_notification(int p_what) { + +#ifdef TOOLS_ENABLED + if (p_what == NOTIFICATION_ENTER_TREE) { + if (get_tree()->is_editor_hint()) { + set_notify_local_transform(true); //used for warnings and only in editor + } + } + + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { + if (get_tree()->is_editor_hint()) { + update_configuration_warning(); + } + } + +#endif } void RigidBody::set_mode(Mode p_mode) { @@ -747,6 +762,22 @@ Array RigidBody::get_colliding_bodies() const { return ret; } +String RigidBody::get_configuration_warning() const { + + Transform t = get_transform(); + + String warning = CollisionObject::get_configuration_warning(); + + if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) { + if (warning != String()) { + warning += "\n"; + } + warning += TTR("Size changes to RigidBody (in character or rigid modes) will be overriden by the physics engine when running.\nChange the size in children collision shapes instead."); + } + + return warning; +} + void RigidBody::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidBody::set_mode); diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index f86d7d957f..83811a1d93 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -252,6 +252,8 @@ public: void apply_impulse(const Vector3 &p_pos, const Vector3 &p_impulse); + virtual String get_configuration_warning() const; + RigidBody(); ~RigidBody(); }; diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 78e8e92afc..1b9b58ceb1 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -502,7 +502,7 @@ void Sprite3D::set_vframes(int p_amount) { ERR_FAIL_COND(p_amount < 1); vframes = p_amount; _queue_update(); - _change_notify("frame"); + _change_notify(); } int Sprite3D::get_vframes() const { @@ -514,7 +514,7 @@ void Sprite3D::set_hframes(int p_amount) { ERR_FAIL_COND(p_amount < 1); hframes = p_amount; _queue_update(); - _change_notify("frame"); + _change_notify(); } int Sprite3D::get_hframes() const { diff --git a/scene/3d/visibility_notifier.cpp b/scene/3d/visibility_notifier.cpp index 5e6561adb7..cc81a4cb56 100644 --- a/scene/3d/visibility_notifier.cpp +++ b/scene/3d/visibility_notifier.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "visibility_notifier.h" +#include "scene/3d/camera.h" #include "scene/3d/physics_body.h" #include "scene/animation/animation_player.h" #include "scene/scene_string_names.h" @@ -42,6 +43,7 @@ void VisibilityNotifier::_enter_camera(Camera *p_camera) { emit_signal(SceneStringNames::get_singleton()->screen_entered); _screen_enter(); } + emit_signal(SceneStringNames::get_singleton()->camera_entered, p_camera); } diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 543b64bd15..85a9ada38a 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -1268,7 +1268,7 @@ void AnimationPlayer::_bind_methods() { ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationPlayer::advance); - ADD_GROUP("Playback", "playback_"); + ADD_GROUP("Playback Options", "playback_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "playback_process_mode", PROPERTY_HINT_ENUM, "Fixed,Idle"), "set_animation_process_mode", "get_animation_process_mode"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "playback_default_blend_time", PROPERTY_HINT_RANGE, "0,4096,0.01"), "set_default_blend_time", "get_default_blend_time"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_node"), "set_root", "get_root"); diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 9a5f55698e..4d55d8df75 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -508,7 +508,7 @@ void BaseButton::_bind_methods() { ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "pressed"))); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "is_pressed"), "set_pressed", "is_pressed"); + ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed"); ADD_PROPERTYNO(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_enabled_focus_mode", "get_enabled_focus_mode"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "ShortCut"), "set_shortcut", "get_shortcut"); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 97f49da2be..34533375b2 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -431,7 +431,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { if (mb.is_valid() && (mb->get_button_index() == BUTTON_LEFT || (allow_rmb_select && mb->get_button_index() == BUTTON_RIGHT)) && mb->is_pressed()) { search_string = ""; //any mousepress cancels - Vector2 pos(mb->get_position().x, mb->get_position().y); + Vector2 pos = mb->get_position(); Ref<StyleBox> bg = get_stylebox("bg"); pos -= bg->get_offset(); pos.y += scroll_bar->get_value(); @@ -475,7 +475,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { if (mb->get_button_index() == BUTTON_RIGHT) { - emit_signal("item_rmb_selected", i, Vector2(mb->get_position().x, mb->get_position().y)); + emit_signal("item_rmb_selected", i, pos); } } else { @@ -486,7 +486,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { if (items[i].selected && mb->get_button_index() == BUTTON_RIGHT) { - emit_signal("item_rmb_selected", i, Vector2(mb->get_position().x, mb->get_position().y)); + emit_signal("item_rmb_selected", i, pos); } else { bool selected = !items[i].selected; @@ -501,7 +501,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) { if (mb->get_button_index() == BUTTON_RIGHT) { - emit_signal("item_rmb_selected", i, Vector2(mb->get_position().x, mb->get_position().y)); + emit_signal("item_rmb_selected", i, pos); } else if (/*select_mode==SELECT_SINGLE &&*/ mb->is_doubleclick()) { emit_signal("item_activated", i); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index f4dd3e92cd..8556ce5db1 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -229,8 +229,8 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { bool handled = true; switch (code) { - case KEY_ENTER: - case KEY_RETURN: { + case KEY_KP_ENTER: + case KEY_ENTER: { emit_signal("text_entered", text); if (OS::get_singleton()->has_virtual_keyboard()) diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index f59a2e06eb..b673f9d68a 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -249,8 +249,8 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { } } } break; - case KEY_RETURN: - case KEY_ENTER: { + case KEY_ENTER: + case KEY_KP_ENTER: { if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index d604738da1..a7c31361e8 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1805,7 +1805,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { return; } - if (k->get_scancode() == KEY_ENTER || k->get_scancode() == KEY_RETURN || k->get_scancode() == KEY_TAB) { + if (k->get_scancode() == KEY_KP_ENTER || k->get_scancode() == KEY_ENTER || k->get_scancode() == KEY_TAB) { _confirm_completion(); accept_event(); @@ -1974,8 +1974,8 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { switch (k->get_scancode()) { - case KEY_ENTER: - case KEY_RETURN: { + case KEY_KP_ENTER: + case KEY_ENTER: { if (readonly) break; @@ -4353,6 +4353,23 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const { bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this + bool inside_quotes = false; + int qbegin, qend; + for (int i = 0; i < s.length(); i++) { + if (s[i] == '"') { + if (inside_quotes) { + qend = i; + inside_quotes = false; + if (col >= qbegin && col <= qend) { + return s.substr(qbegin, qend - qbegin); + } + } else { + qbegin = i + 1; + inside_quotes = true; + } + } + } + while (beg > 0 && s[beg - 1] > 32 && (symbol == _is_symbol(s[beg - 1]))) { beg--; } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 0b57841be7..9499ba9dcd 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -1917,8 +1917,8 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool void Tree::_text_editor_modal_close() { if (Input::get_singleton()->is_key_pressed(KEY_ESCAPE) || - Input::get_singleton()->is_key_pressed(KEY_ENTER) || - Input::get_singleton()->is_key_pressed(KEY_RETURN)) { + Input::get_singleton()->is_key_pressed(KEY_KP_ENTER) || + Input::get_singleton()->is_key_pressed(KEY_ENTER)) { return; } @@ -2237,8 +2237,8 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } break; case KEY_F2: - case KEY_RETURN: - case KEY_ENTER: { + case KEY_ENTER: + case KEY_KP_ENTER: { if (selected_item) { //bring up editor if possible diff --git a/scene/main/node.cpp b/scene/main/node.cpp index c3849f79df..fcb9c1a842 100755 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -705,12 +705,12 @@ void Node::rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, co ERR_FAIL_COND(!is_inside_tree()); bool skip_rpc = false; + bool call_local_native = false; + bool call_local_script = false; if (p_peer_id == 0 || p_peer_id == get_tree()->get_network_unique_id() || (p_peer_id < 0 && p_peer_id != -get_tree()->get_network_unique_id())) { //check that send mode can use local call - bool call_local = false; - Map<StringName, RPCMode>::Element *E = data.rpc_methods.find(p_method); if (E) { @@ -724,29 +724,22 @@ void Node::rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, co } break; case RPC_MODE_SYNC: { //call it, sync always results in call - call_local = true; + call_local_native = true; } break; case RPC_MODE_MASTER: { - call_local = is_network_master(); - if (call_local) { + call_local_native = is_network_master(); + if (call_local_native) { skip_rpc = true; //no other master so.. } } break; case RPC_MODE_SLAVE: { - call_local = !is_network_master(); + call_local_native = !is_network_master(); } break; } } - if (call_local) { - Variant::CallError ce; - call(p_method, p_arg, p_argcount, ce); - if (ce.error != Variant::CallError::CALL_OK) { - String error = Variant::get_call_error_text(this, p_method, p_arg, p_argcount, ce); - error = "rpc() aborted in local call: - " + error; - ERR_PRINTS(error); - return; - } + if (call_local_native) { + // done below } else if (get_script_instance()) { //attempt with script ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rpc_mode(p_method); @@ -761,37 +754,47 @@ void Node::rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, co } break; case ScriptInstance::RPC_MODE_SYNC: { //call it, sync always results in call - call_local = true; + call_local_script = true; } break; case ScriptInstance::RPC_MODE_MASTER: { - call_local = is_network_master(); - if (call_local) { + call_local_script = is_network_master(); + if (call_local_script) { skip_rpc = true; //no other master so.. } } break; case ScriptInstance::RPC_MODE_SLAVE: { - call_local = !is_network_master(); + call_local_script = !is_network_master(); } break; } - - if (call_local) { - Variant::CallError ce; - ce.error = Variant::CallError::CALL_OK; - get_script_instance()->call(p_method, p_arg, p_argcount, ce); - if (ce.error != Variant::CallError::CALL_OK) { - String error = Variant::get_call_error_text(this, p_method, p_arg, p_argcount, ce); - error = "rpc() aborted in script local call: - " + error; - ERR_PRINTS(error); - return; - } - } } } - if (skip_rpc) - return; + if (!skip_rpc) { + get_tree()->_rpc(this, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount); + } + + if (call_local_native) { + Variant::CallError ce; + call(p_method, p_arg, p_argcount, ce); + if (ce.error != Variant::CallError::CALL_OK) { + String error = Variant::get_call_error_text(this, p_method, p_arg, p_argcount, ce); + error = "rpc() aborted in local call: - " + error; + ERR_PRINTS(error); + return; + } + } - get_tree()->_rpc(this, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount); + if (call_local_script) { + Variant::CallError ce; + ce.error = Variant::CallError::CALL_OK; + get_script_instance()->call(p_method, p_arg, p_argcount, ce); + if (ce.error != Variant::CallError::CALL_OK) { + String error = Variant::get_call_error_text(this, p_method, p_arg, p_argcount, ce); + error = "rpc() aborted in script local call: - " + error; + ERR_PRINTS(error); + return; + } + } } /******** RSET *********/ diff --git a/scene/main/node.h b/scene/main/node.h index 1794cce9f6..0447deccc1 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -33,7 +33,7 @@ #include "class_db.h" #include "map.h" #include "object.h" -#include "path_db.h" +#include "node_path.h" #include "project_settings.h" #include "scene/main/scene_tree.h" #include "script_language.h" diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index f286bfb81a..3e6d80d314 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -409,6 +409,7 @@ void register_scene_types() { ClassDB::register_class<Listener>(); ClassDB::register_class<ARVRCamera>(); ClassDB::register_class<ARVRController>(); + ClassDB::register_class<ARVRAnchor>(); ClassDB::register_class<ARVROrigin>(); ClassDB::register_class<InterpolatedCamera>(); ClassDB::register_class<MeshInstance>(); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 24ec39afe3..5a79e49240 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -234,6 +234,14 @@ void SpatialMaterial::init_shaders() { shader_names->grow = "grow"; + shader_names->metallic_texture_channel = "metallic_texture_channel"; + shader_names->roughness_texture_channel = "roughness_texture_channel"; + shader_names->ao_texture_channel = "ao_texture_channel"; + shader_names->clearcoat_texture_channel = "clearcoat_texture_channel"; + shader_names->rim_texture_channel = "rim_texture_channel"; + shader_names->depth_texture_channel = "depth_texture_channel"; + shader_names->refraction_texture_channel = "refraction_texture_channel"; + shader_names->texture_names[TEXTURE_ALBEDO] = "texture_albedo"; shader_names->texture_names[TEXTURE_METALLIC] = "texture_metallic"; shader_names->texture_names[TEXTURE_ROUGHNESS] = "texture_roughness"; @@ -354,7 +362,9 @@ void SpatialMaterial::_update_shader() { code += "uniform float roughness : hint_range(0,1);\n"; code += "uniform float point_size : hint_range(0,128);\n"; code += "uniform sampler2D texture_metallic : hint_white;\n"; + code += "uniform vec4 metallic_texture_channel;\n"; code += "uniform sampler2D texture_roughness : hint_white;\n"; + code += "uniform vec4 roughness_texture_channel;\n"; if (billboard_mode == BILLBOARD_PARTICLES) { code += "uniform int particles_anim_h_frames;\n"; code += "uniform int particles_anim_v_frames;\n"; @@ -371,6 +381,7 @@ void SpatialMaterial::_update_shader() { if (features[FEATURE_REFRACTION]) { code += "uniform sampler2D texture_refraction;\n"; code += "uniform float refraction : hint_range(-16,16);\n"; + code += "uniform vec4 refraction_texture_channel;\n"; } if (features[FEATURE_NORMAL_MAPPING]) { @@ -393,6 +404,7 @@ void SpatialMaterial::_update_shader() { } if (features[FEATURE_AMBIENT_OCCLUSION]) { code += "uniform sampler2D texture_ambient_occlusion : hint_white;\n"; + code += "uniform vec4 ao_texture_channel;\n"; } if (features[FEATURE_DETAIL]) { @@ -617,15 +629,15 @@ void SpatialMaterial::_update_shader() { code += "\tALBEDO = albedo.rgb * albedo_tex.rgb;\n"; if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tfloat metallic_tex = triplanar_texture(texture_metallic,uv1_power_normal,uv1_world_pos).r;\n"; + code += "\tfloat metallic_tex = dot(triplanar_texture(texture_metallic,uv1_power_normal,uv1_world_pos),metallic_texture_channel);\n"; } else { - code += "\tfloat metallic_tex = texture(texture_metallic,base_uv).r;\n"; + code += "\tfloat metallic_tex = dot(texture(texture_metallic,base_uv),metallic_texture_channel);\n"; } code += "\tMETALLIC = metallic_tex * metallic;\n"; if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tfloat roughness_tex = triplanar_texture(texture_roughness,uv1_power_normal,uv1_world_pos).r;\n"; + code += "\tfloat roughness_tex = dot(triplanar_texture(texture_roughness,uv1_power_normal,uv1_world_pos),roughness_texture_channel);\n"; } else { - code += "\tfloat roughness_tex = texture(texture_roughness,base_uv).r;\n"; + code += "\tfloat roughness_tex = dot(texture(texture_roughness,base_uv),roughness_texture_channel);\n"; } code += "\tROUGHNESS = roughness_tex * roughness;\n"; code += "\tSPECULAR = specular;\n"; @@ -656,7 +668,7 @@ void SpatialMaterial::_update_shader() { code += "\tvec3 ref_normal = NORMAL;\n"; } - code += "\tvec2 ref_ofs = SCREEN_UV - ref_normal.xy * texture(texture_refraction,base_uv).r * refraction;\n"; + code += "\tvec2 ref_ofs = SCREEN_UV - ref_normal.xy * dot(texture(texture_refraction,base_uv),refraction_texture_channel) * refraction;\n"; code += "\tfloat ref_amount = 1.0 - albedo.a * albedo_tex.a;\n"; code += "\tEMISSION += textureLod(SCREEN_TEXTURE,ref_ofs,ROUGHNESS * 8.0).rgb * ref_amount;\n"; code += "\tALBEDO *= 1.0 - ref_amount;\n"; @@ -699,15 +711,15 @@ void SpatialMaterial::_update_shader() { if (features[FEATURE_AMBIENT_OCCLUSION]) { if (flags[FLAG_AO_ON_UV2]) { if (flags[FLAG_UV2_USE_TRIPLANAR]) { - code += "\tAO = triplanar_texture(texture_ambient_occlusion,uv2_power_normal,uv2_world_pos).r;\n"; + code += "\tAO = dot(triplanar_texture(texture_ambient_occlusion,uv2_power_normal,uv2_world_pos),ao_texture_channel);\n"; } else { - code += "\tAO = texture(texture_ambient_occlusion,base_uv2).r;\n"; + code += "\tAO = dot(texture(texture_ambient_occlusion,base_uv2),ao_texture_channel);\n"; } } else { if (flags[FLAG_UV1_USE_TRIPLANAR]) { - code += "\tAO = triplanar_texture(texture_ambient_occlusion,uv1_power_normal,uv1_world_pos).r;\n"; + code += "\tAO = dot(triplanar_texture(texture_ambient_occlusion,uv1_power_normal,uv1_world_pos),ao_texture_channel);\n"; } else { - code += "\tAO = texture(texture_ambient_occlusion,base_uv).r;\n"; + code += "\tAO = dot(texture(texture_ambient_occlusion,base_uv),ao_texture_channel);\n"; } } } @@ -1327,6 +1339,58 @@ float SpatialMaterial::get_grow() const { return grow; } +static Plane _get_texture_mask(SpatialMaterial::TextureChannel p_channel) { + static const Plane masks[5] = { + Plane(1, 0, 0, 0), + Plane(0, 1, 0, 0), + Plane(0, 0, 1, 0), + Plane(0, 0, 0, 1), + Plane(0.3333333, 0.3333333, 0.3333333, 0), + }; + + return masks[p_channel]; +} + +void SpatialMaterial::set_metallic_texture_channel(TextureChannel p_channel) { + + metallic_texture_channel = p_channel; + VS::get_singleton()->material_set_param(_get_material(), shader_names->metallic_texture_channel, _get_texture_mask(p_channel)); +} + +SpatialMaterial::TextureChannel SpatialMaterial::get_metallic_texture_channel() const { + return metallic_texture_channel; +} + +void SpatialMaterial::set_roughness_texture_channel(TextureChannel p_channel) { + + roughness_texture_channel = p_channel; + VS::get_singleton()->material_set_param(_get_material(), shader_names->roughness_texture_channel, _get_texture_mask(p_channel)); +} + +SpatialMaterial::TextureChannel SpatialMaterial::get_roughness_texture_channel() const { + return roughness_texture_channel; +} + +void SpatialMaterial::set_ao_texture_channel(TextureChannel p_channel) { + + ao_texture_channel = p_channel; + VS::get_singleton()->material_set_param(_get_material(), shader_names->ao_texture_channel, _get_texture_mask(p_channel)); +} + +SpatialMaterial::TextureChannel SpatialMaterial::get_ao_texture_channel() const { + return ao_texture_channel; +} + +void SpatialMaterial::set_refraction_texture_channel(TextureChannel p_channel) { + + refraction_texture_channel = p_channel; + VS::get_singleton()->material_set_param(_get_material(), shader_names->refraction_texture_channel, _get_texture_mask(p_channel)); +} + +SpatialMaterial::TextureChannel SpatialMaterial::get_refraction_texture_channel() const { + return refraction_texture_channel; +} + void SpatialMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_albedo", "albedo"), &SpatialMaterial::set_albedo); @@ -1455,6 +1519,18 @@ void SpatialMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_grow_enabled", "enable"), &SpatialMaterial::set_grow_enabled); ClassDB::bind_method(D_METHOD("is_grow_enabled"), &SpatialMaterial::is_grow_enabled); + ClassDB::bind_method(D_METHOD("set_metallic_texture_channel", "channel"), &SpatialMaterial::set_metallic_texture_channel); + ClassDB::bind_method(D_METHOD("get_metallic_texture_channel"), &SpatialMaterial::get_metallic_texture_channel); + + ClassDB::bind_method(D_METHOD("set_roughness_texture_channel", "channel"), &SpatialMaterial::set_roughness_texture_channel); + ClassDB::bind_method(D_METHOD("get_roughness_texture_channel"), &SpatialMaterial::get_roughness_texture_channel); + + ClassDB::bind_method(D_METHOD("set_ao_texture_channel", "channel"), &SpatialMaterial::set_ao_texture_channel); + ClassDB::bind_method(D_METHOD("get_ao_texture_channel"), &SpatialMaterial::get_ao_texture_channel); + + ClassDB::bind_method(D_METHOD("set_refraction_texture_channel", "channel"), &SpatialMaterial::set_refraction_texture_channel); + ClassDB::bind_method(D_METHOD("get_refraction_texture_channel"), &SpatialMaterial::get_refraction_texture_channel); + ADD_GROUP("Flags", "flags_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_transparent"), "set_feature", "get_feature", FEATURE_TRANSPARENT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_unshaded"), "set_flag", "get_flag", FLAG_UNSHADED); @@ -1490,10 +1566,12 @@ void SpatialMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "metallic", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_metallic", "get_metallic"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "metallic_specular", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_specular", "get_specular"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "metallic_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_METALLIC); + ADD_PROPERTY(PropertyInfo(Variant::INT, "metallic_texture_channel", PROPERTY_HINT_ENUM, "Red,Green,Blue,Alpha,Gray"), "set_metallic_texture_channel", "get_metallic_texture_channel"); ADD_GROUP("Roughness", "roughness_"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "roughness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_roughness", "get_roughness"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "roughness_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_ROUGHNESS); + ADD_PROPERTY(PropertyInfo(Variant::INT, "roughness_texture_channel", PROPERTY_HINT_ENUM, "Red,Green,Blue,Alpha,Gray"), "set_roughness_texture_channel", "get_roughness_texture_channel"); ADD_GROUP("Emission", "emission_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "emission_enabled"), "set_feature", "get_feature", FEATURE_EMISSION); @@ -1527,6 +1605,7 @@ void SpatialMaterial::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "ao_enabled"), "set_feature", "get_feature", FEATURE_AMBIENT_OCCLUSION); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "ao_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_AMBIENT_OCCLUSION); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "ao_on_uv2"), "set_flag", "get_flag", FLAG_AO_ON_UV2); + ADD_PROPERTY(PropertyInfo(Variant::INT, "ao_texture_channel", PROPERTY_HINT_ENUM, "Red,Green,Blue,Alpha,Gray"), "set_ao_texture_channel", "get_ao_texture_channel"); ADD_GROUP("Depth", "depth_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "depth_enabled"), "set_feature", "get_feature", FEATURE_DEPTH_MAPPING); @@ -1545,6 +1624,7 @@ void SpatialMaterial::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "refraction_enabled"), "set_feature", "get_feature", FEATURE_REFRACTION); ADD_PROPERTY(PropertyInfo(Variant::REAL, "refraction_scale", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_refraction", "get_refraction"); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "refraction_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture", TEXTURE_REFRACTION); + ADD_PROPERTY(PropertyInfo(Variant::INT, "refraction_texture_channel", PROPERTY_HINT_ENUM, "Red,Green,Blue,Alpha,Gray"), "set_refraction_texture_channel", "get_refraction_texture_channel"); ADD_GROUP("Detail", "detail_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "detail_enabled"), "set_feature", "get_feature", FEATURE_DETAIL); @@ -1638,6 +1718,12 @@ void SpatialMaterial::_bind_methods() { BIND_CONSTANT(BILLBOARD_ENABLED); BIND_CONSTANT(BILLBOARD_FIXED_Y); BIND_CONSTANT(BILLBOARD_PARTICLES); + + BIND_CONSTANT(TEXTURE_CHANNEL_RED); + BIND_CONSTANT(TEXTURE_CHANNEL_GREEN); + BIND_CONSTANT(TEXTURE_CHANNEL_BLUE); + BIND_CONSTANT(TEXTURE_CHANNEL_ALPHA); + BIND_CONSTANT(TEXTURE_CHANNEL_GRAYSCALE); } SpatialMaterial::SpatialMaterial() @@ -1672,6 +1758,11 @@ SpatialMaterial::SpatialMaterial() set_particles_anim_v_frames(1); set_particles_anim_loop(false); + set_metallic_texture_channel(TEXTURE_CHANNEL_RED); + set_roughness_texture_channel(TEXTURE_CHANNEL_RED); + set_ao_texture_channel(TEXTURE_CHANNEL_RED); + set_refraction_texture_channel(TEXTURE_CHANNEL_RED); + grow_enabled = false; set_grow(0.0); diff --git a/scene/resources/material.h b/scene/resources/material.h index 7587fc7927..1484b79fc6 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -190,6 +190,14 @@ public: BILLBOARD_PARTICLES, }; + enum TextureChannel { + TEXTURE_CHANNEL_RED, + TEXTURE_CHANNEL_GREEN, + TEXTURE_CHANNEL_BLUE, + TEXTURE_CHANNEL_ALPHA, + TEXTURE_CHANNEL_GRAYSCALE + }; + private: union MaterialKey { @@ -283,6 +291,14 @@ private: StringName uv2_blend_sharpness; StringName grow; + StringName metallic_texture_channel; + StringName roughness_texture_channel; + StringName ao_texture_channel; + StringName clearcoat_texture_channel; + StringName rim_texture_channel; + StringName depth_texture_channel; + StringName refraction_texture_channel; + StringName texture_names[TEXTURE_MAX]; }; @@ -342,6 +358,11 @@ private: DiffuseMode diffuse_mode; BillboardMode billboard_mode; + TextureChannel metallic_texture_channel; + TextureChannel roughness_texture_channel; + TextureChannel ao_texture_channel; + TextureChannel refraction_texture_channel; + bool features[FEATURE_MAX]; Ref<Texture> textures[TEXTURE_MAX]; @@ -478,6 +499,15 @@ public: void set_grow(float p_grow); float get_grow() const; + void set_metallic_texture_channel(TextureChannel p_channel); + TextureChannel get_metallic_texture_channel() const; + void set_roughness_texture_channel(TextureChannel p_channel); + TextureChannel get_roughness_texture_channel() const; + void set_ao_texture_channel(TextureChannel p_channel); + TextureChannel get_ao_texture_channel() const; + void set_refraction_texture_channel(TextureChannel p_channel); + TextureChannel get_refraction_texture_channel() const; + static void init_shaders(); static void finish_shaders(); static void flush_changes(); @@ -496,6 +526,7 @@ VARIANT_ENUM_CAST(SpatialMaterial::Flags) VARIANT_ENUM_CAST(SpatialMaterial::DiffuseMode) VARIANT_ENUM_CAST(SpatialMaterial::SpecularMode) VARIANT_ENUM_CAST(SpatialMaterial::BillboardMode) +VARIANT_ENUM_CAST(SpatialMaterial::TextureChannel) ////////////////////// diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 6bf3590c12..648900a5cd 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -516,6 +516,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map bool isdefault = ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one()); + if (E->get().usage & PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE) { + isdefault = true; //is script default value + } /* if (nd.instance<0 && ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one())) { continue; diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 8478432a04..3932917ebe 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -224,30 +224,22 @@ void SurfaceTool::add_index(int p_index) { index_array.push_back(p_index); } -Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing) { - - Ref<ArrayMesh> mesh; - if (p_existing.is_valid()) - mesh = p_existing; - else - mesh.instance(); +Array SurfaceTool::commit_to_arrays() { int varr_len = vertex_array.size(); - if (varr_len == 0) - return mesh; - - int surface = mesh->get_surface_count(); - Array a; a.resize(Mesh::ARRAY_MAX); for (int i = 0; i < Mesh::ARRAY_MAX; i++) { - switch (format & (1 << i)) { + if (!(format & (1 << i))) + continue; //not in format + + switch (i) { - case Mesh::ARRAY_FORMAT_VERTEX: - case Mesh::ARRAY_FORMAT_NORMAL: { + case Mesh::ARRAY_VERTEX: + case Mesh::ARRAY_NORMAL: { PoolVector<Vector3> array; array.resize(varr_len); @@ -273,8 +265,8 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing) { } break; - case Mesh::ARRAY_FORMAT_TEX_UV: - case Mesh::ARRAY_FORMAT_TEX_UV2: { + case Mesh::ARRAY_TEX_UV: + case Mesh::ARRAY_TEX_UV2: { PoolVector<Vector2> array; array.resize(varr_len); @@ -299,7 +291,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing) { w = PoolVector<Vector2>::Write(); a[i] = array; } break; - case Mesh::ARRAY_FORMAT_TANGENT: { + case Mesh::ARRAY_TANGENT: { PoolVector<float> array; array.resize(varr_len * 4); @@ -323,7 +315,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing) { a[i] = array; } break; - case Mesh::ARRAY_FORMAT_COLOR: { + case Mesh::ARRAY_COLOR: { PoolVector<Color> array; array.resize(varr_len); @@ -339,7 +331,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing) { w = PoolVector<Color>::Write(); a[i] = array; } break; - case Mesh::ARRAY_FORMAT_BONES: { + case Mesh::ARRAY_BONES: { PoolVector<int> array; array.resize(varr_len * 4); @@ -361,7 +353,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing) { a[i] = array; } break; - case Mesh::ARRAY_FORMAT_WEIGHTS: { + case Mesh::ARRAY_WEIGHTS: { PoolVector<float> array; array.resize(varr_len * 4); @@ -383,7 +375,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing) { a[i] = array; } break; - case Mesh::ARRAY_FORMAT_INDEX: { + case Mesh::ARRAY_INDEX: { ERR_CONTINUE(index_array.size() == 0); @@ -398,6 +390,7 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing) { } w = PoolVector<int>::Write(); + a[i] = array; } break; @@ -405,6 +398,26 @@ Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing) { } } + return a; +} + +Ref<ArrayMesh> SurfaceTool::commit(const Ref<ArrayMesh> &p_existing) { + + Ref<ArrayMesh> mesh; + if (p_existing.is_valid()) + mesh = p_existing; + else + mesh.instance(); + + int varr_len = vertex_array.size(); + + if (varr_len == 0) + return mesh; + + int surface = mesh->get_surface_count(); + + Array a = commit_to_arrays(); + mesh->add_surface_from_arrays(primitive, a); if (material.is_valid()) mesh->surface_set_material(surface, material); @@ -459,12 +472,17 @@ void SurfaceTool::deindex() { vertex_array.push_back(varr[E->get()]); } format &= ~Mesh::ARRAY_FORMAT_INDEX; + index_array.clear(); } void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, List<Vertex> *r_vertex, List<int> *r_index, int &lformat) { Array arr = p_existing->surface_get_arrays(p_surface); ERR_FAIL_COND(arr.size() != VS::ARRAY_MAX); + _create_list_from_arrays(arr, r_vertex, r_index, lformat); +} + +void SurfaceTool::_create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, int &lformat) { PoolVector<Vector3> varr = arr[VS::ARRAY_VERTEX]; PoolVector<Vector3> narr = arr[VS::ARRAY_NORMAL]; @@ -536,7 +554,7 @@ void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, List< if (lformat & VS::ARRAY_FORMAT_TANGENT) { Plane p(tarr[i * 4 + 0], tarr[i * 4 + 1], tarr[i * 4 + 2], tarr[i * 4 + 3]); v.tangent = p.normal; - v.binormal = p.normal.cross(last_normal).normalized() * p.d; + v.binormal = p.normal.cross(v.tangent).normalized() * p.d; } if (lformat & VS::ARRAY_FORMAT_COLOR) v.color = carr[i]; @@ -580,6 +598,13 @@ void SurfaceTool::_create_list(const Ref<Mesh> &p_existing, int p_surface, List< } } +void SurfaceTool::create_from_triangle_arrays(const Array &p_arrays) { + + clear(); + primitive = Mesh::PRIMITIVE_TRIANGLES; + _create_list_from_arrays(p_arrays, &vertex_array, &index_array, format); +} + void SurfaceTool::create_from(const Ref<Mesh> &p_existing, int p_surface) { clear(); @@ -711,8 +736,9 @@ void SurfaceTool::generate_tangents() { ERR_FAIL_COND(!res); format |= Mesh::ARRAY_FORMAT_TANGENT; - if (indexed) + if (indexed) { index(); + } } void SurfaceTool::generate_normals() { @@ -784,7 +810,6 @@ void SurfaceTool::generate_normals() { vertex_hash.clear(); if (E) { smooth = smooth_groups[count]; - print_line("SMOOTH AT " + itos(count) + ": " + itos(smooth)); } } } diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h index 753c3626b8..d02e170b02 100644 --- a/scene/resources/surface_tool.h +++ b/scene/resources/surface_tool.h @@ -80,6 +80,7 @@ private: Vector<float> last_weights; Plane last_tangent; + void _create_list_from_arrays(Array arr, List<Vertex> *r_vertex, List<int> *r_index, int &lformat); void _create_list(const Ref<Mesh> &p_existing, int p_surface, List<Vertex> *r_vertex, List<int> *r_index, int &lformat); //mikktspace callbacks @@ -123,6 +124,8 @@ public: List<Vertex> &get_vertex_array() { return vertex_array; } + void create_from_triangle_arrays(const Array &p_arrays); + Array commit_to_arrays(); void create_from(const Ref<Mesh> &p_existing, int p_surface); void append_from(const Ref<Mesh> &p_existing, int p_surface, const Transform &p_xform); Ref<ArrayMesh> commit(const Ref<ArrayMesh> &p_existing = Ref<ArrayMesh>()); diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp index fe7cd0097c..2b078f1985 100644 --- a/scene/resources/texture.cpp +++ b/scene/resources/texture.cpp @@ -73,6 +73,7 @@ void Texture::_bind_methods() { ClassDB::bind_method(D_METHOD("draw", "canvas_item", "pos", "modulate", "transpose", "normal_map:Texture"), &Texture::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("draw_rect", "canvas_item", "rect", "tile", "modulate", "transpose", "normal_map:Texture"), &Texture::draw_rect, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("draw_rect_region", "canvas_item", "rect", "src_rect", "modulate", "transpose", "normal_map:Texture", "clip_uv"), &Texture::draw_rect_region, DEFVAL(Color(1, 1, 1)), DEFVAL(false), DEFVAL(Variant()), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("get_data:Image", "cube_side"), &ImageTexture::get_data); BIND_CONSTANT(FLAG_MIPMAPS); BIND_CONSTANT(FLAG_REPEAT); @@ -194,6 +195,7 @@ void ImageTexture::create(int p_width, int p_height, Image::Format p_format, uin } void ImageTexture::create_from_image(const Ref<Image> &p_image, uint32_t p_flags) { + ERR_FAIL_COND(p_image.is_null()); flags = p_flags; w = p_image->get_width(); h = p_image->get_height(); @@ -352,7 +354,6 @@ void ImageTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("get_format"), &ImageTexture::get_format); ClassDB::bind_method(D_METHOD("load", "path"), &ImageTexture::load); ClassDB::bind_method(D_METHOD("set_data", "image:Image"), &ImageTexture::set_data); - ClassDB::bind_method(D_METHOD("get_data:Image", "cube_side"), &ImageTexture::get_data); ClassDB::bind_method(D_METHOD("set_storage", "mode"), &ImageTexture::set_storage); ClassDB::bind_method(D_METHOD("get_storage"), &ImageTexture::get_storage); ClassDB::bind_method(D_METHOD("set_lossy_storage_quality", "quality"), &ImageTexture::set_lossy_storage_quality); diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index ec71333ded..f247e7cde8 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -142,9 +142,9 @@ SceneStringNames::SceneStringNames() { h_offset = StaticCString::create("h_offset"); v_offset = StaticCString::create("v_offset"); - transform_pos = StaticCString::create("transform/pos"); - transform_rot = StaticCString::create("transform/rot"); - transform_scale = StaticCString::create("transform/scale"); + transform_pos = StaticCString::create("position"); + transform_rot = StaticCString::create("rotation_deg"); + transform_scale = StaticCString::create("scale"); _update_remote = StaticCString::create("_update_remote"); _update_pairs = StaticCString::create("_update_pairs"); @@ -158,8 +158,6 @@ SceneStringNames::SceneStringNames() { line_separation = StaticCString::create("line_separation"); - play_play = StaticCString::create("play/play"); - get_drag_data = StaticCString::create("get_drag_data"); drop_data = StaticCString::create("drop_data"); can_drop_data = StaticCString::create("can_drop_data"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 0802a73973..0b70cd36ff 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -30,7 +30,7 @@ #ifndef SCENE_STRING_NAMES_H #define SCENE_STRING_NAMES_H -#include "path_db.h" +#include "node_path.h" #include "string_db.h" class SceneStringNames { @@ -173,8 +173,6 @@ public: StringName _get_minimum_size; - StringName play_play; - StringName _im_update; StringName _queue_update; diff --git a/servers/arvr/arvr_positional_tracker.cpp b/servers/arvr/arvr_positional_tracker.cpp index 4215363d16..9f3d01267b 100644 --- a/servers/arvr/arvr_positional_tracker.cpp +++ b/servers/arvr/arvr_positional_tracker.cpp @@ -40,6 +40,13 @@ void ARVRPositionalTracker::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tracks_position"), &ARVRPositionalTracker::get_tracks_position); ClassDB::bind_method(D_METHOD("get_position"), &ARVRPositionalTracker::get_position); ClassDB::bind_method(D_METHOD("get_transform", "adjust_by_reference_frame"), &ARVRPositionalTracker::get_transform); + + // these functions we don't want to expose to normal users but do need to be callable from GDNative + ClassDB::bind_method(D_METHOD("_set_type", "type"), &ARVRPositionalTracker::set_type); + ClassDB::bind_method(D_METHOD("_set_name", "name"), &ARVRPositionalTracker::set_name); + ClassDB::bind_method(D_METHOD("_set_joy_id", "joy_id"), &ARVRPositionalTracker::set_joy_id); + ClassDB::bind_method(D_METHOD("_set_orientation", "orientation"), &ARVRPositionalTracker::set_orientation); + ClassDB::bind_method(D_METHOD("_set_rw_position", "rw_position"), &ARVRPositionalTracker::set_rw_position); }; void ARVRPositionalTracker::set_type(ARVRServer::TrackerType p_type) { @@ -102,14 +109,36 @@ bool ARVRPositionalTracker::get_tracks_position() const { void ARVRPositionalTracker::set_position(const Vector3 &p_position) { _THREAD_SAFE_METHOD_ + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL(arvr_server); + real_t world_scale = arvr_server->get_world_scale(); + ERR_FAIL_COND(world_scale == 0); + tracks_position = true; // obviously we have this - position = p_position; + rw_position = p_position / world_scale; }; Vector3 ARVRPositionalTracker::get_position() const { _THREAD_SAFE_METHOD_ - return position; + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, rw_position); + real_t world_scale = arvr_server->get_world_scale(); + + return rw_position * world_scale; +}; + +void ARVRPositionalTracker::set_rw_position(const Vector3 &p_rw_position) { + _THREAD_SAFE_METHOD_ + + tracks_position = true; // obviously we have this + rw_position = p_rw_position; +}; + +Vector3 ARVRPositionalTracker::get_rw_position() const { + _THREAD_SAFE_METHOD_ + + return rw_position; }; Transform ARVRPositionalTracker::get_transform(bool p_adjust_by_reference_frame) const { diff --git a/servers/arvr/arvr_positional_tracker.h b/servers/arvr/arvr_positional_tracker.h index e8c613b29d..dba203b73c 100644 --- a/servers/arvr/arvr_positional_tracker.h +++ b/servers/arvr/arvr_positional_tracker.h @@ -56,7 +56,7 @@ private: bool tracks_orientation; // do we track orientation? Basis orientation; // our orientation bool tracks_position; // do we track position? - Vector3 position; // our position + Vector3 rw_position; // our position "in the real world, so without world_scale applied" protected: static void _bind_methods(); @@ -73,8 +73,10 @@ public: void set_orientation(const Basis &p_orientation); Basis get_orientation() const; bool get_tracks_position() const; - void set_position(const Vector3 &p_position); - Vector3 get_position() const; + void set_position(const Vector3 &p_position); // set position with world_scale applied + Vector3 get_position() const; // get position with world_scale applied + void set_rw_position(const Vector3 &p_rw_position); + Vector3 get_rw_position() const; Transform get_transform(bool p_adjust_by_reference_frame) const; diff --git a/thirdparty/misc/base64.h b/thirdparty/misc/base64.h index 456ef1811b..4c300382c1 100644 --- a/thirdparty/misc/base64.h +++ b/thirdparty/misc/base64.h @@ -11,9 +11,8 @@ extern "C" { -uint32_t base64_encode (char* to, char* from, uint32_t len); -uint32_t base64_decode (char* to, char* from, uint32_t len); - +uint32_t base64_encode(char *to, char *from, uint32_t len); +uint32_t base64_decode(char *to, char *from, uint32_t len); }; #endif /* BASE64_H */ |