diff options
170 files changed, 6913 insertions, 1075 deletions
diff --git a/core/class_db.cpp b/core/class_db.cpp index 92aa131e2d..08c1ade679 100644 --- a/core/class_db.cpp +++ b/core/class_db.cpp @@ -254,11 +254,13 @@ HashMap<StringName, StringName, StringNameHasher> ClassDB::compat_classes; ClassDB::ClassInfo::ClassInfo() { + api = API_NONE; creation_func = NULL; inherits_ptr = NULL; disabled = false; exposed = false; } + ClassDB::ClassInfo::~ClassInfo() { } diff --git a/core/image.cpp b/core/image.cpp index 2ac8ffea56..58f49d69e6 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -366,6 +366,8 @@ int Image::get_mipmap_count() const { template <uint32_t read_bytes, bool read_alpha, uint32_t write_bytes, bool write_alpha, bool read_gray, bool write_gray> static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p_dst) { + uint32_t max_bytes = MAX(read_bytes, write_bytes); + for (int y = 0; y < p_height; y++) { for (int x = 0; x < p_width; x++) { @@ -379,7 +381,8 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p rgba[1] = rofs[0]; rgba[2] = rofs[0]; } else { - for (uint32_t i = 0; i < MAX(read_bytes, write_bytes); i++) { + + for (uint32_t i = 0; i < max_bytes; i++) { rgba[i] = (i < read_bytes) ? rofs[i] : 0; } @@ -937,7 +940,7 @@ bool Image::_can_modify(Format p_format) const { return p_format <= FORMAT_RGBE9995; } -template <int CC> +template <int CC, bool renormalize> static void _generate_po2_mipmap(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_width, uint32_t p_height) { //fast power of 2 mipmap generation @@ -963,6 +966,19 @@ static void _generate_po2_mipmap(const uint8_t *p_src, uint8_t *p_dst, uint32_t dst_ptr[j] = val >> 2; } + if (renormalize) { + Vector3 n(dst_ptr[0] / 255.0, dst_ptr[1] / 255.0, dst_ptr[2] / 255.0); + n *= 2.0; + n -= Vector3(1, 1, 1); + n.normalize(); + n += Vector3(1, 1, 1); + n *= 0.5; + n *= 255; + dst_ptr[0] = CLAMP(int(n.x), 0, 255); + dst_ptr[1] = CLAMP(int(n.y), 0, 255); + dst_ptr[2] = CLAMP(int(n.z), 0, 255); + } + dst_ptr += CC; rup_ptr += CC * 2; rdown_ptr += CC * 2; @@ -1045,11 +1061,11 @@ void Image::shrink_x2() { switch (format) { case FORMAT_L8: - case FORMAT_R8: _generate_po2_mipmap<1>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_LA8: _generate_po2_mipmap<2>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_RG8: _generate_po2_mipmap<2>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_RGB8: _generate_po2_mipmap<3>(r.ptr(), w.ptr(), width, height); break; - case FORMAT_RGBA8: _generate_po2_mipmap<4>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_R8: _generate_po2_mipmap<1, false>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_LA8: _generate_po2_mipmap<2, false>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_RG8: _generate_po2_mipmap<2, false>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_RGB8: _generate_po2_mipmap<3, false>(r.ptr(), w.ptr(), width, height); break; + case FORMAT_RGBA8: _generate_po2_mipmap<4, false>(r.ptr(), w.ptr(), width, height); break; default: {} } } @@ -1060,7 +1076,7 @@ void Image::shrink_x2() { } } -Error Image::generate_mipmaps() { +Error Image::generate_mipmaps(bool p_renormalize) { if (!_can_modify(format)) { ERR_EXPLAIN("Cannot generate mipmaps in indexed, compressed or custom image formats."); @@ -1089,11 +1105,22 @@ Error Image::generate_mipmaps() { switch (format) { case FORMAT_L8: - case FORMAT_R8: _generate_po2_mipmap<1>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; + case FORMAT_R8: _generate_po2_mipmap<1, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; case FORMAT_LA8: - case FORMAT_RG8: _generate_po2_mipmap<2>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; - case FORMAT_RGB8: _generate_po2_mipmap<3>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; - case FORMAT_RGBA8: _generate_po2_mipmap<4>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; + case FORMAT_RG8: _generate_po2_mipmap<2, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break; + case FORMAT_RGB8: + if (p_renormalize) + _generate_po2_mipmap<3, true>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + else + _generate_po2_mipmap<3, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + + break; + case FORMAT_RGBA8: + if (p_renormalize) + _generate_po2_mipmap<4, true>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + else + _generate_po2_mipmap<4, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); + break; default: {} } @@ -2217,7 +2244,7 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("crop", "width", "height"), &Image::crop); ClassDB::bind_method(D_METHOD("flip_x"), &Image::flip_x); ClassDB::bind_method(D_METHOD("flip_y"), &Image::flip_y); - ClassDB::bind_method(D_METHOD("generate_mipmaps"), &Image::generate_mipmaps); + ClassDB::bind_method(D_METHOD("generate_mipmaps", "renormalize"), &Image::generate_mipmaps, DEFVAL(false)); ClassDB::bind_method(D_METHOD("clear_mipmaps"), &Image::clear_mipmaps); ClassDB::bind_method(D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::_create_empty); diff --git a/core/image.h b/core/image.h index 17477d88ea..3c43e49950 100644 --- a/core/image.h +++ b/core/image.h @@ -217,7 +217,7 @@ public: /** * Generate a mipmap to an image (creates an image 1/4 the size, with averaging of 4->1) */ - Error generate_mipmaps(); + Error generate_mipmaps(bool p_renormalize = false); void clear_mipmaps(); diff --git a/core/input_map.cpp b/core/input_map.cpp index 973edcb5b5..67c0e4eb04 100644 --- a/core/input_map.cpp +++ b/core/input_map.cpp @@ -98,7 +98,7 @@ List<StringName> InputMap::get_actions() const { return actions; } -List<Ref<InputEvent> >::Element *InputMap::_find_event(Action p_action, const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength) const { +List<Ref<InputEvent> >::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength) const { for (List<Ref<InputEvent> >::Element *E = p_action.inputs.front(); E; E = E->next()) { diff --git a/core/input_map.h b/core/input_map.h index a00be8c859..f497a2b86e 100644 --- a/core/input_map.h +++ b/core/input_map.h @@ -55,7 +55,7 @@ private: mutable Map<StringName, Action> input_map; - List<Ref<InputEvent> >::Element *_find_event(Action p_action, const Ref<InputEvent> &p_event, bool *p_pressed = NULL, float *p_strength = NULL) const; + List<Ref<InputEvent> >::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed = NULL, float *p_strength = NULL) const; Array _get_action_list(const StringName &p_action); Array _get_actions(); diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index 999c9a8ca2..8ebd9d6cd9 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -37,7 +37,7 @@ bool ImageFormatLoader::recognize(const String &p_extension) const { get_recognized_extensions(&extensions); for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - if (E->get().nocasecmp_to(p_extension.get_extension()) == 0) + if (E->get().nocasecmp_to(p_extension) == 0) return true; } diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp index 596060221e..b6377662de 100644 --- a/core/io/pck_packer.cpp +++ b/core/io/pck_packer.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "pck_packer.h" - #include "core/os/file_access.h" +#include "version.h" static uint64_t _align(uint64_t p_n, int p_alignment) { @@ -70,9 +70,9 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment) { alignment = p_alignment; file->store_32(0x43504447); // MAGIC - file->store_32(0); // # version - file->store_32(0); // # major - file->store_32(0); // # minor + file->store_32(1); // # version + file->store_32(VERSION_MAJOR); // # major + file->store_32(VERSION_MINOR); // # minor file->store_32(0); // # revision for (int i = 0; i < 16; i++) { diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 609dd7e93c..3dcd94880a 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -56,7 +56,7 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - if (E->get().nocasecmp_to(extension.get_extension()) == 0) + if (E->get().nocasecmp_to(extension) == 0) recognized = true; } diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index 102e454e02..fc90417413 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -74,7 +74,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me int longest_axis = aabb.get_longest_axis_index(); //first two vertices are the most distant - int simplex[4]; + int simplex[4] = { 0 }; { real_t max = 0, min = 0; diff --git a/core/node_path.cpp b/core/node_path.cpp index cd7ad77534..64983fc091 100644 --- a/core/node_path.cpp +++ b/core/node_path.cpp @@ -264,8 +264,9 @@ NodePath NodePath::get_as_property_path() const { Vector<StringName> new_path = data->subpath; String initial_subname = data->path[0]; + for (size_t i = 1; i < data->path.size(); i++) { - initial_subname += i == 0 ? data->path[i].operator String() : "/" + data->path[i]; + initial_subname += "/" + data->path[i]; } new_path.insert(0, initial_subname); diff --git a/core/os/input.h b/core/os/input.h index 027147b987..001871c5dc 100644 --- a/core/os/input.h +++ b/core/os/input.h @@ -118,7 +118,8 @@ public: void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; - virtual bool is_emulating_touchscreen() const = 0; + virtual bool is_emulating_touch_from_mouse() const = 0; + virtual bool is_emulating_mouse_from_touch() const = 0; virtual CursorShape get_default_cursor_shape() = 0; virtual void set_default_cursor_shape(CursorShape p_shape) = 0; diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index 5003be77ab..4ebb821a2f 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -656,12 +656,14 @@ bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool * if (jm.is_null()) return false; - bool match = (axis == jm->axis && (((axis_value < 0) == (jm->axis_value < 0)) || jm->axis_value == 0)); + bool match = (axis == jm->axis); // Matches even if not in the same direction, but returns a "not pressed" event. if (match) { + bool same_direction = (((axis_value < 0) == (jm->axis_value < 0)) || jm->axis_value == 0); + bool pressed = same_direction ? Math::abs(jm->get_axis_value()) >= p_deadzone : false; if (p_pressed != NULL) - *p_pressed = Math::abs(jm->get_axis_value()) >= p_deadzone; + *p_pressed = pressed; if (p_strength != NULL) - *p_strength = (*p_pressed) ? Math::inverse_lerp(p_deadzone, 1.0f, Math::abs(jm->get_axis_value())) : 0.0f; + *p_strength = pressed ? CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, Math::abs(jm->get_axis_value())), 0.0f, 1.0f) : 0.0f; } return match; } diff --git a/core/os/os.cpp b/core/os/os.cpp index 618a4bbac3..854d554b10 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -411,7 +411,7 @@ Error OS::set_cwd(const String &p_cwd) { bool OS::has_touchscreen_ui_hint() const { //return false; - return Input::get_singleton() && Input::get_singleton()->is_emulating_touchscreen(); + return Input::get_singleton() && Input::get_singleton()->is_emulating_touch_from_mouse(); } int OS::get_free_static_memory() const { diff --git a/core/project_settings.cpp b/core/project_settings.cpp index d3a62263ac..ac4a4b7d15 100644 --- a/core/project_settings.cpp +++ b/core/project_settings.cpp @@ -816,12 +816,11 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default) { Variant ret; - if (ProjectSettings::get_singleton()->has_setting(p_var)) { - ret = ProjectSettings::get_singleton()->get(p_var); - } else { + if (!ProjectSettings::get_singleton()->has_setting(p_var)) { ProjectSettings::get_singleton()->set(p_var, p_default); - ret = p_default; } + ret = ProjectSettings::get_singleton()->get(p_var); + ProjectSettings::get_singleton()->set_initial_value(p_var, p_default); ProjectSettings::get_singleton()->set_builtin_order(p_var); return ret; diff --git a/core/ustring.cpp b/core/ustring.cpp index b98e202175..921d20a6fd 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -3755,8 +3755,8 @@ String String::get_file() const { String String::get_extension() const { int pos = find_last("."); - if (pos < 0) - return *this; + if (pos < 0 || pos < MAX(find_last("/"), find_last("\\"))) + return ""; return substr(pos + 1, length()); } @@ -3834,7 +3834,7 @@ String String::percent_decode() const { String String::get_basename() const { int pos = find_last("."); - if (pos < 0) + if (pos < 0 || pos < MAX(find_last("/"), find_last("\\"))) return *this; return substr(0, pos); diff --git a/core/variant.cpp b/core/variant.cpp index 5d48c8785e..a6df95e310 100644 --- a/core/variant.cpp +++ b/core/variant.cpp @@ -3167,7 +3167,11 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method, if (ce.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) { int errorarg = ce.argument; - err_text = "Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(p_argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(ce.expected) + "."; + if (p_argptrs) { + err_text = "Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(p_argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(ce.expected) + "."; + } else { + err_text = "Cannot convert argument " + itos(errorarg + 1) + " from [missing argptr, type unknown] to " + Variant::get_type_name(ce.expected) + "."; + } } else if (ce.error == Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) { err_text = "Method expected " + itos(ce.argument) + " arguments, but called with " + itos(p_argcount) + "."; } else if (ce.error == Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) { diff --git a/core/variant_call.cpp b/core/variant_call.cpp index d05c7b174c..1b938e3f41 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -153,9 +153,9 @@ struct _VariantCall { funcdata.func = p_func; funcdata.default_args = p_defaultarg; funcdata._const = p_const; + funcdata.returns = p_has_return; #ifdef DEBUG_ENABLED funcdata.return_type = p_return; - funcdata.returns = p_has_return; #endif if (p_argtype1.name) { diff --git a/core/variant_op.cpp b/core/variant_op.cpp index 4e37593915..621af2dfb7 100644 --- a/core/variant_op.cpp +++ b/core/variant_op.cpp @@ -1459,13 +1459,13 @@ void Variant::set_named(const StringName &p_index, const Variant &p_value, bool v->a = p_value._data._int / 255.0; valid = true; } else if (p_index == CoreStringNames::singleton->h) { - v->set_hsv(p_value._data._int, v->get_s(), v->get_v()); + v->set_hsv(p_value._data._int, v->get_s(), v->get_v(), v->a); valid = true; } else if (p_index == CoreStringNames::singleton->s) { - v->set_hsv(v->get_h(), p_value._data._int, v->get_v()); + v->set_hsv(v->get_h(), p_value._data._int, v->get_v(), v->a); valid = true; } else if (p_index == CoreStringNames::singleton->v) { - v->set_hsv(v->get_h(), v->get_v(), p_value._data._int); + v->set_hsv(v->get_h(), v->get_v(), p_value._data._int, v->a); valid = true; } } else if (p_value.type == Variant::REAL) { @@ -1495,13 +1495,13 @@ void Variant::set_named(const StringName &p_index, const Variant &p_value, bool v->a = p_value._data._real / 255.0; valid = true; } else if (p_index == CoreStringNames::singleton->h) { - v->set_hsv(p_value._data._real, v->get_s(), v->get_v()); + v->set_hsv(p_value._data._real, v->get_s(), v->get_v(), v->a); valid = true; } else if (p_index == CoreStringNames::singleton->s) { - v->set_hsv(v->get_h(), p_value._data._real, v->get_v()); + v->set_hsv(v->get_h(), p_value._data._real, v->get_v(), v->a); valid = true; } else if (p_index == CoreStringNames::singleton->v) { - v->set_hsv(v->get_h(), v->get_s(), p_value._data._real); + v->set_hsv(v->get_h(), v->get_s(), p_value._data._real, v->a); valid = true; } } @@ -2117,15 +2117,15 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) return; } else if (*str == "h") { valid = true; - v->set_hsv(p_value, v->get_s(), v->get_v()); + v->set_hsv(p_value, v->get_s(), v->get_v(), v->a); return; } else if (*str == "s") { valid = true; - v->set_hsv(v->get_h(), p_value, v->get_v()); + v->set_hsv(v->get_h(), p_value, v->get_v(), v->a); return; } else if (*str == "v") { valid = true; - v->set_hsv(v->get_h(), v->get_s(), p_value); + v->set_hsv(v->get_h(), v->get_s(), p_value, v->a); return; } else if (*str == "r8") { valid = true; diff --git a/doc/classes/CapsuleShape2D.xml b/doc/classes/CapsuleShape2D.xml index 488c1ddc4f..f05b194601 100644 --- a/doc/classes/CapsuleShape2D.xml +++ b/doc/classes/CapsuleShape2D.xml @@ -17,7 +17,7 @@ The capsule's height. </member> <member name="radius" type="float" setter="set_radius" getter="get_radius"> - The capsules's radius. + The capsule's radius. </member> </members> <constants> diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index b6adf84abd..c31438283e 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -110,7 +110,10 @@ Text shown when the [LineEdit] is empty. It is [b]not[/b] the [LineEdit]'s default value (see [member text]). </member> <member name="secret" type="bool" setter="set_secret" getter="is_secret"> - If [code]true[/code] every character is shown as "*". + If [code]true[/code], every character is replaced with the secret character (see [member secret_character]). + </member> + <member name="secret_character" type="string" setter="set_secret_character" getter="get_secret_character"> + The character to use to mask secret input (defaults to "*"). Only a single character can be used as the secret character. </member> <member name="text" type="String" setter="set_text" getter="get_text"> String value of the [LineEdit]. diff --git a/doc/classes/Spatial.xml b/doc/classes/Spatial.xml index 822a699984..9ef60109de 100644 --- a/doc/classes/Spatial.xml +++ b/doc/classes/Spatial.xml @@ -99,7 +99,9 @@ <argument index="1" name="up" type="Vector3"> </argument> <description> - Rotates itself to point into direction of target position. Operations take place in global space. + Rotates itself so that the local -Z axis points towards the [code]target[/code] position. + The transform will first be rotated around the given [code]up[/code] vector, and then fully aligned to the target by a further rotation around an axis perpendicular to both the [code]target[/code] and [code]up[/code] vectors. + Operations take place in global space. </description> </method> <method name="look_at_from_position"> @@ -112,7 +114,7 @@ <argument index="2" name="up" type="Vector3"> </argument> <description> - Moves the node to specified position and then rotates itself to point into direction of target position. Operations take place in global space. + Moves the node to the specified [code]position[/code], and then rotates itself to point toward the [code]target[/code] as per [method look_at]. Operations take place in global space. </description> </method> <method name="orthonormalize"> diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml index d9f9d8cc73..4567f1681c 100644 --- a/doc/classes/Transform.xml +++ b/doc/classes/Transform.xml @@ -99,7 +99,9 @@ <argument index="1" name="up" type="Vector3"> </argument> <description> - Rotate the transform around the up vector to face the target. + Returns a copy of the transform rotated such that its -Z axis points towards the [code]target[/code] position. + The transform will first be rotated around the given [code]up[/code] vector, and then fully aligned to the target by a further rotation around an axis perpendicular to both the [code]target[/code] and [code]up[/code] vectors. + Operations take place in global space. </description> </method> <method name="orthonormalized"> diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index ca39d9f966..0fb69494f4 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -111,8 +111,6 @@ static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GL strcpy(debType, "Portability"); else if (type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB) strcpy(debType, "Performance"); - else if (type == _EXT_DEBUG_TYPE_OTHER_ARB) - strcpy(debType, "Other"); if (severity == _EXT_DEBUG_SEVERITY_HIGH_ARB) strcpy(debSev, "High"); diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp index 733c7cc80c..0f47949b4b 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp +++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp @@ -340,13 +340,22 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) { bytes = byte_size; } - int ret = pa_stream_write(ad->pa_str, ptr, bytes, NULL, 0LL, PA_SEEK_RELATIVE); + ret = pa_stream_write(ad->pa_str, ptr, bytes, NULL, 0LL, PA_SEEK_RELATIVE); if (ret >= 0) { byte_size -= bytes; ptr = (const char *)ptr + bytes; } } else { - pa_mainloop_iterate(ad->pa_ml, 1, NULL); + ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL); + if (ret == 0) { + // If pa_mainloop_iterate returns 0 sleep for 1 msec to wait + // for the stream to be able to process more bytes + ad->unlock(); + + OS::get_singleton()->delay_usec(1000); + + ad->lock(); + } } } } diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index a7fb0c3887..949609bb9a 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -101,7 +101,7 @@ IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) { hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_ADDRCONFIG; }; - hints.ai_flags &= !AI_NUMERICHOST; + hints.ai_flags &= ~AI_NUMERICHOST; int s = getaddrinfo(p_hostname.utf8().get_data(), NULL, &hints, &result); if (s != 0) { @@ -111,6 +111,8 @@ IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) { if (result == NULL || result->ai_addr == NULL) { ERR_PRINT("Invalid response from getaddrinfo"); + if (result) + freeaddrinfo(result); return IP_Address(); }; diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 31c8e4ade9..eeb3b31fc2 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -349,6 +349,12 @@ Error OS_Unix::open_dynamic_library(const String p_path, void *&p_library_handle String path = p_path; + if (FileAccess::exists(path) && path.is_rel_path()) { + // dlopen expects a slash, in this case a leading ./ for it to be interpreted as a relative path, + // otherwise it will end up searching various system directories for the lib instead and finally failing. + path = "./" + path; + } + if (!FileAccess::exists(path)) { //this code exists so gdnative can load .so files from within the executable path path = get_executable_path().get_base_dir().plus_file(p_path.get_file()); diff --git a/drivers/unix/stream_peer_tcp_posix.cpp b/drivers/unix/stream_peer_tcp_posix.cpp index 17112e5ab5..6d798f32f9 100644 --- a/drivers/unix/stream_peer_tcp_posix.cpp +++ b/drivers/unix/stream_peer_tcp_posix.cpp @@ -124,11 +124,14 @@ void StreamPeerTCPPosix::set_socket(int p_sockfd, IP_Address p_host, int p_port, sock_type = p_sock_type; sockfd = p_sockfd; #ifndef NO_FCNTL - fcntl(sockfd, F_SETFL, O_NONBLOCK); + if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #else int bval = 1; - ioctl(sockfd, FIONBIO, &bval); - + if (ioctl(sockfd, FIONBIO, &bval) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #endif status = STATUS_CONNECTING; @@ -150,10 +153,14 @@ Error StreamPeerTCPPosix::connect_to_host(const IP_Address &p_host, uint16_t p_p }; #ifndef NO_FCNTL - fcntl(sockfd, F_SETFL, O_NONBLOCK); + if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #else int bval = 1; - ioctl(sockfd, FIONBIO, &bval); + if (ioctl(sockfd, FIONBIO, &bval) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #endif struct sockaddr_storage their_addr; @@ -308,7 +315,9 @@ void StreamPeerTCPPosix::set_no_delay(bool p_enabled) { ERR_FAIL_COND(!is_connected_to_host()); int flag = p_enabled ? 1 : 0; - setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)); + if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) < 0) { + ERR_PRINT("Unable to set TCP no delay option"); + } } bool StreamPeerTCPPosix::is_connected_to_host() const { diff --git a/drivers/unix/tcp_server_posix.cpp b/drivers/unix/tcp_server_posix.cpp index 07ffe3b00a..67ab981f46 100644 --- a/drivers/unix/tcp_server_posix.cpp +++ b/drivers/unix/tcp_server_posix.cpp @@ -91,10 +91,14 @@ Error TCPServerPosix::listen(uint16_t p_port, const IP_Address &p_bind_address) ERR_FAIL_COND_V(sockfd == -1, FAILED); #ifndef NO_FCNTL - fcntl(sockfd, F_SETFL, O_NONBLOCK); + if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #else int bval = 1; - ioctl(sockfd, FIONBIO, &bval); + if (ioctl(sockfd, FIONBIO, &bval) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #endif int reuse = 1; @@ -113,6 +117,7 @@ Error TCPServerPosix::listen(uint16_t p_port, const IP_Address &p_bind_address) ERR_FAIL_V(FAILED); }; } else { + close(sockfd); return ERR_ALREADY_IN_USE; }; @@ -157,10 +162,14 @@ Ref<StreamPeerTCP> TCPServerPosix::take_connection() { int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &size); ERR_FAIL_COND_V(fd == -1, Ref<StreamPeerTCP>()); #ifndef NO_FCNTL - fcntl(fd, F_SETFL, O_NONBLOCK); + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #else int bval = 1; - ioctl(fd, FIONBIO, &bval); + if (ioctl(fd, FIONBIO, &bval) < 0) { + WARN_PRINT("Error setting socket as non blocking"); + } #endif Ref<StreamPeerTCPPosix> conn = memnew(StreamPeerTCPPosix); diff --git a/drivers/windows/stream_peer_tcp_winsock.cpp b/drivers/windows/stream_peer_tcp_winsock.cpp index 55775fc231..cb501ce35d 100644 --- a/drivers/windows/stream_peer_tcp_winsock.cpp +++ b/drivers/windows/stream_peer_tcp_winsock.cpp @@ -335,7 +335,9 @@ Error StreamPeerTCPWinsock::connect_to_host(const IP_Address &p_host, uint16_t p void StreamPeerTCPWinsock::set_no_delay(bool p_enabled) { ERR_FAIL_COND(!is_connected_to_host()); int flag = p_enabled ? 1 : 0; - setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)); + if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) != 0) { + ERR_PRINT("Unable to set TCP no delay option"); + } } int StreamPeerTCPWinsock::get_available_bytes() const { diff --git a/drivers/windows/tcp_server_winsock.cpp b/drivers/windows/tcp_server_winsock.cpp index 413a0d19b5..ddb955549f 100644 --- a/drivers/windows/tcp_server_winsock.cpp +++ b/drivers/windows/tcp_server_winsock.cpp @@ -105,6 +105,7 @@ Error TCPServerWinsock::listen(uint16_t p_port, const IP_Address &p_bind_address ERR_FAIL_V(FAILED); }; } else { + closesocket(sockfd); return ERR_ALREADY_IN_USE; }; diff --git a/editor/animation_editor.cpp b/editor/animation_editor.cpp index 51e08d63c6..f7c8cac93f 100644 --- a/editor/animation_editor.cpp +++ b/editor/animation_editor.cpp @@ -1076,6 +1076,9 @@ void AnimationKeyEditor::_track_editor_draw() { if (!animation.is_valid()) { v_scroll->hide(); h_scroll->hide(); + length->set_editable(false); + step->set_editable(false); + loop->set_disabled(true); menu_add_track->set_disabled(true); menu_track->set_disabled(true); edit_button->set_disabled(true); @@ -1087,6 +1090,9 @@ void AnimationKeyEditor::_track_editor_draw() { return; } + length->set_editable(true); + step->set_editable(true); + loop->set_disabled(false); menu_add_track->set_disabled(false); menu_track->set_disabled(false); edit_button->set_disabled(false); @@ -3856,6 +3862,7 @@ AnimationKeyEditor::AnimationKeyEditor() { length->set_h_size_flags(SIZE_EXPAND_FILL); length->set_stretch_ratio(1); length->set_tooltip(TTR("Animation length (in seconds).")); + length->set_editable(false); hb->add_child(length); length->connect("value_changed", this, "_animation_len_changed"); @@ -3872,6 +3879,7 @@ AnimationKeyEditor::AnimationKeyEditor() { step->set_h_size_flags(SIZE_EXPAND_FILL); step->set_stretch_ratio(1); step->set_tooltip(TTR("Cursor step snap (in seconds).")); + step->set_editable(false); hb->add_child(step); step->connect("value_changed", this, "_step_changed"); @@ -3881,6 +3889,7 @@ AnimationKeyEditor::AnimationKeyEditor() { loop->connect("pressed", this, "_animation_loop_changed"); hb->add_child(loop); loop->set_tooltip(TTR("Enable/Disable looping in animation.")); + loop->set_disabled(true); hb->add_child(memnew(VSeparator)); @@ -3918,7 +3927,7 @@ AnimationKeyEditor::AnimationKeyEditor() { menu_track = memnew(MenuButton); hb->add_child(menu_track); menu_track->get_popup()->connect("id_pressed", this, "_menu_track"); - menu_track->set_tooltip(TTR("Track tools")); + menu_track->set_tooltip(TTR("Track Tools")); edit_button = memnew(ToolButton); edit_button->set_toggle_mode(true); diff --git a/editor/collada/collada.cpp b/editor/collada/collada.cpp index 4ce57d7f63..734229d014 100644 --- a/editor/collada/collada.cpp +++ b/editor/collada/collada.cpp @@ -2266,10 +2266,8 @@ void Collada::_merge_skeletons2(VisualScene *p_vscene) { } node = node->parent; } - ERR_CONTINUE(!sk); - if (!sk) - continue; //bleh + ERR_CONTINUE(!sk); if (!skeleton) { skeleton = sk; diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 19fd297f69..953d787322 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -472,17 +472,18 @@ void DependencyRemoveDialog::_build_removed_dependency_tree(const Vector<Removed void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector<String> &p_files) { all_remove_files.clear(); - to_delete.clear(); + dirs_to_delete.clear(); + files_to_delete.clear(); owners->clear(); for (int i = 0; i < p_folders.size(); ++i) { String folder = p_folders[i].ends_with("/") ? p_folders[i] : (p_folders[i] + "/"); _find_files_in_removed_folder(EditorFileSystem::get_singleton()->get_filesystem_path(folder), folder); - to_delete.push_back(folder); + dirs_to_delete.push_back(folder); } for (int i = 0; i < p_files.size(); ++i) { all_remove_files[p_files[i]] = String(); - to_delete.push_back(p_files[i]); + files_to_delete.push_back(p_files[i]); } Vector<RemovedDependency> removed_deps; @@ -502,29 +503,49 @@ void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector< } void DependencyRemoveDialog::ok_pressed() { - bool files_only = true; - for (int i = 0; i < to_delete.size(); ++i) { - if (to_delete[i].ends_with("/")) { - files_only = false; - } else if (ResourceCache::has(to_delete[i])) { - Resource *res = ResourceCache::get(to_delete[i]); - res->set_path(""); //clear reference to path + + if (dirs_to_delete.size() == 0) { + //If we only deleted files we should only need to tell the file system about the files we touched. + for (int i = 0; i < files_to_delete.size(); ++i) + EditorFileSystem::get_singleton()->update_file(files_to_delete[i]); + } else { + + for (int i = 0; i < files_to_delete.size(); ++i) { + if (ResourceCache::has(files_to_delete[i])) { + Resource *res = ResourceCache::get(files_to_delete[i]); + res->set_path(""); + } + String path = OS::get_singleton()->get_resource_dir() + files_to_delete[i].replace_first("res://", "/"); + print_line("Moving to trash: " + path); + Error err = OS::get_singleton()->move_to_trash(path); + if (err != OK) { + EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + files_to_delete[i] + "\n"); + } } - String path = OS::get_singleton()->get_resource_dir() + to_delete[i].replace_first("res://", "/"); - print_line("Moving to trash: " + path); - Error err = OS::get_singleton()->move_to_trash(path); - if (err != OK) { - EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + to_delete[i] + "\n"); + for (int i = 0; i < dirs_to_delete.size(); ++i) { + String path = OS::get_singleton()->get_resource_dir() + dirs_to_delete[i].replace_first("res://", "/"); + print_line("Moving to trash: " + path); + Error err = OS::get_singleton()->move_to_trash(path); + if (err != OK) { + EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + dirs_to_delete[i] + "\n"); + } } - } - if (files_only) { - //If we only deleted files we should only need to tell the file system about the files we touched. - for (int i = 0; i < to_delete.size(); ++i) { - EditorFileSystem::get_singleton()->update_file(to_delete[i]); + // if some dirs would be deleted, favorite dirs need to be updated + Vector<String> previous_favorite_dirs = EditorSettings::get_singleton()->get_favorite_dirs(); + Vector<String> new_favorite_dirs; + + for (int i = 0; i < previous_favorite_dirs.size(); ++i) { + if (dirs_to_delete.find(previous_favorite_dirs[i] + "/") < 0) { + new_favorite_dirs.push_back(previous_favorite_dirs[i]); + } } - } else { + + if (new_favorite_dirs.size() < previous_favorite_dirs.size()) { + EditorSettings::get_singleton()->set_favorite_dirs(new_favorite_dirs); + } + EditorFileSystem::get_singleton()->scan_changes(); } } diff --git a/editor/dependency_editor.h b/editor/dependency_editor.h index 16135c352b..4f268de748 100644 --- a/editor/dependency_editor.h +++ b/editor/dependency_editor.h @@ -102,7 +102,8 @@ class DependencyRemoveDialog : public ConfirmationDialog { Tree *owners; Map<String, String> all_remove_files; - Vector<String> to_delete; + Vector<String> dirs_to_delete; + Vector<String> files_to_delete; struct RemovedDependency { String file; diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 660eaafe8e..37a35b6ebf 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -588,8 +588,6 @@ bool EditorData::check_and_update_scene(int p_idx) { bool must_reload = _find_updated_instances(edited_scene[p_idx].root, edited_scene[p_idx].root, checked_scenes); - print_line("MUST RELOAD? " + itos(must_reload)); - if (must_reload) { Ref<PackedScene> pscene; pscene.instance(); diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 7456cc902a..7739b08eff 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -911,6 +911,16 @@ Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, co return OK; } +Error EditorExportPlatform::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + return save_pack(p_preset, p_path); +} + +Error EditorExportPlatform::export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + return save_zip(p_preset, p_path); +} + void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags) { String host = EditorSettings::get_singleton()->get("network/debug/remote_host"); @@ -1262,6 +1272,7 @@ bool EditorExportPlatformPC::can_export(const Ref<EditorExportPreset> &p_preset, String err; bool valid = true; + bool use64 = p_preset->get("binary_format/64_bits"); if (use64 && (!exists_export_template(debug_file_64, &err) || !exists_export_template(release_file_64, &err))) { valid = false; diff --git a/editor/editor_export.h b/editor/editor_export.h index e851769279..1d0b89cf16 100644 --- a/editor/editor_export.h +++ b/editor/editor_export.h @@ -243,6 +243,8 @@ public: virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const = 0; virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) = 0; + virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); + virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual void get_platform_features(List<String> *r_features) = 0; EditorExportPlatform(); @@ -373,7 +375,6 @@ class EditorExportPlatformPC : public EditorExportPlatform { Set<String> extra_features; - bool use64; int chmod_flags; public: diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 4ae6e9a4f4..8a8a21543b 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -1027,6 +1027,7 @@ void EditorFileDialog::invalidate() { if (is_visible_in_tree()) { update_file_list(); + _update_favorites(); invalidated = false; } else { invalidated = true; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index d6e1a91245..d8c85df83d 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -469,26 +469,26 @@ void EditorNode::_fs_changed() { preset.unref(); } if (preset.is_null()) { - String err = "Unknown export preset: " + export_defer.preset; - ERR_PRINTS(err); + String errstr = "Unknown export preset: " + export_defer.preset; + ERR_PRINTS(errstr); } else { Ref<EditorExportPlatform> platform = preset->get_platform(); if (platform.is_null()) { - String err = "Preset \"" + export_defer.preset + "\" doesn't have a platform."; - ERR_PRINTS(err); + String errstr = "Preset \"" + export_defer.preset + "\" doesn't have a platform."; + ERR_PRINTS(errstr); } else { // ensures export_project does not loop infinitely, because notifications may // come during the export export_defer.preset = ""; - Error err; + Error err = OK; if (!preset->is_runnable() && (export_defer.path.ends_with(".pck") || export_defer.path.ends_with(".zip"))) { if (export_defer.path.ends_with(".zip")) { - err = platform->save_zip(preset, export_defer.path); + err = platform->export_zip(preset, export_defer.debug, export_defer.path); } else if (export_defer.path.ends_with(".pck")) { - err = platform->save_pack(preset, export_defer.path); + err = platform->export_pack(preset, export_defer.debug, export_defer.path); } } else { - err = platform->export_project(preset, export_defer.debug, export_defer.path, /*p_flags*/ 0); + err = platform->export_project(preset, export_defer.debug, export_defer.path); } if (err != OK) { ERR_PRINTS(vformat(TTR("Project export failed with error code %d."), (int)err)); @@ -4886,7 +4886,7 @@ EditorNode::EditorNode() { if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton()) { //only if no touchscreen ui hint, set emulation - id->set_emulate_touch(false); //just disable just in case + id->set_emulate_touch_from_mouse(false); //just disable just in case } id->set_custom_mouse_cursor(RES()); } @@ -5858,7 +5858,7 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(ParticlesEditorPlugin(this))); add_editor_plugin(memnew(ResourcePreloaderEditorPlugin(this))); add_editor_plugin(memnew(ItemListEditorPlugin(this))); - add_editor_plugin(memnew(CollisionPolygonEditorPlugin(this))); + add_editor_plugin(memnew(Polygon3DEditorPlugin(this))); add_editor_plugin(memnew(CollisionPolygon2DEditorPlugin(this))); add_editor_plugin(memnew(TileSetEditorPlugin(this))); add_editor_plugin(memnew(TileMapEditorPlugin(this))); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 85f6d99c67..15d3b28da8 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -371,7 +371,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/line_numbers/code_folding", true); _initial_set("text_editor/line_numbers/show_line_length_guideline", false); _initial_set("text_editor/line_numbers/line_length_guideline_column", 80); - hints["text_editor/line_numbers/line_length_guideline_column"] = PropertyInfo(Variant::INT, "text_editor/line_numbers/line_length_guideline_column", PROPERTY_HINT_RANGE, "20, 160, 10"); + hints["text_editor/line_numbers/line_length_guideline_column"] = PropertyInfo(Variant::INT, "text_editor/line_numbers/line_length_guideline_column", PROPERTY_HINT_RANGE, "20, 160, 1"); _initial_set("text_editor/open_scripts/smooth_scrolling", true); _initial_set("text_editor/open_scripts/v_scroll_speed", 80); @@ -385,7 +385,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/cursor/block_caret", false); _initial_set("text_editor/cursor/caret_blink", true); _initial_set("text_editor/cursor/caret_blink_speed", 0.65); - hints["text_editor/cursor/caret_blink_speed"] = PropertyInfo(Variant::REAL, "text_editor/cursor/caret_blink_speed", PROPERTY_HINT_RANGE, "0.1, 10, 0.1"); + hints["text_editor/cursor/caret_blink_speed"] = PropertyInfo(Variant::REAL, "text_editor/cursor/caret_blink_speed", PROPERTY_HINT_RANGE, "0.1, 10, 0.01"); _initial_set("text_editor/cursor/right_click_moves_caret", true); _initial_set("text_editor/completion/auto_brace_complete", false); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 9442bbc0e8..74ea46838b 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -154,9 +154,7 @@ void FindInFiles::_iterate() { PoolStringArray sub_dirs; _scan_dir(_root_prefix + _current_dir, sub_dirs); - if (sub_dirs.size() != 0) { - _folders_stack.push_back(sub_dirs); - } + _folders_stack.push_back(sub_dirs); } else { // Go back one level @@ -176,7 +174,7 @@ void FindInFiles::_iterate() { String fpath = _files_to_scan[_files_to_scan.size() - 1]; pop_back(_files_to_scan); - _scan_file(_root_prefix + fpath); + _scan_file(fpath); } else { print_line("Search complete"); @@ -202,8 +200,6 @@ void FindInFiles::_scan_dir(String path, PoolStringArray &out_folders) { return; } - //print_line(String("Scanning ") + path); - dir->list_dir_begin(); for (int i = 0; i < 1000; ++i) { @@ -222,7 +218,7 @@ void FindInFiles::_scan_dir(String path, PoolStringArray &out_folders) { else { String file_ext = file.get_extension(); if (_extension_filter.has(file_ext)) { - _files_to_scan.push_back(file); + _files_to_scan.push_back(path.plus_file(file)); } } } @@ -232,7 +228,6 @@ void FindInFiles::_scan_file(String fpath) { FileAccess *f = FileAccess::open(fpath, FileAccess::READ); if (f == NULL) { - f->close(); print_line(String("Cannot open file ") + fpath); return; } diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index bac18be4a9..8443311a54 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -29,12 +29,426 @@ /*************************************************************************/ #include "groups_editor.h" - +#include "editor/scene_tree_editor.h" #include "editor_node.h" #include "scene/gui/box_container.h" #include "scene/gui/label.h" #include "scene/resources/packed_scene.h" +void GroupDialog::ok_pressed() { +} + +void GroupDialog::_cancel_pressed() { +} + +void GroupDialog::_group_selected() { + nodes_to_add->clear(); + add_node_root = nodes_to_add->create_item(); + + nodes_to_remove->clear(); + remove_node_root = nodes_to_remove->create_item(); + + if (!groups->is_anything_selected()) { + return; + } + + selected_group = groups->get_selected()->get_text(0); + _load_nodes(scene_tree->get_edited_scene_root()); +} + +void GroupDialog::_load_nodes(Node *p_current) { + String item_name = p_current->get_name(); + if (p_current != scene_tree->get_edited_scene_root()) { + item_name = String(p_current->get_parent()->get_name()) + "/" + String(item_name); + } + + bool keep = true; + Node *root = scene_tree->get_edited_scene_root(); + Node *owner = p_current->get_owner(); + if (owner != root && p_current != root && !owner && !root->is_editable_instance(owner)) { + keep = false; + } + + TreeItem *node; + NodePath path = scene_tree->get_edited_scene_root()->get_path_to(p_current); + if (keep && p_current->is_in_group(selected_group)) { + if (remove_filter->get_text().is_subsequence_ofi(String(p_current->get_name()))) { + node = nodes_to_remove->create_item(remove_node_root); + keep = true; + } else { + keep = false; + } + } else if (keep && add_filter->get_text().is_subsequence_ofi(String(p_current->get_name()))) { + node = nodes_to_add->create_item(add_node_root); + keep = true; + } else { + keep = false; + } + + if (keep) { + node->set_text(0, item_name); + node->set_metadata(0, path); + node->set_tooltip(0, path); + + Ref<Texture> icon; + if (p_current->has_meta("_editor_icon")) { + icon = p_current->get_meta("_editor_icon"); + } else { + icon = get_icon((has_icon(p_current->get_class(), "EditorIcons") ? p_current->get_class() : String("Object")), "EditorIcons"); + } + node->set_icon(0, icon); + + if (!_can_edit(p_current, selected_group)) { + node->set_selectable(0, false); + node->set_custom_color(0, get_color("disabled_font_color", "Editor")); + } + } + + for (int i = 0; i < p_current->get_child_count(); i++) { + _load_nodes(p_current->get_child(i)); + } +} + +bool GroupDialog::_can_edit(Node *p_node, String p_group) { + Node *n = p_node; + bool can_edit = true; + while (n) { + Ref<SceneState> ss = (n == EditorNode::get_singleton()->get_edited_scene()) ? n->get_scene_inherited_state() : n->get_scene_instance_state(); + if (ss.is_valid()) { + int path = ss->find_node_by_path(n->get_path_to(p_node)); + if (path != -1) { + if (ss->is_node_in_group(path, p_group)) { + can_edit = false; + } + } + } + n = n->get_owner(); + } + return can_edit; +} + +void GroupDialog::_add_pressed() { + TreeItem *selected = nodes_to_add->get_selected(); + + if (!selected) { + return; + } + + while (selected) { + Node *node = scene_tree->get_edited_scene_root()->get_node(selected->get_metadata(0)); + node->add_to_group(selected_group, true); + + selected = nodes_to_add->get_next_selected(selected); + } + + _group_selected(); + EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->update_tree(); +} + +void GroupDialog::_removed_pressed() { + TreeItem *selected = nodes_to_remove->get_selected(); + + if (!selected) { + return; + } + + while (selected) { + Node *node = scene_tree->get_edited_scene_root()->get_node(selected->get_metadata(0)); + node->remove_from_group(selected_group); + + selected = nodes_to_add->get_next_selected(selected); + } + + _group_selected(); + EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->update_tree(); +} + +void GroupDialog::_remove_filter_changed(const String &p_filter) { + _group_selected(); +} + +void GroupDialog::_add_filter_changed(const String &p_filter) { + _group_selected(); +} + +void GroupDialog::_add_group_pressed() { + _add_group(add_group_text->get_text()); + add_group_text->clear(); +} + +void GroupDialog::_group_renamed() { + TreeItem *renamed_group = groups->get_edited(); + if (!renamed_group) { + return; + } + + String name = renamed_group->get_text(0).strip_edges(); + for (TreeItem *E = groups_root->get_children(); E; E = E->get_next()) { + if (E != renamed_group && E->get_text(0) == name) { + renamed_group->set_text(0, selected_group); + error->set_text(TTR("Group name already exists.")); + error->popup_centered(); + return; + } + } + + if (name == "") { + renamed_group->set_text(0, selected_group); + error->set_text(TTR("invalid Group name.")); + error->popup_centered(); + return; + } + + List<Node *> nodes; + scene_tree->get_nodes_in_group(selected_group, &nodes); + bool removed_all = true; + for (List<Node *>::Element *E = nodes.front(); E; E = E->next()) { + Node *node = E->get(); + if (_can_edit(node, selected_group)) { + node->remove_from_group(selected_group); + node->add_to_group(name, true); + } else { + removed_all = false; + } + } + + if (!removed_all) { + _add_group(selected_group); + } + + selected_group = renamed_group->get_text(0); + _group_selected(); +} + +void GroupDialog::_add_group(String p_name) { + + String name = p_name.strip_edges(); + if (name == "" || groups->search_item_text(name)) { + return; + } + + TreeItem *new_group = groups->create_item(groups_root); + new_group->set_text(0, name); + new_group->add_button(0, get_icon("Remove", "EditorIcons"), 0); + new_group->set_editable(0, true); +} + +void GroupDialog::_load_groups(Node *p_current) { + List<Node::GroupInfo> gi; + p_current->get_groups(&gi); + + for (List<Node::GroupInfo>::Element *E = gi.front(); E; E = E->next()) { + if (!E->get().persistent) { + continue; + } + _add_group(E->get().name); + } + + for (int i = 0; i < p_current->get_child_count(); i++) { + _load_groups(p_current->get_child(i)); + } +} + +void GroupDialog::_delete_group_pressed(Object *p_item, int p_column, int p_id) { + TreeItem *ti = Object::cast_to<TreeItem>(p_item); + if (!ti) + return; + + String name = ti->get_text(0); + + List<Node *> nodes; + scene_tree->get_nodes_in_group(name, &nodes); + bool removed_all = true; + for (List<Node *>::Element *E = nodes.front(); E; E = E->next()) { + if (_can_edit(E->get(), name)) { + E->get()->remove_from_group(name); + } else { + removed_all = false; + } + } + + if (removed_all) { + if (selected_group == name) { + add_filter->clear(); + remove_filter->clear(); + nodes_to_remove->clear(); + nodes_to_add->clear(); + groups->deselect_all(); + selected_group = ""; + } + groups_root->remove_child(ti); + } + EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->update_tree(); +} + +void GroupDialog::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + add_button->set_icon(get_icon("Forward", "EditorIcons")); + remove_button->set_icon(get_icon("Back", "EditorIcons")); + } break; + } +} + +void GroupDialog::edit() { + + popup_centered(Size2(600, 400)); + + groups->clear(); + groups_root = groups->create_item(); + + nodes_to_add->clear(); + nodes_to_remove->clear(); + + add_group_text->clear(); + add_filter->clear(); + remove_filter->clear(); + + _load_groups(scene_tree->get_edited_scene_root()); +} + +void GroupDialog::_bind_methods() { + ClassDB::bind_method("_cancel", &GroupDialog::_cancel_pressed); + + ClassDB::bind_method("_add_pressed", &GroupDialog::_add_pressed); + ClassDB::bind_method("_removed_pressed", &GroupDialog::_removed_pressed); + ClassDB::bind_method("_delete_group_pressed", &GroupDialog::_delete_group_pressed); + + ClassDB::bind_method("_group_selected", &GroupDialog::_group_selected); + ClassDB::bind_method("_add_group_pressed", &GroupDialog::_add_group_pressed); + + ClassDB::bind_method("_add_filter_changed", &GroupDialog::_add_filter_changed); + ClassDB::bind_method("_remove_filter_changed", &GroupDialog::_remove_filter_changed); + + ClassDB::bind_method("_group_renamed", &GroupDialog::_group_renamed); +} + +GroupDialog::GroupDialog() { + + scene_tree = SceneTree::get_singleton(); + + VBoxContainer *vbc = memnew(VBoxContainer); + add_child(vbc); + + HBoxContainer *hbc = memnew(HBoxContainer); + vbc->add_child(hbc); + hbc->set_v_size_flags(SIZE_EXPAND_FILL); + + VBoxContainer *vbc_left = memnew(VBoxContainer); + hbc->add_child(vbc_left); + vbc_left->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *group_title = memnew(Label); + group_title->set_text(TTR("Groups")); + vbc_left->add_child(group_title); + + groups = memnew(Tree); + vbc_left->add_child(groups); + groups->set_hide_root(true); + groups->set_v_size_flags(SIZE_EXPAND_FILL); + groups->set_select_mode(Tree::SELECT_SINGLE); + groups->set_allow_reselect(true); + groups->set_allow_rmb_select(true); + groups->connect("item_selected", this, "_group_selected"); + groups->connect("button_pressed", this, "_delete_group_pressed"); + groups->connect("item_edited", this, "_group_renamed"); + + HBoxContainer *chbc = memnew(HBoxContainer); + vbc_left->add_child(chbc); + chbc->set_h_size_flags(SIZE_EXPAND_FILL); + + add_group_text = memnew(LineEdit); + chbc->add_child(add_group_text); + add_group_text->set_h_size_flags(SIZE_EXPAND_FILL); + + Button *add_group_button = memnew(Button); + add_group_button->set_text("Add"); + chbc->add_child(add_group_button); + add_group_button->connect("pressed", this, "_add_group_pressed"); + + VBoxContainer *vbc_add = memnew(VBoxContainer); + hbc->add_child(vbc_add); + vbc_add->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *out_of_group_title = memnew(Label); + out_of_group_title->set_text(TTR("Nodes not in Group")); + vbc_add->add_child(out_of_group_title); + + nodes_to_add = memnew(Tree); + vbc_add->add_child(nodes_to_add); + nodes_to_add->set_hide_root(true); + nodes_to_add->set_hide_folding(true); + nodes_to_add->set_v_size_flags(SIZE_EXPAND_FILL); + nodes_to_add->set_select_mode(Tree::SELECT_MULTI); + nodes_to_add->connect("item_selected", this, "_nodes_to_add_selected"); + + HBoxContainer *add_filter_hbc = memnew(HBoxContainer); + add_filter_hbc->add_constant_override("separate", 0); + vbc_add->add_child(add_filter_hbc); + + add_filter = memnew(LineEdit); + add_filter->set_h_size_flags(SIZE_EXPAND_FILL); + add_filter->set_placeholder(TTR("Filter nodes")); + add_filter_hbc->add_child(add_filter); + add_filter->connect("text_changed", this, "_add_filter_changed"); + + VBoxContainer *vbc_buttons = memnew(VBoxContainer); + hbc->add_child(vbc_buttons); + vbc_buttons->set_h_size_flags(SIZE_SHRINK_CENTER); + vbc_buttons->set_v_size_flags(SIZE_SHRINK_CENTER); + + add_button = memnew(ToolButton); + add_button->set_text(TTR("Add")); + add_button->connect("pressed", this, "_add_pressed"); + + vbc_buttons->add_child(add_button); + vbc_buttons->add_spacer(); + vbc_buttons->add_spacer(); + vbc_buttons->add_spacer(); + + remove_button = memnew(ToolButton); + remove_button->set_text(TTR("Remove")); + remove_button->connect("pressed", this, "_removed_pressed"); + + vbc_buttons->add_child(remove_button); + + VBoxContainer *vbc_remove = memnew(VBoxContainer); + hbc->add_child(vbc_remove); + vbc_remove->set_h_size_flags(SIZE_EXPAND_FILL); + + Label *in_group_title = memnew(Label); + in_group_title->set_text(TTR("Nodes in Group")); + vbc_remove->add_child(in_group_title); + + nodes_to_remove = memnew(Tree); + vbc_remove->add_child(nodes_to_remove); + nodes_to_remove->set_v_size_flags(SIZE_EXPAND_FILL); + nodes_to_remove->set_hide_root(true); + nodes_to_remove->set_hide_folding(true); + nodes_to_remove->set_select_mode(Tree::SELECT_MULTI); + nodes_to_remove->connect("item_selected", this, "_node_to_remove_selected"); + + HBoxContainer *remove_filter_hbc = memnew(HBoxContainer); + remove_filter_hbc->add_constant_override("separate", 0); + vbc_remove->add_child(remove_filter_hbc); + + remove_filter = memnew(LineEdit); + remove_filter->set_h_size_flags(SIZE_EXPAND_FILL); + remove_filter->set_placeholder(TTR("Filter nodes")); + remove_filter_hbc->add_child(remove_filter); + remove_filter->connect("text_changed", this, "_remove_filter_changed"); + + set_title("Group Editor"); + get_cancel()->hide(); + set_as_toplevel(true); + + error = memnew(ConfirmationDialog); + add_child(error); + error->get_ok()->set_text(TTR("Close")); +} + +//////////////////////////////////////////////////////////////////////////////// + void GroupsEditor::_add_group(const String &p_group) { if (!node) @@ -146,11 +560,22 @@ void GroupsEditor::set_current(Node *p_node) { update_tree(); } +void GroupsEditor::_show_group_dialog() { + group_dialog->edit(); +} + +void GroupsEditor::_group_dialog_closed() { + update_tree(); +} + void GroupsEditor::_bind_methods() { ClassDB::bind_method("_add_group", &GroupsEditor::_add_group); ClassDB::bind_method("_remove_group", &GroupsEditor::_remove_group); ClassDB::bind_method("update_tree", &GroupsEditor::update_tree); + + ClassDB::bind_method("_show_group_dialog", &GroupsEditor::_show_group_dialog); + ClassDB::bind_method("_group_dialog_closed", &GroupsEditor::_group_dialog_closed); } GroupsEditor::GroupsEditor() { @@ -159,6 +584,16 @@ GroupsEditor::GroupsEditor() { VBoxContainer *vbc = this; + group_dialog = memnew(GroupDialog); + group_dialog->set_as_toplevel(true); + add_child(group_dialog); + group_dialog->connect("popup_hide", this, "_group_dialog_closed"); + + Button *group_dialog_button = memnew(Button); + group_dialog_button->set_text(TTR("Manage Groups")); + vbc->add_child(group_dialog_button); + group_dialog_button->connect("pressed", this, "_show_group_dialog"); + HBoxContainer *hbc = memnew(HBoxContainer); vbc->add_child(hbc); diff --git a/editor/groups_editor.h b/editor/groups_editor.h index ad3d5cd14f..461cf0f8c2 100644 --- a/editor/groups_editor.h +++ b/editor/groups_editor.h @@ -31,9 +31,13 @@ #ifndef GROUPS_EDITOR_H #define GROUPS_EDITOR_H +#include "editor/scene_tree_editor.h" #include "scene/gui/button.h" #include "scene/gui/dialogs.h" +#include "scene/gui/item_list.h" #include "scene/gui/line_edit.h" +#include "scene/gui/popup.h" +#include "scene/gui/tool_button.h" #include "scene/gui/tree.h" #include "undo_redo.h" @@ -41,12 +45,71 @@ @author Juan Linietsky <reduzio@gmail.com> */ +class GroupDialog : public ConfirmationDialog { + + GDCLASS(GroupDialog, ConfirmationDialog); + + ConfirmationDialog *error; + + SceneTree *scene_tree; + TreeItem *groups_root; + + LineEdit *add_group_text; + + Tree *groups; + + Tree *nodes_to_add; + TreeItem *add_node_root; + LineEdit *add_filter; + + Tree *nodes_to_remove; + TreeItem *remove_node_root; + LineEdit *remove_filter; + + ToolButton *add_button; + ToolButton *remove_button; + + String selected_group; + + void ok_pressed(); + void _cancel_pressed(); + void _group_selected(); + + void _remove_filter_changed(const String &p_filter); + void _add_filter_changed(const String &p_filter); + + void _add_pressed(); + void _removed_pressed(); + void _add_group_pressed(); + + void _group_renamed(); + + void _add_group(String p_name); + void _delete_group_pressed(Object *p_item, int p_column, int p_id); + + bool _can_edit(Node *p_node, String p_group); + + void _load_groups(Node *p_current); + void _load_nodes(Node *p_current); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void edit(); + + GroupDialog(); +}; + class GroupsEditor : public VBoxContainer { GDCLASS(GroupsEditor, VBoxContainer); Node *node; + GroupDialog *group_dialog; + LineEdit *group_name; Button *add; Tree *tree; @@ -58,6 +121,9 @@ class GroupsEditor : public VBoxContainer { void _remove_group(Object *p_item, int p_column, int p_id); void _close(); + void _show_group_dialog(); + void _group_dialog_closed(); + protected: static void _bind_methods(); diff --git a/editor/icons/icon_add_split.svg b/editor/icons/icon_add_split.svg index 6cfd419e7f..4555fceb7c 100644 --- a/editor/icons/icon_add_split.svg +++ b/editor/icons/icon_add_split.svg @@ -1,72 +1,8 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - 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="16" - height="16" - version="1.1" - viewBox="0 0 16 16" - id="svg4" - sodipodi:docname="icon_add_split.svg" - inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"> - <metadata - id="metadata10"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <defs - id="defs8" /> - <sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="3066" - inkscape:window-height="1689" - id="namedview6" - showgrid="false" - inkscape:zoom="41.7193" - inkscape:cx="7.7924561" - inkscape:cy="6.0148972" - inkscape:window-x="134" - inkscape:window-y="55" - inkscape:window-maximized="1" - inkscape:current-layer="svg4" /> - <rect - style="fill:#800000" - id="rect12" - width="1.8456686" - height="2.0853658" - x="0.62321275" - y="6.9394455" /> - <rect - style="fill:#800000" - id="rect14" - width="1.6299411" - height="1.9894869" - x="12.488225" - y="7.1791425" /> - <rect - style="fill:#e9afaf" - id="rect16" - width="10.067283" - height="0.69512194" - x="2.492851" - y="7.7304463" /> +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<path d="m3 13 10-10" fill="none" stroke="#f5f5f5" stroke-opacity=".39216" stroke-width="2"/> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m11 9v2h-2v2h2v2h2v-2h2v-2h-2v-2z" fill="#84ffb1"/> +</g> +<circle cx="4" cy="12" r="2" fill="none"/> +<path d="m13 1a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2 -2 2 2 0 0 0 -2 -2zm-10 10a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2 -2 2 2 0 0 0 -2 -2z" fill="#e0e0e0"/> </svg> diff --git a/editor/icons/icon_asset_lib.svg b/editor/icons/icon_asset_lib.svg index 1348c491fc..967c5bf708 100644 --- a/editor/icons/icon_asset_lib.svg +++ b/editor/icons/icon_asset_lib.svg @@ -1,3 +1,3 @@ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<path d="m8 1c-1.6569 0-3 1.3431-3 3v2h-4v7c0 1.108 0.89199 2 2 2h10c1.108 0 2-0.89199 2-2v-7h-4v-2c0-1.6569-1.3431-3-3-3zm0 2c0.55228 0 1 0.44772 1 1v2h-2v-2c0-0.55228 0.44772-1 1-1z" fill="#e0e0e0"/> +<path d="m8 1c-1.6569 0-3 1.3431-3 3v2h-3c-0.66446 3.505e-4 -1.1438 0.6366-0.96094 1.2754l2 7c0.12287 0.42881 0.51487 0.7244 0.96094 0.72461h8c0.44606-2.09e-4 0.83806-0.2958 0.96094-0.72461l2-7c0.1829-0.63879-0.29648-1.275-0.96094-1.2754h-3v-2c0-1.6569-1.3431-3-3-3zm0 2c0.55228 0 1 0.44772 1 1v2h-2v-2c0-0.55228 0.44772-1 1-1z" fill="#e0e0e0"/> </svg> diff --git a/editor/icons/icon_audio_bus_layout.svg b/editor/icons/icon_audio_bus_layout.svg index 9162722eb2..fa6b60bc3e 100644 --- a/editor/icons/icon_audio_bus_layout.svg +++ b/editor/icons/icon_audio_bus_layout.svg @@ -1,9 +1,9 @@ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="a" x1="8" x2="8" y1="1" y2="15" gradientUnits="userSpaceOnUse"> -<stop stop-color="#ff8484" offset="0"/> +<stop stop-color="#ff7a7a" offset="0"/> <stop stop-color="#e1dc7a" offset=".5"/> -<stop stop-color="#84ffb1" offset="1"/> +<stop stop-color="#66ff9e" offset="1"/> </linearGradient> </defs> <g transform="translate(0 -1036.4)"> diff --git a/editor/icons/icon_audio_stream_player.svg b/editor/icons/icon_audio_stream_player.svg index 218fd995a0..754b72bc96 100644 --- a/editor/icons/icon_audio_stream_player.svg +++ b/editor/icons/icon_audio_stream_player.svg @@ -1,9 +1,9 @@ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="a" x1="8" x2="8" y1="1" y2="15" gradientUnits="userSpaceOnUse"> -<stop stop-color="#ff8484" offset="0"/> +<stop stop-color="#ff7a7a" offset="0"/> <stop stop-color="#e1dc7a" offset=".5"/> -<stop stop-color="#84ffb1" offset="1"/> +<stop stop-color="#66ff9e" offset="1"/> </linearGradient> </defs> <g transform="translate(0 -1036.4)" shape-rendering="auto"> diff --git a/editor/icons/icon_audio_stream_player_2_d.svg b/editor/icons/icon_audio_stream_player_2_d.svg index a431b84a55..0e9c0ca5b1 100644 --- a/editor/icons/icon_audio_stream_player_2_d.svg +++ b/editor/icons/icon_audio_stream_player_2_d.svg @@ -1,9 +1,9 @@ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="a" x1="8" x2="8" y1="1" y2="15" gradientUnits="userSpaceOnUse"> -<stop stop-color="#ff8484" offset="0"/> +<stop stop-color="#ff7a7a" offset="0"/> <stop stop-color="#e1dc7a" offset=".5"/> -<stop stop-color="#84ffb1" offset="1"/> +<stop stop-color="#66ff9e" offset="1"/> </linearGradient> </defs> <g transform="translate(0 -1036.4)" shape-rendering="auto"> diff --git a/editor/icons/icon_audio_stream_player_3_d.svg b/editor/icons/icon_audio_stream_player_3_d.svg index 4ce9d6da58..d947586f63 100644 --- a/editor/icons/icon_audio_stream_player_3_d.svg +++ b/editor/icons/icon_audio_stream_player_3_d.svg @@ -1,9 +1,9 @@ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="a" x1="8" x2="8" y1="1" y2="15" gradientUnits="userSpaceOnUse"> -<stop stop-color="#ff8484" offset="0"/> +<stop stop-color="#ff7a7a" offset="0"/> <stop stop-color="#e1dc7a" offset=".5"/> -<stop stop-color="#84ffb1" offset="1"/> +<stop stop-color="#66ff9e" offset="1"/> </linearGradient> </defs> <g transform="translate(0 -1036.4)" shape-rendering="auto"> diff --git a/editor/icons/icon_audio_stream_sample.svg b/editor/icons/icon_audio_stream_sample.svg index f0be1dc303..a7c7232ee0 100644 --- a/editor/icons/icon_audio_stream_sample.svg +++ b/editor/icons/icon_audio_stream_sample.svg @@ -1,9 +1,9 @@ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="a" x1="8" x2="8" y1="1" y2="15" gradientUnits="userSpaceOnUse"> -<stop stop-color="#ff8484" offset="0"/> +<stop stop-color="#ff7a7a" offset="0"/> <stop stop-color="#e1dc7a" offset=".5"/> -<stop stop-color="#84ffb1" offset="1"/> +<stop stop-color="#66ff9e" offset="1"/> </linearGradient> </defs> <g transform="translate(0 -1036.4)"> diff --git a/editor/icons/icon_bus_vu_db.svg b/editor/icons/icon_bus_vu_db.svg index 23bcd8841c..236e41e1f5 100644 --- a/editor/icons/icon_bus_vu_db.svg +++ b/editor/icons/icon_bus_vu_db.svg @@ -1,9 +1,9 @@ <svg width="32" height="128" version="1.1" viewBox="0 0 32 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="a" x1="16" x2="16" y2="128" gradientUnits="userSpaceOnUse"> -<stop stop-color="#ff8484" offset="0"/> +<stop stop-color="#ff7a7a" offset="0"/> <stop stop-color="#e1dc7a" offset=".5"/> -<stop stop-color="#84ffb1" offset="1"/> +<stop stop-color="#66ff9e" offset="1"/> </linearGradient> </defs> <g transform="translate(0 -924.36)"> diff --git a/editor/icons/icon_bus_vu_empty.svg b/editor/icons/icon_bus_vu_empty.svg index 52c86ac704..60fddc535f 100644 --- a/editor/icons/icon_bus_vu_empty.svg +++ b/editor/icons/icon_bus_vu_empty.svg @@ -1,9 +1,9 @@ <svg width="16" height="128" version="1.1" viewBox="0 0 16 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="a" x1="8" x2="8" y1="2" y2="126" gradientTransform="translate(0 924.36)" gradientUnits="userSpaceOnUse"> -<stop stop-color="#ff8484" offset="0"/> +<stop stop-color="#ff7a7a" offset="0"/> <stop stop-color="#e1dc7a" offset=".5"/> -<stop stop-color="#84ffb1" offset="1"/> +<stop stop-color="#66ff9e" offset="1"/> </linearGradient> </defs> <g transform="translate(0 -924.36)"> diff --git a/editor/icons/icon_bus_vu_full.svg b/editor/icons/icon_bus_vu_full.svg index a91b8a06c6..4f2ce5df11 100644 --- a/editor/icons/icon_bus_vu_full.svg +++ b/editor/icons/icon_bus_vu_full.svg @@ -1,9 +1,9 @@ <svg width="16" height="128" version="1.1" viewBox="0 0 16 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="a" x1="8" x2="8" y1="2" y2="126" gradientUnits="userSpaceOnUse"> -<stop stop-color="#ff8484" offset="0"/> +<stop stop-color="#ff7a7a" offset="0"/> <stop stop-color="#e1dc7a" offset=".5"/> -<stop stop-color="#84ffb1" offset="1"/> +<stop stop-color="#66ff9e" offset="1"/> </linearGradient> </defs> <g transform="translate(0 -924.36)"> diff --git a/editor/icons/icon_c_s_g_box.svg b/editor/icons/icon_c_s_g_box.svg new file mode 100644 index 0000000000..67e34df444 --- /dev/null +++ b/editor/icons/icon_c_s_g_box.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/> +<path transform="translate(0 1036.4)" d="m8 0.94531-7 3.5v7.2227l7 3.5 0.29492-0.14844c-0.18282-0.30101-0.29492-0.64737-0.29492-1.0195v-2c0-0.72651 0.40824-1.3664 1-1.7168v-1.6699l4-2v1.3867h1c0.36419 0 0.70336 0.10754 1 0.2832v-3.8379zm0 2.1152 3.9395 1.9707-3.9395 1.9688-3.9395-1.9688zm-5 3.5527 4 2v3.9414l-4-2.002z" fill="#fc9c9c" stroke-width="1.0667"/> +</g> +</svg> diff --git a/editor/icons/icon_c_s_g_capsule.svg b/editor/icons/icon_c_s_g_capsule.svg new file mode 100644 index 0000000000..92a7b5a870 --- /dev/null +++ b/editor/icons/icon_c_s_g_capsule.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g> +<path d="m8 1c-2.7527 0-5 2.2418-5 4.9902v4.0176c0 2.7484 2.2473 4.9922 5 4.9922 0.092943 0 0.18367-0.008623 0.27539-0.013672-0.17055-0.29341-0.27539-0.62792-0.27539-0.98633v-2c0-0.72887 0.41095-1.3691 1.0059-1.7188v-0.28125c0.34771-0.034464 0.68259-0.10691 1.0156-0.19922 0.10394-0.99856 0.95603-1.8008 1.9785-1.8008h1v-2.0098c0-2.7484-2.2473-4.9902-5-4.9902zm-1.0059 2.127v4.8574c-0.66556-0.1047-1.2974-0.37231-1.9941-0.66211v-1.3223c0-1.3474 0.79841-2.4642 1.9941-2.873zm2.0117 0c1.1957 0.4088 1.9941 1.5256 1.9941 2.873v1.3457c-0.68406 0.3054-1.3142 0.57292-1.9941 0.66602v-4.8848zm-4.0059 6.334c0.67836 0.2231 1.3126 0.44599 1.9941 0.52539v2.8848c-1.1957-0.4092-1.9941-1.5237-1.9941-2.8711v-0.53906z" fill="#fc9c9c"/> +<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/> +</g> +</svg> diff --git a/editor/icons/icon_c_s_g_combiner.svg b/editor/icons/icon_c_s_g_combiner.svg new file mode 100644 index 0000000000..cce2902e24 --- /dev/null +++ b/editor/icons/icon_c_s_g_combiner.svg @@ -0,0 +1,8 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/> +<g fill="#fc9c9c"> +<path transform="translate(0 1036.4)" d="m3 1c-1.1046 0-2 0.89543-2 2h2zm2 0v2h2v-2zm4 0v2h2v-2zm4 0v2h2c0-1.1046-0.89543-2-2-2zm-12 4v2h2v-2zm12 0v2h2v-2zm-12 4v2h2v-2zm0 4c0 1.1046 0.89543 2 2 2v-2zm4 0v2h2v-2z" fill="#fc9c9c"/> +</g> +</g> +</svg> diff --git a/editor/icons/icon_c_s_g_cylinder.svg b/editor/icons/icon_c_s_g_cylinder.svg new file mode 100644 index 0000000000..645a74c79b --- /dev/null +++ b/editor/icons/icon_c_s_g_cylinder.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 14.999999 14.999999" xmlns="http://www.w3.org/2000/svg"> +<g> +<path transform="scale(.9375)" d="m8 1c-1.7469 0-3.328 0.22648-4.5586 0.63672-0.61528 0.20512-1.1471 0.45187-1.5898 0.80078-0.44272 0.34891-0.85156 0.88101-0.85156 1.5625v8c0 0.68149 0.40884 1.2155 0.85156 1.5645 0.44272 0.34891 0.97457 0.59577 1.5898 0.80078 1.2306 0.41024 2.8117 0.63477 4.5586 0.63477 0.095648 0 0.18467-0.008426 0.2793-0.009766-0.1722-0.29446-0.2793-0.62995-0.2793-0.99023v-1c-1.5668 0-2.9867-0.2195-3.9277-0.5332-0.46329-0.15435-0.90474-0.33752-1.0723-0.4668v-5.8125c0.1468 0.058667 0.2835 0.12515 0.44141 0.17773 1.2306 0.41024 2.8117 0.63477 4.5586 0.63477s3.328-0.22453 4.5586-0.63477c0.15791-0.052267 0.29461-0.11864 0.44141-0.17773v1.8125h1c0.36396 0 0.70348 0.10774 1 0.2832v-4.2832c0-0.68149-0.40884-1.2136-0.85156-1.5625-0.44272-0.34891-0.97457-0.59566-1.5898-0.80078-1.2306-0.41024-2.8117-0.63672-4.5586-0.63672zm0 2c1.5668 0 2.9867 0.22145 3.9277 0.53516 0.46368 0.15456 0.80138 0.33741 0.96875 0.4668-0.16752 0.12928-0.50546 0.3105-0.96875 0.46484-0.94102 0.31371-2.361 0.5332-3.9277 0.5332s-2.9867-0.2195-3.9277-0.5332c-0.46329-0.15435-0.80123-0.33556-0.96875-0.46484 0.16737-0.12939 0.50507-0.31224 0.96875-0.4668 0.94102-0.31371 2.361-0.53516 3.9277-0.53516z" fill="#fc9c9c" stroke-width="1.0667"/> +<path d="m11.25 8.4375c-0.51938 0-0.9375 0.41812-0.9375 0.9375v0.9375h1.875v1.875h0.9375c0.51938 0 0.9375-0.41812 0.9375-0.9375v-1.875c0-0.51938-0.41812-0.9375-0.9375-0.9375zm0.9375 3.75h-1.875v-1.875h-0.9375c-0.51938 0-0.9375 0.41812-0.9375 0.9375v1.875c0 0.51938 0.41812 0.9375 0.9375 0.9375h1.875c0.51938 0 0.9375-0.41812 0.9375-0.9375z" fill="#84c2ff"/> +</g> +</svg> diff --git a/editor/icons/icon_c_s_g_mesh.svg b/editor/icons/icon_c_s_g_mesh.svg new file mode 100644 index 0000000000..6e940a4aa5 --- /dev/null +++ b/editor/icons/icon_c_s_g_mesh.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g> +<path d="m3 1c-1.1046 0-2 0.89543-2 2 5.649e-4 0.71397 0.38169 1.3735 1 1.7305v6.541c-0.61771 0.35663-0.99874 1.0152-1 1.7285 0 1.1046 0.89543 2 2 2 0.71397-5.65e-4 1.3735-0.38169 1.7305-1h3.2695v-2h-3.2715c-0.17478-0.30301-0.42598-0.55488-0.72852-0.73047v-5.8555l4.916 4.916c0.31428-0.20669 0.68609-0.33008 1.084-0.33008 0-0.3979 0.12338-0.76971 0.33008-1.084l-4.916-4.916h5.8574c0.17478 0.30301 0.42598 0.55488 0.72852 0.73047v3.2695h2v-3.2715c0.61771-0.35663 0.99874-1.0152 1-1.7285 0-1.1046-0.89543-2-2-2-0.71397 5.648e-4 -1.3735 0.38169-1.7305 1h-6.541c-0.35663-0.61771-1.0152-0.99874-1.7285-1z" fill="#fc9c9c"/> +<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/> +</g> +</svg> diff --git a/editor/icons/icon_c_s_g_polygon.svg b/editor/icons/icon_c_s_g_polygon.svg new file mode 100644 index 0000000000..71b03cb8e6 --- /dev/null +++ b/editor/icons/icon_c_s_g_polygon.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m7.9629 1.002c-0.14254 0.00487-0.28238 0.04016-0.41016 0.10352l-6 3c-0.33878 0.16944-0.55276 0.51574-0.55273 0.89453v5.832c-0.105 0.61631 0.37487 1.1768 1 1.168h5v2c2.16e-5 0.67546 0.64487 1.1297 1.2617 0.95898-0.16118-0.28721-0.26172-0.61135-0.26172-0.95898v-2c0-0.72673 0.40794-1.3664 1-1.7168v-1.666l4-2v1.3828h1c0.36397 0 0.70348 0.10774 1 0.2832v-3.2773c6e-6 -0.00195 6e-6 -0.0039094 0-0.0058594 2.6e-5 -0.37879-0.21395-0.72509-0.55273-0.89453l-6-3c-0.15022-0.074574-0.31679-0.11017-0.48438-0.10352zm0.037109 2.1172l3.7637 1.8809-2.7637 1.3809v-1.3809c-5.52e-5 -0.55226-0.44774-0.99994-1-1h-1.7617l1.7617-0.88086zm-5 2.8809h4v4h-4v-4z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#fc9c9c" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> +<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/> +</g> +</svg> diff --git a/editor/icons/icon_c_s_g_sphere.svg b/editor/icons/icon_c_s_g_sphere.svg new file mode 100644 index 0000000000..f81b566993 --- /dev/null +++ b/editor/icons/icon_c_s_g_sphere.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g> +<path d="m8 1c-3.8541 0-7 3.1459-7 7 0 3.8542 3.1459 7 7 7 0.093042 0 0.18321-0.01004 0.27539-0.013672-0.17055-0.29341-0.27539-0.62792-0.27539-0.98633v-2c0-0.72673 0.40794-1.3664 1-1.7168v-0.33398c0.34074-0.019259 0.67728-0.069097 1.0156-0.10547 0.083091-1.0187 0.94713-1.8438 1.9844-1.8438h2c0.35841 0 0.69292 0.10484 0.98633 0.27539 0.003633-0.092184 0.013672-0.18235 0.013672-0.27539 0-3.8541-3.1459-7-7-7zm-1 2.0977v4.8711c-1.2931-0.071342-2.6061-0.29819-3.9434-0.69141 0.30081-2.0978 1.8852-3.7665 3.9434-4.1797zm2 0c2.0549 0.41253 3.637 2.0767 3.9414 4.1699-1.3046 0.36677-2.6158 0.60259-3.9414 0.6875v-4.8574zm-5.7793 6.2988c1.2733 0.31892 2.5337 0.50215 3.7793 0.5625v2.9414c-1.8291-0.36719-3.266-1.7339-3.7793-3.5039z" fill="#fc9c9c"/> +<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/> +</g> +</svg> diff --git a/editor/icons/icon_c_s_g_torus.svg b/editor/icons/icon_c_s_g_torus.svg new file mode 100644 index 0000000000..3d30aa47b2 --- /dev/null +++ b/editor/icons/icon_c_s_g_torus.svg @@ -0,0 +1,6 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m8 3c-1.8145 0-3.4691 0.41721-4.7461 1.1621-1.277 0.745-2.2539 1.9082-2.2539 3.3379 0 1.4298 0.9769 2.5949 2.2539 3.3398 1.277 0.7449 2.9316 1.1602 4.7461 1.1602 0-1.0907 0.90931-2 2-2 0-0.080836 0.013744-0.15778 0.023438-0.23633-0.61769 0.14673-1.3008 0.23633-2.0234 0.23633-1.4992 0-2.8437-0.36687-3.7383-0.88867-0.89456-0.5219-1.2617-1.108-1.2617-1.6113 0-0.5032 0.36716-1.0876 1.2617-1.6094 0.89456-0.5219 2.2391-0.89062 3.7383-0.89062s2.8437 0.36872 3.7383 0.89062c0.89456 0.5218 1.2617 1.1062 1.2617 1.6094 0 0.15978-0.053679 0.32822-0.13281 0.5h1.1328c0.32481 0 0.62893 0.088408 0.90234 0.23047 0.057552-0.23582 0.097656-0.47718 0.097656-0.73047 0-1.4297-0.9769-2.5929-2.2539-3.3379-1.277-0.7449-2.9316-1.1621-4.7461-1.1621z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#fc9c9c" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> +<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/> +</g> +</svg> diff --git a/editor/icons/icon_color_rect.svg b/editor/icons/icon_color_rect.svg index c0cd07061e..c2d4cf344d 100644 --- a/editor/icons/icon_color_rect.svg +++ b/editor/icons/icon_color_rect.svg @@ -1,9 +1,6 @@ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> <g transform="translate(0 -1036.4)"> -<rect x="1" y="1037.4" width="2" height="14" fill="#a5efac"/> -<rect x="1" y="1049.4" width="14" height="2" fill="#a5efac"/> -<rect x="1" y="1037.4" width="14" height="2" fill="#a5efac"/> -<rect x="13" y="1037.4" width="2" height="14" fill="#a5efac"/> +<path transform="translate(0 1036.4)" d="m1 1v14h14v-14zm2 2h10v10h-10z" fill="#a5efac"/> <path d="m12 1048.4h-4.8l4.8-4.8z" fill="#70bfff" fill-rule="evenodd"/> <path d="m4 1040.4h4.8l-4.8 4.8z" fill="#ff7070" fill-rule="evenodd"/> <path d="m4 1048.4v-3.2l4.8-4.8h3.2v3.2l-4.8 4.8z" fill="#7aff70" fill-rule="evenodd"/> diff --git a/editor/icons/icon_editor_position.svg b/editor/icons/icon_editor_position.svg index 7cbce07fab..7657eb5160 100644 --- a/editor/icons/icon_editor_position.svg +++ b/editor/icons/icon_editor_position.svg @@ -1,69 +1,4 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - 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="16" - height="16" - version="1.1" - viewBox="0 0 16 16" - id="svg919" - sodipodi:docname="icon_editor_position.svg" - inkscape:version="0.92.2 5c3e80d, 2017-08-06"> - <metadata - id="metadata925"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <defs - id="defs923" /> - <sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="2550" - inkscape:window-height="1414" - id="namedview921" - showgrid="false" - inkscape:zoom="64" - inkscape:cx="13.492036" - inkscape:cy="5.8518769" - inkscape:window-x="1370" - inkscape:window-y="20" - inkscape:window-maximized="0" - inkscape:current-layer="svg919" - inkscape:snap-page="true" /> - <g - id="g6479"> - <path - id="path913" - d="M 6 0 L 6 4.4199219 A 4.2661548 4.0576186 0 0 0 4.2910156 6 L 0 6 L 0 10 L 4.2949219 10 A 4.2661548 4.0576186 0 0 0 6 11.582031 L 6 16 L 10 16 L 10 11.580078 A 4.2661548 4.0576186 0 0 0 11.708984 10 L 16 10 L 16 6 L 11.705078 6 A 4.2661548 4.0576186 0 0 0 10 4.4179688 L 10 0 L 6 0 z " - style="fill:#ffffff;fill-opacity:0.70588237" /> - <path - id="path915" - d="M 7 1 L 7 4.0605469 A 4.2661548 4.0576186 0 0 1 8 3.9414062 A 4.2661548 4.0576186 0 0 1 9 4.0605469 L 9 1 L 7 1 z M 1 7 L 1 9 L 3.8691406 9 A 4.2661548 4.0576186 0 0 1 3.734375 8 A 4.2661548 4.0576186 0 0 1 3.8710938 7 L 1 7 z M 12.130859 7 A 4.2661548 4.0576186 0 0 1 12.265625 8 A 4.2661548 4.0576186 0 0 1 12.128906 9 L 15 9 L 15 7 L 12.130859 7 z M 7 11.939453 L 7 15 L 9 15 L 9 11.939453 A 4.2661548 4.0576186 0 0 1 8 12.058594 A 4.2661548 4.0576186 0 0 1 7 11.939453 z " - style="fill:#ff8484;stroke:none;fill-opacity:1" /> - <circle - id="circle1517" - r="2.9201488" - cy="8" - cx="8" - style="fill:#ff8484;fill-opacity:1;stroke-width:0.97338283" /> - </g> +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<path d="m6 0v4.4199a4.2662 4.0576 0 0 0 -1.709 1.5801h-4.291v4h4.2949a4.2662 4.0576 0 0 0 1.7051 1.582v4.418h4v-4.4199a4.2662 4.0576 0 0 0 1.709 -1.5801h4.291v-4h-4.2949a4.2662 4.0576 0 0 0 -1.7051 -1.582v-4.418z" fill="#fff" fill-opacity=".70588"/> +<path d="m7 1v3.0605a4.2662 4.0576 0 0 1 1 -0.11914 4.2662 4.0576 0 0 1 1 0.11914v-3.0605h-2zm1 4.0801a2.9201 2.9201 0 0 0 -2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 -2.9199 2.9201 2.9201 0 0 0 -2.9199 -2.9199zm-7 1.9199v2h2.8691a4.2662 4.0576 0 0 1 -0.13477 -1 4.2662 4.0576 0 0 1 0.13672 -1h-2.8711zm11.131 0a4.2662 4.0576 0 0 1 0.13477 1 4.2662 4.0576 0 0 1 -0.13672 1h2.8711v-2h-2.8691zm-5.1309 4.9395v3.0605h2v-3.0605a4.2662 4.0576 0 0 1 -1 0.11914 4.2662 4.0576 0 0 1 -1 -0.11914z" fill="#ff8484"/> </svg> diff --git a/editor/icons/icon_editor_position_previous.svg b/editor/icons/icon_editor_position_previous.svg index d9eb7d7ce2..180156e13a 100644 --- a/editor/icons/icon_editor_position_previous.svg +++ b/editor/icons/icon_editor_position_previous.svg @@ -1,62 +1,3 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - 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="16" - height="16" - version="1.1" - viewBox="0 0 16 16" - id="svg919" - sodipodi:docname="icon_editor_position_previous.svg" - inkscape:version="0.92.2 5c3e80d, 2017-08-06"> - <metadata - id="metadata925"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title /> - </cc:Work> - </rdf:RDF> - </metadata> - <defs - id="defs923" /> - <sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="1356" - inkscape:window-height="742" - id="namedview921" - showgrid="false" - inkscape:zoom="11.313709" - inkscape:cx="14.523034" - inkscape:cy="-5.7323703" - inkscape:window-x="4" - inkscape:window-y="20" - inkscape:window-maximized="0" - inkscape:current-layer="svg919" - inkscape:snap-page="true" /> - <path - style="fill:#6699ff;stroke:none;fill-opacity:0.69803923" - d="M 7 1 L 7 4.0605469 A 4.2661548 4.0576186 0 0 1 8 3.9414062 A 4.2661548 4.0576186 0 0 1 9 4.0605469 L 9 1 L 7 1 z M 1 7 L 1 9 L 3.8691406 9 A 4.2661548 4.0576186 0 0 1 3.734375 8 A 4.2661548 4.0576186 0 0 1 3.8710938 7 L 1 7 z M 12.130859 7 A 4.2661548 4.0576186 0 0 1 12.265625 8 A 4.2661548 4.0576186 0 0 1 12.128906 9 L 15 9 L 15 7 L 12.130859 7 z M 7 11.939453 L 7 15 L 9 15 L 9 11.939453 A 4.2661548 4.0576186 0 0 1 8 12.058594 A 4.2661548 4.0576186 0 0 1 7 11.939453 z " - id="path915" /> - <circle - style="fill:#6699ff;fill-opacity:0.69803923;stroke-width:0.97338283" - cx="8" - cy="8" - r="2.9201488" - id="circle1517" /> +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<path d="m7 1v3.0605a4.2662 4.0576 0 0 1 1 -0.11914 4.2662 4.0576 0 0 1 1 0.11914v-3.0605h-2zm1 4.0801a2.9201 2.9201 0 0 0 -2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 -2.9199 2.9201 2.9201 0 0 0 -2.9199 -2.9199zm-7 1.9199v2h2.8691a4.2662 4.0576 0 0 1 -0.13477 -1 4.2662 4.0576 0 0 1 0.13672 -1h-2.8711zm11.131 0a4.2662 4.0576 0 0 1 0.13477 1 4.2662 4.0576 0 0 1 -0.13672 1h2.8711v-2h-2.8691zm-5.1309 4.9395v3.0605h2v-3.0605a4.2662 4.0576 0 0 1 -1 0.11914 4.2662 4.0576 0 0 1 -1 -0.11914z" fill="#69f" fill-opacity=".69804"/> </svg> diff --git a/editor/icons/icon_editor_position_unselected.svg b/editor/icons/icon_editor_position_unselected.svg index 8f32c89f16..3c7d479b88 100644 --- a/editor/icons/icon_editor_position_unselected.svg +++ b/editor/icons/icon_editor_position_unselected.svg @@ -1,66 +1,4 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - 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="16" - height="16" - version="1.1" - viewBox="0 0 16 16" - id="svg919" - sodipodi:docname="icon_editor_position_unselected.svg" - inkscape:version="0.92.2 5c3e80d, 2017-08-06"> - <metadata - id="metadata925"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <defs - id="defs923" /> - <sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="2550" - inkscape:window-height="1414" - id="namedview921" - showgrid="false" - inkscape:zoom="64" - inkscape:cx="13.492036" - inkscape:cy="8.3518769" - inkscape:window-x="1370" - inkscape:window-y="20" - inkscape:window-maximized="0" - inkscape:current-layer="svg919" - inkscape:snap-page="true" /> - <path - style="fill:#000000;fill-opacity:0.41077441" - d="M 6 0 L 6 4.4199219 A 4.2661548 4.0576186 0 0 0 4.2910156 6 L 0 6 L 0 10 L 4.2949219 10 A 4.2661548 4.0576186 0 0 0 6 11.582031 L 6 16 L 10 16 L 10 11.580078 A 4.2661548 4.0576186 0 0 0 11.708984 10 L 16 10 L 16 6 L 11.705078 6 A 4.2661548 4.0576186 0 0 0 10 4.4179688 L 10 0 L 6 0 z " - id="path913" /> - <path - style="fill:#d9d9d9;stroke:none;fill-opacity:1" - d="M 7 1 L 7 4.0605469 A 4.2661548 4.0576186 0 0 1 8 3.9414062 A 4.2661548 4.0576186 0 0 1 9 4.0605469 L 9 1 L 7 1 z M 1 7 L 1 9 L 3.8691406 9 A 4.2661548 4.0576186 0 0 1 3.734375 8 A 4.2661548 4.0576186 0 0 1 3.8710938 7 L 1 7 z M 12.130859 7 A 4.2661548 4.0576186 0 0 1 12.265625 8 A 4.2661548 4.0576186 0 0 1 12.128906 9 L 15 9 L 15 7 L 12.130859 7 z M 7 11.939453 L 7 15 L 9 15 L 9 11.939453 A 4.2661548 4.0576186 0 0 1 8 12.058594 A 4.2661548 4.0576186 0 0 1 7 11.939453 z " - id="path915" /> - <circle - style="fill:#d9d9d9;fill-opacity:1;stroke-width:0.97338283" - cx="8" - cy="8" - r="2.9201488" - id="circle1517" /> +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<path d="m6 0v4.4199a4.2662 4.0576 0 0 0 -1.709 1.5801h-4.291v4h4.2949a4.2662 4.0576 0 0 0 1.7051 1.582v4.418h4v-4.4199a4.2662 4.0576 0 0 0 1.709 -1.5801h4.291v-4h-4.2949a4.2662 4.0576 0 0 0 -1.7051 -1.582v-4.418h-4z" fill-opacity=".41077"/> +<path d="m7 1v3.0605a4.2662 4.0576 0 0 1 1 -0.11914 4.2662 4.0576 0 0 1 1 0.11914v-3.0605h-2zm1 4.0801a2.9201 2.9201 0 0 0 -2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 -2.9199 2.9201 2.9201 0 0 0 -2.9199 -2.9199zm-7 1.9199v2h2.8691a4.2662 4.0576 0 0 1 -0.13477 -1 4.2662 4.0576 0 0 1 0.13672 -1h-2.8711zm11.131 0a4.2662 4.0576 0 0 1 0.13477 1 4.2662 4.0576 0 0 1 -0.13672 1h2.8711v-2h-2.8691zm-5.1309 4.9395v3.0605h2v-3.0605a4.2662 4.0576 0 0 1 -1 0.11914 4.2662 4.0576 0 0 1 -1 -0.11914z" fill="#d9d9d9"/> </svg> diff --git a/editor/icons/icon_insert_after.svg b/editor/icons/icon_insert_after.svg new file mode 100644 index 0000000000..4696a2fc22 --- /dev/null +++ b/editor/icons/icon_insert_after.svg @@ -0,0 +1,7 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<circle cx="4" cy="12" r="2" fill="none"/> +<path d="m11.99 0.99023a1.0001 1.0001 0 0 0 -0.69726 1.7168l0.29297 0.29297h-2.5859v2h2.5859l-0.29297 0.29297a1.0001 1.0001 0 1 0 1.4141 1.4141l2-2a1.0001 1.0001 0 0 0 0 -1.4141l-2-2a1.0001 1.0001 0 0 0 -0.7168 -0.30273zm-8.9902 0.0097656c-1.108 0-2 0.892-2 2v2c0 1.108 0.892 2 2 2h2c1.108 0 2-0.892 2-2v-2c0-1.108-0.892-2-2-2h-2z" fill="#e0e0e0"/> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m11 9v2h-2v2h2v2h2v-2h2v-2h-2v-2z" fill="#84ffb1"/> +</g> +</svg> diff --git a/editor/icons/icon_insert_before.svg b/editor/icons/icon_insert_before.svg new file mode 100644 index 0000000000..eb85144214 --- /dev/null +++ b/editor/icons/icon_insert_before.svg @@ -0,0 +1,7 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<circle cx="4" cy="12" r="2" fill="none"/> +<path d="m4.0096 0.99023a1.0001 1.0001 0 0 1 0.69726 1.7168l-0.29297 0.29297h2.5859v2h-2.5859l0.29297 0.29297a1.0001 1.0001 0 1 1 -1.4141 1.4141l-2-2a1.0001 1.0001 0 0 1 0 -1.4141l2-2a1.0001 1.0001 0 0 1 0.7168 -0.30273zm8.9902 0.0097656c1.108 0 2 0.892 2 2v2c0 1.108-0.892 2-2 2h-2c-1.108 0-2-0.892-2-2v-2c0-1.108 0.892-2 2-2z" fill="#e0e0e0"/> +<g transform="translate(0 -1036.4)"> +<path transform="translate(0 1036.4)" d="m11 9v2h-2v2h2v2h2v-2h2v-2h-2v-2z" fill="#84ffb1"/> +</g> +</svg> diff --git a/editor/icons/icon_key_hover.svg b/editor/icons/icon_key_hover.svg new file mode 100644 index 0000000000..4a3fab4754 --- /dev/null +++ b/editor/icons/icon_key_hover.svg @@ -0,0 +1,5 @@ +<svg width="8" height="8" version="1.1" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1044.4)"> +<rect transform="rotate(-45)" x="-741.53" y="741.08" width="6.1027" height="6.1027" ry=".76286" fill="#fff"/> +</g> +</svg> diff --git a/editor/icons/icon_key_selected.svg b/editor/icons/icon_key_selected.svg new file mode 100644 index 0000000000..c73d31981d --- /dev/null +++ b/editor/icons/icon_key_selected.svg @@ -0,0 +1,5 @@ +<svg width="8" height="8" version="1.1" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg"> +<g transform="translate(0 -1044.4)"> +<rect transform="rotate(-45)" x="-741.53" y="741.08" width="6.1027" height="6.1027" ry=".76286" fill="#84c2ff"/> +</g> +</svg> diff --git a/editor/icons/icon_move_down.svg b/editor/icons/icon_move_down.svg index 466fa10205..70c5abf9e8 100644 --- a/editor/icons/icon_move_down.svg +++ b/editor/icons/icon_move_down.svg @@ -1,5 +1,3 @@ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path d="m7.9964 1051.4a1.0002 1.0001 0 0 1 -0.77738 -0.377l-4.0002-5a1.0001 1 0 0 1 0.15626 -1.4043 1.0001 1 0 0 1 1.4063 0.1563l2.2189 2.7734v-5.1484a1.0001 1 0 0 1 1.0001 -1 1.0001 1 0 0 1 1.0001 1v5.1484l2.2189-2.7734a1.0001 1 0 0 1 1.4063 -0.1563 1.0001 1 0 0 1 0.15626 1.4043l-4.0002 5a1.0002 1.0001 0 0 1 -0.7852 0.377zm0.00391-12a1.0001 1 0 0 1 -1.0001 -1 1.0001 1 0 0 1 1.0001 -1 1.0001 1 0 0 1 1.0001 1 1.0001 1 0 0 1 -1.0001 1z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" fill-opacity=".99608" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> -</g> +<path d="m6 1a1.0001 1.0001 0 1 0 0 2h4a1.0001 1.0001 0 1 0 0 -2zm2 4c-0.55231 0-1 0.4477-1 1v5.1484l-2.2188-2.7734c-0.34504-0.4317-0.97482-0.50165-1.4062-0.15625-0.4305 0.3449-0.5004 0.9732-0.15625 1.4043l4 5c0.18868 0.2369 0.4745 0.37695 0.77734 0.37695 0.30559 9e-4 0.59477-0.13795 0.78516-0.37695l4-5c0.34415-0.4311 0.27424-1.0594-0.15625-1.4043-0.43143-0.3454-1.0612-0.27545-1.4062 0.15625l-2.2188 2.7734v-5.1484c0-0.5523-0.44769-1-1-1z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" fill-opacity=".99608" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> </svg> diff --git a/editor/icons/icon_move_left.svg b/editor/icons/icon_move_left.svg new file mode 100644 index 0000000000..bab817bfdf --- /dev/null +++ b/editor/icons/icon_move_left.svg @@ -0,0 +1,3 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<path d="m15 10a1.0001 1.0001 0 1 1 -2 0v-4a1.0001 1.0001 0 1 1 2 0zm-4-2c0 0.55231-0.4477 1-1 1h-5.1484l2.7734 2.2188c0.4317 0.34504 0.50165 0.97482 0.15625 1.4062-0.3449 0.4305-0.9732 0.5004-1.4043 0.15625l-5-4c-0.2369-0.18868-0.37695-0.4745-0.37695-0.77734-9e-4 -0.30559 0.13795-0.59477 0.37695-0.78516l5-4c0.4311-0.34415 1.0594-0.27424 1.4043 0.15625 0.3454 0.43143 0.27545 1.0612-0.15625 1.4062l-2.7734 2.2188h5.1484c0.5523 0 1 0.44769 1 1z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" fill-opacity=".99608" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> +</svg> diff --git a/editor/icons/icon_move_right.svg b/editor/icons/icon_move_right.svg new file mode 100644 index 0000000000..7721633de5 --- /dev/null +++ b/editor/icons/icon_move_right.svg @@ -0,0 +1,3 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> +<path d="m1 10a1.0001 1.0001 0 1 0 2 0v-4a1.0001 1.0001 0 1 0 -2 0zm4-2c0 0.55231 0.4477 1 1 1h5.1484l-2.7734 2.2188c-0.4317 0.34504-0.50165 0.97482-0.15625 1.4062 0.3449 0.4305 0.9732 0.5004 1.4043 0.15625l5-4c0.2369-0.18868 0.37695-0.4745 0.37695-0.77734 9e-4 -0.30559-0.13795-0.59477-0.37695-0.78516l-5-4c-0.4311-0.34415-1.0594-0.27424-1.4043 0.15625-0.3454 0.43143-0.27545 1.0612 0.15625 1.4062l2.7734 2.2188h-5.1484c-0.5523 0-1 0.44769-1 1z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" fill-opacity=".99608" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> +</svg> diff --git a/editor/icons/icon_move_up.svg b/editor/icons/icon_move_up.svg index 6e148216d2..06bb26fad3 100644 --- a/editor/icons/icon_move_up.svg +++ b/editor/icons/icon_move_up.svg @@ -1,5 +1,3 @@ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path d="m7.9964 1037.4a1.0002 1.0001 0 0 0 -0.77739 0.377l-4.0002 5a1.0001 1 0 0 0 0.15626 1.4043 1.0001 1 0 0 0 1.4063 -0.1563l2.2189-2.7734v5.1484a1.0001 1 0 0 0 1.0001 1 1.0001 1 0 0 0 1.0001 -1v-5.1484l2.2189 2.7734a1.0001 1 0 0 0 1.4063 0.1563 1.0001 1 0 0 0 0.15626 -1.4043l-4.0002-5a1.0002 1.0001 0 0 0 -0.7852 -0.377zm0.00391 12a1.0001 1 0 0 0 -1.0001 1 1.0001 1 0 0 0 1.0001 1 1.0001 1 0 0 0 1.0001 -1 1.0001 1 0 0 0 -1.0001 -1z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" fill-opacity=".99608" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> -</g> +<path d="m6 15a1.0001 1.0001 0 1 1 0 -2h4a1.0001 1.0001 0 1 1 0 2zm2-4c-0.55231 0-1-0.4477-1-1v-5.1484l-2.2188 2.7734c-0.34504 0.4317-0.97482 0.50165-1.4062 0.15625-0.4305-0.3449-0.5004-0.9732-0.15625-1.4043l4-5c0.18868-0.2369 0.4745-0.37695 0.77734-0.37695 0.30559-9e-4 0.59477 0.13795 0.78516 0.37695l4 5c0.34415 0.4311 0.27424 1.0594-0.15625 1.4043-0.43143 0.3454-1.0612 0.27545-1.4062-0.15625l-2.2188-2.7734v5.1484c0 0.5523-0.44769 1-1 1z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" fill-opacity=".99608" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> </svg> diff --git a/editor/icons/icon_paint_vertex.svg b/editor/icons/icon_paint_vertex.svg new file mode 100644 index 0000000000..b53e005dae --- /dev/null +++ b/editor/icons/icon_paint_vertex.svg @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + 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="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg4" + sodipodi:docname="icon_paint_vertex.svg" + inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"> + <metadata + id="metadata10"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs8" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="3066" + inkscape:window-height="1689" + id="namedview6" + showgrid="false" + inkscape:zoom="14.75" + inkscape:cx="8" + inkscape:cy="8" + inkscape:window-x="134" + inkscape:window-y="55" + inkscape:window-maximized="1" + inkscape:current-layer="svg4" /> + <ellipse + style="fill:#ffffff" + id="path12" + cx="8.3728809" + cy="8.1694918" + rx="6.6779661" + ry="6.0677967" /> +</svg> diff --git a/editor/icons/icon_panel.svg b/editor/icons/icon_panel.svg index aebf885e7c..7bd3db7c09 100644 --- a/editor/icons/icon_panel.svg +++ b/editor/icons/icon_panel.svg @@ -1,5 +1,3 @@ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path transform="translate(0 1036.4)" d="m3 1c-1.1046 0-2 0.89543-2 2v10c0 1.1046 0.89543 2 2 2h10c1.1046 0 2-0.89543 2-2v-10c0-1.1046-0.89543-2-2-2z" fill="#a5efac"/> -</g> +<path d="m3 1c-1.1046 0-2 0.89543-2 2v10c0 1.1046 0.89543 2 2 2h10c1.1046 0 2-0.89543 2-2v-10c0-1.1046-0.89543-2-2-2z" fill="#a5efac"/> </svg> diff --git a/editor/icons/icon_panel_container.svg b/editor/icons/icon_panel_container.svg index a52493b665..df8a2c0a0e 100644 --- a/editor/icons/icon_panel_container.svg +++ b/editor/icons/icon_panel_container.svg @@ -1,5 +1,3 @@ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> -<g transform="translate(0 -1036.4)"> -<path transform="translate(0 1036.4)" d="m3 1c-1.1046 0-2 0.89543-2 2v10c0 1.1046 0.89543 2 2 2h10c1.1046 0 2-0.89543 2-2v-10c0-1.1046-0.89543-2-2-2zm0 2h10v10h-10z" fill="#a5efac"/> -</g> +<path d="m3 1c-1.1046 0-2 0.89543-2 2v10c0 1.1046 0.89543 2 2 2h10c1.1046 0 2-0.89543 2-2v-10c0-1.1046-0.89543-2-2-2zm0 2h10v10h-10z" fill="#a5efac"/> </svg> diff --git a/editor/icons/icon_translation.svg b/editor/icons/icon_translation.svg index 81abe5070e..fa6d7e1ff2 100644 --- a/editor/icons/icon_translation.svg +++ b/editor/icons/icon_translation.svg @@ -1,5 +1,5 @@ <svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> <g transform="translate(0 -1036.4)"> -<path transform="translate(0 1036.4)" d="m2 1v14h10a2 2 0 0 0 2 -2v-10a2 2 0 0 0 -2 -2v5 2l-2-2-2 2v-2-5h-6z" fill="#e0e0e0"/> +<path transform="translate(0 1036.4)" d="m4 1c-1.645 0-3 1.355-3 3s1.355 3 3 3c0.46079 0 0.89328-0.11549 1.2852-0.30469 0.18147 0.1867 0.43274 0.30469 0.71484 0.30469 0.554 0 1-0.446 1-1v-2-2c0-0.554-0.446-1-1-1-0.28152 0-0.53345 0.11683-0.71484 0.30273-0.39187-0.1892-0.82436-0.30273-1.2852-0.30273zm0 2c0.56412 0 1 0.4359 1 1s-0.43588 1-1 1-1-0.4359-1-1 0.43588-1 1-1zm6.8867 3.5293l-1.7891 0.89453 0.28906 0.57617h-2.3867v2h0.82031c0.13264 0.9292 0.4994 1.8938 1.1992 2.7305-0.61509 0.163-1.3569 0.26523-2.2656 0.26953l0.0097657 2c1.6777-0.01 3.0414-0.31328 4.1113-0.83398 1.07 0.5208 2.4336 0.82608 4.1113 0.83398l0.009766-2c-0.90873 0-1.6505-0.10653-2.2656-0.26953 0.7-0.8367 1.068-1.8013 1.2012-2.7305h1.0684v-2h-3.3789l-0.73438-1.4707zm-1.0234 3.4707h2.0234c-0.12578 0.5801-0.37537 1.147-0.83594 1.623-0.05313 0.055-0.11651 0.10676-0.17578 0.16016-0.05927-0.053-0.12265-0.10516-0.17578-0.16016-0.46056-0.476-0.71015-1.0429-0.83594-1.623z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/> </g> </svg> diff --git a/editor/icons/icon_unpaint_vertex.svg b/editor/icons/icon_unpaint_vertex.svg new file mode 100644 index 0000000000..a4633ee80b --- /dev/null +++ b/editor/icons/icon_unpaint_vertex.svg @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + 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="16" + height="16" + version="1.1" + viewBox="0 0 16 16" + id="svg4" + sodipodi:docname="icon_unpaint_vertex.svg" + inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"> + <metadata + id="metadata10"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs8" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="3066" + inkscape:window-height="1689" + id="namedview6" + showgrid="false" + inkscape:zoom="14.75" + inkscape:cx="8" + inkscape:cy="8" + inkscape:window-x="134" + inkscape:window-y="55" + inkscape:window-maximized="1" + inkscape:current-layer="svg4" /> + <ellipse + style="fill:#000000" + id="path12" + cx="8.3728809" + cy="8.1694918" + rx="6.6779661" + ry="6.0677967" /> +</svg> diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 8119b84b7e..beaa8d9600 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -295,7 +295,7 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String case COMPRESS_VIDEO_RAM: { Ref<Image> image = p_image->duplicate(); - image->generate_mipmaps(); + image->generate_mipmaps(p_force_normal); if (p_force_rgbe && image->get_format() >= Image::FORMAT_R8 && image->get_format() <= Image::FORMAT_RGBE9995) { image->convert(Image::FORMAT_RGBE9995); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 281c9028f2..b387972558 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -459,6 +459,12 @@ void AnimationPlayerEditor::_animation_remove_confirmed() { Ref<Animation> anim = player->get_animation(current); undo_redo->create_action(TTR("Remove Animation")); + if (player->get_autoplay() == current) { + undo_redo->add_do_method(player, "set_autoplay", ""); + undo_redo->add_undo_method(player, "set_autoplay", current); + // Avoid having the autoplay icon linger around if there is only one animation in the player + undo_redo->add_do_method(this, "_animation_player_changed", player); + } undo_redo->add_do_method(player, "remove_animation", current); undo_redo->add_undo_method(player, "add_animation", current, anim); undo_redo->add_do_method(this, "_animation_player_changed", player); @@ -491,7 +497,7 @@ void AnimationPlayerEditor::_animation_name_edited() { String new_name = name->get_text(); if (new_name == "" || new_name.find(":") != -1 || new_name.find("/") != -1) { - error_dialog->set_text(TTR("ERROR: Invalid animation name!")); + error_dialog->set_text(TTR("Invalid animation name!")); error_dialog->popup_centered_minsize(); return; } @@ -502,7 +508,7 @@ void AnimationPlayerEditor::_animation_name_edited() { } if (player->has_animation(new_name)) { - error_dialog->set_text(TTR("ERROR: Animation name already exists!")); + error_dialog->set_text(TTR("Animation name already exists!")); error_dialog->popup_centered_minsize(); return; } @@ -811,6 +817,8 @@ void AnimationPlayerEditor::_update_player() { play_bw->set_disabled(animlist.size() == 0); play_bw_from->set_disabled(animlist.size() == 0); play_from->set_disabled(animlist.size() == 0); + frame->set_editable(animlist.size() != 0); + animation->set_disabled(animlist.size() == 0); autoplay->set_disabled(animlist.size() == 0); duplicate_anim->set_disabled(animlist.size() == 0); rename_anim->set_disabled(animlist.size() == 0); @@ -820,6 +828,7 @@ void AnimationPlayerEditor::_update_player() { save_anim->set_disabled(animlist.size() == 0); tool_anim->set_disabled(player == NULL); onion_skinning->set_disabled(player == NULL); + pin->set_disabled(player == NULL); int active_idx = -1; for (List<StringName>::Element *E = animlist.front(); E; E = E->next()) { @@ -1088,7 +1097,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { case TOOL_COPY_ANIM: { if (!animation->get_item_count()) { - error_dialog->set_text(TTR("ERROR: No animation to copy!")); + error_dialog->set_text(TTR("No animation to copy!")); error_dialog->popup_centered_minsize(); return; } @@ -1103,7 +1112,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { Ref<Animation> anim = EditorSettings::get_singleton()->get_resource_clipboard(); if (!anim.is_valid()) { - error_dialog->set_text(TTR("ERROR: No animation resource on clipboard!")); + error_dialog->set_text(TTR("No animation resource on clipboard!")); error_dialog->popup_centered_minsize(); return; } @@ -1134,7 +1143,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { case TOOL_EDIT_RESOURCE: { if (!animation->get_item_count()) { - error_dialog->set_text(TTR("ERROR: No animation to edit!")); + error_dialog->set_text(TTR("No animation to edit!")); error_dialog->popup_centered_minsize(); return; } @@ -1611,7 +1620,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay hb->add_child(load_anim); save_anim = memnew(MenuButton); - save_anim->set_tooltip(TTR("Save the current animation")); + save_anim->set_tooltip(TTR("Save the current animation.")); save_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save", TTR("Save")), ANIM_SAVE); save_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save_as", TTR("Save As")), ANIM_SAVE_AS); save_anim->set_focus_mode(Control::FOCUS_NONE); @@ -1691,6 +1700,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay pin = memnew(ToolButton); pin->set_toggle_mode(true); + pin->set_tooltip(TTR("Pin AnimationPlayer")); hb->add_child(pin); resource_edit_anim = memnew(Button); @@ -1718,7 +1728,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay error_dialog = memnew(ConfirmationDialog); error_dialog->get_ok()->set_text(TTR("Close")); - error_dialog->set_text(TTR("Error!")); + error_dialog->set_title(TTR("Error!")); add_child(error_dialog); name_dialog->connect("confirmed", this, "_animation_name_edited"); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 23cdde299c..05833704d1 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -639,7 +639,7 @@ const char *EditorAssetLibrary::support_key[SUPPORT_MAX] = { void EditorAssetLibrary::_select_author(int p_id) { - //opemn author window + // Open author window } void EditorAssetLibrary::_select_category(int p_id) { @@ -659,16 +659,6 @@ void EditorAssetLibrary::_select_category(int p_id) { void EditorAssetLibrary::_select_asset(int p_id) { _api_request("asset/" + itos(p_id), REQUESTING_ASSET); - - /* - if (description) { - memdelete(description); - } - - - description = memnew( EditorAssetLibraryItemDescription ); - add_child(description); - description->popup_centered_minsize();*/ } void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByteArray &p_data, int p_queue_id) { @@ -774,7 +764,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons _image_update(p_code == HTTPClient::RESPONSE_NOT_MODIFIED, true, p_data, p_queue_id); } else { - WARN_PRINTS("Error getting PNG file for asset id " + itos(image_queue[p_queue_id].asset_id)); + WARN_PRINTS("Error getting PNG file from URL: " + image_queue[p_queue_id].image_url); Object *obj = ObjectDB::get_instance(image_queue[p_queue_id].target); if (obj) { obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("ErrorSign", "EditorIcons")); diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index 89b79d7cfb..69a65dd3dc 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -241,7 +241,6 @@ class EditorAssetLibrary : public PanelContainer { bool active; int queue_id; - int asset_id; ImageType image_type; int image_index; String image_url; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index bb94aee462..ff38e12250 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -457,7 +457,7 @@ Rect2 CanvasItemEditor::_get_encompassing_rect(const Node *p_node) { return rect; } -void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int limit, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { +void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { if (!p_node) return; if (Object::cast_to<Viewport>(p_node)) @@ -466,37 +466,73 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no const real_t grab_distance = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node); - Node *scene = editor->get_edited_scene(); - if (!(p_node != scene && p_node->get_owner() != scene && (!scene->is_editable_instance(p_node) || p_node->has_meta("_edit_lock_")))) { + bool locked = p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_"); + + if (!locked) { for (int i = p_node->get_child_count() - 1; i >= 0; i--) { if (canvas_item && !canvas_item->is_set_as_toplevel()) { - _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, limit, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); } else { CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node); - _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, limit, Transform2D(), cl ? cl->get_transform() : p_canvas_xform); + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, Transform2D(), cl ? cl->get_transform() : p_canvas_xform); } - if (limit != 0 && r_items.size() >= limit) + if (p_limit != 0 && r_items.size() >= p_limit) return; } - } - - if (canvas_item && canvas_item->is_visible_in_tree() && (canvas_item->get_owner() != editor->get_edited_scene() || !canvas_item->has_meta("_edit_lock_"))) { - Transform2D xform = (p_parent_xform * p_canvas_xform * canvas_item->get_transform()).affine_inverse(); - const real_t local_grab_distance = xform.basis_xform(Vector2(grab_distance, 0)).length(); - if (canvas_item->_edit_is_selected_on_click(xform.xform(p_pos), local_grab_distance)) { - Node2D *node = Object::cast_to<Node2D>(canvas_item); - _SelectResult res; - res.item = canvas_item; - res.z_index = node ? node->get_z_index() : 0; - res.has_z = node; - r_items.push_back(res); + if (canvas_item && canvas_item->is_visible_in_tree()) { + Transform2D xform = (p_parent_xform * p_canvas_xform * canvas_item->get_transform()).affine_inverse(); + const real_t local_grab_distance = xform.basis_xform(Vector2(grab_distance, 0)).length(); + if (canvas_item->_edit_is_selected_on_click(xform.xform(p_pos), local_grab_distance)) { + Node2D *node = Object::cast_to<Node2D>(canvas_item); + + _SelectResult res; + res.item = canvas_item; + res.z_index = node ? node->get_z_index() : 0; + res.has_z = node; + r_items.push_back(res); + } } } return; } +void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit) { + + Node *scene = editor->get_edited_scene(); + + _find_canvas_items_at_pos(p_pos, scene, r_items, p_limit); + + //Remove invalid results + for (int i = 0; i < r_items.size(); i++) { + Node *node = r_items[i].item; + + // Make sure the selected node is in the current scene + while (node && node != scene && node->get_owner() != scene) { + node = node->get_parent(); + }; + + // Replace the node by the group if grouped + CanvasItem *canvas_item = Object::cast_to<CanvasItem>(node); + while (node && node != scene) { + CanvasItem *canvas_item_tmp = Object::cast_to<CanvasItem>(node); + if (canvas_item_tmp && node->has_meta("_edit_group_")) { + canvas_item = canvas_item_tmp; + } + node = node->get_parent(); + } + + //Remove the item if invalid + if (!canvas_item || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner()))) { + r_items.remove(i); + i--; + } else { + r_items[i].item = canvas_item; + } + } +} + void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { if (!p_node) return; @@ -628,7 +664,7 @@ void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items if (bone && bone->has_meta("_edit_bone_")) { // Check if we have an IK chain List<Node2D *> bone_ik_list; - bool ik_found; + bool ik_found = false; bone = Object::cast_to<Node2D>(bone->get_parent()); while (bone) { bone_ik_list.push_back(bone); @@ -896,7 +932,7 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { - if (b->get_button_index() == BUTTON_WHEEL_DOWN) { + if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_DOWN) { // Scroll or pan down if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { view_offset.y += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); @@ -908,7 +944,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { return true; } - if (b->get_button_index() == BUTTON_WHEEL_UP) { + if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_UP) { // Scroll or pan up if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { view_offset.y -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); @@ -920,7 +956,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { return true; } - if (b->get_button_index() == BUTTON_WHEEL_LEFT) { + if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_LEFT) { // Pan left if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { view_offset.x -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); @@ -930,7 +966,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { } } - if (b->get_button_index() == BUTTON_WHEEL_RIGHT) { + if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_RIGHT) { // Pan right if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) { view_offset.x += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor(); @@ -939,31 +975,60 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { return true; } } - } - Ref<InputEventMouseMotion> m = p_event; - if (m.is_valid()) { if (drag_type == DRAG_NONE) { - if (((m->get_button_mask() & BUTTON_MASK_LEFT) && tool == TOOL_PAN) || - (m->get_button_mask() & BUTTON_MASK_MIDDLE) || - ((m->get_button_mask() & BUTTON_MASK_LEFT) && Input::get_singleton()->is_key_pressed(KEY_SPACE)) || - (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") && Input::get_singleton()->is_key_pressed(KEY_SPACE))) { + if (b->is_pressed() && + (b->get_button_index() == BUTTON_MIDDLE || + (b->get_button_index() == BUTTON_LEFT && tool == TOOL_PAN) || + (b->get_button_index() == BUTTON_LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) { // Pan the viewport - Point2i relative; - if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) { - relative = Input::get_singleton()->warp_mouse_motion(m, viewport->get_global_rect()); - } else { - relative = m->get_relative(); + drag_type = DRAG_PAN; + } + } + + if (drag_type == DRAG_PAN) { + if (!b->is_pressed()) { + // Stop panning the viewport (for any mouse button press) + drag_type = DRAG_NONE; + } + } + } + + Ref<InputEventKey> k = p_event; + if (k.is_valid()) { + if (k->get_scancode() == KEY_SPACE && EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning")) { + if (drag_type == DRAG_NONE) { + if (k->is_pressed() && !k->is_echo()) { + //Pan the viewport + drag_type = DRAG_PAN; + } + } else if (drag_type == DRAG_PAN) { + if (!k->is_pressed()) { + // Stop panning the viewport (for any mouse button press) + drag_type = DRAG_NONE; } - view_offset.x -= relative.x / zoom; - view_offset.y -= relative.y / zoom; - _update_scrollbars(); - viewport->update(); - return true; } } } + Ref<InputEventMouseMotion> m = p_event; + if (m.is_valid()) { + if (drag_type == DRAG_PAN) { + // Pan the viewport + Point2i relative; + if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) { + relative = Input::get_singleton()->warp_mouse_motion(m, viewport->get_global_rect()); + } else { + relative = m->get_relative(); + } + view_offset.x -= relative.x / zoom; + view_offset.y -= relative.y / zoom; + _update_scrollbars(); + viewport->update(); + return true; + } + } + Ref<InputEventMagnifyGesture> magnify_gesture = p_event; if (magnify_gesture.is_valid()) { // Zoom gesture @@ -1675,17 +1740,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { // Popup the selection menu list Point2 click = transform.affine_inverse().xform(b->get_position()); - Node *scene = editor->get_edited_scene(); - - _find_canvas_items_at_pos(click, scene, selection_results); - for (int i = 0; i < selection_results.size(); i++) { - CanvasItem *item = selection_results[i].item; - if (item != scene && item->get_owner() != scene && !scene->is_editable_instance(item->get_owner())) { - //invalid result - selection_results.remove(i); - i--; - } - } + _get_canvas_items_at_pos(click, selection_results); if (selection_results.size() == 1) { CanvasItem *item = selection_results[0].item; @@ -1735,7 +1790,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { // Find the item to select CanvasItem *canvas_item = NULL; Vector<_SelectResult> selection; - _find_canvas_items_at_pos(click, scene, selection, editor_selection->get_selection().empty() ? 1 : 0); + _get_canvas_items_at_pos(click, selection, editor_selection->get_selection().empty() ? 1 : 0); for (int i = 0; i < selection.size(); i++) { if (editor_selection->is_selected(selection[i].item)) { @@ -1754,23 +1809,6 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { if (!selection.empty()) canvas_item = selection[0].item; - // Check if the canvas item is in a group, and select the group instead if it is the case - Node *node = canvas_item; - while (node && node != scene) { - CanvasItem *canvas_item_tmp = Object::cast_to<CanvasItem>(node); - if (canvas_item_tmp && node->has_meta("_edit_group_")) { - canvas_item = canvas_item_tmp; - } - node = node->get_parent(); - } - - // Make sure the selected node is in the current scene - node = canvas_item; - while (node && ((node != scene && node->get_owner() != scene) || !node->is_class("CanvasItem"))) { - node = node->get_parent(); - }; - canvas_item = Object::cast_to<CanvasItem>(node); - if (!canvas_item) { // Start a box selection if (!b->get_shift()) { @@ -1855,11 +1893,10 @@ bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) { if (m.is_valid()) { if (drag_type == DRAG_NONE && tool == TOOL_SELECT) { Point2 click = transform.affine_inverse().xform(m->get_position()); - Node *scene = editor->get_edited_scene(); //Checks if the hovered items changed, update the viewport if so Vector<_SelectResult> hovering_results_tmp; - _find_canvas_items_at_pos(click, scene, hovering_results_tmp); + _get_canvas_items_at_pos(click, hovering_results_tmp); hovering_results_tmp.sort(); bool changed = false; if (hovering_results.size() == hovering_results_tmp.size()) { @@ -1903,10 +1940,10 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { //printf("Rotate\n"); } else if ((accepted = _gui_input_move(p_event))) { //printf("Move\n"); - } else if ((accepted = _gui_input_select(p_event))) { - //printf("Selection\n"); } else if ((accepted = _gui_input_zoom_or_pan(p_event))) { //printf("Zoom or pan\n"); + } else if ((accepted = _gui_input_select(p_event))) { + //printf("Selection\n"); } if (accepted) @@ -1919,22 +1956,18 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { CursorShape c = CURSOR_ARROW; switch (drag_type) { case DRAG_NONE: - if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_MIDDLE) || Input::get_singleton()->is_key_pressed(KEY_SPACE)) { - c = CURSOR_DRAG; - } else { - switch (tool) { - case TOOL_MOVE: - c = CURSOR_MOVE; - break; - case TOOL_EDIT_PIVOT: - c = CURSOR_CROSS; - break; - case TOOL_PAN: - c = CURSOR_DRAG; - break; - default: - break; - } + switch (tool) { + case TOOL_MOVE: + c = CURSOR_MOVE; + break; + case TOOL_EDIT_PIVOT: + c = CURSOR_CROSS; + break; + case TOOL_PAN: + c = CURSOR_DRAG; + break; + default: + break; } break; case DRAG_LEFT: @@ -1956,6 +1989,8 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { case DRAG_ALL: c = CURSOR_MOVE; break; + case DRAG_PAN: + c = CURSOR_DRAG; default: break; } @@ -3183,7 +3218,7 @@ void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) { } void CanvasItemEditor::_button_zoom_minus() { - _zoom_on_position(zoom / 2.0, viewport_scrollable->get_size() / 2.0); + _zoom_on_position(zoom / 1.5, viewport_scrollable->get_size() / 2.0); } void CanvasItemEditor::_button_zoom_reset() { @@ -3191,7 +3226,7 @@ void CanvasItemEditor::_button_zoom_reset() { } void CanvasItemEditor::_button_zoom_plus() { - _zoom_on_position(zoom * 2.0, viewport_scrollable->get_size() / 2.0); + _zoom_on_position(zoom * 1.5, viewport_scrollable->get_size() / 2.0); } void CanvasItemEditor::_button_toggle_snap(bool p_status) { @@ -4064,16 +4099,19 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { zoom_minus = memnew(ToolButton); zoom_hb->add_child(zoom_minus); zoom_minus->connect("pressed", this, "_button_zoom_minus"); + zoom_minus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_minus", TTR("Zoom out"), KEY_MASK_CMD | KEY_MINUS)); zoom_minus->set_focus_mode(FOCUS_NONE); zoom_reset = memnew(ToolButton); zoom_hb->add_child(zoom_reset); zoom_reset->connect("pressed", this, "_button_zoom_reset"); + zoom_reset->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_reset", TTR("Zoom reset"), KEY_MASK_CMD | KEY_0)); zoom_reset->set_focus_mode(FOCUS_NONE); zoom_plus = memnew(ToolButton); zoom_hb->add_child(zoom_plus); zoom_plus->connect("pressed", this, "_button_zoom_plus"); + zoom_plus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_plus", TTR("Zoom in"), KEY_MASK_CMD | KEY_PLUS)); zoom_plus->set_focus_mode(FOCUS_NONE); updating_scroll = false; diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 5ca8a37610..373a4b799e 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -194,7 +194,8 @@ class CanvasItemEditor : public VBoxContainer { DRAG_V_GUIDE, DRAG_H_GUIDE, DRAG_DOUBLE_GUIDE, - DRAG_KEY_MOVE + DRAG_KEY_MOVE, + DRAG_PAN }; EditorSelection *editor_selection; @@ -336,7 +337,9 @@ class CanvasItemEditor : public VBoxContainer { Ref<ShortCut> multiply_grid_step_shortcut; Ref<ShortCut> divide_grid_step_shortcut; - void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit = 0); + void _find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); bool _select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append); diff --git a/editor/plugins/collision_polygon_editor_plugin.cpp b/editor/plugins/collision_polygon_editor_plugin.cpp index 4410242d9c..e837359d0c 100644 --- a/editor/plugins/collision_polygon_editor_plugin.cpp +++ b/editor/plugins/collision_polygon_editor_plugin.cpp @@ -33,10 +33,12 @@ #include "canvas_item_editor_plugin.h" #include "editor/editor_settings.h" #include "os/file_access.h" +#include "os/input.h" +#include "os/keyboard.h" #include "scene/3d/camera.h" #include "spatial_editor_plugin.h" -void CollisionPolygonEditor::_notification(int p_what) { +void Polygon3DEditor::_notification(int p_what) { switch (p_what) { @@ -53,15 +55,15 @@ void CollisionPolygonEditor::_notification(int p_what) { return; } - if (node->get_depth() != prev_depth) { + if (_get_depth() != prev_depth) { _polygon_draw(); - prev_depth = node->get_depth(); + prev_depth = _get_depth(); } } break; } } -void CollisionPolygonEditor::_node_removed(Node *p_node) { +void Polygon3DEditor::_node_removed(Node *p_node) { if (p_node == node) { node = NULL; @@ -72,7 +74,7 @@ void CollisionPolygonEditor::_node_removed(Node *p_node) { } } -void CollisionPolygonEditor::_menu_option(int p_option) { +void Polygon3DEditor::_menu_option(int p_option) { switch (p_option) { @@ -91,10 +93,10 @@ void CollisionPolygonEditor::_menu_option(int p_option) { } } -void CollisionPolygonEditor::_wip_close() { +void Polygon3DEditor::_wip_close() { undo_redo->create_action(TTR("Create Poly3D")); - undo_redo->add_undo_method(node, "set_polygon", node->get_polygon()); + undo_redo->add_undo_method(node, "set_polygon", node->call("get_polygon")); undo_redo->add_do_method(node, "set_polygon", wip); undo_redo->add_do_method(this, "_polygon_draw"); undo_redo->add_undo_method(this, "_polygon_draw"); @@ -107,14 +109,14 @@ void CollisionPolygonEditor::_wip_close() { undo_redo->commit_action(); } -bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { +bool Polygon3DEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { if (!node) return false; Transform gt = node->get_global_transform(); Transform gi = gt.affine_inverse(); - float depth = node->get_depth() * 0.5; + float depth = _get_depth() * 0.5; Vector3 n = gt.basis.get_axis(2).normalized(); Plane p(gt.origin + n * depth, n); @@ -135,9 +137,11 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R Vector2 cpoint(spoint.x, spoint.y); - cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); + //DO NOT snap here, it's confusing in 3D for adding points. + //Let the snap happen when the point is being moved, instead. + //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); - Vector<Vector2> poly = node->get_polygon(); + Vector<Vector2> poly = node->call("get_polygon"); //first check if a point is to be added (segment split) real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8); @@ -154,6 +158,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R wip.push_back(cpoint); wip_active = true; edited_point_pos = cpoint; + snap_ignore = false; _polygon_draw(); edited_point = 1; return true; @@ -168,6 +173,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R wip.push_back(cpoint); edited_point = wip.size(); + snap_ignore = false; _polygon_draw(); return true; } @@ -226,8 +232,10 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R poly.insert(closest_idx + 1, cpoint); edited_point = closest_idx + 1; edited_point_pos = cpoint; - node->set_polygon(poly); + node->call("set_polygon", poly); _polygon_draw(); + snap_ignore = true; + return true; } } else { @@ -255,11 +263,14 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R edited_point = closest_idx; edited_point_pos = poly[closest_idx]; _polygon_draw(); + snap_ignore = false; return true; } } } else { + snap_ignore = false; + if (edited_point != -1) { //apply @@ -315,7 +326,6 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - if (edited_point != -1 && (wip_active || mm->get_button_mask() & BUTTON_MASK_LEFT)) { Vector2 gpoint = mm->get_position(); @@ -332,7 +342,13 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R Vector2 cpoint(spoint.x, spoint.y); - cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); + if (snap_ignore && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { + snap_ignore = false; + } + + if (!snap_ignore) { + cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); + } edited_point_pos = cpoint; _polygon_draw(); @@ -341,7 +357,16 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R return false; } -void CollisionPolygonEditor::_polygon_draw() { + +float Polygon3DEditor::_get_depth() { + + if (bool(node->call("_has_editable_3d_polygon_no_depth"))) + return 0; + + return float(node->call("get_depth")); +} + +void Polygon3DEditor::_polygon_draw() { if (!node) return; @@ -351,9 +376,9 @@ void CollisionPolygonEditor::_polygon_draw() { if (wip_active) poly = wip; else - poly = node->get_polygon(); + poly = node->call("get_polygon"); - float depth = node->get_depth() * 0.5; + float depth = _get_depth() * 0.5; imgeom->clear(); imgeom->set_material_override(line_material); @@ -464,13 +489,13 @@ void CollisionPolygonEditor::_polygon_draw() { m->surface_set_material(0, handle_material); } -void CollisionPolygonEditor::edit(Node *p_collision_polygon) { +void Polygon3DEditor::edit(Node *p_collision_polygon) { if (p_collision_polygon) { - node = Object::cast_to<CollisionPolygon>(p_collision_polygon); + node = Object::cast_to<Spatial>(p_collision_polygon); //Enable the pencil tool if the polygon is empty - if (node->get_polygon().size() == 0) { + if (Vector<Vector2>(node->call("get_polygon")).size() == 0) { _menu_option(MODE_CREATE); } wip.clear(); @@ -491,14 +516,14 @@ void CollisionPolygonEditor::edit(Node *p_collision_polygon) { } } -void CollisionPolygonEditor::_bind_methods() { +void Polygon3DEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_menu_option"), &CollisionPolygonEditor::_menu_option); - ClassDB::bind_method(D_METHOD("_polygon_draw"), &CollisionPolygonEditor::_polygon_draw); - ClassDB::bind_method(D_METHOD("_node_removed"), &CollisionPolygonEditor::_node_removed); + ClassDB::bind_method(D_METHOD("_menu_option"), &Polygon3DEditor::_menu_option); + ClassDB::bind_method(D_METHOD("_polygon_draw"), &Polygon3DEditor::_polygon_draw); + ClassDB::bind_method(D_METHOD("_node_removed"), &Polygon3DEditor::_node_removed); } -CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) { +Polygon3DEditor::Polygon3DEditor(EditorNode *p_editor) { node = NULL; editor = p_editor; @@ -543,24 +568,26 @@ CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) { m.instance(); pointsm->set_mesh(m); pointsm->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001))); + + snap_ignore = false; } -CollisionPolygonEditor::~CollisionPolygonEditor() { +Polygon3DEditor::~Polygon3DEditor() { memdelete(imgeom); } -void CollisionPolygonEditorPlugin::edit(Object *p_object) { +void Polygon3DEditorPlugin::edit(Object *p_object) { collision_polygon_editor->edit(Object::cast_to<Node>(p_object)); } -bool CollisionPolygonEditorPlugin::handles(Object *p_object) const { +bool Polygon3DEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("CollisionPolygon"); + return Object::cast_to<Spatial>(p_object) && bool(p_object->call("_is_editable_3d_polygon")); } -void CollisionPolygonEditorPlugin::make_visible(bool p_visible) { +void Polygon3DEditorPlugin::make_visible(bool p_visible) { if (p_visible) { collision_polygon_editor->show(); @@ -571,14 +598,14 @@ void CollisionPolygonEditorPlugin::make_visible(bool p_visible) { } } -CollisionPolygonEditorPlugin::CollisionPolygonEditorPlugin(EditorNode *p_node) { +Polygon3DEditorPlugin::Polygon3DEditorPlugin(EditorNode *p_node) { editor = p_node; - collision_polygon_editor = memnew(CollisionPolygonEditor(p_node)); + collision_polygon_editor = memnew(Polygon3DEditor(p_node)); SpatialEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor); collision_polygon_editor->hide(); } -CollisionPolygonEditorPlugin::~CollisionPolygonEditorPlugin() { +Polygon3DEditorPlugin::~Polygon3DEditorPlugin() { } diff --git a/editor/plugins/collision_polygon_editor_plugin.h b/editor/plugins/collision_polygon_editor_plugin.h index f1f215b092..4229808e2f 100644 --- a/editor/plugins/collision_polygon_editor_plugin.h +++ b/editor/plugins/collision_polygon_editor_plugin.h @@ -44,9 +44,9 @@ class CanvasItemEditor; -class CollisionPolygonEditor : public HBoxContainer { +class Polygon3DEditor : public HBoxContainer { - GDCLASS(CollisionPolygonEditor, HBoxContainer); + GDCLASS(Polygon3DEditor, HBoxContainer); UndoRedo *undo_redo; enum Mode { @@ -66,7 +66,7 @@ class CollisionPolygonEditor : public HBoxContainer { EditorNode *editor; Panel *panel; - CollisionPolygon *node; + Spatial *node; ImmediateGeometry *imgeom; MeshInstance *pointsm; Ref<ArrayMesh> m; @@ -78,6 +78,7 @@ class CollisionPolygonEditor : public HBoxContainer { Vector<Vector2> pre_move_edit; Vector<Vector2> wip; bool wip_active; + bool snap_ignore; float prev_depth; @@ -85,6 +86,8 @@ class CollisionPolygonEditor : public HBoxContainer { void _polygon_draw(); void _menu_option(int p_option); + float _get_depth(); + protected: void _notification(int p_what); void _node_removed(Node *p_node); @@ -93,28 +96,28 @@ protected: public: virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event); void edit(Node *p_collision_polygon); - CollisionPolygonEditor(EditorNode *p_editor); - ~CollisionPolygonEditor(); + Polygon3DEditor(EditorNode *p_editor); + ~Polygon3DEditor(); }; -class CollisionPolygonEditorPlugin : public EditorPlugin { +class Polygon3DEditorPlugin : public EditorPlugin { - GDCLASS(CollisionPolygonEditorPlugin, EditorPlugin); + GDCLASS(Polygon3DEditorPlugin, EditorPlugin); - CollisionPolygonEditor *collision_polygon_editor; + Polygon3DEditor *collision_polygon_editor; EditorNode *editor; public: virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); } - virtual String get_name() const { return "CollisionPolygon"; } + virtual String get_name() const { return "Polygon3DEditor"; } bool has_main_screen() const { return false; } virtual void edit(Object *p_object); virtual bool handles(Object *p_object) const; virtual void make_visible(bool p_visible); - CollisionPolygonEditorPlugin(EditorNode *p_node); - ~CollisionPolygonEditorPlugin(); + Polygon3DEditorPlugin(EditorNode *p_node); + ~Polygon3DEditorPlugin(); }; #endif // COLLISION_POLYGON_EDITOR_PLUGIN_H diff --git a/editor/plugins/navigation_mesh_generator.cpp b/editor/plugins/navigation_mesh_generator.cpp index 5d4e5520f4..0537c5c31f 100644 --- a/editor/plugins/navigation_mesh_generator.cpp +++ b/editor/plugins/navigation_mesh_generator.cpp @@ -280,26 +280,20 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) _build_recast_navigation_mesh(p_nav_mesh, &ep, hf, chf, cset, poly_mesh, detail_mesh, vertices, indices); - if (hf) { - rcFreeHeightField(hf); - hf = 0; - } - if (chf) { - rcFreeCompactHeightfield(chf); - chf = 0; - } - if (cset) { - rcFreeContourSet(cset); - cset = 0; - } - if (poly_mesh) { - rcFreePolyMesh(poly_mesh); - poly_mesh = 0; - } - if (detail_mesh) { - rcFreePolyMeshDetail(detail_mesh); - detail_mesh = 0; - } + rcFreeHeightField(hf); + hf = 0; + + rcFreeCompactHeightfield(chf); + chf = 0; + + rcFreeContourSet(cset); + cset = 0; + + rcFreePolyMesh(poly_mesh); + poly_mesh = 0; + + rcFreePolyMeshDetail(detail_mesh); + detail_mesh = 0; } ep.step(TTR("Done!"), 11); } diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp index 056219a575..6dde639c54 100644 --- a/editor/plugins/path_editor_plugin.cpp +++ b/editor/plugins/path_editor_plugin.cpp @@ -167,18 +167,12 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p Vector3 ofs; - if (p_cancel) { - - return; - } - if (t == 0) { - if (p_cancel) { - c->set_point_in(p_idx, p_restore); return; } + ur->create_action(TTR("Set Curve In Position")); ur->add_do_method(c.ptr(), "set_point_in", idx, c->get_point_in(idx)); ur->add_undo_method(c.ptr(), "set_point_in", idx, p_restore); @@ -186,10 +180,11 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p } else { if (p_cancel) { - c->set_point_out(idx, p_restore); + return; } + ur->create_action(TTR("Set Curve Out Position")); ur->add_do_method(c.ptr(), "set_point_out", idx, c->get_point_out(idx)); ur->add_undo_method(c.ptr(), "set_point_out", idx, p_restore); diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 3a169bd780..2815fa6406 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -35,7 +35,7 @@ #include "os/file_access.h" #include "os/input.h" #include "os/keyboard.h" - +#include "scene/2d/skeleton_2d.h" Node2D *Polygon2DEditor::_get_node() const { return node; @@ -66,6 +66,8 @@ void Polygon2DEditor::_notification(int p_what) { uv_button[UV_MODE_SCALE]->set_icon(get_icon("ToolScale", "EditorIcons")); uv_button[UV_MODE_ADD_SPLIT]->set_icon(get_icon("AddSplit", "EditorIcons")); uv_button[UV_MODE_REMOVE_SPLIT]->set_icon(get_icon("DeleteSplit", "EditorIcons")); + uv_button[UV_MODE_PAINT_WEIGHT]->set_icon(get_icon("PaintVertex", "EditorIcons")); + uv_button[UV_MODE_CLEAR_WEIGHT]->set_icon(get_icon("UnpaintVertex", "EditorIcons")); b_snap_grid->set_icon(get_icon("Grid", "EditorIcons")); b_snap_enable->set_icon(get_icon("SnapGrid", "EditorIcons")); @@ -78,31 +80,167 @@ void Polygon2DEditor::_notification(int p_what) { } } +void Polygon2DEditor::_sync_bones() { + + print_line("syncinc"); + if (!node->has_node(node->get_skeleton())) { + error->set_text(TTR("The skeleton property of the Polygon2D does not point to a Skeleton2D node")); + error->popup_centered_minsize(); + return; + } + + Node *sn = node->get_node(node->get_skeleton()); + Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(sn); + + if (!skeleton) { + error->set_text(TTR("The skeleton property of the Polygon2D does not point to a Skeleton2D node")); + error->popup_centered_minsize(); + return; + } + + Array prev_bones = node->call("_get_bones"); + node->clear_bones(); + + print_line("bones in skeleton: " + itos(skeleton->get_bone_count())); + + for (int i = 0; i < skeleton->get_bone_count(); i++) { + NodePath path = skeleton->get_path_to(skeleton->get_bone(i)); + PoolVector<float> weights; + int wc = node->get_polygon().size(); + + for (int j = 0; j < prev_bones.size(); j += 2) { + NodePath pvp = prev_bones[j]; + PoolVector<float> pv = prev_bones[j + 1]; + if (pvp == path && pv.size() == wc) { + weights = pv; + } + } + + if (weights.size() == 0) { //create them + weights.resize(node->get_polygon().size()); + PoolVector<float>::Write w = weights.write(); + for (int j = 0; j < wc; j++) { + w[j] = 0.0; + } + } + + node->add_bone(path, weights); + } + Array new_bones = node->call("_get_bones"); + + undo_redo->create_action(TTR("Sync bones")); + undo_redo->add_do_method(node, "_set_bones", new_bones); + undo_redo->add_undo_method(node, "_set_bones", prev_bones); + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->add_do_method(this, "_update_bone_list"); + undo_redo->add_undo_method(this, "_update_bone_list"); + undo_redo->commit_action(); +} + +void Polygon2DEditor::_update_bone_list() { + + NodePath selected; + while (bone_scroll_vb->get_child_count()) { + CheckBox *cb = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(0)); + if (cb && cb->is_pressed()) { + selected = cb->get_meta("bone_path"); + } + memdelete(bone_scroll_vb->get_child(0)); + } + + Ref<ButtonGroup> bg; + bg.instance(); + for (int i = 0; i < node->get_bone_count(); i++) { + CheckBox *cb = memnew(CheckBox); + NodePath np = node->get_bone_path(i); + String name; + if (np.get_name_count()) { + name = np.get_name(np.get_name_count() - 1); + } + if (name == String()) { + name = "Bone " + itos(i); + } + cb->set_text(name); + cb->set_button_group(bg); + cb->set_meta("bone_path", np); + bone_scroll_vb->add_child(cb); + + if (np == selected) + cb->set_pressed(true); + + cb->connect("pressed", this, "_bone_paint_selected", varray(i)); + } + + uv_edit_draw->update(); +} + +void Polygon2DEditor::_bone_paint_selected(int p_index) { + uv_edit_draw->update(); +} + void Polygon2DEditor::_uv_edit_mode_select(int p_mode) { - if (p_mode == 0) { + if (p_mode == 0) { //uv uv_button[UV_MODE_CREATE]->hide(); for (int i = UV_MODE_MOVE; i <= UV_MODE_SCALE; i++) { uv_button[i]->show(); } uv_button[UV_MODE_ADD_SPLIT]->hide(); uv_button[UV_MODE_REMOVE_SPLIT]->hide(); + uv_button[UV_MODE_PAINT_WEIGHT]->hide(); + uv_button[UV_MODE_CLEAR_WEIGHT]->hide(); _uv_mode(UV_MODE_EDIT_POINT); - } else if (p_mode == 1) { + bone_scroll_main_vb->hide(); + bone_paint_strength->hide(); + bone_paint_radius->hide(); + bone_paint_radius_label->hide(); + + } else if (p_mode == 1) { //poly for (int i = 0; i <= UV_MODE_SCALE; i++) { uv_button[i]->show(); } uv_button[UV_MODE_ADD_SPLIT]->hide(); uv_button[UV_MODE_REMOVE_SPLIT]->hide(); + uv_button[UV_MODE_PAINT_WEIGHT]->hide(); + uv_button[UV_MODE_CLEAR_WEIGHT]->hide(); _uv_mode(UV_MODE_EDIT_POINT); - } else { + + bone_scroll_main_vb->hide(); + bone_paint_strength->hide(); + bone_paint_radius->hide(); + bone_paint_radius_label->hide(); + + } else if (p_mode == 2) { //splits for (int i = 0; i <= UV_MODE_SCALE; i++) { uv_button[i]->hide(); } uv_button[UV_MODE_ADD_SPLIT]->show(); uv_button[UV_MODE_REMOVE_SPLIT]->show(); + uv_button[UV_MODE_PAINT_WEIGHT]->hide(); + uv_button[UV_MODE_CLEAR_WEIGHT]->hide(); _uv_mode(UV_MODE_ADD_SPLIT); + + bone_scroll_main_vb->hide(); + bone_paint_strength->hide(); + bone_paint_radius->hide(); + bone_paint_radius_label->hide(); + + } else if (p_mode == 3) { //bones´ + for (int i = 0; i <= UV_MODE_REMOVE_SPLIT; i++) { + uv_button[i]->hide(); + } + uv_button[UV_MODE_PAINT_WEIGHT]->show(); + uv_button[UV_MODE_CLEAR_WEIGHT]->show(); + _uv_mode(UV_MODE_PAINT_WEIGHT); + + bone_scroll_main_vb->show(); + bone_paint_strength->show(); + bone_paint_radius->show(); + bone_paint_radius_label->show(); + _update_bone_list(); + bone_paint_pos = Vector2(-100000, -100000); //send brush away when switching } uv_edit_draw->update(); @@ -261,6 +399,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_create = true; uv_create_uv_prev = node->get_uv(); uv_create_poly_prev = node->get_polygon(); + uv_create_bones_prev = node->call("_get_bones"); splits_prev = node->get_splits(); node->set_polygon(uv_prev); node->set_uv(uv_prev); @@ -274,6 +413,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { undo_redo->add_undo_method(node, "set_uv", uv_prev); undo_redo->add_do_method(node, "set_polygon", node->get_polygon()); undo_redo->add_undo_method(node, "set_polygon", uv_prev); + undo_redo->add_do_method(node, "clear_bones"); + undo_redo->add_undo_method(node, "_set_bones", node->call("_get_bones")); undo_redo->add_do_method(uv_edit_draw, "update"); undo_redo->add_undo_method(uv_edit_draw, "update"); undo_redo->commit_action(); @@ -419,6 +560,25 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } } + if (uv_move_current == UV_MODE_PAINT_WEIGHT || uv_move_current == UV_MODE_CLEAR_WEIGHT) { + + int bone_selected = -1; + for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) { + CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i)); + if (c && c->is_pressed()) { + bone_selected = i; + break; + } + } + + if (bone_selected != -1 && node->get_bone_weights(bone_selected).size() == uv_prev.size()) { + + prev_weights = node->get_bone_weights(bone_selected); + bone_painting = true; + bone_painting_bone = bone_selected; + } + } + } else if (uv_drag && !uv_create) { undo_redo->create_action(TTR("Transform UV Map")); @@ -435,6 +595,15 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { undo_redo->commit_action(); uv_drag = false; + } else if (bone_painting) { + + undo_redo->create_action(TTR("Paint bone weights")); + undo_redo->add_do_method(node, "set_bone_weights", bone_painting_bone, node->get_bone_weights(bone_painting_bone)); + undo_redo->add_undo_method(node, "set_bone_weights", bone_painting_bone, prev_weights); + undo_redo->add_do_method(uv_edit_draw, "update"); + undo_redo->add_undo_method(uv_edit_draw, "update"); + undo_redo->commit_action(); + bone_painting = false; } } else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) { @@ -445,6 +614,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_create = false; node->set_uv(uv_create_uv_prev); node->set_polygon(uv_create_poly_prev); + node->call("_set_bones", uv_create_bones_prev); node->set_splits(splits_prev); uv_edit_draw->update(); } else if (uv_drag) { @@ -459,6 +629,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } else if (split_create) { split_create = false; uv_edit_draw->update(); + } else if (bone_painting) { + node->set_bone_weights(bone_painting_bone, prev_weights); } } else if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) { @@ -569,10 +741,40 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } } break; } + + if (bone_painting) { + bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y); + PoolVector<float> painted_weights = node->get_bone_weights(bone_painting_bone); + + { + int pc = painted_weights.size(); + float amount = bone_paint_strength->get_value(); + float radius = bone_paint_radius->get_value() * EDSCALE; + + if (uv_mode == UV_MODE_CLEAR_WEIGHT) { + amount = -amount; + } + + PoolVector<float>::Write w = painted_weights.write(); + PoolVector<float>::Read r = prev_weights.read(); + PoolVector<Vector2>::Read rv = uv_prev.read(); + + for (int i = 0; i < pc; i++) { + if (mtx.xform(rv[i]).distance_to(bone_paint_pos) < radius) { + w[i] = CLAMP(r[i] + amount, 0, 1); + } + } + } + + node->set_bone_weights(bone_painting_bone, painted_weights); + } uv_edit_draw->update(); } else if (split_create) { uv_create_to = mtx.affine_inverse().xform(Vector2(mm->get_position().x, mm->get_position().y)); uv_edit_draw->update(); + } else if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) { + bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y); + uv_edit_draw->update(); } } @@ -649,6 +851,24 @@ void Polygon2DEditor::_uv_draw() { uvs = node->get_polygon(); } + PoolVector<float>::Read weight_r; + + if (uv_edit_mode[3]->is_pressed()) { + int bone_selected = -1; + for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) { + CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i)); + if (c && c->is_pressed()) { + bone_selected = i; + break; + } + } + + if (bone_selected != -1 && node->get_bone_weights(bone_selected).size() == uvs.size()) { + + weight_r = node->get_bone_weights(bone_selected).read(); + } + } + Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons"); Rect2 rect(Point2(), mtx.basis_xform(base_tex->get_size())); @@ -662,7 +882,14 @@ void Polygon2DEditor::_uv_draw() { next_point = uv_create_to; } uv_edit_draw->draw_line(mtx.xform(uvs[i]), mtx.xform(next_point), Color(0.9, 0.5, 0.5), 2); - uv_edit_draw->draw_texture(handle, mtx.xform(uvs[i]) - handle->get_size() * 0.5); + if (weight_r.ptr()) { + + Vector2 draw_pos = mtx.xform(uvs[i]); + float weight = weight_r[i]; + uv_edit_draw->draw_rect(Rect2(draw_pos - Vector2(2, 2) * EDSCALE, Vector2(5, 5) * EDSCALE), Color(weight, weight, weight, 1.0)); + } else { + uv_edit_draw->draw_texture(handle, mtx.xform(uvs[i]) - handle->get_size() * 0.5); + } rect.expand_to(mtx.basis_xform(uvs[i])); } @@ -682,6 +909,11 @@ void Polygon2DEditor::_uv_draw() { uv_edit_draw->draw_line(mtx.xform(uvs[idx_from]), mtx.xform(uvs[idx_to]), Color(0.9, 0.5, 0.5), 2); } + if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) { + + uv_edit_draw->draw_circle(bone_paint_pos, bone_paint_radius->get_value() * EDSCALE, Color(1, 1, 1, 0.1)); + } + rect = rect.grow(200); updating_uv_scroll = true; uv_hscroll->set_min(rect.position.x); @@ -711,6 +943,10 @@ void Polygon2DEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_snap_step_x"), &Polygon2DEditor::_set_snap_step_x); ClassDB::bind_method(D_METHOD("_set_snap_step_y"), &Polygon2DEditor::_set_snap_step_y); ClassDB::bind_method(D_METHOD("_uv_edit_mode_select"), &Polygon2DEditor::_uv_edit_mode_select); + ClassDB::bind_method(D_METHOD("_sync_bones"), &Polygon2DEditor::_sync_bones); + ClassDB::bind_method(D_METHOD("_update_bone_list"), &Polygon2DEditor::_update_bone_list); + + ClassDB::bind_method(D_METHOD("_bone_paint_selected"), &Polygon2DEditor::_bone_paint_selected); } Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const { @@ -755,19 +991,25 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_edit_mode[2] = memnew(ToolButton); uv_mode_hb->add_child(uv_edit_mode[2]); uv_edit_mode[2]->set_toggle_mode(true); + uv_edit_mode[3] = memnew(ToolButton); + uv_mode_hb->add_child(uv_edit_mode[3]); + uv_edit_mode[3]->set_toggle_mode(true); uv_edit_mode[0]->set_text(TTR("UV")); uv_edit_mode[0]->set_pressed(true); uv_edit_mode[1]->set_text(TTR("Poly")); uv_edit_mode[2]->set_text(TTR("Splits")); + uv_edit_mode[3]->set_text(TTR("Bones")); uv_edit_mode[0]->set_button_group(uv_edit_group); uv_edit_mode[1]->set_button_group(uv_edit_group); uv_edit_mode[2]->set_button_group(uv_edit_group); + uv_edit_mode[3]->set_button_group(uv_edit_group); uv_edit_mode[0]->connect("pressed", this, "_uv_edit_mode_select", varray(0)); uv_edit_mode[1]->connect("pressed", this, "_uv_edit_mode_select", varray(1)); uv_edit_mode[2]->connect("pressed", this, "_uv_edit_mode_select", varray(2)); + uv_edit_mode[3]->connect("pressed", this, "_uv_edit_mode_select", varray(3)); uv_mode_hb->add_child(memnew(VSeparator)); @@ -788,11 +1030,38 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_button[4]->set_tooltip(TTR("Scale Polygon")); uv_button[5]->set_tooltip(TTR("Connect two points to make a split")); uv_button[6]->set_tooltip(TTR("Select a split to erase it")); + uv_button[7]->set_tooltip(TTR("Paint weights with specified intensity")); + uv_button[8]->set_tooltip(TTR("UnPaint weights with specified intensity")); uv_button[0]->hide(); uv_button[5]->hide(); uv_button[6]->hide(); + uv_button[7]->hide(); + uv_button[8]->hide(); uv_button[1]->set_pressed(true); + + bone_paint_strength = memnew(HSlider); + uv_mode_hb->add_child(bone_paint_strength); + bone_paint_strength->set_custom_minimum_size(Size2(75 * EDSCALE, 0)); + bone_paint_strength->set_v_size_flags(SIZE_SHRINK_CENTER); + bone_paint_strength->set_min(0); + bone_paint_strength->set_max(1); + bone_paint_strength->set_step(0.01); + bone_paint_strength->set_value(0.5); + + bone_paint_radius_label = memnew(Label(" " + TTR("Radius:") + " ")); + uv_mode_hb->add_child(bone_paint_radius_label); + bone_paint_radius = memnew(SpinBox); + uv_mode_hb->add_child(bone_paint_radius); + + bone_paint_strength->hide(); + bone_paint_radius->hide(); + bone_paint_radius_label->hide(); + bone_paint_radius->set_min(1); + bone_paint_radius->set_max(100); + bone_paint_radius->set_step(1); + bone_paint_radius->set_value(32); + HBoxContainer *uv_main_hb = memnew(HBoxContainer); uv_main_vb->add_child(uv_main_hb); uv_edit_draw = memnew(Control); @@ -878,6 +1147,8 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_zoom->set_max(4); uv_zoom->set_value(1); uv_zoom->set_step(0.01); + uv_zoom->set_v_size_flags(SIZE_SHRINK_CENTER); + uv_mode_hb->add_child(uv_zoom); uv_zoom->set_custom_minimum_size(Size2(200, 0)); uv_zoom_value = memnew(SpinBox); @@ -893,6 +1164,20 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_main_vb->add_child(uv_hscroll); uv_hscroll->connect("value_changed", this, "_uv_scroll_changed"); + bone_scroll_main_vb = memnew(VBoxContainer); + bone_scroll_main_vb->hide(); + sync_bones = memnew(Button(TTR("Sync Bones"))); + bone_scroll_main_vb->add_child(sync_bones); + uv_main_hb->add_child(bone_scroll_main_vb); + bone_scroll = memnew(ScrollContainer); + bone_scroll->set_v_scroll(true); + bone_scroll->set_h_scroll(false); + bone_scroll_main_vb->add_child(bone_scroll); + bone_scroll->set_v_size_flags(SIZE_EXPAND_FILL); + bone_scroll_vb = memnew(VBoxContainer); + bone_scroll->add_child(bone_scroll_vb); + sync_bones->connect("pressed", this, "_sync_bones"); + uv_edit_draw->connect("draw", this, "_uv_draw"); uv_edit_draw->connect("gui_input", this, "_uv_input"); uv_draw_zoom = 1.0; @@ -901,6 +1186,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) : uv_create = false; updating_uv_scroll = false; split_create = false; + bone_painting = false; error = memnew(AcceptDialog); add_child(error); diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index 8631ffb9a7..b18ead4d98 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -32,7 +32,7 @@ #define POLYGON_2D_EDITOR_PLUGIN_H #include "editor/plugins/abstract_polygon_2d_editor.h" - +#include "scene/gui/scroll_container.h" /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -57,10 +57,12 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { UV_MODE_SCALE, UV_MODE_ADD_SPLIT, UV_MODE_REMOVE_SPLIT, + UV_MODE_PAINT_WEIGHT, + UV_MODE_CLEAR_WEIGHT, UV_MODE_MAX }; - ToolButton *uv_edit_mode[3]; + ToolButton *uv_edit_mode[4]; Ref<ButtonGroup> uv_edit_group; Polygon2D *node; @@ -78,11 +80,27 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { MenuButton *uv_menu; TextureRect *uv_icon_zoom; + VBoxContainer *bone_scroll_main_vb; + ScrollContainer *bone_scroll; + VBoxContainer *bone_scroll_vb; + Button *sync_bones; + HSlider *bone_paint_strength; + SpinBox *bone_paint_radius; + Label *bone_paint_radius_label; + bool bone_painting; + int bone_painting_bone; + PoolVector<float> prev_weights; + Vector2 bone_paint_pos; + + void _sync_bones(); + void _update_bone_list(); + Vector2 uv_draw_ofs; float uv_draw_zoom; PoolVector<Vector2> uv_prev; PoolVector<Vector2> uv_create_uv_prev; PoolVector<Vector2> uv_create_poly_prev; + Array uv_create_bones_prev; PoolVector<int> splits_prev; Vector2 uv_create_to; @@ -118,6 +136,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { void _set_snap_step_y(float p_val); void _uv_edit_mode_select(int p_mode); + void _bone_paint_selected(int p_index); protected: virtual Node2D *_get_node() const; diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 71a3c90795..a9afc7a670 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -45,6 +45,12 @@ void SpriteFramesEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { load->set_icon(get_icon("Load", "EditorIcons")); + copy->set_icon(get_icon("ActionCopy", "EditorIcons")); + paste->set_icon(get_icon("ActionPaste", "EditorIcons")); + empty->set_icon(get_icon("InsertBefore", "EditorIcons")); + empty2->set_icon(get_icon("InsertAfter", "EditorIcons")); + move_up->set_icon(get_icon("MoveLeft", "EditorIcons")); + move_down->set_icon(get_icon("MoveRight", "EditorIcons")); _delete->set_icon(get_icon("Remove", "EditorIcons")); new_anim->set_icon(get_icon("New", "EditorIcons")); remove_anim->set_icon(get_icon("Remove", "EditorIcons")); @@ -736,27 +742,35 @@ SpriteFramesEditor::SpriteFramesEditor() { hbc->add_child(load); copy = memnew(Button); - copy->set_text(TTR("Copy")); + copy->set_flat(true); + copy->set_tooltip(TTR("Copy")); hbc->add_child(copy); paste = memnew(Button); - paste->set_text(TTR("Paste")); + paste->set_flat(true); + paste->set_tooltip(TTR("Paste")); hbc->add_child(paste); empty = memnew(Button); - empty->set_text(TTR("Insert Empty (Before)")); + empty->set_flat(true); + empty->set_tooltip(TTR("Insert Empty (Before)")); hbc->add_child(empty); empty2 = memnew(Button); - empty2->set_text(TTR("Insert Empty (After)")); + empty2->set_flat(true); + empty2->set_tooltip(TTR("Insert Empty (After)")); hbc->add_child(empty2); + hbc->add_spacer(false); + move_up = memnew(Button); - move_up->set_text(TTR("Move (Before)")); + move_up->set_flat(true); + move_up->set_tooltip(TTR("Move (Before)")); hbc->add_child(move_up); move_down = memnew(Button); - move_down->set_text(TTR("Move (After)")); + move_down->set_flat(true); + move_down->set_tooltip(TTR("Move (After)")); hbc->add_child(move_down); _delete = memnew(Button); diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 111154cf32..2427cd966b 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -691,11 +691,11 @@ ThemeEditor::ThemeEditor() { test_menu_button->get_popup()->add_separator(); test_menu_button->get_popup()->add_check_item(TTR("Check Item")); test_menu_button->get_popup()->add_check_item(TTR("Checked Item")); - test_menu_button->get_popup()->set_item_checked(2, true); + test_menu_button->get_popup()->set_item_checked(3, true); test_menu_button->get_popup()->add_separator(); - test_menu_button->get_popup()->add_check_item(TTR("Radio Item")); + test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item")); test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item")); - test_menu_button->get_popup()->set_item_checked(5, true); + test_menu_button->get_popup()->set_item_checked(6, true); first_vb->add_child(test_menu_button); OptionButton *test_option_button = memnew(OptionButton); diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 14c584fa35..72b3af5a09 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -724,7 +724,11 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (mb->get_shift()) { +#ifdef APPLE_STYLE_KEYS + if (mb->get_command()) +#else if (mb->get_control()) +#endif tool = TOOL_RECTANGLE_PAINT; else tool = TOOL_LINE_PAINT; @@ -734,9 +738,11 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return true; } - +#ifdef APPLE_STYLE_KEYS + if (mb->get_command()) { +#else if (mb->get_control()) { - +#endif tool = TOOL_PICKING; _pick_tile(over_tile); @@ -940,8 +946,11 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data")); if (mb->get_shift()) { - +#ifdef APPLE_STYLE_KEYS + if (mb->get_command()) +#else if (mb->get_control()) +#endif tool = TOOL_RECTANGLE_ERASE; else tool = TOOL_LINE_ERASE; diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index 3257901c88..642870aec0 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -125,12 +125,11 @@ class TileMapEditor : public VBoxContainer { bool yf; bool tr; - CellOp() { - idx = -1; - xf = false; - yf = false; - tr = false; - } + CellOp() : + idx(TileMap::INVALID_CELL), + xf(false), + yf(false), + tr(false) {} }; Map<Point2i, CellOp> paint_undo; @@ -141,8 +140,12 @@ class TileMapEditor : public VBoxContainer { bool flip_h; bool flip_v; bool transpose; - int auto_x; - int auto_y; + + TileData() : + cell(TileMap::INVALID_CELL), + flip_h(false), + flip_v(false), + transpose(false) {} }; List<TileData> copydata; diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 41692e805f..385fa24ad8 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -149,6 +149,7 @@ void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) { p_library->tile_set_light_occluder(id, occluder); p_library->tile_set_occluder_offset(id, -phys_offset); p_library->tile_set_navigation_polygon_offset(id, -phys_offset); + p_library->tile_set_z_index(id, mi->get_z_index()); } } @@ -805,7 +806,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y))); Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); pos = mb->get_position() - pos; - uint16_t bit; + uint16_t bit = 0; if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { if (pos.x < size.x / 2) { if (pos.y < size.y / 2) { @@ -868,7 +869,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { Vector2 coord((int)(mm->get_position().x / (spacing + size.x)), (int)(mm->get_position().y / (spacing + size.y))); Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); pos = mm->get_position() - pos; - uint16_t bit; + uint16_t bit = 0; if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { if (pos.x < size.x / 2) { if (pos.y < size.y / 2) { @@ -1146,7 +1147,7 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { case EDITMODE_COLLISION: { if (!edited_collision_shape.is_null()) { Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile()); - int index; + int index = -1; for (int i = 0; i < sd.size(); i++) { if (sd[i].shape == edited_collision_shape) { index = i; diff --git a/editor/project_export.cpp b/editor/project_export.cpp index 7e9a884142..8b8c756219 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -703,9 +703,9 @@ void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) { ERR_FAIL_COND(platform.is_null()); if (p_path.ends_with(".zip")) { - platform->save_zip(current, p_path); + platform->export_zip(current, export_pck_zip_debug->is_pressed(), p_path); } else if (p_path.ends_with(".pck")) { - platform->save_pack(current, p_path); + platform->export_pack(current, export_pck_zip_debug->is_pressed(), p_path); } } @@ -981,6 +981,11 @@ ProjectExportDialog::ProjectExportDialog() { export_debug->set_pressed(true); export_project->get_vbox()->add_child(export_debug); + export_pck_zip_debug = memnew(CheckButton); + export_pck_zip_debug->set_text(TTR("Export With Debug")); + export_pck_zip_debug->set_pressed(true); + export_pck_zip->get_vbox()->add_child(export_pck_zip_debug); + set_hide_on_ok(false); editor_icons = "EditorIcons"; diff --git a/editor/project_export.h b/editor/project_export.h index 6c74743769..b62254974d 100644 --- a/editor/project_export.h +++ b/editor/project_export.h @@ -131,6 +131,7 @@ private: FileDialog *export_pck_zip; FileDialog *export_project; CheckButton *export_debug; + CheckButton *export_pck_zip_debug; void _open_export_template_manager(); diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index e9529eb1c0..50519e2c6e 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -710,25 +710,6 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da error_list->set_item_metadata(error_list->get_item_count() - 1, stack); error_count++; - /* - int count = p_data[1]; - - Array cstack; - - OutputError oe = errors.front()->get(); - - packet_peer_stream->put_var(oe.hr); - packet_peer_stream->put_var(oe.min); - packet_peer_stream->put_var(oe.sec); - packet_peer_stream->put_var(oe.msec); - packet_peer_stream->put_var(oe.source_func); - packet_peer_stream->put_var(oe.source_file); - packet_peer_stream->put_var(oe.source_line); - packet_peer_stream->put_var(oe.error); - packet_peer_stream->put_var(oe.error_descr); - packet_peer_stream->put_var(oe.warning); - packet_peer_stream->put_var(oe.callstack); - */ } else if (p_msg == "profile_sig") { //cache a signature @@ -755,6 +736,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da EditorProfiler::Metric::Category::Item item; item.calls = 1; item.line = 0; + item.name = "Physics Time"; item.total = metric.physics_time; item.self = item.total; @@ -792,8 +774,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da for (int i = 0; i < values.size(); i += 2) { EditorProfiler::Metric::Category::Item item; - item.name = values[i]; item.calls = 1; + item.line = 0; + item.name = values[i]; item.self = values[i + 1]; item.total = item.self; item.signature = "categ::" + name + "::" + item.name; diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index 8c90d86b9e..a3c4b73ae4 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -4052,9 +4052,9 @@ SpatialEditorGizmos::SpatialEditorGizmos() { for (int k = 0; k < 3; k++) { if (i < 3) - face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[j][(i + k) % 3] = v[k]; else - face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[3 - j][(i + k) % 3] = -v[k]; } } //tri 1 diff --git a/main/input_default.cpp b/main/input_default.cpp index 3c40be5082..29d30110e3 100644 --- a/main/input_default.cpp +++ b/main/input_default.cpp @@ -194,8 +194,6 @@ void InputDefault::joy_connection_changed(int p_idx, bool p_connected, String p_ Joypad js; js.name = p_connected ? p_name : ""; js.uid = p_connected ? p_guid : ""; - js.mapping = -1; - js.hat_current = 0; if (p_connected) { @@ -258,6 +256,11 @@ Vector3 InputDefault::get_gyroscope() const { void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) { + _parse_input_event_impl(p_event, false); +} + +void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) { + _THREAD_SAFE_METHOD_ Ref<InputEventKey> k = p_event; @@ -281,25 +284,30 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) { mouse_button_mask &= ~(1 << (mb->get_button_index() - 1)); } - if (main_loop && emulate_touch && mb->get_button_index() == 1) { + Point2 pos = mb->get_global_position(); + if (mouse_pos != pos) { + set_mouse_position(pos); + } + + if (main_loop && emulate_touch_from_mouse && !p_is_emulated && mb->get_button_index() == 1) { Ref<InputEventScreenTouch> touch_event; touch_event.instance(); touch_event->set_pressed(mb->is_pressed()); touch_event->set_position(mb->get_position()); main_loop->input_event(touch_event); } - - Point2 pos = mb->get_global_position(); - if (mouse_pos != pos) { - set_mouse_position(pos); - } } Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - if (main_loop && emulate_touch && mm->get_button_mask() & 1) { + Point2 pos = mm->get_global_position(); + if (mouse_pos != pos) { + set_mouse_position(pos); + } + + if (main_loop && emulate_touch_from_mouse && !p_is_emulated && mm->get_button_mask() & 1) { Ref<InputEventScreenDrag> drag_event; drag_event.instance(); @@ -311,6 +319,58 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) { } } + if (emulate_mouse_from_touch) { + + Ref<InputEventScreenTouch> st = p_event; + + if (st.is_valid()) { + bool translate = false; + if (st->is_pressed()) { + if (mouse_from_touch_index == -1) { + translate = true; + mouse_from_touch_index = st->get_index(); + } + } else { + if (st->get_index() == mouse_from_touch_index) { + translate = true; + mouse_from_touch_index = -1; + } + } + + if (translate) { + Ref<InputEventMouseButton> button_event; + button_event.instance(); + + button_event->set_position(st->get_position()); + button_event->set_global_position(st->get_position()); + button_event->set_pressed(st->is_pressed()); + button_event->set_button_index(BUTTON_LEFT); + if (st->is_pressed()) { + button_event->set_button_mask(mouse_button_mask | (1 << BUTTON_LEFT - 1)); + } else { + button_event->set_button_mask(mouse_button_mask & ~(1 << BUTTON_LEFT - 1)); + } + + _parse_input_event_impl(button_event, true); + } + } + + Ref<InputEventScreenDrag> sd = p_event; + + if (sd.is_valid() && sd->get_index() == mouse_from_touch_index) { + Ref<InputEventMouseMotion> motion_event; + motion_event.instance(); + + motion_event->set_position(sd->get_position()); + motion_event->set_global_position(sd->get_position()); + motion_event->set_relative(sd->get_relative()); + motion_event->set_speed(sd->get_speed()); + motion_event->set_button_mask(mouse_button_mask); + + _parse_input_event_impl(motion_event, true); + } + } + Ref<InputEventJoypadButton> jb = p_event; if (jb.is_valid()) { @@ -495,14 +555,44 @@ void InputDefault::action_release(const StringName &p_action) { action_state[p_action] = action; } -void InputDefault::set_emulate_touch(bool p_emulate) { +void InputDefault::set_emulate_touch_from_mouse(bool p_emulate) { + + emulate_touch_from_mouse = p_emulate; +} + +bool InputDefault::is_emulating_touch_from_mouse() const { + + return emulate_touch_from_mouse; +} + +// Calling this whenever the game window is focused helps unstucking the "touch mouse" +// if the OS or its abstraction class hasn't properly reported that touch pointers raised +void InputDefault::ensure_touch_mouse_raised() { + + if (mouse_from_touch_index != -1) { + mouse_from_touch_index = -1; + + Ref<InputEventMouseButton> button_event; + button_event.instance(); + + button_event->set_position(mouse_pos); + button_event->set_global_position(mouse_pos); + button_event->set_pressed(false); + button_event->set_button_index(BUTTON_LEFT); + button_event->set_button_mask(mouse_button_mask & ~(1 << BUTTON_LEFT - 1)); + + _parse_input_event_impl(button_event, true); + } +} + +void InputDefault::set_emulate_mouse_from_touch(bool p_emulate) { - emulate_touch = p_emulate; + emulate_mouse_from_touch = p_emulate; } -bool InputDefault::is_emulating_touchscreen() const { +bool InputDefault::is_emulating_mouse_from_touch() const { - return emulate_touch; + return emulate_mouse_from_touch; } Input::CursorShape InputDefault::get_default_cursor_shape() { @@ -539,7 +629,9 @@ void InputDefault::set_mouse_in_window(bool p_in_window) { InputDefault::InputDefault() { mouse_button_mask = 0; - emulate_touch = false; + emulate_touch_from_mouse = false; + emulate_mouse_from_touch = false; + mouse_from_touch_index = -1; main_loop = NULL; hat_map_default[HAT_UP].type = TYPE_BUTTON; @@ -797,12 +889,12 @@ InputDefault::JoyEvent InputDefault::_find_to_event(String p_to) { JoyEvent ret; ret.type = -1; + ret.index = 0; int i = 0; while (buttons[i]) { if (p_to == buttons[i]) { - //printf("mapping button %s\n", buttons[i]); ret.type = TYPE_BUTTON; ret.index = i; ret.value = 0; diff --git a/main/input_default.h b/main/input_default.h index 6dd88cd31e..2e3cae8520 100644 --- a/main/input_default.h +++ b/main/input_default.h @@ -60,7 +60,10 @@ class InputDefault : public Input { Map<StringName, Action> action_state; - bool emulate_touch; + bool emulate_touch_from_mouse; + bool emulate_mouse_from_touch; + + int mouse_from_touch_index; struct VibrationInfo { float weak_magnitude; @@ -97,7 +100,6 @@ class InputDefault : public Input { int hat_current; Joypad() { - for (int i = 0; i < JOY_AXIS_MAX; i++) { last_axis[i] = 0.0f; @@ -110,6 +112,7 @@ class InputDefault : public Input { last_hat = HAT_MASK_CENTER; filter = 0.01f; mapping = -1; + hat_current = 0; } }; @@ -176,6 +179,8 @@ private: void _axis_event(int p_device, int p_axis, float p_value); float _handle_deadzone(int p_device, int p_axis, float p_value); + void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated); + public: virtual bool is_key_pressed(int p_scancode) const; virtual bool is_mouse_button_pressed(int p_button) const; @@ -225,8 +230,12 @@ public: void iteration(float p_step); - void set_emulate_touch(bool p_emulate); - virtual bool is_emulating_touchscreen() const; + void set_emulate_touch_from_mouse(bool p_emulate); + virtual bool is_emulating_touch_from_mouse() const; + void ensure_touch_mouse_raised(); + + void set_emulate_mouse_from_touch(bool p_emulate); + virtual bool is_emulating_mouse_from_touch() const; virtual CursorShape get_default_cursor_shape(); virtual void set_default_cursor_shape(CursorShape p_shape); diff --git a/main/main.cpp b/main/main.cpp index 3542133719..ffc02d8823 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1141,13 +1141,16 @@ Error Main::setup2(Thread::ID p_main_tid_override) { GLOBAL_DEF("application/config/icon", String()); ProjectSettings::get_singleton()->set_custom_property_info("application/config/icon", PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.webp")); - if (bool(GLOBAL_DEF("display/window/handheld/emulate_touchscreen", false))) { - if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton() && !(editor || project_manager)) { - //only if no touchscreen ui hint, set emulation - InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); - if (id) - id->set_emulate_touch(true); + InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + if (id) { + if (bool(GLOBAL_DEF("input/pointing_devices/emulate_touch_from_mouse", false)) && !(editor || project_manager)) { + if (!OS::get_singleton()->has_touchscreen_ui_hint()) { + //only if no touchscreen ui hint, set emulation + id->set_emulate_touch_from_mouse(true); + } } + + id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF("input/pointing_devices/emulate_mouse_from_touch", true))); } MAIN_PRINT("Main: Load Remaps"); diff --git a/modules/csg/SCsub b/modules/csg/SCsub new file mode 100644 index 0000000000..57c504efd8 --- /dev/null +++ b/modules/csg/SCsub @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +Import('env') +Import('env_modules') + +env_csg = env_modules.Clone() + +# Godot's own source files +env_csg.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/csg/config.py b/modules/csg/config.py new file mode 100644 index 0000000000..5f133eba90 --- /dev/null +++ b/modules/csg/config.py @@ -0,0 +1,5 @@ +def can_build(platform): + return True + +def configure(env): + pass diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp new file mode 100644 index 0000000000..9752defa79 --- /dev/null +++ b/modules/csg/csg.cpp @@ -0,0 +1,1488 @@ +#include "csg.h" +#include "face3.h" +#include "geometry.h" +#include "os/os.h" +#include "sort.h" +#include "thirdparty/misc/triangulator.h" + +void CSGBrush::clear() { + faces.clear(); +} + +void CSGBrush::build_from_faces(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uvs, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials, const PoolVector<bool> &p_invert_faces) { + + clear(); + + int vc = p_vertices.size(); + + ERR_FAIL_COND((vc % 3) != 0) + + PoolVector<Vector3>::Read rv = p_vertices.read(); + int uvc = p_uvs.size(); + PoolVector<Vector2>::Read ruv = p_uvs.read(); + int sc = p_smooth.size(); + PoolVector<bool>::Read rs = p_smooth.read(); + int mc = p_materials.size(); + PoolVector<Ref<Material> >::Read rm = p_materials.read(); + int ic = p_invert_faces.size(); + PoolVector<bool>::Read ri = p_invert_faces.read(); + + Map<Ref<Material>, int> material_map; + + faces.resize(p_vertices.size() / 3); + + for (int i = 0; i < faces.size(); i++) { + Face &f = faces[i]; + f.vertices[0] = rv[i * 3 + 0]; + f.vertices[1] = rv[i * 3 + 1]; + f.vertices[2] = rv[i * 3 + 2]; + if (uvc == vc) { + f.uvs[0] = ruv[i * 3 + 0]; + f.uvs[1] = ruv[i * 3 + 1]; + f.uvs[2] = ruv[i * 3 + 2]; + } + if (sc == vc / 3) { + f.smooth = rs[i]; + } else { + f.smooth = false; + } + + if (ic == vc / 3) { + f.invert = ri[i]; + } else { + f.invert = false; + } + + if (mc == vc / 3) { + Ref<Material> mat = rm[i]; + if (mat.is_valid()) { + const Map<Ref<Material>, int>::Element *E = material_map.find(mat); + if (E) { + f.material = E->get(); + } else { + f.material = material_map.size(); + material_map[mat] = f.material; + } + } else { + f.material = -1; + } + } + } + + materials.resize(material_map.size()); + for (Map<Ref<Material>, int>::Element *E = material_map.front(); E; E = E->next()) { + materials[E->get()] = E->key(); + } + + _regen_face_aabbs(); +} + +void CSGBrush::_regen_face_aabbs() { + + for (int i = 0; i < faces.size(); i++) { + + faces[i].aabb.position = faces[i].vertices[0]; + faces[i].aabb.expand_to(faces[i].vertices[1]); + faces[i].aabb.expand_to(faces[i].vertices[2]); + faces[i].aabb.grow_by(faces[i].aabb.get_longest_axis_size() * 0.001); //make it a tad bigger to avoid num precision erros + } +} + +void CSGBrush::copy_from(const CSGBrush &p_brush, const Transform &p_xform) { + + faces = p_brush.faces; + materials = p_brush.materials; + + for (int i = 0; i < faces.size(); i++) { + for (int j = 0; j < 3; j++) { + faces[i].vertices[j] = p_xform.xform(p_brush.faces[i].vertices[j]); + } + } + + _regen_face_aabbs(); +} + +//////////////////////// + +void CSGBrushOperation::BuildPoly::create(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B) { + + //creates the initial face that will be used for clipping against the other faces + + Vector3 va[3] = { + p_brush->faces[p_face].vertices[0], + p_brush->faces[p_face].vertices[1], + p_brush->faces[p_face].vertices[2], + }; + + plane = Plane(va[0], va[1], va[2]); + + to_world.origin = va[0]; + + to_world.basis.set_axis(2, plane.normal); + to_world.basis.set_axis(0, (va[1] - va[2]).normalized()); + to_world.basis.set_axis(1, to_world.basis.get_axis(0).cross(to_world.basis.get_axis(2)).normalized()); + + to_poly = to_world.affine_inverse(); + + face_index = p_face; + + for (int i = 0; i < 3; i++) { + + Point p; + Vector3 localp = to_poly.xform(va[i]); + p.point.x = localp.x; + p.point.y = localp.y; + p.uv = p_brush->faces[p_face].uvs[i]; + + points.push_back(p); + + ///edge + + Edge e; + e.points[0] = i; + e.points[1] = (i + 1) % 3; + e.outer = true; + edges.push_back(e); + } + + smooth = p_brush->faces[p_face].smooth; + invert = p_brush->faces[p_face].invert; + + if (p_brush->faces[p_face].material != -1) { + material = p_brush->materials[p_brush->faces[p_face].material]; + } + + base_edges = 3; +} + +static Vector2 interpolate_uv(const Vector2 &p_vertex_a, const Vector2 &p_vertex_b, const Vector2 &p_vertex_c, const Vector2 &p_uv_a, const Vector2 &p_uv_c) { + + float len_a_c = (p_vertex_c - p_vertex_a).length(); + if (len_a_c < CMP_EPSILON) { + return p_uv_a; + } + + float len_a_b = (p_vertex_b - p_vertex_a).length(); + + float c = len_a_b / len_a_c; + + return p_uv_a.linear_interpolate(p_uv_c, c); +} + +static Vector2 interpolate_triangle_uv(const Vector2 &p_pos, const Vector2 *p_vtx, const Vector2 *p_uv) { + + if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2) { + return p_uv[0]; + } + if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2) { + return p_uv[1]; + } + if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2) { + return p_uv[2]; + } + + Vector2 v0 = p_vtx[1] - p_vtx[0]; + Vector2 v1 = p_vtx[2] - p_vtx[0]; + Vector2 v2 = p_pos - p_vtx[0]; + + float d00 = v0.dot(v0); + float d01 = v0.dot(v1); + float d11 = v1.dot(v1); + float d20 = v2.dot(v0); + float d21 = v2.dot(v1); + float denom = (d00 * d11 - d01 * d01); + if (denom == 0) { + return p_uv[0]; + } + float v = (d11 * d20 - d01 * d21) / denom; + float w = (d00 * d21 - d01 * d20) / denom; + float u = 1.0f - v - w; + + return p_uv[0] * u + p_uv[1] * v + p_uv[2] * w; +} + +void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_face, const Vector2 *segment, MeshMerge &mesh_merge, bool p_for_B) { + + //keep track of what was inserted + Vector<int> inserted_points; + + //keep track of point indices for what was inserted, allowing reuse of points. + int segment_idx[2] = { -1, -1 }; + + //check if edge and poly share a vertex, of so, assign it to segment_idx + for (int i = 0; i < points.size(); i++) { + for (int j = 0; j < 2; j++) { + if (segment[j].distance_to(points[i].point) < CMP_EPSILON) { + segment_idx[j] = i; + inserted_points.push_back(i); + break; + } + } + } + + //check if both segment points are shared with other vertices + if (segment_idx[0] != -1 && segment_idx[1] != -1) { + + if (segment_idx[0] == segment_idx[1]) { + return; //segment was too tiny, both mapped to same point + } + + bool found = false; + + //check if the segment already exists + for (int i = 0; i < edges.size(); i++) { + + if ( + (edges[i].points[0] == segment_idx[0] && edges[i].points[1] == segment_idx[1]) || + (edges[i].points[0] == segment_idx[1] && edges[i].points[1] == segment_idx[0])) { + found = true; + break; + } + } + + if (found) { + //it does already exist, do nothing + return; + } + + //directly add the new segment + Edge new_edge; + new_edge.points[0] = segment_idx[0]; + new_edge.points[1] = segment_idx[1]; + edges.push_back(new_edge); + return; + } + + //check edge by edge against the segment points to see if intersects + + for (int i = 0; i < base_edges; i++) { + + //if a point is shared with one of the edge points, then this edge must not be tested, as it will result in a numerical precision error. + bool edge_valid = true; + for (int j = 0; j < 2; j++) { + + if (edges[i].points[0] == segment_idx[0] || edges[i].points[1] == segment_idx[1] || edges[i].points[0] == segment_idx[1] || edges[i].points[1] == segment_idx[0]) { + edge_valid = false; //segment has this point, cant check against this + break; + } + } + + if (!edge_valid) //already hit a point in this edge, so dont test it + continue; + + //see if either points are within the edge isntead of crossing it + Vector2 res; + bool found = false; + int assign_segment_id = -1; + + for (int j = 0; j < 2; j++) { + + Vector2 edgeseg[2] = { points[edges[i].points[0]].point, points[edges[i].points[1]].point }; + Vector2 closest = Geometry::get_closest_point_to_segment_2d(segment[j], edgeseg); + + if (closest.distance_to(segment[j]) < CMP_EPSILON) { + //point rest of this edge + res = closest; + found = true; + assign_segment_id = j; + } + } + + //test if the point crosses the edge + if (!found && Geometry::segment_intersects_segment_2d(segment[0], segment[1], points[edges[i].points[0]].point, points[edges[i].points[1]].point, &res)) { + //point does cross the edge + found = true; + } + + //check whether an intersection against the segment happened + if (found) { + + //It did! so first, must slice the segment + Point new_point; + new_point.point = res; + //make sure to interpolate UV too + new_point.uv = interpolate_uv(points[edges[i].points[0]].point, new_point.point, points[edges[i].points[1]].point, points[edges[i].points[0]].uv, points[edges[i].points[1]].uv); + + int point_idx = points.size(); + points.push_back(new_point); + + //split the edge in 2 + Edge new_edge; + new_edge.points[0] = edges[i].points[0]; + new_edge.points[1] = point_idx; + new_edge.outer = edges[i].outer; + edges[i].points[0] = point_idx; + edges.insert(i, new_edge); + i++; //skip newly inserted edge + base_edges++; //will need an extra one in the base triangle + if (assign_segment_id >= 0) { + //point did split a segment, so make sure to remember this + segment_idx[assign_segment_id] = point_idx; + } + inserted_points.push_back(point_idx); + } + } + + //final step: after cutting the original triangle, try to see if we can still insert + //this segment + + //if already inserted two points, just use them for a segment + + if (inserted_points.size() >= 2) { //should never be >2 on non-manifold geometry, but cope with error + //two points were inserted, create the new edge + Edge new_edge; + new_edge.points[0] = inserted_points[0]; + new_edge.points[1] = inserted_points[1]; + edges.push_back(new_edge); + return; + } + + // One or no points were inserted (besides splitting), so try to see if extra points can be placed inside the triangle. + // This needs to be done here, after the previous tests were exhausted + for (int i = 0; i < 2; i++) { + + if (segment_idx[i] != -1) + continue; //already assigned to something, so skip + + //check whether one of the segment endpoints is inside the triangle. If it is, this points needs to be inserted + if (Geometry::is_point_in_triangle(segment[i], points[0].point, points[1].point, points[2].point)) { + + Point new_point; + new_point.point = segment[i]; + + Vector2 point3[3] = { points[0].point, points[1].point, points[2].point }; + Vector2 uv3[3] = { points[0].uv, points[1].uv, points[2].uv }; + + new_point.uv = interpolate_triangle_uv(new_point.point, point3, uv3); + + int point_idx = points.size(); + points.push_back(new_point); + inserted_points.push_back(point_idx); + } + } + + //check again whether two points were inserted, if so then create the new edge + if (inserted_points.size() >= 2) { //should never be >2 on non-manifold geometry, but cope with error + Edge new_edge; + new_edge.points[0] = inserted_points[0]; + new_edge.points[1] = inserted_points[1]; + edges.push_back(new_edge); + } +} + +void CSGBrushOperation::BuildPoly::clip(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B) { + + //Clip function.. find triangle points that will be mapped to the plane and form a segment + + Vector2 segment[3]; //2D + + int src_points = 0; + + for (int i = 0; i < 3; i++) { + Vector3 p = p_brush->faces[p_face].vertices[i]; + if (plane.has_point(p)) { + Vector3 pp = plane.project(p); + pp = to_poly.xform(pp); + segment[src_points++] = Vector2(pp.x, pp.y); + } else { + Vector3 q = p_brush->faces[p_face].vertices[(i + 1) % 3]; + if (plane.has_point(q)) + continue; //next point is in plane, will be added eventually + if (plane.is_point_over(p) == plane.is_point_over(q)) + continue; // both on same side of the plane, don't add + + Vector3 res; + if (plane.intersects_segment(p, q, &res)) { + res = to_poly.xform(res); + segment[src_points++] = Vector2(res.x, res.y); + } + } + } + + //all above or all below, nothing to do. Should not happen though since a precheck was done before. + if (src_points == 0) + return; + + //just one point in plane is not worth doing anything + if (src_points == 1) + return; + + //transform A points to 2D + + if (segment[0].distance_to(segment[1]) < CMP_EPSILON) + return; //too small + + _clip_segment(p_brush, p_face, segment, mesh_merge, p_for_B); +} + +void CSGBrushOperation::_collision_callback(const CSGBrush *A, int p_face_a, Map<int, BuildPoly> &build_polys_a, const CSGBrush *B, int p_face_b, Map<int, BuildPoly> &build_polys_b, MeshMerge &mesh_merge) { + + //construct a frame of reference for both transforms, in order to do intersection test + Vector3 va[3] = { + A->faces[p_face_a].vertices[0], + A->faces[p_face_a].vertices[1], + A->faces[p_face_a].vertices[2], + }; + Vector3 vb[3] = { + B->faces[p_face_b].vertices[0], + B->faces[p_face_b].vertices[1], + B->faces[p_face_b].vertices[2], + }; + + { + //check if either is a degenerate + if (va[0].distance_to(va[1]) < CMP_EPSILON || va[0].distance_to(va[2]) < CMP_EPSILON || va[1].distance_to(va[2]) < CMP_EPSILON) + return; + + if (vb[0].distance_to(vb[1]) < CMP_EPSILON || vb[0].distance_to(vb[2]) < CMP_EPSILON || vb[1].distance_to(vb[2]) < CMP_EPSILON) + return; + } + + { + //check if points are the same + int equal_count = 0; + + for (int i = 0; i < 3; i++) { + + for (int j = 0; j < 3; j++) { + if (va[i].distance_to(vb[j]) < mesh_merge.vertex_snap) { + equal_count++; + break; + } + } + } + + //if 2 or 3 points are the same, there is no point in doing anything. They can't + //be clipped either, so add both. + if (equal_count == 2 || equal_count == 3) { + return; + } + } + + // do a quick pre-check for no-intersection using the SAT theorem + + { + + //b under or over a plane + int over_count = 0, in_plane_count = 0, under_count = 0; + Plane plane_a(va[0], va[1], va[2]); + if (plane_a.normal == Vector3()) { + return; //degenerate + } + + for (int i = 0; i < 3; i++) { + if (plane_a.has_point(vb[i])) + in_plane_count++; + else if (plane_a.is_point_over(vb[i])) + over_count++; + else + under_count++; + } + + if (over_count == 0 || under_count == 0) + return; //no intersection, something needs to be under AND over + + //a under or over b plane + over_count = 0; + under_count = 0; + in_plane_count = 0; + + Plane plane_b(vb[0], vb[1], vb[2]); + if (plane_b.normal == Vector3()) + return; //degenerate + + for (int i = 0; i < 3; i++) { + if (plane_b.has_point(va[i])) + in_plane_count++; + else if (plane_b.is_point_over(va[i])) + over_count++; + else + under_count++; + } + + if (over_count == 0 || under_count == 0) + return; //no intersection, something needs to be under AND over + + //edge pairs (cross product combinations), see SAT theorem + + for (int i = 0; i < 3; i++) { + + Vector3 axis_a = (va[i] - va[(i + 1) % 3]).normalized(); + + for (int j = 0; j < 3; j++) { + + Vector3 axis_b = (vb[j] - vb[(j + 1) % 3]).normalized(); + + Vector3 sep_axis = axis_a.cross(axis_b); + if (sep_axis == Vector3()) + continue; //colineal + sep_axis.normalize(); + + real_t min_a = 1e20, max_a = -1e20; + real_t min_b = 1e20, max_b = -1e20; + + for (int k = 0; k < 3; k++) { + real_t d = sep_axis.dot(va[k]); + min_a = MIN(min_a, d); + max_a = MAX(max_a, d); + d = sep_axis.dot(vb[k]); + min_b = MIN(min_b, d); + max_b = MAX(max_b, d); + } + + min_b -= (max_a - min_a) * 0.5; + max_b += (max_a - min_a) * 0.5; + + real_t dmin = min_b - (min_a + max_a) * 0.5; + real_t dmax = max_b - (min_a + max_a) * 0.5; + + if (dmin > CMP_EPSILON || dmax < -CMP_EPSILON) { + return; //does not contain zero, so they don't overlap + } + } + } + } + + //if we are still here, it means they most likely intersect, so create BuildPolys if they dont existy + + BuildPoly *poly_a = NULL; + + if (!build_polys_a.has(p_face_a)) { + + BuildPoly bp; + bp.create(A, p_face_a, mesh_merge, false); + build_polys_a[p_face_a] = bp; + } + + poly_a = &build_polys_a[p_face_a]; + + BuildPoly *poly_b = NULL; + + if (!build_polys_b.has(p_face_b)) { + + BuildPoly bp; + bp.create(B, p_face_b, mesh_merge, true); + build_polys_b[p_face_b] = bp; + } + + poly_b = &build_polys_b[p_face_b]; + + //clip each other, this could be improved by using vertex unique IDs (more vertices may be shared instead of using snap) + poly_a->clip(B, p_face_b, mesh_merge, false); + poly_b->clip(A, p_face_a, mesh_merge, true); +} + +void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<bool> &edge_process, Vector<PolyPoints> &r_poly) { + + //this function follows the polygon points counter clockwise and adds them. It creates lists of unique polygons + //every time an unused edge is found, it's pushed to a stack and continues from there. + + List<EdgeSort> edge_stack; + + { + EdgeSort es; + es.angle = 0; //wont be checked here + es.edge = p_edge; + es.prev_point = p_from_point; + es.edge_point = p_to_point; + + edge_stack.push_back(es); + } + + //attempt to empty the stack. + while (edge_stack.size()) { + + EdgeSort e = edge_stack.front()->get(); + edge_stack.pop_front(); + + if (edge_process[e.edge]) { + //nothing to do here + continue; + } + + Vector<int> points; + points.push_back(e.prev_point); + + int prev_point = e.prev_point; + int to_point = e.edge_point; + int current_edge = e.edge; + + edge_process[e.edge] = true; //mark as processed + + int limit = p_poly.points.size() * 4; //avoid infinite recursion + + while (to_point != e.prev_point && limit) { + + Vector2 segment[2] = { p_poly.points[prev_point].point, p_poly.points[to_point].point }; + + //construct a basis transform from the segment, which will be used to check the angle + Transform2D t2d; + t2d[0] = (segment[1] - segment[0]).normalized(); //use as Y + t2d[1] = Vector2(-t2d[0].y, t2d[0].x); // use as tangent + t2d[2] = segment[1]; //origin + + if (t2d.basis_determinant() == 0) + break; //abort poly + + t2d.affine_invert(); + + //push all edges found here, they will be sorted by minimum angle later. + Vector<EdgeSort> next_edges; + + for (int i = 0; i < vertex_process[to_point].size(); i++) { + + int edge = vertex_process[to_point][i]; + int opposite_point = p_poly.edges[edge].points[0] == to_point ? p_poly.edges[edge].points[1] : p_poly.edges[edge].points[0]; + if (opposite_point == prev_point) + continue; //not going back + + EdgeSort e; + Vector2 local_vec = t2d.xform(p_poly.points[opposite_point].point); + e.angle = -local_vec.angle(); //negate so we can sort by minimum angle + e.edge = edge; + e.edge_point = opposite_point; + e.prev_point = to_point; + + next_edges.push_back(e); + } + + //finally, sort by minimum angle + next_edges.sort(); + + int next_point = -1; + int next_edge = -1; + + for (int i = 0; i < next_edges.size(); i++) { + + if (i == 0) { + //minimum angle found is the next point + next_point = next_edges[i].edge_point; + next_edge = next_edges[i].edge; + + } else { + //the rest are pushed to the stack IF they were not processed yet. + if (!edge_process[next_edges[i].edge]) { + edge_stack.push_back(next_edges[i]); + } + } + } + + if (next_edge == -1) { + //did not find anything, may be a dead-end edge (this should normally not happen) + //just flip the direction and go back + next_point = prev_point; + next_edge = current_edge; + } + + points.push_back(to_point); + + prev_point = to_point; + to_point = next_point; + edge_process[next_edge] = true; //mark this edge as processed + current_edge = next_edge; + + limit--; + } + + //if more than 2 points were added to the polygon, add it to the list of polygons. + if (points.size() > 2) { + PolyPoints pp; + pp.points = points; + r_poly.push_back(pp); + } + } +} + +void CSGBrushOperation::_add_poly_outline(const BuildPoly &p_poly, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<int> &r_outline) { + + //this is the opposite of the function above. It adds polygon outlines instead. + //this is used for triangulating holes. + //no stack is used here because only the bigger outline is interesting. + + r_outline.push_back(p_from_point); + + int prev_point = p_from_point; + int to_point = p_to_point; + + int limit = p_poly.points.size() * 4; //avoid infinite recursion + + while (to_point != p_from_point && limit) { + + Vector2 segment[2] = { p_poly.points[prev_point].point, p_poly.points[to_point].point }; + //again create a transform to compute the angle. + Transform2D t2d; + t2d[0] = (segment[1] - segment[0]).normalized(); //use as Y + t2d[1] = Vector2(-t2d[0].y, t2d[0].x); // use as tangent + t2d[2] = segment[1]; //origin + + if (t2d.basis_determinant() == 0) + break; //abort poly + + t2d.affine_invert(); + + float max_angle; + int next_point_angle = -1; + + for (int i = 0; i < vertex_process[to_point].size(); i++) { + + int edge = vertex_process[to_point][i]; + int opposite_point = p_poly.edges[edge].points[0] == to_point ? p_poly.edges[edge].points[1] : p_poly.edges[edge].points[0]; + if (opposite_point == prev_point) + continue; //not going back + + float angle = -t2d.xform(p_poly.points[opposite_point].point).angle(); + if (next_point_angle == -1 || angle > max_angle) { //same as before but use greater to check. + max_angle = angle; + next_point_angle = opposite_point; + } + } + + if (next_point_angle == -1) { + //go back because no route found + next_point_angle = prev_point; + } + + r_outline.push_back(to_point); + prev_point = to_point; + to_point = next_point_angle; + + limit--; + } +} + +void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const BuildPoly &p_poly, bool p_from_b) { + + //finally, merge the 2D polygon back to 3D + + Vector<Vector<int> > vertex_process; + Vector<bool> edge_process; + + vertex_process.resize(p_poly.points.size()); + edge_process.resize(p_poly.edges.size()); + + //none processed by default + for (int i = 0; i < edge_process.size(); i++) { + edge_process[i] = false; + } + + //put edges in points, so points can go through them + for (int i = 0; i < p_poly.edges.size(); i++) { + vertex_process[p_poly.edges[i].points[0]].push_back(i); + vertex_process[p_poly.edges[i].points[1]].push_back(i); + } + + Vector<PolyPoints> polys; + + //process points that were not processed + for (int i = 0; i < edge_process.size(); i++) { + if (edge_process[i] == true) + continue; //already processed + + int intersect_poly = -1; + + if (i > 0) { + //this is disconnected, so it's clearly a hole. lets find where it belongs + Vector2 ref_point = p_poly.points[p_poly.edges[i].points[0]].point; + + for (int j = 0; j < polys.size(); j++) { + + //find a point outside poly + Vector2 out_point(-1e20, -1e20); + + const PolyPoints &pp = polys[j]; + + for (int k = 0; k < pp.points.size(); k++) { + Vector2 p = p_poly.points[pp.points[k]].point; + out_point.x = MAX(out_point.x, p.x); + out_point.y = MAX(out_point.y, p.y); + } + + out_point += Vector2(0.12341234, 0.4123412); // move to a random place to avoid direct edge-point chances + + int intersections = 0; + + for (int k = 0; k < pp.points.size(); k++) { + Vector2 p1 = p_poly.points[pp.points[k]].point; + Vector2 p2 = p_poly.points[pp.points[(k + 1) % pp.points.size()]].point; + + if (Geometry::segment_intersects_segment_2d(ref_point, out_point, p1, p2, NULL)) { + intersections++; + } + } + + if (intersections % 2 == 1) { + //hole is inside this poly + intersect_poly = j; + break; + } + } + } + + if (intersect_poly != -1) { + //must add this as a hole + Vector<int> outline; + _add_poly_outline(p_poly, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, outline); + + if (outline.size() > 1) { + polys[intersect_poly].holes.push_back(outline); + } + } + _add_poly_points(p_poly, i, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, edge_process, polys); + } + + //get rid of holes, not the most optiomal way, but also not a common case at all to be inoptimal + for (int i = 0; i < polys.size(); i++) { + + if (!polys[i].holes.size()) + continue; + + //repeat until no more holes are left to be merged + while (polys[i].holes.size()) { + + //try to merge a hole with the outline + bool added_hole = false; + + for (int j = 0; j < polys[i].holes.size(); j++) { + + //try hole vertices + int with_outline_vertex = -1; + int from_hole_vertex = -1; + + bool found = false; + + for (int k = 0; k < polys[i].holes[j].size(); k++) { + + int from_idx = polys[i].holes[j][k]; + Vector2 from = p_poly.points[from_idx].point; + + //try a segment from hole vertex to outline vertices + from_hole_vertex = k; + + bool valid = true; + + for (int l = 0; l < polys[i].points.size(); l++) { + + int to_idx = polys[i].points[l]; + Vector2 to = p_poly.points[to_idx].point; + with_outline_vertex = l; + + //try agaisnt outline (other points) first + + valid = true; + + for (int m = 0; m < polys[i].points.size(); m++) { + + int m_next = (m + 1) % polys[i].points.size(); + if (m == with_outline_vertex || m_next == with_outline_vertex) //do not test with edges that share this point + continue; + + if (Geometry::segment_intersects_segment_2d(from, to, p_poly.points[polys[i].points[m]].point, p_poly.points[polys[i].points[m_next]].point, NULL)) { + valid = false; + break; + } + } + + if (!valid) + continue; + + //try agaisnt all holes including self + + for (int m = 0; m < polys[i].holes.size(); m++) { + + for (int n = 0; n < polys[i].holes[m].size(); n++) { + + int n_next = (n + 1) % polys[i].holes[m].size(); + if (m == j && (n == from_hole_vertex || n_next == from_hole_vertex)) //contains vertex being tested from current hole, skip + continue; + + if (Geometry::segment_intersects_segment_2d(from, to, p_poly.points[polys[i].holes[m][n]].point, p_poly.points[polys[i].holes[m][n_next]].point, NULL)) { + valid = false; + break; + } + } + + if (!valid) + break; + } + + if (valid) //all passed! exit loop + break; + else + continue; //something went wrong, go on. + } + + if (valid) { + found = true; //if in the end this was valid, use it + break; + } + } + + if (found) { + + //hook this hole with outline, and remove from list of holes + + //duplicate point + int insert_at = with_outline_vertex; + polys[i].points.insert(insert_at, polys[i].points[insert_at]); + insert_at++; + //insert all others, outline should be backwards (must check) + int holesize = polys[i].holes[j].size(); + for (int k = 0; k <= holesize; k++) { + int idx = (from_hole_vertex + k) % holesize; + polys[i].points.insert(insert_at, polys[i].holes[j][idx]); + insert_at++; + } + + added_hole = true; + polys[i].holes.remove(j); + break; //got rid of hole, break and continue + } + } + + ERR_BREAK(!added_hole); + } + } + + //triangulate polygons + + for (int i = 0; i < polys.size(); i++) { + + Vector<Vector2> vertices; + vertices.resize(polys[i].points.size()); + for (int j = 0; j < vertices.size(); j++) { + vertices[j] = p_poly.points[polys[i].points[j]].point; + } + + Vector<int> indices = Geometry::triangulate_polygon(vertices); + + for (int j = 0; j < indices.size(); j += 3) { + + //obtain the vertex + + Vector3 face[3]; + Vector2 uv[3]; + float cp = Geometry::vec2_cross(p_poly.points[polys[i].points[indices[j + 0]]].point, p_poly.points[polys[i].points[indices[j + 1]]].point, p_poly.points[polys[i].points[indices[j + 2]]].point); + if (Math::abs(cp) < CMP_EPSILON) + continue; + + for (int k = 0; k < 3; k++) { + + Vector2 p = p_poly.points[polys[i].points[indices[j + k]]].point; + face[k] = p_poly.to_world.xform(Vector3(p.x, p.y, 0)); + uv[k] = p_poly.points[polys[i].points[indices[j + k]]].uv; + } + + mesh.add_face(face[0], face[1], face[2], uv[0], uv[1], uv[2], p_poly.smooth, p_poly.invert, p_poly.material, p_from_b); + } + } +} + +//use a limit to speed up bvh and limit the depth +#define BVH_LIMIT 8 + +int CSGBrushOperation::MeshMerge::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc) { + + if (p_depth > max_depth) { + max_depth = p_depth; + } + + if (p_size <= BVH_LIMIT) { + + for (int i = 0; i < p_size - 1; i++) { + p_bb[p_from + i]->next = p_bb[p_from + i + 1] - p_bvh; + } + return p_bb[p_from] - p_bvh; + } else if (p_size == 0) { + + return -1; + } + + AABB aabb; + aabb = p_bb[p_from]->aabb; + for (int i = 1; i < p_size; i++) { + + aabb.merge_with(p_bb[p_from + i]->aabb); + } + + int li = aabb.get_longest_axis_index(); + + switch (li) { + + case Vector3::AXIS_X: { + SortArray<BVH *, BVHCmpX> sort_x; + sort_x.nth_element(0, p_size, p_size / 2, &p_bb[p_from]); + //sort_x.sort(&p_bb[p_from],p_size); + } break; + case Vector3::AXIS_Y: { + SortArray<BVH *, BVHCmpY> sort_y; + sort_y.nth_element(0, p_size, p_size / 2, &p_bb[p_from]); + //sort_y.sort(&p_bb[p_from],p_size); + } break; + case Vector3::AXIS_Z: { + SortArray<BVH *, BVHCmpZ> sort_z; + sort_z.nth_element(0, p_size, p_size / 2, &p_bb[p_from]); + //sort_z.sort(&p_bb[p_from],p_size); + + } break; + } + + int left = _create_bvh(p_bvh, p_bb, p_from, p_size / 2, p_depth + 1, max_depth, max_alloc); + int right = _create_bvh(p_bvh, p_bb, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, max_depth, max_alloc); + + int index = max_alloc++; + BVH *_new = &p_bvh[index]; + _new->aabb = aabb; + _new->center = aabb.position + aabb.size * 0.5; + _new->face = -1; + _new->left = left; + _new->right = right; + _new->next = -1; + + return index; +} + +int CSGBrushOperation::MeshMerge::_bvh_count_intersections(BVH *bvhptr, int p_max_depth, int p_bvh_first, const Vector3 &p_begin, const Vector3 &p_end, int p_exclude) const { + + uint32_t *stack = (uint32_t *)alloca(sizeof(int) * p_max_depth); + + enum { + TEST_AABB_BIT = 0, + VISIT_LEFT_BIT = 1, + VISIT_RIGHT_BIT = 2, + VISIT_DONE_BIT = 3, + VISITED_BIT_SHIFT = 29, + NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1, + VISITED_BIT_MASK = ~NODE_IDX_MASK, + + }; + + int intersections = 0; + + int level = 0; + + const Vector3 *vertexptr = points.ptr(); + const Face *facesptr = faces.ptr(); + AABB segment_aabb; + segment_aabb.position = p_begin; + segment_aabb.expand_to(p_end); + + int pos = p_bvh_first; + + stack[0] = pos; + while (true) { + + uint32_t node = stack[level] & NODE_IDX_MASK; + const BVH &b = bvhptr[node]; + bool done = false; + + switch (stack[level] >> VISITED_BIT_SHIFT) { + case TEST_AABB_BIT: { + + if (b.face >= 0) { + + const BVH *bp = &b; + + while (bp) { + + bool valid = segment_aabb.intersects(bp->aabb) && bp->aabb.intersects_segment(p_begin, p_end); + + if (valid && p_exclude != bp->face) { + const Face &s = facesptr[bp->face]; + Face3 f3(vertexptr[s.points[0]], vertexptr[s.points[1]], vertexptr[s.points[2]]); + + Vector3 res; + + if (f3.intersects_segment(p_begin, p_end, &res)) { + intersections++; + } + } + if (bp->next != -1) { + bp = &bvhptr[bp->next]; + } else { + bp = NULL; + } + } + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + + } else { + + bool valid = segment_aabb.intersects(b.aabb) && b.aabb.intersects_segment(p_begin, p_end); + + if (!valid) { + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + + } else { + stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node; + } + } + continue; + } + case VISIT_LEFT_BIT: { + + stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node; + stack[level + 1] = b.left | TEST_AABB_BIT; + level++; + continue; + } + case VISIT_RIGHT_BIT: { + + stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node; + stack[level + 1] = b.right | TEST_AABB_BIT; + level++; + continue; + } + case VISIT_DONE_BIT: { + + if (level == 0) { + done = true; + break; + } else + level--; + continue; + } + } + + if (done) + break; + } + + return intersections; +} + +void CSGBrushOperation::MeshMerge::mark_inside_faces() { + + // mark faces that are inside. This helps later do the boolean ops when merging. + // this approach is very brute force (with a bunch of optimizatios, such as BVH and pre AABB intersection test) + + AABB aabb; + + for (int i = 0; i < points.size(); i++) { + if (i == 0) { + aabb.position = points[i]; + } else { + aabb.expand_to(points[i]); + } + } + + float max_distance = aabb.size.length() * 1.2; + + Vector<BVH> bvhvec; + bvhvec.resize(faces.size() * 3); //will never be larger than this (todo make better) + BVH *bvh = bvhvec.ptrw(); + + AABB faces_a; + AABB faces_b; + + bool first_a = true; + bool first_b = true; + + for (int i = 0; i < faces.size(); i++) { + bvh[i].left = -1; + bvh[i].right = -1; + bvh[i].face = i; + bvh[i].aabb.position = points[faces[i].points[0]]; + bvh[i].aabb.expand_to(points[faces[i].points[1]]); + bvh[i].aabb.expand_to(points[faces[i].points[2]]); + bvh[i].center = bvh[i].aabb.position + bvh[i].aabb.size * 0.5; + bvh[i].next = -1; + if (faces[i].from_b) { + if (first_b) { + faces_b = bvh[i].aabb; + first_b = false; + } else { + faces_b.merge_with(bvh[i].aabb); + } + } else { + if (first_a) { + faces_a = bvh[i].aabb; + first_a = false; + } else { + faces_a.merge_with(bvh[i].aabb); + } + } + } + + AABB intersection_aabb = faces_a.intersection(faces_b); + intersection_aabb.grow_by(intersection_aabb.get_longest_axis_size() * 0.01); //grow a little, avoid numerical error + + if (intersection_aabb.size == Vector3()) //AABB do not intersect, so neither do shapes. + return; + + Vector<BVH *> bvhtrvec; + bvhtrvec.resize(faces.size()); + BVH **bvhptr = bvhtrvec.ptrw(); + for (int i = 0; i < faces.size(); i++) { + + bvhptr[i] = &bvh[i]; + } + + int max_depth = 0; + int max_alloc = faces.size(); + _create_bvh(bvh, bvhptr, 0, faces.size(), 1, max_depth, max_alloc); + + for (int i = 0; i < faces.size(); i++) { + + if (!intersection_aabb.intersects(bvh[i].aabb)) + continue; //not in AABB intersection, so not in face intersection + Vector3 center = points[faces[i].points[0]]; + center += points[faces[i].points[1]]; + center += points[faces[i].points[2]]; + center /= 3.0; + + Plane plane(points[faces[i].points[0]], points[faces[i].points[1]], points[faces[i].points[2]]); + Vector3 target = center + plane.normal * max_distance + Vector3(0.0001234, 0.000512, 0.00013423); //reduce chance of edge hits by doing a small increment + + int intersections = _bvh_count_intersections(bvh, max_depth, max_alloc - 1, center, target, i); + + if (intersections & 1) { + faces[i].inside = true; + } + } +} + +void CSGBrushOperation::MeshMerge::add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector2 &p_uv_a, const Vector2 &p_uv_b, const Vector2 &p_uv_c, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b) { + + Vector3 src_points[3] = { p_a, p_b, p_c }; + Vector2 src_uvs[3] = { p_uv_a, p_uv_b, p_uv_c }; + int indices[3]; + for (int i = 0; i < 3; i++) { + + VertexKey vk; + vk.x = int((double(src_points[i].x) + double(vertex_snap) * 0.31234) / double(vertex_snap)); + vk.y = int((double(src_points[i].y) + double(vertex_snap) * 0.31234) / double(vertex_snap)); + vk.z = int((double(src_points[i].z) + double(vertex_snap) * 0.31234) / double(vertex_snap)); + + int res; + if (snap_cache.lookup(vk, &res)) { + indices[i] = res; + } else { + indices[i] = points.size(); + points.push_back(src_points[i]); + snap_cache.set(vk, indices[i]); + } + } + + if (indices[0] == indices[2] || indices[0] == indices[1] || indices[1] == indices[2]) + return; //not adding degenerate + + MeshMerge::Face face; + face.from_b = p_from_b; + face.inside = false; + face.smooth = p_smooth; + face.invert = p_invert; + if (p_material.is_valid()) { + if (!materials.has(p_material)) { + face.material_idx = materials.size(); + materials[p_material] = face.material_idx; + } else { + face.material_idx = materials[p_material]; + } + } else { + face.material_idx = -1; + } + + for (int k = 0; k < 3; k++) { + + face.points[k] = indices[k]; + face.uvs[k] = src_uvs[k]; + ; + } + + faces.push_back(face); +} + +void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A, const CSGBrush &p_B, CSGBrush &result, float p_snap) { + + CallbackData cd; + cd.self = this; + cd.A = &p_A; + cd.B = &p_B; + + MeshMerge mesh_merge; + mesh_merge.vertex_snap = p_snap; + + //check intersections between faces. Use AABB to speed up precheck + //this generates list of buildpolys and clips them. + //this was originally BVH optimized, but its not really worth it. + for (int i = 0; i < p_A.faces.size(); i++) { + cd.face_a = i; + for (int j = 0; j < p_B.faces.size(); j++) { + if (p_A.faces[i].aabb.intersects(p_B.faces[j].aabb)) { + _collision_callback(&p_A, i, cd.build_polys_A, &p_B, j, cd.build_polys_B, mesh_merge); + } + } + } + + //merge the already cliped polys back to 3D + for (Map<int, BuildPoly>::Element *E = cd.build_polys_A.front(); E; E = E->next()) { + _merge_poly(mesh_merge, E->key(), E->get(), false); + } + + for (Map<int, BuildPoly>::Element *E = cd.build_polys_B.front(); E; E = E->next()) { + _merge_poly(mesh_merge, E->key(), E->get(), true); + } + + //merge the non clipped faces back + + for (int i = 0; i < p_A.faces.size(); i++) { + + if (cd.build_polys_A.has(i)) + continue; //made from buildpoly, skipping + + Vector3 points[3]; + Vector2 uvs[3]; + for (int j = 0; j < 3; j++) { + points[j] = p_A.faces[i].vertices[j]; + uvs[j] = p_A.faces[i].uvs[j]; + } + Ref<Material> material; + if (p_A.faces[i].material != -1) { + material = p_A.materials[p_A.faces[i].material]; + } + mesh_merge.add_face(points[0], points[1], points[2], uvs[0], uvs[1], uvs[2], p_A.faces[i].smooth, p_A.faces[i].invert, material, false); + } + + for (int i = 0; i < p_B.faces.size(); i++) { + + if (cd.build_polys_B.has(i)) + continue; //made from buildpoly, skipping + + Vector3 points[3]; + Vector2 uvs[3]; + for (int j = 0; j < 3; j++) { + points[j] = p_B.faces[i].vertices[j]; + uvs[j] = p_B.faces[i].uvs[j]; + } + Ref<Material> material; + if (p_B.faces[i].material != -1) { + material = p_B.materials[p_B.faces[i].material]; + } + mesh_merge.add_face(points[0], points[1], points[2], uvs[0], uvs[1], uvs[2], p_B.faces[i].smooth, p_B.faces[i].invert, material, true); + } + + //mark faces that ended up inside the intersection + mesh_merge.mark_inside_faces(); + + //regen new brush to start filling it again + result.clear(); + + switch (p_operation) { + + case OPERATION_UNION: { + + int outside_count = 0; + + for (int i = 0; i < mesh_merge.faces.size(); i++) { + if (mesh_merge.faces[i].inside) + continue; + + outside_count++; + } + + result.faces.resize(outside_count); + + outside_count = 0; + + for (int i = 0; i < mesh_merge.faces.size(); i++) { + if (mesh_merge.faces[i].inside) + continue; + for (int j = 0; j < 3; j++) { + result.faces[outside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]]; + result.faces[outside_count].uvs[j] = mesh_merge.faces[i].uvs[j]; + } + + result.faces[outside_count].smooth = mesh_merge.faces[i].smooth; + result.faces[outside_count].invert = mesh_merge.faces[i].invert; + result.faces[outside_count].material = mesh_merge.faces[i].material_idx; + outside_count++; + } + + result._regen_face_aabbs(); + + } break; + case OPERATION_INTERSECTION: { + + int inside_count = 0; + + for (int i = 0; i < mesh_merge.faces.size(); i++) { + if (!mesh_merge.faces[i].inside) + continue; + + inside_count++; + } + + result.faces.resize(inside_count); + + inside_count = 0; + + for (int i = 0; i < mesh_merge.faces.size(); i++) { + if (!mesh_merge.faces[i].inside) + continue; + for (int j = 0; j < 3; j++) { + result.faces[inside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]]; + result.faces[inside_count].uvs[j] = mesh_merge.faces[i].uvs[j]; + } + + result.faces[inside_count].smooth = mesh_merge.faces[i].smooth; + result.faces[inside_count].invert = mesh_merge.faces[i].invert; + result.faces[inside_count].material = mesh_merge.faces[i].material_idx; + inside_count++; + } + + result._regen_face_aabbs(); + + } break; + case OPERATION_SUBSTRACTION: { + + int face_count = 0; + + for (int i = 0; i < mesh_merge.faces.size(); i++) { + if (mesh_merge.faces[i].from_b && !mesh_merge.faces[i].inside) + continue; + if (!mesh_merge.faces[i].from_b && mesh_merge.faces[i].inside) + continue; + + face_count++; + } + + result.faces.resize(face_count); + + face_count = 0; + + for (int i = 0; i < mesh_merge.faces.size(); i++) { + + if (mesh_merge.faces[i].from_b && !mesh_merge.faces[i].inside) + continue; + if (!mesh_merge.faces[i].from_b && mesh_merge.faces[i].inside) + continue; + + for (int j = 0; j < 3; j++) { + result.faces[face_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]]; + result.faces[face_count].uvs[j] = mesh_merge.faces[i].uvs[j]; + } + + if (mesh_merge.faces[i].from_b) { + //invert facing of insides of B + SWAP(result.faces[face_count].vertices[1], result.faces[face_count].vertices[2]); + SWAP(result.faces[face_count].uvs[1], result.faces[face_count].uvs[2]); + } + + result.faces[face_count].smooth = mesh_merge.faces[i].smooth; + result.faces[face_count].invert = mesh_merge.faces[i].invert; + result.faces[face_count].material = mesh_merge.faces[i].material_idx; + face_count++; + } + + result._regen_face_aabbs(); + + } break; + } + + //updatelist of materials + result.materials.resize(mesh_merge.materials.size()); + for (const Map<Ref<Material>, int>::Element *E = mesh_merge.materials.front(); E; E = E->next()) { + result.materials[E->get()] = E->key(); + } +} diff --git a/modules/csg/csg.h b/modules/csg/csg.h new file mode 100644 index 0000000000..d89e542b5e --- /dev/null +++ b/modules/csg/csg.h @@ -0,0 +1,206 @@ +#ifndef CSG_H +#define CSG_H + +#include "aabb.h" +#include "dvector.h" +#include "map.h" +#include "math_2d.h" +#include "oa_hash_map.h" +#include "plane.h" +#include "scene/resources/material.h" +#include "transform.h" +#include "vector3.h" + +struct CSGBrush { + + struct Face { + + Vector3 vertices[3]; + Vector2 uvs[3]; + AABB aabb; + bool smooth; + bool invert; + int material; + }; + + Vector<Face> faces; + Vector<Ref<Material> > materials; + + void _regen_face_aabbs(); + //create a brush from faces + void build_from_faces(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uvs, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials, const PoolVector<bool> &p_invert_faces); + void copy_from(const CSGBrush &p_brush, const Transform &p_xform); + + void clear(); +}; + +struct CSGBrushOperation { + + enum Operation { + OPERATION_UNION, + OPERATION_INTERSECTION, + OPERATION_SUBSTRACTION, + + }; + + struct MeshMerge { + + struct BVH { + int face; + int left; + int right; + int next; + Vector3 center; + AABB aabb; + }; + + struct BVHCmpX { + + bool operator()(const BVH *p_left, const BVH *p_right) const { + + return p_left->center.x < p_right->center.x; + } + }; + + struct BVHCmpY { + + bool operator()(const BVH *p_left, const BVH *p_right) const { + + return p_left->center.y < p_right->center.y; + } + }; + struct BVHCmpZ { + + bool operator()(const BVH *p_left, const BVH *p_right) const { + + return p_left->center.z < p_right->center.z; + } + }; + + int _bvh_count_intersections(BVH *bvhptr, int p_max_depth, int p_bvh_first, const Vector3 &p_begin, const Vector3 &p_end, int p_exclude) const; + int _create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc); + + struct VertexKey { + int32_t x, y, z; + _FORCE_INLINE_ bool operator<(const VertexKey &p_key) const { + if (x == p_key.x) { + if (y == p_key.y) { + return z < p_key.z; + } else { + return y < p_key.y; + } + } else { + return x < p_key.x; + } + } + + _FORCE_INLINE_ bool operator==(const VertexKey &p_key) const { + return (x == p_key.x && y == p_key.y && z == p_key.z); + } + }; + + struct VertexKeyHash { + static _FORCE_INLINE_ uint32_t hash(const VertexKey &p_vk) { + uint32_t h = hash_djb2_one_32(p_vk.x); + h = hash_djb2_one_32(p_vk.y, h); + h = hash_djb2_one_32(p_vk.z, h); + return h; + } + }; + + OAHashMap<VertexKey, int, 64, VertexKeyHash> snap_cache; + + Vector<Vector3> points; + + struct Face { + bool from_b; + bool inside; + int points[3]; + Vector2 uvs[3]; + bool smooth; + bool invert; + int material_idx; + }; + + Vector<Face> faces; + + Map<Ref<Material>, int> materials; + + Map<Vector3, int> vertex_map; + void add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector2 &p_uv_a, const Vector2 &p_uv_b, const Vector2 &p_uv_c, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b); + // void add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, bool p_from_b); + + float vertex_snap; + void mark_inside_faces(); + }; + + struct BuildPoly { + + Plane plane; + Transform to_poly; + Transform to_world; + int face_index; + + struct Point { + Vector2 point; + Vector2 uv; + }; + + Vector<Point> points; + + struct Edge { + bool outer; + int points[2]; + Edge() { + outer = false; + } + }; + + Vector<Edge> edges; + Ref<Material> material; + bool smooth; + bool invert; + + int base_edges; //edges from original triangle, even if split + + void _clip_segment(const CSGBrush *p_brush, int p_face, const Vector2 *segment, MeshMerge &mesh_merge, bool p_for_B); + + void create(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B); + void clip(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B); + }; + + struct PolyPoints { + + Vector<int> points; + + Vector<Vector<int> > holes; + }; + + struct EdgeSort { + int edge; + int prev_point; + int edge_point; + float angle; + bool operator<(const EdgeSort &p_edge) const { return angle < p_edge.angle; } + }; + + struct CallbackData { + const CSGBrush *A; + const CSGBrush *B; + int face_a; + CSGBrushOperation *self; + Map<int, BuildPoly> build_polys_A; + Map<int, BuildPoly> build_polys_B; + }; + + void _add_poly_points(const BuildPoly &p_poly, int p_edge, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<bool> &edge_process, Vector<PolyPoints> &r_poly); + void _add_poly_outline(const BuildPoly &p_poly, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<int> &r_outline); + void _merge_poly(MeshMerge &mesh, int p_face_idx, const BuildPoly &p_poly, bool p_from_b); + + void _collision_callback(const CSGBrush *A, int p_face_a, Map<int, BuildPoly> &build_polys_a, const CSGBrush *B, int p_face_b, Map<int, BuildPoly> &build_polys_b, MeshMerge &mesh_merge); + + static void _collision_callbacks(void *ud, int p_face_b); + void merge_brushes(Operation p_operation, const CSGBrush &p_A, const CSGBrush &p_B, CSGBrush &result, float p_snap = 0.001); +}; + +#endif // CSG_H diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp new file mode 100644 index 0000000000..06cbaab3b0 --- /dev/null +++ b/modules/csg/csg_gizmos.cpp @@ -0,0 +1,315 @@ +#include "csg_gizmos.h" + +/////////// + +String CSGShapeSpatialGizmo::get_handle_name(int p_idx) const { + + if (Object::cast_to<CSGSphere>(cs)) { + + return "Radius"; + } + + if (Object::cast_to<CSGBox>(cs)) { + + static const char *hname[3] = { "Width", "Height", "Depth" }; + return hname[p_idx]; + } + + if (Object::cast_to<CSGCylinder>(cs)) { + + return p_idx == 0 ? "Radius" : "Height"; + } + + if (Object::cast_to<CSGTorus>(cs)) { + + return p_idx == 0 ? "InnerRadius" : "OuterRadius"; + } + + return ""; +} +Variant CSGShapeSpatialGizmo::get_handle_value(int p_idx) const { + + if (Object::cast_to<CSGSphere>(cs)) { + + CSGSphere *s = Object::cast_to<CSGSphere>(cs); + return s->get_radius(); + } + + if (Object::cast_to<CSGBox>(cs)) { + + CSGBox *s = Object::cast_to<CSGBox>(cs); + switch (p_idx) { + case 0: return s->get_width(); + case 1: return s->get_height(); + case 2: return s->get_depth(); + } + } + + if (Object::cast_to<CSGCylinder>(cs)) { + + CSGCylinder *s = Object::cast_to<CSGCylinder>(cs); + return p_idx == 0 ? s->get_radius() : s->get_height(); + } + + if (Object::cast_to<CSGTorus>(cs)) { + + CSGTorus *s = Object::cast_to<CSGTorus>(cs); + return p_idx == 0 ? s->get_inner_radius() : s->get_outer_radius(); + } + + return Variant(); +} +void CSGShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) { + + Transform gt = cs->get_global_transform(); + gt.orthonormalize(); + Transform gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; + + if (Object::cast_to<CSGSphere>(cs)) { + + CSGSphere *s = Object::cast_to<CSGSphere>(cs); + + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); + float d = ra.x; + if (d < 0.001) + d = 0.001; + + s->set_radius(d); + } + + if (Object::cast_to<CSGBox>(cs)) { + + CSGBox *s = Object::cast_to<CSGBox>(cs); + + Vector3 axis; + axis[p_idx] = 1.0; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = ra[p_idx]; + if (d < 0.001) + d = 0.001; + + switch (p_idx) { + case 0: s->set_width(d); break; + case 1: s->set_height(d); break; + case 2: s->set_depth(d); break; + } + } + + if (Object::cast_to<CSGCylinder>(cs)) { + + CSGCylinder *s = Object::cast_to<CSGCylinder>(cs); + + Vector3 axis; + axis[p_idx == 0 ? 0 : 1] = 1.0; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = axis.dot(ra); + + if (d < 0.001) + d = 0.001; + + if (p_idx == 0) + s->set_radius(d); + else if (p_idx == 1) + s->set_height(d * 2.0); + } + + if (Object::cast_to<CSGTorus>(cs)) { + + CSGTorus *s = Object::cast_to<CSGTorus>(cs); + + Vector3 axis; + axis[0] = 1.0; + Vector3 ra, rb; + Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = axis.dot(ra); + + if (d < 0.001) + d = 0.001; + + if (p_idx == 0) + s->set_inner_radius(d); + else if (p_idx == 1) + s->set_outer_radius(d); + } +} +void CSGShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { + + if (Object::cast_to<CSGSphere>(cs)) { + CSGSphere *s = Object::cast_to<CSGSphere>(cs); + if (p_cancel) { + s->set_radius(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Sphere Shape Radius")); + ur->add_do_method(s, "set_radius", s->get_radius()); + ur->add_undo_method(s, "set_radius", p_restore); + ur->commit_action(); + } + + if (Object::cast_to<CSGBox>(cs)) { + CSGBox *s = Object::cast_to<CSGBox>(cs); + if (p_cancel) { + switch (p_idx) { + case 0: s->set_width(p_restore); break; + case 1: s->set_height(p_restore); break; + case 2: s->set_depth(p_restore); break; + } + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Box Shape Extents")); + static const char *method[3] = { "set_width", "set_height", "set_depth" }; + float current; + switch (p_idx) { + case 0: current = s->get_width(); break; + case 1: current = s->get_height(); break; + case 2: current = s->get_depth(); break; + } + + ur->add_do_method(s, method[p_idx], current); + ur->add_undo_method(s, method[p_idx], p_restore); + ur->commit_action(); + } + + if (Object::cast_to<CSGCylinder>(cs)) { + CSGCylinder *s = Object::cast_to<CSGCylinder>(cs); + if (p_cancel) { + if (p_idx == 0) + s->set_radius(p_restore); + else + s->set_height(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + if (p_idx == 0) { + ur->create_action(TTR("Change Cylinder Radius")); + ur->add_do_method(s, "set_radius", s->get_radius()); + ur->add_undo_method(s, "set_radius", p_restore); + } else { + ur->create_action(TTR("Change Cylinder Height")); + ur->add_do_method(s, "set_height", s->get_height()); + ur->add_undo_method(s, "set_height", p_restore); + } + + ur->commit_action(); + } + + if (Object::cast_to<CSGTorus>(cs)) { + CSGTorus *s = Object::cast_to<CSGTorus>(cs); + if (p_cancel) { + if (p_idx == 0) + s->set_inner_radius(p_restore); + else + s->set_outer_radius(p_restore); + return; + } + + UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo(); + if (p_idx == 0) { + ur->create_action(TTR("Change Torus Inner Radius")); + ur->add_do_method(s, "set_inner_radius", s->get_inner_radius()); + ur->add_undo_method(s, "set_inner_radius", p_restore); + } else { + ur->create_action(TTR("Change Torus Outer Radius")); + ur->add_do_method(s, "set_outer_radius", s->get_outer_radius()); + ur->add_undo_method(s, "set_outer_radius", p_restore); + } + + ur->commit_action(); + } +} +void CSGShapeSpatialGizmo::redraw() { + + clear(); + + Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/csg"); + Ref<Material> material = create_material("shape_material", gizmo_color); + + PoolVector<Vector3> faces = cs->get_brush_faces(); + + Vector<Vector3> lines; + lines.resize(faces.size() * 2); + { + PoolVector<Vector3>::Read r = faces.read(); + + for (int i = 0; i < lines.size(); i += 6) { + int f = i / 6; + for (int j = 0; j < 3; j++) { + int j_n = (j + 1) % 3; + lines[i + j * 2 + 0] = r[f * 3 + j]; + lines[i + j * 2 + 1] = r[f * 3 + j_n]; + } + } + } + + add_lines(lines, material); + add_collision_segments(lines); + + if (Object::cast_to<CSGSphere>(cs)) { + CSGSphere *s = Object::cast_to<CSGSphere>(cs); + + float r = s->get_radius(); + Vector<Vector3> handles; + handles.push_back(Vector3(r, 0, 0)); + add_handles(handles); + } + + if (Object::cast_to<CSGBox>(cs)) { + CSGBox *s = Object::cast_to<CSGBox>(cs); + + Vector<Vector3> handles; + handles.push_back(Vector3(s->get_width(), 0, 0)); + handles.push_back(Vector3(0, s->get_height(), 0)); + handles.push_back(Vector3(0, 0, s->get_depth())); + add_handles(handles); + } + + if (Object::cast_to<CSGCylinder>(cs)) { + CSGCylinder *s = Object::cast_to<CSGCylinder>(cs); + + Vector<Vector3> handles; + handles.push_back(Vector3(s->get_radius(), 0, 0)); + handles.push_back(Vector3(0, s->get_height() * 0.5, 0)); + add_handles(handles); + } + + if (Object::cast_to<CSGTorus>(cs)) { + CSGTorus *s = Object::cast_to<CSGTorus>(cs); + + Vector<Vector3> handles; + handles.push_back(Vector3(s->get_inner_radius(), 0, 0)); + handles.push_back(Vector3(s->get_outer_radius(), 0, 0)); + add_handles(handles); + } +} +CSGShapeSpatialGizmo::CSGShapeSpatialGizmo(CSGShape *p_cs) { + + cs = p_cs; + set_spatial_node(p_cs); +} + +Ref<SpatialEditorGizmo> EditorPluginCSG::create_spatial_gizmo(Spatial *p_spatial) { + if (Object::cast_to<CSGSphere>(p_spatial) || Object::cast_to<CSGBox>(p_spatial) || Object::cast_to<CSGCylinder>(p_spatial) || Object::cast_to<CSGTorus>(p_spatial) || Object::cast_to<CSGMesh>(p_spatial) || Object::cast_to<CSGPolygon>(p_spatial)) { + Ref<CSGShapeSpatialGizmo> csg = memnew(CSGShapeSpatialGizmo(Object::cast_to<CSGShape>(p_spatial))); + return csg; + } + + return Ref<SpatialEditorGizmo>(); +} + +EditorPluginCSG::EditorPluginCSG(EditorNode *p_editor) { + + EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.2, 0.5, 1, 0.1)); +} diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h new file mode 100644 index 0000000000..b5e394ecad --- /dev/null +++ b/modules/csg/csg_gizmos.h @@ -0,0 +1,30 @@ +#ifndef CSG_GIZMOS_H +#define CSG_GIZMOS_H + +#include "csg_shape.h" +#include "editor/editor_plugin.h" +#include "editor/spatial_editor_gizmos.h" + +class CSGShapeSpatialGizmo : public EditorSpatialGizmo { + + GDCLASS(CSGShapeSpatialGizmo, EditorSpatialGizmo); + + CSGShape *cs; + +public: + virtual String get_handle_name(int p_idx) const; + virtual Variant get_handle_value(int p_idx) const; + virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); + virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); + void redraw(); + CSGShapeSpatialGizmo(CSGShape *p_cs = NULL); +}; + +class EditorPluginCSG : public EditorPlugin { + GDCLASS(EditorPluginCSG, EditorPlugin) +public: + virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial); + EditorPluginCSG(EditorNode *p_editor); +}; + +#endif // CSG_GIZMOS_H diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp new file mode 100644 index 0000000000..279edbcec5 --- /dev/null +++ b/modules/csg/csg_shape.cpp @@ -0,0 +1,2125 @@ +#include "csg_shape.h" +#include "scene/3d/path.h" + +void CSGShape::set_use_collision(bool p_enable) { + + if (use_collision == p_enable) + return; + + use_collision = p_enable; + + if (!is_inside_tree() || !is_root_shape()) + return; + + if (use_collision) { + root_collision_shape.instance(); + root_collision_instance = PhysicsServer::get_singleton()->body_create(PhysicsServer::BODY_MODE_STATIC); + PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform()); + PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid()); + PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space()); + _make_dirty(); //force update + } else { + PhysicsServer::get_singleton()->free(root_collision_instance); + root_collision_instance = RID(); + root_collision_shape.unref(); + } +} + +bool CSGShape::is_using_collision() const { + return use_collision; +} + +bool CSGShape::is_root_shape() const { + + return !parent; +} + +void CSGShape::set_snap(float p_snap) { + snap = p_snap; +} + +float CSGShape::get_snap() const { + return snap; +} + +void CSGShape::_make_dirty() { + + if (!is_inside_tree()) + return; + + if (dirty) { + return; + } + + dirty = true; + + if (parent) { + parent->_make_dirty(); + } else { + //only parent will do + call_deferred("_update_shape"); + } +} + +CSGBrush *CSGShape::_get_brush() { + + if (dirty) { + if (brush) { + memdelete(brush); + } + brush = NULL; + + CSGBrush *n = _build_brush(); + + for (int i = 0; i < get_child_count(); i++) { + + CSGShape *child = Object::cast_to<CSGShape>(get_child(i)); + if (!child) + continue; + if (!child->is_visible_in_tree()) + continue; + + CSGBrush *n2 = child->_get_brush(); + if (!n2) + continue; + if (!n) { + n = memnew(CSGBrush); + + n->copy_from(*n2, child->get_transform()); + + } else { + + CSGBrush *nn = memnew(CSGBrush); + CSGBrush *nn2 = memnew(CSGBrush); + nn2->copy_from(*n2, child->get_transform()); + + CSGBrushOperation bop; + + switch (child->get_operation()) { + case CSGShape::OPERATION_UNION: bop.merge_brushes(CSGBrushOperation::OPERATION_UNION, *n, *nn2, *nn, snap); break; + case CSGShape::OPERATION_INTERSECTION: bop.merge_brushes(CSGBrushOperation::OPERATION_INTERSECTION, *n, *nn2, *nn, snap); break; + case CSGShape::OPERATION_SUBTRACTION: bop.merge_brushes(CSGBrushOperation::OPERATION_SUBSTRACTION, *n, *nn2, *nn, snap); break; + } + memdelete(n); + memdelete(nn2); + n = nn; + } + } + + if (n) { + AABB aabb; + for (int i = 0; i < n->faces.size(); i++) { + for (int j = 0; j < 3; j++) { + if (i == 0 && j == 0) + aabb.position = n->faces[i].vertices[j]; + else + aabb.expand_to(n->faces[i].vertices[j]); + } + } + node_aabb = aabb; + } else { + node_aabb = AABB(); + } + + brush = n; + + dirty = false; + } + + return brush; +} + +void CSGShape::_update_shape() { + + //print_line("updating shape for " + String(get_path())); + set_base(RID()); + root_mesh.unref(); //byebye root mesh + + CSGBrush *n = _get_brush(); + ERR_FAIL_COND(!n); + + OAHashMap<Vector3, Vector3> vec_map; + + Vector<int> face_count; + face_count.resize(n->materials.size() + 1); + for (int i = 0; i < face_count.size(); i++) { + face_count[i] = 0; + } + + for (int i = 0; i < n->faces.size(); i++) { + int mat = n->faces[i].material; + ERR_CONTINUE(mat < -1 || mat >= face_count.size()); + int idx = mat == -1 ? face_count.size() - 1 : mat; + if (n->faces[i].smooth) { + + Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]); + + for (int j = 0; j < 3; j++) { + Vector3 v = n->faces[i].vertices[j]; + Vector3 add; + if (vec_map.lookup(v, &add)) { + add += p.normal; + } else { + add = p.normal; + } + vec_map.set(v, add); + } + } + + face_count[idx]++; + } + + Vector<ShapeUpdateSurface> surfaces; + + surfaces.resize(face_count.size()); + + //create arrays + for (int i = 0; i < surfaces.size(); i++) { + + surfaces[i].vertices.resize(face_count[i] * 3); + surfaces[i].normals.resize(face_count[i] * 3); + surfaces[i].uvs.resize(face_count[i] * 3); + surfaces[i].last_added = 0; + + if (i != surfaces.size() - 1) { + surfaces[i].material = n->materials[i]; + } + + surfaces[i].verticesw = surfaces[i].vertices.write(); + surfaces[i].normalsw = surfaces[i].normals.write(); + surfaces[i].uvsw = surfaces[i].uvs.write(); + } + + //fill arrays + PoolVector<Vector3> physics_faces; + bool fill_physics_faces = false; + if (root_collision_shape.is_valid()) { + physics_faces.resize(n->faces.size() * 3); + fill_physics_faces = true; + } + + { + PoolVector<Vector3>::Write physicsw; + + if (fill_physics_faces) { + physicsw = physics_faces.write(); + } + + for (int i = 0; i < n->faces.size(); i++) { + + int order[3] = { 0, 1, 2 }; + + if (n->faces[i].invert) { + SWAP(order[1], order[2]); + } + + if (fill_physics_faces) { + physicsw[i * 3 + 0] = n->faces[i].vertices[order[0]]; + physicsw[i * 3 + 1] = n->faces[i].vertices[order[1]]; + physicsw[i * 3 + 2] = n->faces[i].vertices[order[2]]; + } + + int mat = n->faces[i].material; + ERR_CONTINUE(mat < -1 || mat >= face_count.size()); + int idx = mat == -1 ? face_count.size() - 1 : mat; + + int last = surfaces[idx].last_added; + + Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]); + + for (int j = 0; j < 3; j++) { + + Vector3 v = n->faces[i].vertices[j]; + + Vector3 normal = p.normal; + + if (n->faces[i].smooth && vec_map.lookup(v, &normal)) { + normal.normalize(); + } + + if (n->faces[i].invert) { + + normal = -normal; + } + + surfaces[idx].verticesw[last + order[j]] = v; + surfaces[idx].uvsw[last + order[j]] = n->faces[i].uvs[j]; + surfaces[idx].normalsw[last + order[j]] = normal; + } + + surfaces[idx].last_added += 3; + } + } + + root_mesh.instance(); + //create surfaces + + for (int i = 0; i < surfaces.size(); i++) { + + surfaces[i].verticesw = PoolVector<Vector3>::Write(); + surfaces[i].normalsw = PoolVector<Vector3>::Write(); + surfaces[i].uvsw = PoolVector<Vector2>::Write(); + + if (surfaces[i].last_added == 0) + continue; + + Array array; + array.resize(Mesh::ARRAY_MAX); + + array[Mesh::ARRAY_VERTEX] = surfaces[i].vertices; + array[Mesh::ARRAY_NORMAL] = surfaces[i].normals; + array[Mesh::ARRAY_TEX_UV] = surfaces[i].uvs; + + int idx = root_mesh->get_surface_count(); + root_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array); + root_mesh->surface_set_material(idx, surfaces[i].material); + } + + if (root_collision_shape.is_valid()) { + root_collision_shape->set_faces(physics_faces); + } + + set_base(root_mesh->get_rid()); +} +AABB CSGShape::get_aabb() const { + return node_aabb; +} + +PoolVector<Vector3> CSGShape::get_brush_faces() { + ERR_FAIL_COND_V(!is_inside_tree(), PoolVector<Vector3>()); + CSGBrush *b = _get_brush(); + if (!b) { + return PoolVector<Vector3>(); + } + + PoolVector<Vector3> faces; + int fc = b->faces.size(); + faces.resize(fc * 3); + { + PoolVector<Vector3>::Write w = faces.write(); + for (int i = 0; i < fc; i++) { + w[i * 3 + 0] = b->faces[i].vertices[0]; + w[i * 3 + 1] = b->faces[i].vertices[1]; + w[i * 3 + 2] = b->faces[i].vertices[2]; + } + } + + return faces; +} + +PoolVector<Face3> CSGShape::get_faces(uint32_t p_usage_flags) const { + + return PoolVector<Face3>(); +} + +void CSGShape::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + Node *parentn = get_parent(); + if (parentn) { + parent = Object::cast_to<CSGShape>(parentn); + } + + if (use_collision && is_root_shape()) { + root_collision_shape.instance(); + root_collision_instance = PhysicsServer::get_singleton()->body_create(PhysicsServer::BODY_MODE_STATIC); + PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform()); + PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid()); + PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space()); + } + + _make_dirty(); + } + + if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { + + //print_line("local xform changed"); + if (parent) { + parent->_make_dirty(); + } + } + + if (p_what == NOTIFICATION_EXIT_TREE) { + if (parent) + parent->_make_dirty(); + parent = NULL; + + if (use_collision && is_root_shape()) { + PhysicsServer::get_singleton()->free(root_collision_instance); + root_collision_instance = RID(); + root_collision_shape.unref(); + } + _make_dirty(); + } +} + +void CSGShape::set_operation(Operation p_operation) { + + operation = p_operation; + _make_dirty(); +} + +CSGShape::Operation CSGShape::get_operation() const { + return operation; +} + +void CSGShape::_validate_property(PropertyInfo &property) const { + if (is_inside_tree() && property.name.begins_with("use_collision") && !is_root_shape()) { + //hide collision if not root + property.usage = PROPERTY_USAGE_NOEDITOR; + } +} + +void CSGShape::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_update_shape"), &CSGShape::_update_shape); + ClassDB::bind_method(D_METHOD("is_root_shape"), &CSGShape::is_root_shape); + + ClassDB::bind_method(D_METHOD("set_operation", "operation"), &CSGShape::set_operation); + ClassDB::bind_method(D_METHOD("get_operation"), &CSGShape::get_operation); + + ClassDB::bind_method(D_METHOD("set_use_collision", "operation"), &CSGShape::set_use_collision); + ClassDB::bind_method(D_METHOD("is_using_collision"), &CSGShape::is_using_collision); + + ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap); + ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap"); + + BIND_CONSTANT(OPERATION_UNION); + BIND_CONSTANT(OPERATION_INTERSECTION); + BIND_CONSTANT(OPERATION_SUBTRACTION); +} + +CSGShape::CSGShape() { + brush = NULL; + set_notify_local_transform(true); + dirty = false; + parent = NULL; + use_collision = false; + operation = OPERATION_UNION; + snap = 0.001; +} + +CSGShape::~CSGShape() { + if (brush) { + memdelete(brush); + brush = NULL; + } +} +////////////////////////////////// + +CSGBrush *CSGCombiner::_build_brush() { + + return NULL; //does not build anything +} + +CSGCombiner::CSGCombiner() { +} + +///////////////////// + +CSGBrush *CSGPrimitive::_create_brush_from_arrays(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uv, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials) { + + CSGBrush *brush = memnew(CSGBrush); + + PoolVector<bool> invert; + invert.resize(p_vertices.size() / 3); + { + int ic = invert.size(); + PoolVector<bool>::Write w = invert.write(); + for (int i = 0; i < ic; i++) { + w[i] = invert_faces; + } + } + brush->build_from_faces(p_vertices, p_uv, p_smooth, p_materials, invert); + + return brush; +} + +void CSGPrimitive::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_invert_faces", "invert_faces"), &CSGPrimitive::set_invert_faces); + ClassDB::bind_method(D_METHOD("is_inverting_faces"), &CSGPrimitive::is_inverting_faces); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_faces"), "set_invert_faces", "is_inverting_faces"); +} + +void CSGPrimitive::set_invert_faces(bool p_invert) { + if (invert_faces == p_invert) + return; + + invert_faces = p_invert; + + _make_dirty(); +} + +bool CSGPrimitive::is_inverting_faces() { + return invert_faces; +} + +CSGPrimitive::CSGPrimitive() { + invert_faces = false; +} + +///////////////////// + +CSGBrush *CSGMesh::_build_brush() { + + if (!mesh.is_valid()) + return NULL; + + PoolVector<Vector3> vertices; + PoolVector<bool> smooth; + PoolVector<Ref<Material> > materials; + PoolVector<Vector2> uvs; + + for (int i = 0; i < mesh->get_surface_count(); i++) { + + if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { + continue; + } + + Array arrays = mesh->surface_get_arrays(i); + + PoolVector<Vector3> avertices = arrays[Mesh::ARRAY_VERTEX]; + if (avertices.size() == 0) + continue; + + PoolVector<Vector3>::Read vr = avertices.read(); + + PoolVector<Vector3> anormals = arrays[Mesh::ARRAY_NORMAL]; + PoolVector<Vector3>::Read nr; + bool nr_used = false; + if (anormals.size()) { + nr = anormals.read(); + nr_used = true; + } + + PoolVector<Vector2> auvs = arrays[Mesh::ARRAY_TEX_UV]; + PoolVector<Vector2>::Read uvr; + bool uvr_used = false; + if (auvs.size()) { + uvr = auvs.read(); + uvr_used = true; + } + + Ref<Material> mat = mesh->surface_get_material(i); + + PoolVector<int> aindices = arrays[Mesh::ARRAY_INDEX]; + if (aindices.size()) { + int as = vertices.size(); + int is = aindices.size(); + + vertices.resize(as + is); + smooth.resize((as + is) / 3); + materials.resize((as + is) / 3); + uvs.resize(as + is); + + PoolVector<Vector3>::Write vw = vertices.write(); + PoolVector<bool>::Write sw = smooth.write(); + PoolVector<Vector2>::Write uvw = uvs.write(); + PoolVector<Ref<Material> >::Write mw = materials.write(); + + PoolVector<int>::Read ir = aindices.read(); + + for (int j = 0; j < is; j += 3) { + + Vector3 vertex[3]; + Vector3 normal[3]; + Vector2 uv[3]; + + for (int k = 0; k < 3; k++) { + int idx = ir[j + k]; + vertex[k] = vr[idx]; + if (nr_used) { + normal[k] = nr[idx]; + } + if (uvr_used) { + uv[k] = uvr[idx]; + } + } + + bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON; + + vw[as + j + 0] = vertex[0]; + vw[as + j + 1] = vertex[1]; + vw[as + j + 2] = vertex[2]; + + uvw[as + j + 0] = uv[0]; + uvw[as + j + 1] = uv[1]; + uvw[as + j + 2] = uv[2]; + + sw[j / 3] = !flat; + mw[j / 3] = mat; + } + } else { + int is = vertices.size(); + int as = avertices.size(); + + vertices.resize(as + is); + smooth.resize((as + is) / 3); + uvs.resize(as + is); + materials.resize((as + is) / 3); + + PoolVector<Vector3>::Write vw = vertices.write(); + PoolVector<bool>::Write sw = smooth.write(); + PoolVector<Vector2>::Write uvw = uvs.write(); + PoolVector<Ref<Material> >::Write mw = materials.write(); + + for (int j = 0; j < is; j += 3) { + + Vector3 vertex[3]; + Vector3 normal[3]; + Vector2 uv[3]; + + for (int k = 0; k < 3; k++) { + vertex[k] = vr[j + k]; + if (nr_used) { + normal[k] = nr[j + k]; + } + if (uvr_used) { + uv[k] = uvr[j + k]; + } + } + + bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON; + + vw[as + j + 0] = vertex[0]; + vw[as + j + 1] = vertex[1]; + vw[as + j + 2] = vertex[2]; + + uvw[as + j + 0] = uv[0]; + uvw[as + j + 1] = uv[1]; + uvw[as + j + 2] = uv[2]; + + sw[j / 3] = !flat; + mw[j / 3] = mat; + } + } + } + + //print_line("total vertices? " + itos(vertices.size())); + if (vertices.size() == 0) + return NULL; + + return _create_brush_from_arrays(vertices, uvs, smooth, materials); +} + +void CSGMesh::_mesh_changed() { + _make_dirty(); + update_gizmo(); +} + +void CSGMesh::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CSGMesh::set_mesh); + ClassDB::bind_method(D_METHOD("get_mesh"), &CSGMesh::get_mesh); + + ClassDB::bind_method(D_METHOD("_mesh_changed"), &CSGMesh::_mesh_changed); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); +} + +void CSGMesh::set_mesh(const Ref<Mesh> &p_mesh) { + + if (mesh == p_mesh) + return; + if (mesh.is_valid()) { + mesh->disconnect("changed", this, "_mesh_changed"); + } + mesh = p_mesh; + + if (mesh.is_valid()) { + mesh->connect("changed", this, "_mesh_changed"); + } + + _make_dirty(); +} + +Ref<Mesh> CSGMesh::get_mesh() { + return mesh; +} + +//////////////////////////////// + +CSGBrush *CSGSphere::_build_brush() { + + // set our bounding box + + CSGBrush *brush = memnew(CSGBrush); + + int face_count = rings * radial_segments * 2 - radial_segments * 2; + + bool invert_val = is_inverting_faces(); + Ref<Material> material = get_material(); + + PoolVector<Vector3> faces; + PoolVector<Vector2> uvs; + PoolVector<bool> smooth; + PoolVector<Ref<Material> > materials; + PoolVector<bool> invert; + + faces.resize(face_count * 3); + uvs.resize(face_count * 3); + + smooth.resize(face_count); + materials.resize(face_count); + invert.resize(face_count); + + { + + PoolVector<Vector3>::Write facesw = faces.write(); + PoolVector<Vector2>::Write uvsw = uvs.write(); + PoolVector<bool>::Write smoothw = smooth.write(); + PoolVector<Ref<Material> >::Write materialsw = materials.write(); + PoolVector<bool>::Write invertw = invert.write(); + + int face = 0; + + for (int i = 1; i <= rings; i++) { + double lat0 = Math_PI * (-0.5 + (double)(i - 1) / rings); + double z0 = Math::sin(lat0); + double zr0 = Math::cos(lat0); + double u0 = double(i - 1) / rings; + + double lat1 = Math_PI * (-0.5 + (double)i / rings); + double z1 = Math::sin(lat1); + double zr1 = Math::cos(lat1); + double u1 = double(i) / rings; + + for (int j = radial_segments; j >= 1; j--) { + + double lng0 = 2 * Math_PI * (double)(j - 1) / radial_segments; + double x0 = Math::cos(lng0); + double y0 = Math::sin(lng0); + double v0 = double(i - 1) / radial_segments; + + double lng1 = 2 * Math_PI * (double)(j) / radial_segments; + double x1 = Math::cos(lng1); + double y1 = Math::sin(lng1); + double v1 = double(i) / radial_segments; + + Vector3 v[4] = { + Vector3(x1 * zr0, z0, y1 * zr0) * radius, + Vector3(x1 * zr1, z1, y1 * zr1) * radius, + Vector3(x0 * zr1, z1, y0 * zr1) * radius, + Vector3(x0 * zr0, z0, y0 * zr0) * radius + }; + + Vector2 u[4] = { + Vector2(v1, u0), + Vector2(v1, u1), + Vector2(v0, u1), + Vector2(v0, u0), + + }; + + if (i < rings) { + + //face 1 + facesw[face * 3 + 0] = v[0]; + facesw[face * 3 + 1] = v[1]; + facesw[face * 3 + 2] = v[2]; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[1]; + uvsw[face * 3 + 2] = u[2]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + } + + if (i > 1) { + //face 2 + facesw[face * 3 + 0] = v[2]; + facesw[face * 3 + 1] = v[3]; + facesw[face * 3 + 2] = v[0]; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[3]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + } + } + } + + if (face != face_count) { + ERR_PRINT("Face mismatch bug! fix code"); + } + } + + brush->build_from_faces(faces, uvs, smooth, materials, invert); + + return brush; +} + +void CSGSphere::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGSphere::set_radius); + ClassDB::bind_method(D_METHOD("get_radius"), &CSGSphere::get_radius); + + ClassDB::bind_method(D_METHOD("set_radial_segments", "radial_segments"), &CSGSphere::set_radial_segments); + ClassDB::bind_method(D_METHOD("get_radial_segments"), &CSGSphere::get_radial_segments); + ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CSGSphere::set_rings); + ClassDB::bind_method(D_METHOD("get_rings"), &CSGSphere::get_rings); + + ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGSphere::set_smooth_faces); + ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGSphere::get_smooth_faces); + + ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGSphere::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CSGSphere::get_material); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); +} + +void CSGSphere::set_radius(const float p_radius) { + ERR_FAIL_COND(p_radius <= 0); + radius = p_radius; + _make_dirty(); + update_gizmo(); +} + +float CSGSphere::get_radius() const { + return radius; +} + +void CSGSphere::set_radial_segments(const int p_radial_segments) { + radial_segments = p_radial_segments > 4 ? p_radial_segments : 4; + _make_dirty(); + update_gizmo(); +} + +int CSGSphere::get_radial_segments() const { + return radial_segments; +} + +void CSGSphere::set_rings(const int p_rings) { + rings = p_rings > 1 ? p_rings : 1; + _make_dirty(); + update_gizmo(); +} + +int CSGSphere::get_rings() const { + return rings; +} + +void CSGSphere::set_smooth_faces(const bool p_smooth_faces) { + smooth_faces = p_smooth_faces; + _make_dirty(); +} + +bool CSGSphere::get_smooth_faces() const { + return smooth_faces; +} + +void CSGSphere::set_material(const Ref<Material> &p_material) { + + material = p_material; + _make_dirty(); +} + +Ref<Material> CSGSphere::get_material() const { + + return material; +} + +CSGSphere::CSGSphere() { + // defaults + radius = 1.0; + radial_segments = 12; + rings = 6; + smooth_faces = true; +} + +/////////////// + +CSGBrush *CSGBox::_build_brush() { + + // set our bounding box + + CSGBrush *brush = memnew(CSGBrush); + + int face_count = 12; //it's a cube.. + + bool invert_val = is_inverting_faces(); + Ref<Material> material = get_material(); + + PoolVector<Vector3> faces; + PoolVector<Vector2> uvs; + PoolVector<bool> smooth; + PoolVector<Ref<Material> > materials; + PoolVector<bool> invert; + + faces.resize(face_count * 3); + uvs.resize(face_count * 3); + + smooth.resize(face_count); + materials.resize(face_count); + invert.resize(face_count); + + { + + PoolVector<Vector3>::Write facesw = faces.write(); + PoolVector<Vector2>::Write uvsw = uvs.write(); + PoolVector<bool>::Write smoothw = smooth.write(); + PoolVector<Ref<Material> >::Write materialsw = materials.write(); + PoolVector<bool>::Write invertw = invert.write(); + + int face = 0; + + Vector3 vertex_mul(width, height, depth); + + { + + for (int i = 0; i < 6; i++) { + + Vector3 face_points[4]; + float uv_points[8] = { 0, 0, 0, 1, 1, 1, 1, 0 }; + + for (int j = 0; j < 4; j++) { + + float v[3]; + v[0] = 1.0; + v[1] = 1 - 2 * ((j >> 1) & 1); + v[2] = v[1] * (1 - 2 * (j & 1)); + + for (int k = 0; k < 3; k++) { + + if (i < 3) + face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + else + face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + } + } + + Vector2 u[4]; + for (int j = 0; j < 4; j++) { + u[j] = Vector2(uv_points[j * 2 + 0], uv_points[j * 2 + 1]); + } + + //face 1 + facesw[face * 3 + 0] = face_points[0] * vertex_mul; + facesw[face * 3 + 1] = face_points[1] * vertex_mul; + facesw[face * 3 + 2] = face_points[2] * vertex_mul; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[1]; + uvsw[face * 3 + 2] = u[2]; + + smoothw[face] = false; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + //face 1 + facesw[face * 3 + 0] = face_points[2] * vertex_mul; + facesw[face * 3 + 1] = face_points[3] * vertex_mul; + facesw[face * 3 + 2] = face_points[0] * vertex_mul; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[3]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = false; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + } + } + + if (face != face_count) { + ERR_PRINT("Face mismatch bug! fix code"); + } + } + + brush->build_from_faces(faces, uvs, smooth, materials, invert); + + return brush; +} + +void CSGBox::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_width", "width"), &CSGBox::set_width); + ClassDB::bind_method(D_METHOD("get_width"), &CSGBox::get_width); + + ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGBox::set_height); + ClassDB::bind_method(D_METHOD("get_height"), &CSGBox::get_height); + + ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGBox::set_depth); + ClassDB::bind_method(D_METHOD("get_depth"), &CSGBox::get_depth); + + ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CSGBox::get_material); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "width", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_width", "get_width"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_depth", "get_depth"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); +} + +void CSGBox::set_width(const float p_width) { + width = p_width; + _make_dirty(); + update_gizmo(); +} + +float CSGBox::get_width() const { + return width; +} + +void CSGBox::set_height(const float p_height) { + height = p_height; + _make_dirty(); + update_gizmo(); +} + +float CSGBox::get_height() const { + return height; +} + +void CSGBox::set_depth(const float p_depth) { + depth = p_depth; + _make_dirty(); + update_gizmo(); +} + +float CSGBox::get_depth() const { + return depth; +} + +void CSGBox::set_material(const Ref<Material> &p_material) { + + material = p_material; + _make_dirty(); + update_gizmo(); +} + +Ref<Material> CSGBox::get_material() const { + + return material; +} + +CSGBox::CSGBox() { + // defaults + width = 1.0; + height = 1.0; + depth = 1.0; +} + +/////////////// + +CSGBrush *CSGCylinder::_build_brush() { + + // set our bounding box + + CSGBrush *brush = memnew(CSGBrush); + + int face_count = sides * (cone ? 1 : 2) + sides + (cone ? 0 : sides); + + bool invert_val = is_inverting_faces(); + Ref<Material> material = get_material(); + + PoolVector<Vector3> faces; + PoolVector<Vector2> uvs; + PoolVector<bool> smooth; + PoolVector<Ref<Material> > materials; + PoolVector<bool> invert; + + faces.resize(face_count * 3); + uvs.resize(face_count * 3); + + smooth.resize(face_count); + materials.resize(face_count); + invert.resize(face_count); + + { + + PoolVector<Vector3>::Write facesw = faces.write(); + PoolVector<Vector2>::Write uvsw = uvs.write(); + PoolVector<bool>::Write smoothw = smooth.write(); + PoolVector<Ref<Material> >::Write materialsw = materials.write(); + PoolVector<bool>::Write invertw = invert.write(); + + int face = 0; + + Vector3 vertex_mul(radius, height * 0.5, radius); + + { + + for (int i = 0; i < sides; i++) { + + float inc = float(i) / sides; + float inc_n = float((i + 1)) / sides; + + float ang = inc * Math_PI * 2.0; + float ang_n = inc_n * Math_PI * 2.0; + + Vector3 base(Math::cos(ang), 0, Math::sin(ang)); + Vector3 base_n(Math::cos(ang_n), 0, Math::sin(ang_n)); + + Vector3 face_points[4] = { + base + Vector3(0, -1, 0), + base_n + Vector3(0, -1, 0), + base_n * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0), + base * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0), + }; + + Vector2 u[4] = { + Vector2(inc, 0), + Vector2(inc_n, 0), + Vector2(inc_n, 1), + Vector2(inc, 1), + }; + + //side face 1 + facesw[face * 3 + 0] = face_points[0] * vertex_mul; + facesw[face * 3 + 1] = face_points[1] * vertex_mul; + facesw[face * 3 + 2] = face_points[2] * vertex_mul; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[1]; + uvsw[face * 3 + 2] = u[2]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + + if (!cone) { + //side face 2 + facesw[face * 3 + 0] = face_points[2] * vertex_mul; + facesw[face * 3 + 1] = face_points[3] * vertex_mul; + facesw[face * 3 + 2] = face_points[0] * vertex_mul; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[3]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + face++; + } + + //bottom face 1 + facesw[face * 3 + 0] = face_points[1] * vertex_mul; + facesw[face * 3 + 1] = face_points[0] * vertex_mul; + facesw[face * 3 + 2] = Vector3(0, -1, 0) * vertex_mul; + + uvsw[face * 3 + 0] = Vector2(face_points[1].x, face_points[1].y) * 0.5 + Vector2(0.5, 0.5); + uvsw[face * 3 + 1] = Vector2(face_points[0].x, face_points[0].y) * 0.5 + Vector2(0.5, 0.5); + uvsw[face * 3 + 2] = Vector2(0.5, 0.5); + + smoothw[face] = false; + invertw[face] = invert_val; + materialsw[face] = material; + face++; + + if (!cone) { + //top face 1 + facesw[face * 3 + 0] = face_points[3] * vertex_mul; + facesw[face * 3 + 1] = face_points[2] * vertex_mul; + facesw[face * 3 + 2] = Vector3(0, 1, 0) * vertex_mul; + + uvsw[face * 3 + 0] = Vector2(face_points[1].x, face_points[1].y) * 0.5 + Vector2(0.5, 0.5); + uvsw[face * 3 + 1] = Vector2(face_points[0].x, face_points[0].y) * 0.5 + Vector2(0.5, 0.5); + uvsw[face * 3 + 2] = Vector2(0.5, 0.5); + + smoothw[face] = false; + invertw[face] = invert_val; + materialsw[face] = material; + face++; + } + } + } + + if (face != face_count) { + ERR_PRINT("Face mismatch bug! fix code"); + } + } + + brush->build_from_faces(faces, uvs, smooth, materials, invert); + + return brush; +} + +void CSGCylinder::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGCylinder::set_radius); + ClassDB::bind_method(D_METHOD("get_radius"), &CSGCylinder::get_radius); + + ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGCylinder::set_height); + ClassDB::bind_method(D_METHOD("get_height"), &CSGCylinder::get_height); + + ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGCylinder::set_sides); + ClassDB::bind_method(D_METHOD("get_sides"), &CSGCylinder::get_sides); + + ClassDB::bind_method(D_METHOD("set_cone", "cone"), &CSGCylinder::set_cone); + ClassDB::bind_method(D_METHOD("is_cone"), &CSGCylinder::is_cone); + + ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGCylinder::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CSGCylinder::get_material); + + ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGCylinder::set_smooth_faces); + ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGCylinder::get_smooth_faces); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_radius", "get_radius"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_height", "get_height"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cone"), "set_cone", "is_cone"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); +} + +void CSGCylinder::set_radius(const float p_radius) { + radius = p_radius; + _make_dirty(); + update_gizmo(); +} + +float CSGCylinder::get_radius() const { + return radius; +} + +void CSGCylinder::set_height(const float p_height) { + height = p_height; + _make_dirty(); + update_gizmo(); +} + +float CSGCylinder::get_height() const { + return height; +} + +void CSGCylinder::set_sides(const int p_sides) { + ERR_FAIL_COND(p_sides < 3); + sides = p_sides; + _make_dirty(); + update_gizmo(); +} + +int CSGCylinder::get_sides() const { + return sides; +} + +void CSGCylinder::set_cone(const bool p_cone) { + cone = p_cone; + _make_dirty(); + update_gizmo(); +} + +bool CSGCylinder::is_cone() const { + return cone; +} + +void CSGCylinder::set_smooth_faces(const bool p_smooth_faces) { + smooth_faces = p_smooth_faces; + _make_dirty(); +} + +bool CSGCylinder::get_smooth_faces() const { + return smooth_faces; +} + +void CSGCylinder::set_material(const Ref<Material> &p_material) { + + material = p_material; + _make_dirty(); +} + +Ref<Material> CSGCylinder::get_material() const { + + return material; +} + +CSGCylinder::CSGCylinder() { + // defaults + radius = 1.0; + height = 1.0; + sides = 8; + cone = false; + smooth_faces = true; +} + +/////////////// + +CSGBrush *CSGTorus::_build_brush() { + + // set our bounding box + + float min_radius = inner_radius; + float max_radius = outer_radius; + + if (min_radius == max_radius) + return NULL; //sorry, can't + + if (min_radius > max_radius) { + SWAP(min_radius, max_radius); + } + + float radius = (max_radius - min_radius) * 0.5; + + CSGBrush *brush = memnew(CSGBrush); + + int face_count = ring_sides * sides * 2; + + bool invert_val = is_inverting_faces(); + Ref<Material> material = get_material(); + + PoolVector<Vector3> faces; + PoolVector<Vector2> uvs; + PoolVector<bool> smooth; + PoolVector<Ref<Material> > materials; + PoolVector<bool> invert; + + faces.resize(face_count * 3); + uvs.resize(face_count * 3); + + smooth.resize(face_count); + materials.resize(face_count); + invert.resize(face_count); + + { + + PoolVector<Vector3>::Write facesw = faces.write(); + PoolVector<Vector2>::Write uvsw = uvs.write(); + PoolVector<bool>::Write smoothw = smooth.write(); + PoolVector<Ref<Material> >::Write materialsw = materials.write(); + PoolVector<bool>::Write invertw = invert.write(); + + int face = 0; + + { + + for (int i = 0; i < sides; i++) { + + float inci = float(i) / sides; + float inci_n = float((i + 1)) / sides; + + float angi = inci * Math_PI * 2.0; + float angi_n = inci_n * Math_PI * 2.0; + + Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi)); + Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n)); + + for (int j = 0; j < ring_sides; j++) { + + float incj = float(j) / ring_sides; + float incj_n = float((j + 1)) / ring_sides; + + float angj = incj * Math_PI * 2.0; + float angj_n = incj_n * Math_PI * 2.0; + + Vector2 normalj = Vector2(Math::cos(angj), Math::sin(angj)) * radius + Vector2(min_radius + radius, 0); + Vector2 normalj_n = Vector2(Math::cos(angj_n), Math::sin(angj_n)) * radius + Vector2(min_radius + radius, 0); + + Vector3 face_points[4] = { + Vector3(normali.x * normalj.x, normalj.y, normali.z * normalj.x), + Vector3(normali.x * normalj_n.x, normalj_n.y, normali.z * normalj_n.x), + Vector3(normali_n.x * normalj_n.x, normalj_n.y, normali_n.z * normalj_n.x), + Vector3(normali_n.x * normalj.x, normalj.y, normali_n.z * normalj.x) + }; + + Vector2 u[4] = { + Vector2(inci, incj), + Vector2(inci, incj_n), + Vector2(inci_n, incj_n), + Vector2(inci_n, incj), + }; + + // face 1 + facesw[face * 3 + 0] = face_points[0]; + facesw[face * 3 + 1] = face_points[2]; + facesw[face * 3 + 2] = face_points[1]; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[2]; + uvsw[face * 3 + 2] = u[1]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + + //face 2 + facesw[face * 3 + 0] = face_points[3]; + facesw[face * 3 + 1] = face_points[2]; + facesw[face * 3 + 2] = face_points[0]; + + uvsw[face * 3 + 0] = u[3]; + uvsw[face * 3 + 1] = u[2]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + face++; + } + } + } + + if (face != face_count) { + ERR_PRINT("Face mismatch bug! fix code"); + } + } + + brush->build_from_faces(faces, uvs, smooth, materials, invert); + + return brush; +} + +void CSGTorus::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_inner_radius", "radius"), &CSGTorus::set_inner_radius); + ClassDB::bind_method(D_METHOD("get_inner_radius"), &CSGTorus::get_inner_radius); + + ClassDB::bind_method(D_METHOD("set_outer_radius", "radius"), &CSGTorus::set_outer_radius); + ClassDB::bind_method(D_METHOD("get_outer_radius"), &CSGTorus::get_outer_radius); + + ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGTorus::set_sides); + ClassDB::bind_method(D_METHOD("get_sides"), &CSGTorus::get_sides); + + ClassDB::bind_method(D_METHOD("set_ring_sides", "sides"), &CSGTorus::set_ring_sides); + ClassDB::bind_method(D_METHOD("get_ring_sides"), &CSGTorus::get_ring_sides); + + ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGTorus::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CSGTorus::get_material); + + ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGTorus::set_smooth_faces); + ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGTorus::get_smooth_faces); + + ADD_PROPERTY(PropertyInfo(Variant::REAL, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_inner_radius", "get_inner_radius"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_outer_radius", "get_outer_radius"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_sides", "get_ring_sides"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); +} + +void CSGTorus::set_inner_radius(const float p_inner_radius) { + inner_radius = p_inner_radius; + _make_dirty(); + update_gizmo(); +} + +float CSGTorus::get_inner_radius() const { + return inner_radius; +} + +void CSGTorus::set_outer_radius(const float p_outer_radius) { + outer_radius = p_outer_radius; + _make_dirty(); + update_gizmo(); +} + +float CSGTorus::get_outer_radius() const { + return outer_radius; +} + +void CSGTorus::set_sides(const int p_sides) { + ERR_FAIL_COND(p_sides < 3); + sides = p_sides; + _make_dirty(); + update_gizmo(); +} + +int CSGTorus::get_sides() const { + return sides; +} + +void CSGTorus::set_ring_sides(const int p_ring_sides) { + ERR_FAIL_COND(p_ring_sides < 3); + ring_sides = p_ring_sides; + _make_dirty(); + update_gizmo(); +} + +int CSGTorus::get_ring_sides() const { + return ring_sides; +} + +void CSGTorus::set_smooth_faces(const bool p_smooth_faces) { + smooth_faces = p_smooth_faces; + _make_dirty(); +} + +bool CSGTorus::get_smooth_faces() const { + return smooth_faces; +} + +void CSGTorus::set_material(const Ref<Material> &p_material) { + + material = p_material; + _make_dirty(); +} + +Ref<Material> CSGTorus::get_material() const { + + return material; +} + +CSGTorus::CSGTorus() { + // defaults + inner_radius = 2.0; + outer_radius = 3.0; + sides = 8; + ring_sides = 6; + smooth_faces = true; +} + +/////////////// + +CSGBrush *CSGPolygon::_build_brush() { + + // set our bounding box + + if (polygon.size() < 3) + return NULL; + + Vector<Point2> final_polygon = polygon; + + if (Triangulate::get_area(final_polygon) > 0) { + final_polygon.invert(); + } + + Vector<int> triangles = Geometry::triangulate_polygon(final_polygon); + + if (triangles.size() < 3) + return NULL; + + Path *path = NULL; + Ref<Curve3D> curve; + + if (mode == MODE_PATH) { + if (!has_node(path_node)) + return NULL; + Node *n = get_node(path_node); + if (!n) + return NULL; + path = Object::cast_to<Path>(n); + if (!path) + return NULL; + + if (path != path_cache) { + if (path_cache) { + path_cache->disconnect("tree_exited", this, "_path_exited"); + path_cache->disconnect("curve_changed", this, "_path_changed"); + path_cache = NULL; + } + + path_cache = path; + + if (path_cache) { + path_cache->connect("tree_exited", this, "_path_exited"); + path_cache->connect("curve_changed", this, "_path_changed"); + path_cache = NULL; + } + } + curve = path->get_curve(); + if (curve.is_null()) + return NULL; + if (curve->get_baked_length() <= 0) + return NULL; + } + CSGBrush *brush = memnew(CSGBrush); + + int face_count; + + switch (mode) { + case MODE_DEPTH: face_count = triangles.size() * 2 / 3 + (final_polygon.size()) * 2; break; + case MODE_SPIN: face_count = (spin_degrees < 360 ? triangles.size() * 2 / 3 : 0) + (final_polygon.size()) * 2 * spin_sides; break; + case MODE_PATH: { + float bl = curve->get_baked_length(); + int splits = MAX(2, Math::ceil(bl / path_interval)); + face_count = triangles.size() * 2 / 3 + splits * final_polygon.size() * 2; + } break; + } + + bool invert_val = is_inverting_faces(); + Ref<Material> material = get_material(); + + PoolVector<Vector3> faces; + PoolVector<Vector2> uvs; + PoolVector<bool> smooth; + PoolVector<Ref<Material> > materials; + PoolVector<bool> invert; + + faces.resize(face_count * 3); + uvs.resize(face_count * 3); + + smooth.resize(face_count); + materials.resize(face_count); + invert.resize(face_count); + + AABB aabb; //must be computed + { + + PoolVector<Vector3>::Write facesw = faces.write(); + PoolVector<Vector2>::Write uvsw = uvs.write(); + PoolVector<bool>::Write smoothw = smooth.write(); + PoolVector<Ref<Material> >::Write materialsw = materials.write(); + PoolVector<bool>::Write invertw = invert.write(); + + int face = 0; + + switch (mode) { + case MODE_DEPTH: { + + //add triangles, front and back + for (int i = 0; i < 2; i++) { + + for (int j = 0; j < triangles.size(); j += 3) { + for (int k = 0; k < 3; k++) { + int src[3] = { 0, i == 0 ? 1 : 2, i == 0 ? 2 : 1 }; + Vector2 p = final_polygon[triangles[j + src[k]]]; + Vector3 v = Vector3(p.x, p.y, 0); + if (i == 0) { + v.z -= depth; + } + facesw[face * 3 + k] = v; + } + + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_val; + face++; + } + } + + //add triangles for depth + for (int i = 0; i < final_polygon.size(); i++) { + + int i_n = (i + 1) % final_polygon.size(); + + Vector3 v[4] = { + Vector3(final_polygon[i].x, final_polygon[i].y, -depth), + Vector3(final_polygon[i_n].x, final_polygon[i_n].y, -depth), + Vector3(final_polygon[i_n].x, final_polygon[i_n].y, 0), + Vector3(final_polygon[i].x, final_polygon[i].y, 0), + }; + + Vector2 u[4] = { + Vector2(0, 0), + Vector2(0, 1), + Vector2(1, 1), + Vector2(1, 0) + }; + + // face 1 + facesw[face * 3 + 0] = v[0]; + facesw[face * 3 + 1] = v[1]; + facesw[face * 3 + 2] = v[2]; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[1]; + uvsw[face * 3 + 2] = u[2]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + + // face 2 + facesw[face * 3 + 0] = v[2]; + facesw[face * 3 + 1] = v[3]; + facesw[face * 3 + 2] = v[0]; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[3]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + } + + } break; + case MODE_SPIN: { + + for (int i = 0; i < spin_sides; i++) { + + float inci = float(i) / spin_sides; + float inci_n = float((i + 1)) / spin_sides; + + float angi = -(inci * spin_degrees / 360.0) * Math_PI * 2.0; + float angi_n = -(inci_n * spin_degrees / 360.0) * Math_PI * 2.0; + + Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi)); + Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n)); + + //add triangles for depth + for (int j = 0; j < final_polygon.size(); j++) { + + int j_n = (j + 1) % final_polygon.size(); + + Vector3 v[4] = { + Vector3(normali.x * final_polygon[j].x, final_polygon[j].y, normali.z * final_polygon[j].x), + Vector3(normali.x * final_polygon[j_n].x, final_polygon[j_n].y, normali.z * final_polygon[j_n].x), + Vector3(normali_n.x * final_polygon[j_n].x, final_polygon[j_n].y, normali_n.z * final_polygon[j_n].x), + Vector3(normali_n.x * final_polygon[j].x, final_polygon[j].y, normali_n.z * final_polygon[j].x), + }; + + Vector2 u[4] = { + Vector2(0, 0), + Vector2(0, 1), + Vector2(1, 1), + Vector2(1, 0) + }; + + // face 1 + facesw[face * 3 + 0] = v[0]; + facesw[face * 3 + 1] = v[2]; + facesw[face * 3 + 2] = v[1]; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[2]; + uvsw[face * 3 + 2] = u[1]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + + // face 2 + facesw[face * 3 + 0] = v[2]; + facesw[face * 3 + 1] = v[0]; + facesw[face * 3 + 2] = v[3]; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[0]; + uvsw[face * 3 + 2] = u[3]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + } + + if (i == 0 && spin_degrees < 360) { + + for (int j = 0; j < triangles.size(); j += 3) { + for (int k = 0; k < 3; k++) { + int src[3] = { 0, 2, 1 }; + Vector2 p = final_polygon[triangles[j + src[k]]]; + Vector3 v = Vector3(p.x, p.y, 0); + facesw[face * 3 + k] = v; + } + + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_val; + face++; + } + } + + if (i == spin_sides - 1 && spin_degrees < 360) { + + for (int j = 0; j < triangles.size(); j += 3) { + for (int k = 0; k < 3; k++) { + int src[3] = { 0, 1, 2 }; + Vector2 p = final_polygon[triangles[j + src[k]]]; + Vector3 v = Vector3(normali_n.x * p.x, p.y, normali_n.z * p.x); + facesw[face * 3 + k] = v; + } + + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_val; + face++; + } + } + } + } break; + case MODE_PATH: { + + float bl = curve->get_baked_length(); + int splits = MAX(2, Math::ceil(bl / path_interval)); + + Transform path_to_this = get_global_transform().affine_inverse() * path->get_global_transform(); + + Transform prev_xf; + + Vector3 lookat_dir; + + if (path_rotation == PATH_ROTATION_POLYGON) { + lookat_dir = (path->get_global_transform().affine_inverse() * get_global_transform()).xform(Vector3(0, 0, -1)); + } else { + Vector3 p1, p2; + p1 = curve->interpolate_baked(0); + p2 = curve->interpolate_baked(0.1); + lookat_dir = (p2 - p1).normalized(); + } + + for (int i = 0; i <= splits; i++) { + + float ofs = i * path_interval; + + Transform xf; + xf.origin = curve->interpolate_baked(ofs); + + Vector3 local_dir; + + if (path_rotation == PATH_ROTATION_PATH_FOLLOW && ofs > 0) { + //before end + Vector3 p1 = curve->interpolate_baked(ofs - 0.1); + Vector3 p2 = curve->interpolate_baked(ofs); + local_dir = (p2 - p1).normalized(); + + } else { + local_dir = lookat_dir; + } + + xf = xf.looking_at(xf.origin + local_dir, Vector3(0, 1, 0)); + Basis rot(Vector3(0, 0, 1), curve->interpolate_baked_tilt(ofs)); + + xf = xf * rot; //post mult + + xf = path_to_this * xf; + + if (i > 0) { + //put triangles where they belong + //add triangles for depth + for (int j = 0; j < final_polygon.size(); j++) { + + int j_n = (j + 1) % final_polygon.size(); + + Vector3 v[4] = { + prev_xf.xform(Vector3(final_polygon[j].x, final_polygon[j].y, 0)), + prev_xf.xform(Vector3(final_polygon[j_n].x, final_polygon[j_n].y, 0)), + xf.xform(Vector3(final_polygon[j_n].x, final_polygon[j_n].y, 0)), + xf.xform(Vector3(final_polygon[j].x, final_polygon[j].y, 0)), + }; + + Vector2 u[4] = { + Vector2(0, 0), + Vector2(0, 1), + Vector2(1, 1), + Vector2(1, 0) + }; + + // face 1 + facesw[face * 3 + 0] = v[0]; + facesw[face * 3 + 1] = v[1]; + facesw[face * 3 + 2] = v[2]; + + uvsw[face * 3 + 0] = u[0]; + uvsw[face * 3 + 1] = u[1]; + uvsw[face * 3 + 2] = u[2]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + + // face 2 + facesw[face * 3 + 0] = v[2]; + facesw[face * 3 + 1] = v[3]; + facesw[face * 3 + 2] = v[0]; + + uvsw[face * 3 + 0] = u[2]; + uvsw[face * 3 + 1] = u[3]; + uvsw[face * 3 + 2] = u[0]; + + smoothw[face] = smooth_faces; + invertw[face] = invert_val; + materialsw[face] = material; + + face++; + } + } + + if (i == 0) { + + for (int j = 0; j < triangles.size(); j += 3) { + for (int k = 0; k < 3; k++) { + int src[3] = { 0, 1, 2 }; + Vector2 p = final_polygon[triangles[j + src[k]]]; + Vector3 v = Vector3(p.x, p.y, 0); + facesw[face * 3 + k] = xf.xform(v); + } + + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_val; + face++; + } + } + + if (i == splits) { + + for (int j = 0; j < triangles.size(); j += 3) { + for (int k = 0; k < 3; k++) { + int src[3] = { 0, 2, 1 }; + Vector2 p = final_polygon[triangles[j + src[k]]]; + Vector3 v = Vector3(p.x, p.y, 0); + facesw[face * 3 + k] = xf.xform(v); + } + + smoothw[face] = false; + materialsw[face] = material; + invertw[face] = invert_val; + face++; + } + } + + prev_xf = xf; + } + + } break; + } + + if (face != face_count) { + ERR_PRINT("Face mismatch bug! fix code"); + } + for (int i = 0; i < face_count * 3; i++) { + if (i == 0) { + aabb.position = facesw[i]; + } else { + aabb.expand_to(facesw[i]); + } + } + } + + brush->build_from_faces(faces, uvs, smooth, materials, invert); + + return brush; +} + +void CSGPolygon::_notification(int p_what) { + if (p_what == NOTIFICATION_EXIT_TREE) { + if (path_cache) { + path_cache->disconnect("tree_exited", this, "_path_exited"); + path_cache->disconnect("curve_changed", this, "_path_changed"); + path_cache = NULL; + } + } +} + +void CSGPolygon::_validate_property(PropertyInfo &property) const { + if (property.name.begins_with("spin") && mode != MODE_SPIN) { + property.usage = 0; + } + if (property.name.begins_with("path") && mode != MODE_PATH) { + property.usage = 0; + } + if (property.name == "depth" && mode != MODE_DEPTH) { + property.usage = 0; + } + + CSGShape::_validate_property(property); +} + +void CSGPolygon::_path_changed() { + _make_dirty(); + update_gizmo(); +} + +void CSGPolygon::_path_exited() { + path_cache = NULL; +} + +void CSGPolygon::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &CSGPolygon::set_polygon); + ClassDB::bind_method(D_METHOD("get_polygon"), &CSGPolygon::get_polygon); + + ClassDB::bind_method(D_METHOD("set_mode", "mode"), &CSGPolygon::set_mode); + ClassDB::bind_method(D_METHOD("get_mode"), &CSGPolygon::get_mode); + + ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGPolygon::set_depth); + ClassDB::bind_method(D_METHOD("get_depth"), &CSGPolygon::get_depth); + + ClassDB::bind_method(D_METHOD("set_spin_degrees", "degrees"), &CSGPolygon::set_spin_degrees); + ClassDB::bind_method(D_METHOD("get_spin_degrees"), &CSGPolygon::get_spin_degrees); + + ClassDB::bind_method(D_METHOD("set_spin_sides", "spin_sides"), &CSGPolygon::set_spin_sides); + ClassDB::bind_method(D_METHOD("get_spin_sides"), &CSGPolygon::get_spin_sides); + + ClassDB::bind_method(D_METHOD("set_path_node", "path"), &CSGPolygon::set_path_node); + ClassDB::bind_method(D_METHOD("get_path_node"), &CSGPolygon::get_path_node); + + ClassDB::bind_method(D_METHOD("set_path_interval", "distance"), &CSGPolygon::set_path_interval); + ClassDB::bind_method(D_METHOD("get_path_interval"), &CSGPolygon::get_path_interval); + + ClassDB::bind_method(D_METHOD("set_path_rotation", "mode"), &CSGPolygon::set_path_rotation); + ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygon::get_path_rotation); + + ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGPolygon::set_material); + ClassDB::bind_method(D_METHOD("get_material"), &CSGPolygon::get_material); + + ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGPolygon::set_smooth_faces); + ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGPolygon::get_smooth_faces); + + ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CSGPolygon::_is_editable_3d_polygon); + ClassDB::bind_method(D_METHOD("_has_editable_3d_polygon_no_depth"), &CSGPolygon::_has_editable_3d_polygon_no_depth); + + ClassDB::bind_method(D_METHOD("_path_exited"), &CSGPolygon::_path_exited); + ClassDB::bind_method(D_METHOD("_path_changed"), &CSGPolygon::_path_changed); + + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Depth,Spin,Path"), "set_mode", "get_mode"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_depth", "get_depth"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "spin_degrees", PROPERTY_HINT_RANGE, "1,360,0.1"), "set_spin_degrees", "get_spin_degrees"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "spin_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_spin_sides", "get_spin_sides"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node"), "set_path_node", "get_path_node"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_interval", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_path_interval", "get_path_interval"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material"); + + BIND_ENUM_CONSTANT(MODE_DEPTH); + BIND_ENUM_CONSTANT(MODE_SPIN); + BIND_ENUM_CONSTANT(MODE_PATH); + + BIND_ENUM_CONSTANT(PATH_ROTATION_POLYGON); + BIND_ENUM_CONSTANT(PATH_ROTATION_PATH); + BIND_ENUM_CONSTANT(PATH_ROTATION_PATH_FOLLOW); +} + +void CSGPolygon::set_polygon(const Vector<Vector2> &p_polygon) { + polygon = p_polygon; + _make_dirty(); + update_gizmo(); +} + +Vector<Vector2> CSGPolygon::get_polygon() const { + return polygon; +} + +void CSGPolygon::set_mode(Mode p_mode) { + mode = p_mode; + _make_dirty(); + update_gizmo(); + _change_notify(); +} + +CSGPolygon::Mode CSGPolygon::get_mode() const { + return mode; +} + +void CSGPolygon::set_depth(const float p_depth) { + ERR_FAIL_COND(p_depth < 0.001); + depth = p_depth; + _make_dirty(); + update_gizmo(); +} + +float CSGPolygon::get_depth() const { + return depth; +} + +void CSGPolygon::set_spin_degrees(const float p_spin_degrees) { + ERR_FAIL_COND(p_spin_degrees < 0.01 || p_spin_degrees > 360); + spin_degrees = p_spin_degrees; + _make_dirty(); + update_gizmo(); +} + +float CSGPolygon::get_spin_degrees() const { + return spin_degrees; +} + +void CSGPolygon::set_spin_sides(const int p_spin_sides) { + ERR_FAIL_COND(p_spin_sides < 3); + spin_sides = p_spin_sides; + _make_dirty(); + update_gizmo(); +} + +int CSGPolygon::get_spin_sides() const { + return spin_sides; +} + +void CSGPolygon::set_path_node(const NodePath &p_path) { + path_node = p_path; + _make_dirty(); + update_gizmo(); +} + +NodePath CSGPolygon::get_path_node() const { + return path_node; +} + +void CSGPolygon::set_path_interval(float p_interval) { + ERR_FAIL_COND(p_interval < 0.001); + path_interval = p_interval; + _make_dirty(); + update_gizmo(); +} +float CSGPolygon::get_path_interval() const { + return path_interval; +} + +void CSGPolygon::set_path_rotation(PathRotation p_rotation) { + path_rotation = p_rotation; + _make_dirty(); + update_gizmo(); +} + +CSGPolygon::PathRotation CSGPolygon::get_path_rotation() const { + return path_rotation; +} + +void CSGPolygon::set_smooth_faces(const bool p_smooth_faces) { + smooth_faces = p_smooth_faces; + _make_dirty(); +} + +bool CSGPolygon::get_smooth_faces() const { + return smooth_faces; +} + +void CSGPolygon::set_material(const Ref<Material> &p_material) { + + material = p_material; + _make_dirty(); +} + +Ref<Material> CSGPolygon::get_material() const { + + return material; +} + +bool CSGPolygon::_is_editable_3d_polygon() const { + return true; +} + +bool CSGPolygon::_has_editable_3d_polygon_no_depth() const { + return true; +} + +CSGPolygon::CSGPolygon() { + // defaults + mode = MODE_DEPTH; + polygon.push_back(Vector2(0, 0)); + polygon.push_back(Vector2(0, 1)); + polygon.push_back(Vector2(1, 1)); + polygon.push_back(Vector2(1, 0)); + depth = 1.0; + spin_degrees = 360; + spin_sides = 8; + smooth_faces = false; + path_interval = 1; + path_rotation = PATH_ROTATION_PATH; + path_cache = NULL; +} diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h new file mode 100644 index 0000000000..c1d2cce606 --- /dev/null +++ b/modules/csg/csg_shape.h @@ -0,0 +1,360 @@ +#ifndef CSG_SHAPE_H +#define CSG_SHAPE_H + +#define CSGJS_HEADER_ONLY + +#include "csg.h" +#include "scene/3d/visual_instance.h" +#include "scene/resources/concave_polygon_shape.h" + +class CSGShape : public VisualInstance { + GDCLASS(CSGShape, VisualInstance); + +public: + enum Operation { + OPERATION_UNION, + OPERATION_INTERSECTION, + OPERATION_SUBTRACTION, + + }; + +private: + Operation operation; + CSGShape *parent; + + CSGBrush *brush; + + AABB node_aabb; + + bool dirty; + float snap; + + bool use_collision; + Ref<ConcavePolygonShape> root_collision_shape; + RID root_collision_instance; + + Ref<ArrayMesh> root_mesh; + + struct Vector3Hasher { + _ALWAYS_INLINE_ uint32_t hash(const Vector3 &p_vec3) const { + uint32_t h = hash_djb2_one_float(p_vec3.x); + h = hash_djb2_one_float(p_vec3.y, h); + h = hash_djb2_one_float(p_vec3.z, h); + return h; + } + }; + + struct ShapeUpdateSurface { + PoolVector<Vector3> vertices; + PoolVector<Vector3> normals; + PoolVector<Vector2> uvs; + Ref<Material> material; + int last_added; + + PoolVector<Vector3>::Write verticesw; + PoolVector<Vector3>::Write normalsw; + PoolVector<Vector2>::Write uvsw; + }; + + void _update_shape(); + +protected: + void _notification(int p_what); + virtual CSGBrush *_build_brush() = 0; + void _make_dirty(); + + static void _bind_methods(); + + friend class CSGCombiner; + CSGBrush *_get_brush(); + + virtual void _validate_property(PropertyInfo &property) const; + +public: + void set_operation(Operation p_operation); + Operation get_operation() const; + + virtual PoolVector<Vector3> get_brush_faces(); + + virtual AABB get_aabb() const; + virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const; + + void set_use_collision(bool p_enable); + bool is_using_collision() const; + + void set_snap(float p_snap); + float get_snap() const; + + bool is_root_shape() const; + CSGShape(); + ~CSGShape(); +}; + +VARIANT_ENUM_CAST(CSGShape::Operation) + +class CSGCombiner : public CSGShape { + GDCLASS(CSGCombiner, CSGShape) +private: + virtual CSGBrush *_build_brush(); + +public: + CSGCombiner(); +}; + +class CSGPrimitive : public CSGShape { + GDCLASS(CSGPrimitive, CSGShape) + +private: + bool invert_faces; + +protected: + CSGBrush *_create_brush_from_arrays(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uv, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials); + static void _bind_methods(); + +public: + void set_invert_faces(bool p_invert); + bool is_inverting_faces(); + + CSGPrimitive(); +}; + +class CSGMesh : public CSGPrimitive { + GDCLASS(CSGMesh, CSGPrimitive) + + virtual CSGBrush *_build_brush(); + + Ref<Mesh> mesh; + + void _mesh_changed(); + +protected: + static void _bind_methods(); + +public: + void set_mesh(const Ref<Mesh> &p_mesh); + Ref<Mesh> get_mesh(); +}; + +class CSGSphere : public CSGPrimitive { + + GDCLASS(CSGSphere, CSGPrimitive) + virtual CSGBrush *_build_brush(); + + Ref<Material> material; + bool smooth_faces; + float radius; + int radial_segments; + int rings; + +protected: + static void _bind_methods(); + +public: + void set_radius(const float p_radius); + float get_radius() const; + + void set_radial_segments(const int p_radial_segments); + int get_radial_segments() const; + + void set_rings(const int p_rings); + int get_rings() const; + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; + + void set_smooth_faces(bool p_smooth_faces); + bool get_smooth_faces() const; + + CSGSphere(); +}; + +class CSGBox : public CSGPrimitive { + + GDCLASS(CSGBox, CSGPrimitive) + virtual CSGBrush *_build_brush(); + + Ref<Material> material; + float width; + float height; + float depth; + +protected: + static void _bind_methods(); + +public: + void set_width(const float p_width); + float get_width() const; + + void set_height(const float p_height); + float get_height() const; + + void set_depth(const float p_depth); + float get_depth() const; + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; + + CSGBox(); +}; + +class CSGCylinder : public CSGPrimitive { + + GDCLASS(CSGCylinder, CSGPrimitive) + virtual CSGBrush *_build_brush(); + + Ref<Material> material; + float radius; + float height; + int sides; + bool cone; + bool smooth_faces; + +protected: + static void _bind_methods(); + +public: + void set_radius(const float p_radius); + float get_radius() const; + + void set_height(const float p_height); + float get_height() const; + + void set_sides(const int p_sides); + int get_sides() const; + + void set_cone(const bool p_cone); + bool is_cone() const; + + void set_smooth_faces(bool p_smooth_faces); + bool get_smooth_faces() const; + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; + + CSGCylinder(); +}; + +class CSGTorus : public CSGPrimitive { + + GDCLASS(CSGTorus, CSGPrimitive) + virtual CSGBrush *_build_brush(); + + Ref<Material> material; + float inner_radius; + float outer_radius; + int sides; + int ring_sides; + bool smooth_faces; + +protected: + static void _bind_methods(); + +public: + void set_inner_radius(const float p_inner_radius); + float get_inner_radius() const; + + void set_outer_radius(const float p_outer_radius); + float get_outer_radius() const; + + void set_sides(const int p_sides); + int get_sides() const; + + void set_ring_sides(const int p_ring_sides); + int get_ring_sides() const; + + void set_smooth_faces(bool p_smooth_faces); + bool get_smooth_faces() const; + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; + + CSGTorus(); +}; + +class CSGPolygon : public CSGPrimitive { + + GDCLASS(CSGPolygon, CSGPrimitive) + +public: + enum Mode { + MODE_DEPTH, + MODE_SPIN, + MODE_PATH + }; + + enum PathRotation { + PATH_ROTATION_POLYGON, + PATH_ROTATION_PATH, + PATH_ROTATION_PATH_FOLLOW, + }; + +private: + virtual CSGBrush *_build_brush(); + + Vector<Vector2> polygon; + Ref<Material> material; + + Mode mode; + + float depth; + + float spin_degrees; + int spin_sides; + + NodePath path_node; + float path_interval; + PathRotation path_rotation; + + Node *path_cache; + + bool smooth_faces; + + bool _is_editable_3d_polygon() const; + bool _has_editable_3d_polygon_no_depth() const; + + void _path_changed(); + void _path_exited(); + +protected: + static void _bind_methods(); + virtual void _validate_property(PropertyInfo &property) const; + void _notification(int p_what); + +public: + void set_polygon(const Vector<Vector2> &p_polygon); + Vector<Vector2> get_polygon() const; + + void set_mode(Mode p_mode); + Mode get_mode() const; + + void set_depth(float p_depth); + float get_depth() const; + + void set_spin_degrees(float p_spin_degrees); + float get_spin_degrees() const; + + void set_spin_sides(int p_sides); + int get_spin_sides() const; + + void set_path_node(const NodePath &p_path); + NodePath get_path_node() const; + + void set_path_interval(float p_interval); + float get_path_interval() const; + + void set_path_rotation(PathRotation p_rotation); + PathRotation get_path_rotation() const; + + void set_smooth_faces(bool p_smooth_faces); + bool get_smooth_faces() const; + + void set_material(const Ref<Material> &p_material); + Ref<Material> get_material() const; + + CSGPolygon(); +}; + +VARIANT_ENUM_CAST(CSGPolygon::Mode) +VARIANT_ENUM_CAST(CSGPolygon::PathRotation) + +#endif // CSG_SHAPE_H diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp new file mode 100644 index 0000000000..020724ee59 --- /dev/null +++ b/modules/csg/register_types.cpp @@ -0,0 +1,59 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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 "register_types.h" + +#include "csg_shape.h" +#include "csg_gizmos.h" + +void register_csg_types() { + +#ifndef _3D_DISABLED + + ClassDB::register_virtual_class<CSGShape>(); + ClassDB::register_virtual_class<CSGPrimitive>(); + ClassDB::register_class<CSGMesh>(); + ClassDB::register_class<CSGSphere>(); + ClassDB::register_class<CSGBox>(); + ClassDB::register_class<CSGCylinder>(); + ClassDB::register_class<CSGTorus>(); + ClassDB::register_class<CSGPolygon>(); + ClassDB::register_class<CSGCombiner>(); + +#ifdef TOOLS_ENABLED + EditorPlugins::add_by_type<EditorPluginCSG>(); +#endif +#endif + +} + +void unregister_csg_types() { + +} diff --git a/modules/csg/register_types.h b/modules/csg/register_types.h new file mode 100644 index 0000000000..49490d31d3 --- /dev/null +++ b/modules/csg/register_types.h @@ -0,0 +1,32 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 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. */ +/*************************************************************************/ + +void register_csg_types(); +void unregister_csg_types(); diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index acfb83bc10..8654ef3d82 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -275,8 +275,8 @@ if ARGUMENTS.get('gdnative_wrapper', False): if gd_wrapper_env['use_lto']: if not env.msvc: - gd_wrapper_env.Append(CCFLAGS=['--no-lto']) - gd_wrapper_env.Append(LINKFLAGS=['--no-lto']) + gd_wrapper_env.Append(CCFLAGS=['-fno-lto']) + gd_wrapper_env.Append(LINKFLAGS=['-fno-lto']) else: gd_wrapper_env.Append(CCFLAGS=['/GL-']) gd_wrapper_env.Append(LINKFLAGS=['/LTCG:OFF']) diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 0d52f0a995..b38b30d4b8 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -430,6 +430,9 @@ struct GDScriptCompletionIdentifier { Ref<GDScript> script; Variant::Type type; Variant value; //im case there is a value, also return it + + GDScriptCompletionIdentifier() : + type(Variant::NIL) {} }; static GDScriptCompletionIdentifier _get_type_from_variant(const Variant &p_variant, bool p_allow_gdnative_class = false) { @@ -551,9 +554,7 @@ static Ref<Reference> _get_parent_class(GDScriptCompletionContext &context) { static GDScriptCompletionIdentifier _get_native_class(GDScriptCompletionContext &context) { - //eeh... GDScriptCompletionIdentifier id; - id.type = Variant::NIL; REF pc = _get_parent_class(context); if (!pc.is_valid()) { @@ -1521,6 +1522,13 @@ static void _find_identifiers(GDScriptCompletionContext &context, int p_line, bo result.insert(_type_names[i]); } + List<String> reserved_words; + GDScriptLanguage::get_singleton()->get_reserved_words(&reserved_words); + + for (List<String>::Element *E = reserved_words.front(); E; E = E->next()) { + result.insert(E->get()); + } + //autoload singletons List<PropertyInfo> props; ProjectSettings::get_singleton()->get_property_list(&props); @@ -2641,6 +2649,18 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol context.function = p.get_completion_function(); context.base = p_owner; context.base_path = p_base_path; + + if (context._class && context._class->extends_class.size() > 0) { + bool success = false; + ClassDB::get_integer_constant(context._class->extends_class[0], p_symbol, &success); + if (success) { + r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT; + r_result.class_name = context._class->extends_class[0]; + r_result.class_member = p_symbol; + return OK; + } + } + bool isfunction = false; switch (p.get_completion_type()) { diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp index d32ce25d3c..1c5b8187ca 100644 --- a/modules/gdscript/gdscript_function.cpp +++ b/modules/gdscript/gdscript_function.cpp @@ -1311,9 +1311,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time; } -#endif if (ScriptDebugger::get_singleton()) GDScriptLanguage::get_singleton()->exit_function(); +#endif if (_stack_size) { //free stack diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index f523eef895..4b96824dca 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -1154,9 +1154,9 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) { for (int k = 0; k < 3; k++) { if (i < 3) - face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[j][(i + k) % 3] = v[k]; else - face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[3 - j][(i + k) % 3] = -v[k]; } } diff --git a/modules/mono/config.py b/modules/mono/config.py index 831d849ea6..18d9c67795 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -59,9 +59,6 @@ def configure(env): mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0'] if env['platform'] == 'windows': - if mono_static: - raise RuntimeError('mono-static: Not supported on Windows') - if bits == '32': if os.getenv('MONO32_PREFIX'): mono_root = os.getenv('MONO32_PREFIX') @@ -81,24 +78,41 @@ def configure(env): env.Append(LIBPATH=mono_lib_path) env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0')) - mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') + if mono_static: + lib_suffix = Environment()['LIBSUFFIX'] + mono_static_lib_name = 'libmono-static-sgen' - if not mono_lib_name: - raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)): + raise RuntimeError('Could not find static mono library in: ' + mono_lib_path) - if os.getenv('VCINSTALLDIR'): - env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) + if env.msvc: + env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix) + + env.Append(LINKFLAGS='Mincore' + lib_suffix) + env.Append(LINKFLAGS='msvcrt' + lib_suffix) + env.Append(LINKFLAGS='LIBCMT' + lib_suffix) + env.Append(LINKFLAGS='Psapi' + lib_suffix) + else: + env.Append(LIBS=mono_static_lib_name) else: - env.Append(LIBS=mono_lib_name) + mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib') + + if not mono_lib_name: + raise RuntimeError('Could not find mono library in: ' + mono_lib_path) + + if env.msvc: + env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX']) + else: + env.Append(LIBS=mono_lib_name) - mono_bin_path = os.path.join(mono_root, 'bin') + mono_bin_path = os.path.join(mono_root, 'bin') - mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') + mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll') - if not mono_dll_name: - raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) + if not mono_dll_name: + raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path) - copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll') + copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll') copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll') else: diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 1d0afa7f2d..adc5f16f92 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -1954,8 +1954,12 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Variant::Call ScriptInstance *CSharpScript::instance_create(Object *p_this) { - if (!valid) - return NULL; + if (!script_class) { + ERR_EXPLAIN("Cannot find class " + name + " for script " + get_path()); + ERR_FAIL_V(NULL); + } + + ERR_FAIL_COND_V(!valid, NULL); if (!tool && !ScriptServer::is_scripting_enabled()) { #ifdef TOOLS_ENABLED @@ -2045,20 +2049,15 @@ Error CSharpScript::reload(bool p_keep_state) { if (project_assembly) { script_class = project_assembly->get_object_derived_class(name); - if (!script_class) { - ERR_PRINTS("Cannot find class " + name + " for script " + get_path()); - } + valid = script_class != NULL; + + if (script_class) { #ifdef DEBUG_ENABLED - else if (OS::get_singleton()->is_stdout_verbose()) { OS::get_singleton()->print(String("Found class " + script_class->get_namespace() + "." + script_class->get_name() + " for script " + get_path() + "\n") .utf8()); - } #endif - valid = script_class != NULL; - - if (script_class) { tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute)); native = GDMonoUtils::get_class_native_base(script_class); @@ -2288,7 +2287,9 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p CRASH_COND(mono_domain_get() == NULL); #endif -#else +#endif + +#ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() && mono_domain_get() == NULL) { CRASH_COND(Thread::get_caller_id() == Thread::get_main_id()); @@ -2297,14 +2298,20 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p // because this may be called by one of the editor's worker threads. // Attach this thread temporarily to reload the script. - MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); - CRASH_COND(mono_thread == NULL); + if (SCRIPTS_DOMAIN) { + MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN); + CRASH_COND(mono_thread == NULL); + script->reload(); + mono_thread_detach(mono_thread); + } + + } else { // just reload it normally +#endif script->reload(); - mono_thread_detach(mono_thread); - } else // just reload it normally +#ifdef TOOLS_ENABLED + } #endif - script->reload(); if (r_error) *r_error = OK; diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index a210b8e480..4c598d4f37 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -248,14 +248,14 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { if (imethod.is_virtual) continue; - const TypeInterface *return_type = _get_type_by_name_or_placeholder(imethod.return_type); + const TypeInterface *return_type = _get_type_or_placeholder(imethod.return_type); String im_sig; String im_unique_sig; if (p_itype.is_object_type) { im_sig += "IntPtr " CS_PARAM_METHODBIND ", "; - im_unique_sig += imethod.return_type.operator String() + ",IntPtr,IntPtr"; + im_unique_sig += imethod.return_type.cname.operator String() + ",IntPtr,IntPtr"; } im_sig += "IntPtr " CS_PARAM_INSTANCE; @@ -263,7 +263,7 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) { // Get arguments information int i = 0; for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) { - const TypeInterface *arg_type = _get_type_by_name_or_placeholder(F->get().type); + const TypeInterface *arg_type = _get_type_or_placeholder(F->get().type); im_sig += ", "; im_sig += arg_type->im_type_in; @@ -730,7 +730,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str } output.push_back(INDENT1 "public "); - bool is_abstract = !ClassDB::can_instance(itype.name) && ClassDB::is_class_enabled(itype.name); // can_instance returns true if there's a constructor and the class is not 'disabled' + bool is_abstract = itype.is_object_type && !ClassDB::can_instance(itype.name) && ClassDB::is_class_enabled(itype.name); // can_instance returns true if there's a constructor and the class is not 'disabled' output.push_back(itype.is_singleton ? "static class " : (is_abstract ? "abstract class " : "class ")); output.push_back(itype.proxy_name); @@ -1069,12 +1069,12 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte } if (getter && setter) { - ERR_FAIL_COND_V(getter->return_type != setter->arguments.back()->get().type, ERR_BUG); + ERR_FAIL_COND_V(getter->return_type.cname != setter->arguments.back()->get().type.cname, ERR_BUG); } - StringName proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type; + const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type; - const TypeInterface *prop_itype = _get_type_by_name_or_null(proptype_name); + const TypeInterface *prop_itype = _get_type_or_null(proptype_name); ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(p_iprop.cname)); @@ -1122,9 +1122,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.push_back(getter->proxy_name + "("); if (p_iprop.index != -1) { const ArgumentInterface &idx_arg = getter->arguments.front()->get(); - if (idx_arg.type != name_cache.type_int) { + if (idx_arg.type.cname != name_cache.type_int) { // Assume the index parameter is an enum - const TypeInterface *idx_arg_type = _get_type_by_name_or_null(idx_arg.type); + const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type); CRASH_COND(idx_arg_type == NULL); p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index)); } else { @@ -1139,9 +1139,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.push_back(setter->proxy_name + "("); if (p_iprop.index != -1) { const ArgumentInterface &idx_arg = setter->arguments.front()->get(); - if (idx_arg.type != name_cache.type_int) { + if (idx_arg.type.cname != name_cache.type_int) { // Assume the index parameter is an enum - const TypeInterface *idx_arg_type = _get_type_by_name_or_null(idx_arg.type); + const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type); CRASH_COND(idx_arg_type == NULL); p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", "); } else { @@ -1158,7 +1158,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output) { - const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type); + const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type); String method_bind_field = "method_bind_" + itos(p_method_bind_count); @@ -1175,7 +1175,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf // Retrieve information from the arguments for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { const ArgumentInterface &iarg = F->get(); - const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type); + const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); // Add the current arguments to the signature // If the argument has a default value which is not a constant, we will make it Nullable @@ -1328,21 +1328,19 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf const InternalCall *im_icall = match->value(); String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS; - im_call += "." + im_icall->name + "(" + icall_params + ");\n"; + im_call += "." + im_icall->name + "(" + icall_params + ")"; if (p_imethod.arguments.size()) p_output.push_back(cs_in_statements); if (return_type->cname == name_cache.type_void) { - p_output.push_back(im_call); + p_output.push_back(im_call + ";\n"); } else if (return_type->cs_out.empty()) { - p_output.push_back("return " + im_call); + p_output.push_back("return " + im_call + ";\n"); } else { - p_output.push_back(return_type->im_type_out); - p_output.push_back(" " LOCAL_RET " = "); - p_output.push_back(im_call); p_output.push_back(INDENT3); - p_output.push_back(sformat(return_type->cs_out, LOCAL_RET) + "\n"); + p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out)); + p_output.push_back("\n"); } p_output.push_back(CLOSE_BLOCK_L2); @@ -1540,9 +1538,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte if (p_imethod.is_virtual) return OK; // Ignore - bool ret_void = p_imethod.return_type == name_cache.type_void; + bool ret_void = p_imethod.return_type.cname == name_cache.type_void; - const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type); + const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type); String argc_str = itos(p_imethod.arguments.size()); @@ -1554,7 +1552,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte int i = 0; for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) { const ArgumentInterface &iarg = F->get(); - const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type); + const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type); String c_param_name = "arg" + itos(i + 1); @@ -1695,42 +1693,49 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte return OK; } -const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_null(const StringName &p_cname) { +const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(const TypeReference &p_typeref) { - const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_cname); + const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_typeref.cname); if (builtin_type_match) return &builtin_type_match->get(); - const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_cname); + const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_typeref.cname); if (obj_type_match) return &obj_type_match.get(); - const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_cname); + if (p_typeref.is_enum) { + const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_typeref.cname); - if (enum_match) - return &enum_match->get(); + if (enum_match) + return &enum_match->get(); + + // Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead. + const Map<StringName, TypeInterface>::Element *int_match = builtin_types.find(name_cache.type_int); + ERR_FAIL_NULL_V(int_match, NULL); + return &int_match->get(); + } return NULL; } -const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_placeholder(const StringName &p_cname) { +const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) { - const TypeInterface *found = _get_type_by_name_or_null(p_cname); + const TypeInterface *found = _get_type_or_null(p_typeref); if (found) return found; - ERR_PRINTS(String() + "Type not found. Creating placeholder: " + p_cname.operator String()); + ERR_PRINTS(String() + "Type not found. Creating placeholder: " + p_typeref.cname.operator String()); - const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_cname); + const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_typeref.cname); if (match) return &match->get(); TypeInterface placeholder; - TypeInterface::create_placeholder_type(placeholder, p_cname); + TypeInterface::create_placeholder_type(placeholder, p_typeref.cname); return &placeholder_types.insert(placeholder.cname, placeholder)->get(); } @@ -1874,7 +1879,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { // The method Object.free is registered as a virtual method, but without the virtual flag. // This is because this method is not supposed to be overridden, but called. // We assume the return type is void. - imethod.return_type = name_cache.type_void; + imethod.return_type.cname = name_cache.type_void; // Actually, more methods like this may be added in the future, // which could actually will return something different. @@ -1889,21 +1894,22 @@ void BindingsGenerator::_populate_object_type_interfaces() { } else { ERR_PRINTS("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name); } - } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { // TODO redundant? - imethod.return_type = return_info.class_name; + } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + imethod.return_type.cname = return_info.class_name; + imethod.return_type.is_enum = true; } else if (return_info.class_name != StringName()) { - imethod.return_type = return_info.class_name; + imethod.return_type.cname = return_info.class_name; } else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) { - imethod.return_type = return_info.hint_string; + imethod.return_type.cname = return_info.hint_string; } else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { - imethod.return_type = name_cache.type_Variant; + imethod.return_type.cname = name_cache.type_Variant; } else if (return_info.type == Variant::NIL) { - imethod.return_type = name_cache.type_void; + imethod.return_type.cname = name_cache.type_void; } else { - imethod.return_type = Variant::get_type_name(return_info.type); + imethod.return_type.cname = Variant::get_type_name(return_info.type); } - if (!itype.requires_collections && imethod.return_type == name_cache.type_Dictionary) + if (!itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary) itype.requires_collections = true; for (int i = 0; i < argc; i++) { @@ -1912,21 +1918,22 @@ void BindingsGenerator::_populate_object_type_interfaces() { ArgumentInterface iarg; iarg.name = arginfo.name; - if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { // TODO redundant? - iarg.type = arginfo.class_name; + if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { + iarg.type.cname = arginfo.class_name; + iarg.type.is_enum = true; } else if (arginfo.class_name != StringName()) { - iarg.type = arginfo.class_name; + iarg.type.cname = arginfo.class_name; } else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { - iarg.type = arginfo.hint_string; + iarg.type.cname = arginfo.hint_string; } else if (arginfo.type == Variant::NIL) { - iarg.type = name_cache.type_Variant; + iarg.type.cname = name_cache.type_Variant; } else { - iarg.type = Variant::get_type_name(arginfo.type); + iarg.type.cname = Variant::get_type_name(arginfo.type); } iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name)); - if (!itype.requires_collections && iarg.type == name_cache.type_Dictionary) + if (!itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary) itype.requires_collections = true; if (m && m->has_default_argument(i)) { @@ -1938,7 +1945,7 @@ void BindingsGenerator::_populate_object_type_interfaces() { if (imethod.is_vararg) { ArgumentInterface ivararg; - ivararg.type = name_cache.type_VarArg; + ivararg.type.cname = name_cache.type_VarArg; ivararg.name = "@args"; imethod.add_argument(ivararg); } @@ -2023,17 +2030,11 @@ void BindingsGenerator::_populate_object_type_interfaces() { itype.enums.push_back(ienum); TypeInterface enum_itype; + enum_itype.is_enum = true; enum_itype.name = itype.name + "." + String(*k); enum_itype.cname = StringName(enum_itype.name); enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name; - enum_itype.c_arg_in = "&%s"; - enum_itype.c_type = "int"; - enum_itype.c_type_in = "int"; - enum_itype.c_type_out = "int"; - enum_itype.cs_type = enum_itype.proxy_name; - enum_itype.im_type_in = enum_itype.proxy_name; - enum_itype.im_type_out = enum_itype.proxy_name; - enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[enum_itype.proxy_name]; + TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); } @@ -2068,7 +2069,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg switch (p_val.get_type()) { case Variant::NIL: - if (ClassDB::class_exists(r_iarg.type)) { + if (ClassDB::class_exists(r_iarg.type.cname)) { // Object type r_iarg.default_argument = "null"; } else { @@ -2081,7 +2082,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg r_iarg.default_argument = bool(p_val) ? "true" : "false"; break; case Variant::INT: - if (r_iarg.type != name_cache.type_int) { + if (r_iarg.type.cname != name_cache.type_int) { r_iarg.default_argument = "(%s)" + r_iarg.default_argument; } break; @@ -2142,7 +2143,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg default: {} } - if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type == name_cache.type_Variant && r_iarg.default_argument != "null") + if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null") r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF; } @@ -2161,7 +2162,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_arg_in = "&%s_in"; \ itype.c_type_in = m_type_in; \ itype.cs_in = "ref %s"; \ - itype.cs_out = "return (" #m_type ")%0;"; \ + itype.cs_out = "return (%1)%0;"; \ itype.im_type_out = "object"; \ builtin_types.insert(itype.cname, itype); \ } @@ -2256,7 +2257,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new NodePath(%0);"; + itype.cs_out = "return new %1(%0);"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; _populate_builtin_type(itype, Variant::NODE_PATH); @@ -2279,7 +2280,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.c_type + "*"; itype.cs_type = itype.proxy_name; itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)"; - itype.cs_out = "return new RID(%0);"; + itype.cs_out = "return new %1(%0);"; itype.im_type_in = "IntPtr"; itype.im_type_out = "IntPtr"; _populate_builtin_type(itype, Variant::_RID); @@ -2408,11 +2409,11 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant:: iarg.name = pi.name; if (pi.type == Variant::NIL) - iarg.type = name_cache.type_Variant; + iarg.type.cname = name_cache.type_Variant; else - iarg.type = Variant::get_type_name(pi.type); + iarg.type.cname = Variant::get_type_name(pi.type); - if (!r_itype.requires_collections && iarg.type == name_cache.type_Dictionary) + if (!r_itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary) r_itype.requires_collections = true; if ((mi.default_arguments.size() - mi.arguments.size() + i) >= 0) @@ -2423,12 +2424,12 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant:: if (mi.return_val.type == Variant::NIL) { if (mi.return_val.name != "") - imethod.return_type = name_cache.type_Variant; + imethod.return_type.cname = name_cache.type_Variant; } else { - imethod.return_type = Variant::get_type_name(mi.return_val.type); + imethod.return_type.cname = Variant::get_type_name(mi.return_val.type); } - if (!r_itype.requires_collections && imethod.return_type == name_cache.type_Dictionary) + if (!r_itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary) r_itype.requires_collections = true; if (r_itype.class_doc) { @@ -2494,13 +2495,11 @@ void BindingsGenerator::_populate_global_constants() { EnumInterface &ienum = E->get(); TypeInterface enum_itype; - enum_itype = TypeInterface::create_value_type(ienum.cname); - enum_itype.c_arg_in = "&%s"; - enum_itype.c_type = "int"; - enum_itype.c_type_in = "int"; - enum_itype.c_type_out = "int"; - enum_itype.im_type_in = enum_itype.name; - enum_itype.im_type_out = enum_itype.name; + enum_itype.is_enum = true; + enum_itype.name = ienum.cname.operator String(); + enum_itype.cname = ienum.cname; + enum_itype.proxy_name = enum_itype.name; + TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); ienum.prefix = _determine_enum_prefix(ienum); @@ -2521,15 +2520,13 @@ void BindingsGenerator::_populate_global_constants() { hardcoded_enums.push_back("Vector3.Axis"); for (List<StringName>::Element *E = hardcoded_enums.front(); E; E = E->next()) { // These enums are not generated and must be written manually (e.g.: Vector3.Axis) - // Here, we are assuming core types do not begin with underscore + // Here, we assume core types do not begin with underscore TypeInterface enum_itype; - enum_itype = TypeInterface::create_value_type(E->get()); - enum_itype.c_arg_in = "&%s"; - enum_itype.c_type = "int"; - enum_itype.c_type_in = "int"; - enum_itype.c_type_out = "int"; - enum_itype.im_type_in = enum_itype.name; - enum_itype.im_type_out = enum_itype.name; + enum_itype.is_enum = true; + enum_itype.name = E->get().operator String(); + enum_itype.cname = E->get(); + enum_itype.proxy_name = enum_itype.name; + TypeInterface::postsetup_enum_type(enum_itype); enum_types.insert(enum_itype.cname, enum_itype); } } diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index f6194139af..5b33a0e53f 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -81,6 +81,15 @@ class BindingsGenerator { const DocData::PropertyDoc *prop_doc; }; + struct TypeReference { + StringName cname; + bool is_enum; + + TypeReference() { + is_enum = false; + } + }; + struct ArgumentInterface { enum DefaultParamMode { CONSTANT, @@ -88,7 +97,8 @@ class BindingsGenerator { NULLABLE_REF }; - StringName type; + TypeReference type; + String name; String default_argument; DefaultParamMode def_param_mode; @@ -110,7 +120,7 @@ class BindingsGenerator { /** * [TypeInterface::name] of the return type */ - StringName return_type; + TypeReference return_type; /** * Determines if the method has a variable number of arguments (VarArg) @@ -146,7 +156,7 @@ class BindingsGenerator { } MethodInterface() { - return_type = BindingsGenerator::get_singleton()->name_cache.type_void; + return_type.cname = BindingsGenerator::get_singleton()->name_cache.type_void; is_vararg = false; is_virtual = false; requires_object_call = false; @@ -175,6 +185,7 @@ class BindingsGenerator { ClassDB::APIType api_type; + bool is_enum; bool is_object_type; bool is_singleton; bool is_reference; @@ -276,7 +287,9 @@ class BindingsGenerator { * One or more statements that determine how a variable of this type is returned from a method. * It must contain the return statement(s). * Formatting elements: - * %0 or %s: name of the variable to be returned + * %0: internal method call statement + * %1: [cs_type] of the return type + * %2: [im_type_out] of the return type */ String cs_out; @@ -293,8 +306,6 @@ class BindingsGenerator { /** * Type used for the return type of internal call methods. - * If [cs_out] is not empty and the method return type is not void, - * it is also used for the type of the return variable. */ String im_type_out; @@ -379,10 +390,24 @@ class BindingsGenerator { r_itype.im_type_out = r_itype.proxy_name; } + static void postsetup_enum_type(TypeInterface &r_enum_itype) { + r_enum_itype.c_arg_in = "&%s"; + r_enum_itype.c_type = "int"; + r_enum_itype.c_type_in = "int"; + r_enum_itype.c_type_out = "int"; + r_enum_itype.cs_type = r_enum_itype.proxy_name; + r_enum_itype.cs_in = "(int)%s"; + r_enum_itype.cs_out = "return (%1)%0;"; + r_enum_itype.im_type_in = "int"; + r_enum_itype.im_type_out = "int"; + r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name]; + } + TypeInterface() { api_type = ClassDB::API_NONE; + is_enum = false; is_object_type = false; is_singleton = false; is_reference = false; @@ -492,6 +517,8 @@ class BindingsGenerator { return "Ref"; else if (p_type.is_object_type) return "Obj"; + else if (p_type.is_enum) + return "int"; return p_type.name; } @@ -501,8 +528,8 @@ class BindingsGenerator { void _generate_header_icalls(); void _generate_method_icalls(const TypeInterface &p_itype); - const TypeInterface *_get_type_by_name_or_null(const StringName &p_cname); - const TypeInterface *_get_type_by_name_or_placeholder(const StringName &p_cname); + const TypeInterface *_get_type_or_null(const TypeReference &p_typeref); + const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref); void _default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg); void _populate_builtin_type(TypeInterface &r_itype, Variant::Type vtype); diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/mono_reg_utils.py index 8ddddb3a24..9c188d07a7 100644 --- a/modules/mono/mono_reg_utils.py +++ b/modules/mono/mono_reg_utils.py @@ -75,7 +75,7 @@ def find_msbuild_tools_path_reg(): vswhere = os.getenv('PROGRAMFILES') vswhere += r'\Microsoft Visual Studio\Installer\vswhere.exe' - vswhere_args = ['-latest', '-requires', 'Microsoft.Component.MSBuild'] + vswhere_args = ['-latest', '-products', '*', '-requires', 'Microsoft.Component.MSBuild'] try: lines = subprocess.check_output([vswhere] + vswhere_args).splitlines() diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index 9bb8da8ac0..7b23cd7579 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -174,6 +174,8 @@ String find_msbuild_tools_path() { List<String> vswhere_args; vswhere_args.push_back("-latest"); + vswhere_args.push_back("-products"); + vswhere_args.push_back("*"); vswhere_args.push_back("-requires"); vswhere_args.push_back("Microsoft.Component.MSBuild"); diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp index 76c0b969d8..8174bccdb7 100644 --- a/modules/pvr/texture_loader_pvr.cpp +++ b/modules/pvr/texture_loader_pvr.cpp @@ -536,8 +536,8 @@ static void decompress_pvrtc(PVRTCBlock *p_comp_img, const int p_2bit, const int int p_x, p_y; - int p_modulation[8][16]; - int p_modulation_modes[8][16]; + int p_modulation[8][16] = { { 0 } }; + int p_modulation_modes[8][16] = { { 0 } }; int Mod, DoPT; diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index ef680547ca..03bc4c114a 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -2028,6 +2028,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o function.flow_stack_size = 0; function.pass_stack_size = 0; function.node_count = 0; + Map<StringName, int> local_var_indices; if (function.node < 0) { diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index 69bb522173..dad9c68312 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -374,12 +374,10 @@ class VisualScriptInstance : public ScriptInstance { int node; int max_stack; int trash_pos; - int return_pos; int flow_stack_size; int pass_stack_size; int node_count; int argument_count; - bool valid; }; Map<StringName, Function> functions; diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp index 55e2cc5fb5..d5f9d21348 100644 --- a/modules/visual_script/visual_script_expression.cpp +++ b/modules/visual_script/visual_script_expression.cpp @@ -455,7 +455,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) { break; } - if (cchar == '-' || (cchar >= '0' && cchar <= '9')) { + if (cchar >= '0' && cchar <= '9') { //a number String num; @@ -466,11 +466,6 @@ Error VisualScriptExpression::_get_token(Token &r_token) { #define READING_DONE 4 int reading = READING_INT; - if (cchar == '-') { - num += '-'; - cchar = GET_CHAR(); - } - CharType c = cchar; bool exp_sign = false; bool exp_beg = false; diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp index 8a064fb5a4..3855a39aef 100644 --- a/modules/websocket/lws_peer.cpp +++ b/modules/websocket/lws_peer.cpp @@ -35,6 +35,7 @@ // Needed for socket_helpers on Android at least. UNIXes has it, just include if not windows #if !defined(WINDOWS_ENABLED) #include <netinet/in.h> +#include <sys/socket.h> #endif #include "drivers/unix/socket_helpers.h" @@ -190,9 +191,11 @@ IP_Address LWSPeer::get_connected_host() const { IP_Address ip; int port = 0; - socklen_t len; + socklen_t len = 0; struct sockaddr_storage addr; + int fd = lws_get_socket_fd(wsi); + ERR_FAIL_COND_V(fd == -1, IP_Address()); int ret = getpeername(fd, (struct sockaddr *)&addr, &len); ERR_FAIL_COND_V(ret != 0, IP_Address()); @@ -209,9 +212,11 @@ uint16_t LWSPeer::get_connected_port() const { IP_Address ip; int port = 0; - socklen_t len; + socklen_t len = 0; struct sockaddr_storage addr; + int fd = lws_get_socket_fd(wsi); + ERR_FAIL_COND_V(fd == -1, 0); int ret = getpeername(fd, (struct sockaddr *)&addr, &len); ERR_FAIL_COND_V(ret != 0, 0); diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 587f8fa89b..6ed03d7aee 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -1323,6 +1323,8 @@ public: virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + String src_apk; EditorProgress ep("export", "Exporting for Android", 105); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 5557c1de44..fc41adeb76 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -330,17 +330,6 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos> if (touch.size()) { //end all if exist - { - Ref<InputEventMouseButton> ev; - ev.instance(); - ev->set_button_index(BUTTON_LEFT); - ev->set_button_mask(BUTTON_MASK_LEFT); - ev->set_pressed(false); - ev->set_position(touch[0].pos); - ev->set_global_position(touch[0].pos); - input->parse_input_event(ev); - } - for (int i = 0; i < touch.size(); i++) { Ref<InputEventScreenTouch> ev; @@ -358,21 +347,6 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos> touch[i].pos = p_points[i].pos; } - { - //send mouse - Ref<InputEventMouseButton> ev; - ev.instance(); - // ev.type = Ref<InputEvent>::MOUSE_BUTTON; - ev->set_button_index(BUTTON_LEFT); - ev->set_button_mask(BUTTON_MASK_LEFT); - ev->set_pressed(true); - ev->set_position(touch[0].pos); - ev->set_global_position(touch[0].pos); - input->set_mouse_position(Point2(touch[0].pos.x, touch[0].pos.y)); - last_mouse = touch[0].pos; - input->parse_input_event(ev); - } - //send touch for (int i = 0; i < touch.size(); i++) { @@ -387,19 +361,6 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos> } break; case 1: { //motion - if (p_points.size()) { - //send mouse, should look for point 0? - Ref<InputEventMouseMotion> ev; - ev.instance(); - ev->set_button_mask(BUTTON_MASK_LEFT); - ev->set_position(p_points[0].pos); - input->set_mouse_position(Point2(ev->get_position().x, ev->get_position().y)); - ev->set_speed(input->get_last_mouse_speed()); - ev->set_relative(p_points[0].pos - last_mouse); - last_mouse = p_points[0].pos; - input->parse_input_event(ev); - } - ERR_FAIL_COND(touch.size() != p_points.size()); for (int i = 0; i < touch.size(); i++) { @@ -432,16 +393,6 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos> if (touch.size()) { //end all if exist - Ref<InputEventMouseButton> ev; - ev.instance(); - ev->set_button_index(BUTTON_LEFT); - ev->set_button_mask(BUTTON_MASK_LEFT); - ev->set_pressed(false); - ev->set_position(touch[0].pos); - ev->set_global_position(touch[0].pos); - input->set_mouse_position(Point2(touch[0].pos.x, touch[0].pos.y)); - input->parse_input_event(ev); - for (int i = 0; i < touch.size(); i++) { Ref<InputEventScreenTouch> ev; diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 12181b3688..d2457e538d 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -93,7 +93,6 @@ public: private: Vector<TouchPos> touch; - Point2 last_mouse; GFXInitFunc gfx_init_func; void *gfx_init_ud; diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm index ff95cf6e5f..478a3125af 100644 --- a/platform/iphone/gl_view.mm +++ b/platform/iphone/gl_view.mm @@ -497,7 +497,7 @@ static void clear_touches() { int tid = get_touch_id(touch); ERR_FAIL_COND(tid == -1); CGPoint touchPoint = [touch locationInView:self]; - OSIPhone::get_singleton()->mouse_button(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1, tid == 0); + OSIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1); }; }; } @@ -514,10 +514,9 @@ static void clear_touches() { continue; int tid = get_touch_id(touch); ERR_FAIL_COND(tid == -1); - int first = get_first_id(touch); CGPoint touchPoint = [touch locationInView:self]; CGPoint prev_point = [touch previousLocationInView:self]; - OSIPhone::get_singleton()->mouse_move(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, first == tid); + OSIPhone::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor); }; }; } @@ -533,9 +532,9 @@ static void clear_touches() { continue; int tid = get_touch_id(touch); ERR_FAIL_COND(tid == -1); - int rem = remove_touch(touch); + remove_touch(touch); CGPoint touchPoint = [touch locationInView:self]; - OSIPhone::get_singleton()->mouse_button(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false, rem == 0); + OSIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false); }; }; } diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 97ebc19335..f618c80a77 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -190,7 +190,7 @@ void OSIPhone::key(uint32_t p_key, bool p_pressed) { queue_event(ev); }; -void OSIPhone::mouse_button(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick, bool p_use_as_mouse) { +void OSIPhone::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick) { if (!GLOBAL_DEF("debug/disable_touch", false)) { Ref<InputEventScreenTouch> ev; @@ -202,28 +202,10 @@ void OSIPhone::mouse_button(int p_idx, int p_x, int p_y, bool p_pressed, bool p_ queue_event(ev); }; - mouse_list.pressed[p_idx] = p_pressed; - - if (p_use_as_mouse) { - - Ref<InputEventMouseButton> ev; - ev.instance(); - - ev->set_position(Vector2(p_x, p_y)); - ev->set_global_position(Vector2(p_x, p_y)); - - //mouse_list.pressed[p_idx] = p_pressed; - - input->set_mouse_position(ev->get_position()); - ev->set_button_index(BUTTON_LEFT); - ev->set_doubleclick(p_doubleclick); - ev->set_pressed(p_pressed); - - queue_event(ev); - }; + touch_list.pressed[p_idx] = p_pressed; }; -void OSIPhone::mouse_move(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, bool p_use_as_mouse) { +void OSIPhone::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y) { if (!GLOBAL_DEF("debug/disable_touch", false)) { @@ -234,21 +216,6 @@ void OSIPhone::mouse_move(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_ ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y)); queue_event(ev); }; - - if (p_use_as_mouse) { - Ref<InputEventMouseMotion> ev; - ev.instance(); - - ev->set_position(Vector2(p_x, p_y)); - ev->set_global_position(Vector2(p_x, p_y)); - ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y)); - - input->set_mouse_position(ev->get_position()); - ev->set_speed(input->get_last_mouse_speed()); - ev->set_button_mask(BUTTON_LEFT); // pressed - - queue_event(ev); - }; }; void OSIPhone::queue_event(const Ref<InputEvent> &p_event) { @@ -262,10 +229,10 @@ void OSIPhone::touches_cancelled() { for (int i = 0; i < MAX_MOUSE_COUNT; i++) { - if (mouse_list.pressed[i]) { + if (touch_list.pressed[i]) { // send a mouse_up outside the screen - mouse_button(i, -1, -1, false, false, false); + touch_press(i, -1, -1, false, false); }; }; }; @@ -376,7 +343,7 @@ Point2 OSIPhone::get_mouse_position() const { int OSIPhone::get_mouse_button_state() const { - return mouse_list.pressed[0]; + return 0; }; void OSIPhone::set_window_title(const String &p_title){}; diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index 00d9efb01a..7d73a6fe5c 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -106,7 +106,7 @@ private: }; }; - MouseList mouse_list; + MouseList touch_list; Vector3 last_accel; @@ -127,8 +127,8 @@ public: uint8_t get_orientations() const; - void mouse_button(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick, bool p_use_as_mouse); - void mouse_move(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, bool p_use_as_mouse); + void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick); + void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y); void touches_cancelled(); void key(uint32_t p_key, bool p_pressed); void set_virtual_keyboard_height(int p_height); diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index a275fb7929..1b5463e40d 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -285,23 +285,6 @@ static EM_BOOL _touchpress_callback(int event_type, const EmscriptenTouchEvent * _input->parse_input_event(ev); } - - if (touch_event->touches[lowest_id_index].isChanged) { - - Ref<InputEventMouseButton> ev_mouse; - ev_mouse.instance(); - ev_mouse->set_button_mask(_input->get_mouse_button_mask()); - dom2godot_mod(touch_event, ev_mouse); - - const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index]; - ev_mouse->set_position(Point2(first_touch.canvasX, first_touch.canvasY)); - ev_mouse->set_global_position(ev_mouse->get_position()); - - ev_mouse->set_button_index(BUTTON_LEFT); - ev_mouse->set_pressed(event_type == EMSCRIPTEN_EVENT_TOUCHSTART); - - _input->parse_input_event(ev_mouse); - } return true; } @@ -327,24 +310,6 @@ static EM_BOOL _touchmove_callback(int event_type, const EmscriptenTouchEvent *t _input->parse_input_event(ev); } - - if (touch_event->touches[lowest_id_index].isChanged) { - - Ref<InputEventMouseMotion> ev_mouse; - ev_mouse.instance(); - dom2godot_mod(touch_event, ev_mouse); - ev_mouse->set_button_mask(_input->get_mouse_button_mask()); - - const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index]; - ev_mouse->set_position(Point2(first_touch.canvasX, first_touch.canvasY)); - ev_mouse->set_global_position(ev_mouse->get_position()); - - ev_mouse->set_relative(ev_mouse->get_position() - _input->get_mouse_position()); - _input->set_mouse_position(ev_mouse->get_position()); - ev_mouse->set_speed(_input->get_last_mouse_speed()); - - _input->parse_input_event(ev_mouse); - } return true; } diff --git a/platform/uwp/app.cpp b/platform/uwp/app.cpp index 5ff62b38f9..c18aa36402 100644 --- a/platform/uwp/app.cpp +++ b/platform/uwp/app.cpp @@ -85,8 +85,7 @@ App::App() : mWindowHeight(0), mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT), - mEglSurface(EGL_NO_SURFACE), - number_of_contacts(0) { + mEglSurface(EGL_NO_SURFACE) { } // The first method called when the IFrameworkView is being created. @@ -271,48 +270,44 @@ void App::pointer_event(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Cor last_touch_y[screen_touch->get_index()] = pos.Y; os->input_event(screen_touch); - if (number_of_contacts > 1) - return; + } else { - }; // fallthrought of sorts - - Ref<InputEventMouseButton> mouse_button; - mouse_button.instance(); - mouse_button->set_device(0); - mouse_button->set_pressed(p_pressed); - mouse_button->set_button_index(but); - mouse_button->set_position(Vector2(pos.X, pos.Y)); - mouse_button->set_global_position(Vector2(pos.X, pos.Y)); - - if (p_is_wheel) { - if (point->Properties->MouseWheelDelta > 0) { - mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_UP); - } else if (point->Properties->MouseWheelDelta < 0) { - mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? BUTTON_WHEEL_LEFT : BUTTON_WHEEL_DOWN); + Ref<InputEventMouseButton> mouse_button; + mouse_button.instance(); + mouse_button->set_device(0); + mouse_button->set_pressed(p_pressed); + mouse_button->set_button_index(but); + mouse_button->set_position(Vector2(pos.X, pos.Y)); + mouse_button->set_global_position(Vector2(pos.X, pos.Y)); + + if (p_is_wheel) { + if (point->Properties->MouseWheelDelta > 0) { + mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_UP); + } else if (point->Properties->MouseWheelDelta < 0) { + mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? BUTTON_WHEEL_LEFT : BUTTON_WHEEL_DOWN); + } } - } - last_touch_x[31] = pos.X; - last_touch_y[31] = pos.Y; + last_touch_x[31] = pos.X; + last_touch_y[31] = pos.Y; - os->input_event(mouse_button); - - if (p_is_wheel) { - // Send release for mouse wheel - mouse_button->set_pressed(false); os->input_event(mouse_button); + + if (p_is_wheel) { + // Send release for mouse wheel + mouse_button->set_pressed(false); + os->input_event(mouse_button); + } } }; void App::OnPointerPressed(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) { - number_of_contacts++; pointer_event(sender, args, true); }; void App::OnPointerReleased(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) { - number_of_contacts--; pointer_event(sender, args, false); }; @@ -351,7 +346,7 @@ void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Co Windows::UI::Input::PointerPoint ^ point = args->CurrentPoint; Windows::Foundation::Point pos = _get_pixel_position(window, point->Position, os); - if (point->IsInContact && _is_touch(point)) { + if (_is_touch(point)) { Ref<InputEventScreenDrag> screen_drag; screen_drag.instance(); @@ -361,25 +356,23 @@ void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Co screen_drag->set_relative(Vector2(screen_drag->get_position().x - last_touch_x[screen_drag->get_index()], screen_drag->get_position().y - last_touch_y[screen_drag->get_index()])); os->input_event(screen_drag); - if (number_of_contacts > 1) - return; - - }; // fallthrought of sorts + } else { - // In case the mouse grabbed, MouseMoved will handle this - if (os->get_mouse_mode() == OS::MouseMode::MOUSE_MODE_CAPTURED) - return; + // In case the mouse grabbed, MouseMoved will handle this + if (os->get_mouse_mode() == OS::MouseMode::MOUSE_MODE_CAPTURED) + return; - Ref<InputEventMouseMotion> mouse_motion; - mouse_motion.instance(); - mouse_motion->set_device(0); - mouse_motion->set_position(Vector2(pos.X, pos.Y)); - mouse_motion->set_global_position(Vector2(pos.X, pos.Y)); - mouse_motion->set_relative(Vector2(pos.X - last_touch_x[31], pos.Y - last_touch_y[31])); + Ref<InputEventMouseMotion> mouse_motion; + mouse_motion.instance(); + mouse_motion->set_device(0); + mouse_motion->set_position(Vector2(pos.X, pos.Y)); + mouse_motion->set_global_position(Vector2(pos.X, pos.Y)); + mouse_motion->set_relative(Vector2(pos.X - last_touch_x[31], pos.Y - last_touch_y[31])); - last_mouse_pos = pos; + last_mouse_pos = pos; - os->input_event(mouse_motion); + os->input_event(mouse_motion); + } } void App::OnMouseMoved(MouseDevice ^ mouse_device, MouseEventArgs ^ args) { diff --git a/platform/uwp/app.h b/platform/uwp/app.h index c23270b8ba..5f69f2cb0e 100644 --- a/platform/uwp/app.h +++ b/platform/uwp/app.h @@ -107,7 +107,6 @@ namespace GodotUWP int last_touch_x[32]; // 20 fingers, index 31 reserved for the mouse int last_touch_y[32]; - int number_of_contacts; Windows::Foundation::Point last_mouse_pos; }; } diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index 7d7bee9227..3c537b3b58 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -122,6 +122,14 @@ class AppxPackager { Vector<BlockHash> hashes; uLong file_crc32; ZPOS64_T zip_offset; + + FileMeta() : + lfh_size(0), + compressed(false), + compressed_size(0), + uncompressed_size(0), + file_crc32(0), + zip_offset(0) {} }; String progress_task; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 9c37b65d77..363f54fc7b 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -342,6 +342,14 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } break; case WM_MOUSEMOVE: { + if (input->is_emulating_mouse_from_touch()) { + // Universal translation enabled; ignore OS translation + LPARAM extra = GetMessageExtraInfo(); + if (IsPenEvent(extra)) { + break; + } + } + if (outside) { //mouse enter @@ -367,18 +375,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode. if (!window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED) break; - /* - LPARAM extra = GetMessageExtraInfo(); - if (IsPenEvent(extra)) { - - int idx = extra & 0x7f; - _drag_event(idx, uMsg, wParam, lParam); - if (idx != 0) { - return 0; - }; - // fallthrough for mouse event - }; - */ Ref<InputEventMouseMotion> mm; mm.instance(); @@ -448,18 +444,13 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) /*case WM_XBUTTONDOWN: case WM_XBUTTONUP: */ { - /* - LPARAM extra = GetMessageExtraInfo(); - if (IsPenEvent(extra)) { - - int idx = extra & 0x7f; - _touch_event(idx, uMsg, wParam, lParam); - if (idx != 0) { - return 0; - }; - // fallthrough for mouse event - }; - */ + if (input->is_emulating_mouse_from_touch()) { + // Universal translation enabled; ignore OS translation + LPARAM extra = GetMessageExtraInfo(); + if (IsPenEvent(extra)) { + break; + } + } Ref<InputEventMouseButton> mb; mb.instance(); diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 1928800d8c..336068cb1e 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -1690,6 +1690,11 @@ void OS_X11::process_xevents() { if (touch.state.has(index)) // Defensive break; touch.state[index] = pos; + if (touch.state.size() == 1) { + // X11 may send a motion event when a touch gesture begins, that would result + // in a spurious mouse motion event being sent to Godot; remember it to be able to filter it out + touch.mouse_pos_to_filter = pos; + } input->parse_input_event(st); } else { if (!touch.state.has(index)) // Defensive @@ -1896,6 +1901,18 @@ void OS_X11::process_xevents() { // to be able to send relative motion events. Point2i pos(event.xmotion.x, event.xmotion.y); + // Avoidance of spurious mouse motion (see handling of touch) + bool filter = false; + // Adding some tolerance to match better Point2i to Vector2 + if (touch.state.size() && Vector2(pos).distance_squared_to(touch.mouse_pos_to_filter) < 2) { + filter = true; + } + // Invalidate to avoid filtering a possible legitimate similar event coming later + touch.mouse_pos_to_filter = Vector2(1e10, 1e10); + if (filter) { + break; + } + if (mouse_mode == MOUSE_MODE_CAPTURED) { if (pos == Point2i(current_videomode.width / 2, current_videomode.height / 2)) { diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 610dba0716..0a39da77de 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -127,6 +127,7 @@ class OS_X11 : public OS_Unix { Vector<int> devices; XIEventMask event_mask; Map<int, Vector2> state; + Vector2 mouse_pos_to_filter; } touch; #endif diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp index f8cec72400..3813bd96fe 100644 --- a/scene/2d/node_2d.cpp +++ b/scene/2d/node_2d.cpp @@ -451,7 +451,7 @@ void Node2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_rotation", PROPERTY_HINT_NONE, "", 0), "set_global_rotation", "get_global_rotation"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_rotation_degrees", PROPERTY_HINT_NONE, "", 0), "set_global_rotation_degrees", "get_global_rotation_degrees"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_scale", PROPERTY_HINT_NONE, "", 0), "set_global_scale", "get_global_scale"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_scale", PROPERTY_HINT_NONE, "", 0), "set_global_scale", "get_global_scale"); ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_transform", PROPERTY_HINT_NONE, "", 0), "set_global_transform", "get_global_transform"); ADD_GROUP("Z Index", ""); diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index bf5bf29b2e..eed26f5399 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -430,6 +430,71 @@ Vector2 Polygon2D::get_offset() const { return offset; } +void Polygon2D::add_bone(const NodePath &p_path, const PoolVector<float> &p_weights) { + + Bone bone; + bone.path = p_path; + bone.weights = p_weights; + bone_weights.push_back(bone); +} +int Polygon2D::get_bone_count() const { + return bone_weights.size(); +} +NodePath Polygon2D::get_bone_path(int p_index) const { + ERR_FAIL_INDEX_V(p_index, bone_weights.size(), NodePath()); + return bone_weights[p_index].path; +} +PoolVector<float> Polygon2D::get_bone_weights(int p_index) const { + + ERR_FAIL_INDEX_V(p_index, bone_weights.size(), PoolVector<float>()); + return bone_weights[p_index].weights; +} +void Polygon2D::erase_bone(int p_idx) { + + ERR_FAIL_INDEX(p_idx, bone_weights.size()); + bone_weights.remove(p_idx); +} + +void Polygon2D::clear_bones() { + bone_weights.clear(); +} + +void Polygon2D::set_bone_weights(int p_index, const PoolVector<float> &p_weights) { + ERR_FAIL_INDEX(p_index, bone_weights.size()); + bone_weights[p_index].weights = p_weights; + update(); +} +void Polygon2D::set_bone_path(int p_index, const NodePath &p_path) { + ERR_FAIL_INDEX(p_index, bone_weights.size()); + bone_weights[p_index].path = p_path; + update(); +} + +Array Polygon2D::_get_bones() const { + Array bones; + for (int i = 0; i < get_bone_count(); i++) { + bones.push_back(get_bone_path(i)); + bones.push_back(get_bone_weights(i)); + } + return bones; +} +void Polygon2D::_set_bones(const Array &p_bones) { + + ERR_FAIL_COND(p_bones.size() & 1); + clear_bones(); + for (int i = 0; i < p_bones.size(); i += 2) { + add_bone(p_bones[i], p_bones[i + 1]); + } +} + +void Polygon2D::set_skeleton(const NodePath &p_skeleton) { + skeleton = p_skeleton; + update(); +} +NodePath Polygon2D::get_skeleton() const { + return skeleton; +} + void Polygon2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &Polygon2D::set_polygon); @@ -474,6 +539,21 @@ void Polygon2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Polygon2D::set_offset); ClassDB::bind_method(D_METHOD("get_offset"), &Polygon2D::get_offset); + ClassDB::bind_method(D_METHOD("add_bone", "path", "weights"), &Polygon2D::add_bone); + ClassDB::bind_method(D_METHOD("get_bone_count"), &Polygon2D::get_bone_count); + ClassDB::bind_method(D_METHOD("get_bone_path", "index"), &Polygon2D::get_bone_path); + ClassDB::bind_method(D_METHOD("get_bone_weights", "index"), &Polygon2D::get_bone_weights); + ClassDB::bind_method(D_METHOD("erase_bone", "index"), &Polygon2D::erase_bone); + ClassDB::bind_method(D_METHOD("clear_bones"), &Polygon2D::clear_bones); + ClassDB::bind_method(D_METHOD("set_bone_path", "index", "path"), &Polygon2D::set_bone_path); + ClassDB::bind_method(D_METHOD("set_bone_weights", "index", "weights"), &Polygon2D::set_bone_weights); + + ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &Polygon2D::set_skeleton); + ClassDB::bind_method(D_METHOD("get_skeleton"), &Polygon2D::get_skeleton); + + ClassDB::bind_method(D_METHOD("_set_bones", "bones"), &Polygon2D::_set_bones); + ClassDB::bind_method(D_METHOD("_get_bones"), &Polygon2D::_get_bones); + ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv"); ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "splits"), "set_splits", "get_splits"); @@ -488,10 +568,14 @@ void Polygon2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_scale"), "set_texture_scale", "get_texture_scale"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation_degrees", PROPERTY_HINT_RANGE, "-1440,1440,0.1"), "set_texture_rotation_degrees", "get_texture_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation", PROPERTY_HINT_NONE, "", 0), "set_texture_rotation", "get_texture_rotation"); + ADD_GROUP("Skeleton", ""); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton"), "set_skeleton", "get_skeleton"); ADD_GROUP("Invert", "invert_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_enable"), "set_invert", "get_invert"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "invert_border", PROPERTY_HINT_RANGE, "0.1,16384,0.1"), "set_invert_border", "get_invert_border"); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_bones", "_get_bones"); } Polygon2D::Polygon2D() { diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h index 262208f2f1..575f71d74a 100644 --- a/scene/2d/polygon_2d.h +++ b/scene/2d/polygon_2d.h @@ -42,6 +42,13 @@ class Polygon2D : public Node2D { PoolVector<Color> vertex_colors; PoolVector<int> splits; + struct Bone { + NodePath path; + PoolVector<float> weights; + }; + + Vector<Bone> bone_weights; + Color color; Ref<Texture> texture; Size2 tex_scale; @@ -56,6 +63,11 @@ class Polygon2D : public Node2D { mutable bool rect_cache_dirty; mutable Rect2 item_rect; + NodePath skeleton; + + Array _get_bones() const; + void _set_bones(const Array &p_bones); + protected: void _notification(int p_what); static void _bind_methods(); @@ -114,6 +126,18 @@ public: void set_offset(const Vector2 &p_offset); Vector2 get_offset() const; + void add_bone(const NodePath &p_path = NodePath(), const PoolVector<float> &p_weights = PoolVector<float>()); + int get_bone_count() const; + NodePath get_bone_path(int p_index) const; + PoolVector<float> get_bone_weights(int p_index) const; + void erase_bone(int p_idx); + void clear_bones(); + void set_bone_weights(int p_index, const PoolVector<float> &p_weights); + void set_bone_path(int p_index, const NodePath &p_path); + + void set_skeleton(const NodePath &p_skeleton); + NodePath get_skeleton() const; + Polygon2D(); }; diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp index 705e82bcbb..3b3561bc1c 100644 --- a/scene/2d/skeleton_2d.cpp +++ b/scene/2d/skeleton_2d.cpp @@ -12,6 +12,8 @@ void Bone2D::_notification(int p_what) { break; if (!Object::cast_to<Bone2D>(parent)) break; //skeletons must be chained to Bone2Ds. + + parent = parent->get_parent(); } if (skeleton) { diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 14eb26b1ec..3d3f43d5c6 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -304,6 +304,7 @@ void TileMap::_update_dirty_quadrants() { } q.occluder_instances.clear(); Ref<ShaderMaterial> prev_material; + int prev_z_index; RID prev_canvas_item; RID prev_debug_canvas_item; @@ -324,11 +325,12 @@ void TileMap::_update_dirty_quadrants() { continue; Ref<ShaderMaterial> mat = tile_set->tile_get_material(c.id); + int z_index = tile_set->tile_get_z_index(c.id); RID canvas_item; RID debug_canvas_item; - if (prev_canvas_item == RID() || prev_material != mat) { + if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) { canvas_item = vs->canvas_item_create(); if (mat.is_valid()) @@ -339,6 +341,7 @@ void TileMap::_update_dirty_quadrants() { xform.set_origin(q.pos); vs->canvas_item_set_transform(canvas_item, xform); vs->canvas_item_set_light_mask(canvas_item, get_light_mask()); + vs->canvas_item_set_z_index(canvas_item, z_index); q.canvas_items.push_back(canvas_item); @@ -354,6 +357,7 @@ void TileMap::_update_dirty_quadrants() { prev_canvas_item = canvas_item; prev_material = mat; + prev_z_index = z_index; } else { canvas_item = prev_canvas_item; diff --git a/scene/3d/collision_polygon.cpp b/scene/3d/collision_polygon.cpp index 3a77360bc2..379dd21c39 100644 --- a/scene/3d/collision_polygon.cpp +++ b/scene/3d/collision_polygon.cpp @@ -173,6 +173,9 @@ String CollisionPolygon::get_configuration_warning() const { return String(); } +bool CollisionPolygon::_is_editable_3d_polygon() const { + return true; +} void CollisionPolygon::_bind_methods() { ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CollisionPolygon::set_depth); @@ -184,6 +187,8 @@ void CollisionPolygon::_bind_methods() { ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon::set_disabled); ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon::is_disabled); + ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CollisionPolygon::_is_editable_3d_polygon); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth"), "set_depth", "get_depth"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); diff --git a/scene/3d/collision_polygon.h b/scene/3d/collision_polygon.h index 971c67f1ad..f1f137c9c5 100644 --- a/scene/3d/collision_polygon.h +++ b/scene/3d/collision_polygon.h @@ -53,6 +53,8 @@ protected: void _update_in_shape_owner(bool p_xform_only = false); + bool _is_editable_3d_polygon() const; + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp index 693b416f6d..d1bd059b63 100644 --- a/scene/3d/particles.cpp +++ b/scene/3d/particles.cpp @@ -1028,8 +1028,6 @@ void ParticlesMaterial::set_param(Parameter p_param, float p_value) { case PARAM_ANIM_OFFSET: { VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset, p_value); } break; - case PARAM_MAX: { - }; } } float ParticlesMaterial::get_param(Parameter p_param) const { @@ -1082,8 +1080,6 @@ void ParticlesMaterial::set_param_randomness(Parameter p_param, float p_value) { case PARAM_ANIM_OFFSET: { VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_random, p_value); } break; - case PARAM_MAX: { - }; } } float ParticlesMaterial::get_param_randomness(Parameter p_param) const { @@ -1160,8 +1156,6 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture> case PARAM_ANIM_OFFSET: { VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, p_texture); } break; - case PARAM_MAX: { - }; } _queue_shader_change(); diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp index 7ac7f74bb0..57d79c960f 100644 --- a/scene/3d/path.cpp +++ b/scene/3d/path.cpp @@ -40,6 +40,9 @@ void Path::_curve_changed() { if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) update_gizmo(); + if (is_inside_tree()) { + emit_signal("curve_changed"); + } } void Path::set_curve(const Ref<Curve3D> &p_curve) { @@ -68,6 +71,8 @@ void Path::_bind_methods() { ClassDB::bind_method(D_METHOD("_curve_changed"), &Path::_curve_changed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve3D"), "set_curve", "get_curve"); + + ADD_SIGNAL(MethodInfo("curve_changed")); } Path::Path() { diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp index d389b69ef3..13700e0bd3 100644 --- a/scene/3d/voxel_light_baker.cpp +++ b/scene/3d/voxel_light_baker.cpp @@ -2338,9 +2338,9 @@ Ref<MultiMesh> VoxelLightBaker::create_debug_multimesh(DebugMode p_mode) { for (int k = 0; k < 3; k++) { if (i < 3) - face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[j][(i + k) % 3] = v[k]; else - face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[3 - j][(i + k) % 3] = -v[k]; } } diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 42fa20068b..afdb8b6f71 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -812,8 +812,6 @@ void AnimationTreePlayer::_process_animation(float p_delta) { t.value = t.object->get_indexed(t.subpath); t.value.zero(); - - t.skip = false; } /* STEP 2 PROCESS ANIMATIONS */ @@ -886,7 +884,7 @@ void AnimationTreePlayer::_process_animation(float p_delta) { Track &t = E->get(); - if (t.skip || !t.object) + if (!t.object) continue; if (t.subpath.size()) { // value track diff --git a/scene/animation/animation_tree_player.h b/scene/animation/animation_tree_player.h index 09d6f6fcb4..873ff8a9da 100644 --- a/scene/animation/animation_tree_player.h +++ b/scene/animation/animation_tree_player.h @@ -107,8 +107,6 @@ private: Vector3 scale; Variant value; - - bool skip; }; typedef Map<TrackKey, Track> TrackMap; diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index b401abd436..278e4123d7 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -36,6 +36,8 @@ void GridContainer::_notification(int p_what) { case NOTIFICATION_SORT_CHILDREN: { + int valid_controls_index; + Map<int, int> col_minw; // max of min_width of all controls in each col (indexed by col) Map<int, int> row_minh; // max of min_height of all controls in each row (indexed by row) Set<int> col_expanded; // columns which have the SIZE_EXPAND flag set @@ -47,13 +49,15 @@ void GridContainer::_notification(int p_what) { int max_row = get_child_count() / columns; // Compute the per-column/per-row data + valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); if (!c || !c->is_visible_in_tree()) continue; - int row = i / columns; - int col = i % columns; + int row = valid_controls_index / columns; + int col = valid_controls_index % columns; + valid_controls_index++; Size2i ms = c->get_combined_minimum_size(); if (col_minw.has(col)) @@ -136,12 +140,14 @@ void GridContainer::_notification(int p_what) { int col_ofs = 0; int row_ofs = 0; + valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); if (!c || !c->is_visible_in_tree()) continue; - int row = i / columns; - int col = i % columns; + int row = valid_controls_index / columns; + int col = valid_controls_index % columns; + valid_controls_index++; if (col == 0) { col_ofs = 0; @@ -178,6 +184,8 @@ void GridContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_columns", "columns"), &GridContainer::set_columns); ClassDB::bind_method(D_METHOD("get_columns"), &GridContainer::get_columns); + ClassDB::bind_method(D_METHOD("get_child_control_at_cell", "row", "column"), + &GridContainer::get_child_control_at_cell); ADD_PROPERTY(PropertyInfo(Variant::INT, "columns", PROPERTY_HINT_RANGE, "1,1024,1"), "set_columns", "get_columns"); } @@ -190,17 +198,19 @@ Size2 GridContainer::get_minimum_size() const { int hsep = get_constant("hseparation"); int vsep = get_constant("vseparation"); - int idx = 0; int max_row = 0; int max_col = 0; + int valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); if (!c || !c->is_visible_in_tree()) continue; - int row = idx / columns; - int col = idx % columns; + int row = valid_controls_index / columns; + int col = valid_controls_index % columns; + valid_controls_index++; + Size2i ms = c->get_combined_minimum_size(); if (col_minw.has(col)) col_minw[col] = MAX(col_minw[col], ms.width); @@ -213,7 +223,6 @@ Size2 GridContainer::get_minimum_size() const { row_minh[row] = ms.height; max_col = MAX(col, max_col); max_row = MAX(row, max_row); - idx++; } Size2 ms; @@ -232,6 +241,21 @@ Size2 GridContainer::get_minimum_size() const { return ms; } +Control *GridContainer::get_child_control_at_cell(int row, int column) { + Control *c; + int grid_index = row * columns + column; + for (int i = 0; i < get_child_count(); i++) { + c = Object::cast_to<Control>(get_child(i)); + if (!c || !c->is_visible_in_tree()) + continue; + + if (grid_index == i) { + break; + } + } + return c; +} + GridContainer::GridContainer() { set_mouse_filter(MOUSE_FILTER_PASS); diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h index 243d06f034..7e3470dc89 100644 --- a/scene/gui/grid_container.h +++ b/scene/gui/grid_container.h @@ -47,6 +47,7 @@ public: void set_columns(int p_columns); int get_columns() const; virtual Size2 get_minimum_size() const; + Control *get_child_control_at_cell(int row, int column); GridContainer(); }; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index fe5f3b769c..ef0d5e4c6e 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -215,6 +215,12 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { case (KEY_A): { //Select All select(); } break; + case (KEY_LEFT): { // Go to start of text - like HOME key + set_cursor_position(0); + } break; + case (KEY_RIGHT): { // Go to end of text - like END key + set_cursor_position(text.length()); + } break; default: { handled = false; } } @@ -663,8 +669,8 @@ void LineEdit::_notification(int p_what) { if (ofs >= ime_text.length()) break; - CharType cchar = (pass && !text.empty()) ? '*' : ime_text[ofs]; - CharType next = (pass && !text.empty()) ? '*' : ime_text[ofs + 1]; + CharType cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs]; + CharType next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1]; int im_char_width = font->get_char_size(cchar, next).width; if ((x_ofs + im_char_width) > ofs_max) @@ -685,8 +691,8 @@ void LineEdit::_notification(int p_what) { } } - CharType cchar = (pass && !text.empty()) ? '*' : t[char_ofs]; - CharType next = (pass && !text.empty()) ? '*' : t[char_ofs + 1]; + CharType cchar = (pass && !text.empty()) ? secret_character[0] : t[char_ofs]; + CharType next = (pass && !text.empty()) ? secret_character[0] : t[char_ofs + 1]; int char_width = font->get_char_size(cchar, next).width; // end of widget, break! @@ -717,8 +723,8 @@ void LineEdit::_notification(int p_what) { if (ofs >= ime_text.length()) break; - CharType cchar = (pass && !text.empty()) ? '*' : ime_text[ofs]; - CharType next = (pass && !text.empty()) ? '*' : ime_text[ofs + 1]; + CharType cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs]; + CharType next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1]; int im_char_width = font->get_char_size(cchar, next).width; if ((x_ofs + im_char_width) > ofs_max) @@ -1225,6 +1231,7 @@ void LineEdit::select_all() { selection.enabled = true; update(); } + void LineEdit::set_editable(bool p_editable) { editable = p_editable; @@ -1241,11 +1248,27 @@ void LineEdit::set_secret(bool p_secret) { pass = p_secret; update(); } + bool LineEdit::is_secret() const { return pass; } +void LineEdit::set_secret_character(const String &p_string) { + + // An empty string as the secret character would crash the engine + // It also wouldn't make sense to use multiple characters as the secret character + ERR_EXPLAIN("Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given)"); + ERR_FAIL_COND(p_string.length() != 1); + + secret_character = p_string; + update(); +} + +String LineEdit::get_secret_character() const { + return secret_character; +} + void LineEdit::select(int p_from, int p_to) { if (p_from == 0 && p_to == 0) { @@ -1434,6 +1457,8 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_editable"), &LineEdit::is_editable); ClassDB::bind_method(D_METHOD("set_secret", "enabled"), &LineEdit::set_secret); ClassDB::bind_method(D_METHOD("is_secret"), &LineEdit::is_secret); + ClassDB::bind_method(D_METHOD("set_secret_character", "character"), &LineEdit::set_secret_character); + ClassDB::bind_method(D_METHOD("get_secret_character"), &LineEdit::get_secret_character); ClassDB::bind_method(D_METHOD("menu_option", "option"), &LineEdit::menu_option); ClassDB::bind_method(D_METHOD("get_menu"), &LineEdit::get_menu); ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &LineEdit::set_context_menu_enabled); @@ -1461,6 +1486,7 @@ void LineEdit::_bind_methods() { ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "max_length"), "set_max_length", "get_max_length"); ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret"); + ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character"); ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length"); ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); @@ -1469,7 +1495,7 @@ void LineEdit::_bind_methods() { ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha"); ADD_GROUP("Caret", "caret_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled"); - ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.1"), "cursor_set_blink_speed", "cursor_get_blink_speed"); + ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed"); ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_position"), "set_cursor_position", "get_cursor_position"); } @@ -1485,6 +1511,7 @@ LineEdit::LineEdit() { window_has_focus = true; max_length = 0; pass = false; + secret_character = "*"; text_changed_dirty = false; placeholder_alpha = 0.6; diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index c60ea36cc1..48dde2461e 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -72,6 +72,7 @@ private: String undo_text; String text; String placeholder; + String secret_character; float placeholder_alpha; String ime_text; Point2 ime_selection; @@ -194,6 +195,9 @@ public: void set_secret(bool p_secret); bool is_secret() const; + void set_secret_character(const String &p_string); + String get_secret_character() const; + virtual Size2 get_minimum_size() const; void set_expand_to_text_length(bool p_enabled); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 6bfc4d4dee..1fcde9e9a8 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -867,9 +867,9 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { // Erase previous selection. if (selection.active) { selection.from = NULL; - selection.from_char = NULL; + selection.from_char = '\0'; selection.to = NULL; - selection.to_char = NULL; + selection.to_char = '\0'; selection.active = false; update(); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index ff0f5815d0..24a13db3c3 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -5644,7 +5644,7 @@ void TextEdit::_bind_methods() { ADD_GROUP("Caret", "caret_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_block_mode"), "cursor_set_block_mode", "cursor_is_block_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled"); - ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.1"), "cursor_set_blink_speed", "cursor_get_blink_speed"); + ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_moving_by_right_click"), "set_right_click_moves_caret", "is_right_click_moving_caret"); ADD_SIGNAL(MethodInfo("cursor_changed")); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 4419dfe70f..727807dbcd 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -33,6 +33,7 @@ #include "editor/editor_node.h" #include "io/marshalls.h" #include "io/resource_loader.h" +#include "main/input_default.h" #include "message_queue.h" #include "node.h" #include "os/keyboard.h" @@ -620,6 +621,13 @@ void SceneTree::_notification(int p_notification) { case NOTIFICATION_WM_FOCUS_IN: case NOTIFICATION_WM_FOCUS_OUT: { + if (p_notification == NOTIFICATION_WM_FOCUS_IN) { + InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + if (id) { + id->ensure_touch_mouse_raised(); + } + } + get_root()->propagate_notification(p_notification); } break; case NOTIFICATION_TRANSLATION_CHANGED: { diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 19d8f09ebe..d94b32afd7 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -58,6 +58,7 @@ #include "scene/2d/ray_cast_2d.h" #include "scene/2d/remote_transform_2d.h" #include "scene/2d/screen_button.h" +#include "scene/2d/skeleton_2d.h" #include "scene/2d/sprite.h" #include "scene/2d/tile_map.h" #include "scene/2d/visibility_notifier_2d.h" @@ -449,6 +450,8 @@ void register_scene_types() { ClassDB::register_class<VisibilityNotifier2D>(); ClassDB::register_class<VisibilityEnabler2D>(); ClassDB::register_class<Polygon2D>(); + ClassDB::register_class<Skeleton2D>(); + ClassDB::register_class<Bone2D>(); ClassDB::register_class<Light2D>(); ClassDB::register_class<LightOccluder2D>(); ClassDB::register_class<OccluderPolygon2D>(); diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index b832ea1239..d87644381c 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -912,6 +912,7 @@ void ArrayMesh::surface_set_material(int p_idx, const Ref<Material> &p_material) VisualServer::get_singleton()->mesh_surface_set_material(mesh, p_idx, p_material.is_null() ? RID() : p_material->get_rid()); _change_notify("material"); + emit_changed(); } void ArrayMesh::surface_set_name(int p_idx, const String &p_name) { @@ -919,6 +920,7 @@ void ArrayMesh::surface_set_name(int p_idx, const String &p_name) { ERR_FAIL_INDEX(p_idx, surfaces.size()); surfaces[p_idx].name = p_name; + emit_changed(); } String ArrayMesh::surface_get_name(int p_idx) const { @@ -931,6 +933,7 @@ void ArrayMesh::surface_update_region(int p_surface, int p_offset, const PoolVec ERR_FAIL_INDEX(p_surface, surfaces.size()); VS::get_singleton()->mesh_surface_update_region(mesh, p_surface, p_offset, p_data); + emit_changed(); } void ArrayMesh::surface_set_custom_aabb(int p_idx, const AABB &p_aabb) { @@ -938,6 +941,7 @@ void ArrayMesh::surface_set_custom_aabb(int p_idx, const AABB &p_aabb) { ERR_FAIL_INDEX(p_idx, surfaces.size()); surfaces[p_idx].aabb = p_aabb; // set custom aabb too? + emit_changed(); } Ref<Material> ArrayMesh::surface_get_material(int p_idx) const { @@ -986,6 +990,7 @@ void ArrayMesh::set_custom_aabb(const AABB &p_custom) { custom_aabb = p_custom; VS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb); + emit_changed(); } AABB ArrayMesh::get_custom_aabb() const { diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp index 94c54c91d3..056ac2772b 100644 --- a/scene/resources/primitive_meshes.cpp +++ b/scene/resources/primitive_meshes.cpp @@ -65,6 +65,8 @@ void PrimitiveMesh::_update() const { pending_request = false; _clear_triangle_mesh(); + + const_cast<PrimitiveMesh *>(this)->emit_changed(); } void PrimitiveMesh::_request_update() { diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp index 626fda50df..ad5a0fd9ab 100644 --- a/scene/resources/style_box.cpp +++ b/scene/resources/style_box.cpp @@ -106,7 +106,11 @@ void StyleBoxTexture::set_texture(Ref<Texture> p_texture) { if (texture == p_texture) return; texture = p_texture; - region_rect = Rect2(Point2(), texture->get_size()); + if (p_texture.is_null()) { + region_rect = Rect2(0, 0, 0, 0); + } else { + region_rect = Rect2(Point2(), texture->get_size()); + } emit_signal("texture_changed"); emit_changed(); _change_notify("texture"); diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index bebbf6e238..42d64376f5 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -142,6 +142,8 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { tile_set_navigation_polygon(id, p_value); else if (what == "navigation_offset") tile_set_navigation_polygon_offset(id, p_value); + else if (what == "z_index") + tile_set_z_index(id, p_value); else return false; @@ -239,6 +241,8 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { r_ret = tile_get_navigation_polygon(id); else if (what == "navigation_offset") r_ret = tile_get_navigation_polygon_offset(id); + else if (what == "z_index") + r_ret = tile_get_z_index(id); else return false; @@ -278,6 +282,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_EDITOR)); p_list->push_back(PropertyInfo(Variant::BOOL, pre + "shape_one_way", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR)); p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "shapes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::INT, pre + "z_index", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1")); } } @@ -748,6 +753,19 @@ Vector<TileSet::ShapeData> TileSet::tile_get_shapes(int p_id) const { return tile_map[p_id].shapes_data; } +int TileSet::tile_get_z_index(int p_id) const { + + ERR_FAIL_COND_V(!tile_map.has(p_id), 0); + return tile_map[p_id].z_index; +} + +void TileSet::tile_set_z_index(int p_id, int p_z_index) { + + ERR_FAIL_COND(!tile_map.has(p_id)); + tile_map[p_id].z_index = p_z_index; + emit_changed(); +} + void TileSet::_tile_set_shapes(int p_id, const Array &p_shapes) { ERR_FAIL_COND(!tile_map.has(p_id)); @@ -929,6 +947,8 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("tile_get_light_occluder", "id"), &TileSet::tile_get_light_occluder); ClassDB::bind_method(D_METHOD("tile_set_occluder_offset", "id", "occluder_offset"), &TileSet::tile_set_occluder_offset); ClassDB::bind_method(D_METHOD("tile_get_occluder_offset", "id"), &TileSet::tile_get_occluder_offset); + ClassDB::bind_method(D_METHOD("tile_set_z_index", "id", "z_index"), &TileSet::tile_set_z_index); + ClassDB::bind_method(D_METHOD("tile_get_z_index", "id"), &TileSet::tile_get_z_index); ClassDB::bind_method(D_METHOD("remove_tile", "id"), &TileSet::remove_tile); ClassDB::bind_method(D_METHOD("clear"), &TileSet::clear); diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 706d04998f..d5704ac9a0 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -113,11 +113,13 @@ private: Color modulate; TileMode tile_mode; AutotileData autotile_data; + int z_index; // Default modulate for back-compat explicit TileData() : tile_mode(SINGLE_TILE), - modulate(1, 1, 1) {} + modulate(1, 1, 1), + z_index(0) {} }; Map<int, TileData> tile_map; @@ -220,6 +222,9 @@ public: Ref<NavigationPolygon> autotile_get_navigation_polygon(int p_id, const Vector2 &p_coord) const; const Map<Vector2, Ref<NavigationPolygon> > &autotile_get_navigation_map(int p_id) const; + void tile_set_z_index(int p_id, int p_z_index); + int tile_get_z_index(int p_id) const; + void remove_tile(int p_id); bool has_tile(int p_id) const; diff --git a/servers/physics/body_pair_sw.cpp b/servers/physics/body_pair_sw.cpp index 882d201f61..2a6a9e08ae 100644 --- a/servers/physics/body_pair_sw.cpp +++ b/servers/physics/body_pair_sw.cpp @@ -78,6 +78,7 @@ void BodyPairSW::contact_added_callback(const Vector3 &p_point_A, const Vector3 contact.local_A = local_A; contact.local_B = local_B; contact.normal = (p_point_A - p_point_B).normalized(); + contact.mass_normal = 0; // will be computed in setup() // attempt to determine if the contact will be reused real_t contact_recycle_radius = space->get_contact_recycle_radius(); diff --git a/servers/physics_2d/body_pair_2d_sw.cpp b/servers/physics_2d/body_pair_2d_sw.cpp index f51882b5ee..61c0e0063f 100644 --- a/servers/physics_2d/body_pair_2d_sw.cpp +++ b/servers/physics_2d/body_pair_2d_sw.cpp @@ -62,6 +62,7 @@ void BodyPair2DSW::_contact_added_callback(const Vector2 &p_point_A, const Vecto contact.local_B = local_B; contact.reused = true; contact.normal = (p_point_A - p_point_B).normalized(); + contact.mass_normal = 0; // will be computed in setup() // attempt to determine if the contact will be reused diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index ba8d134b5c..bfb9c871ce 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -2677,7 +2677,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons return NULL; } - bool index_valid = false; DataType member_type = TYPE_VOID; switch (expr->get_datatype()) { @@ -2696,7 +2695,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons _set_error("Only integer constants are allowed as index at the moment"); return NULL; } - index_valid = true; + switch (expr->get_datatype()) { case TYPE_BVEC2: member_type = TYPE_BOOL; break; case TYPE_VEC2: member_type = TYPE_FLOAT; break; @@ -2721,7 +2720,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons _set_error("Only integer constants are allowed as index at the moment"); return NULL; } - index_valid = true; + switch (expr->get_datatype()) { case TYPE_BVEC3: member_type = TYPE_BOOL; break; case TYPE_VEC3: member_type = TYPE_FLOAT; break; @@ -2745,7 +2744,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons _set_error("Only integer constants are allowed as index at the moment"); return NULL; } - index_valid = true; + switch (expr->get_datatype()) { case TYPE_BVEC4: member_type = TYPE_BOOL; break; case TYPE_VEC4: member_type = TYPE_FLOAT; break; @@ -2760,11 +2759,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } } - if (!index_valid) { - _set_error("Invalid index"); - return NULL; - } - OperatorNode *op = alloc_node<OperatorNode>(); op->op = OP_INDEX; op->return_cache = member_type; @@ -3662,7 +3656,8 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct _set_error("void datatype not allowed here"); return ERR_PARSE_ERROR; } - if (!uniform && type < TYPE_FLOAT && type > TYPE_VEC4) { // FIXME: always false! should it be || instead? + + if (!uniform && (type < TYPE_FLOAT || type > TYPE_VEC4)) { _set_error("Invalid type for varying, only float,vec2,vec3,vec4 allowed."); return ERR_PARSE_ERROR; } diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h index e8cdf1f897..2e3881179a 100644 --- a/servers/visual/shader_language.h +++ b/servers/visual/shader_language.h @@ -425,6 +425,7 @@ public: FunctionNode() { type = TYPE_FUNCTION; + return_type = TYPE_VOID; return_precision = PRECISION_DEFAULT; can_discard = false; } diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index f8640720cb..59dd1ab495 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -214,9 +214,9 @@ RID VisualServer::_make_test_cube() { for (int k = 0; k < 3; k++) { if (i < 3) - face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[j][(i + k) % 3] = v[k]; else - face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1); + face_points[3 - j][(i + k) % 3] = -v[k]; } normal_points[j] = Vector3(); normal_points[j][i % 3] = (i >= 3 ? -1 : 1); |