diff options
242 files changed, 6286 insertions, 1055 deletions
diff --git a/SConstruct b/SConstruct index 9783b3eaca..45765976cd 100644 --- a/SConstruct +++ b/SConstruct @@ -288,7 +288,7 @@ if selected_platform in platform_list: if (env["warnings"] == 'yes'): print("WARNING: warnings=yes is deprecated; assuming warnings=all") - if (os.name == "nt" and os.getenv("VSINSTALLDIR") and (platform_arg == "windows" or platform_arg == "uwp")): # MSVC, needs to stand out of course + if (os.name == "nt" and os.getenv("VCINSTALLDIR") and (platform_arg == "windows" or platform_arg == "uwp")): # MSVC, needs to stand out of course disable_nonessential_warnings = ['/wd4267', '/wd4244', '/wd4305', '/wd4800'] # Truncations, narrowing conversions... if (env["warnings"] == 'extra'): env.Append(CCFLAGS=['/Wall']) # Implies /W4 @@ -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 1abe019167..85e627f352 100644 --- a/core/input_map.cpp +++ b/core/input_map.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "input_map.h" -#include "project_settings.h" #include "os/keyboard.h" +#include "project_settings.h" InputMap *InputMap::singleton = NULL; @@ -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/io/compression.cpp b/core/io/compression.cpp index ca35ece8f5..139383710c 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "compression.h" -#include "project_settings.h" #include "os/copymem.h" +#include "project_settings.h" #include "zip_io.h" #include "thirdparty/misc/fastlz.h" diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index 2197b187eb..8e719568e5 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -29,10 +29,10 @@ /*************************************************************************/ #include "file_access_memory.h" -#include "project_settings.h" #include "map.h" #include "os/copymem.h" #include "os/dir_access.h" +#include "project_settings.h" static Map<String, Vector<uint8_t> > *files = NULL; diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp index 46457d1425..aa67479d7e 100644 --- a/core/io/file_access_network.cpp +++ b/core/io/file_access_network.cpp @@ -28,10 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "file_access_network.h" -#include "project_settings.h" #include "io/ip.h" #include "marshalls.h" #include "os/os.h" +#include "project_settings.h" //#define DEBUG_PRINT(m_p) print_line(m_p) //#define DEBUG_TIME(m_what) printf("MS: %s - %lli\n",m_what,OS::get_singleton()->get_ticks_usec()); diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 81446a3886..a3d33593dd 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "packet_peer.h" -#include "project_settings.h" #include "io/marshalls.h" +#include "project_settings.h" /* helpers / binders */ PacketPeer::PacketPeer() { diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 602cbe6f30..9aa16ed7e7 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -28,11 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "resource_format_binary.h" -#include "project_settings.h" #include "image.h" #include "io/file_access_compressed.h" #include "io/marshalls.h" #include "os/dir_access.h" +#include "project_settings.h" #include "version.h" //#define print_bl(m_what) print_line(m_what) #define print_bl(m_what) diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 58913b80cc..314259b2e9 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "resource_saver.h" -#include "project_settings.h" #include "os/file_access.h" +#include "project_settings.h" #include "resource_loader.h" #include "script_language.h" diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index 33ad522315..a1666ccd8b 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -91,6 +91,72 @@ void CameraMatrix::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_ matrix[3][3] = 0; } +void CameraMatrix::set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist) { + if (p_flip_fov) { + p_fovy_degrees = get_fovy(p_fovy_degrees, 1.0 / p_aspect); + } + + real_t left, right, modeltranslation, ymax, xmax, frustumshift; + + ymax = p_z_near * tan(p_fovy_degrees * Math_PI / 360.0f); + xmax = ymax * p_aspect; + frustumshift = (p_intraocular_dist / 2.0) * p_z_near / p_convergence_dist; + + switch (p_eye) { + case 1: { // left eye + left = -xmax + frustumshift; + right = xmax + frustumshift; + modeltranslation = p_intraocular_dist / 2.0; + }; break; + case 2: { // right eye + left = -xmax - frustumshift; + right = xmax - frustumshift; + modeltranslation = -p_intraocular_dist / 2.0; + }; break; + default: { // mono, should give the same result as set_perspective(p_fovy_degrees,p_aspect,p_z_near,p_z_far,p_flip_fov) + left = -xmax; + right = xmax; + modeltranslation = 0.0; + }; break; + }; + + set_frustum(left, right, -ymax, ymax, p_z_near, p_z_far); + + // translate matrix by (modeltranslation, 0.0, 0.0) + CameraMatrix cm; + cm.set_identity(); + cm.matrix[3][0] = modeltranslation; + *this = *this * cm; +} + +void CameraMatrix::set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far) { + // we first calculate our base frustum on our values without taking our lens magnification into account. + real_t display_to_eye = 2.0 * p_display_to_lens; + real_t f1 = (p_intraocular_dist * 0.5) / p_display_to_lens; + real_t f2 = ((p_display_width - p_intraocular_dist) * 0.5) / p_display_to_lens; + real_t f3 = (p_display_width / 4.0) / p_display_to_lens; + + // now we apply our oversample factor to increase our FOV. how much we oversample is always a balance we strike between performance and how much + // we're willing to sacrifice in FOV. + real_t add = ((f1 + f2) * (p_oversample - 1.0)) / 2.0; + f1 += add; + f2 += add; + + // always apply KEEP_WIDTH aspect ratio + f3 *= p_aspect; + + switch (p_eye) { + case 1: { // left eye + set_frustum(-f2 * p_z_near, f1 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); + }; break; + case 2: { // right eye + set_frustum(-f1 * p_z_near, f2 * p_z_near, -f3 * p_z_near, f3 * p_z_near, p_z_near, p_z_far); + }; break; + default: { // mono, does not apply here! + }; break; + }; +}; + void CameraMatrix::set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar) { set_identity(); @@ -243,23 +309,44 @@ bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8point -matrix[15] + matrix[13]); top_plane.normalize(); - Vector3 near_endpoint; - Vector3 far_endpoint; + Vector3 near_endpoint_left, near_endpoint_right; + Vector3 far_endpoint_left, far_endpoint_right; - bool res = near_plane.intersect_3(right_plane, top_plane, &near_endpoint); + bool res = near_plane.intersect_3(right_plane, top_plane, &near_endpoint_right); ERR_FAIL_COND_V(!res, false); - res = far_plane.intersect_3(right_plane, top_plane, &far_endpoint); + res = far_plane.intersect_3(right_plane, top_plane, &far_endpoint_right); ERR_FAIL_COND_V(!res, false); - p_8points[0] = p_transform.xform(Vector3(near_endpoint.x, near_endpoint.y, near_endpoint.z)); - p_8points[1] = p_transform.xform(Vector3(near_endpoint.x, -near_endpoint.y, near_endpoint.z)); - p_8points[2] = p_transform.xform(Vector3(-near_endpoint.x, near_endpoint.y, near_endpoint.z)); - p_8points[3] = p_transform.xform(Vector3(-near_endpoint.x, -near_endpoint.y, near_endpoint.z)); - p_8points[4] = p_transform.xform(Vector3(far_endpoint.x, far_endpoint.y, far_endpoint.z)); - p_8points[5] = p_transform.xform(Vector3(far_endpoint.x, -far_endpoint.y, far_endpoint.z)); - p_8points[6] = p_transform.xform(Vector3(-far_endpoint.x, far_endpoint.y, far_endpoint.z)); - p_8points[7] = p_transform.xform(Vector3(-far_endpoint.x, -far_endpoint.y, far_endpoint.z)); + if ((matrix[8] == 0) && (matrix[9] == 0)) { + near_endpoint_left = near_endpoint_right; + near_endpoint_left.x = -near_endpoint_left.x; + + far_endpoint_left = far_endpoint_right; + far_endpoint_left.x = -far_endpoint_left.x; + } else { + ///////--- Left Plane ---/////// + Plane left_plane = Plane(matrix[0] + matrix[3], + matrix[4] + matrix[7], + matrix[8] + matrix[11], + -matrix[15] - matrix[12]); + left_plane.normalize(); + + res = near_plane.intersect_3(left_plane, top_plane, &near_endpoint_left); + ERR_FAIL_COND_V(!res, false); + + res = far_plane.intersect_3(left_plane, top_plane, &far_endpoint_left); + ERR_FAIL_COND_V(!res, false); + } + + p_8points[0] = p_transform.xform(Vector3(near_endpoint_right.x, near_endpoint_right.y, near_endpoint_right.z)); + p_8points[1] = p_transform.xform(Vector3(near_endpoint_right.x, -near_endpoint_right.y, near_endpoint_right.z)); + p_8points[2] = p_transform.xform(Vector3(near_endpoint_left.x, near_endpoint_left.y, near_endpoint_left.z)); + p_8points[3] = p_transform.xform(Vector3(near_endpoint_left.x, -near_endpoint_left.y, near_endpoint_left.z)); + p_8points[4] = p_transform.xform(Vector3(far_endpoint_right.x, far_endpoint_right.y, far_endpoint_right.z)); + p_8points[5] = p_transform.xform(Vector3(far_endpoint_right.x, -far_endpoint_right.y, far_endpoint_right.z)); + p_8points[6] = p_transform.xform(Vector3(far_endpoint_left.x, far_endpoint_left.y, far_endpoint_left.z)); + p_8points[7] = p_transform.xform(Vector3(far_endpoint_left.x, -far_endpoint_left.y, far_endpoint_left.z)); return true; } @@ -546,7 +633,18 @@ real_t CameraMatrix::get_fov() const { -matrix[15] + matrix[12]); right_plane.normalize(); - return Math::rad2deg(Math::acos(Math::abs(right_plane.normal.x))) * 2.0; + if ((matrix[8] == 0) && (matrix[9] == 0)) { + return Math::rad2deg(Math::acos(Math::abs(right_plane.normal.x))) * 2.0; + } else { + // our frustum is asymetrical need to calculate the left planes angle seperately.. + Plane left_plane = Plane(matrix[3] + matrix[0], + matrix[7] + matrix[4], + matrix[11] + matrix[8], + matrix[15] + matrix[12]); + left_plane.normalize(); + + return Math::rad2deg(Math::acos(Math::abs(left_plane.normal.x))) + Math::rad2deg(Math::acos(Math::abs(right_plane.normal.x))); + } } void CameraMatrix::make_scale(const Vector3 &p_scale) { diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h index af61e35993..4be8ffab8c 100644 --- a/core/math/camera_matrix.h +++ b/core/math/camera_matrix.h @@ -54,6 +54,8 @@ struct CameraMatrix { void set_light_bias(); void set_light_atlas_rect(const Rect2 &p_rect); void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false); + void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist); + void set_for_hmd(int p_eye, real_t p_aspect, real_t p_intraocular_dist, real_t p_display_width, real_t p_display_to_lens, real_t p_oversample, real_t p_z_near, real_t p_z_far); void set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar); void set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false); void set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far); diff --git a/core/math/face3.h b/core/math/face3.h index 1cc94321c3..3d02ae4014 100644 --- a/core/math/face3.h +++ b/core/math/face3.h @@ -115,20 +115,20 @@ bool Face3::intersects_aabb2(const Rect3 &p_aabb) const { if (dist_a * dist_b > 0) return false; //does not intersect the plane -#define TEST_AXIS(m_ax) \ - { \ +#define TEST_AXIS(m_ax) \ + { \ real_t aabb_min = p_aabb.position.m_ax; \ real_t aabb_max = p_aabb.position.m_ax + p_aabb.size.m_ax; \ - real_t tri_min, tri_max; \ - for (int i = 0; i < 3; i++) { \ - if (i == 0 || vertex[i].m_ax > tri_max) \ - tri_max = vertex[i].m_ax; \ - if (i == 0 || vertex[i].m_ax < tri_min) \ - tri_min = vertex[i].m_ax; \ - } \ - \ - if (tri_max < aabb_min || aabb_max < tri_min) \ - return false; \ + real_t tri_min, tri_max; \ + for (int i = 0; i < 3; i++) { \ + if (i == 0 || vertex[i].m_ax > tri_max) \ + tri_max = vertex[i].m_ax; \ + if (i == 0 || vertex[i].m_ax < tri_min) \ + tri_min = vertex[i].m_ax; \ + } \ + \ + if (tri_max < aabb_min || aabb_max < tri_min) \ + return false; \ } TEST_AXIS(x); 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/dir_access.cpp b/core/os/dir_access.cpp index 484942bad5..391ae78c85 100644 --- a/core/os/dir_access.cpp +++ b/core/os/dir_access.cpp @@ -28,10 +28,10 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "dir_access.h" -#include "project_settings.h" #include "os/file_access.h" #include "os/memory.h" #include "os/os.h" +#include "project_settings.h" String DirAccess::_get_root_path() const { diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp index 7f5a84843c..3bd5ac3f41 100644 --- a/core/os/file_access.cpp +++ b/core/os/file_access.cpp @@ -31,8 +31,8 @@ #include "core/io/file_access_pack.h" #include "core/io/marshalls.h" -#include "project_settings.h" #include "os/os.h" +#include "project_settings.h" #include "thirdparty/misc/md5.h" #include "thirdparty/misc/sha256.h" diff --git a/core/os/input.cpp b/core/os/input.cpp index 18d644668c..a90a552d1d 100644 --- a/core/os/input.cpp +++ b/core/os/input.cpp @@ -28,9 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "input.h" -#include "project_settings.h" #include "input_map.h" #include "os/os.h" +#include "project_settings.h" Input *Input::singleton = NULL; Input *Input::get_singleton() { 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/os/memory.cpp b/core/os/memory.cpp index 069ee48fae..acc960acd9 100644 --- a/core/os/memory.cpp +++ b/core/os/memory.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "memory.h" #include "copymem.h" +#include "core/safe_refcount.h" #include "error_macros.h" #include <stdio.h> #include <stdlib.h> @@ -43,14 +44,12 @@ void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)) { return p_allocfunc(p_size); } -#include <stdio.h> - #ifdef DEBUG_ENABLED -size_t Memory::mem_usage = 0; -size_t Memory::max_usage = 0; +uint64_t Memory::mem_usage = 0; +uint64_t Memory::max_usage = 0; #endif -size_t Memory::alloc_count = 0; +uint64_t Memory::alloc_count = 0; void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) { @@ -62,10 +61,10 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) { void *mem = malloc(p_bytes + (prepad ? PAD_ALIGN : 0)); - alloc_count++; - ERR_FAIL_COND_V(!mem, NULL); + atomic_increment(&alloc_count); + if (prepad) { uint64_t *s = (uint64_t *)mem; *s = p_bytes; @@ -73,10 +72,8 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) { uint8_t *s8 = (uint8_t *)mem; #ifdef DEBUG_ENABLED - mem_usage += p_bytes; - if (mem_usage > max_usage) { - max_usage = mem_usage; - } + atomic_add(&mem_usage, p_bytes); + atomic_exchange_if_greater(&max_usage, mem_usage); #endif return s8 + PAD_ALIGN; } else { @@ -103,8 +100,12 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) { uint64_t *s = (uint64_t *)mem; #ifdef DEBUG_ENABLED - mem_usage -= *s; - mem_usage += p_bytes; + if (p_bytes > *s) { + atomic_add(&mem_usage, p_bytes - *s); + atomic_exchange_if_greater(&max_usage, mem_usage); + } else { + atomic_sub(&mem_usage, *s - p_bytes); + } #endif if (p_bytes == 0) { @@ -144,14 +145,14 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) { bool prepad = p_pad_align; #endif - alloc_count--; + atomic_decrement(&alloc_count); if (prepad) { mem -= PAD_ALIGN; uint64_t *s = (uint64_t *)mem; #ifdef DEBUG_ENABLED - mem_usage -= *s; + atomic_sub(&mem_usage, *s); #endif free(mem); @@ -161,19 +162,20 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) { } } -size_t Memory::get_mem_available() { +uint64_t Memory::get_mem_available() { - return 0xFFFFFFFFFFFFF; + return -1; // 0xFFFF... } -size_t Memory::get_mem_usage() { +uint64_t Memory::get_mem_usage() { #ifdef DEBUG_ENABLED return mem_usage; #else return 0; #endif } -size_t Memory::get_mem_max_usage() { + +uint64_t Memory::get_mem_max_usage() { #ifdef DEBUG_ENABLED return max_usage; #else diff --git a/core/os/memory.h b/core/os/memory.h index b3eb599955..e1d7138ad5 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -45,20 +45,20 @@ class Memory { Memory(); #ifdef DEBUG_ENABLED - static size_t mem_usage; - static size_t max_usage; + static uint64_t mem_usage; + static uint64_t max_usage; #endif - static size_t alloc_count; + static uint64_t alloc_count; public: static void *alloc_static(size_t p_bytes, bool p_pad_align = false); static void *realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align = false); static void free_static(void *p_ptr, bool p_pad_align = false); - static size_t get_mem_available(); - static size_t get_mem_usage(); - static size_t get_mem_max_usage(); + static uint64_t get_mem_available(); + static uint64_t get_mem_usage(); + static uint64_t get_mem_max_usage(); }; class DefaultAllocator { diff --git a/core/project_settings.cpp b/core/project_settings.cpp index 0426af3fa0..f6e0d2e991 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -455,8 +455,10 @@ Error ProjectSettings::_load_settings(const String p_path) { memdelete(f); ERR_FAIL_COND_V(config_version > FORMAT_VERSION, ERR_FILE_CANT_OPEN); } + } else { + // config_version is checked and dropped + set(section + "/" + assign, value); } - set(section + "/" + assign, value); } else if (next_tag.name != String()) { section = next_tag.name; } @@ -600,6 +602,15 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin ERR_FAIL_COND_V(err, err) } + file->store_line("; Engine configuration file."); + file->store_line("; It's best edited using the editor UI and not directly,"); + file->store_line("; since the parameters that go here are not all obvious."); + file->store_line("; "); + file->store_line("; Format: "); + file->store_line("; [section] ; section goes between []"); + file->store_line("; param=value ; assign values to parameters"); + file->store_line(""); + file->store_string("config_version=" + itos(FORMAT_VERSION) + "\n"); if (p_custom_features != String()) file->store_string("custom_features=\"" + p_custom_features + "\"\n"); @@ -640,38 +651,43 @@ Error ProjectSettings::_save_custom_bnd(const String &p_file) { // add other par return save_custom(p_file); }; -Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_custom, const Vector<String> &p_custom_features) { +Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_custom, const Vector<String> &p_custom_features, bool p_merge_with_current) { ERR_FAIL_COND_V(p_path == "", ERR_INVALID_PARAMETER); Set<_VCSort> vclist; - for (Map<StringName, VariantContainer>::Element *G = props.front(); G; G = G->next()) { + if (p_merge_with_current) { + for (Map<StringName, VariantContainer>::Element *G = props.front(); G; G = G->next()) { - const VariantContainer *v = &G->get(); + const VariantContainer *v = &G->get(); - if (v->hide_from_editor) - continue; + if (v->hide_from_editor) + continue; - if (p_custom.has(G->key())) - continue; + if (p_custom.has(G->key())) + continue; - _VCSort vc; - vc.name = G->key(); //*k; - vc.order = v->order; - vc.type = v->variant.get_type(); - vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE; - if (v->variant == v->initial) - continue; + _VCSort vc; + vc.name = G->key(); //*k; + vc.order = v->order; + vc.type = v->variant.get_type(); + vc.flags = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE; + if (v->variant == v->initial) + continue; - vclist.insert(vc); + vclist.insert(vc); + } } for (const Map<String, Variant>::Element *E = p_custom.front(); E; E = E->next()) { + // Lookup global prop to store in the same order + Map<StringName, VariantContainer>::Element *global_prop = props.find(E->key()); + _VCSort vc; vc.name = E->key(); - vc.order = 0xFFFFFFF; + vc.order = global_prop ? global_prop->get().order : 0xFFFFFFF; vc.type = E->get().get_type(); vc.flags = PROPERTY_USAGE_STORAGE; vclist.insert(vc); @@ -909,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/project_settings.h b/core/project_settings.h index 278d4b8132..c58ac3ca49 100644 --- a/core/project_settings.h +++ b/core/project_settings.h @@ -138,7 +138,7 @@ public: Error setup(const String &p_path, const String &p_main_pack); - Error save_custom(const String &p_path = "", const CustomMap &p_custom = CustomMap(), const Vector<String> &p_custom_features = Vector<String>()); + Error save_custom(const String &p_path = "", const CustomMap &p_custom = CustomMap(), const Vector<String> &p_custom_features = Vector<String>(), bool p_merge_with_current = true); Error save(); void set_custom_property_info(const String &p_prop, const PropertyInfo &p_info); diff --git a/core/reference.h b/core/reference.h index 4e2d6c36c0..90f2791f4b 100644 --- a/core/reference.h +++ b/core/reference.h @@ -330,7 +330,7 @@ struct PtrToArg<Ref<T> > { _FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) { - return Ref<T>(reinterpret_cast<const T *>(p_ptr)); + return Ref<T>(const_cast<T *>(reinterpret_cast<const T *>(p_ptr))); } _FORCE_INLINE_ static void encode(Ref<T> p_val, const void *p_ptr) { @@ -355,7 +355,7 @@ struct PtrToArg<RefPtr> { _FORCE_INLINE_ static RefPtr convert(const void *p_ptr) { - return Ref<Reference>(reinterpret_cast<const Reference *>(p_ptr)).get_ref_ptr(); + return Ref<Reference>(const_cast<Reference *>(reinterpret_cast<const Reference *>(p_ptr))).get_ref_ptr(); } _FORCE_INLINE_ static void encode(RefPtr p_val, const void *p_ptr) { @@ -370,7 +370,7 @@ struct PtrToArg<const RefPtr &> { _FORCE_INLINE_ static RefPtr convert(const void *p_ptr) { - return Ref<Reference>(reinterpret_cast<const Reference *>(p_ptr)).get_ref_ptr(); + return Ref<Reference>(const_cast<Reference *>(reinterpret_cast<const Reference *>(p_ptr))).get_ref_ptr(); } }; diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 07715f9718..43f781af55 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -36,7 +36,6 @@ #include "core_string_names.h" #include "func_ref.h" #include "geometry.h" -#include "project_settings.h" #include "input_map.h" #include "io/config_file.h" #include "io/http_client.h" @@ -54,6 +53,7 @@ #include "os/main_loop.h" #include "packed_data_container.h" #include "path_remap.h" +#include "project_settings.h" #include "translation.h" #include "undo_redo.h" 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/safe_refcount.cpp b/core/safe_refcount.cpp index d7e5297321..971e9ad1d5 100644 --- a/core/safe_refcount.cpp +++ b/core/safe_refcount.cpp @@ -78,6 +78,15 @@ static _ALWAYS_INLINE_ T _atomic_add_impl(register T *pw, register T val) { return *pw; } +template <class T> +static _ALWAYS_INLINE_ T _atomic_exchange_if_greater_impl(register T *pw, register T val) { + + if (val > *pw) + *pw = val; + + return *pw; +} + #elif defined(__GNUC__) /* Implementation for GCC & Clang */ @@ -121,6 +130,18 @@ static _ALWAYS_INLINE_ T _atomic_add_impl(register T *pw, register T val) { return __sync_add_and_fetch(pw, val); } +template <class T> +static _ALWAYS_INLINE_ T _atomic_exchange_if_greater_impl(register T *pw, register T val) { + + while (true) { + T tmp = static_cast<T const volatile &>(*pw); + if (tmp >= val) + return tmp; // already greater, or equal + if (__sync_val_compare_and_swap(pw, tmp, val) == tmp) + return val; + } +} + #elif defined(_MSC_VER) /* Implementation for MSVC-Windows */ @@ -139,6 +160,15 @@ static _ALWAYS_INLINE_ T _atomic_add_impl(register T *pw, register T val) { return tmp + 1; \ } +#define ATOMIC_EXCHANGE_IF_GREATER_BODY(m_pw, m_val, m_win_type, m_win_cmpxchg, m_cpp_type) \ + while (true) { \ + m_cpp_type tmp = static_cast<m_cpp_type const volatile &>(*(m_pw)); \ + if (tmp >= m_val) \ + return tmp; /* already greater, or equal */ \ + if (m_win_cmpxchg((m_win_type volatile *)(m_pw), m_val, tmp) == tmp) \ + return m_val; \ + } + static _ALWAYS_INLINE_ uint32_t _atomic_conditional_increment_impl(register uint32_t *pw) { ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONG, InterlockedCompareExchange, uint32_t) @@ -156,11 +186,7 @@ static _ALWAYS_INLINE_ uint32_t _atomic_increment_impl(register uint32_t *pw) { static _ALWAYS_INLINE_ uint32_t _atomic_sub_impl(register uint32_t *pw, register uint32_t val) { -#if _WIN32_WINNT >= 0x0601 // Windows 7+ - return InterlockedExchangeSubtract(pw, val) - val; -#else return InterlockedExchangeAdd((LONG volatile *)pw, -(int32_t)val) - val; -#endif } static _ALWAYS_INLINE_ uint32_t _atomic_add_impl(register uint32_t *pw, register uint32_t val) { @@ -168,6 +194,11 @@ static _ALWAYS_INLINE_ uint32_t _atomic_add_impl(register uint32_t *pw, register return InterlockedAdd((LONG volatile *)pw, val); } +static _ALWAYS_INLINE_ uint32_t _atomic_exchange_if_greater_impl(register uint32_t *pw, register uint32_t val) { + + ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONG, InterlockedCompareExchange, uint32_t) +} + static _ALWAYS_INLINE_ uint64_t _atomic_conditional_increment_impl(register uint64_t *pw) { ATOMIC_CONDITIONAL_INCREMENT_BODY(pw, LONGLONG, InterlockedCompareExchange64, uint64_t) @@ -185,11 +216,7 @@ static _ALWAYS_INLINE_ uint64_t _atomic_increment_impl(register uint64_t *pw) { static _ALWAYS_INLINE_ uint64_t _atomic_sub_impl(register uint64_t *pw, register uint64_t val) { -#if _WIN32_WINNT >= 0x0601 && !defined(UWP_ENABLED) // Windows 7+ except UWP - return InterlockedExchangeSubtract64(pw, val) - val; -#else return InterlockedExchangeAdd64((LONGLONG volatile *)pw, -(int64_t)val) - val; -#endif } static _ALWAYS_INLINE_ uint64_t _atomic_add_impl(register uint64_t *pw, register uint64_t val) { @@ -197,6 +224,11 @@ static _ALWAYS_INLINE_ uint64_t _atomic_add_impl(register uint64_t *pw, register return InterlockedAdd64((LONGLONG volatile *)pw, val); } +static _ALWAYS_INLINE_ uint64_t _atomic_exchange_if_greater_impl(register uint64_t *pw, register uint64_t val) { + + ATOMIC_EXCHANGE_IF_GREATER_BODY(pw, val, LONGLONG, InterlockedCompareExchange64, uint64_t) +} + #else //no threads supported? @@ -226,6 +258,10 @@ uint32_t atomic_add(register uint32_t *pw, register uint32_t val) { return _atomic_add_impl(pw, val); } +uint32_t atomic_exchange_if_greater(register uint32_t *pw, register uint32_t val) { + return _atomic_exchange_if_greater_impl(pw, val); +} + uint64_t atomic_conditional_increment(register uint64_t *counter) { return _atomic_conditional_increment_impl(counter); } @@ -245,3 +281,7 @@ uint64_t atomic_sub(register uint64_t *pw, register uint64_t val) { uint64_t atomic_add(register uint64_t *pw, register uint64_t val) { return _atomic_add_impl(pw, val); } + +uint64_t atomic_exchange_if_greater(register uint64_t *pw, register uint64_t val) { + return _atomic_exchange_if_greater_impl(pw, val); +} diff --git a/core/safe_refcount.h b/core/safe_refcount.h index a2d2b5e127..ed0620c777 100644 --- a/core/safe_refcount.h +++ b/core/safe_refcount.h @@ -41,12 +41,14 @@ uint32_t atomic_decrement(register uint32_t *pw); uint32_t atomic_increment(register uint32_t *pw); uint32_t atomic_sub(register uint32_t *pw, register uint32_t val); uint32_t atomic_add(register uint32_t *pw, register uint32_t val); +uint32_t atomic_exchange_if_greater(register uint32_t *pw, register uint32_t val); uint64_t atomic_conditional_increment(register uint64_t *counter); uint64_t atomic_decrement(register uint64_t *pw); uint64_t atomic_increment(register uint64_t *pw); uint64_t atomic_sub(register uint64_t *pw, register uint64_t val); uint64_t atomic_add(register uint64_t *pw, register uint64_t val); +uint64_t atomic_exchange_if_greater(register uint64_t *pw, register uint64_t val); struct SafeRefCount { diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index dec41e7976..d19fe213f6 100644 --- a/core/script_debugger_remote.cpp +++ b/core/script_debugger_remote.cpp @@ -29,10 +29,10 @@ /*************************************************************************/ #include "script_debugger_remote.h" -#include "project_settings.h" #include "io/ip.h" #include "os/input.h" #include "os/os.h" +#include "project_settings.h" void ScriptDebuggerRemote::_send_video_memory() { 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/translation.cpp b/core/translation.cpp index d782006ddc..50694e4a2d 100644 --- a/core/translation.cpp +++ b/core/translation.cpp @@ -29,9 +29,9 @@ /*************************************************************************/ #include "translation.h" -#include "project_settings.h" #include "io/resource_loader.h" #include "os/os.h" +#include "project_settings.h" static const char *locale_list[] = { "aa", // Afar 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 cd366f01f8..cd5b950f57 100644 --- a/doc/base/classes.xml +++ b/doc/base/classes.xml @@ -245,7 +245,7 @@ <description> Returns the floating-point remainder of x/y (rounded towards zero): [codeblock] - fmod = x - tquot * y + fmod = x - tquot * y [/codeblock] Where tquot is the truncated (i.e., rounded towards zero) result of: x/y. </description> @@ -746,13 +746,16 @@ [AudioServer] singleton </member> <member name="ClassDB" type="ClassDB" setter="" getter="" brief=""> + [ClassDB] singleton </member> <member name="Engine" type="Engine" setter="" getter="" brief=""> + [Engine] singleton </member> <member name="Geometry" type="Geometry" setter="" getter="" brief=""> [Geometry] singleton </member> <member name="ProjectSettings" type="ProjectSettings" setter="" getter="" brief=""> + [ProjectSettings] singleton </member> <member name="IP" type="IP" setter="" getter="" brief=""> [IP] singleton @@ -843,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"> @@ -4632,7 +4635,7 @@ <argument index="0" name="from" type="PoolColorArray"> </argument> <description> - Construct an array from a [PoolByteArray]. + Construct an array from a [PoolColorArray]. </description> </method> <method name="Array"> @@ -4641,7 +4644,7 @@ <argument index="0" name="from" type="PoolVector3Array"> </argument> <description> - Construct an array from a [PoolByteArray]. + Construct an array from a [PoolVector3Array]. </description> </method> <method name="Array"> @@ -4650,7 +4653,7 @@ <argument index="0" name="from" type="PoolVector2Array"> </argument> <description> - Construct an array from a [PoolByteArray]. + Construct an array from a [PoolVector2Array]. </description> </method> <method name="Array"> @@ -4677,7 +4680,7 @@ <argument index="0" name="from" type="PoolIntArray"> </argument> <description> - Construct an array from a [PoolByteArray]. + Construct an array from a [PoolIntArray]. </description> </method> <method name="Array"> @@ -10169,12 +10172,73 @@ <description> </description> </method> + <method name="create_shape_owner"> + <return type="int"> + </return> + <argument index="0" name="owner" type="Object"> + </argument> + <description> + Creates new holder for the shapes. Argument is a [CollisionShape] node. It will return owner_id which usually you will want to save for later use. + </description> + </method> <method name="get_rid" qualifiers="const"> <return type="RID"> </return> <description> </description> </method> + <method name="get_shape_owners"> + <return type="Array"> + </return> + <description> + Shape owner is a node which is holding concrete shape resources. This method will return an array which is holding an integer numbers that are representing unique ID of each owner. You can use those ids when you are using others shape_owner methods. + </description> + </method> + <method name="shape_owner_clear_shapes"> + <argument index="0" name="owner_id" type="int"> + </argument> + <description> + Will remove all the shapes associated with given owner. + </description> + </method> + <method name="shape_owner_get_shape"> + <return type="Shape"> + </return> + <argument index="0" name="owner_id" type="int"> + </argument> + <argument index="1" name="shape_id" type="int"> + </argument> + <description> + Will return a [Shape]. First argument owner_id is an integer that can be obtained from [method get_shape_owners]. Shape_id is a position of the shape inside owner; it's a value in range from 0 to [method shape_owner_get_shape_count]. + </description> + </method> + <method name="shape_owner_get_shape_count"> + <return type="int"> + </return> + <argument index="0" name="owner_id" type="int"> + </argument> + <description> + Returns number of shapes to which given owner is associated to. + </description> + </method> + <method name="shape_owner_get_transform"> + <return type="Transform"> + </return> + <argument index="0" name="owner_id" type="int"> + </argument> + <description> + Will return [Transform] of an owner node. + </description> + </method> + <method name="shape_owner_remove_shape"> + <argument index="0" name="owner_id" type="int"> + </argument> + <argument index="1" name="shape_id" type="int"> + </argument> + <description> + Removes related shape from the owner. + </description> + </method> <method name="is_ray_pickable" qualifiers="const"> <return type="bool"> </return> @@ -10245,6 +10309,15 @@ <description> </description> </method> + <method name="create_shape_owner"> + <return type="int"> + </return> + <argument index="0" name="owner" type="Object"> + </argument> + <description> + Creates new holder for the shapes. Argument is a [CollisionShape2D] node. It will return owner_id which usually you will want to save for later use. + </description> + </method> <method name="get_rid" qualifiers="const"> <return type="RID"> </return> @@ -10252,6 +10325,58 @@ Return the RID of this object. </description> </method> + <method name="get_shape_owners"> + <return type="Array"> + </return> + <description> + Shape owner is a node which is holding concrete shape resources. This method will return an array which is holding an integer numbers that are representing unique ID of each owner. You can use those ids when you are using others shape_owner methods. + </description> + </method> + <method name="shape_owner_clear_shapes"> + <argument index="0" name="owner_id" type="int"> + </argument> + <description> + Will remove all the shapes associated with given owner. + </description> + </method> + <method name="shape_owner_get_shape"> + <return type="Shape2D"> + </return> + <argument index="0" name="owner_id" type="int"> + </argument> + <argument index="1" name="shape_id" type="int"> + </argument> + <description> + Will return a [Shape2D]. First argument owner_id is an integer that can be obtained from [method get_shape_owners]. Shape_id is a position of the shape inside owner; it's a value in range from 0 to [method shape_owner_get_shape_count]. + </description> + </method> + <method name="shape_owner_get_shape_count"> + <return type="int"> + </return> + <argument index="0" name="owner_id" type="int"> + </argument> + <description> + Returns number of shapes to which given owner is associated to. + </description> + </method> + <method name="shape_owner_get_transform"> + <return type="Transform2D"> + </return> + <argument index="0" name="owner_id" type="int"> + </argument> + <description> + Will return [Transform2D] of an owner node. + </description> + </method> + <method name="shape_owner_remove_shape"> + <argument index="0" name="owner_id" type="int"> + </argument> + <argument index="1" name="shape_id" type="int"> + </argument> + <description> + Removes related shape from the owner. + </description> + </method> <method name="is_pickable" qualifiers="const"> <return type="bool"> </return> @@ -11394,7 +11519,7 @@ <return type="Rect2"> </return> <description> - Return position and size of the Control, relative to the top-left corner of the [i]window[/i] Control. This is a helper (see [method get_global_pos], [method get_size]). + Return position and size of the Control, relative to the top-left corner of the [i]window[/i] Control. This is a helper (see [method get_global_position], [method get_size]). </description> </method> <method name="get_h_grow_direction" qualifiers="const"> @@ -11472,7 +11597,7 @@ <return type="Rect2"> </return> <description> - Return position and size of the Control, relative to the top-left corner of the parent Control. This is a helper (see [method get_pos], [method get_size]). + Return position and size of the Control, relative to the top-left corner of the parent Control. This is a helper (see [method get_position], [method get_size]). </description> </method> <method name="get_rotation" qualifiers="const"> @@ -17793,7 +17918,7 @@ Contains global variables accessible from everywhere. </brief_description> <description> - Contains global variables accessible from everywhere. Use the normal [Object] API, such as "Globals.get(variable)", "Globals.set(variable,value)" or "Globals.has(variable)" to access them. Variables stored in project.godot are also loaded into globals, making this object very useful for reading custom game configuration options. + Contains global variables accessible from everywhere. Use the normal [Object] API, such as "ProjectSettings.get(variable)", "ProjectSettings.set(variable,value)" or "ProjectSettings.has(variable)" to access them. Variables stored in project.godot are also loaded into ProjectSettings, making this object very useful for reading custom game configuration options. </description> <methods> <method name="add_property_info"> @@ -17803,7 +17928,7 @@ Add a custom property info to a property. The dictionary must contain: name:[String](the name of the property) and type:[int](see TYPE_* in [@Global Scope]), and optionally hint:[int](see PROPERTY_HINT_* in [@Global Scope]), hint_string:[String]. Example: [codeblock] - Globals.set("category/property_name", 0) + ProjectSettings.set("category/property_name", 0) var property_info = { "name": "category/property_name", @@ -17812,7 +17937,7 @@ "hint_string": "one,two,three" } - Globals.add_property_info(property_info) + ProjectSettings.add_property_info(property_info) [/codeblock] </description> </method> @@ -21520,7 +21645,7 @@ </method> <method name="load_from_globals"> <description> - Clear the [InputMap] and load it anew from [Globals]. + Clear the [InputMap] and load it anew from [ProjectSettings]. </description> </method> </methods> @@ -22387,7 +22512,16 @@ <argument index="0" name="rel_vec" type="Vector3"> </argument> <description> - Move the body in the given direction, stopping if there is an obstacle. The returned vector is how much movement was remaining before being stopped. + Move the body in the given direction, stopping if there is an obstacle. If as a result of a movement there will be any collision then informations about this collision will be in returned dictionary. Dictionary will contains those keys: + - "position" - collision position + - "normal" - collision normal + - "local_shape" - id of this kinematic body shape that took part in a collision + - "travel" - traveled movement before being stopped + - "remainder" - remaining movement before being stopped + - "collider_id" - id of the collider, it can be used when dealing with [PhysicsServer] + - "collider" - colliding body + - "collider_shape_index" - index of the colliding shape, inside collider body "collider_metadata" + If the body did not intersect anything, then an empty dictionary (dir.empty()==true) is returned instead. Please note that this method is less user friendly than [method move_and_slide]. If you don't want to program each edge case manually, then it's recommended to use [method move_and_slide] instead. </description> </method> <method name="move_and_slide"> @@ -22569,7 +22703,16 @@ <argument index="0" name="rel_vec" type="Vector2"> </argument> <description> - Move the body in the given direction, stopping if there is an obstacle. The returned vector is how much movement was remaining before being stopped. + Move the body in the given direction, stopping if there is an obstacle. If as a result of a movement there will be any collision then informations about this collision will be in returned dictionary. Dictionary will contains those keys: + - "position" - collision position + - "normal" - collision normal + - "local_shape" - id of this kinematic body shape that took part in a collision + - "travel" - traveled movement before being stopped + - "remainder" - remaining movement before being stopped + - "collider_id" - id of the collider, it can be used when dealing with [Physics2DServer] + - "collider" - colliding body + - "collider_shape_index" - index of the colliding shape, inside collider body "collider_metadata" + If the body did not intersect anything, then an empty dictionary (dir.empty()==true) is returned instead. Please note that this method is less user friendly than [method move_and_slide]. If you don't want to program each edge case manually, then it's recommended to use [method move_and_slide] instead. </description> </method> <method name="move_and_slide"> @@ -24949,7 +25092,7 @@ MultiMesh provides low level mesh instancing. If the amount of [Mesh] instances needed goes from hundreds to thousands (and most need to be visible at close proximity) creating such a large amount of [MeshInstance] nodes may affect performance by using too much CPU or video memory. For this case a MultiMesh becomes very useful, as it can draw thousands of instances with little API overhead. As a drawback, if the instances are too far away of each other, performance may be reduced as every single instance will always rendered (they are spatially indexed as one, for the whole object). - Since instances may have any behavior, the Rect3 used for visibility must be provided by the user, or generated with [method generate_aabb]. + Since instances may have any behavior, the Rect3 used for visibility must be provided by the user. </description> <methods> <method name="get_aabb" qualifiers="const"> @@ -27879,7 +28022,7 @@ <return type="Array"> </return> <description> - Return the property list, array of dictionaries, dictionaries must contain: name:String, type:int (see TYPE_* enum in globals) and optionally: hint:int (see PROPERTY_HINT_* in globals), hint_string:String, usage:int (see PROPERTY_USAGE_* in globals). + Return the property list, array of dictionaries, dictionaries must contain: name:String, type:int (see TYPE_* enum in [@Global Scope]) and optionally: hint:int (see PROPERTY_HINT_* in [@Global Scope]), hint_string:String, usage:int (see PROPERTY_USAGE_* in [@Global Scope]). </description> </method> <method name="_init" qualifiers="virtual"> @@ -28040,7 +28183,7 @@ <return type="Array"> </return> <description> - Return the list of properties as an array of dictionaries, dictionaries contain: name:String, type:int (see TYPE_* enum in globals) and optionally: hint:int (see PROPERTY_HINT_* in globals), hint_string:String, usage:int (see PROPERTY_USAGE_* in globals). + Return the list of properties as an array of dictionaries, dictionaries contain: name:String, type:int (see TYPE_* enum in [@Global Scope]) and optionally: hint:int (see PROPERTY_HINT_* in [@Global Scope]), hint_string:String, usage:int (see PROPERTY_USAGE_* in [@Global Scope]). </description> </method> <method name="get_script" qualifiers="const"> @@ -34483,7 +34626,7 @@ <argument index="1" name="to" type="int"> </argument> <description> - Returns the slice of the [PoolByteArray] between indices (inclusive) as a new [RawArray]. Any negative index is considered to be from the end of the array. + Returns the slice of the [PoolByteArray] between indices (inclusive) as a new [PoolByteArray]. Any negative index is considered to be from the end of the array. </description> </method> </methods> @@ -37140,7 +37283,7 @@ <argument index="0" name="with" type="Rect3"> </argument> <description> - Return the intersection between two [Rect3]. An empty AABB (size 0,0,0) is returned on failure. + Return the intersection between two [Rect3]. An empty Rect3 (size 0,0,0) is returned on failure. </description> </method> <method name="intersects"> diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index bb7b85e653..3fc5bed80b 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -83,7 +83,6 @@ static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GL if (type == _EXT_DEBUG_TYPE_OTHER_ARB) return; - print_line("mesege"); char debSource[256], debType[256], debSev[256]; if (source == _EXT_DEBUG_SOURCE_API_ARB) strcpy(debSource, "OpenGL"); diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp index 2a9f078d8c..f7ecc3b158 100644 --- a/drivers/gles3/rasterizer_storage_gles3.cpp +++ b/drivers/gles3/rasterizer_storage_gles3.cpp @@ -5999,6 +5999,13 @@ void RasterizerStorageGLES3::_render_target_allocate(RenderTarget *rt) { static const int msaa_value[] = { 0, 2, 4, 8, 16 }; int msaa = msaa_value[rt->msaa]; + int max_samples = 0; + glGetIntegerv(GL_MAX_SAMPLES, &max_samples); + if (msaa > max_samples) { + WARN_PRINTS("MSAA must be <= GL_MAX_SAMPLES, falling-back to GL_MAX_SAMPLES = " + itos(max_samples)); + msaa = max_samples; + } + //regular fbo glGenFramebuffers(1, &rt->buffers.fbo); glBindFramebuffer(GL_FRAMEBUFFER, rt->buffers.fbo); @@ -6895,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.s3tc_supported = true; + config.rgtc_supported = true; //RGTC - core since OpenGL version 3.0 #else config.etc2_supported = true; config.hdr_supported = false; + 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.rgtc_supported = config.extensions.has("GL_EXT_texture_compression_rgtc") || config.extensions.has("GL_ARB_texture_compression_rgtc"); #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 acece2e639..efb82441f4 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -810,6 +810,9 @@ float contact_shadow_compute(vec3 pos, vec3 dir, float max_distance) { vec2 screen_rel = to_screen - from_screen; + if (length(screen_rel)<0.00001) + return 1.0; //too small, don't do anything + /*float pixel_size; //approximate pixel size if (screen_rel.x > screen_rel.y) { @@ -825,15 +828,16 @@ float contact_shadow_compute(vec3 pos, vec3 dir, float max_distance) { vec2 pixel_incr = normalize(screen_rel)*screen_pixel_size; - float steps = length(screen_rel) / length(pixel_incr); + float steps = length(screen_rel) / length(pixel_incr); + steps = min(2000.0,steps); //put a limit to avoid freezing in some strange situation //steps=10.0; vec4 incr = (dest - source)/steps; float ratio=0.0; float ratio_incr = 1.0/steps; - do { + while(steps>0.0) { source += incr*2.0; bias+=incr*2.0; @@ -851,7 +855,7 @@ float contact_shadow_compute(vec3 pos, vec3 dir, float max_distance) { ratio+=ratio_incr; steps-=1.0; - } while (steps>0.0); + } return 1.0; } @@ -1925,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/drivers/rtaudio/audio_driver_rtaudio.cpp b/drivers/rtaudio/audio_driver_rtaudio.cpp index da998db66f..3de25c32ad 100644 --- a/drivers/rtaudio/audio_driver_rtaudio.cpp +++ b/drivers/rtaudio/audio_driver_rtaudio.cpp @@ -79,7 +79,7 @@ int AudioDriverRtAudio::callback(void *outputBuffer, void *inputBuffer, unsigned Error AudioDriverRtAudio::init() { active = false; - mutex = NULL; + mutex = Mutex::create(true); dac = memnew(RtAudio); ERR_EXPLAIN("Cannot initialize RtAudio audio driver: No devices present.") @@ -136,7 +136,6 @@ Error AudioDriverRtAudio::init() { try { dac->openStream(¶meters, NULL, RTAUDIO_SINT32, mix_rate, &buffer_size, &callback, this, &options); - mutex = Mutex::create(true); active = true; break; @@ -162,6 +161,7 @@ Error AudioDriverRtAudio::init() { try { dac->closeStream(); + active = false; } catch (RtAudioError &e) { ERR_PRINT(e.what()); ERR_FAIL_V(ERR_UNAVAILABLE); @@ -212,17 +212,27 @@ void AudioDriverRtAudio::unlock() { void AudioDriverRtAudio::finish() { - if (active && dac->isStreamOpen()) + lock(); + if (active && dac->isStreamOpen()) { dac->closeStream(); - if (mutex) + active = false; + } + unlock(); + + if (mutex) { memdelete(mutex); - if (dac) + mutex = NULL; + } + if (dac) { memdelete(dac); + dac = NULL; + } } AudioDriverRtAudio::AudioDriverRtAudio() { mutex = NULL; + dac = NULL; mix_rate = 44100; speaker_mode = SPEAKER_MODE_STEREO; } diff --git a/editor/asset_library_editor_plugin.cpp b/editor/asset_library_editor_plugin.cpp index f92b70d0c0..559a0db57b 100644 --- a/editor/asset_library_editor_plugin.cpp +++ b/editor/asset_library_editor_plugin.cpp @@ -281,7 +281,7 @@ EditorAssetLibraryItemDescription::EditorAssetLibraryItemDescription() { description = memnew(RichTextLabel); description->connect("meta_clicked", this, "_link_click"); desc_bg->add_child(description); - desc_bg->add_style_override("panel", get_stylebox("normal", "TextEdit")); + desc_bg->add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("normal", "TextEdit")); preview = memnew(TextureRect); preview->set_custom_minimum_size(Size2(640, 345)); @@ -290,7 +290,7 @@ EditorAssetLibraryItemDescription::EditorAssetLibraryItemDescription() { PanelContainer *previews_bg = memnew(PanelContainer); vbox->add_child(previews_bg); previews_bg->set_custom_minimum_size(Size2(0, 85)); - previews_bg->add_style_override("panel", get_stylebox("normal", "TextEdit")); + previews_bg->add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("normal", "TextEdit")); previews = memnew(ScrollContainer); previews_bg->add_child(previews); @@ -1362,7 +1362,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { PanelContainer *library_scroll_bg = memnew(PanelContainer); library_main->add_child(library_scroll_bg); - library_scroll_bg->add_style_override("panel", get_stylebox("normal", "TextEdit")); + library_scroll_bg->add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("bg", "Tree")); library_scroll_bg->set_v_size_flags(SIZE_EXPAND_FILL); library_scroll = memnew(ScrollContainer); diff --git a/editor/collada/collada.h b/editor/collada/collada.h index 38e66a7e45..8945e14cce 100644 --- a/editor/collada/collada.h +++ b/editor/collada/collada.h @@ -32,9 +32,9 @@ #ifndef COLLADA_H #define COLLADA_H -#include "project_settings.h" #include "io/xml_parser.h" #include "map.h" +#include "project_settings.h" #include "scene/resources/material.h" class Collada { diff --git a/editor/doc/doc_data.cpp b/editor/doc/doc_data.cpp index fa90cd36b3..69d7475f4c 100644 --- a/editor/doc/doc_data.cpp +++ b/editor/doc/doc_data.cpp @@ -29,10 +29,10 @@ /*************************************************************************/ #include "doc_data.h" -#include "project_settings.h" #include "global_constants.h" #include "io/compression.h" #include "io/marshalls.h" +#include "project_settings.h" #include "scene/resources/theme.h" #include "script_language.h" #include "version.h" diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index 399c22bf2c..dde94cb334 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -30,8 +30,8 @@ #include "editor_autoload_settings.h" #include "editor_node.h" -#include "project_settings.h" #include "global_constants.h" +#include "project_settings.h" #define PREVIEW_LIST_MAX_SIZE 10 diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index f8dbd9abe5..c82e8f1226 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -31,10 +31,10 @@ #include "editor_node.h" #include "editor_settings.h" -#include "project_settings.h" #include "io/resource_loader.h" #include "os/dir_access.h" #include "os/file_access.h" +#include "project_settings.h" #include "scene/resources/packed_scene.h" void EditorHistory::_cleanup_history() { 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_help.cpp b/editor/editor_help.cpp index e890082ee1..8b1f558c0a 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -1621,12 +1621,18 @@ void EditorHelp::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { - //forward->set_icon(get_icon("Forward","EditorIcons")); //back->set_icon(get_icon("Back","EditorIcons")); _update_doc(); + } break; + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + Ref<StyleBoxFlat> style(memnew(StyleBoxFlat)); + style->set_bg_color(EditorSettings::get_singleton()->get("text_editor/highlighting/background_color")); + background_panel->add_style_override("panel", style); } break; + + default: break; } } @@ -1695,14 +1701,14 @@ EditorHelp::EditorHelp() { //class_list->set_selection_enabled(true); { - Panel *pc = memnew(Panel); + background_panel = memnew(Panel); Ref<StyleBoxFlat> style(memnew(StyleBoxFlat)); style->set_bg_color(EditorSettings::get_singleton()->get("text_editor/highlighting/background_color")); - pc->set_v_size_flags(SIZE_EXPAND_FILL); - pc->add_style_override("panel", style); //get_stylebox("normal","TextEdit")); - vbc->add_child(pc); + background_panel->set_v_size_flags(SIZE_EXPAND_FILL); + background_panel->add_style_override("panel", style); //get_stylebox("normal","TextEdit")); + vbc->add_child(background_panel); class_desc = memnew(RichTextLabel); - pc->add_child(class_desc); + background_panel->add_child(class_desc); class_desc->set_area_as_parent_rect(8); class_desc->connect("meta_clicked", this, "_class_desc_select"); class_desc->connect("gui_input", this, "_class_desc_input"); @@ -1782,7 +1788,7 @@ void EditorHelpBit::_bind_methods() { void EditorHelpBit::_notification(int p_what) { if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { - add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("ScriptPanel", "EditorStyles")); + add_style_override("panel", get_stylebox("ScriptPanel", "EditorStyles")); } } diff --git a/editor/editor_help.h b/editor/editor_help.h index 46d83490f4..de30b543fc 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -129,6 +129,7 @@ class EditorHelp : public VBoxContainer { HSplitContainer *h_split; static DocData *doc; + Panel *background_panel; ConfirmationDialog *search_dialog; LineEdit *search; 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 0cdb981306..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" @@ -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_plugin_settings.cpp b/editor/editor_plugin_settings.cpp index 6ab1468b1d..63bee33092 100644 --- a/editor/editor_plugin_settings.cpp +++ b/editor/editor_plugin_settings.cpp @@ -30,10 +30,10 @@ #include "editor_plugin_settings.h" #include "editor_node.h" -#include "project_settings.h" #include "io/config_file.h" #include "os/file_access.h" #include "os/main_loop.h" +#include "project_settings.h" #include "scene/gui/margin_container.h" void EditorPluginSettings::_notification(int p_what) { diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index 3a6b34b7b4..89fa004eb1 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -31,11 +31,11 @@ #include "editor_scale.h" #include "editor_settings.h" -#include "project_settings.h" #include "io/resource_loader.h" #include "io/resource_saver.h" #include "message_queue.h" #include "os/file_access.h" +#include "project_settings.h" bool EditorResourcePreviewGenerator::handles(const String &p_type) const { diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 755ac75180..07af60d634 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -216,17 +216,43 @@ Variant _EDITOR_DEF(const String &p_var, const Variant &p_default) { return p_default; } +static Dictionary _get_builtin_script_templates() { + Dictionary templates; + + //No Comments + templates["no_comments.gd"] = + "extends %BASE%\n" + "\n" + "func _ready():\n" + "%TS%pass\n"; + + //Empty + templates["empty.gd"] = + "extends %BASE%" + "\n" + "\n"; + + return templates; +} + static void _create_script_templates(const String &p_path) { - FileAccess *file = FileAccess::open(p_path.plus_file("no_comments.gd"), FileAccess::WRITE); - ERR_FAIL_COND(!file); - String script = String("extends %BASE%\n\nfunc _ready():\n%TS%pass\n"); - file->store_string(script); - file->close(); - file->reopen(p_path.plus_file("empty.gd"), FileAccess::WRITE); - script = "extends %BASE%\n\n"; - file->store_string(script); - file->close(); + Dictionary templates = _get_builtin_script_templates(); + List<Variant> keys; + templates.get_key_list(&keys); + FileAccess *file = FileAccess::create(FileAccess::ACCESS_FILESYSTEM); + + DirAccess *dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + dir->change_dir(p_path); + for (int i = 0; i < keys.size(); i++) { + if (!dir->file_exists(keys[i])) { + file->reopen(p_path.plus_file((String)keys[i]), FileAccess::WRITE); + ERR_FAIL_COND(!file); + file->store_string(templates[keys[i]]); + file->close(); + } + } + memdelete(file); } @@ -308,10 +334,10 @@ void EditorSettings::create() { if (dir->change_dir("script_templates") != OK) { dir->make_dir("script_templates"); - _create_script_templates(dir->get_current_dir() + "/script_templates"); } else { dir->change_dir(".."); } + _create_script_templates(dir->get_current_dir() + "/script_templates"); if (dir->change_dir("tmp") != OK) { dir->make_dir("tmp"); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 6b985c7b4b..807aed4267 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -143,7 +143,7 @@ Ref<Theme> create_editor_theme() { Color light_color_1 = base_color.linear_interpolate(Color(1, 1, 1, 1), contrast); Color light_color_2 = base_color.linear_interpolate(Color(1, 1, 1, 1), contrast * 1.5); - const int border_width = (border_size % 3) * EDSCALE; + const int border_width = CLAMP(border_size, 0, 3) * EDSCALE; Color title_color_hl = base_color; if (highlight_tabs) @@ -163,6 +163,13 @@ Ref<Theme> create_editor_theme() { theme->set_color("light_color_1", "Editor", light_color_1); theme->set_color("light_color_2", "Editor", light_color_2); + Color success_color = highlight_color.linear_interpolate(Color(0, 1, .8), 0.8); + Color warning_color = highlight_color.linear_interpolate(Color(1, 1, .2), 0.8); + Color error_color = highlight_color.linear_interpolate(Color(1, .2, .2), 0.8); + theme->set_color("success_color", "Editor", success_color); + theme->set_color("warning_color", "Editor", warning_color); + theme->set_color("error_color", "Editor", error_color); + // Checkbox icon theme->set_icon("checked", "CheckBox", theme->get_icon("GuiChecked", "EditorIcons")); theme->set_icon("unchecked", "CheckBox", theme->get_icon("GuiUnchecked", "EditorIcons")); @@ -231,7 +238,7 @@ Ref<Theme> create_editor_theme() { theme->set_stylebox("panel", "TabContainer", style_content_panel); theme->set_stylebox("Content", "EditorStyles", style_content_panel_vp); - Ref<StyleBoxFlat> style_button_type = make_flat_stylebox(dark_color_1, 4, 4, 6, 4); + Ref<StyleBoxFlat> style_button_type = make_flat_stylebox(dark_color_1, 6, 4, 6, 4); style_button_type->set_draw_center(true); style_button_type->set_border_size(border_width); style_button_type->set_light_color(light_color_1); @@ -280,12 +287,12 @@ Ref<Theme> create_editor_theme() { // PopupMenu Ref<StyleBoxFlat> style_popup_menu = make_flat_stylebox(dark_color_1, 8, 8, 8, 8); - style_popup_menu->set_border_size(border_width); + style_popup_menu->set_border_size(MAX(EDSCALE, border_width)); style_popup_menu->set_light_color(light_color_1); style_popup_menu->set_dark_color(light_color_1); style_popup_menu->set_border_blend(false); theme->set_stylebox("panel", "PopupMenu", style_popup_menu); - theme->set_stylebox("separator", "PopupMenu", make_line_stylebox(separator_color, border_width, 8 - border_width)); + theme->set_stylebox("separator", "PopupMenu", make_line_stylebox(separator_color, MAX(EDSCALE, border_width), 8 - MAX(EDSCALE, border_width))); // Tree & ItemList background Ref<StyleBoxFlat> style_tree_bg = make_flat_stylebox(dark_color_1, 2, 4, 2, 4); @@ -307,8 +314,8 @@ Ref<Theme> create_editor_theme() { theme->set_icon("arrow_collapsed", "Tree", theme->get_icon("GuiTreeArrowRight", "EditorIcons")); theme->set_icon("select_arrow", "Tree", theme->get_icon("GuiDropdown", "EditorIcons")); theme->set_stylebox("bg_focus", "Tree", focus_sbt); - theme->set_stylebox("custom_button", "Tree", style_button_type); - theme->set_stylebox("custom_button_pressed", "Tree", style_button_type); + theme->set_stylebox("custom_button", "Tree", make_empty_stylebox()); + theme->set_stylebox("custom_button_pressed", "Tree", make_empty_stylebox()); theme->set_stylebox("custom_button_hover", "Tree", style_button_type); theme->set_color("custom_button_font_highlight", "Tree", HIGHLIGHT_COLOR_LIGHT); @@ -424,7 +431,7 @@ Ref<Theme> create_editor_theme() { // WindowDialog Ref<StyleBoxFlat> style_window = make_flat_stylebox(dark_color_2, 4, 4, 4, 4); - style_window->set_border_size(border_width); + style_window->set_border_size(MAX(EDSCALE, border_width)); style_window->set_border_blend(false); style_window->set_light_color(title_color_hl); style_window->set_dark_color(title_color_hl); @@ -471,6 +478,9 @@ Ref<Theme> create_editor_theme() { theme->set_icon("grabber", "VSlider", theme->get_icon("GuiSliderGrabber", "EditorIcons")); theme->set_icon("grabber_highlight", "VSlider", theme->get_icon("GuiSliderGrabberHl", "EditorIcons")); + //RichTextLabel + theme->set_stylebox("focus", "RichTextLabel", make_empty_stylebox()); + // Panel theme->set_stylebox("panel", "Panel", style_panel); diff --git a/editor/file_type_cache.cpp b/editor/file_type_cache.cpp index 100ebe21cd..728e80bba7 100644 --- a/editor/file_type_cache.cpp +++ b/editor/file_type_cache.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "file_type_cache.h" -#include "project_settings.h" #include "os/file_access.h" +#include "project_settings.h" FileTypeCache *FileTypeCache::singleton = NULL; diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index eacb1d2cd9..77ca1a5145 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -31,11 +31,11 @@ #include "editor_node.h" #include "editor_settings.h" -#include "project_settings.h" #include "io/resource_loader.h" #include "os/dir_access.h" #include "os/file_access.h" #include "os/os.h" +#include "project_settings.h" #include "scene/main/viewport.h" bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir) { diff --git a/editor/icons/2x/icon_GUI_dropdown.png b/editor/icons/2x/icon_GUI_dropdown.png Binary files differindex c959378430..78d3352e4e 100644 --- a/editor/icons/2x/icon_GUI_dropdown.png +++ b/editor/icons/2x/icon_GUI_dropdown.png diff --git a/editor/icons/icon_GUI_dropdown.png b/editor/icons/icon_GUI_dropdown.png Binary files differindex 4bd6544830..d21cdb634e 100644 --- a/editor/icons/icon_GUI_dropdown.png +++ b/editor/icons/icon_GUI_dropdown.png diff --git a/editor/icons/source/icon_GUI_dropdown.svg b/editor/icons/source/icon_GUI_dropdown.svg index f313b09983..897f63c268 100644 --- a/editor/icons/source/icon_GUI_dropdown.svg +++ b/editor/icons/source/icon_GUI_dropdown.svg @@ -9,9 +9,9 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="8" + width="14" height="14" - viewBox="0 0 8 14" + viewBox="0 0 14 14" id="svg2" version="1.1" inkscape:version="0.92+devel unknown" @@ -28,9 +28,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="45.254834" - inkscape:cx="1.2944669" - inkscape:cy="5.9830116" + inkscape:zoom="32" + inkscape:cx="6.5843041" + inkscape:cy="6.8000184" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" @@ -72,21 +72,21 @@ id="layer1" transform="translate(0,-1038.3622)"> <circle - style="fill:#ffffff;fill-opacity:0.58823532;stroke-width:2;stroke-linejoin:round;stroke-opacity:0.39215686" + style="fill:#ffffff;fill-opacity:0.58823529;stroke-width:2;stroke-linejoin:round;stroke-opacity:0.39215686" id="path4268" - cx="4.5" + cx="7.5" cy="1040.8622" r="1.5" /> <circle r="1.5" cy="1045.8622" - cx="4.5" + cx="7.5" id="circle4271" - style="fill:#ffffff;fill-opacity:0.58823532;stroke-width:2;stroke-linejoin:round;stroke-opacity:0.39215686" /> + style="fill:#ffffff;fill-opacity:0.58823529;stroke-width:2;stroke-linejoin:round;stroke-opacity:0.39215686" /> <circle - style="fill:#ffffff;fill-opacity:0.58823532;stroke-width:2;stroke-linejoin:round;stroke-opacity:0.39215686" + style="fill:#ffffff;fill-opacity:0.58823529;stroke-width:2;stroke-linejoin:round;stroke-opacity:0.39215686" id="circle4273" - cx="4.5" + cx="7.5" cy="1050.8622" r="1.5" /> </g> diff --git a/editor/icons/source/icon_connect.svg b/editor/icons/source/icon_connect.svg index 745d3cc436..15c8b481a1 100644 --- a/editor/icons/source/icon_connect.svg +++ b/editor/icons/source/icon_connect.svg @@ -14,7 +14,7 @@ viewBox="0 0 16 16" id="svg2" version="1.1" - inkscape:version="0.91 r13725" + inkscape:version="0.92+devel unknown" inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_add_track.png" inkscape:export-xdpi="45" inkscape:export-ydpi="45" @@ -28,9 +28,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="22.627418" - inkscape:cx="0.78663326" - inkscape:cy="12.940707" + inkscape:zoom="32.000001" + inkscape:cx="13.864856" + inkscape:cy="7.2235346" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" @@ -46,7 +46,8 @@ inkscape:window-height="1016" inkscape:window-x="0" inkscape:window-y="27" - inkscape:window-maximized="1"> + inkscape:window-maximized="1" + inkscape:document-rotation="0"> <inkscape:grid type="xygrid" id="grid3336" /> @@ -68,10 +69,37 @@ inkscape:groupmode="layer" id="layer1" transform="translate(0,-1036.3622)"> + <circle + style="fill:#e0e0e0;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" + id="path4266" + cx="4" + cy="1048.3622" + r="2" /> <path - style="fill:#e0e0e0;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="M 7 2 A 2 2 0 0 0 5 4 L 5 7 L 1 7 L 1 9 L 5 9 L 5 12 A 2 2 0 0 0 7 14 L 11 14 L 11 12 L 14 12 L 14 10 L 11 10 L 11 6 L 14 6 L 14 4 L 11 4 L 11 2 L 7 2 z " - transform="translate(0,1036.3622)" - id="rect4155" /> + id="circle4268" + style="fill:none;stroke:#e0e0e0;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:type="arc" + sodipodi:cx="4" + sodipodi:cy="1048.3622" + sodipodi:rx="5" + sodipodi:ry="5" + sodipodi:start="4.712389" + sodipodi:end="0" + sodipodi:arc-type="arc" + d="M 4.0000001,1043.3622 A 5,5 0 0 1 9,1048.3622" + sodipodi:open="true" /> + <path + id="circle4270" + style="fill:none;stroke:#e0e0e0;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:type="arc" + sodipodi:cx="4" + sodipodi:cy="1048.3622" + sodipodi:rx="9" + sodipodi:ry="9" + sodipodi:start="4.712389" + sodipodi:end="0" + sodipodi:open="true" + sodipodi:arc-type="arc" + d="m 4.0000002,1039.3622 a 9,9 0 0 1 8.9999998,9" /> </g> </svg> diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index 5bf2da9912..3626c6be59 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -554,10 +554,10 @@ static void _generate_tangents_and_binormals(const PoolVector<int> &p_indices, c tangent = Vector3(); } else { tangent = Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, - (t2 * z1 - t1 * z2) * r) + (t2 * z1 - t1 * z2) * r) .normalized(); binormal = Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, - (s1 * z2 - s2 * z1) * r) + (s1 * z2 - s2 * z1) * r) .normalized(); } 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/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp index 25548f7899..8f86e64cf2 100644 --- a/editor/import/resource_importer_obj.cpp +++ b/editor/import/resource_importer_obj.cpp @@ -323,8 +323,8 @@ Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, in surf_tool->add_smooth_group(false); else surf_tool->add_smooth_group(true); - } else if (l.begins_with("g ") || l.begins_with("usemtl ") || (l.begins_with("o ") || f->eof_reached())) { //commit group to mesh - + } else if (/*l.begins_with("g ") ||*/ l.begins_with("usemtl ") || (l.begins_with("o ") || f->eof_reached())) { //commit group to mesh + //groups are too annoying if (surf_tool->get_vertex_array().size()) { //another group going on, commit it if (normals.size() == 0) { diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 98020ed9b8..c0c507c2d6 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -236,7 +236,7 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String format |= StreamTexture::FORMAT_BIT_DETECT_NORMAL; if ((p_compress_mode == COMPRESS_LOSSLESS || p_compress_mode == COMPRESS_LOSSY) && p_image->get_format() > Image::FORMAT_RGBA8) { - p_compress_mode == COMPRESS_UNCOMPRESSED; //these can't go as lossy + p_compress_mode = COMPRESS_UNCOMPRESSED; //these can't go as lossy } switch (p_compress_mode) { diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index 18c4bed5dd..8cb712cb78 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -291,7 +291,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s bool limit_rate = p_options["force/max_rate"]; int limit_rate_hz = p_options["force/max_rate_hz"]; - if (limit_rate && rate > limit_rate_hz) { + if (limit_rate && rate > limit_rate_hz && rate > 0 && frames > 0) { //resampleeee!!! int new_data_frames = frames * limit_rate_hz / rate; Vector<float> new_data; @@ -356,7 +356,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s bool trim = p_options["edit/trim"]; - if (trim && !loop) { + if (trim && !loop && format_channels > 0) { int first = 0; int last = (frames * format_channels) - 1; diff --git a/editor/io_plugins/editor_export_scene.cpp b/editor/io_plugins/editor_export_scene.cpp index 390217053a..6392b4a715 100644 --- a/editor/io_plugins/editor_export_scene.cpp +++ b/editor/io_plugins/editor_export_scene.cpp @@ -30,11 +30,11 @@ #include "editor_export_scene.h" #if 0 #include "editor/editor_settings.h" -#include "project_settings.h" #include "io/resource_loader.h" #include "io/resource_saver.h" #include "os/dir_access.h" #include "os/file_access.h" +#include "project_settings.h" #include "scene/resources/packed_scene.h" Vector<uint8_t> EditorSceneExportPlugin::custom_export(String& p_path,const Ref<EditorExportPlatform> &p_platform) { diff --git a/editor/io_plugins/editor_scene_import_plugin.cpp b/editor/io_plugins/editor_scene_import_plugin.cpp index 5f0e928844..1890ca906a 100644 --- a/editor/io_plugins/editor_scene_import_plugin.cpp +++ b/editor/io_plugins/editor_scene_import_plugin.cpp @@ -31,10 +31,10 @@ #if 0 #include "editor/create_dialog.h" #include "editor/editor_node.h" -#include "project_settings.h" #include "io/resource_saver.h" #include "os/file_access.h" #include "os/os.h" +#include "project_settings.h" #include "scene/3d/body_shape.h" #include "scene/3d/mesh_instance.h" #include "scene/3d/navigation.h" @@ -1307,7 +1307,7 @@ EditorSceneImportDialog::EditorSceneImportDialog(EditorNode *p_editor, EditorSce //confirm_import->set_child_rect(cvb); PanelContainer *pc = memnew( PanelContainer ); - pc->add_style_override("panel",get_stylebox("normal","TextEdit")); + pc->add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("normal","TextEdit")); //ec->add_child(pc); missing_files = memnew( RichTextLabel ); cvb->add_margin_child(TTR("The Following Files are Missing:"),pc,true); diff --git a/editor/io_plugins/editor_texture_import_plugin.cpp b/editor/io_plugins/editor_texture_import_plugin.cpp index 842c43f4de..095bc02fa0 100644 --- a/editor/io_plugins/editor_texture_import_plugin.cpp +++ b/editor/io_plugins/editor_texture_import_plugin.cpp @@ -33,10 +33,10 @@ #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor_atlas.h" -#include "project_settings.h" #include "io/image_loader.h" #include "io/marshalls.h" #include "io/resource_saver.h" +#include "project_settings.h" #include "scene/gui/button_group.h" #include "scene/gui/check_button.h" #include "scene/gui/margin_container.h" diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 82b03d3a19..ecae272b6d 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -31,10 +31,10 @@ #include "editor/animation_editor.h" #include "editor/editor_settings.h" -#include "project_settings.h" #include "io/resource_loader.h" #include "io/resource_saver.h" #include "os/keyboard.h" +#include "project_settings.h" void AnimationPlayerEditor::_node_removed(Node *p_node) { @@ -1246,7 +1246,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) { set_focus_mode(FOCUS_ALL); player = NULL; - add_style_override("panel", get_stylebox("panel", "Panel")); + add_style_override("panel", editor->get_gui_base()->get_stylebox("panel", "Panel")); Label *l; diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp index 463b6360ad..ccefbc1843 100644 --- a/editor/plugins/resource_preloader_editor_plugin.cpp +++ b/editor/plugins/resource_preloader_editor_plugin.cpp @@ -30,8 +30,8 @@ #include "resource_preloader_editor_plugin.h" #include "editor/editor_settings.h" -#include "project_settings.h" #include "io/resource_loader.h" +#include "project_settings.h" void ResourcePreloaderEditor::_gui_input(Ref<InputEvent> p_event) { } @@ -362,7 +362,7 @@ void ResourcePreloaderEditor::_bind_methods() { ResourcePreloaderEditor::ResourcePreloaderEditor() { - //add_style_override("panel", get_stylebox("panel","Panel")); + //add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("panel","Panel")); VBoxContainer *vbc = memnew(VBoxContainer); add_child(vbc); diff --git a/editor/plugins/sample_editor_plugin.cpp b/editor/plugins/sample_editor_plugin.cpp index 0b99ab5460..aee208caf0 100644 --- a/editor/plugins/sample_editor_plugin.cpp +++ b/editor/plugins/sample_editor_plugin.cpp @@ -31,8 +31,8 @@ #if 0 #include "editor/editor_settings.h" -#include "project_settings.h" #include "io/resource_loader.h" +#include "project_settings.h" @@ -360,7 +360,7 @@ SampleEditor::SampleEditor() { player = memnew(SamplePlayer); add_child(player); - add_style_override("panel", get_stylebox("panel","Panel")); + add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("panel","Panel")); library = Ref<SampleLibrary>(memnew(SampleLibrary)); player->set_sample_library(library); sample_texframe = memnew( TextureRect ); diff --git a/editor/plugins/sample_library_editor_plugin.cpp b/editor/plugins/sample_library_editor_plugin.cpp index 04c805165f..00168685b6 100644 --- a/editor/plugins/sample_library_editor_plugin.cpp +++ b/editor/plugins/sample_library_editor_plugin.cpp @@ -32,8 +32,8 @@ #include "sample_library_editor_plugin.h" #include "editor/editor_settings.h" -#include "project_settings.h" #include "io/resource_loader.h" +#include "project_settings.h" #include "sample_editor_plugin.h" #include "scene/main/viewport.h" @@ -432,7 +432,7 @@ SampleLibraryEditor::SampleLibraryEditor() { player = memnew(SamplePlayer); add_child(player); - add_style_override("panel", get_stylebox("panel","Panel")); + add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("panel","Panel")); load = memnew( Button ); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index fef7f07abb..1873a3f58b 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -211,9 +211,11 @@ void ScriptEditorQuickOpen::_confirmed() { void ScriptEditorQuickOpen::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { - connect("confirmed", this, "_confirmed"); + connect("confirmed", this, "_confirmed"); + } break; } } @@ -1064,58 +1066,73 @@ void ScriptEditor::_tab_changed(int p_which) { void ScriptEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - - editor->connect("play_pressed", this, "_editor_play"); - editor->connect("pause_pressed", this, "_editor_pause"); - editor->connect("stop_pressed", this, "_editor_stop"); - editor->connect("script_add_function_request", this, "_add_callback"); - editor->connect("resource_saved", this, "_res_saved_callback"); - script_list->connect("item_selected", this, "_script_selected"); - members_overview->connect("item_selected", this, "_members_overview_selected"); - script_split->connect("dragged", this, "_script_split_dragged"); - autosave_timer->connect("timeout", this, "_autosave_scripts"); - { - float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs"); - if (autosave_time > 0) { - autosave_timer->set_wait_time(autosave_time); - autosave_timer->start(); - } else { - autosave_timer->stop(); + switch (p_what) { + + case NOTIFICATION_ENTER_TREE: { + + editor->connect("play_pressed", this, "_editor_play"); + editor->connect("pause_pressed", this, "_editor_pause"); + editor->connect("stop_pressed", this, "_editor_stop"); + editor->connect("script_add_function_request", this, "_add_callback"); + editor->connect("resource_saved", this, "_res_saved_callback"); + script_list->connect("item_selected", this, "_script_selected"); + members_overview->connect("item_selected", this, "_members_overview_selected"); + script_split->connect("dragged", this, "_script_split_dragged"); + autosave_timer->connect("timeout", this, "_autosave_scripts"); + { + float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs"); + if (autosave_time > 0) { + autosave_timer->set_wait_time(autosave_time); + autosave_timer->start(); + } else { + autosave_timer->stop(); + } } - } - EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed"); - help_search->set_icon(get_icon("HelpSearch", "EditorIcons")); - site_search->set_icon(get_icon("Instance", "EditorIcons")); - class_search->set_icon(get_icon("ClassList", "EditorIcons")); + EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed"); + help_search->set_icon(get_icon("HelpSearch", "EditorIcons")); + site_search->set_icon(get_icon("Instance", "EditorIcons")); + class_search->set_icon(get_icon("ClassList", "EditorIcons")); - script_forward->set_icon(get_icon("Forward", "EditorIcons")); - script_back->set_icon(get_icon("Back", "EditorIcons")); - } + script_forward->set_icon(get_icon("Forward", "EditorIcons")); + script_back->set_icon(get_icon("Back", "EditorIcons")); + } break; - if (p_what == NOTIFICATION_READY) { + case NOTIFICATION_READY: { - get_tree()->connect("tree_changed", this, "_tree_changed"); - editor->connect("request_help", this, "_request_help"); - editor->connect("request_help_search", this, "_help_search"); - editor->connect("request_help_index", this, "_help_index"); - } + get_tree()->connect("tree_changed", this, "_tree_changed"); + editor->connect("request_help", this, "_request_help"); + editor->connect("request_help_search", this, "_help_search"); + editor->connect("request_help_index", this, "_help_index"); + } break; - if (p_what == NOTIFICATION_EXIT_TREE) { + case NOTIFICATION_EXIT_TREE: { - editor->disconnect("play_pressed", this, "_editor_play"); - editor->disconnect("pause_pressed", this, "_editor_pause"); - editor->disconnect("stop_pressed", this, "_editor_stop"); - } + editor->disconnect("play_pressed", this, "_editor_play"); + editor->disconnect("pause_pressed", this, "_editor_pause"); + editor->disconnect("stop_pressed", this, "_editor_stop"); + } break; - if (p_what == MainLoop::NOTIFICATION_WM_FOCUS_IN) { + case MainLoop::NOTIFICATION_WM_FOCUS_IN: { - _test_script_times_on_disk(); - _update_modified_scripts_for_external_editor(); - } + _test_script_times_on_disk(); + _update_modified_scripts_for_external_editor(); + } break; + + case NOTIFICATION_PROCESS: { + } break; - if (p_what == NOTIFICATION_PROCESS) { + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + + tab_container->add_style_override("panel", editor->get_gui_base()->get_stylebox("ScriptPanel", "EditorStyles")); + + Ref<StyleBox> sb = editor->get_gui_base()->get_stylebox("panel", "TabContainer")->duplicate(); + sb->set_default_margin(MARGIN_TOP, 0); + add_style_override("panel", sb); + } break; + + default: + break; } } @@ -2229,7 +2246,7 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("get_open_scripts"), &ScriptEditor::_get_open_scripts); ADD_SIGNAL(MethodInfo("editor_script_changed", PropertyInfo(Variant::OBJECT, "script:Script"))); - ADD_SIGNAL(MethodInfo("script_close", PropertyInfo(Variant::STRING, "script:String"))); + ADD_SIGNAL(MethodInfo("script_close", PropertyInfo(Variant::OBJECT, "script:Script"))); } ScriptEditor::ScriptEditor(EditorNode *p_editor) { 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/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index c31e11cc38..def4dd0b63 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -57,6 +57,12 @@ #define FREELOOK_MIN_SPEED 0.1 +#define MIN_Z 0.01 +#define MAX_Z 10000 + +#define MIN_FOV 0.01 +#define MAX_FOV 179 + void SpatialEditorViewport::_update_camera() { if (orthogonal) { //camera->set_orthogonal(size.width*cursor.distance,get_znear(),get_zfar()); @@ -153,26 +159,15 @@ int SpatialEditorViewport::get_selected_count() const { float SpatialEditorViewport::get_znear() const { - float val = spatial_editor->get_znear(); - if (val < 0.001) - val = 0.001; - return val; + return CLAMP(spatial_editor->get_znear(), MIN_Z, MAX_Z); } float SpatialEditorViewport::get_zfar() const { - float val = spatial_editor->get_zfar(); - if (val < 0.001) - val = 0.001; - return val; + return CLAMP(spatial_editor->get_zfar(), MIN_Z, MAX_Z); } float SpatialEditorViewport::get_fov() const { - float val = spatial_editor->get_fov(); - if (val < 0.001) - val = 0.001; - if (val > 89) - val = 89; - return val; + return CLAMP(spatial_editor->get_fov(), MIN_FOV, MAX_FOV); } Transform SpatialEditorViewport::_get_camera_transform() const { @@ -2390,7 +2385,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed view_menu = memnew(MenuButton); surface->add_child(view_menu); - view_menu->set_position(Point2(4, 4)); + view_menu->set_position(Point2(4, 4) * EDSCALE); view_menu->set_self_modulate(Color(1, 1, 1, 0.5)); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/top_view"), VIEW_TOP); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/bottom_view"), VIEW_BOTTOM); @@ -2434,8 +2429,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed preview_camera = memnew(Button); preview_camera->set_toggle_mode(true); - preview_camera->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, 90); - preview_camera->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10); + preview_camera->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, 90 * EDSCALE); + preview_camera->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); preview_camera->set_text("preview"); surface->add_child(preview_camera); preview_camera->hide(); @@ -2455,7 +2450,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed selection_menu = memnew(PopupMenu); add_child(selection_menu); - selection_menu->set_custom_minimum_size(Vector2(100, 0)); + selection_menu->set_custom_minimum_size(Size2(100, 0) * EDSCALE); selection_menu->connect("id_pressed", this, "_selection_result_pressed"); selection_menu->connect("popup_hide", this, "_selection_menu_hide"); @@ -2464,7 +2459,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed viewport->set_as_audio_listener(true); } - name = TTR("Top"); + name = ""; _update_name(); EditorSettings::get_singleton()->connect("settings_changed", this, "update_transform_gizmo_view"); @@ -3142,7 +3137,7 @@ void SpatialEditor::_menu_item_pressed(int p_option) { xform_scale[i]->set_text("1"); } - xform_dialog->popup_centered(Size2(200, 200)); + xform_dialog->popup_centered(Size2(320, 240) * EDSCALE); } break; case MENU_VIEW_USE_1_VIEWPORT: { @@ -3933,27 +3928,27 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { settings_dialog->set_title(TTR("Viewport Settings")); add_child(settings_dialog); settings_vbc = memnew(VBoxContainer); - settings_vbc->set_custom_minimum_size(Size2(200, 0)); + settings_vbc->set_custom_minimum_size(Size2(200, 0) * EDSCALE); settings_dialog->add_child(settings_vbc); //settings_dialog->set_child_rect(settings_vbc); settings_fov = memnew(SpinBox); - settings_fov->set_max(179); - settings_fov->set_min(1); + settings_fov->set_max(MAX_FOV); + settings_fov->set_min(MIN_FOV); settings_fov->set_step(0.01); settings_fov->set_value(EDITOR_DEF("editors/3d/default_fov", 55.0)); settings_vbc->add_margin_child(TTR("Perspective FOV (deg.):"), settings_fov); settings_znear = memnew(SpinBox); - settings_znear->set_max(10000); - settings_znear->set_min(0.1); + settings_znear->set_max(MAX_Z); + settings_znear->set_min(MIN_Z); settings_znear->set_step(0.01); settings_znear->set_value(EDITOR_DEF("editors/3d/default_z_near", 0.1)); settings_vbc->add_margin_child(TTR("View Z-Near:"), settings_znear); settings_zfar = memnew(SpinBox); - settings_zfar->set_max(10000); - settings_zfar->set_min(0.1); + settings_zfar->set_max(MAX_Z); + settings_zfar->set_min(MIN_Z); settings_zfar->set_step(0.01); settings_zfar->set_value(EDITOR_DEF("editors/3d/default_z_far", 1500)); settings_vbc->add_margin_child(TTR("View Z-Far:"), settings_zfar); @@ -3964,55 +3959,59 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { xform_dialog = memnew(ConfirmationDialog); xform_dialog->set_title(TTR("Transform Change")); add_child(xform_dialog); + + VBoxContainer *xform_vbc = memnew(VBoxContainer); + xform_dialog->add_child(xform_vbc); + Label *l = memnew(Label); l->set_text(TTR("Translate:")); - l->set_position(Point2(5, 5)); - xform_dialog->add_child(l); + xform_vbc->add_child(l); + + HBoxContainer *xform_hbc = memnew(HBoxContainer); + xform_vbc->add_child(xform_hbc); for (int i = 0; i < 3; i++) { xform_translate[i] = memnew(LineEdit); - xform_translate[i]->set_position(Point2(15 + i * 60, 22)); - xform_translate[i]->set_size(Size2(50, 12)); - xform_dialog->add_child(xform_translate[i]); + xform_translate[i]->set_h_size_flags(SIZE_EXPAND_FILL); + xform_hbc->add_child(xform_translate[i]); } l = memnew(Label); l->set_text(TTR("Rotate (deg.):")); - l->set_position(Point2(5, 45)); - xform_dialog->add_child(l); + xform_vbc->add_child(l); + + xform_hbc = memnew(HBoxContainer); + xform_vbc->add_child(xform_hbc); for (int i = 0; i < 3; i++) { xform_rotate[i] = memnew(LineEdit); - xform_rotate[i]->set_position(Point2(15 + i * 60, 62)); - xform_rotate[i]->set_size(Size2(50, 22)); - xform_dialog->add_child(xform_rotate[i]); + xform_rotate[i]->set_h_size_flags(SIZE_EXPAND_FILL); + xform_hbc->add_child(xform_rotate[i]); } l = memnew(Label); l->set_text(TTR("Scale (ratio):")); - l->set_position(Point2(5, 85)); - xform_dialog->add_child(l); + xform_vbc->add_child(l); + + xform_hbc = memnew(HBoxContainer); + xform_vbc->add_child(xform_hbc); for (int i = 0; i < 3; i++) { xform_scale[i] = memnew(LineEdit); - xform_scale[i]->set_position(Point2(15 + i * 60, 102)); - xform_scale[i]->set_size(Size2(50, 22)); - xform_dialog->add_child(xform_scale[i]); + xform_scale[i]->set_h_size_flags(SIZE_EXPAND_FILL); + xform_hbc->add_child(xform_scale[i]); } l = memnew(Label); l->set_text(TTR("Transform Type")); - l->set_position(Point2(5, 125)); - xform_dialog->add_child(l); + xform_vbc->add_child(l); xform_type = memnew(OptionButton); - xform_type->set_anchor(MARGIN_RIGHT, ANCHOR_END); - xform_type->set_begin(Point2(15, 142)); - xform_type->set_end(Point2(15, 75)); + xform_type->set_h_size_flags(SIZE_EXPAND_FILL); xform_type->add_item(TTR("Pre")); xform_type->add_item(TTR("Post")); - xform_dialog->add_child(xform_type); + xform_vbc->add_child(xform_type); xform_dialog->connect("confirmed", this, "_xform_dialog_action"); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index fa80894f7b..0f008552d0 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -30,8 +30,8 @@ #include "sprite_frames_editor_plugin.h" #include "editor/editor_settings.h" -#include "project_settings.h" #include "io/resource_loader.h" +#include "project_settings.h" #include "scene/3d/sprite_3d.h" void SpriteFramesEditor::_gui_input(Ref<InputEvent> p_event) { @@ -714,7 +714,7 @@ void SpriteFramesEditor::_bind_methods() { SpriteFramesEditor::SpriteFramesEditor() { - //add_style_override("panel", get_stylebox("panel","Panel")); + //add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("panel","Panel")); split = memnew(HSplitContainer); add_child(split); diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index 70a1227c52..57c27a8a7e 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -30,8 +30,8 @@ #include "texture_editor_plugin.h" #include "editor/editor_settings.h" -#include "project_settings.h" #include "io/resource_loader.h" +#include "project_settings.h" void TextureEditor::_gui_input(Ref<InputEvent> p_event) { } @@ -102,14 +102,24 @@ void TextureEditor::_notification(int p_what) { } } +void TextureEditor::_changed_callback(Object *p_changed, const char *p_prop) { + + if (!is_visible()) + return; + update(); +} + void TextureEditor::edit(Ref<Texture> p_texture) { + if (!texture.is_null()) + texture->remove_change_receptor(this); + texture = p_texture; - if (!texture.is_null()) + if (!texture.is_null()) { + texture->add_change_receptor(this); update(); - else { - + } else { hide(); } } diff --git a/editor/plugins/texture_editor_plugin.h b/editor/plugins/texture_editor_plugin.h index 9382983538..13f8dd7fbd 100644 --- a/editor/plugins/texture_editor_plugin.h +++ b/editor/plugins/texture_editor_plugin.h @@ -43,6 +43,7 @@ class TextureEditor : public Control { protected: void _notification(int p_what); void _gui_input(Ref<InputEvent> p_event); + void _changed_callback(Object *p_changed, const char *p_prop); static void _bind_methods(); public: diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 8a7dcea393..4cd18b090a 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -617,38 +617,24 @@ void TextureRegionEditor::_bind_methods() { } void TextureRegionEditor::edit(Object *p_obj) { - if (node_sprite && node_sprite->is_connected("texture_changed", this, "_edit_region")) - node_sprite->disconnect("texture_changed", this, "_edit_region"); - if (node_patch9 && node_patch9->is_connected("texture_changed", this, "_edit_region")) - node_patch9->disconnect("texture_changed", this, "_edit_region"); - if (obj_styleBox.is_valid() && obj_styleBox->is_connected("texture_changed", this, "_edit_region")) - obj_styleBox->disconnect("texture_changed", this, "_edit_region"); - if (atlas_tex.is_valid() && atlas_tex->is_connected("atlas_changed", this, "_edit_region")) - atlas_tex->disconnect("atlas_changed", this, "_edit_region"); + if (node_sprite) + node_sprite->remove_change_receptor(this); + if (node_patch9) + node_patch9->remove_change_receptor(this); + if (obj_styleBox.is_valid()) + obj_styleBox->remove_change_receptor(this); + if (atlas_tex.is_valid()) + atlas_tex->remove_change_receptor(this); if (p_obj) { node_sprite = p_obj->cast_to<Sprite>(); node_patch9 = p_obj->cast_to<NinePatchRect>(); if (p_obj->cast_to<StyleBoxTexture>()) obj_styleBox = Ref<StyleBoxTexture>(p_obj->cast_to<StyleBoxTexture>()); - if (p_obj->cast_to<AtlasTexture>()) { + if (p_obj->cast_to<AtlasTexture>()) atlas_tex = Ref<AtlasTexture>(p_obj->cast_to<AtlasTexture>()); - atlas_tex->connect("atlas_changed", this, "_edit_region"); - } else { - p_obj->connect("texture_changed", this, "_edit_region"); - } p_obj->add_change_receptor(this); - p_obj->connect("tree_exited", this, "_node_removed", varray(p_obj), CONNECT_ONESHOT); _edit_region(); } else { - if (node_sprite) - node_sprite->disconnect("tree_exited", this, "_node_removed"); - else if (node_patch9) - node_patch9->disconnect("tree_exited", this, "_node_removed"); - else if (obj_styleBox.is_valid()) - obj_styleBox->disconnect("tree_exited", this, "_node_removed"); - else if (atlas_tex.is_valid()) - atlas_tex->disconnect("tree_exited", this, "_node_removed"); - node_sprite = NULL; node_patch9 = NULL; obj_styleBox = Ref<StyleBoxTexture>(NULL); @@ -658,9 +644,11 @@ void TextureRegionEditor::edit(Object *p_obj) { } void TextureRegionEditor::_changed_callback(Object *p_changed, const char *p_prop) { - if ((String)p_prop == "region_rect") { + + if (!is_visible()) + return; + if (p_prop == StringName("atlas") || p_prop == StringName("texture")) _edit_region(); - } } void TextureRegionEditor::_edit_region() { diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index a83ea70508..f5bb9d4a35 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -493,11 +493,10 @@ void ThemeEditor::_theme_menu_cbk(int p_option) { Ref<Theme> base_theme; - type_select->show(); - type_select_label->show(); name_select_label->show(); - name_edit->show(); - name_menu->show(); + name_hbc->show(); + type_select_label->show(); + type_select->show(); if (p_option == POPUP_ADD) { //add @@ -515,11 +514,10 @@ void ThemeEditor::_theme_menu_cbk(int p_option) { base_theme = Theme::get_default(); - type_select->hide(); name_select_label->hide(); + name_hbc->hide(); type_select_label->hide(); - name_edit->hide(); - name_menu->hide(); + type_select->hide(); } else if (p_option == POPUP_REMOVE) { @@ -537,11 +535,10 @@ void ThemeEditor::_theme_menu_cbk(int p_option) { base_theme = Theme::get_default(); - type_select->hide(); name_select_label->hide(); + name_hbc->hide(); type_select_label->hide(); - name_edit->hide(); - name_menu->hide(); + type_select->hide(); } popup_mode = p_option; @@ -832,48 +829,46 @@ ThemeEditor::ThemeEditor() { add_del_dialog->hide(); add_child(add_del_dialog); + VBoxContainer *dialog_vbc = memnew(VBoxContainer); + add_del_dialog->add_child(dialog_vbc); + Label *l = memnew(Label); - l->set_position(Point2(5, 5) * EDSCALE); l->set_text(TTR("Type:")); - add_del_dialog->add_child(l); - dtype_select_label = l; + dialog_vbc->add_child(l); + + type_hbc = memnew(HBoxContainer); + dialog_vbc->add_child(type_hbc); type_edit = memnew(LineEdit); - type_edit->set_position(Point2(5, 25) * EDSCALE); - type_edit->set_size(Point2(150, 5) * EDSCALE); - add_del_dialog->add_child(type_edit); + type_edit->set_h_size_flags(SIZE_EXPAND_FILL); + type_hbc->add_child(type_edit); type_menu = memnew(MenuButton); - type_menu->set_position(Point2(160, 25) * EDSCALE); - type_menu->set_size(Point2(30, 5) * EDSCALE); type_menu->set_text(".."); - add_del_dialog->add_child(type_menu); + type_hbc->add_child(type_menu); type_menu->get_popup()->connect("id_pressed", this, "_type_menu_cbk"); l = memnew(Label); - l->set_position(Point2(200, 5) * EDSCALE); l->set_text(TTR("Name:")); - add_del_dialog->add_child(l); + dialog_vbc->add_child(l); name_select_label = l; + name_hbc = memnew(HBoxContainer); + dialog_vbc->add_child(name_hbc); + name_edit = memnew(LineEdit); - name_edit->set_position(Point2(200, 25) * EDSCALE); - name_edit->set_size(Point2(150, 5) * EDSCALE); - add_del_dialog->add_child(name_edit); + name_edit->set_h_size_flags(SIZE_EXPAND_FILL); + name_hbc->add_child(name_edit); name_menu = memnew(MenuButton); - name_menu->set_position(Point2(360, 25) * EDSCALE); - name_menu->set_size(Point2(30, 5) * EDSCALE); name_menu->set_text(".."); - - add_del_dialog->add_child(name_menu); + name_hbc->add_child(name_menu); name_menu->get_popup()->connect("about_to_show", this, "_name_menu_about_to_show"); name_menu->get_popup()->connect("id_pressed", this, "_name_menu_cbk"); type_select_label = memnew(Label); - type_select_label->set_position(Point2(400, 5) * EDSCALE); type_select_label->set_text(TTR("Data Type:")); - add_del_dialog->add_child(type_select_label); + dialog_vbc->add_child(type_select_label); type_select = memnew(OptionButton); type_select->add_item(TTR("Icon")); @@ -881,10 +876,8 @@ ThemeEditor::ThemeEditor() { type_select->add_item(TTR("Font")); type_select->add_item(TTR("Color")); type_select->add_item(TTR("Constant")); - type_select->set_position(Point2(400, 25) * EDSCALE); - type_select->set_size(Point2(80, 5) * EDSCALE); - add_del_dialog->add_child(type_select); + dialog_vbc->add_child(type_select); add_del_dialog->get_ok()->connect("pressed", this, "_dialog_cbk"); diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 6db01c6246..a75b83e76a 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -54,14 +54,15 @@ class ThemeEditor : public Control { MenuButton *theme_menu; ConfirmationDialog *add_del_dialog; + HBoxContainer *type_hbc; MenuButton *type_menu; LineEdit *type_edit; + HBoxContainer *name_hbc; MenuButton *name_menu; LineEdit *name_edit; OptionButton *type_select; Label *type_select_label; Label *name_select_label; - Label *dtype_select_label; enum PopupMode { POPUP_ADD, 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_export.cpp b/editor/project_export.cpp index b498044a65..b9694dffcb 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -32,7 +32,6 @@ #include "editor_data.h" #include "editor_node.h" #include "editor_settings.h" -#include "project_settings.h" #include "io/image_loader.h" #include "io/resource_loader.h" #include "io/resource_saver.h" @@ -40,6 +39,7 @@ #include "os/dir_access.h" #include "os/file_access.h" #include "os/os.h" +#include "project_settings.h" #include "scene/gui/box_container.h" #include "scene/gui/margin_container.h" #include "scene/gui/scroll_container.h" diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index acf5fe02cc..d5a56f644f 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -187,30 +187,17 @@ private: } else { if (mode == MODE_NEW) { - FileAccess *f = FileAccess::open(dir.plus_file("/project.godot"), FileAccess::WRITE); - if (!f) { + ProjectSettings::CustomMap initial_settings; + initial_settings["application/config/name"] = project_name->get_text(); + initial_settings["application/config/icon"] = "res://icon.png"; + initial_settings["rendering/environment/default_environment"] = "res://default_env.tres"; + + if (ProjectSettings::get_singleton()->save_custom(dir.plus_file("/project.godot"), initial_settings, Vector<String>(), false)) { error->set_text(TTR("Couldn't create project.godot in project path.")); } else { - - f->store_line("; Engine configuration file."); - f->store_line("; It's best edited using the editor UI and not directly,"); - f->store_line("; since the parameters that go here are not all obvious."); - f->store_line("; "); - f->store_line("; Format: "); - f->store_line("; [section] ; section goes between []"); - f->store_line("; param=value ; assign values to parameters"); - f->store_line("\n"); - f->store_line("[application]"); - f->store_line("\n"); - f->store_line("config/name=\"" + project_name->get_text() + "\""); - f->store_line("config/icon=\"res://icon.png\""); - f->store_line("[rendering]"); - f->store_line("environment/default_environment=\"res://default_env.tres\""); - memdelete(f); - ResourceSaver::save(dir.plus_file("/icon.png"), get_icon("DefaultProjectIcon", "EditorIcons")); - f = FileAccess::open(dir.plus_file("/default_env.tres"), FileAccess::WRITE); + FileAccess *f = FileAccess::open(dir.plus_file("/default_env.tres"), FileAccess::WRITE); if (!f) { error->set_text(TTR("Couldn't create project.godot in project path.")); } else { @@ -586,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; diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 6d23e874df..6238cad14d 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -140,7 +140,7 @@ void ProjectSettingsEditor::_action_edited() { add_at = "input/" + old_name; message->set_text(TTR("Invalid action (anything goes but '/' or ':').")); - message->popup_centered(Size2(300, 100)); + message->popup_centered(Size2(300, 100) * EDSCALE); return; } @@ -152,7 +152,7 @@ void ProjectSettingsEditor::_action_edited() { add_at = "input/" + old_name; message->set_text(vformat(TTR("Action '%s' already exists!"), new_name)); - message->popup_centered(Size2(300, 100)); + message->popup_centered(Size2(300, 100) * EDSCALE); return; } @@ -399,7 +399,7 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even device_index->add_item(TTR("Button 7")); device_index->add_item(TTR("Button 8")); device_index->add_item(TTR("Button 9")); - device_input->popup_centered_minsize(Size2(350, 95)); + device_input->popup_centered_minsize(Size2(350, 95) * EDSCALE); Ref<InputEventMouseButton> mb = p_exiting_event; if (mb.is_valid()) { @@ -420,7 +420,7 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even String desc = _axis_names[i]; device_index->add_item(TTR("Axis") + " " + itos(i / 2) + " " + (i & 1 ? "+" : "-") + desc); } - device_input->popup_centered_minsize(Size2(350, 95)); + device_input->popup_centered_minsize(Size2(350, 95) * EDSCALE); Ref<InputEventJoypadMotion> jm = p_exiting_event; if (jm.is_valid()) { @@ -441,7 +441,7 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even device_index->add_item(itos(i) + ": " + String(_button_names[i])); } - device_input->popup_centered_minsize(Size2(350, 95)); + device_input->popup_centered_minsize(Size2(350, 95) * EDSCALE); Ref<InputEventJoypadButton> jb = p_exiting_event; if (jb.is_valid()) { @@ -835,13 +835,13 @@ void ProjectSettingsEditor::_action_add() { String action = action_name->get_text(); if (action.find("/") != -1 || action.find(":") != -1 || action == "") { message->set_text(TTR("Invalid action (anything goes but '/' or ':').")); - message->popup_centered(Size2(300, 100)); + message->popup_centered(Size2(300, 100) * EDSCALE); return; } if (ProjectSettings::get_singleton()->has("input/" + action)) { message->set_text(vformat(TTR("Action '%s' already exists!"), action)); - message->popup_centered(Size2(300, 100)); + message->popup_centered(Size2(300, 100) * EDSCALE); return; } @@ -879,7 +879,7 @@ void ProjectSettingsEditor::_save() { Error err = ProjectSettings::get_singleton()->save(); message->set_text(err != OK ? TTR("Error saving settings.") : TTR("Settings saved OK.")); - message->popup_centered(Size2(300, 100)); + message->popup_centered(Size2(300, 100) * EDSCALE); } void ProjectSettingsEditor::_settings_prop_edited(const String &p_name) { @@ -1554,7 +1554,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { add = memnew(Button); hbc->add_child(add); - add->set_custom_minimum_size(Size2(150, 0)); + add->set_custom_minimum_size(Size2(150, 0) * EDSCALE); add->set_text(TTR("Add")); add->connect("pressed", this, "_action_add"); diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index 71afa8ac79..6b02afe0c1 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -1957,11 +1957,6 @@ CustomPropertyEditor::CustomPropertyEditor() { add_child(error); //error->get_cancel()->hide(); - type_button = memnew(MenuButton); - add_child(type_button); - type_button->hide(); - type_button->get_popup()->connect("id_pressed", this, "_type_create_selected"); - scene_tree = memnew(SceneTreeDialog); add_child(scene_tree); scene_tree->connect("selected", this, "_node_path_selected"); @@ -1979,6 +1974,11 @@ CustomPropertyEditor::CustomPropertyEditor() { //easing_draw->emit_signal(SceneStringNames::get_singleton()->input_event,InputEvent()); easing_draw->set_default_cursor_shape(Control::CURSOR_MOVE); + type_button = memnew(MenuButton); + add_child(type_button); + type_button->hide(); + type_button->get_popup()->connect("id_pressed", this, "_type_create_selected"); + menu = memnew(PopupMenu); add_child(menu); menu->connect("id_pressed", this, "_menu_option"); @@ -2199,7 +2199,6 @@ void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String &p case Variant::BOOL: { p_item->set_checked(1, obj->get(p_name)); - p_item->set_text(1, obj->get(p_name) ? TTR("On") : TTR("Off")); } break; case Variant::REAL: case Variant::INT: { @@ -3142,7 +3141,7 @@ void PropertyEditor::update_tree() { case Variant::BOOL: { item->set_cell_mode(1, TreeItem::CELL_MODE_CHECK); - item->set_text(1, obj->get(p.name) ? TTR("On") : TTR("Off")); + item->set_text(1, TTR("On")); item->set_tooltip(1, obj->get(p.name) ? "True" : "False"); item->set_checked(1, obj->get(p.name)); if (show_type_icons) @@ -3859,7 +3858,7 @@ void PropertyEditor::_item_edited() { break; if (type == Variant::INT) - _edit_set(name, int(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/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 57ab931827..47a9185389 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -37,9 +37,9 @@ #include "editor/plugins/spatial_editor_plugin.h" #include "editor_node.h" #include "editor_settings.h" -#include "project_settings.h" #include "multi_node_edit.h" #include "os/keyboard.h" +#include "project_settings.h" #include "scene/main/viewport.h" #include "scene/resources/packed_scene.h" #include "script_editor_debugger.h" @@ -1885,7 +1885,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { if (is_external) { bool is_inherited = selection[0]->get_scene_inherited_state() != NULL; bool is_top_level = selection[0]->get_owner() == NULL; - if (is_inherited) { + if (is_inherited && is_top_level) { menu->add_separator(); menu->add_item(TTR("Clear Inheritance"), TOOL_SCENE_CLEAR_INHERITANCE); menu->add_icon_item(get_icon("Load", "EditorIcons"), TTR("Open in Editor"), TOOL_SCENE_OPEN_INHERITED); diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index c3a3410717..0748c43b5f 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -29,11 +29,12 @@ /*************************************************************************/ #include "script_create_dialog.h" +#include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor_file_system.h" -#include "project_settings.h" #include "io/resource_saver.h" #include "os/file_access.h" +#include "project_settings.h" #include "script_language.h" void ScriptCreateDialog::_notification(int p_what) { @@ -229,7 +230,7 @@ void ScriptCreateDialog::_lang_changed(int l) { List<String> extensions; // get all possible extensions for script for (int l = 0; l < language_menu->get_item_count(); l++) { - language->get_recognized_extensions(&extensions); + ScriptServer::get_language(l)->get_recognized_extensions(&extensions); } for (List<String>::Element *E = extensions.front(); E; E = E->next()) { @@ -240,8 +241,11 @@ void ScriptCreateDialog::_lang_changed(int l) { } } } - file_path->set_text(path); + } else { + path = "class" + selected_ext; + _path_changed(path); } + file_path->set_text(path); bool use_templates = language->is_using_templates(); template_menu->set_disabled(!use_templates); @@ -403,9 +407,9 @@ void ScriptCreateDialog::_msg_script_valid(bool valid, const String &p_msg) { error_label->set_text(TTR(p_msg)); if (valid) { - error_label->add_color_override("font_color", Color(0, 1.0, 0.8, 0.8)); + error_label->add_color_override("font_color", get_color("success_color", "Editor")); } else { - error_label->add_color_override("font_color", Color(1, 0.2, 0.2, 0.8)); + error_label->add_color_override("font_color", get_color("error_color", "Editor")); } } @@ -413,9 +417,9 @@ void ScriptCreateDialog::_msg_path_valid(bool valid, const String &p_msg) { path_error_label->set_text(TTR(p_msg)); if (valid) { - path_error_label->add_color_override("font_color", Color(0, 1.0, 0.8, 0.8)); + path_error_label->add_color_override("font_color", get_color("success_color", "Editor")); } else { - path_error_label->add_color_override("font_color", Color(1, 0.4, 0.0, 0.8)); + path_error_label->add_color_override("font_color", get_color("error_color", "Editor")); } } @@ -543,19 +547,6 @@ ScriptCreateDialog::ScriptCreateDialog() { gc = memnew(GridContainer); gc->set_columns(2); - /* Error Stylebox Background */ - - StyleBoxFlat *sb = memnew(StyleBoxFlat); - sb->set_bg_color(Color(0, 0, 0, 0.05)); - sb->set_light_color(Color(1, 1, 1, 0.05)); - sb->set_dark_color(Color(1, 1, 1, 0.05)); - sb->set_border_blend(false); - sb->set_border_size(1); - sb->set_default_margin(MARGIN_TOP, 10.0 * EDSCALE); - sb->set_default_margin(MARGIN_BOTTOM, 10.0 * EDSCALE); - sb->set_default_margin(MARGIN_LEFT, 10.0 * EDSCALE); - sb->set_default_margin(MARGIN_RIGHT, 10.0 * EDSCALE); - /* Error Messages Field */ vb = memnew(VBoxContainer); @@ -582,7 +573,7 @@ ScriptCreateDialog::ScriptCreateDialog() { pc = memnew(PanelContainer); pc->set_h_size_flags(Control::SIZE_FILL); - pc->add_style_override("panel", sb); + pc->add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("bg", "Tree")); pc->add_child(vb); /* Margins */ diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 2af3bd5f31..f4ed430d3d 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -32,8 +32,8 @@ #include "editor_node.h" #include "editor_profiler.h" #include "editor_settings.h" -#include "project_settings.h" #include "main/performance.h" +#include "project_settings.h" #include "property_editor.h" #include "scene/gui/dialogs.h" #include "scene/gui/label.h" diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp index eec047cd9a..d455e0cea9 100644 --- a/editor/settings_config_dialog.cpp +++ b/editor/settings_config_dialog.cpp @@ -32,8 +32,8 @@ #include "editor_file_system.h" #include "editor_node.h" #include "editor_settings.h" -#include "project_settings.h" #include "os/keyboard.h" +#include "project_settings.h" #include "scene/gui/margin_container.h" void EditorSettingsDialog::ok_pressed() { diff --git a/main/main.cpp b/main/main.cpp index 8960d85c45..ed6ed019f4 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1704,6 +1704,7 @@ void Main::cleanup() { #endif if (audio_server) { + audio_server->finish(); memdelete(audio_server); } diff --git a/main/performance.cpp b/main/performance.cpp index c819e15f71..3d8e21bf33 100644 --- a/main/performance.cpp +++ b/main/performance.cpp @@ -117,8 +117,8 @@ float Performance::get_monitor(Monitor p_monitor) const { case TIME_FIXED_PROCESS: return _fixed_process_time; case MEMORY_STATIC: return Memory::get_mem_usage(); case MEMORY_DYNAMIC: return MemoryPool::total_memory; - case MEMORY_STATIC_MAX: return MemoryPool::max_memory; - case MEMORY_DYNAMIC_MAX: return 0; + case MEMORY_STATIC_MAX: return Memory::get_mem_max_usage(); + case MEMORY_DYNAMIC_MAX: return MemoryPool::max_memory; case MEMORY_MESSAGE_BUFFER_MAX: return MessageQueue::get_singleton()->get_max_buffer_usage(); case OBJECT_COUNT: return ObjectDB::get_object_count(); case OBJECT_RESOURCE_COUNT: return ResourceCache::get_cached_resource_count(); 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/SCsub b/modules/gdnative/SCsub index ac13319a1d..65970d48c1 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -6,9 +6,8 @@ env.add_source_files(env.modules_sources, "*.cpp") env.add_source_files(env.modules_sources, "godot/*.cpp") env.Append(CPPFLAGS=['-DGDAPI_BUILT_IN']) +env.Append(CPPPATH=['#modules/gdnative/']) if "platform" in env and env["platform"] == "x11": # there has to be a better solution? env.Append(LINKFLAGS=["-rdynamic"]) env.use_ptrcall = True - -Export('env') diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index e810c33f1c..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; } @@ -185,6 +184,8 @@ void GDNative::_bind_methods() { } void GDNative::set_library(Ref<GDNativeLibrary> p_library) { + ERR_EXPLAIN("Tried to change library of GDNative when it is already set"); + ERR_FAIL_COND(library.is_valid()); library = p_library; } @@ -217,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; } @@ -229,6 +233,7 @@ bool GDNative::initialize() { options.core_api_hash = ClassDB::get_api_hash(ClassDB::API_CORE); options.editor_api_hash = ClassDB::get_api_hash(ClassDB::API_EDITOR); options.no_api_hash = ClassDB::get_api_hash(ClassDB::API_NONE); + options.gd_native_library = (godot_object *)(get_library().ptr()); library_init_fpointer(&options); @@ -269,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 dd845cab7a..b03866f432 100644 --- a/modules/gdnative/gdnative.h +++ b/modules/gdnative/gdnative.h @@ -35,7 +35,7 @@ #include "os/thread_safe.h" #include "resource.h" -#include "godot/gdnative.h" +#include <godot/gdnative.h> class GDNativeLibrary : public Resource { GDCLASS(GDNativeLibrary, Resource) @@ -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/array.cpp b/modules/gdnative/godot/array.cpp index c068eecf8f..97c73dc253 100644 --- a/modules/gdnative/godot/array.cpp +++ b/modules/gdnative/godot/array.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 "array.h" +#include <godot/array.h> #include "core/array.h" #include "core/os/memory.h" diff --git a/modules/gdnative/godot/array.h b/modules/gdnative/godot/array.h index cbdbfbdde3..158170ba0e 100644 --- a/modules/gdnative/godot/array.h +++ b/modules/gdnative/godot/array.h @@ -46,10 +46,10 @@ typedef struct { } godot_array; #endif -#include "pool_arrays.h" -#include "variant.h" +#include <godot/pool_arrays.h> +#include <godot/variant.h> -#include "gdnative.h" +#include <godot/gdnative.h> void GDAPI godot_array_new(godot_array *r_dest); void GDAPI godot_array_new_copy(godot_array *r_dest, const godot_array *p_src); diff --git a/modules/gdnative/godot/basis.cpp b/modules/gdnative/godot/basis.cpp index 7188215d04..5cf379b7d5 100644 --- a/modules/gdnative/godot/basis.cpp +++ b/modules/gdnative/godot/basis.cpp @@ -27,10 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "basis.h" -#include "core/variant.h" +#include <godot/basis.h> #include "core/math/matrix3.h" +#include "core/variant.h" #ifdef __cplusplus extern "C" { diff --git a/modules/gdnative/godot/basis.h b/modules/gdnative/godot/basis.h index 79b2b45fdd..e96b8b730d 100644 --- a/modules/gdnative/godot/basis.h +++ b/modules/gdnative/godot/basis.h @@ -45,9 +45,9 @@ typedef struct { } godot_basis; #endif -#include "gdnative.h" -#include "quat.h" -#include "vector3.h" +#include <godot/gdnative.h> +#include <godot/quat.h> +#include <godot/vector3.h> void GDAPI godot_basis_new_with_rows(godot_basis *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis); void GDAPI godot_basis_new_with_axis_and_angle(godot_basis *r_dest, const godot_vector3 *p_axis, const godot_real p_phi); diff --git a/modules/gdnative/godot/color.cpp b/modules/gdnative/godot/color.cpp index eac966ca1f..a5ffee1e0b 100644 --- a/modules/gdnative/godot/color.cpp +++ b/modules/gdnative/godot/color.cpp @@ -27,10 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "color.h" -#include "core/variant.h" +#include <godot/color.h> #include "core/color.h" +#include "core/variant.h" #ifdef __cplusplus extern "C" { diff --git a/modules/gdnative/godot/color.h b/modules/gdnative/godot/color.h index 77e709fbe3..2cd6b48b48 100644 --- a/modules/gdnative/godot/color.h +++ b/modules/gdnative/godot/color.h @@ -45,8 +45,8 @@ typedef struct { } godot_color; #endif -#include "gdnative.h" -#include "string.h" +#include <godot/gdnative.h> +#include <godot/string.h> void GDAPI godot_color_new_rgba(godot_color *r_dest, const godot_real p_r, const godot_real p_g, const godot_real p_b, const godot_real p_a); void GDAPI godot_color_new_rgb(godot_color *r_dest, const godot_real p_r, const godot_real p_g, const godot_real p_b); diff --git a/modules/gdnative/godot/dictionary.cpp b/modules/gdnative/godot/dictionary.cpp index 1c0761edfd..b92c8125bb 100644 --- a/modules/gdnative/godot/dictionary.cpp +++ b/modules/gdnative/godot/dictionary.cpp @@ -27,7 +27,8 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "dictionary.h" +#include <godot/dictionary.h> + #include "core/variant.h" #include "core/dictionary.h" diff --git a/modules/gdnative/godot/dictionary.h b/modules/gdnative/godot/dictionary.h index a08deb27df..594b02b4dd 100644 --- a/modules/gdnative/godot/dictionary.h +++ b/modules/gdnative/godot/dictionary.h @@ -45,9 +45,9 @@ typedef struct { } godot_dictionary; #endif -#include "array.h" -#include "gdnative.h" -#include "variant.h" +#include <godot/array.h> +#include <godot/gdnative.h> +#include <godot/variant.h> void GDAPI godot_dictionary_new(godot_dictionary *r_dest); void GDAPI godot_dictionary_new_copy(godot_dictionary *r_dest, const godot_dictionary *p_src); diff --git a/modules/gdnative/godot/gdnative.cpp b/modules/gdnative/godot/gdnative.cpp index 29b499ebab..4cda1f4560 100644 --- a/modules/gdnative/godot/gdnative.cpp +++ b/modules/gdnative/godot/gdnative.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 "gdnative.h" +#include <godot/gdnative.h> #include "class_db.h" #include "error_macros.h" diff --git a/modules/gdnative/godot/gdnative.h b/modules/gdnative/godot/gdnative.h index 510bf36cd4..d849999079 100644 --- a/modules/gdnative/godot/gdnative.h +++ b/modules/gdnative/godot/gdnative.h @@ -174,72 +174,72 @@ typedef struct godot_pool_color_array godot_pool_color_array; */ /////// String -#include "string.h" +#include <godot/string.h> ////// Vector2 -#include "vector2.h" +#include <godot/vector2.h> ////// Rect2 -#include "rect2.h" +#include <godot/rect2.h> ////// Vector3 -#include "vector3.h" +#include <godot/vector3.h> ////// Transform2D -#include "transform2d.h" +#include <godot/transform2d.h> /////// Plane -#include "plane.h" +#include <godot/plane.h> /////// Quat -#include "quat.h" +#include <godot/quat.h> /////// Rect3 -#include "rect3.h" +#include <godot/rect3.h> /////// Basis -#include "basis.h" +#include <godot/basis.h> /////// Transform -#include "transform.h" +#include <godot/transform.h> /////// Color -#include "color.h" +#include <godot/color.h> /////// NodePath -#include "node_path.h" +#include <godot/node_path.h> /////// RID -#include "rid.h" +#include <godot/rid.h> /////// Dictionary -#include "dictionary.h" +#include <godot/dictionary.h> /////// Array -#include "array.h" +#include <godot/array.h> // single API file for Pool*Array -#include "pool_arrays.h" +#include <godot/pool_arrays.h> void GDAPI godot_object_destroy(godot_object *p_o); ////// Variant -#include "variant.h" +#include <godot/variant.h> ////// Singleton API @@ -265,6 +265,7 @@ typedef struct { uint64_t core_api_hash; uint64_t editor_api_hash; uint64_t no_api_hash; + godot_object *gd_native_library; // pointer to GDNativeLibrary that is being initialized } godot_gdnative_init_options; typedef struct { diff --git a/modules/gdnative/godot/node_path.cpp b/modules/gdnative/godot/node_path.cpp index a9edbc8352..f4179361be 100644 --- a/modules/gdnative/godot/node_path.cpp +++ b/modules/gdnative/godot/node_path.cpp @@ -27,10 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "node_path.h" -#include "core/variant.h" +#include <godot/node_path.h> -#include "core/path_db.h" +#include "core/node_path.h" +#include "core/variant.h" #ifdef __cplusplus extern "C" { diff --git a/modules/gdnative/godot/node_path.h b/modules/gdnative/godot/node_path.h index 06955a052e..4d3dc4e0ee 100644 --- a/modules/gdnative/godot/node_path.h +++ b/modules/gdnative/godot/node_path.h @@ -45,8 +45,8 @@ typedef struct { } godot_node_path; #endif -#include "gdnative.h" -#include "string.h" +#include <godot/gdnative.h> +#include <godot/string.h> void GDAPI godot_node_path_new(godot_node_path *r_dest, const godot_string *p_from); void GDAPI godot_node_path_new_copy(godot_node_path *r_dest, const godot_node_path *p_src); diff --git a/modules/gdnative/godot/plane.cpp b/modules/gdnative/godot/plane.cpp index e9e659e5da..5c5b302345 100644 --- a/modules/gdnative/godot/plane.cpp +++ b/modules/gdnative/godot/plane.cpp @@ -27,10 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "plane.h" -#include "core/variant.h" +#include <godot/plane.h> #include "core/math/plane.h" +#include "core/variant.h" #ifdef __cplusplus extern "C" { diff --git a/modules/gdnative/godot/plane.h b/modules/gdnative/godot/plane.h index e9e3b71e03..8519ac60c4 100644 --- a/modules/gdnative/godot/plane.h +++ b/modules/gdnative/godot/plane.h @@ -45,8 +45,8 @@ typedef struct { } godot_plane; #endif -#include "gdnative.h" -#include "vector3.h" +#include <godot/gdnative.h> +#include <godot/vector3.h> void GDAPI godot_plane_new_with_reals(godot_plane *r_dest, const godot_real p_a, const godot_real p_b, const godot_real p_c, const godot_real p_d); void GDAPI godot_plane_new_with_vectors(godot_plane *r_dest, const godot_vector3 *p_v1, const godot_vector3 *p_v2, const godot_vector3 *p_v3); diff --git a/modules/gdnative/godot/pool_arrays.cpp b/modules/gdnative/godot/pool_arrays.cpp index 6a6ee0f126..fa460be8bc 100644 --- a/modules/gdnative/godot/pool_arrays.cpp +++ b/modules/gdnative/godot/pool_arrays.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 "pool_arrays.h" +#include <godot/pool_arrays.h> #include "array.h" #include "core/variant.h" diff --git a/modules/gdnative/godot/pool_arrays.h b/modules/gdnative/godot/pool_arrays.h index 1e2916cea9..29517d21ac 100644 --- a/modules/gdnative/godot/pool_arrays.h +++ b/modules/gdnative/godot/pool_arrays.h @@ -113,12 +113,12 @@ typedef struct { } godot_pool_color_array; #endif -#include "array.h" -#include "color.h" -#include "vector2.h" -#include "vector3.h" +#include <godot/array.h> +#include <godot/color.h> +#include <godot/vector2.h> +#include <godot/vector3.h> -#include "gdnative.h" +#include <godot/gdnative.h> // byte diff --git a/modules/gdnative/godot/quat.cpp b/modules/gdnative/godot/quat.cpp index 6800f7fc7e..37ee4d6b15 100644 --- a/modules/gdnative/godot/quat.cpp +++ b/modules/gdnative/godot/quat.cpp @@ -27,10 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "quat.h" -#include "core/variant.h" +#include <godot/quat.h> #include "core/math/quat.h" +#include "core/variant.h" #ifdef __cplusplus extern "C" { diff --git a/modules/gdnative/godot/quat.h b/modules/gdnative/godot/quat.h index b86cbacc62..0979653d93 100644 --- a/modules/gdnative/godot/quat.h +++ b/modules/gdnative/godot/quat.h @@ -45,8 +45,8 @@ typedef struct { } godot_quat; #endif -#include "gdnative.h" -#include "vector3.h" +#include <godot/gdnative.h> +#include <godot/vector3.h> void GDAPI godot_quat_new(godot_quat *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_z, const godot_real p_w); void GDAPI godot_quat_new_with_axis_angle(godot_quat *r_dest, const godot_vector3 *p_axis, const godot_real p_angle); diff --git a/modules/gdnative/godot/rect2.cpp b/modules/gdnative/godot/rect2.cpp index 830d7bb496..023584c4f6 100644 --- a/modules/gdnative/godot/rect2.cpp +++ b/modules/gdnative/godot/rect2.cpp @@ -27,10 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "rect2.h" -#include "core/variant.h" +#include <godot/rect2.h> #include "core/math/math_2d.h" +#include "core/variant.h" #ifdef __cplusplus extern "C" { diff --git a/modules/gdnative/godot/rect2.h b/modules/gdnative/godot/rect2.h index 7b6613d9dd..cb1ddb58cf 100644 --- a/modules/gdnative/godot/rect2.h +++ b/modules/gdnative/godot/rect2.h @@ -43,8 +43,8 @@ typedef struct godot_rect2 { } godot_rect2; #endif -#include "gdnative.h" -#include "vector2.h" +#include <godot/gdnative.h> +#include <godot/vector2.h> void GDAPI godot_rect2_new_with_position_and_size(godot_rect2 *r_dest, const godot_vector2 *p_pos, const godot_vector2 *p_size); void GDAPI godot_rect2_new(godot_rect2 *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_width, const godot_real p_height); diff --git a/modules/gdnative/godot/rect3.cpp b/modules/gdnative/godot/rect3.cpp index 0fabba5b7b..708d2987f2 100644 --- a/modules/gdnative/godot/rect3.cpp +++ b/modules/gdnative/godot/rect3.cpp @@ -27,10 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "rect3.h" -#include "core/variant.h" +#include <godot/rect3.h> #include "core/math/rect3.h" +#include "core/variant.h" #ifdef __cplusplus extern "C" { diff --git a/modules/gdnative/godot/rect3.h b/modules/gdnative/godot/rect3.h index 638d89f76f..69279351c4 100644 --- a/modules/gdnative/godot/rect3.h +++ b/modules/gdnative/godot/rect3.h @@ -45,9 +45,9 @@ typedef struct { } godot_rect3; #endif -#include "gdnative.h" -#include "plane.h" -#include "vector3.h" +#include <godot/gdnative.h> +#include <godot/plane.h> +#include <godot/vector3.h> void GDAPI godot_rect3_new(godot_rect3 *r_dest, const godot_vector3 *p_pos, const godot_vector3 *p_size); diff --git a/modules/gdnative/godot/rid.cpp b/modules/gdnative/godot/rid.cpp index 2b724e554d..eb9538e692 100644 --- a/modules/gdnative/godot/rid.cpp +++ b/modules/gdnative/godot/rid.cpp @@ -27,11 +27,11 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "rid.h" -#include "core/variant.h" +#include <godot/rid.h> #include "core/resource.h" #include "core/rid.h" +#include "core/variant.h" #ifdef __cplusplus extern "C" { diff --git a/modules/gdnative/godot/rid.h b/modules/gdnative/godot/rid.h index 92e101fd2e..ac5b5383d9 100644 --- a/modules/gdnative/godot/rid.h +++ b/modules/gdnative/godot/rid.h @@ -45,7 +45,7 @@ typedef struct { } godot_rid; #endif -#include "gdnative.h" +#include <godot/gdnative.h> void GDAPI godot_rid_new(godot_rid *r_dest); diff --git a/modules/gdnative/godot/string.cpp b/modules/gdnative/godot/string.cpp index e54ef3655f..3573f266ac 100644 --- a/modules/gdnative/godot/string.cpp +++ b/modules/gdnative/godot/string.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 "string.h" +#include <godot/string.h> #include "string_db.h" #include "ustring.h" diff --git a/modules/gdnative/godot/string.h b/modules/gdnative/godot/string.h index d4d6d6c1d0..9013326454 100644 --- a/modules/gdnative/godot/string.h +++ b/modules/gdnative/godot/string.h @@ -46,7 +46,7 @@ typedef struct { } godot_string; #endif -#include "gdnative.h" +#include <godot/gdnative.h> void GDAPI godot_string_new(godot_string *r_dest); void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src); diff --git a/modules/gdnative/godot/transform.cpp b/modules/gdnative/godot/transform.cpp index e566ed0b6e..87fee918bd 100644 --- a/modules/gdnative/godot/transform.cpp +++ b/modules/gdnative/godot/transform.cpp @@ -27,10 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "transform.h" -#include "core/variant.h" +#include <godot/transform.h> #include "core/math/transform.h" +#include "core/variant.h" #ifdef __cplusplus extern "C" { diff --git a/modules/gdnative/godot/transform.h b/modules/gdnative/godot/transform.h index d14190ec49..30b9970f67 100644 --- a/modules/gdnative/godot/transform.h +++ b/modules/gdnative/godot/transform.h @@ -45,10 +45,10 @@ typedef struct { } godot_transform; #endif -#include "basis.h" -#include "gdnative.h" -#include "variant.h" -#include "vector3.h" +#include <godot/basis.h> +#include <godot/gdnative.h> +#include <godot/variant.h> +#include <godot/vector3.h> void GDAPI godot_transform_new_with_axis_origin(godot_transform *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis, const godot_vector3 *p_origin); void GDAPI godot_transform_new(godot_transform *r_dest, const godot_basis *p_basis, const godot_vector3 *p_origin); diff --git a/modules/gdnative/godot/transform2d.cpp b/modules/gdnative/godot/transform2d.cpp index 01db3f7ae0..65f9f8ee32 100644 --- a/modules/gdnative/godot/transform2d.cpp +++ b/modules/gdnative/godot/transform2d.cpp @@ -27,10 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "transform2d.h" -#include "core/variant.h" +#include <godot/transform2d.h> #include "core/math/math_2d.h" +#include "core/variant.h" #ifdef __cplusplus extern "C" { diff --git a/modules/gdnative/godot/transform2d.h b/modules/gdnative/godot/transform2d.h index 7171e528f2..41c8ba982c 100644 --- a/modules/gdnative/godot/transform2d.h +++ b/modules/gdnative/godot/transform2d.h @@ -45,9 +45,9 @@ typedef struct { } godot_transform2d; #endif -#include "gdnative.h" -#include "variant.h" -#include "vector2.h" +#include <godot/gdnative.h> +#include <godot/variant.h> +#include <godot/vector2.h> void GDAPI godot_transform2d_new(godot_transform2d *r_dest, const godot_real p_rot, const godot_vector2 *p_pos); void GDAPI godot_transform2d_new_axis_origin(godot_transform2d *r_dest, const godot_vector2 *p_x_axis, const godot_vector2 *p_y_axis, const godot_vector2 *p_origin); diff --git a/modules/gdnative/godot/variant.cpp b/modules/gdnative/godot/variant.cpp index 3469058cfd..d814ef913c 100644 --- a/modules/gdnative/godot/variant.cpp +++ b/modules/gdnative/godot/variant.cpp @@ -27,7 +27,8 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "variant.h" +#include <godot/variant.h> + #include "core/variant.h" #ifdef __cplusplus @@ -432,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/gdnative/godot/variant.h b/modules/gdnative/godot/variant.h index b56d5824fa..1c19d2a25b 100644 --- a/modules/gdnative/godot/variant.h +++ b/modules/gdnative/godot/variant.h @@ -99,25 +99,25 @@ typedef struct godot_variant_call_error { godot_variant_type expected; } godot_variant_call_error; -#include "array.h" -#include "basis.h" -#include "color.h" -#include "dictionary.h" -#include "node_path.h" -#include "plane.h" -#include "pool_arrays.h" -#include "quat.h" -#include "rect2.h" -#include "rect3.h" -#include "rid.h" -#include "string.h" -#include "transform.h" -#include "transform2d.h" -#include "variant.h" -#include "vector2.h" -#include "vector3.h" - -#include "gdnative.h" +#include <godot/array.h> +#include <godot/basis.h> +#include <godot/color.h> +#include <godot/dictionary.h> +#include <godot/node_path.h> +#include <godot/plane.h> +#include <godot/pool_arrays.h> +#include <godot/quat.h> +#include <godot/rect2.h> +#include <godot/rect3.h> +#include <godot/rid.h> +#include <godot/string.h> +#include <godot/transform.h> +#include <godot/transform2d.h> +#include <godot/variant.h> +#include <godot/vector2.h> +#include <godot/vector3.h> + +#include <godot/gdnative.h> godot_variant_type GDAPI godot_variant_get_type(const godot_variant *p_v); diff --git a/modules/gdnative/godot/vector2.cpp b/modules/gdnative/godot/vector2.cpp index 6b40e31a89..05d4b1acc8 100644 --- a/modules/gdnative/godot/vector2.cpp +++ b/modules/gdnative/godot/vector2.cpp @@ -27,10 +27,10 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "vector2.h" -#include "core/variant.h" +#include <godot/vector2.h> #include "core/math/math_2d.h" +#include "core/variant.h" #ifdef __cplusplus extern "C" { diff --git a/modules/gdnative/godot/vector2.h b/modules/gdnative/godot/vector2.h index 9934ddadbb..9db238b4fd 100644 --- a/modules/gdnative/godot/vector2.h +++ b/modules/gdnative/godot/vector2.h @@ -45,7 +45,7 @@ typedef struct { } godot_vector2; #endif -#include "gdnative.h" +#include <godot/gdnative.h> void GDAPI godot_vector2_new(godot_vector2 *r_dest, const godot_real p_x, const godot_real p_y); diff --git a/modules/gdnative/godot/vector3.cpp b/modules/gdnative/godot/vector3.cpp index 904cdad9d0..fe27e740e2 100644 --- a/modules/gdnative/godot/vector3.cpp +++ b/modules/gdnative/godot/vector3.cpp @@ -27,9 +27,9 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "vector3.h" -#include "core/variant.h" +#include <godot/vector3.h> +#include "core/variant.h" #include "core/vector.h" #ifdef __cplusplus diff --git a/modules/gdnative/godot/vector3.h b/modules/gdnative/godot/vector3.h index b5f8d0f49a..8aba1d9a85 100644 --- a/modules/gdnative/godot/vector3.h +++ b/modules/gdnative/godot/vector3.h @@ -45,8 +45,8 @@ typedef struct { } godot_vector3; #endif -#include "basis.h" -#include "gdnative.h" +#include <godot/basis.h> +#include <godot/gdnative.h> typedef enum { GODOT_VECTOR3_AXIS_X, diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index d180d5aada..da50104e26 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -76,4 +76,26 @@ void register_gdnative_types() { void unregister_gdnative_types() { memdelete(GDNativeCallRegistry::singleton); + + // This is for printing out the sizes of the core types + + /* + print_line(String("array:\t") + itos(sizeof(Array))); + print_line(String("basis:\t") + itos(sizeof(Basis))); + print_line(String("color:\t") + itos(sizeof(Color))); + print_line(String("dict:\t" ) + itos(sizeof(Dictionary))); + print_line(String("node_path:\t") + itos(sizeof(NodePath))); + print_line(String("plane:\t") + itos(sizeof(Plane))); + print_line(String("poolarray:\t") + itos(sizeof(PoolByteArray))); + print_line(String("quat:\t") + itos(sizeof(Quat))); + print_line(String("rect2:\t") + itos(sizeof(Rect2))); + print_line(String("rect3:\t") + itos(sizeof(Rect3))); + print_line(String("rid:\t") + itos(sizeof(RID))); + print_line(String("string:\t") + itos(sizeof(String))); + print_line(String("transform:\t") + itos(sizeof(Transform))); + print_line(String("transfo2D:\t") + itos(sizeof(Transform2D))); + print_line(String("variant:\t") + itos(sizeof(Variant))); + print_line(String("vector2:\t") + itos(sizeof(Vector2))); + print_line(String("vector3:\t") + itos(sizeof(Vector3))); + */ } diff --git a/modules/nativescript/nativescript.cpp b/modules/nativescript/nativescript.cpp index c4cbfcce51..fb334e573c 100644 --- a/modules/nativescript/nativescript.cpp +++ b/modules/nativescript/nativescript.cpp @@ -213,11 +213,6 @@ ScriptInstance *NativeScript::instance_create(Object *p_this) { owners_lock->unlock(); #endif - // try to call _init - // we don't care if it doesn't exist, so we ignore errors. - Variant::CallError err; - call("_init", NULL, 0, err); - return nsi; } @@ -288,9 +283,13 @@ ScriptLanguage *NativeScript::get_language() const { bool NativeScript::has_script_signal(const StringName &p_signal) const { NativeScriptDesc *script_data = get_script_desc(); - if (!script_data) - return false; - return script_data->signals_.has(p_signal); + + while (script_data) { + if (script_data->signals_.has(p_signal)) + return true; + script_data = script_data->base_data; + } + return false; } void NativeScript::get_script_signal_list(List<MethodInfo> *r_signals) const { @@ -1053,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()) { @@ -1068,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/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp index c645a55703..7b8b2abebb 100644 --- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp +++ b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp @@ -39,7 +39,7 @@ void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fra int todo = p_frames; - while (todo) { + while (todo && active) { int mixed = stb_vorbis_get_samples_float_interleaved(ogg_stream, 2, (float *)p_buffer, todo * 2); todo -= mixed; diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index ba3463445d..35358d5a1f 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -2328,6 +2328,16 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, undo_redo->add_do_method(script.ptr(), "sequence_connect", edited_func, p_from.to_int(), from_port, p_to.to_int()); undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int()); } else { + + // disconect current, and connect the new one + if (script->is_input_value_port_connected(edited_func, p_to.to_int(), to_port)) { + int conn_from; + int conn_port; + script->get_input_value_port_connection_source(edited_func, p_to.to_int(), to_port, &conn_from, &conn_port); + undo_redo->add_do_method(script.ptr(), "data_disconnect", edited_func, conn_from, conn_port, p_to.to_int(), to_port); + undo_redo->add_undo_method(script.ptr(), "data_connect", edited_func, conn_from, conn_port, p_to.to_int(), to_port); + } + undo_redo->add_do_method(script.ptr(), "data_connect", edited_func, p_from.to_int(), from_port, p_to.to_int(), to_port); undo_redo->add_undo_method(script.ptr(), "data_disconnect", edited_func, p_from.to_int(), from_port, p_to.to_int(), to_port); //update nodes in sgraph 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/SCsub b/platform/android/SCsub index 7fb3c876be..b124a1a5a8 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -141,6 +141,8 @@ if env['android_arch'] == 'armv6': lib_arch_dir = 'armeabi' elif env['android_arch'] == 'armv7': lib_arch_dir = 'armeabi-v7a' +elif env['android_arch'] == 'arm64v8': + lib_arch_dir = 'arm64-v8a' elif env['android_arch'] == 'x86': lib_arch_dir = 'x86' else: 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/detect.py b/platform/android/detect.py index fae1df3f27..ad5bfb4949 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -22,7 +22,7 @@ def get_opts(): return [ ('ANDROID_NDK_ROOT', 'Path to the Android NDK', os.environ.get("ANDROID_NDK_ROOT", 0)), ('ndk_platform', 'Target platform (android-<api>, e.g. "android-18")', "android-18"), - ('android_arch', 'Target architecture (armv7/armv6/x86)', "armv7"), + ('android_arch', 'Target architecture (armv7/armv6/arm64v8/x86)', "armv7"), ('android_neon', 'Enable NEON support (armv7 only)', "yes"), ('android_stl', 'Enable Android STL support (for modules)', "no") ] @@ -89,7 +89,7 @@ def configure(env): ## Architecture - if env['android_arch'] not in ['armv7', 'armv6', 'x86']: + if env['android_arch'] not in ['armv7', 'armv6', 'arm64v8', 'x86']: env['android_arch'] = 'armv7' neon_text = "" @@ -99,18 +99,21 @@ def configure(env): can_vectorize = True if env['android_arch'] == 'x86': + env['ARCH'] = 'arch-x86' env.extra_suffix = ".x86" + env.extra_suffix target_subpath = "x86-4.9" abi_subpath = "i686-linux-android" arch_subpath = "x86" env["x86_libtheora_opt_gcc"] = True elif env['android_arch'] == 'armv6': + env['ARCH'] = 'arch-arm' env.extra_suffix = ".armv6" + env.extra_suffix target_subpath = "arm-linux-androideabi-4.9" abi_subpath = "arm-linux-androideabi" arch_subpath = "armeabi" can_vectorize = False elif env["android_arch"] == "armv7": + env['ARCH'] = 'arch-arm' target_subpath = "arm-linux-androideabi-4.9" abi_subpath = "arm-linux-androideabi" arch_subpath = "armeabi-v7a" @@ -118,6 +121,12 @@ def configure(env): env.extra_suffix = ".armv7.neon" + env.extra_suffix else: env.extra_suffix = ".armv7" + env.extra_suffix + elif env["android_arch"] == "arm64v8": + env['ARCH'] = 'arch-arm64' + target_subpath = "aarch64-linux-android-4.9" + abi_subpath = "aarch64-linux-android" + arch_subpath = "arm64-v8a" + env.extra_suffix = ".armv8" + env.extra_suffix ## Build type @@ -149,6 +158,8 @@ def configure(env): elif (sys.platform.startswith('win')): if (platform.machine().endswith('64')): host_subpath = "windows-x86_64" + if env["android_arch"] == "arm64v8": + mt_link = False else: mt_link = False host_subpath = "windows" @@ -166,11 +177,6 @@ def configure(env): env['RANLIB'] = tools_path + "/ranlib" env['AS'] = tools_path + "/as" - if env['android_arch'] == 'x86': - env['ARCH'] = 'arch-x86' - else: - env['ARCH'] = 'arch-arm' - sysroot = env["ANDROID_NDK_ROOT"] + "/platforms/" + env['ndk_platform'] + "/" + env['ARCH'] common_opts = ['-fno-integrated-as', '-gcc-toolchain', gcc_toolchain_path] @@ -199,6 +205,11 @@ def configure(env): else: env.Append(CPPFLAGS=['-mfpu=vfpv3-d16']) + elif env["android_arch"] == "arm64v8": + target_opts = ['-target', 'aarch64-none-linux-android'] + env.Append(CPPFLAGS=['-D__ARM_ARCH_8A__']) + env.Append(CPPFLAGS=['-mfix-cortex-a53-835769']) + env.Append(CPPFLAGS=target_opts) env.Append(CPPFLAGS=common_opts) @@ -213,7 +224,8 @@ def configure(env): ## Link flags env['LINKFLAGS'] = ['-shared', '--sysroot=' + sysroot, '-Wl,--warn-shared-textrel'] - env.Append(LINKFLAGS=string.split('-Wl,--fix-cortex-a8')) + if env["android_arch"] == "armv7": + env.Append(LINKFLAGS=string.split('-Wl,--fix-cortex-a8')) env.Append(LINKFLAGS=string.split('-Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now')) env.Append(LINKFLAGS=string.split('-Wl,-soname,libgodot_android.so -Wl,--gc-sections')) if mt_link: diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 97f239c4da..3c52834d92 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -219,6 +219,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { bool use_32_fb; bool immersive; bool export_arm; + bool export_arm64; bool export_x86; String apk_expansion_salt; String apk_expansion_pkey; @@ -319,6 +320,8 @@ bool EditorExportPlatformAndroid::_set(const StringName& p_name, const Variant& _signed=p_value; else if (n=="architecture/arm") export_arm=p_value; + else if (n=="architecture/arm64") + export_arm64=p_value; else if (n=="architecture/x86") export_x86=p_value; else if (n=="screen/use_32_bits_view") @@ -392,6 +395,8 @@ bool EditorExportPlatformAndroid::_get(const StringName& p_name,Variant &r_ret) r_ret=_signed; else if (n=="architecture/arm") r_ret=export_arm; + else if (n=="architecture/arm64") + r_ret=export_arm64; else if (n=="architecture/x86") r_ret=export_x86; else if (n=="screen/use_32_bits_view") @@ -1164,6 +1169,10 @@ Error EditorExportPlatformAndroid::export_project(const String& p_path, bool p_d skip=true; } + if (file.match("lib/arm64*/libgodot_android.so") && !export_arm64) { + skip = true; + } + if (file.begins_with("META-INF") && _signed) { skip=true; } @@ -1801,6 +1810,7 @@ EditorExportPlatformAndroid::EditorExportPlatformAndroid() { immersive=true; export_arm=true; + export_arm64=false; export_x86=false; @@ -2050,6 +2060,7 @@ class EditorExportAndroid : public EditorExportPlatform { String id; String name; String description; + int release; }; struct APKExportData { @@ -2123,6 +2134,7 @@ class EditorExportAndroid : public EditorExportPlatform { if (ea->devices[j].id == ldevices[i]) { d.description = ea->devices[j].description; d.name = ea->devices[j].name; + d.release = ea->devices[j].release; } } @@ -2143,6 +2155,7 @@ class EditorExportAndroid : public EditorExportPlatform { String vendor; String device; d.description + "Device ID: " + d.id + "\n"; + d.release = 0; for (int j = 0; j < props.size(); j++) { String p = props[j]; @@ -2153,7 +2166,9 @@ class EditorExportAndroid : public EditorExportPlatform { } else if (p.begins_with("ro.build.display.id=")) { d.description += "Build: " + p.get_slice("=", 1).strip_edges() + "\n"; } else if (p.begins_with("ro.build.version.release=")) { - d.description += "Release: " + p.get_slice("=", 1).strip_edges() + "\n"; + const String release_str = p.get_slice("=", 1).strip_edges(); + d.description += "Release: " + release_str + "\n"; + d.release = release_str.to_int(); } else if (p.begins_with("ro.product.cpu.abi=")) { d.description += "CPU: " + p.get_slice("=", 1).strip_edges() + "\n"; } else if (p.begins_with("ro.product.manufacturer=")) { @@ -3005,15 +3020,19 @@ public: if (use_adb_over_usb) { args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); args.push_back("reverse"); args.push_back("--remove-all"); err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv); - int port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port"); + int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); args.push_back("reverse"); - args.push_back("tcp:" + itos(port)); - args.push_back("tcp:" + itos(port)); + args.push_back("tcp:" + itos(dbg_port)); + args.push_back("tcp:" + itos(dbg_port)); err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv); print_line("Reverse result: " + itos(rv)); @@ -3021,6 +3040,8 @@ public: int fs_port = EditorSettings::get_singleton()->get("filesystem/file_server/port"); args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); args.push_back("reverse"); args.push_back("tcp:" + itos(fs_port)); args.push_back("tcp:" + itos(fs_port)); @@ -3036,7 +3057,10 @@ public: args.push_back("shell"); args.push_back("am"); args.push_back("start"); - args.push_back("--user 0"); + if ((bool)EditorSettings::get_singleton()->get("export/android/force_system_user") && devices[p_device].release >= 17) { // Multi-user introduced in Android 17 + args.push_back("--user"); + args.push_back("0"); + } args.push_back("-a"); args.push_back("android.intent.action.MAIN"); args.push_back("-n"); @@ -3044,7 +3068,7 @@ public: err = OS::get_singleton()->execute(adb, args, true, NULL, NULL, &rv); if (err || rv != 0) { - EditorNode::add_io_error("Could not execute ondevice."); + EditorNode::add_io_error("Could not execute on device."); device_lock->unlock(); return ERR_CANT_CREATE; } @@ -3162,6 +3186,7 @@ public: bool export_x86 = p_preset->get("architecture/x86"); bool export_arm = p_preset->get("architecture/arm"); + bool export_arm64 = p_preset->get("architecture/arm64"); bool use_32_fb = p_preset->get("screen/use_32_bits_view"); bool immersive = p_preset->get("screen/immersive_mode"); @@ -3253,6 +3278,10 @@ public: skip = true; } + if (file.match("lib/arm64*/libgodot_android.so") && !export_arm64) { + skip = true; + } + if (file.begins_with("META-INF") && _signed) { skip = true; } @@ -3586,6 +3615,7 @@ void register_android_exporter() { EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "keystore")); EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey"); EDITOR_DEF("export/android/debug_keystore_pass", "android"); + EDITOR_DEF("export/android/force_system_user", false); EDITOR_DEF("export/android/timestamping_authority_url", ""); EDITOR_DEF("export/android/use_remote_debug_over_adb", false); 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/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp index 56a27fa0e0..52ff9cd562 100644 --- a/platform/android/java_class_wrapper.cpp +++ b/platform/android/java_class_wrapper.cpp @@ -190,7 +190,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, argv[i].i = *p_args[i]; } break; case ARG_TYPE_LONG: { - argv[i].j = *p_args[i]; + argv[i].j = (int64_t)*p_args[i]; } break; case ARG_TYPE_FLOAT: { argv[i].f = *p_args[i]; @@ -350,7 +350,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, Array arr = *p_args[i]; jlongArray a = env->NewLongArray(arr.size()); for (int j = 0; j < arr.size(); j++) { - jlong val = arr[j]; + jlong val = (int64_t)arr[j]; env->SetLongArrayRegion(a, j, 1, &val); } argv[i].l = a; @@ -460,9 +460,9 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method, case ARG_TYPE_LONG: { if (method->_static) { - ret = env->CallStaticLongMethodA(_class, method->method, argv); + ret = (int64_t)env->CallStaticLongMethodA(_class, method->method, argv); } else { - ret = env->CallLongMethodA(p_instance->instance, method->method, argv); + ret = (int64_t)env->CallLongMethodA(p_instance->instance, method->method, argv); } } break; @@ -680,7 +680,7 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va } break; case ARG_TYPE_LONG | ARG_NUMBER_CLASS_BIT: { - var = env->CallLongMethod(obj, JavaClassWrapper::singleton->Long_longValue); + var = (int64_t)env->CallLongMethod(obj, JavaClassWrapper::singleton->Long_longValue); return true; } break; @@ -802,7 +802,7 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va jlong val; env->GetLongArrayRegion((jlongArray)arr, 0, 1, &val); - ret.push_back(val); + ret.push_back((int64_t)val); } var = ret; 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/iphone/gl_view.mm b/platform/iphone/gl_view.mm index 4b137b7ffa..f2778e8165 100644 --- a/platform/iphone/gl_view.mm +++ b/platform/iphone/gl_view.mm @@ -29,8 +29,8 @@ /*************************************************************************/ #import "gl_view.h" -#include "core/project_settings.h" #include "core/os/keyboard.h" +#include "core/project_settings.h" #include "os_iphone.h" #include "servers/audio_server.h" diff --git a/platform/iphone/ios.mm b/platform/iphone/ios.mm index e9c164393b..6c95903241 100644 --- a/platform/iphone/ios.mm +++ b/platform/iphone/ios.mm @@ -36,7 +36,7 @@ void iOS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_rate_url", "app_id"), &iOS::get_rate_url); }; -void iOS::alert(const char* p_alert, const char* p_title) { +void iOS::alert(const char *p_alert, const char *p_title) { UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:[NSString stringWithUTF8String:p_title] message:[NSString stringWithUTF8String:p_alert] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] autorelease]; [alert show]; } 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/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 168c513ae1..5513fca809 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -96,30 +96,18 @@ static EM_BOOL _browser_resize_callback(int event_type, const EmscriptenUiEvent ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_RESIZE, false); OS_JavaScript *os = static_cast<OS_JavaScript *>(user_data); - - // the order in which _browser_resize_callback and - // _fullscreen_change_callback are called is browser-dependent, - // so try adjusting for fullscreen in both - if (os->is_window_fullscreen() || os->is_window_maximized()) { - - OS::VideoMode vm = os->get_video_mode(); - vm.width = ui_event->windowInnerWidth; - vm.height = ui_event->windowInnerHeight; - os->set_video_mode(vm); - emscripten_set_canvas_size(ui_event->windowInnerWidth, ui_event->windowInnerHeight); - } + // The order of the fullscreen change event and the window size change + // event varies, even within just one browser, so defer handling + os->request_canvas_size_adjustment(); return false; } -static Size2 _windowed_size; - static EM_BOOL _fullscreen_change_callback(int event_type, const EmscriptenFullscreenChangeEvent *event, void *user_data) { ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_FULLSCREENCHANGE, false); OS_JavaScript *os = static_cast<OS_JavaScript *>(user_data); String id = String::utf8(event->id); - // empty id is canvas if (id.empty() || id == "canvas") { @@ -127,18 +115,8 @@ static EM_BOOL _fullscreen_change_callback(int event_type, const EmscriptenFulls // this event property is the only reliable information on // browser fullscreen state vm.fullscreen = event->isFullscreen; - - if (event->isFullscreen) { - vm.width = event->screenWidth; - vm.height = event->screenHeight; - os->set_video_mode(vm); - emscripten_set_canvas_size(vm.width, vm.height); - } else { - os->set_video_mode(vm); - if (!os->is_window_maximized()) { - os->set_window_size(_windowed_size); - } - } + os->set_video_mode(vm); + os->request_canvas_size_adjustment(); } return false; } @@ -719,14 +697,17 @@ Size2 OS_JavaScript::get_screen_size(int p_screen) const { void OS_JavaScript::set_window_size(const Size2 p_size) { - window_maximized = false; + windowed_size = p_size; if (is_window_fullscreen()) { + window_maximized = false; set_window_fullscreen(false); + } else if (is_window_maximized()) { + set_window_maximized(false); + } else { + video_mode.width = p_size.x; + video_mode.height = p_size.y; + emscripten_set_canvas_size(p_size.x, p_size.y); } - _windowed_size = p_size; - video_mode.width = p_size.x; - video_mode.height = p_size.y; - emscripten_set_canvas_size(p_size.x, p_size.y); } Size2 OS_JavaScript::get_window_size() const { @@ -739,20 +720,30 @@ Size2 OS_JavaScript::get_window_size() const { void OS_JavaScript::set_window_maximized(bool p_enabled) { window_maximized = p_enabled; - if (p_enabled) { - - if (is_window_fullscreen()) { - // _browser_resize callback will set canvas size - set_window_fullscreen(false); - } else { - /* clang-format off */ - video_mode.width = EM_ASM_INT_V(return window.innerWidth); - video_mode.height = EM_ASM_INT_V(return window.innerHeight); - /* clang-format on */ - emscripten_set_canvas_size(video_mode.width, video_mode.height); - } - } else { - set_window_size(_windowed_size); + if (is_window_fullscreen()) { + set_window_fullscreen(false); + return; + } + // Calling emscripten_enter_soft_fullscreen mutltiple times hides all + // page elements except the canvas permanently, so track state + if (p_enabled && !soft_fs_enabled) { + + EmscriptenFullscreenStrategy strategy; + strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; + strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; + strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; + strategy.canvasResizedCallback = NULL; + emscripten_enter_soft_fullscreen(NULL, &strategy); + soft_fs_enabled = true; + video_mode.width = get_window_size().width; + video_mode.height = get_window_size().height; + } else if (!p_enabled) { + + emscripten_exit_soft_fullscreen(); + soft_fs_enabled = false; + video_mode.width = windowed_size.width; + video_mode.height = windowed_size.height; + emscripten_set_canvas_size(video_mode.width, video_mode.height); } } @@ -766,9 +757,17 @@ void OS_JavaScript::set_window_fullscreen(bool p_enable) { // _browser_resize_callback or _fullscreen_change_callback EMSCRIPTEN_RESULT result; if (p_enable) { - /* clang-format off */ - EM_ASM(Module.requestFullscreen(false, false);); - /* clang-format on */ + if (window_maximized) { + // soft fs during real fs can cause issues + set_window_maximized(false); + window_maximized = true; + } + EmscriptenFullscreenStrategy strategy; + strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; + strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; + strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; + strategy.canvasResizedCallback = NULL; + emscripten_request_fullscreen_strategy(NULL, false, &strategy); } else { result = emscripten_exit_fullscreen(); if (result != EMSCRIPTEN_RESULT_SUCCESS) { @@ -782,6 +781,11 @@ bool OS_JavaScript::is_window_fullscreen() const { return video_mode.fullscreen; } +void OS_JavaScript::request_canvas_size_adjustment() { + + canvas_size_adjustment_requested = true; +} + void OS_JavaScript::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const { Size2 screen = get_screen_size(); @@ -841,6 +845,17 @@ bool OS_JavaScript::main_loop_iterate() { } } process_joypads(); + if (canvas_size_adjustment_requested) { + + if (video_mode.fullscreen || window_maximized) { + video_mode.width = get_window_size().width; + video_mode.height = get_window_size().height; + } + if (!video_mode.fullscreen) { + set_window_maximized(window_maximized); + } + canvas_size_adjustment_requested = false; + } return Main::iteration(); } @@ -984,6 +999,8 @@ OS_JavaScript::OS_JavaScript(const char *p_execpath, GetDataDirFunc p_get_data_d main_loop = NULL; gl_extensions = NULL; window_maximized = false; + soft_fs_enabled = false; + canvas_size_adjustment_requested = false; get_data_dir_func = p_get_data_dir_func; FileAccessUnix::close_notification_func = _close_notification_funcs; diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index 24e96e20dd..13c500b3dc 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -59,7 +59,10 @@ class OS_JavaScript : public OS_Unix { const char *gl_extensions; InputDefault *input; + Vector2 windowed_size; bool window_maximized; + bool soft_fs_enabled; + bool canvas_size_adjustment_requested; VideoMode video_mode; CursorShape cursor_shape; MainLoop *main_loop; @@ -130,6 +133,8 @@ public: virtual void set_window_fullscreen(bool p_enable); virtual bool is_window_fullscreen() const; + void request_canvas_size_adjustment(); + virtual String get_name(); virtual MainLoop *get_main_loop() const; diff --git a/platform/osx/audio_driver_osx.cpp b/platform/osx/audio_driver_osx.cpp index dabc7989c0..d7a91b1653 100644 --- a/platform/osx/audio_driver_osx.cpp +++ b/platform/osx/audio_driver_osx.cpp @@ -103,6 +103,7 @@ Error AudioDriverOSX::finishDevice() { Error AudioDriverOSX::init() { OSStatus result; + mutex = Mutex::create(); active = false; channels = 2; @@ -159,43 +160,30 @@ OSStatus AudioDriverOSX::output_callback(void *inRefCon, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { - AudioBuffer *abuf; AudioDriverOSX *ad = (AudioDriverOSX *)inRefCon; - bool mix = true; - - if (!ad->active) - mix = false; - else if (ad->mutex) { - mix = ad->mutex->try_lock() == OK; - }; - - if (!mix) { + if (!ad->active || !ad->try_lock()) { for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { - abuf = &ioData->mBuffers[i]; + AudioBuffer *abuf = &ioData->mBuffers[i]; zeromem(abuf->mData, abuf->mDataByteSize); }; return 0; }; - int frames_left; - for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { - abuf = &ioData->mBuffers[i]; - frames_left = inNumberFrames; + AudioBuffer *abuf = &ioData->mBuffers[i]; + int frames_left = inNumberFrames; int16_t *out = (int16_t *)abuf->mData; while (frames_left) { int frames = MIN(frames_left, ad->buffer_frames); - //ad->lock(); ad->audio_server_process(frames, ad->samples_in); - //ad->unlock(); - for (int i = 0; i < frames * ad->channels; i++) { + for (int j = 0; j < frames * ad->channels; j++) { - out[i] = ad->samples_in[i] >> 16; + out[j] = ad->samples_in[j] >> 16; } frames_left -= frames; @@ -203,8 +191,7 @@ OSStatus AudioDriverOSX::output_callback(void *inRefCon, }; }; - if (ad->mutex) - ad->mutex->unlock(); + ad->unlock(); return 0; }; @@ -232,11 +219,18 @@ void AudioDriverOSX::lock() { if (mutex) mutex->lock(); }; + void AudioDriverOSX::unlock() { if (mutex) mutex->unlock(); }; +bool AudioDriverOSX::try_lock() { + if (mutex) + return mutex->try_lock() == OK; + return true; +} + void AudioDriverOSX::finish() { OSStatus result; @@ -247,16 +241,22 @@ void AudioDriverOSX::finish() { ERR_PRINT("AudioObjectRemovePropertyListener failed"); } - memdelete_arr(samples_in); + if (mutex) { + memdelete(mutex); + mutex = NULL; + } + + if (samples_in) { + memdelete_arr(samples_in); + samples_in = NULL; + } }; AudioDriverOSX::AudioDriverOSX() { - - mutex = Mutex::create(); //NULL; + mutex = NULL; + samples_in = NULL; }; -AudioDriverOSX::~AudioDriverOSX(){ - -}; +AudioDriverOSX::~AudioDriverOSX(){}; #endif diff --git a/platform/osx/audio_driver_osx.h b/platform/osx/audio_driver_osx.h index d6d00b7970..287c9d6cbf 100644 --- a/platform/osx/audio_driver_osx.h +++ b/platform/osx/audio_driver_osx.h @@ -70,6 +70,7 @@ public: virtual void unlock(); virtual void finish(); + bool try_lock(); Error reopen(); AudioDriverOSX(); diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 44f4334786..cb9dd1dd8e 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -101,11 +101,7 @@ public: bool maximized; bool zoomed; - Vector<Rect2> screens; - Vector<int> screen_dpi; - Size2 window_size; - int current_screen; Rect2 restore_rect; power_osx *power_manager; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 924417fb36..b45647b8bb 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -152,6 +152,46 @@ static bool mouse_down_control = false; return NO; } +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 +- (void)windowDidEnterFullScreen:(NSNotification *)notification { + OS_OSX::singleton->zoomed = true; +} + +- (void)windowDidExitFullScreen:(NSNotification *)notification { + OS_OSX::singleton->zoomed = false; +} +#endif // MAC_OS_X_VERSION_MAX_ALLOWED + +- (void)windowDidChangeBackingProperties:(NSNotification *)notification { + if (!OS_OSX::singleton) + return; + + NSWindow *window = (NSWindow *)[notification object]; + CGFloat newBackingScaleFactor = [window backingScaleFactor]; + CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue]; + + if (newBackingScaleFactor != oldBackingScaleFactor) { + //Set new display scale and window size + OS_OSX::singleton->display_scale = newBackingScaleFactor; + + const NSRect contentRect = [OS_OSX::singleton->window_view frame]; + const NSRect fbRect = contentRect; //convertRectToBacking(contentRect); + + OS_OSX::singleton->window_size.width = fbRect.size.width * OS_OSX::singleton->display_scale; + OS_OSX::singleton->window_size.height = fbRect.size.height * OS_OSX::singleton->display_scale; + + //Update context + if (OS_OSX::singleton->main_loop) { + [OS_OSX::singleton->context update]; + + //Force window resize ??? + NSRect frame = [OS_OSX::singleton->window_object frame]; + [OS_OSX::singleton->window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, 1, 1) display:YES]; + [OS_OSX::singleton->window_object setFrame:frame display:YES]; + } + } +} + - (void)windowDidResize:(NSNotification *)notification { [OS_OSX::singleton->context update]; @@ -161,6 +201,12 @@ static bool mouse_down_control = false; OS_OSX::singleton->window_size.width = fbRect.size.width * OS_OSX::singleton->display_scale; OS_OSX::singleton->window_size.height = fbRect.size.height * OS_OSX::singleton->display_scale; + if (OS_OSX::singleton->main_loop) { + Main::force_redraw(); + //Event retrieval blocks until resize is over. Call Main::iteration() directly. + Main::iteration(); + } + /* _GodotInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); _GodotInputWindowSize(window, contentRect.size.width, contentRect.size.height); @@ -472,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, @@ -512,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, @@ -930,33 +976,6 @@ void OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_au _ensure_data_dir(); - NSArray *screenArray = [NSScreen screens]; - printf("nscreen count %i\n", (int)[screenArray count]); - for (int i = 0; i < [screenArray count]; i++) { - - float displayScale = 1.0; - - if (display_scale > 1.0 && [[screenArray objectAtIndex:i] respondsToSelector:@selector(backingScaleFactor)]) { - displayScale = [[screenArray objectAtIndex:i] backingScaleFactor]; - } - - // Note: Use frame to get the whole screen size - NSRect nsrect = [[screenArray objectAtIndex:i] frame]; - Rect2 rect = Rect2(nsrect.origin.x, nsrect.origin.y, nsrect.size.width, nsrect.size.height); - rect.position *= displayScale; - rect.size *= displayScale; - screens.push_back(rect); - - NSDictionary *description = [[screenArray objectAtIndex:i] deviceDescription]; - NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; - CGSize displayPhysicalSize = CGDisplayScreenSize( - [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); - - //printf("width: %i pwidth %i rect width %i\n",int(displayPixelSize.width*displayScale),int(displayPhysicalSize.width*displayScale),int(nsrect.size.width)); - int dpi = (displayPixelSize.width * 25.4f / displayPhysicalSize.width) * displayScale; - - screen_dpi.push_back(dpi); - }; restore_rect = Rect2(get_window_position(), get_window_size()); } @@ -977,8 +996,6 @@ void OS_OSX::finalize() { physics_2d_server->finish(); memdelete(physics_2d_server); - - screens.clear(); } void OS_OSX::set_main_loop(MainLoop *p_main_loop) { @@ -1064,18 +1081,13 @@ void OS_OSX::warp_mouse_pos(const Point2 &p_to) { mouse_y = p_to.y; } else { //set OS position - /* this code has not been tested, please be a kind soul and fix it if it fails! */ - //local point in window coords - NSPoint localPoint = { p_to.x, p_to.y }; - - NSPoint pointInWindow = [window_view convertPoint:localPoint toView:nil]; - NSRect pointInWindowRect; - pointInWindowRect.origin = pointInWindow; + const NSRect contentRect = [window_view frame]; + NSRect pointInWindowRect = NSMakeRect(p_to.x / display_scale, contentRect.size.height - (p_to.y / display_scale) - 1, 0, 0); NSPoint pointOnScreen = [[window_view window] convertRectToScreen:pointInWindowRect].origin; //point in scren coords - CGPoint lMouseWarpPos = { pointOnScreen.x, pointOnScreen.y }; + CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y }; //do the warping CGEventSourceRef lEventRef = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); @@ -1228,37 +1240,80 @@ void OS_OSX::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) con } int OS_OSX::get_screen_count() const { - - return screens.size(); + NSArray *screenArray = [NSScreen screens]; + return [screenArray count]; }; int OS_OSX::get_current_screen() const { - - return current_screen; + Vector2 wpos = get_window_position(); + + int count = get_screen_count(); + for (int i = 0; i < count; i++) { + Point2 pos = get_screen_position(i); + Size2 size = get_screen_size(i); + if ((wpos.x >= pos.x && wpos.x < pos.x + size.width) && (wpos.y >= pos.y && wpos.y < pos.y + size.height)) + return i; + } + return 0; }; void OS_OSX::set_current_screen(int p_screen) { - - current_screen = p_screen; + Vector2 wpos = get_window_position() - get_screen_position(get_current_screen()); + set_window_position(wpos + get_screen_position(p_screen)); }; Point2 OS_OSX::get_screen_position(int p_screen) const { + NSArray *screenArray = [NSScreen screens]; + if (p_screen < [screenArray count]) { + float displayScale = 1.0; - ERR_FAIL_INDEX_V(p_screen, screens.size(), Point2()); - return screens[p_screen].position; -}; + if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) { + displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor]; + } + + NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; + return Point2(nsrect.origin.x, nsrect.origin.y) * displayScale; + } + + return Point2(); +} int OS_OSX::get_screen_dpi(int p_screen) const { + NSArray *screenArray = [NSScreen screens]; + if (p_screen < [screenArray count]) { + float displayScale = 1.0; - ERR_FAIL_INDEX_V(p_screen, screens.size(), 72); - return screen_dpi[p_screen]; + if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) { + displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor]; + } + + NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription]; + NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; + CGSize displayPhysicalSize = CGDisplayScreenSize( + [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); + + return (displayPixelSize.width * 25.4f / displayPhysicalSize.width) * displayScale; + } + + return 72; } Size2 OS_OSX::get_screen_size(int p_screen) const { + NSArray *screenArray = [NSScreen screens]; + if (p_screen < [screenArray count]) { + float displayScale = 1.0; - ERR_FAIL_INDEX_V(p_screen, screens.size(), Point2()); - return screens[p_screen].size; -}; + if (display_scale > 1.0 && [[screenArray objectAtIndex:p_screen] respondsToSelector:@selector(backingScaleFactor)]) { + displayScale = [[screenArray objectAtIndex:p_screen] backingScaleFactor]; + } + + // Note: Use frame to get the whole screen size + NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame]; + return Size2(nsrect.size.width, nsrect.size.height) * displayScale; + } + + return Size2(); +} void OS_OSX::_update_window() { bool borderless_full = false; @@ -1383,7 +1438,7 @@ void OS_OSX::set_window_maximized(bool p_enabled) { if (p_enabled) { restore_rect = Rect2(get_window_position(), get_window_size()); - [window_object setFrame:[[[NSScreen screens] objectAtIndex:current_screen] visibleFrame] display:YES]; + [window_object setFrame:[[[NSScreen screens] objectAtIndex:get_current_screen()] visibleFrame] display:YES]; } else { set_window_size(restore_rect.size); set_window_position(restore_rect.position); @@ -1661,12 +1716,52 @@ OS_OSX::OS_OSX() { // In case we are unbundled, make us a proper UI application [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; -#if 0 // Menu bar setup must go between sharedApplication above and // finishLaunching below, in order to properly emulate the behavior // of NSApplicationMain - createMenuBar(); -#endif + NSMenuItem *menu_item; + NSString *title; + + NSString *nsappname = [[[NSBundle mainBundle] performSelector:@selector(localizedInfoDictionary)] objectForKey:@"CFBundleName"]; + if (nsappname == nil) + nsappname = [[NSProcessInfo processInfo] processName]; + + // Setup Apple menu + NSMenu *apple_menu = [[NSMenu alloc] initWithTitle:@""]; + title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname]; + [apple_menu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [apple_menu addItem:[NSMenuItem separatorItem]]; + + NSMenu *services = [[NSMenu alloc] initWithTitle:@""]; + menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""]; + [apple_menu setSubmenu:services forItem:menu_item]; + [NSApp setServicesMenu:services]; + [services release]; + + [apple_menu addItem:[NSMenuItem separatorItem]]; + + title = [NSString stringWithFormat:NSLocalizedString(@"Hide %@", nil), nsappname]; + [apple_menu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + + menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [menu_item setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)]; + + [apple_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [apple_menu addItem:[NSMenuItem separatorItem]]; + + title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname]; + [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + + // Setup menu bar + NSMenu *main_menu = [[NSMenu alloc] initWithTitle:@""]; + menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""]; + [main_menu setSubmenu:apple_menu forItem:menu_item]; + [NSApp setMainMenu:main_menu]; + + [main_menu release]; + [apple_menu release]; [NSApp finishLaunching]; @@ -1676,8 +1771,6 @@ OS_OSX::OS_OSX() { cursor_shape = CURSOR_ARROW; - current_screen = 0; - maximized = false; minimized = false; window_size = Vector2(1024, 600); 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 c091c7d022..9cab19fffb 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -805,7 +805,12 @@ void OS_Windows::process_key_events() { k->set_pressed(ke.uMsg == WM_KEYDOWN); - k->set_scancode(KeyMappingWindows::get_keysym(ke.wParam)); + if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) { + // Special case for Numpad Enter key + k->set_scancode(KEY_KP_ENTER); + } else { + k->set_scancode(KeyMappingWindows::get_keysym(ke.wParam)); + } if (i + 1 < key_event_pos && key_event_buffer[i + 1].uMsg == WM_CHAR) { k->set_unicode(key_event_buffer[i + 1].wParam); @@ -1187,10 +1192,6 @@ void OS_Windows::finalize() { main_loop = NULL; - for (int i = 0; i < get_audio_driver_count(); i++) { - AudioDriverManager::get_driver(i)->finish(); - } - memdelete(joypad); memdelete(input); 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/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 1dde328eda..ade3a0a0c5 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -529,10 +529,6 @@ void OS_X11::finalize() { memdelete(main_loop); main_loop = NULL; - for (int i = 0; i < get_audio_driver_count(); i++) { - AudioDriverManager::get_driver(i)->finish(); - } - /* if (debugger_connection_console) { memdelete(debugger_connection_console); 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 b469013819..01d101a89c 100644 --- a/scene/2d/sprite.cpp +++ b/scene/2d/sprite.cpp @@ -109,6 +109,7 @@ void Sprite::set_texture(const Ref<Texture> &p_texture) { update(); emit_signal("texture_changed"); item_rect_changed(); + _change_notify("texture"); } void Sprite::set_normal_map(const Ref<Texture> &p_texture) { @@ -236,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 { @@ -249,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 new file mode 100644 index 0000000000..3c99f7fb3a --- /dev/null +++ b/scene/3d/arvr_nodes.cpp @@ -0,0 +1,426 @@ +/*************************************************************************/ +/* arvr_nodes.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "arvr_nodes.h" +#include "core/os/input.h" +#include "servers/arvr/arvr_interface.h" +#include "servers/arvr/arvr_positional_tracker.h" +#include "servers/arvr_server.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void ARVRCamera::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + // need to find our ARVROrigin parent and let it know we're it's camera! + ARVROrigin *origin = get_parent()->cast_to<ARVROrigin>(); + if (origin != NULL) { + origin->set_tracked_camera(this); + } + }; break; + case NOTIFICATION_EXIT_TREE: { + // need to find our ARVROrigin parent and let it know we're no longer it's camera! + ARVROrigin *origin = get_parent()->cast_to<ARVROrigin>(); + if (origin != NULL) { + origin->clear_tracked_camera_if(this); + } + }; break; + }; +}; + +String ARVRCamera::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("ARVRCamera must have an ARVROrigin node as its parent"); + }; + + return String(); +}; + +ARVRCamera::ARVRCamera(){ + // nothing to do here yet for now.. +}; + +ARVRCamera::~ARVRCamera(){ + // nothing to do here yet for now.. +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void ARVRController::_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 controller + ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, controller_id); + if (tracker == NULL) { + // this controller is currently turned off + is_active = false; + button_states = 0; + } else { + is_active = true; + set_transform(tracker->get_transform(true)); + + int joy_id = tracker->get_joy_id(); + if (joy_id >= 0) { + int mask = 1; + // check button states + for (int i = 0; i < 16; i++) { + bool was_pressed = (button_states && mask) == mask; + bool is_pressed = Input::get_singleton()->is_joy_button_pressed(joy_id, i); + + if (!was_pressed && is_pressed) { + emit_signal("button_pressed", i); + button_states += mask; + } else if (was_pressed && !is_pressed) { + emit_signal("button_release", i); + button_states -= mask; + }; + + mask = mask << 1; + }; + + } else { + button_states = 0; + }; + }; + }; break; + default: + break; + }; +}; + +void ARVRController::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_controller_id", "controller_id"), &ARVRController::set_controller_id); + ClassDB::bind_method(D_METHOD("get_controller_id"), &ARVRController::get_controller_id); + ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_id"), "set_controller_id", "get_controller_id"); + ClassDB::bind_method(D_METHOD("get_controller_name"), &ARVRController::get_controller_name); + + // passthroughs to information about our related joystick + ClassDB::bind_method(D_METHOD("get_joystick_id"), &ARVRController::get_joystick_id); + ClassDB::bind_method(D_METHOD("is_button_pressed", "button"), &ARVRController::is_button_pressed); + ClassDB::bind_method(D_METHOD("get_joystick_axis", "axis"), &ARVRController::get_joystick_axis); + + ClassDB::bind_method(D_METHOD("get_is_active"), &ARVRController::get_is_active); + + ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::INT, "button"))); + ADD_SIGNAL(MethodInfo("button_release", PropertyInfo(Variant::INT, "button"))); +}; + +void ARVRController::set_controller_id(int p_controller_id) { + // we don't check any bounds here, this controller may not yet be active and just be a place holder until it is. + controller_id = p_controller_id; +}; + +int ARVRController::get_controller_id(void) const { + return controller_id; +}; + +String ARVRController::get_controller_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_CONTROLLER, controller_id); + if (tracker == NULL) { + return String("Not connected"); + }; + + return tracker->get_name(); +}; + +int ARVRController::get_joystick_id() const { + // get our ARVRServer + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, 0); + + ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, controller_id); + if (tracker == NULL) { + return 0; + }; + + return tracker->get_joy_id(); +}; + +int ARVRController::is_button_pressed(int p_button) const { + int joy_id = get_joystick_id(); + if (joy_id == 0) { + return false; + }; + + return Input::get_singleton()->is_joy_button_pressed(joy_id, p_button); +}; + +float ARVRController::get_joystick_axis(int p_axis) const { + int joy_id = get_joystick_id(); + if (joy_id == 0) { + return 0.0; + }; + + return Input::get_singleton()->get_joy_axis(joy_id, p_axis); +}; + +bool ARVRController::get_is_active() const { + return is_active; +}; + +String ARVRController::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("ARVRController must have an ARVROrigin node as its parent"); + }; + + if (controller_id == 0) { + return TTR("The controller id must not be 0 or this controller will not be bound to an actual controller"); + }; + + return String(); +}; + +ARVRController::ARVRController() { + controller_id = 0; + is_active = true; +}; + +ARVRController::~ARVRController(){ + // nothing to do here yet for now.. +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +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(); + + if (tracked_camera == NULL) + return TTR("ARVROrigin requires an ARVRCamera child node"); + + return String(); +}; + +void ARVROrigin::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_world_scale", "world_scale"), &ARVROrigin::set_world_scale); + ClassDB::bind_method(D_METHOD("get_world_scale"), &ARVROrigin::get_world_scale); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "world_scale"), "set_world_scale", "get_world_scale"); +}; + +void ARVROrigin::set_tracked_camera(ARVRCamera *p_tracked_camera) { + tracked_camera = p_tracked_camera; +}; + +void ARVROrigin::clear_tracked_camera_if(ARVRCamera *p_tracked_camera) { + if (tracked_camera == p_tracked_camera) { + tracked_camera = NULL; + }; +}; + +float ARVROrigin::get_world_scale() const { + // get our ARVRServer + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, 1.0); + + return arvr_server->get_world_scale(); +}; + +void ARVROrigin::set_world_scale(float p_world_scale) { + // get our ARVRServer + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL(arvr_server); + + arvr_server->set_world_scale(p_world_scale); +}; + +void ARVROrigin::_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); + + // set our world origin to our node transform + arvr_server->set_world_origin(get_global_transform()); + + // check if we have a primary interface + Ref<ARVRInterface> arvr_interface = arvr_server->get_primary_interface(); + if (arvr_interface.is_valid() && tracked_camera != NULL) { + // get our positioning transform for our headset + Transform t = arvr_interface->get_transform_for_eye(ARVRInterface::EYE_MONO, Transform()); + + // now apply this to our camera + tracked_camera->set_transform(t); + }; + }; break; + default: + break; + }; +}; + +ARVROrigin::ARVROrigin() { + tracked_camera = NULL; +}; + +ARVROrigin::~ARVROrigin(){ + // nothing to do here yet for now.. +}; diff --git a/scene/3d/arvr_nodes.h b/scene/3d/arvr_nodes.h new file mode 100644 index 0000000000..936519126b --- /dev/null +++ b/scene/3d/arvr_nodes.h @@ -0,0 +1,157 @@ +/*************************************************************************/ +/* arvr_nodes.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ARVR_NODES_H +#define ARVR_NODES_H + +#include "scene/3d/camera.h" +#include "scene/3d/spatial.h" + +/** + @author Bastiaan Olij <mux213@gmail.com> +**/ + +/* + 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 { + + GDCLASS(ARVRCamera, Camera); + +protected: + void _notification(int p_what); + +public: + String get_configuration_warning() const; + + ARVRCamera(); + ~ARVRCamera(); +}; + +/* + 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 +*/ + +class ARVRController : public Spatial { + + GDCLASS(ARVRController, Spatial); + +private: + int controller_id; + bool is_active; + int button_states; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_controller_id(int p_controller_id); + int get_controller_id(void) const; + String get_controller_name(void) const; + + int get_joystick_id() const; + int is_button_pressed(int p_button) const; + float get_joystick_axis(int p_axis) const; + + bool get_is_active() const; + + String get_configuration_warning() const; + + ARVRController(); + ~ARVRController(); +}; + +/* + 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. + + Our camera and controllers will always be child nodes and thus place relative to this origin point. + This node will automatically locate any camera child nodes and update its position while our ARVRController node will handle tracked controllers. +*/ +class ARVROrigin : public Spatial { + + GDCLASS(ARVROrigin, Spatial); + +private: + ARVRCamera *tracked_camera; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + String get_configuration_warning() const; + + void set_tracked_camera(ARVRCamera *p_tracked_camera); + void clear_tracked_camera_if(ARVRCamera *p_tracked_camera); + + float get_world_scale() const; + void set_world_scale(float p_world_scale); + + ARVROrigin(); + ~ARVROrigin(); +}; + +#endif /* ARVR_NODES_H */ diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp index 0c31282a6c..7aaf4a6e3d 100644 --- a/scene/3d/gi_probe.cpp +++ b/scene/3d/gi_probe.cpp @@ -999,7 +999,7 @@ GIProbe::Baker::MaterialCache GIProbe::_get_material_cache(Ref<Material> p_mater return mc; } -void GIProbe::_plot_mesh(const Transform &p_xform, Ref<ArrayMesh> &p_mesh, Baker *p_baker, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material) { +void GIProbe::_plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, Baker *p_baker, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material) { for (int i = 0; i < p_mesh->get_surface_count(); i++) { @@ -1093,7 +1093,7 @@ void GIProbe::_find_meshes(Node *p_at_node, Baker *p_baker) { MeshInstance *mi = p_at_node->cast_to<MeshInstance>(); if (mi && mi->get_flag(GeometryInstance::FLAG_USE_BAKED_LIGHT)) { - Ref<ArrayMesh> mesh = mi->get_mesh(); + Ref<Mesh> mesh = mi->get_mesh(); if (mesh.is_valid()) { Rect3 aabb = mesh->get_aabb(); @@ -1120,7 +1120,7 @@ void GIProbe::_find_meshes(Node *p_at_node, Baker *p_baker) { for (int i = 0; i < meshes.size(); i += 2) { Transform mxf = meshes[i]; - Ref<ArrayMesh> mesh = meshes[i + 1]; + Ref<Mesh> mesh = meshes[i + 1]; if (!mesh.is_valid()) continue; diff --git a/scene/3d/gi_probe.h b/scene/3d/gi_probe.h index 3f5a50a560..8346437ebd 100644 --- a/scene/3d/gi_probe.h +++ b/scene/3d/gi_probe.h @@ -148,7 +148,7 @@ private: struct PlotMesh { Ref<Material> override_material; Vector<Ref<Material> > instance_materials; - Ref<ArrayMesh> mesh; + Ref<Mesh> mesh; Transform local_xform; }; @@ -177,7 +177,7 @@ private: Vector<Color> _get_bake_texture(Ref<Image> p_image, const Color &p_color); Baker::MaterialCache _get_material_cache(Ref<Material> p_material, Baker *p_baker); void _plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, const Vector3 *p_vtx, const Vector2 *p_uv, const Baker::MaterialCache &p_material, const Rect3 &p_aabb, Baker *p_baker); - void _plot_mesh(const Transform &p_xform, Ref<ArrayMesh> &p_mesh, Baker *p_baker, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material); + void _plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, Baker *p_baker, const Vector<Ref<Material> > &p_materials, const Ref<Material> &p_override_material); void _find_meshes(Node *p_at_node, Baker *p_baker); void _fixup_plot(int p_idx, int p_level, int p_x, int p_y, int p_z, Baker *p_baker); 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/control.cpp b/scene/gui/control.cpp index ee908428d9..ca81b72e89 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2536,7 +2536,7 @@ void Control::_bind_methods() { ADD_GROUP("Size Flags", "size_flags_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_h_size_flags", "get_h_size_flags"); ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_v_size_flags", "get_v_size_flags"); - ADD_PROPERTYNO(PropertyInfo(Variant::INT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "1,128,0.01"), "set_stretch_ratio", "get_stretch_ratio"); + ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_stretch_ratio", "get_stretch_ratio"); ADD_GROUP("Theme", ""); ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme"); ADD_GROUP("", ""); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 1d37529a87..11f750ea70 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -601,7 +601,7 @@ void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const int cp_neg_len = get_constant("bezier_len_neg"); if (diff > 0) { - cp_offset = MAX(cp_len, diff * 0.5); + cp_offset = MIN(cp_len, diff * 0.5); } else { cp_offset = MAX(MIN(cp_len - diff, cp_neg_len), -diff * 0.5); } 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/menu_button.cpp b/scene/gui/menu_button.cpp index fe76b16460..57aa72b7d0 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -55,7 +55,6 @@ void MenuButton::pressed() { popup->set_size(Size2(size.width, 0)); popup->set_parent_rect(Rect2(Point2(gp - popup->get_global_position()), get_size())); popup->popup(); - popup->call_deferred("grab_click_focus"); popup->set_invalidate_click_until_motion(); } @@ -112,6 +111,7 @@ MenuButton::MenuButton() { popup->hide(); add_child(popup); popup->set_as_toplevel(true); + connect("button_up", popup, "call_deferred", make_binds("grab_click_focus")); set_process_unhandled_key_input(true); set_action_mode(ACTION_MODE_BUTTON_PRESS); } diff --git a/scene/gui/patch_9_rect.cpp b/scene/gui/patch_9_rect.cpp index 16f2bb6b6f..e577000f99 100644 --- a/scene/gui/patch_9_rect.cpp +++ b/scene/gui/patch_9_rect.cpp @@ -99,6 +99,7 @@ void NinePatchRect::set_texture(const Ref<Texture> &p_tex) { */ minimum_size_changed(); emit_signal("texture_changed"); + _change_notify("texture"); } Ref<Texture> NinePatchRect::get_texture() const { 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/tabs.cpp b/scene/gui/tabs.cpp index c477a3156f..24eb19fbc2 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -793,7 +793,7 @@ void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &Tabs::set_tab_disabled); ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &Tabs::get_tab_disabled); ClassDB::bind_method(D_METHOD("remove_tab", "tab_idx"), &Tabs::remove_tab); - ClassDB::bind_method(D_METHOD("add_tab", "title", "icon:Texture"), &Tabs::add_tab); + ClassDB::bind_method(D_METHOD("add_tab", "title", "icon:Texture"), &Tabs::add_tab, DEFVAL(""), DEFVAL(Ref<Texture>())); ClassDB::bind_method(D_METHOD("set_tab_align", "align"), &Tabs::set_tab_align); ClassDB::bind_method(D_METHOD("get_tab_align"), &Tabs::get_tab_align); ClassDB::bind_method(D_METHOD("ensure_tab_visible", "idx"), &Tabs::ensure_tab_visible); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 8baca50d32..a7c31361e8 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -49,6 +49,10 @@ static bool _is_symbol(CharType c) { return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' '); } +static bool _is_whitespace(CharType c) { + return c == '\t' || c == ' '; +} + static bool _is_char(CharType c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; @@ -1801,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(); @@ -1970,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; @@ -2096,45 +2100,43 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { break; #ifdef APPLE_STYLE_KEYS - if (k->get_alt()) { + if (k->get_alt() && cursor.column > 1) { #else if (k->get_alt()) { scancode_handled = false; break; - } else if (k->get_command()) { + } else if (k->get_command() && cursor.column > 1) { #endif int line = cursor.line; int column = cursor.column; - bool prev_char = false; - bool only_whitespace = true; - - while (only_whitespace && line > 0) { - - while (column > 0) { - CharType c = text[line][column - 1]; - - if (c != '\t' && c != ' ') { - only_whitespace = false; - break; - } + // check if we are removing a single whitespace, if so remove it and the next char type + // else we just remove the whitespace + bool only_whitespace = false; + if (_is_whitespace(text[line][column - 1]) && _is_whitespace(text[line][column - 2])) { + only_whitespace = true; + } else if (_is_whitespace(text[line][column - 1])) { + // remove the single whitespace + column--; + } - column--; - } + // check if its a text char + bool only_char = (_is_text_char(text[line][column - 1]) && !only_whitespace); - if (only_whitespace) { - line--; - column = text[line].length(); - } - } + // if its not whitespace or char then symbol. + bool only_symbols = !(only_whitespace || only_char); while (column > 0) { - bool ischar = _is_text_char(text[line][column - 1]); + bool is_whitespace = _is_whitespace(text[line][column - 1]); + bool is_text_char = _is_text_char(text[line][column - 1]); - if (prev_char && !ischar) + if (only_whitespace && !is_whitespace) { break; - - prev_char = ischar; + } else if (only_char && !is_text_char) { + break; + } else if (only_symbols && (is_whitespace || is_text_char)) { + break; + } column--; } @@ -2356,52 +2358,50 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { int next_column; #ifdef APPLE_STYLE_KEYS - if (k->get_alt()) { + if (k->get_alt() && cursor.column < curline_len - 1) { #else if (k->get_alt()) { scancode_handled = false; break; - } else if (k->get_command()) { + } else if (k->get_command() && cursor.column < curline_len - 1) { #endif - int last_line = text.size() - 1; int line = cursor.line; int column = cursor.column; - bool prev_char = false; - bool only_whitespace = true; - - while (only_whitespace && line < last_line) { - - while (column < text[line].length()) { - CharType c = text[line][column]; - - if (c != '\t' && c != ' ') { - only_whitespace = false; - break; - } - - column++; - } - - if (only_whitespace) { - line++; - column = 0; - } + // check if we are removing a single whitespace, if so remove it and the next char type + // else we just remove the whitespace + bool only_whitespace = false; + if (_is_whitespace(text[line][column]) && _is_whitespace(text[line][column + 1])) { + only_whitespace = true; + } else if (_is_whitespace(text[line][column])) { + // remove the single whitespace + column++; } - while (column < text[line].length()) { + // check if its a text char + bool only_char = (_is_text_char(text[line][column]) && !only_whitespace); - bool ischar = _is_text_char(text[line][column]); + // if its not whitespace or char then symbol. + bool only_symbols = !(only_whitespace || only_char); - if (prev_char && !ischar) + while (column < curline_len) { + bool is_whitespace = _is_whitespace(text[line][column]); + bool is_text_char = _is_text_char(text[line][column]); + + if (only_whitespace && !is_whitespace) { break; - prev_char = ischar; + } else if (only_char && !is_text_char) { + break; + } else if (only_symbols && (is_whitespace || is_text_char)) { + break; + } column++; } next_line = line; next_column = column; + } else { next_column = cursor.column < curline_len ? (cursor.column + 1) : 0; } @@ -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 1456ab51c0..9499ba9dcd 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -1605,7 +1605,6 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, bool int plus = 1; while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text == "" && p_item->cells[i + plus].icon.is_null()) { - plus++; col_width += cache.hseparation; col_width += get_column_width(i + plus); plus++; @@ -1918,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; } @@ -2238,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/io/resource_format_image.cpp b/scene/io/resource_format_image.cpp index 8b3f939f1a..04b6177c3c 100644 --- a/scene/io/resource_format_image.cpp +++ b/scene/io/resource_format_image.cpp @@ -30,9 +30,9 @@ #include "resource_format_image.h" #if 0 -#include "project_settings.h" #include "io/image_loader.h" #include "os/os.h" +#include "project_settings.h" #include "scene/resources/texture.h" RES ResourceFormatLoaderImage::load(const String &p_path, const String& p_original_path, Error *r_error) { 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 cfd5c07921..0447deccc1 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -31,10 +31,10 @@ #define NODE_H #include "class_db.h" -#include "project_settings.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/main/viewport.cpp b/scene/main/viewport.cpp index 5ce347d79b..76b281ebac 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -701,6 +701,16 @@ RID Viewport::get_viewport_rid() const { return viewport; } +void Viewport::set_use_arvr(bool p_use_arvr) { + arvr = p_use_arvr; + + VS::get_singleton()->viewport_set_use_arvr(viewport, arvr); +} + +bool Viewport::use_arvr() { + return arvr; +} + void Viewport::set_size(const Size2 &p_size) { if (size == p_size.floor()) @@ -2543,6 +2553,9 @@ int Viewport::get_render_info(RenderInfo p_info) { void Viewport::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_use_arvr", "use"), &Viewport::set_use_arvr); + ClassDB::bind_method(D_METHOD("use_arvr"), &Viewport::use_arvr); + ClassDB::bind_method(D_METHOD("set_size", "size"), &Viewport::set_size); ClassDB::bind_method(D_METHOD("get_size"), &Viewport::get_size); ClassDB::bind_method(D_METHOD("set_world_2d", "world_2d:World2D"), &Viewport::set_world_2d); @@ -2644,6 +2657,8 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shadow_atlas_quadrant_subdiv", "quadrant", "subdiv"), &Viewport::set_shadow_atlas_quadrant_subdiv); ClassDB::bind_method(D_METHOD("get_shadow_atlas_quadrant_subdiv", "quadrant"), &Viewport::get_shadow_atlas_quadrant_subdiv); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "arvr"), "set_use_arvr", "use_arvr"); + ADD_PROPERTY(PropertyInfo(Variant::RECT2, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world"), "set_use_own_world", "is_using_own_world"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world", PROPERTY_HINT_RESOURCE_TYPE, "World"), "set_world", "get_world"); @@ -2729,6 +2744,7 @@ Viewport::Viewport() { parent = NULL; listener = NULL; camera = NULL; + arvr = false; size_override = false; size_override_stretch = false; size_override_size = Size2(1, 1); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index bd9747d878..83c989db54 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -148,6 +148,8 @@ private: Listener *listener; Set<Listener *> listeners; + bool arvr; + Camera *camera; Set<Camera *> cameras; @@ -349,6 +351,9 @@ public: Listener *get_listener() const; Camera *get_camera() const; + void set_use_arvr(bool p_use_arvr); + bool use_arvr(); + void set_as_audio_listener(bool p_enable); bool is_audio_listener() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 74c556931c..3e6d80d314 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "register_scene_types.h" -#include "project_settings.h" #include "os/os.h" +#include "project_settings.h" #include "scene/io/resource_format_image.h" #include "scene/io/resource_format_wav.h" @@ -199,6 +199,8 @@ #include "scene/3d/camera.h" #include "scene/3d/listener.h" +#include "scene/3d/arvr_nodes.h" + #include "scene/3d/gi_probe.h" #include "scene/3d/interpolated_camera.h" #include "scene/3d/light.h" @@ -405,6 +407,10 @@ void register_scene_types() { ClassDB::register_virtual_class<VisualInstance>(); ClassDB::register_class<Camera>(); 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>(); ClassDB::register_class<ImmediateGeometry>(); 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 01a8342fed..648900a5cd 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "packed_scene.h" #include "core/core_string_names.h" -#include "project_settings.h" #include "io/resource_loader.h" +#include "project_settings.h" #include "scene/2d/node_2d.h" #include "scene/3d/spatial.h" #include "scene/gui/control.h" @@ -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/scene_format_text.cpp b/scene/resources/scene_format_text.cpp index 585579aad5..03a862b744 100644 --- a/scene/resources/scene_format_text.cpp +++ b/scene/resources/scene_format_text.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "scene_format_text.h" -#include "project_settings.h" #include "os/dir_access.h" +#include "project_settings.h" #include "version.h" //version 2: changed names for basis, rect3, poolvectors, etc. diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index e0a9de6062..3666c18487 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -107,6 +107,7 @@ void StyleBoxTexture::set_texture(RES p_texture) { region_rect = Rect2(Point2(), texture->get_size()); emit_signal("texture_changed"); emit_changed(); + _change_notify("texture"); } RES StyleBoxTexture::get_texture() const { diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 8b747e1b43..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)); } } } @@ -818,6 +843,7 @@ void SurfaceTool::clear() { void SurfaceTool::_bind_methods() { ClassDB::bind_method(D_METHOD("begin", "primitive"), &SurfaceTool::begin); + ClassDB::bind_method(D_METHOD("add_vertex", "vertex"), &SurfaceTool::add_vertex); ClassDB::bind_method(D_METHOD("add_color", "color"), &SurfaceTool::add_color); ClassDB::bind_method(D_METHOD("add_normal", "normal"), &SurfaceTool::add_normal); @@ -827,15 +853,25 @@ void SurfaceTool::_bind_methods() { ClassDB::bind_method(D_METHOD("add_bones", "bones"), &SurfaceTool::add_bones); ClassDB::bind_method(D_METHOD("add_weights", "weights"), &SurfaceTool::add_weights); ClassDB::bind_method(D_METHOD("add_smooth_group", "smooth"), &SurfaceTool::add_smooth_group); + ClassDB::bind_method(D_METHOD("add_triangle_fan", "vertexes", "uvs", "colors", "uv2s", "normals", "tangents"), &SurfaceTool::add_triangle_fan, DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Color>()), DEFVAL(Vector<Vector2>()), DEFVAL(Vector<Vector3>()), DEFVAL(Vector<Plane>())); - ClassDB::bind_method(D_METHOD("set_material", "material:Material"), &SurfaceTool::set_material); + + ClassDB::bind_method(D_METHOD("add_index", "index"), &SurfaceTool::add_index); + ClassDB::bind_method(D_METHOD("index"), &SurfaceTool::index); ClassDB::bind_method(D_METHOD("deindex"), &SurfaceTool::deindex); - ///ClassDB::bind_method(D_METHOD("generate_flat_normals"),&SurfaceTool::generate_flat_normals); ClassDB::bind_method(D_METHOD("generate_normals"), &SurfaceTool::generate_normals); - ClassDB::bind_method(D_METHOD("add_index", "index"), &SurfaceTool::add_index); - ClassDB::bind_method(D_METHOD("commit:Mesh", "existing:Mesh"), &SurfaceTool::commit, DEFVAL(Variant())); + ClassDB::bind_method(D_METHOD("generate_tangents"), &SurfaceTool::generate_tangents); + + ClassDB::bind_method(D_METHOD("add_to_format", "flags"), &SurfaceTool::add_to_format); + + ClassDB::bind_method(D_METHOD("set_material", "material:Material"), &SurfaceTool::set_material); + ClassDB::bind_method(D_METHOD("clear"), &SurfaceTool::clear); + + ClassDB::bind_method(D_METHOD("create_from", "existing:Mesh", "surface"), &SurfaceTool::create_from); + ClassDB::bind_method(D_METHOD("append_from", "existing:Mesh", "surface", "transform"), &SurfaceTool::append_from); + ClassDB::bind_method(D_METHOD("commit:Mesh", "existing:Mesh"), &SurfaceTool::commit, DEFVAL(Variant())); } SurfaceTool::SurfaceTool() { 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 5cd75b5a69..f69c83bf08 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"), &Texture::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); @@ -830,7 +831,7 @@ void AtlasTexture::set_atlas(const Ref<Texture> &p_atlas) { return; atlas = p_atlas; emit_changed(); - emit_signal("atlas_changed"); + _change_notify("atlas"); } Ref<Texture> AtlasTexture::get_atlas() const { @@ -839,8 +840,11 @@ Ref<Texture> AtlasTexture::get_atlas() const { void AtlasTexture::set_region(const Rect2 &p_region) { + if (region == p_region) + return; region = p_region; emit_changed(); + _change_notify("region"); } Rect2 AtlasTexture::get_region() const { @@ -850,8 +854,11 @@ Rect2 AtlasTexture::get_region() const { void AtlasTexture::set_margin(const Rect2 &p_margin) { + if (margin == p_margin) + return; margin = p_margin; emit_changed(); + _change_notify("margin"); } Rect2 AtlasTexture::get_margin() const { @@ -870,8 +877,6 @@ void AtlasTexture::_bind_methods() { ClassDB::bind_method(D_METHOD("set_margin", "margin"), &AtlasTexture::set_margin); ClassDB::bind_method(D_METHOD("get_margin"), &AtlasTexture::get_margin); - ADD_SIGNAL(MethodInfo("atlas_changed")); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "atlas", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_atlas", "get_atlas"); ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region"), "set_region", "get_region"); ADD_PROPERTY(PropertyInfo(Variant::RECT2, "margin"), "set_margin", "get_margin"); diff --git a/scene/resources/texture.h b/scene/resources/texture.h index 9bbbd1d091..6c20c71af0 100644 --- a/scene/resources/texture.h +++ b/scene/resources/texture.h @@ -414,7 +414,7 @@ public: void set_width(int p_width); int get_width() const; - void ensure_default_setup(float p_min=0, float p_max=1); + void ensure_default_setup(float p_min = 0, float p_max = 1); void set_curve(Ref<Curve> p_curve); Ref<Curve> get_curve() const; 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/SCsub b/servers/SCsub index eefa6278c4..df55010a36 100644 --- a/servers/SCsub +++ b/servers/SCsub @@ -7,12 +7,11 @@ env.add_source_files(env.servers_sources, "*.cpp") Export('env') +SConscript('arvr/SCsub') SConscript('physics/SCsub') SConscript('physics_2d/SCsub') SConscript('visual/SCsub') SConscript('audio/SCsub') -SConscript('spatial_sound/SCsub') -SConscript('spatial_sound_2d/SCsub') lib = env.Library("servers", env.servers_sources) diff --git a/servers/arvr/SCsub b/servers/arvr/SCsub new file mode 100644 index 0000000000..ccc76e823f --- /dev/null +++ b/servers/arvr/SCsub @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +Import('env') + +env.add_source_files(env.servers_sources, "*.cpp") + +Export('env') diff --git a/servers/arvr/arvr_interface.cpp b/servers/arvr/arvr_interface.cpp new file mode 100644 index 0000000000..81eb011932 --- /dev/null +++ b/servers/arvr/arvr_interface.cpp @@ -0,0 +1,82 @@ +/*************************************************************************/ +/* arvr_interface.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "arvr_interface.h" + +void ARVRInterface::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_name"), &ARVRInterface::get_name); + + ClassDB::bind_method(D_METHOD("is_primary"), &ARVRInterface::is_primary); + ClassDB::bind_method(D_METHOD("set_is_primary", "enable"), &ARVRInterface::set_is_primary); + + ClassDB::bind_method(D_METHOD("is_installed"), &ARVRInterface::is_installed); + ClassDB::bind_method(D_METHOD("hmd_is_present"), &ARVRInterface::hmd_is_present); + ClassDB::bind_method(D_METHOD("supports_hmd"), &ARVRInterface::supports_hmd); + ClassDB::bind_method(D_METHOD("is_initialized"), &ARVRInterface::is_initialized); + ClassDB::bind_method(D_METHOD("initialize"), &ARVRInterface::initialize); + ClassDB::bind_method(D_METHOD("uninitialize"), &ARVRInterface::uninitialize); + + ClassDB::bind_method(D_METHOD("get_recommended_render_targetsize"), &ARVRInterface::get_recommended_render_targetsize); + + // These are now purely used internally, we may expose them again if we expose CameraMatrix through Variant but reduz is not a fan for good reasons :) + // ClassDB::bind_method(D_METHOD("get_transform_for_eye", "eye", "cam_transform"), &ARVRInterface::get_transform_for_eye); + // ClassDB::bind_method(D_METHOD("get_projection_for_eye", "eye"), &ARVRInterface::get_projection_for_eye); + // ClassDB::bind_method(D_METHOD("commit_for_eye", "node:viewport"), &ARVRInterface::commit_for_eye); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "primary"), "set_is_primary", "is_primary"); + + BIND_CONSTANT(EYE_MONO); + BIND_CONSTANT(EYE_LEFT); + BIND_CONSTANT(EYE_RIGHT); +}; + +StringName ARVRInterface::get_name() const { + return "Unknown"; +}; + +bool ARVRInterface::is_primary() { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, false); + + return arvr_server->get_primary_interface() == this; +}; + +void ARVRInterface::set_is_primary(bool p_is_primary) { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL(arvr_server); + + if (p_is_primary) { + ERR_FAIL_COND(!is_initialized()); + ERR_FAIL_COND(!supports_hmd()); + + arvr_server->set_primary_interface(this); + } else { + arvr_server->clear_primary_interface_if(this); + }; +}; diff --git a/servers/arvr/arvr_interface.h b/servers/arvr/arvr_interface.h new file mode 100644 index 0000000000..e405499fbb --- /dev/null +++ b/servers/arvr/arvr_interface.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* arvr_interface.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef ARVR_INTERFACE_H +#define ARVR_INTERFACE_H + +#include "core/math/camera_matrix.h" +#include "os/thread_safe.h" +#include "scene/main/viewport.h" +#include "servers/arvr_server.h" + +/** + @author Bastiaan Olij <mux213@gmail.com> + + The ARVR interface is a template class ontop of which we build interface to differt AR, VR and tracking SDKs. + The idea is that we subclass this class, implement the logic, and then instantiate a singleton of each interface + when Godot starts. These instances do not initialize themselves but register themselves with the AR/VR server. + + If the user wants to enable AR/VR the choose the interface they want to use and initialize it. + + Note that we may make this into a fully instantiable class for GDNative support. +*/ + +class ARVRInterface : public Reference { + GDCLASS(ARVRInterface, Reference); + +protected: + _THREAD_SAFE_CLASS_ + + static void _bind_methods(); + +public: + enum Eyes { + EYE_MONO, /* my son says we should call this EYE_CYCLOPS */ + EYE_LEFT, + EYE_RIGHT + }; + + virtual StringName get_name() const; + + bool is_primary(); + void set_is_primary(bool p_is_primary); + + virtual bool is_installed() = 0; /* returns true if the middle ware related to this interface has been installed */ + virtual bool hmd_is_present() = 0; /* returns true if our HMD is connected */ + virtual bool supports_hmd() = 0; /* returns true is this interface handles output to an HMD or only handles positioning */ + + virtual bool is_initialized() = 0; /* returns true if we've initialized this interface */ + virtual bool initialize() = 0; /* initialize this interface, if this has an HMD it becomes the primary interface */ + virtual void uninitialize() = 0; /* deinitialize this interface */ + + virtual Size2 get_recommended_render_targetsize() = 0; /* returns the recommended render target size per eye for this device */ + virtual bool is_stereo() = 0; /* returns true if this interface requires stereo rendering (for VR HMDs) or mono rendering (for mobile AR) */ + virtual Transform get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform) = 0; /* get each eyes camera transform, also implement EYE_MONO */ + virtual CameraMatrix get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) = 0; /* get each eyes projection matrix */ + virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) = 0; /* output the left or right eye */ + + virtual void process() = 0; +}; + +VARIANT_ENUM_CAST(ARVRInterface::Eyes); + +#endif diff --git a/servers/arvr/arvr_positional_tracker.cpp b/servers/arvr/arvr_positional_tracker.cpp new file mode 100644 index 0000000000..9f3d01267b --- /dev/null +++ b/servers/arvr/arvr_positional_tracker.cpp @@ -0,0 +1,171 @@ +/*************************************************************************/ +/* arvr_postional_tracker.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "arvr_positional_tracker.h" +#include "core/os/input.h" + +void ARVRPositionalTracker::_bind_methods() { + // this class is read only from GDScript, so we only have access to getters.. + ClassDB::bind_method(D_METHOD("get_type"), &ARVRPositionalTracker::get_type); + ClassDB::bind_method(D_METHOD("get_name"), &ARVRPositionalTracker::get_name); + ClassDB::bind_method(D_METHOD("get_joy_id"), &ARVRPositionalTracker::get_joy_id); + ClassDB::bind_method(D_METHOD("get_tracks_orientation"), &ARVRPositionalTracker::get_tracks_orientation); + ClassDB::bind_method(D_METHOD("get_orientation"), &ARVRPositionalTracker::get_orientation); + 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) { + if (type != p_type) { + type = p_type; + + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL(arvr_server); + + // get a tracker id for our type + tracker_id = arvr_server->get_free_tracker_id_for_type(p_type); + } +}; + +ARVRServer::TrackerType ARVRPositionalTracker::get_type() const { + return type; +}; + +void ARVRPositionalTracker::set_name(const String p_name) { + name = p_name; +}; + +StringName ARVRPositionalTracker::get_name() const { + return name; +}; + +int ARVRPositionalTracker::get_tracker_id() const { + return tracker_id; +}; + +void ARVRPositionalTracker::set_joy_id(int p_joy_id) { + joy_id = p_joy_id; +}; + +int ARVRPositionalTracker::get_joy_id() const { + return joy_id; +}; + +bool ARVRPositionalTracker::get_tracks_orientation() const { + return tracks_orientation; +}; + +void ARVRPositionalTracker::set_orientation(const Basis &p_orientation) { + _THREAD_SAFE_METHOD_ + + tracks_orientation = true; // obviously we have this + orientation = p_orientation; +}; + +Basis ARVRPositionalTracker::get_orientation() const { + _THREAD_SAFE_METHOD_ + + return orientation; +}; + +bool ARVRPositionalTracker::get_tracks_position() const { + return tracks_position; +}; + +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 + rw_position = p_position / world_scale; +}; + +Vector3 ARVRPositionalTracker::get_position() const { + _THREAD_SAFE_METHOD_ + + 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 { + Transform new_transform; + + new_transform.basis = get_orientation(); + new_transform.origin = get_position(); + + if (p_adjust_by_reference_frame) { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + ERR_FAIL_NULL_V(arvr_server, new_transform); + + new_transform = arvr_server->get_reference_frame() * new_transform; + }; + + return new_transform; +}; + +ARVRPositionalTracker::ARVRPositionalTracker() { + type = ARVRServer::TRACKER_UNKNOWN; + name = "Unknown"; + joy_id = -1; + tracker_id = 0; + tracks_orientation = false; + tracks_position = false; +}; + +ARVRPositionalTracker::~ARVRPositionalTracker(){ + +}; diff --git a/servers/arvr/arvr_positional_tracker.h b/servers/arvr/arvr_positional_tracker.h new file mode 100644 index 0000000000..dba203b73c --- /dev/null +++ b/servers/arvr/arvr_positional_tracker.h @@ -0,0 +1,87 @@ +/*************************************************************************/ +/* arvr_positional_tracker.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#ifndef ARVR_POSITIONAL_TRACKER_H +#define ARVR_POSITIONAL_TRACKER_H + +#include "os/thread_safe.h" +#include "servers/arvr_server.h" + +/** + @author Bastiaan Olij <mux213@gmail.com> + + The positional tracker object as an object that represents the position and orientation of a tracked object like a controller or headset. + An AR/VR Interface will registered the trackers it manages with our AR/VR server and update its position and orientation. + This is where potentially additional AR/VR interfaces may be active as there are AR/VR SDKs that solely deal with positional tracking. + + @TODO: + - create subclass of spatial node that uses one of our positional trackers to automatically determine its position +*/ + +class ARVRPositionalTracker : public Object { + GDCLASS(ARVRPositionalTracker, Object); + _THREAD_SAFE_CLASS_ + +private: + ARVRServer::TrackerType type; // type of tracker + StringName name; // (unique) name of the tracker + int tracker_id; // tracker index id that is unique per type + int joy_id; // if we also have a related joystick entity, the id of the joystick + bool tracks_orientation; // do we track orientation? + Basis orientation; // our orientation + bool tracks_position; // do we track position? + Vector3 rw_position; // our position "in the real world, so without world_scale applied" + +protected: + static void _bind_methods(); + +public: + void set_type(ARVRServer::TrackerType p_type); + ARVRServer::TrackerType get_type() const; + void set_name(const String p_name); + StringName get_name() const; + int get_tracker_id() const; + void set_joy_id(int p_joy_id); + int get_joy_id() const; + bool get_tracks_orientation() const; + void set_orientation(const Basis &p_orientation); + Basis get_orientation() const; + bool get_tracks_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; + + ARVRPositionalTracker(); + ~ARVRPositionalTracker(); +}; + +#endif diff --git a/servers/arvr/arvr_script_interface.cpp b/servers/arvr/arvr_script_interface.cpp new file mode 100644 index 0000000000..16e607920e --- /dev/null +++ b/servers/arvr/arvr_script_interface.cpp @@ -0,0 +1,127 @@ +#include "arvr_script_interface.h" + +ARVRScriptInterface::ARVRScriptInterface() { + // testing + printf("Construct script interface"); +} + +ARVRScriptInterface::~ARVRScriptInterface() { + if (is_initialized()) { + uninitialize(); + }; + + // testing + printf("Destruct script interface"); +} + +StringName ARVRScriptInterface::get_name() const { + if (get_script_instance() && get_script_instance()->has_method("get_name")) { + return get_script_instance()->call("get_name"); + } else { + // just return something for now + return "ARVR Script interface"; + } +} + +bool ARVRScriptInterface::is_installed() { + ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("is_installed")), false); + return get_script_instance()->call("is_installed"); +} + +bool ARVRScriptInterface::hmd_is_present() { + ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("hmd_is_present")), false); + return get_script_instance()->call("hmd_is_present"); +} + +bool ARVRScriptInterface::supports_hmd() { + ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("supports_hmd")), false); + return get_script_instance()->call("supports_hmd"); +} + +bool ARVRScriptInterface::is_stereo() { + ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("is_stereo")), false); + return get_script_instance()->call("is_stereo"); +} + +bool ARVRScriptInterface::is_initialized() { + ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("is_initialized")), false); + return get_script_instance()->call("is_initialized"); +} + +bool ARVRScriptInterface::initialize() { + ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("initialize")), false); + return get_script_instance()->call("initialize"); +} + +void ARVRScriptInterface::uninitialize() { + ARVRServer *arvr_server = ARVRServer::get_singleton(); + if (arvr_server != NULL) { + // Whatever happens, make sure this is no longer our primary interface + arvr_server->clear_primary_interface_if(this); + } + + ERR_FAIL_COND(!(get_script_instance() && get_script_instance()->has_method("uninitialize"))); + get_script_instance()->call("uninitialize"); +} + +Size2 ARVRScriptInterface::get_recommended_render_targetsize() { + ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("get_recommended_render_targetsize")), Size2()); + return get_script_instance()->call("get_recommended_render_targetsize"); +} + +Transform ARVRScriptInterface::get_transform_for_eye(Eyes p_eye, const Transform &p_cam_transform) { + ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("get_transform_for_eye")), Transform()); + return get_script_instance()->call("get_transform_for_eye", p_eye, p_cam_transform); +} + +// Suggestion from Reduz, as we can't return a CameraMatrix, return a PoolVector with our 16 floats +PoolVector<float> ARVRScriptInterface::_get_projection_for_eye(Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { + ERR_FAIL_COND_V(!(get_script_instance() && get_script_instance()->has_method("_get_projection_for_eye")), PoolVector<float>()); + return get_script_instance()->call("_get_projection_for_eye", p_eye, p_aspect, p_z_near, p_z_far); +} + +CameraMatrix ARVRScriptInterface::get_projection_for_eye(Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { + CameraMatrix cm; + int i = 0; + int j = 0; + + PoolVector<float> cm_as_floats = _get_projection_for_eye(p_eye, p_aspect, p_z_near, p_z_far); + + for (int k = 0; k < cm_as_floats.size() && i < 4; k++) { + cm.matrix[i][j] = cm_as_floats[k]; + j++; + if (j == 4) { + j = 0; + i++; + }; + }; + + return cm; +} + +void ARVRScriptInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { + ERR_FAIL_COND(!(get_script_instance() && get_script_instance()->has_method("commit_for_eye"))); + get_script_instance()->call("commit_for_eye"); +} + +void ARVRScriptInterface::process() { + ERR_FAIL_COND(!(get_script_instance() && get_script_instance()->has_method("process"))); + get_script_instance()->call("process"); +} + +void ARVRScriptInterface::_bind_methods() { + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "is_installed")); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "hmd_is_present")); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "supports_hmd")); + + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "is_initialized")); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "initialize")); + ClassDB::add_virtual_method(get_class_static(), MethodInfo("uninitialize")); + + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::BOOL, "is_stereo")); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::VECTOR2, "get_recommended_render_targetsize")); + ClassDB::add_virtual_method(get_class_static(), MethodInfo(Variant::TRANSFORM, "get_transform_for_eye", PropertyInfo(Variant::INT, "eye"), PropertyInfo(Variant::TRANSFORM, "cam_transform"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo("_get_projection_for_eye")); + ClassDB::add_virtual_method(get_class_static(), MethodInfo("commit_for_eye", PropertyInfo(Variant::INT, "eye"), PropertyInfo(Variant::_RID, "render_target"))); + ClassDB::add_virtual_method(get_class_static(), MethodInfo("process")); +} diff --git a/servers/arvr/arvr_script_interface.h b/servers/arvr/arvr_script_interface.h new file mode 100644 index 0000000000..04ca33901a --- /dev/null +++ b/servers/arvr/arvr_script_interface.h @@ -0,0 +1,47 @@ +#ifndef SCRIPT_INTERFACE_H +#define SCRIPT_INTERFACE_H + +#include "arvr_interface.h" + +/** + @authors Hinsbart & Karroffel + + This subclass of our AR/VR interface forms a bridge to GDNative. +*/ + +class ARVRScriptInterface : public ARVRInterface { + GDCLASS(ARVRScriptInterface, ARVRInterface); + +protected: + static void _bind_methods(); + +public: + ARVRScriptInterface(); + ~ARVRScriptInterface(); + + virtual StringName get_name() const; + + virtual bool is_installed(); + virtual bool hmd_is_present(); + virtual bool supports_hmd(); + + virtual bool is_initialized(); + virtual bool initialize(); + virtual void uninitialize(); + + virtual Size2 get_recommended_render_targetsize(); + virtual bool is_stereo(); + virtual Transform get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform); + + // we expose a PoolVector<float> version of this function to GDNative + PoolVector<float> _get_projection_for_eye(Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); + + // and a CameraMatrix version to ARVRServer + virtual CameraMatrix get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); + + virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); + + virtual void process(); +}; + +#endif // SCRIPT_INTERFACE_H diff --git a/servers/arvr_server.cpp b/servers/arvr_server.cpp new file mode 100644 index 0000000000..78b3e514e6 --- /dev/null +++ b/servers/arvr_server.cpp @@ -0,0 +1,313 @@ +/*************************************************************************/ +/* arvr_server.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "arvr_server.h" +#include "arvr/arvr_interface.h" +#include "arvr/arvr_positional_tracker.h" +#include "project_settings.h" + +ARVRServer *ARVRServer::singleton = NULL; + +ARVRServer *ARVRServer::get_singleton() { + return singleton; +}; + +void ARVRServer::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_world_scale"), &ARVRServer::get_world_scale); + ClassDB::bind_method(D_METHOD("set_world_scale"), &ARVRServer::set_world_scale); + ClassDB::bind_method(D_METHOD("get_reference_frame"), &ARVRServer::get_reference_frame); + ClassDB::bind_method(D_METHOD("request_reference_frame", "ignore_tilt", "keep_height"), &ARVRServer::request_reference_frame); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "world_scale"), "set_world_scale", "get_world_scale"); + + ClassDB::bind_method(D_METHOD("get_interface_count"), &ARVRServer::get_interface_count); + ClassDB::bind_method(D_METHOD("get_interface:ARVRInterface", "idx"), &ARVRServer::get_interface); + ClassDB::bind_method(D_METHOD("find_interface:ARVRInterface", "name"), &ARVRServer::find_interface); + ClassDB::bind_method(D_METHOD("get_tracker_count"), &ARVRServer::get_tracker_count); + ClassDB::bind_method(D_METHOD("get_tracker:ARVRPositionalTracker", "idx"), &ARVRServer::get_tracker); + + ClassDB::bind_method(D_METHOD("set_primary_interface"), &ARVRServer::set_primary_interface); + + ClassDB::bind_method(D_METHOD("add_interface"), &ARVRServer::add_interface); + ClassDB::bind_method(D_METHOD("remove_interface"), &ARVRServer::remove_interface); + + BIND_CONSTANT(TRACKER_CONTROLLER); + BIND_CONSTANT(TRACKER_BASESTATION); + BIND_CONSTANT(TRACKER_ANCHOR); + BIND_CONSTANT(TRACKER_UNKNOWN); + BIND_CONSTANT(TRACKER_ANY_KNOWN); + BIND_CONSTANT(TRACKER_ANY); + + ADD_SIGNAL(MethodInfo("interface_added", PropertyInfo(Variant::STRING, "name"))); + ADD_SIGNAL(MethodInfo("interface_removed", PropertyInfo(Variant::STRING, "name"))); + + ADD_SIGNAL(MethodInfo("tracker_added", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::INT, "type"))); + ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING, "name"))); +}; + +real_t ARVRServer::get_world_scale() const { + return world_scale; +}; + +void ARVRServer::set_world_scale(real_t p_world_scale) { + if (world_scale < 0.01) { + world_scale = 0.01; + } else if (world_scale > 1000.0) { + world_scale = 1000.0; + }; + + world_scale = p_world_scale; +}; + +Transform ARVRServer::get_world_origin() const { + return world_origin; +}; + +void ARVRServer::set_world_origin(const Transform p_world_origin) { + world_origin = p_world_origin; +}; + +Transform ARVRServer::get_reference_frame() const { + return reference_frame; +}; + +void ARVRServer::request_reference_frame(bool p_ignore_tilt, bool p_keep_height) { + if (primary_interface != NULL) { + // clear our current reference frame or we'll end up double adjusting it + reference_frame = Transform(); + + // requesting our EYE_MONO transform should return our current HMD position + Transform new_reference_frame = primary_interface->get_transform_for_eye(ARVRInterface::EYE_MONO, Transform()); + + // remove our tilt + if (p_ignore_tilt) { + // take the Y out of our Z + new_reference_frame.basis.set_axis(2, Vector3(new_reference_frame.basis.elements[0][2], 0.0, new_reference_frame.basis.elements[2][2]).normalized()); + + // Y is straight up + new_reference_frame.basis.set_axis(1, Vector3(0.0, 1.0, 0.0)); + + // and X is our cross reference + new_reference_frame.basis.set_axis(0, new_reference_frame.basis.get_axis(1).cross(new_reference_frame.basis.get_axis(2)).normalized()); + }; + + // don't negate our height + if (p_keep_height) { + new_reference_frame.origin.y = 0.0; + }; + + reference_frame = new_reference_frame.inverse(); + }; +}; + +void ARVRServer::add_interface(const Ref<ARVRInterface> &p_interface) { + ERR_FAIL_COND(p_interface.is_null()); + + int idx = -1; + for (int i = 0; i < interfaces.size(); i++) { + + if (interfaces[i] == p_interface) { + ERR_PRINT("Interface was already added"); + return; + }; + }; + + print_line("Registered interface " + p_interface->get_name()); + + interfaces.push_back(p_interface); + emit_signal("interface_added", p_interface->get_name()); +}; + +void ARVRServer::remove_interface(const Ref<ARVRInterface> &p_interface) { + ERR_FAIL_COND(p_interface.is_null()); + + int idx = -1; + for (int i = 0; i < interfaces.size(); i++) { + + if (interfaces[i] == p_interface) { + + idx = i; + break; + }; + }; + + ERR_FAIL_COND(idx == -1); + + print_line("Removed interface" + p_interface->get_name()); + + emit_signal("interface_removed", p_interface->get_name()); + interfaces.remove(idx); +}; + +int ARVRServer::get_interface_count() const { + return interfaces.size(); +}; + +Ref<ARVRInterface> ARVRServer::get_interface(int p_index) const { + ERR_FAIL_INDEX_V(p_index, interfaces.size(), NULL); + + return interfaces[p_index]; +}; + +Ref<ARVRInterface> ARVRServer::find_interface(const String &p_name) const { + int idx = -1; + for (int i = 0; i < interfaces.size(); i++) { + + if (interfaces[i]->get_name() == p_name) { + + idx = i; + break; + }; + }; + + ERR_FAIL_COND_V(idx == -1, NULL); + + return interfaces[idx]; +}; + +/* + A little extra info on the tracker ids, these are unique per tracker type so we get soem consistency in recognising our trackers, specifically controllers. + + The first controller that is turned of will get ID 1, the second will get ID 2, etc. + The magic happens when one of the controllers is turned off, say controller 1 turns off, controller 2 will remain controller 2, controller 3 will remain controller 3. + If controller number 1 is turned on again it again gets ID 1 unless another new controller was turned on since. + + The most likely scenario however is a controller that runs out of battery and another controller being used to replace it. + Because the controllers are often linked to physical objects, say you're holding a shield in controller 1, your left hand, and a gun in controller 2, your right hand, and controller 1 dies: + - using our tracker index would suddenly make the gun disappear and the shield jump into your right hand because controller 2 becomes controller 1. + - using this approach the shield disappears or is no longer tracked, but the gun stays firmly in your right hand because that is still controller 2, further more, if controller 1 is replaced the shield will return. +*/ + +bool ARVRServer::is_tracker_id_in_use_for_type(TrackerType p_tracker_type, int p_tracker_id) const { + for (int i = 0; i < trackers.size(); i++) { + if (trackers[i]->get_type() == p_tracker_type && trackers[i]->get_tracker_id() == p_tracker_id) { + return true; + }; + }; + + // all good + return false; +}; + +int ARVRServer::get_free_tracker_id_for_type(TrackerType p_tracker_type) { + // we start checking at 1, 0 means that it's not a controller.. + int tracker_id = 1; + + while (is_tracker_id_in_use_for_type(p_tracker_type, tracker_id)) { + // try the next one + tracker_id++; + }; + + return tracker_id; +}; + +void ARVRServer::add_tracker(ARVRPositionalTracker *p_tracker) { + ERR_FAIL_NULL(p_tracker); + + trackers.push_back(p_tracker); + emit_signal("tracker_added", p_tracker->get_name(), p_tracker->get_type()); +}; + +void ARVRServer::remove_tracker(ARVRPositionalTracker *p_tracker) { + ERR_FAIL_NULL(p_tracker); + + int idx = -1; + for (int i = 0; i < trackers.size(); i++) { + + if (trackers[i] == p_tracker) { + + idx = i; + break; + }; + }; + + ERR_FAIL_COND(idx == -1); + + emit_signal("tracker_removed", p_tracker->get_name()); + trackers.remove(idx); +}; + +int ARVRServer::get_tracker_count() const { + return trackers.size(); +}; + +ARVRPositionalTracker *ARVRServer::get_tracker(int p_index) const { + ERR_FAIL_INDEX_V(p_index, trackers.size(), NULL); + + return trackers[p_index]; +}; + +ARVRPositionalTracker *ARVRServer::find_by_type_and_id(TrackerType p_tracker_type, int p_tracker_id) const { + ERR_FAIL_COND_V(p_tracker_id == 0, NULL); + + for (int i = 0; i < trackers.size(); i++) { + if (trackers[i]->get_type() == p_tracker_type && trackers[i]->get_tracker_id() == p_tracker_id) { + return trackers[i]; + }; + }; + + return NULL; +}; + +Ref<ARVRInterface> ARVRServer::get_primary_interface() const { + return primary_interface; +}; + +void ARVRServer::set_primary_interface(const Ref<ARVRInterface> &p_primary_interface) { + primary_interface = p_primary_interface; + + print_line("Primary interface set to: " + primary_interface->get_name()); +}; + +void ARVRServer::clear_primary_interface_if(const Ref<ARVRInterface> &p_primary_interface) { + if (primary_interface == p_primary_interface) { + print_line("Clearing primary interface"); + primary_interface.unref(); + }; +}; + +ARVRServer::ARVRServer() { + singleton = this; + world_scale = 1.0; +}; + +ARVRServer::~ARVRServer() { + primary_interface.unref(); + + while (interfaces.size() > 0) { + interfaces.remove(0); + } + + while (trackers.size() > 0) { + trackers.remove(0); + } + + singleton = NULL; +}; diff --git a/servers/arvr_server.h b/servers/arvr_server.h new file mode 100644 index 0000000000..fd7c5470c3 --- /dev/null +++ b/servers/arvr_server.h @@ -0,0 +1,167 @@ +/*************************************************************************/ +/* arvr_server.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ARVR_SERVER_H +#define ARVR_SERVER_H + +#include "os/thread_safe.h" +#include "reference.h" +#include "rid.h" +#include "variant.h" + +class ARVRInterface; +class ARVRPositionalTracker; + +/** + @author Bastiaan Olij <mux213@gmail.com> + + The ARVR server is a singleton object that gives access to the various + objects and SDKs that are available on the system. + Because there can be multiple SDKs active this is exposed as an array + and our ARVR server object acts as a pass through + Also each positioning tracker is accessible from here. + + I've added some additional info into this header file that should move + into the documention, I will do so when we're close to accepting this PR + or as a separate PR once this has been merged into the master branch. +**/ + +class ARVRServer : public Object { + GDCLASS(ARVRServer, Object); + _THREAD_SAFE_CLASS_ + +public: + enum TrackerType { + TRACKER_CONTROLLER = 0x01, /* tracks a controller */ + TRACKER_BASESTATION = 0x02, /* tracks location of a base station */ + TRACKER_ANCHOR = 0x04, /* tracks an anchor point, used in AR to track a real live location */ + TRACKER_UNKNOWN = 0x80, /* unknown tracker */ + + TRACKER_ANY_KNOWN = 0x7f, /* all except unknown */ + TRACKER_ANY = 0xff /* used by get_connected_trackers to return all types */ + }; + +private: + Vector<Ref<ARVRInterface> > interfaces; + Vector<ARVRPositionalTracker *> trackers; + + Ref<ARVRInterface> primary_interface; /* we'll identify one interface as primary, this will be used by our viewports */ + + real_t world_scale; /* scale by which we multiply our tracker positions */ + Transform world_origin; /* our world origin point, maps a location in our virtual world to the origin point in our real world tracking volume */ + Transform reference_frame; /* our reference frame */ + + bool is_tracker_id_in_use_for_type(TrackerType p_tracker_type, int p_tracker_id) const; + +protected: + static ARVRServer *singleton; + + static void _bind_methods(); + +public: + static ARVRServer *get_singleton(); + + /* + World scale allows you to specify a scale factor that is applied to all positioning vectors in our VR world in essence scaling up, or scaling down the world. + For stereoscopic rendering specifically this is very important to give an accurate sense of scale. + Add controllers into the mix and an accurate mapping of real world movement to percieved virtual movement becomes very important. + + Most VR platforms, and our assumption, is that 1 unit in our virtual world equates to 1 meter in the real mode. + This scale basically effects the unit size relationship to real world size. + + I may remove access to this property in GDScript in favour of exposing it on the ARVROrigin node + */ + real_t get_world_scale() const; + void set_world_scale(real_t p_world_scale); + + /* + The world maps the 0,0,0 coordinate of our real world coordinate system for our tracking volume to a location in our + virtual world. It is this origin point that should be moved when the player is moved through the world by controller + actions be it straffing, teleporting, etc. Movement of the player by moving through the physical space is always tracked + in relation to this point. + + Note that the ARVROrigin spatial node in your scene automatically updates this property and it should be used instead of + direct access to this property and it therefor is not available in GDScript + + Note: this should not be used in AR and should be ignored by an AR based interface as it would throw what you're looking at in the real world + and in the virtual world out of sync + */ + Transform get_world_origin() const; + void set_world_origin(const Transform p_origin); + + /* + Requesting a reference frame results in a matrix being calculated that ensures the HMD is positioned to 0,0,0 facing 0,0,-1 (need to verify this direction) + in the virtual world. + + Note: this should not be used in AR and should be ignored by an AR based interface as it would throw what you're looking at in the real world + and in the virtual world out of sync + */ + Transform get_reference_frame() const; + void request_reference_frame(bool p_ignore_tilt, bool p_keep_height); + + /* + Interfaces are objects that 'glue' Godot to an AR or VR SDK such as the Oculus SDK, OpenVR, OpenHMD, etc. + */ + void add_interface(const Ref<ARVRInterface> &p_interface); + void remove_interface(const Ref<ARVRInterface> &p_interface); + int get_interface_count() const; + Ref<ARVRInterface> get_interface(int p_index) const; + Ref<ARVRInterface> find_interface(const String &p_name) const; + + /* + note, more then one interface can technically be active, especially on mobile, but only one interface is used for + rendering. This interface identifies itself by calling set_primary_interface when it is initialized + */ + Ref<ARVRInterface> get_primary_interface() const; + void set_primary_interface(const Ref<ARVRInterface> &p_primary_interface); + void clear_primary_interface_if(const Ref<ARVRInterface> &p_primary_interface); /* this is automatically called if an interface destructs */ + + /* + Our trackers are objects that expose the orientation and position of physical devices such as controller, anchor points, etc. + They are created and managed by our active AR/VR interfaces. + + Note that for trackers that + */ + int get_free_tracker_id_for_type(TrackerType p_tracker_type); + void add_tracker(ARVRPositionalTracker *p_tracker); + void remove_tracker(ARVRPositionalTracker *p_tracker); + int get_tracker_count() const; + ARVRPositionalTracker *get_tracker(int p_index) const; + ARVRPositionalTracker *find_by_type_and_id(TrackerType p_tracker_type, int p_tracker_id) const; + + ARVRServer(); + ~ARVRServer(); +}; + +#define ARVR ARVRServer + +VARIANT_ENUM_CAST(ARVRServer::TrackerType); + +#endif diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 5303aea6d0..d5f351454d 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -66,7 +66,8 @@ void AudioDriver::audio_server_process(int p_frames, int32_t *p_buffer, bool p_u void AudioDriver::update_mix_time(int p_frames) { _mix_amount += p_frames; - _last_mix_time = OS::get_singleton()->get_ticks_usec(); + if (OS::get_singleton()) + _last_mix_time = OS::get_singleton()->get_ticks_usec(); } double AudioDriver::get_mix_time() const { @@ -771,10 +772,11 @@ void AudioServer::finish() { buses.clear(); - if (AudioDriver::get_singleton()) { - AudioDriver::get_singleton()->finish(); + for (int i = 0; i < AudioDriverManager::get_driver_count(); i++) { + AudioDriverManager::get_driver(i)->finish(); } } + void AudioServer::update() { } diff --git a/servers/physics/area_sw.h b/servers/physics/area_sw.h index 06e58e3d5a..3dae1db13f 100644 --- a/servers/physics/area_sw.h +++ b/servers/physics/area_sw.h @@ -154,6 +154,7 @@ public: _FORCE_INLINE_ void add_constraint(ConstraintSW *p_constraint) { constraints.insert(p_constraint); } _FORCE_INLINE_ void remove_constraint(ConstraintSW *p_constraint) { constraints.erase(p_constraint); } _FORCE_INLINE_ const Set<ConstraintSW *> &get_constraints() const { return constraints; } + _FORCE_INLINE_ void clear_constraints() { constraints.clear(); } void set_monitorable(bool p_monitorable); _FORCE_INLINE_ bool is_monitorable() const { return monitorable; } diff --git a/servers/physics/body_sw.cpp b/servers/physics/body_sw.cpp index 1f32c059a8..e065fae2be 100644 --- a/servers/physics/body_sw.cpp +++ b/servers/physics/body_sw.cpp @@ -757,7 +757,8 @@ BodySW::BodySW() contact_count = 0; gravity_scale = 1.0; - + linear_damp = -1; + angular_damp = -1; area_angular_damp = 0; area_linear_damp = 0; diff --git a/servers/physics/body_sw.h b/servers/physics/body_sw.h index c3e051c2d0..512b868570 100644 --- a/servers/physics/body_sw.h +++ b/servers/physics/body_sw.h @@ -194,6 +194,7 @@ public: _FORCE_INLINE_ void add_constraint(ConstraintSW *p_constraint, int p_pos) { constraint_map[p_constraint] = p_pos; } _FORCE_INLINE_ void remove_constraint(ConstraintSW *p_constraint) { constraint_map.erase(p_constraint); } const Map<ConstraintSW *, int> &get_constraint_map() const { return constraint_map; } + _FORCE_INLINE_ void clear_constraint_map() { constraint_map.clear(); } _FORCE_INLINE_ void set_omit_force_integration(bool p_omit_force_integration) { omit_force_integration = p_omit_force_integration; } _FORCE_INLINE_ bool get_omit_force_integration() const { return omit_force_integration; } diff --git a/servers/physics/physics_server_sw.cpp b/servers/physics/physics_server_sw.cpp index 101bd4b185..833c77216e 100644 --- a/servers/physics/physics_server_sw.cpp +++ b/servers/physics/physics_server_sw.cpp @@ -222,12 +222,24 @@ void PhysicsServerSW::area_set_space(RID p_area, RID p_space) { AreaSW *area = area_owner.get(p_area); ERR_FAIL_COND(!area); + SpaceSW *space = NULL; if (p_space.is_valid()) { space = space_owner.get(p_space); ERR_FAIL_COND(!space); } + if (area->get_space() == space) + return; //pointless + + for (Set<ConstraintSW *>::Element *E = area->get_constraints().front(); E; E = E->next()) { + RID self = E->get()->get_self(); + if (!self.is_valid()) + continue; + free(self); + } + area->clear_constraints(); + area->set_space(space); }; @@ -471,15 +483,23 @@ void PhysicsServerSW::body_set_space(RID p_body, RID p_space) { BodySW *body = body_owner.get(p_body); ERR_FAIL_COND(!body); - SpaceSW *space = NULL; + SpaceSW *space = NULL; if (p_space.is_valid()) { space = space_owner.get(p_space); ERR_FAIL_COND(!space); } if (body->get_space() == space) - return; //pointles + return; //pointless + + while (body->get_constraint_map().size()) { + RID self = body->get_constraint_map().front()->key()->get_self(); + if (!self.is_valid()) + continue; + free(self); + } + body->clear_constraint_map(); body->set_space(space); }; @@ -1329,12 +1349,6 @@ void PhysicsServerSW::free(RID p_rid) { body->remove_shape(0); } - while (body->get_constraint_map().size()) { - RID self = body->get_constraint_map().front()->key()->get_self(); - ERR_FAIL_COND(!self.is_valid()); - free(self); - } - body_owner.free(p_rid); memdelete(body); diff --git a/servers/physics/space_sw.cpp b/servers/physics/space_sw.cpp index 5679fc8f60..094cfa4656 100644 --- a/servers/physics/space_sw.cpp +++ b/servers/physics/space_sw.cpp @@ -34,12 +34,12 @@ _FORCE_INLINE_ static bool _match_object_type_query(CollisionObjectSW *p_object, uint32_t p_collision_layer, uint32_t p_type_mask) { - if (p_object->get_type() == CollisionObjectSW::TYPE_AREA) - return p_type_mask & PhysicsDirectSpaceState::TYPE_MASK_AREA; - if ((p_object->get_collision_layer() & p_collision_layer) == 0) return false; + if (p_object->get_type() == CollisionObjectSW::TYPE_AREA) + return p_type_mask & PhysicsDirectSpaceState::TYPE_MASK_AREA; + BodySW *body = static_cast<BodySW *>(p_object); return (1 << body->get_mode()) & p_type_mask; diff --git a/servers/physics_2d/area_2d_sw.h b/servers/physics_2d/area_2d_sw.h index 68b3c61e44..6d74a4b0f6 100644 --- a/servers/physics_2d/area_2d_sw.h +++ b/servers/physics_2d/area_2d_sw.h @@ -153,6 +153,7 @@ public: _FORCE_INLINE_ void add_constraint(Constraint2DSW *p_constraint) { constraints.insert(p_constraint); } _FORCE_INLINE_ void remove_constraint(Constraint2DSW *p_constraint) { constraints.erase(p_constraint); } _FORCE_INLINE_ const Set<Constraint2DSW *> &get_constraints() const { return constraints; } + _FORCE_INLINE_ void clear_constraints() { constraints.clear(); } void set_monitorable(bool p_monitorable); _FORCE_INLINE_ bool is_monitorable() const { return monitorable; } diff --git a/servers/physics_2d/body_2d_sw.h b/servers/physics_2d/body_2d_sw.h index 9e5deef3f2..412f2f51cd 100644 --- a/servers/physics_2d/body_2d_sw.h +++ b/servers/physics_2d/body_2d_sw.h @@ -181,6 +181,7 @@ public: _FORCE_INLINE_ void add_constraint(Constraint2DSW *p_constraint, int p_pos) { constraint_map[p_constraint] = p_pos; } _FORCE_INLINE_ void remove_constraint(Constraint2DSW *p_constraint) { constraint_map.erase(p_constraint); } const Map<Constraint2DSW *, int> &get_constraint_map() const { return constraint_map; } + _FORCE_INLINE_ void clear_constraint_map() { constraint_map.clear(); } _FORCE_INLINE_ void set_omit_force_integration(bool p_omit_force_integration) { omit_force_integration = p_omit_force_integration; } _FORCE_INLINE_ bool get_omit_force_integration() const { return omit_force_integration; } diff --git a/servers/physics_2d/broad_phase_2d_hash_grid.cpp b/servers/physics_2d/broad_phase_2d_hash_grid.cpp index 5b6c7e2f38..0330bfa9f3 100644 --- a/servers/physics_2d/broad_phase_2d_hash_grid.cpp +++ b/servers/physics_2d/broad_phase_2d_hash_grid.cpp @@ -203,9 +203,11 @@ void BroadPhase2DHashGrid::_exit_grid(Element *p_elem, const Rect2 &p_rect, bool if (sz.width * sz.height > large_object_min_surface) { //unpair all elements, instead of checking all, just check what is already paired, so we at least save from checking static vs static - for (Map<Element *, PairData *>::Element *E = p_elem->paired.front(); E; E = E->next()) { - + Map<Element *, PairData *>::Element *E = p_elem->paired.front(); + while (E) { + Map<Element *, PairData *>::Element *next = E->next(); _unpair_attempt(p_elem, E->key()); + E = next; } if (large_elements[p_elem].dec() == 0) { diff --git a/servers/physics_2d/physics_2d_server_sw.cpp b/servers/physics_2d/physics_2d_server_sw.cpp index c20d0d14a2..add376bfb2 100644 --- a/servers/physics_2d/physics_2d_server_sw.cpp +++ b/servers/physics_2d/physics_2d_server_sw.cpp @@ -286,12 +286,24 @@ void Physics2DServerSW::area_set_space(RID p_area, RID p_space) { Area2DSW *area = area_owner.get(p_area); ERR_FAIL_COND(!area); + Space2DSW *space = NULL; if (p_space.is_valid()) { space = space_owner.get(p_space); ERR_FAIL_COND(!space); } + if (area->get_space() == space) + return; //pointless + + for (Set<Constraint2DSW *>::Element *E = area->get_constraints().front(); E; E = E->next()) { + RID self = E->get()->get_self(); + if (!self.is_valid()) + continue; + free(self); + } + area->clear_constraints(); + area->set_space(space); }; @@ -533,6 +545,17 @@ void Physics2DServerSW::body_set_space(RID p_body, RID p_space) { ERR_FAIL_COND(!space); } + if (body->get_space() == space) + return; //pointless + + while (body->get_constraint_map().size()) { + RID self = body->get_constraint_map().front()->key()->get_self(); + if (!self.is_valid()) + continue; + free(self); + } + body->clear_constraint_map(); + body->set_space(space); }; @@ -1073,19 +1096,13 @@ void Physics2DServerSW::free(RID p_rid) { _clear_query(body->get_direct_state_query()); */ - body->set_space(NULL); + body_set_space(p_rid, RID()); while (body->get_shape_count()) { body->remove_shape(0); } - while (body->get_constraint_map().size()) { - RID self = body->get_constraint_map().front()->key()->get_self(); - ERR_FAIL_COND(!self.is_valid()); - free(self); - } - body_owner.free(p_rid); memdelete(body); diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index c5029d1e13..092f445c13 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -30,6 +30,10 @@ #include "register_server_types.h" #include "project_settings.h" +#include "arvr/arvr_interface.h" +#include "arvr/arvr_positional_tracker.h" +#include "arvr/arvr_script_interface.h" +#include "arvr_server.h" #include "audio/audio_effect.h" #include "audio/audio_stream.h" #include "audio/effects/audio_effect_amplify.h" @@ -70,16 +74,23 @@ static void _debugger_get_resource_usage(List<ScriptDebuggerRemote::ResourceUsag } ShaderTypes *shader_types = NULL; +ARVRServer *arvr_server = NULL; void register_server_types() { + arvr_server = memnew(ARVRServer); ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("VisualServer", VisualServer::get_singleton())); ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("AudioServer", AudioServer::get_singleton())); ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("PhysicsServer", PhysicsServer::get_singleton())); ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("Physics2DServer", Physics2DServer::get_singleton())); + ProjectSettings::get_singleton()->add_singleton(ProjectSettings::Singleton("ARVRServer", ARVRServer::get_singleton())); shader_types = memnew(ShaderTypes); + ClassDB::register_virtual_class<ARVRInterface>(); + ClassDB::register_class<ARVRPositionalTracker>(); + ClassDB::register_class<ARVRScriptInterface>(); + ClassDB::register_virtual_class<AudioStream>(); ClassDB::register_virtual_class<AudioStreamPlayback>(); ClassDB::register_class<AudioStreamRandomPitch>(); @@ -133,5 +144,9 @@ void register_server_types() { void unregister_server_types() { + //@TODO move this into iPhone/Android implementation? just have this here for testing... + // mobile_interface = NULL; + memdelete(shader_types); + memdelete(arvr_server); } diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index 58481fc3f6..b13bb904ab 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -908,6 +908,7 @@ public: BIND0R(RID, viewport_create) + BIND2(viewport_set_use_arvr, RID, bool) BIND3(viewport_set_size, RID, int, int) BIND2(viewport_set_active, RID, bool) diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index 97583dc849..87431a2ce4 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -1658,6 +1658,7 @@ void VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons } void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas) { + // render to mono camera Camera *camera = camera_owner.getornull(p_camera); ERR_FAIL_COND(!camera); @@ -1697,6 +1698,25 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_view _render_scene(camera->transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), -1); } +void VisualServerScene::render_camera(Ref<ARVRInterface> &p_interface, ARVRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas) { + // render for AR/VR interface + + Camera *camera = camera_owner.getornull(p_camera); + ERR_FAIL_COND(!camera); + + /* SETUP CAMERA, we are ignoring type and FOV here */ + bool ortho = false; + float aspect = p_viewport_size.width / (float)p_viewport_size.height; + CameraMatrix camera_matrix = p_interface->get_projection_for_eye(p_eye, aspect, camera->znear, camera->zfar); + + // We also ignore our camera position, it will have been positioned with a slightly old tracking position. + // Instead we take our origin point and have our ar/vr interface add fresh tracking data! Whoohoo! + Transform world_origin = ARVRServer::get_singleton()->get_world_origin(); + Transform cam_transform = p_interface->get_transform_for_eye(p_eye, world_origin); + + _render_scene(cam_transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), -1); +}; + void VisualServerScene::_render_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass) { Scenario *scenario = scenario_owner.getornull(p_scenario); @@ -2716,8 +2736,8 @@ void VisualServerScene::_bake_gi_probe_light(const GIProbeDataHeader *header, co success_count++; } } - print_line("BAKE TIME: " + rtos((OS::get_singleton()->get_ticks_usec() - us) / 1000000.0)); - print_line("valid cells: " + itos(success_count)); + //print_line("BAKE TIME: " + rtos((OS::get_singleton()->get_ticks_usec() - us) / 1000000.0)); + //print_line("valid cells: " + itos(success_count)); } break; case VS::LIGHT_OMNI: @@ -2822,7 +2842,7 @@ void VisualServerScene::_bake_gi_probe_light(const GIProbeDataHeader *header, co light->energy[2] += int32_t(light_b * att * ((cell->albedo) & 0xFF) / 255.0); } } - print_line("BAKE TIME: " + rtos((OS::get_singleton()->get_ticks_usec() - us) / 1000000.0)); + //print_line("BAKE TIME: " + rtos((OS::get_singleton()->get_ticks_usec() - us) / 1000000.0)); } break; } @@ -2921,7 +2941,7 @@ void VisualServerScene::_bake_gi_probe(Instance *p_gi_probe) { if (stage >= probe_data->dynamic.mipmaps_3d.size()) continue; //no mipmap for this one - print_line("generating mipmap stage: " + itos(stage)); + //print_line("generating mipmap stage: " + itos(stage)); int level_cell_count = probe_data->dynamic.level_cell_lists[i].size(); const uint32_t *level_cells = probe_data->dynamic.level_cell_lists[i].ptr(); diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h index 168dfddfd4..17b95946b3 100644 --- a/servers/visual/visual_server_scene.h +++ b/servers/visual/visual_server_scene.h @@ -38,6 +38,7 @@ #include "os/semaphore.h" #include "os/thread.h" #include "self_list.h" +#include "servers/arvr/arvr_interface.h" class VisualServerScene { public: @@ -521,6 +522,7 @@ public: void render_empty_scene(RID p_scenario, RID p_shadow_atlas); void render_camera(RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas); + void render_camera(Ref<ARVRInterface> &p_interface, ARVRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas); void update_dirty_instances(); //probes diff --git a/servers/visual/visual_server_viewport.cpp b/servers/visual/visual_server_viewport.cpp index 2a37b64759..ad9dec090a 100644 --- a/servers/visual/visual_server_viewport.cpp +++ b/servers/visual/visual_server_viewport.cpp @@ -33,7 +33,7 @@ #include "visual_server_global.h" #include "visual_server_scene.h" -void VisualServerViewport::_draw_viewport(Viewport *p_viewport) { +void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::Eyes p_eye) { /* Camera should always be BEFORE any other 3D */ @@ -90,8 +90,13 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport) { } if (!scenario_draw_canvas_bg && can_draw_3d) { + Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); - VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); + if (p_viewport->use_arvr && arvr_interface.is_valid()) { + VSG::scene->render_camera(arvr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); + } else { + VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); + } } if (!p_viewport->hide_canvas) { @@ -260,15 +265,19 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport) { } void VisualServerViewport::draw_viewports() { - - //sort viewports - - //draw viewports + // get our arvr interface in case we need it + Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); + if (arvr_interface.is_valid()) { + // update our positioning information as late as possible... + arvr_interface->process(); + } clear_color = GLOBAL_GET("rendering/environment/default_clear_color"); + //sort viewports active_viewports.sort_custom<ViewportSort>(); + //draw viewports for (int i = 0; i < active_viewports.size(); i++) { Viewport *vp = active_viewports[i]; @@ -286,25 +295,47 @@ void VisualServerViewport::draw_viewports() { VSG::storage->render_target_clear_used(vp->render_target); - VSG::rasterizer->set_current_render_target(vp->render_target); + if (vp->use_arvr && arvr_interface.is_valid()) { + // override our size, make sure it matches our required size + Size2 size = arvr_interface->get_recommended_render_targetsize(); + VSG::storage->render_target_set_size(vp->render_target, size.x, size.y); - VSG::scene_render->set_debug_draw_mode(vp->debug_draw); - VSG::storage->render_info_begin_capture(); + // render mono or left eye first + ARVRInterface::Eyes leftOrMono = arvr_interface->is_stereo() ? ARVRInterface::EYE_LEFT : ARVRInterface::EYE_MONO; + VSG::rasterizer->set_current_render_target(vp->render_target); + _draw_viewport(vp, leftOrMono); + arvr_interface->commit_for_eye(leftOrMono, vp->render_target, vp->viewport_to_screen_rect); - _draw_viewport(vp); + // render right eye + if (leftOrMono == ARVRInterface::EYE_LEFT) { + // commit for eye may have changed the render target + VSG::rasterizer->set_current_render_target(vp->render_target); - VSG::storage->render_info_end_capture(); - vp->render_info[VS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] = VSG::storage->get_captured_render_info(VS::INFO_OBJECTS_IN_FRAME); - vp->render_info[VS::VIEWPORT_RENDER_INFO_VERTICES_IN_FRAME] = VSG::storage->get_captured_render_info(VS::INFO_VERTICES_IN_FRAME); - vp->render_info[VS::VIEWPORT_RENDER_INFO_MATERIAL_CHANGES_IN_FRAME] = VSG::storage->get_captured_render_info(VS::INFO_MATERIAL_CHANGES_IN_FRAME); - vp->render_info[VS::VIEWPORT_RENDER_INFO_SHADER_CHANGES_IN_FRAME] = VSG::storage->get_captured_render_info(VS::INFO_SHADER_CHANGES_IN_FRAME); - vp->render_info[VS::VIEWPORT_RENDER_INFO_SURFACE_CHANGES_IN_FRAME] = VSG::storage->get_captured_render_info(VS::INFO_SURFACE_CHANGES_IN_FRAME); - vp->render_info[VS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME] = VSG::storage->get_captured_render_info(VS::INFO_DRAW_CALLS_IN_FRAME); - - if (vp->viewport_to_screen_rect != Rect2()) { - //copy to screen if set as such - VSG::rasterizer->set_current_render_target(RID()); - VSG::rasterizer->blit_render_target_to_screen(vp->render_target, vp->viewport_to_screen_rect, vp->viewport_to_screen); + _draw_viewport(vp, ARVRInterface::EYE_RIGHT); + arvr_interface->commit_for_eye(ARVRInterface::EYE_RIGHT, vp->render_target, vp->viewport_to_screen_rect); + } + } else { + VSG::rasterizer->set_current_render_target(vp->render_target); + + VSG::scene_render->set_debug_draw_mode(vp->debug_draw); + VSG::storage->render_info_begin_capture(); + + // render standard mono camera + _draw_viewport(vp); + + VSG::storage->render_info_end_capture(); + vp->render_info[VS::VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME] = VSG::storage->get_captured_render_info(VS::INFO_OBJECTS_IN_FRAME); + vp->render_info[VS::VIEWPORT_RENDER_INFO_VERTICES_IN_FRAME] = VSG::storage->get_captured_render_info(VS::INFO_VERTICES_IN_FRAME); + vp->render_info[VS::VIEWPORT_RENDER_INFO_MATERIAL_CHANGES_IN_FRAME] = VSG::storage->get_captured_render_info(VS::INFO_MATERIAL_CHANGES_IN_FRAME); + vp->render_info[VS::VIEWPORT_RENDER_INFO_SHADER_CHANGES_IN_FRAME] = VSG::storage->get_captured_render_info(VS::INFO_SHADER_CHANGES_IN_FRAME); + vp->render_info[VS::VIEWPORT_RENDER_INFO_SURFACE_CHANGES_IN_FRAME] = VSG::storage->get_captured_render_info(VS::INFO_SURFACE_CHANGES_IN_FRAME); + vp->render_info[VS::VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME] = VSG::storage->get_captured_render_info(VS::INFO_DRAW_CALLS_IN_FRAME); + + if (vp->viewport_to_screen_rect != Rect2()) { + //copy to screen if set as such + VSG::rasterizer->set_current_render_target(RID()); + VSG::rasterizer->blit_render_target_to_screen(vp->render_target, vp->viewport_to_screen_rect, vp->viewport_to_screen); + } } if (vp->update_mode == VS::VIEWPORT_UPDATE_ONCE) { @@ -329,6 +360,13 @@ RID VisualServerViewport::viewport_create() { return rid; } +void VisualServerViewport::viewport_set_use_arvr(RID p_viewport, bool p_use_arvr) { + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + viewport->use_arvr = p_use_arvr; +} + void VisualServerViewport::viewport_set_size(RID p_viewport, int p_width, int p_height) { ERR_FAIL_COND(p_width < 0 && p_height < 0); diff --git a/servers/visual/visual_server_viewport.h b/servers/visual/visual_server_viewport.h index f963ce4aa3..93227d1c31 100644 --- a/servers/visual/visual_server_viewport.h +++ b/servers/visual/visual_server_viewport.h @@ -32,6 +32,7 @@ #include "rasterizer.h" #include "self_list.h" +#include "servers/arvr/arvr_interface.h" #include "servers/visual_server.h" class VisualServerViewport { @@ -44,6 +45,8 @@ public: RID self; RID parent; + bool use_arvr; /* use arvr interface to override camera positioning and projection matrices and control output */ + Size2i size; RID camera; RID scenario; @@ -107,6 +110,7 @@ public: for (int i = 0; i < VS::VIEWPORT_RENDER_INFO_MAX; i++) { render_info[i] = 0; } + use_arvr = false; } }; @@ -131,11 +135,13 @@ public: private: Color clear_color; - void _draw_viewport(Viewport *p_viewport); + void _draw_viewport(Viewport *p_viewport, ARVRInterface::Eyes p_eye = ARVRInterface::EYE_MONO); public: RID viewport_create(); + void viewport_set_use_arvr(RID p_viewport, bool p_use_arvr); + void viewport_set_size(RID p_viewport, int p_width, int p_height); void viewport_attach_to_screen(RID p_viewport, const Rect2 &p_rect = Rect2(), int p_screen = 0); diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index 4567d87706..e6ce3f6a54 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -344,6 +344,8 @@ public: FUNC0R(RID, viewport_create) + FUNC2(viewport_set_use_arvr, RID, bool) + FUNC3(viewport_set_size, RID, int, int) FUNC2(viewport_set_active, RID, bool) diff --git a/servers/visual_server.h b/servers/visual_server.h index f515a7b990..5c9f4202f9 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -532,6 +532,7 @@ public: virtual RID viewport_create() = 0; + virtual void viewport_set_use_arvr(RID p_viewport, bool p_use_arvr) = 0; virtual void viewport_set_size(RID p_viewport, int p_width, int p_height) = 0; virtual void viewport_set_active(RID p_viewport, bool p_active) = 0; virtual void viewport_set_parent_viewport(RID p_viewport, RID p_parent_viewport) = 0; 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 */ |