diff options
41 files changed, 1124 insertions, 244 deletions
diff --git a/SConstruct b/SConstruct index 78d77d1b7a..8e6795cbcb 100644 --- a/SConstruct +++ b/SConstruct @@ -347,11 +347,18 @@ if selected_platform in platform_list: if (env["warnings"] == 'extra'): # FIXME: enable -Wclobbered once #26351 is fixed + # FIXME: enable -Wlogical-op and -Wduplicated-branches once #27594 is merged # Note: enable -Wimplicit-fallthrough for Clang (already part of -Wextra for GCC) # once we switch to C++11 or later (necessary for our FALLTHROUGH macro). - env.Append(CCFLAGS=['-Wall', '-Wextra', '-Wno-unused-parameter'] + all_plus_warnings + shadow_local_warning) + env.Append(CCFLAGS=['-Wall', '-Wextra', '-Wno-unused-parameter', + '-Wctor-dtor-privacy', '-Wnon-virtual-dtor'] + + all_plus_warnings + shadow_local_warning) if methods.using_gcc(env): - env['CCFLAGS'] += ['-Wno-clobbered'] + env['CCFLAGS'] += ['-Wno-clobbered', '-Walloc-zero', '-Wnoexcept', + '-Wduplicated-cond', '-Wplacement-new=1', '-Wstringop-overflow=4'] + version = methods.get_compiler_version(env) + if version != None and version[0] >= '9': + env['CCFLAGS'] += ['-Wattribute-alias=2'] elif (env["warnings"] == 'all'): env.Append(CCFLAGS=['-Wall'] + shadow_local_warning) elif (env["warnings"] == 'moderate'): diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index caf08c7379..f615cc8c65 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -210,6 +210,14 @@ void CameraMatrix::set_frustum(real_t p_left, real_t p_right, real_t p_bottom, r te[15] = 0; } +void CameraMatrix::set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov) { + if (!p_flip_fov) { + p_size *= p_aspect; + } + + set_frustum(-p_size / 2 + p_offset.x, +p_size / 2 + p_offset.x, -p_size / p_aspect / 2 + p_offset.y, +p_size / p_aspect / 2 + p_offset.y, p_near, p_far); +} + real_t CameraMatrix::get_z_far() const { const real_t *matrix = (const real_t *)this->matrix; diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h index 015588a8cb..3bcf48f5da 100644 --- a/core/math/camera_matrix.h +++ b/core/math/camera_matrix.h @@ -61,6 +61,7 @@ struct CameraMatrix { void set_orthogonal(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_znear, real_t p_zfar); void set_orthogonal(real_t p_size, real_t p_aspect, real_t p_znear, real_t p_zfar, bool p_flip_fov = false); void set_frustum(real_t p_left, real_t p_right, real_t p_bottom, real_t p_top, real_t p_near, real_t p_far); + void set_frustum(real_t p_size, real_t p_aspect, Vector2 p_offset, real_t p_near, real_t p_far, bool p_flip_fov = false); static real_t get_fovy(real_t p_fovx, real_t p_aspect) { diff --git a/core/ustring.cpp b/core/ustring.cpp index d60bd16921..78feddb229 100644 --- a/core/ustring.cpp +++ b/core/ustring.cpp @@ -123,6 +123,31 @@ const char *CharString::get_data() const { return ""; } +CharString &CharString::operator=(const char *p_cstr) { + + copy_from(p_cstr); + return *this; +} + +void CharString::copy_from(const char *p_cstr) { + + if (!p_cstr) { + resize(0); + return; + } + + size_t len = strlen(p_cstr); + + if (len == 0) { + resize(0); + return; + } + + resize(len + 1); // include terminating null char + + strcpy(ptrw(), p_cstr); +} + void String::copy_from(const char *p_cstr) { if (!p_cstr) { diff --git a/core/ustring.h b/core/ustring.h index 85103057df..e2e62874d6 100644 --- a/core/ustring.h +++ b/core/ustring.h @@ -101,12 +101,17 @@ public: _cowdata._ref(p_str._cowdata); return *this; } + _FORCE_INLINE_ CharString(const char *p_cstr) { copy_from(p_cstr); } + CharString &operator=(const char *p_cstr); bool operator<(const CharString &p_right) const; CharString &operator+=(char p_char); int length() const { return size() ? size() - 1 : 0; } const char *get_data() const; operator const char *() const { return get_data(); }; + +protected: + void copy_from(const char *p_cstr); }; typedef wchar_t CharType; diff --git a/core/variant_call.cpp b/core/variant_call.cpp index 143b07418e..f9f73b4e51 100644 --- a/core/variant_call.cpp +++ b/core/variant_call.cpp @@ -36,6 +36,7 @@ #include "core/object.h" #include "core/os/os.h" #include "core/script_language.h" +#include "thirdparty/misc/sha256.h" typedef void (*VariantFunc)(Variant &r_ret, Variant &p_self, const Variant **p_args); typedef void (*VariantConstructFunc)(Variant &r_ret, const Variant **p_args); @@ -587,6 +588,19 @@ struct _VariantCall { r_ret = decompressed; } + static void _call_PoolByteArray_sha256_string(Variant &r_ret, Variant &p_self, const Variant **p_args) { + PoolByteArray *ba = reinterpret_cast<PoolByteArray *>(p_self._data._mem); + PoolByteArray::Read r = ba->read(); + String s; + unsigned char hash[32]; + sha256_context sha256; + sha256_init(&sha256); + sha256_hash(&sha256, (unsigned char *)r.ptr(), ba->size()); + sha256_done(&sha256, hash); + s = String::hex_encode_buffer(hash, 32); + r_ret = s; + } + VCALL_LOCALMEM0R(PoolByteArray, size); VCALL_LOCALMEM2(PoolByteArray, set); VCALL_LOCALMEM1R(PoolByteArray, get); @@ -1733,6 +1747,7 @@ void register_variant_methods() { ADDFUNC0R(POOL_BYTE_ARRAY, STRING, PoolByteArray, get_string_from_ascii, varray()); ADDFUNC0R(POOL_BYTE_ARRAY, STRING, PoolByteArray, get_string_from_utf8, varray()); + ADDFUNC0R(POOL_BYTE_ARRAY, STRING, PoolByteArray, sha256_string, varray()); ADDFUNC1R(POOL_BYTE_ARRAY, POOL_BYTE_ARRAY, PoolByteArray, compress, INT, "compression_mode", varray(0)); ADDFUNC2R(POOL_BYTE_ARRAY, POOL_BYTE_ARRAY, PoolByteArray, decompress, INT, "buffer_size", INT, "compression_mode", varray(0)); diff --git a/doc/classes/DynamicFont.xml b/doc/classes/DynamicFont.xml index 99ba5143e3..5fdc1ebb75 100644 --- a/doc/classes/DynamicFont.xml +++ b/doc/classes/DynamicFont.xml @@ -5,6 +5,12 @@ </brief_description> <description> DynamicFont renders vector font files (such as TTF or OTF) dynamically at runtime instead of using a prerendered texture atlas like [BitmapFont]. This trades the faster loading time of [BitmapFont]s for the ability to change font parameters like size and spacing during runtime. [DynamicFontData] is used for referencing the font file paths. + [codeblock] + var dynamic_font = DynamicFont.new() + dynamic_font.font_data = load("res://BarlowCondensed-Bold.ttf") + dynamic_font.size = 64 + $"Label".set("custom_fonts/font", dynamic_font) + [/codeblock] </description> <tutorials> </tutorials> diff --git a/doc/classes/PoolByteArray.xml b/doc/classes/PoolByteArray.xml index 67419377e8..012a38a3ea 100644 --- a/doc/classes/PoolByteArray.xml +++ b/doc/classes/PoolByteArray.xml @@ -112,6 +112,11 @@ Change the byte at the given index. </description> </method> + <method name="sha256_string"> + <description> + Return SHA256 string of the PoolByteArray. + </description> + </method> <method name="size"> <return type="int"> </return> diff --git a/editor/icons/icon_bucket.svg b/editor/icons/icon_bucket.svg new file mode 100644 index 0000000000..4a5df39e93 --- /dev/null +++ b/editor/icons/icon_bucket.svg @@ -0,0 +1,86 @@ +<?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="svg6" + sodipodi:docname="icon_bucket.svg" + inkscape:version="0.92.2 2405546, 2018-03-11"> + <metadata + id="metadata12"> + <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="defs10" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1876" + inkscape:window-height="1574" + id="namedview8" + showgrid="true" + inkscape:snap-bbox="true" + inkscape:bbox-paths="false" + inkscape:bbox-nodes="false" + inkscape:snap-bbox-edge-midpoints="false" + inkscape:snap-bbox-midpoints="true" + inkscape:snap-intersection-paths="true" + inkscape:snap-smooth-nodes="true" + inkscape:snap-object-midpoints="true" + inkscape:snap-nodes="false" + inkscape:snap-others="false" + inkscape:zoom="16" + inkscape:cx="-4.3713942" + inkscape:cy="-1.9091903" + inkscape:window-x="4" + inkscape:window-y="20" + inkscape:window-maximized="0" + inkscape:current-layer="g4"> + <inkscape:grid + type="xygrid" + id="grid4524" /> + </sodipodi:namedview> + <g + transform="translate(0 -1036.4)" + id="g4"> + <g + id="g4576" + transform="matrix(0.53348552,0.53348552,-0.53348552,0.53348552,561.06065,484.40406)" + style="stroke-width:1.32544696"> + <path + id="path2" + transform="translate(0,1036.4)" + d="M 2,1 C 1.4477645,1.0001 1.0000523,1.4477 1,2 V 3.5 H 3.8847656 A 1.4999877,1.5 0 0 1 5,3 1.4999877,1.5 0 0 1 6.5,4.5 1.4999877,1.5 0 0 1 5,6 1.4999877,1.5 0 0 1 3.8847656,5.5 H 1 V 7 H -0.26953125 -2 c -0.5522769,0 -0.999989,-0.4477 -1,-1 1.1e-5,-0.5523 0.4477231,-1 1,-1 h 3 2.5878906 0.546875 A 1,1 0 0 0 5,5.5 1,1 0 0 0 6,4.5 1,1 0 0 0 5,3.5 1,1 0 0 0 4.1367188,4 H 3.5878906 1 -2 c -1.1045647,0 -1.9999933,0.8954285 -2,2 6.7e-6,1.1045715 0.8954353,2 2,2 h 3 v 6 c 7.35e-5,0.5523 0.4477232,0.9999 1,1 h 8 c 0.552235,-10e-5 0.999947,-0.4477 1,-1 V 1 Z" + style="color:#000000;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;text-orientation:mixed;dominant-baseline:auto;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000000;fill:#e0e0e0;stroke-width:1.32544696;color-rendering:auto;image-rendering:auto;shape-rendering:auto" + inkscape:connector-curvature="0" /> + <path + sodipodi:nodetypes="cczcc" + inkscape:connector-curvature="0" + id="path4526" + d="m 12,1038.4 c 0.707107,3.5356 0.707107,3.5356 1.414213,4.2427 0.707107,0.7071 2.121321,0.7071 2.828428,0 0.707106,-0.7071 0.707106,-2.1213 0,-2.8284 C 15.535534,1039.1071 15.535534,1039.1071 12,1038.4 Z" + style="fill:#e0e0e0;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.32544696px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /> + </g> + </g> +</svg> diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 4a4e7f25b8..1afd7df049 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -481,6 +481,17 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) if (edited_point.valid() && (wip_active || (mm->get_button_mask() & BUTTON_MASK_LEFT))) { Vector2 cpoint = _get_node()->get_global_transform().affine_inverse().xform(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint))); + + //Move the point in a single axis. Should only work when editing a polygon and while holding shift. + if (mode == MODE_EDIT && mm->get_shift()) { + Vector2 old_point = pre_move_edit.get(selected_point.vertex); + if (ABS(cpoint.x - old_point.x) > ABS(cpoint.y - old_point.y)) { + cpoint.y = old_point.y; + } else { + cpoint.x = old_point.x; + } + } + edited_point = PosVertex(edited_point, cpoint); if (!wip_active) { diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index cb3e5a8129..567c336eff 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -876,7 +876,7 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { float len = MAX(0.0001, playback->get_current_length()); - float pos = CLAMP(playback->get_current_play_pos(), 0, len); + float pos = CLAMP(play_pos, 0, len); float c = pos / len; Color fg = get_color("font_color", "Label"); Color bg = fg; @@ -1011,7 +1011,7 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { bool is_playing = false; StringName current_node; StringName blend_from_node; - float play_pos = 0; + play_pos = 0; if (playback.is_valid()) { tp = playback->get_travel_path(); @@ -1046,6 +1046,25 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { state_machine_play_pos->update(); } + { + if (current_node != StringName() && state_machine->has_node(current_node)) { + + String next = current_node; + Ref<AnimationNodeStateMachine> anodesm = state_machine->get_node(next); + Ref<AnimationNodeStateMachinePlayback> current_node_playback; + + while (anodesm.is_valid()) { + current_node_playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + next + "/playback"); + next += "/" + current_node_playback->get_current_node(); + anodesm = anodesm->get_node(current_node_playback->get_current_node()); + } + + // when current_node is a state machine, use playback of current_node to set play_pos + if (current_node_playback.is_valid()) + play_pos = current_node_playback->get_current_play_pos(); + } + } + if (last_play_pos != play_pos) { last_play_pos = play_pos; diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index 1c4c06090a..135aff9027 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -160,6 +160,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { StringName last_current_node; Vector<StringName> last_travel_path; float last_play_pos; + float play_pos; float error_time; String error_text; diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index e9b9c03c1e..0bbffc8a0b 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -36,7 +36,7 @@ void ThemeEditor::edit(const Ref<Theme> &p_theme) { theme = p_theme; - main_vb->set_theme(p_theme); + main_container->set_theme(p_theme); } void ThemeEditor::_propagate_redraw(Control *p_at) { @@ -53,7 +53,7 @@ void ThemeEditor::_propagate_redraw(Control *p_at) { void ThemeEditor::_refresh_interval() { - _propagate_redraw(main_vb); + _propagate_redraw(main_container); } void ThemeEditor::_type_menu_cbk(int p_option) { @@ -574,7 +574,6 @@ void ThemeEditor::_theme_menu_cbk(int p_option) { } } - //types.sort(); types.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *E = types.front(); E; E = E->next()) { @@ -610,30 +609,14 @@ ThemeEditor::ThemeEditor() { time_left = 0; - scroll = memnew(ScrollContainer); - add_child(scroll); - scroll->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 3); - scroll->set_margin(MARGIN_TOP, 30 * EDSCALE); - //scroll->set_enable_h_scroll(true); - scroll->set_enable_v_scroll(true); - scroll->set_enable_h_scroll(false); - - Panel *panel = memnew(Panel); - scroll->add_child(panel); - panel->set_custom_minimum_size(Size2(500, 800) * EDSCALE); - panel->set_theme(Theme::get_default()); - panel->set_h_size_flags(SIZE_EXPAND_FILL); - - main_vb = memnew(VBoxContainer); - panel->add_child(main_vb); - main_vb->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 4 * EDSCALE); + HBoxContainer *top_menu = memnew(HBoxContainer); + add_child(top_menu); - HBoxContainer *hb_menu = memnew(HBoxContainer); - main_vb->add_child(hb_menu); + top_menu->add_child(memnew(Label(TTR("Preview:")))); + top_menu->add_spacer(false); theme_menu = memnew(MenuButton); - theme_menu->set_text(TTR("Edit theme...")); - theme_menu->set_flat(false); + theme_menu->set_text(TTR("Edit Theme")); theme_menu->set_tooltip(TTR("Theme editing menu.")); theme_menu->get_popup()->add_item(TTR("Add Item"), POPUP_ADD); theme_menu->get_popup()->add_item(TTR("Add Class Items"), POPUP_CLASS_ADD); @@ -643,51 +626,73 @@ ThemeEditor::ThemeEditor() { theme_menu->get_popup()->add_item(TTR("Create Empty Template"), POPUP_CREATE_EMPTY); theme_menu->get_popup()->add_item(TTR("Create Empty Editor Template"), POPUP_CREATE_EDITOR_EMPTY); theme_menu->get_popup()->add_item(TTR("Create From Current Editor Theme"), POPUP_IMPORT_EDITOR_THEME); - add_child(theme_menu); - theme_menu->set_position(Vector2(3, 3) * EDSCALE); + top_menu->add_child(theme_menu); theme_menu->get_popup()->connect("id_pressed", this, "_theme_menu_cbk"); + scroll = memnew(ScrollContainer); + add_child(scroll); + scroll->set_enable_v_scroll(true); + scroll->set_enable_h_scroll(false); + scroll->set_v_size_flags(SIZE_EXPAND_FILL); + + main_container = memnew(MarginContainer); + scroll->add_child(main_container); + main_container->set_clip_contents(true); + main_container->set_custom_minimum_size(Size2(700, 0) * EDSCALE); + main_container->set_v_size_flags(SIZE_EXPAND_FILL); + main_container->set_h_size_flags(SIZE_EXPAND_FILL); + + //// Preview Controls //// + + Panel *panel = memnew(Panel); + main_container->add_child(panel); + panel->set_theme(Theme::get_default()); + + MarginContainer *mc = memnew(MarginContainer); + main_container->add_child(mc); + mc->set_theme(Theme::get_default()); + mc->add_constant_override("margin_right", 4 * EDSCALE); + mc->add_constant_override("margin_top", 4 * EDSCALE); + mc->add_constant_override("margin_left", 4 * EDSCALE); + mc->add_constant_override("margin_bottom", 4 * EDSCALE); + HBoxContainer *main_hb = memnew(HBoxContainer); - main_vb->add_child(main_hb); + mc->add_child(main_hb); VBoxContainer *first_vb = memnew(VBoxContainer); - first_vb->set_h_size_flags(SIZE_EXPAND_FILL); main_hb->add_child(first_vb); - - //main_panel->add_child(panel); - //panel->set_anchors_and_margins_preset(Control::PRESET_WIDE); - //panel->set_margin( MARGIN_TOP,20 ); + first_vb->set_h_size_flags(SIZE_EXPAND_FILL); + first_vb->add_constant_override("separation", 10 * EDSCALE); first_vb->add_child(memnew(Label("Label"))); first_vb->add_child(memnew(Button("Button"))); + Button *bt = memnew(Button); + bt->set_text(TTR("Toggle Button")); + bt->set_toggle_mode(true); + bt->set_pressed(true); + first_vb->add_child(bt); + bt = memnew(Button); + bt->set_text(TTR("Disabled Button")); + bt->set_disabled(true); + first_vb->add_child(bt); ToolButton *tb = memnew(ToolButton); tb->set_text("ToolButton"); first_vb->add_child(tb); + CheckButton *cb = memnew(CheckButton); cb->set_text("CheckButton"); first_vb->add_child(cb); + cb = memnew(CheckButton); CheckBox *cbx = memnew(CheckBox); cbx->set_text("CheckBox"); first_vb->add_child(cbx); - VBoxContainer *bg = memnew(VBoxContainer); - bg->set_v_size_flags(SIZE_EXPAND_FILL); - VBoxContainer *gbvb = memnew(VBoxContainer); - gbvb->set_v_size_flags(SIZE_EXPAND_FILL); - CheckBox *rbx1 = memnew(CheckBox); - rbx1->set_text(TTR("CheckBox Radio1")); - rbx1->set_pressed(true); - gbvb->add_child(rbx1); - CheckBox *rbx2 = memnew(CheckBox); - rbx2->set_text(TTR("CheckBox Radio2")); - gbvb->add_child(rbx2); - bg->add_child(gbvb); - first_vb->add_child(bg); - MenuButton *test_menu_button = memnew(MenuButton); test_menu_button->set_text("MenuButton"); test_menu_button->get_popup()->add_item(TTR("Item")); + test_menu_button->get_popup()->add_item(TTR("Disabled Item")); + test_menu_button->get_popup()->set_item_disabled(1, true); 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")); @@ -696,6 +701,14 @@ ThemeEditor::ThemeEditor() { 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(6, true); + test_menu_button->get_popup()->add_separator(TTR("Named Sep.")); + + PopupMenu *test_submenu = memnew(PopupMenu); + test_menu_button->get_popup()->add_child(test_submenu); + test_submenu->set_name("submenu"); + test_menu_button->get_popup()->add_submenu_item(TTR("Submenu"), "submenu"); + test_submenu->add_item(TTR("Item 1")); + test_submenu->add_item(TTR("Item 2")); first_vb->add_child(test_menu_button); OptionButton *test_option_button = memnew(OptionButton); @@ -705,21 +718,7 @@ ThemeEditor::ThemeEditor() { test_option_button->add_item(TTR("Many")); test_option_button->add_item(TTR("Options")); first_vb->add_child(test_option_button); - - ColorPickerButton *cpb = memnew(ColorPickerButton); - first_vb->add_child(cpb); - - first_vb->add_child(memnew(HSeparator)); - first_vb->add_child(memnew(HSlider)); - first_vb->add_child(memnew(HScrollBar)); - first_vb->add_child(memnew(SpinBox)); - ProgressBar *pb = memnew(ProgressBar); - pb->set_value(50); - first_vb->add_child(pb); - Panel *pn = memnew(Panel); - pn->set_custom_minimum_size(Size2(40, 40) * EDSCALE); - first_vb->add_child(pn); - first_vb->add_constant_override("separation", 10 * EDSCALE); + first_vb->add_child(memnew(ColorPickerButton)); VBoxContainer *second_vb = memnew(VBoxContainer); second_vb->set_h_size_flags(SIZE_EXPAND_FILL); @@ -728,50 +727,48 @@ ThemeEditor::ThemeEditor() { LineEdit *le = memnew(LineEdit); le->set_text("LineEdit"); second_vb->add_child(le); + le = memnew(LineEdit); + le->set_text(TTR("Disabled LineEdit")); + le->set_editable(false); + second_vb->add_child(le); TextEdit *te = memnew(TextEdit); te->set_text("TextEdit"); - //te->set_v_size_flags(SIZE_EXPAND_FILL); - te->set_custom_minimum_size(Size2(0, 160) * EDSCALE); + te->set_custom_minimum_size(Size2(0, 100) * EDSCALE); second_vb->add_child(te); + second_vb->add_child(memnew(SpinBox)); - Tree *test_tree = memnew(Tree); - second_vb->add_child(test_tree); - test_tree->set_custom_minimum_size(Size2(0, 160) * EDSCALE); - - TreeItem *item = test_tree->create_item(); - item->set_editable(0, true); - item->set_text(0, "Tree"); - item = test_tree->create_item(test_tree->get_root()); - item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - item->set_editable(0, true); - item->set_text(0, "Check"); - item = test_tree->create_item(test_tree->get_root()); - item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); - item->set_editable(0, true); - item->set_range_config(0, 0, 20, 0.1); - item->set_range(0, 2); - item = test_tree->create_item(test_tree->get_root()); - item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); - item->set_editable(0, true); - item->set_text(0, TTR("Has,Many,Options")); - item->set_range(0, 2); + HBoxContainer *vhb = memnew(HBoxContainer); + second_vb->add_child(vhb); + vhb->set_custom_minimum_size(Size2(0, 100) * EDSCALE); + vhb->add_child(memnew(VSlider)); + VScrollBar *vsb = memnew(VScrollBar); + vsb->set_page(25); + vhb->add_child(vsb); + vhb->add_child(memnew(VSeparator)); + VBoxContainer *hvb = memnew(VBoxContainer); + vhb->add_child(hvb); + hvb->set_alignment(ALIGN_CENTER); + hvb->set_h_size_flags(SIZE_EXPAND_FILL); + hvb->add_child(memnew(HSlider)); + HScrollBar *hsb = memnew(HScrollBar); + hsb->set_page(25); + hvb->add_child(hsb); + HSlider *hs = memnew(HSlider); + hs->set_editable(false); + hvb->add_child(hs); + hvb->add_child(memnew(HSeparator)); + ProgressBar *pb = memnew(ProgressBar); + pb->set_value(50); + hvb->add_child(pb); VBoxContainer *third_vb = memnew(VBoxContainer); third_vb->set_h_size_flags(SIZE_EXPAND_FILL); - third_vb->add_constant_override("separation", 10); - + third_vb->add_constant_override("separation", 10 * EDSCALE); main_hb->add_child(third_vb); - HBoxContainer *vhb = memnew(HBoxContainer); - vhb->set_custom_minimum_size(Size2(0, 160) * EDSCALE); - vhb->add_child(memnew(VSeparator)); - vhb->add_child(memnew(VSlider)); - vhb->add_child(memnew(VScrollBar)); - third_vb->add_child(vhb); - TabContainer *tc = memnew(TabContainer); third_vb->add_child(tc); - tc->set_custom_minimum_size(Size2(0, 160) * EDSCALE); + tc->set_custom_minimum_size(Size2(0, 135) * EDSCALE); Control *tcc = memnew(Control); tcc->set_name(TTR("Tab 1")); tc->add_child(tcc); @@ -781,9 +778,41 @@ ThemeEditor::ThemeEditor() { tcc = memnew(Control); tcc->set_name(TTR("Tab 3")); tc->add_child(tcc); + tc->set_tab_disabled(2, true); + + Tree *test_tree = memnew(Tree); + third_vb->add_child(test_tree); + test_tree->set_custom_minimum_size(Size2(0, 175) * EDSCALE); + test_tree->add_constant_override("draw_relationship_lines", 1); + + TreeItem *item = test_tree->create_item(); + item->set_text(0, "Tree"); + item = test_tree->create_item(test_tree->get_root()); + item->set_text(0, "Item"); + item = test_tree->create_item(test_tree->get_root()); + item->set_editable(0, true); + item->set_text(0, TTR("Editable Item")); + TreeItem *sub_tree = test_tree->create_item(test_tree->get_root()); + sub_tree->set_text(0, TTR("Subtree")); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_editable(0, true); + item->set_text(0, "Check Item"); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0, true); + item->set_range_config(0, 0, 20, 0.1); + item->set_range(0, 2); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0, true); + item->set_text(0, TTR("Has,Many,Options")); + item->set_range(0, 2); main_hb->add_constant_override("separation", 20 * EDSCALE); + //////// + add_del_dialog = memnew(ConfirmationDialog); add_del_dialog->hide(); add_child(add_del_dialog); @@ -844,9 +873,6 @@ ThemeEditor::ThemeEditor() { file_dialog->add_filter("*.theme ; Theme File"); add_child(file_dialog); file_dialog->connect("file_selected", this, "_save_template_cbk"); - - //MenuButton *name_menu; - //LineEdit *name_edit; } void ThemeEditorPlugin::edit(Object *p_node) { @@ -886,7 +912,6 @@ ThemeEditorPlugin::ThemeEditorPlugin(EditorNode *p_node) { theme_editor = memnew(ThemeEditor); theme_editor->set_custom_minimum_size(Size2(0, 200)); - //p_node->get_viewport()->add_child(theme_editor); button = editor->add_bottom_panel_item(TTR("Theme"), theme_editor); button->hide(); } diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 352988d69e..cc236907a9 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -33,6 +33,7 @@ #include "scene/gui/check_box.h" #include "scene/gui/file_dialog.h" +#include "scene/gui/margin_container.h" #include "scene/gui/option_button.h" #include "scene/gui/scroll_container.h" #include "scene/gui/texture_rect.h" @@ -40,12 +41,12 @@ #include "editor/editor_node.h" -class ThemeEditor : public Control { +class ThemeEditor : public VBoxContainer { - GDCLASS(ThemeEditor, Control); + GDCLASS(ThemeEditor, VBoxContainer); ScrollContainer *scroll; - VBoxContainer *main_vb; + MarginContainer *main_container; Ref<Theme> theme; EditorFileDialog *file_dialog; diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 33e4bb2336..467b83f4f8 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -66,6 +66,11 @@ void TileMapEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { + paint_button->set_icon(get_icon("Edit", "EditorIcons")); + bucket_fill_button->set_icon(get_icon("Bucket", "EditorIcons")); + picker_button->set_icon(get_icon("ColorPick", "EditorIcons")); + select_button->set_icon(get_icon("ActionCopy", "EditorIcons")); + rotate_left_button->set_icon(get_icon("Rotate270", "EditorIcons")); rotate_right_button->set_icon(get_icon("Rotate90", "EditorIcons")); flip_horizontal_button->set_icon(get_icon("MirrorX", "EditorIcons")); @@ -76,9 +81,6 @@ void TileMapEditor::_notification(int p_what) { search_box->set_clear_button_enabled(true); PopupMenu *p = options->get_popup(); - p->set_item_icon(p->get_item_index(OPTION_PAINTING), get_icon("Edit", "EditorIcons")); - p->set_item_icon(p->get_item_index(OPTION_PICK_TILE), get_icon("ColorPick", "EditorIcons")); - p->set_item_icon(p->get_item_index(OPTION_SELECT), get_icon("ActionCopy", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_CUT), get_icon("ActionCut", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_COPY), get_icon("Duplicate", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_ERASE_SELECTION), get_icon("Remove", "EditorIcons")); @@ -87,37 +89,51 @@ void TileMapEditor::_notification(int p_what) { } } -void TileMapEditor::_menu_option(int p_option) { +void TileMapEditor::_update_button_tool() { - switch (p_option) { - - case OPTION_PAINTING: { - // NOTE: We do not set tool = TOOL_PAINTING as this begins painting - // immediately without pressing the left mouse button first - tool = TOOL_NONE; - - CanvasItemEditor::get_singleton()->update_viewport(); + ToolButton *tb[4] = { paint_button, bucket_fill_button, picker_button, select_button }; + // Unpress all buttons + for (int i = 0; i < 4; i++) { + tb[i]->set_pressed(false); + } + // Press the good button + switch (tool) { + case TOOL_NONE: + case TOOL_PAINTING: { + paint_button->set_pressed(true); } break; - case OPTION_BUCKET: { - - tool = TOOL_BUCKET; - - CanvasItemEditor::get_singleton()->update_viewport(); + case TOOL_BUCKET: { + bucket_fill_button->set_pressed(true); } break; - case OPTION_PICK_TILE: { + case TOOL_PICKING: { + picker_button->set_pressed(true); + } break; + case TOOL_SELECTING: { + select_button->set_pressed(true); + } break; + default: + break; + } +} - tool = TOOL_PICKING; +void TileMapEditor::_button_tool_select(int p_tool) { + tool = (Tool)p_tool; + _update_button_tool(); + switch (tool) { + case TOOL_SELECTING: { - CanvasItemEditor::get_singleton()->update_viewport(); + selection_active = false; } break; - case OPTION_SELECT: { + default: + break; + } + CanvasItemEditor::get_singleton()->update_viewport(); +} - tool = TOOL_SELECTING; - selection_active = false; +void TileMapEditor::_menu_option(int p_option) { - CanvasItemEditor::get_singleton()->update_viewport(); - } break; + switch (p_option) { case OPTION_COPY: { _update_copydata(); @@ -168,6 +184,7 @@ void TileMapEditor::_menu_option(int p_option) { } } break; } + _update_button_tool(); } void TileMapEditor::_palette_selected(int index) { @@ -944,6 +961,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { selection_active = false; rectangle_begin = over_tile; + _update_button_tool(); return true; } #ifdef APPLE_STYLE_KEYS @@ -953,11 +971,13 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { #endif tool = TOOL_PICKING; _pick_tile(over_tile); + _update_button_tool(); return true; } tool = TOOL_PAINTING; + _update_button_tool(); } if (tool == TOOL_PAINTING) { @@ -979,6 +999,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { rectangle_begin = over_tile; } + _update_button_tool(); return true; } else { @@ -1075,6 +1096,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { } tool = TOOL_NONE; + _update_button_tool(); return true; } @@ -1090,6 +1112,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } @@ -1100,6 +1123,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } @@ -1130,6 +1154,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { _set_cell(local, invalid_cell); } + _update_button_tool(); return true; } @@ -1144,6 +1169,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_NONE; + _update_button_tool(); return true; } else if (tool == TOOL_BUCKET) { @@ -1329,6 +1355,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } @@ -1343,17 +1370,20 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_NONE; CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/bucket_fill", p_event)) { tool = TOOL_BUCKET; CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/erase_selection", p_event)) { _menu_option(OPTION_ERASE_SELECTION); + _update_button_tool(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/select", p_event)) { @@ -1362,6 +1392,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/copy_selection", p_event)) { @@ -1372,6 +1403,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } } @@ -1388,6 +1420,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_PASTING; CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } } @@ -1700,6 +1733,7 @@ void TileMapEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_text_entered"), &TileMapEditor::_text_entered); ClassDB::bind_method(D_METHOD("_text_changed"), &TileMapEditor::_text_changed); ClassDB::bind_method(D_METHOD("_sbox_input"), &TileMapEditor::_sbox_input); + ClassDB::bind_method(D_METHOD("_button_tool_select"), &TileMapEditor::_button_tool_select); ClassDB::bind_method(D_METHOD("_menu_option"), &TileMapEditor::_menu_option); ClassDB::bind_method(D_METHOD("_canvas_mouse_enter"), &TileMapEditor::_canvas_mouse_enter); ClassDB::bind_method(D_METHOD("_canvas_mouse_exit"), &TileMapEditor::_canvas_mouse_exit); @@ -1880,37 +1914,64 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { // Add menu items toolbar = memnew(HBoxContainer); - toolbar->set_h_size_flags(SIZE_EXPAND_FILL); - toolbar->set_alignment(BoxContainer::ALIGN_END); toolbar->hide(); CanvasItemEditor::get_singleton()->add_control_to_menu_panel(toolbar); + // Separator + toolbar->add_child(memnew(VSeparator)); + + // Tools + paint_button = memnew(ToolButton); + paint_button->set_shortcut(ED_SHORTCUT("tile_map_editor/paint_tile", TTR("Paint Tile"), KEY_P)); + paint_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_NONE)); + paint_button->set_toggle_mode(true); + toolbar->add_child(paint_button); + + bucket_fill_button = memnew(ToolButton); + bucket_fill_button->set_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_G)); + bucket_fill_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_BUCKET)); + bucket_fill_button->set_toggle_mode(true); + toolbar->add_child(bucket_fill_button); + + picker_button = memnew(ToolButton); + picker_button->set_shortcut(ED_SHORTCUT("tile_map_editor/pîck_tile", TTR("Pick Tile"), KEY_CONTROL)); + picker_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_PICKING)); + picker_button->set_toggle_mode(true); + toolbar->add_child(picker_button); + + select_button = memnew(ToolButton); + select_button->set_shortcut(ED_SHORTCUT("tile_map_editor/pîck_tile", TTR("Select"), KEY_MASK_CMD + KEY_B)); + select_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_SELECTING)); + select_button->set_toggle_mode(true); + toolbar->add_child(select_button); + + _update_button_tool(); + + // Container to the right of the toolbar + toolbar_right = memnew(HBoxContainer); + toolbar_right->set_h_size_flags(SIZE_EXPAND_FILL); + toolbar_right->set_alignment(BoxContainer::ALIGN_END); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(toolbar_right); + // Tile position tile_info = memnew(Label); - toolbar->add_child(tile_info); + toolbar_right->add_child(tile_info); + // Menu options = memnew(MenuButton); options->set_text("TileMap"); options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("TileMap", "EditorIcons")); options->set_process_unhandled_key_input(false); + toolbar_right->add_child(options); PopupMenu *p = options->get_popup(); - - p->add_shortcut(ED_SHORTCUT("tile_map_editor/paint_tile", TTR("Paint Tile"), KEY_P), OPTION_PAINTING); - p->add_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_G), OPTION_BUCKET); - p->add_separator(); - p->add_item(TTR("Pick Tile"), OPTION_PICK_TILE, KEY_CONTROL); - p->add_separator(); - p->add_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD + KEY_B), OPTION_SELECT); p->add_shortcut(ED_SHORTCUT("tile_map_editor/cut_selection", TTR("Cut Selection"), KEY_MASK_CMD + KEY_X), OPTION_CUT); p->add_shortcut(ED_SHORTCUT("tile_map_editor/copy_selection", TTR("Copy Selection"), KEY_MASK_CMD + KEY_C), OPTION_COPY); p->add_shortcut(ED_GET_SHORTCUT("tile_map_editor/erase_selection"), OPTION_ERASE_SELECTION); p->add_separator(); p->add_item(TTR("Fix Invalid Tiles"), OPTION_FIX_INVALID); - p->connect("id_pressed", this, "_menu_option"); - toolbar->add_child(options); rotate_left_button = memnew(ToolButton); rotate_left_button->set_tooltip(TTR("Rotate left")); rotate_left_button->set_focus_mode(FOCUS_NONE); diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index b30426eabe..296e861949 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -66,12 +66,8 @@ class TileMapEditor : public VBoxContainer { enum Options { - OPTION_BUCKET, - OPTION_PICK_TILE, - OPTION_SELECT, OPTION_COPY, OPTION_ERASE_SELECTION, - OPTION_PAINTING, OPTION_FIX_INVALID, OPTION_CUT }; @@ -90,10 +86,16 @@ class TileMapEditor : public VBoxContainer { ItemList *manual_palette; HBoxContainer *toolbar; + HBoxContainer *toolbar_right; Label *tile_info; MenuButton *options; + ToolButton *paint_button; + ToolButton *bucket_fill_button; + ToolButton *picker_button; + ToolButton *select_button; + ToolButton *flip_horizontal_button; ToolButton *flip_vertical_button; ToolButton *rotate_left_button; @@ -184,6 +186,8 @@ class TileMapEditor : public VBoxContainer { void _text_changed(const String &p_text); void _sbox_input(const Ref<InputEvent> &p_ie); void _update_palette(); + void _update_button_tool(); + void _button_tool_select(int p_tool); void _menu_option(int p_option); void _palette_selected(int index); void _palette_multi_selected(int index, bool selected); diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index 6f37919737..f540b386aa 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -1309,6 +1309,28 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { Ref<Material> material = get_material("camera_material", p_gizmo); Ref<Material> icon = get_material("camera_icon", p_gizmo); +#define ADD_TRIANGLE(m_a, m_b, m_c) \ + { \ + lines.push_back(m_a); \ + lines.push_back(m_b); \ + lines.push_back(m_b); \ + lines.push_back(m_c); \ + lines.push_back(m_c); \ + lines.push_back(m_a); \ + } + +#define ADD_QUAD(m_a, m_b, m_c, m_d) \ + { \ + lines.push_back(m_a); \ + lines.push_back(m_b); \ + lines.push_back(m_b); \ + lines.push_back(m_c); \ + lines.push_back(m_c); \ + lines.push_back(m_d); \ + lines.push_back(m_d); \ + lines.push_back(m_a); \ + } + switch (camera->get_projection()) { case Camera::PROJECTION_PERSPECTIVE: { @@ -1321,16 +1343,6 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { nside.x = -nside.x; Vector3 up = Vector3(0, side.x, 0); -#define ADD_TRIANGLE(m_a, m_b, m_c) \ - { \ - lines.push_back(m_a); \ - lines.push_back(m_b); \ - lines.push_back(m_b); \ - lines.push_back(m_c); \ - lines.push_back(m_c); \ - lines.push_back(m_a); \ - } - ADD_TRIANGLE(Vector3(), side + up, side - up); ADD_TRIANGLE(Vector3(), nside + up, nside - up); ADD_TRIANGLE(Vector3(), side + up, nside + up); @@ -1345,17 +1357,6 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { } break; case Camera::PROJECTION_ORTHOGONAL: { -#define ADD_QUAD(m_a, m_b, m_c, m_d) \ - { \ - lines.push_back(m_a); \ - lines.push_back(m_b); \ - lines.push_back(m_b); \ - lines.push_back(m_c); \ - lines.push_back(m_c); \ - lines.push_back(m_d); \ - lines.push_back(m_d); \ - lines.push_back(m_a); \ - } float size = camera->get_size(); float hsize = size * 0.5; @@ -1368,6 +1369,7 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { ADD_QUAD(-up - right + back, -up + right + back, up + right + back, up - right + back); ADD_QUAD(up + right, up + right + back, up - right + back, up - right); ADD_QUAD(-up + right, -up + right + back, -up - right + back, -up - right); + handles.push_back(right + back); right.x *= 0.25; @@ -1375,8 +1377,30 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { ADD_TRIANGLE(tup, right + up + back, -right + up + back); } break; + case Camera::PROJECTION_FRUSTUM: { + float hsize = camera->get_size() / 2.0; + + Vector3 side = Vector3(hsize, 0, -camera->get_znear()).normalized(); + Vector3 nside = side; + nside.x = -nside.x; + Vector3 up = Vector3(0, side.x, 0); + Vector3 offset = Vector3(camera->get_frustum_offset().x, camera->get_frustum_offset().y, 0.0); + + ADD_TRIANGLE(Vector3(), side + up + offset, side - up + offset); + ADD_TRIANGLE(Vector3(), nside + up + offset, nside - up + offset); + ADD_TRIANGLE(Vector3(), side + up + offset, nside + up + offset); + ADD_TRIANGLE(Vector3(), side - up + offset, nside - up + offset); + + side.x *= 0.25; + nside.x *= 0.25; + Vector3 tup(0, up.y * 3 / 2, side.z); + ADD_TRIANGLE(tup + offset, side + up + offset, nside + up + offset); + } } +#undef ADD_TRIANGLE +#undef ADD_QUAD + p_gizmo->add_lines(lines, material); p_gizmo->add_unscaled_billboard(icon, 0.05); p_gizmo->add_handles(handles, get_material("handles")); diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index a8172c7f52..509b30c7e6 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -46,11 +46,11 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, size_t height = (size_t)p_header.bmp_info_header.bmp_height; size_t bits_per_pixel = (size_t)p_header.bmp_info_header.bmp_bit_count; - if (p_header.bmp_info_header.bmp_compression != 0) { + if (p_header.bmp_info_header.bmp_compression != BI_RGB) { err = FAILED; } - if (!(bits_per_pixel == 24 || bits_per_pixel == 32)) { + if (!(bits_per_pixel == 8 || bits_per_pixel == 24 || bits_per_pixel == 32)) { err = FAILED; } @@ -67,11 +67,26 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image, PoolVector<uint8_t>::Write image_data_w = image_data.write(); uint8_t *write_buffer = image_data_w.ptr(); + const uint32_t color_index_max = p_header.bmp_info_header.bmp_colors_used - 1; const uint8_t *line = p_buffer + (line_width * (height - 1)); for (unsigned int i = 0; i < height; i++) { const uint8_t *line_ptr = line; for (unsigned int j = 0; j < width; j++) { switch (bits_per_pixel) { + case 8: { + uint8_t color_index = CLAMP(*line_ptr, 0, color_index_max); + uint32_t color = 0x000000; + + if (p_color_buffer != NULL) + color = ((uint32_t *)p_color_buffer)[color_index]; + + write_buffer[index + 2] = color & 0xff; + write_buffer[index + 1] = (color >> 8) & 0xff; + write_buffer[index + 0] = (color >> 16) & 0xff; + write_buffer[index + 3] = 0xff; + index += 4; + line_ptr += 1; + } break; case 24: { uint32_t color = *((uint32_t *)line_ptr); @@ -108,7 +123,12 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, bmp_header_s bmp_header; Error err = ERR_INVALID_DATA; - if (f->get_len() > sizeof(bmp_header)) { + static const size_t FILE_HEADER_SIZE = 14; + static const size_t INFO_HEADER_SIZE = 40; + + // A valid bmp file should always at least have a + // file header and a minimal info header + if (f->get_len() > FILE_HEADER_SIZE + INFO_HEADER_SIZE) { // File Header bmp_header.bmp_file_header.bmp_signature = f->get_16(); if (bmp_header.bmp_file_header.bmp_signature == BITMAP_SIGNATURE) { @@ -129,35 +149,27 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f, bmp_header.bmp_info_header.bmp_colors_used = f->get_32(); bmp_header.bmp_info_header.bmp_important_colors = f->get_32(); - bmp_header.bmp_info_header.bmp_red_mask = f->get_32(); - bmp_header.bmp_info_header.bmp_green_mask = f->get_32(); - bmp_header.bmp_info_header.bmp_blue_mask = f->get_32(); - bmp_header.bmp_info_header.bmp_alpha_mask = f->get_32(); - bmp_header.bmp_info_header.bmp_cs_type = f->get_32(); - for (int i = 0; i < 9; i++) - bmp_header.bmp_info_header.bmp_endpoints[i] = f->get_32(); - - bmp_header.bmp_info_header.bmp_gamma_red = f->get_32(); - bmp_header.bmp_info_header.bmp_gamma_green = f->get_32(); - bmp_header.bmp_info_header.bmp_gamma_blue = f->get_32(); + // Compressed bitmaps not supported, stop parsing + if (bmp_header.bmp_info_header.bmp_compression != BI_RGB) { + ERR_EXPLAIN("Unsupported bmp file: " + f->get_path()); + f->close(); + ERR_FAIL_V(err) + } - f->seek(sizeof(bmp_header.bmp_file_header) + + f->seek(FILE_HEADER_SIZE + bmp_header.bmp_info_header.bmp_header_size); - uint32_t color_table_size = 0; - if (bmp_header.bmp_info_header.bmp_bit_count == 1) - color_table_size = 2; - else if (bmp_header.bmp_info_header.bmp_bit_count == 4) - color_table_size = 16; - else if (bmp_header.bmp_info_header.bmp_bit_count == 8) - color_table_size = 256; + if (bmp_header.bmp_info_header.bmp_bit_count < 16 && bmp_header.bmp_info_header.bmp_colors_used == 0) + bmp_header.bmp_info_header.bmp_colors_used = 1 << bmp_header.bmp_info_header.bmp_bit_count; + + // Color table is usually 4 bytes per color -> [B][G][R][0] + uint32_t color_table_size = bmp_header.bmp_info_header.bmp_colors_used * 4; PoolVector<uint8_t> bmp_color_table; if (color_table_size > 0) { - err = bmp_color_table.resize(color_table_size * 4); + err = bmp_color_table.resize(color_table_size); PoolVector<uint8_t>::Write bmp_color_table_w = bmp_color_table.write(); - f->get_buffer(bmp_color_table_w.ptr(), - bmp_header.bmp_info_header.bmp_colors_used * 4); + f->get_buffer(bmp_color_table_w.ptr(), color_table_size); } f->seek(bmp_header.bmp_file_header.bmp_file_offset); diff --git a/modules/bmp/image_loader_bmp.h b/modules/bmp/image_loader_bmp.h index d6899061d0..b27a47d402 100644 --- a/modules/bmp/image_loader_bmp.h +++ b/modules/bmp/image_loader_bmp.h @@ -37,6 +37,19 @@ class ImageLoaderBMP : public ImageFormatLoader { protected: static const unsigned BITMAP_SIGNATURE = 0x4d42; + enum bmp_compression_s { + BI_RGB = 0x00, + BI_RLE8 = 0x01, + BI_RLE4 = 0x02, + BI_BITFIELDS = 0x03, + BI_JPEG = 0x04, + BI_PNG = 0x05, + BI_ALPHABITFIELDS = 0x06, + BI_CMYK = 0x0b, + BI_CMYKRLE8 = 0x0c, + BI_CMYKRLE4 = 0x0d + }; + struct bmp_header_s { struct bmp_file_header_s { uint16_t bmp_signature; @@ -57,15 +70,6 @@ protected: uint32_t bmp_pixels_per_meter_y; uint32_t bmp_colors_used; uint32_t bmp_important_colors; - uint32_t bmp_red_mask; - uint32_t bmp_green_mask; - uint32_t bmp_blue_mask; - uint32_t bmp_alpha_mask; - uint32_t bmp_cs_type; - uint32_t bmp_endpoints[9]; - uint32_t bmp_gamma_red; - uint32_t bmp_gamma_green; - uint32_t bmp_gamma_blue; } bmp_info_header; }; diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index 22f2214898..e5f70a0b34 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -597,6 +597,8 @@ void RigidBodyBullet::set_state(PhysicsServer::BodyState p_state, const Variant if (!can_sleep) { // Can't sleep btBody->forceActivationState(DISABLE_DEACTIVATION); + } else { + btBody->forceActivationState(ACTIVE_TAG); } break; } diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp index b590d63167..f15bcec914 100644 --- a/modules/bullet/shape_bullet.cpp +++ b/modules/bullet/shape_bullet.cpp @@ -148,7 +148,13 @@ btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<rea const bool flipQuadEdges = false; const void *heightsPtr = p_heights.read().ptr(); - return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, p_min_height, p_max_height, YAxis, PHY_FLOAT, flipQuadEdges)); + btHeightfieldTerrainShape *heightfield = bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, p_min_height, p_max_height, YAxis, PHY_FLOAT, flipQuadEdges)); + + // The shape can be created without params when you do PhysicsServer.shape_create(PhysicsServer.SHAPE_HEIGHTMAP) + if (heightsPtr) + heightfield->buildAccelerator(16); + + return heightfield; } btRayShape *ShapeBullet::create_shape_ray(real_t p_length, bool p_slips_on_slope) { diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 193f1bb48d..492b365128 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -346,11 +346,10 @@ void NetworkedMultiplayerENet::poll() { uint32_t *id = (uint32_t *)event.peer->data; - ERR_CONTINUE(event.packet->dataLength < 12) + ERR_CONTINUE(event.packet->dataLength < 8) uint32_t source = decode_uint32(&event.packet->data[0]); int target = decode_uint32(&event.packet->data[4]); - uint32_t flags = decode_uint32(&event.packet->data[8]); packet.from = source; packet.channel = event.channelID; @@ -371,7 +370,7 @@ void NetworkedMultiplayerENet::poll() { if (uint32_t(E->key()) == source) // Do not resend to self continue; - ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags); + ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, packet.packet->flags); enet_peer_send(E->get(), event.channelID, packet2); } @@ -385,7 +384,7 @@ void NetworkedMultiplayerENet::poll() { if (uint32_t(E->key()) == source || E->key() == -target) // Do not resend to self, also do not send to excluded continue; - ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags); + ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, packet.packet->flags); enet_peer_send(E->get(), event.channelID, packet2); } @@ -503,8 +502,8 @@ Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer, int &r_buff current_packet = incoming_packets.front()->get(); incoming_packets.pop_front(); - *r_buffer = (const uint8_t *)(¤t_packet.packet->data[12]); - r_buffer_size = current_packet.packet->dataLength - 12; + *r_buffer = (const uint8_t *)(¤t_packet.packet->data[8]); + r_buffer_size = current_packet.packet->dataLength - 8; return OK; } @@ -549,11 +548,10 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer } } - ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 12, packet_flags); + ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 8, packet_flags); encode_uint32(unique_id, &packet->data[0]); // Source ID encode_uint32(target_peer, &packet->data[4]); // Dest ID - encode_uint32(packet_flags, &packet->data[8]); // Dest ID - copymem(&packet->data[12], p_buffer, p_buffer_size); + copymem(&packet->data[8], p_buffer, p_buffer_size); if (server) { diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 5f7b825f5e..94a87a7c90 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -58,11 +58,12 @@ def get_opts(): return [ BoolVariable('use_llvm', 'Use the LLVM compiler', False), + BoolVariable('use_lld', 'Use the LLD linker', False), BoolVariable('use_static_cpp', 'Link libgcc and libstdc++ statically for better portability', False), BoolVariable('use_ubsan', 'Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)', False), BoolVariable('use_asan', 'Use LLVM/GCC compiler address sanitizer (ASAN))', False), BoolVariable('use_lsan', 'Use LLVM/GCC compiler leak sanitizer (LSAN))', False), - BoolVariable('pulseaudio', 'Detect & use pulseaudio', True), + BoolVariable('pulseaudio', 'Detect and use PulseAudio', True), BoolVariable('udev', 'Use udev for gamepad connection callbacks', False), EnumVariable('debug_symbols', 'Add debugging symbols to release builds', 'yes', ('yes', 'no', 'full')), BoolVariable('separate_debug_symbols', 'Create a separate file containing debugging symbols', False), @@ -130,6 +131,12 @@ def configure(env): env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) env.extra_suffix = ".llvm" + env.extra_suffix + if env['use_lld']: + if env['use_llvm']: + env.Append(LINKFLAGS=['-fuse-ld=lld']) + else: + print("Using LLD with GCC is not supported yet, try compiling with 'use_llvm=yes'.") + sys.exit(255) if env['use_ubsan'] or env['use_asan'] or env['use_lsan']: env.extra_suffix += "s" @@ -148,6 +155,7 @@ def configure(env): if env['use_lto']: env.Append(CCFLAGS=['-flto']) + if not env['use_llvm'] and env.GetOption("num_jobs") > 1: env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))]) else: diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index ad4bcb283f..f6161a9485 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -3036,34 +3036,40 @@ bool OS_X11::is_vsync_enabled() const { */ void OS_X11::set_context(int p_context) { - char *config_name = NULL; XClassHint *classHint = XAllocClassHint(); if (classHint) { - char *wm_class = (char *)"Godot"; - if (p_context == CONTEXT_EDITOR) - classHint->res_name = (char *)"Godot_Editor"; - if (p_context == CONTEXT_PROJECTMAN) - classHint->res_name = (char *)"Godot_ProjectList"; + CharString name_str; + switch (p_context) { + case CONTEXT_EDITOR: + name_str = "Godot_Editor"; + break; + case CONTEXT_PROJECTMAN: + name_str = "Godot_ProjectList"; + break; + case CONTEXT_ENGINE: + name_str = "Godot_Engine"; + break; + } + CharString class_str; if (p_context == CONTEXT_ENGINE) { - classHint->res_name = (char *)"Godot_Engine"; - String config_name_tmp = GLOBAL_GET("application/config/name"); - if (config_name_tmp.length() > 0) { - config_name = strdup(config_name_tmp.utf8().get_data()); + String config_name = GLOBAL_GET("application/config/name"); + if (config_name.length() == 0) { + class_str = "Godot_Engine"; } else { - config_name = strdup("Godot Engine"); + class_str = config_name.utf8(); } - - wm_class = config_name; + } else { + class_str = "Godot"; } - classHint->res_class = wm_class; + classHint->res_class = class_str.ptrw(); + classHint->res_name = name_str.ptrw(); XSetClassHint(x11_display, x11_window, classHint); XFree(classHint); - free(config_name); } } diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp index 932db8f001..25ad6bd5c9 100644 --- a/scene/2d/animated_sprite.cpp +++ b/scene/2d/animated_sprite.cpp @@ -645,6 +645,9 @@ void AnimatedSprite::_reset_timeout() { void AnimatedSprite::set_animation(const StringName &p_animation) { + ERR_EXPLAIN(vformat("There is no animation with name '%s'.", p_animation)); + ERR_FAIL_COND(frames->get_animation_names().find(p_animation) == -1); + if (animation == p_animation) return; diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp index 368cebeeab..54d7681a3a 100644 --- a/scene/3d/camera.cpp +++ b/scene/3d/camera.cpp @@ -55,16 +55,23 @@ void Camera::_update_camera_mode() { case PROJECTION_ORTHOGONAL: { set_orthogonal(size, near, far); } break; + case PROJECTION_FRUSTUM: { + set_frustum(size, frustum_offset, near, far); + } break; } } void Camera::_validate_property(PropertyInfo &p_property) const { if (p_property.name == "fov") { - if (mode == PROJECTION_ORTHOGONAL) { + if (mode != PROJECTION_PERSPECTIVE) { p_property.usage = PROPERTY_USAGE_NOEDITOR; } } else if (p_property.name == "size") { - if (mode == PROJECTION_PERSPECTIVE) { + if (mode != PROJECTION_ORTHOGONAL && mode != PROJECTION_FRUSTUM) { + p_property.usage = PROPERTY_USAGE_NOEDITOR; + } + } else if (p_property.name == "frustum_offset") { + if (mode != PROJECTION_FRUSTUM) { p_property.usage = PROPERTY_USAGE_NOEDITOR; } } @@ -177,8 +184,24 @@ void Camera::set_orthogonal(float p_size, float p_z_near, float p_z_far) { update_gizmo(); } +void Camera::set_frustum(float p_size, Vector2 p_offset, float p_z_near, float p_z_far) { + if (!force_change && size == p_size && frustum_offset == p_offset && p_z_near == near && p_z_far == far && mode == PROJECTION_FRUSTUM) + return; + + size = p_size; + frustum_offset = p_offset; + + near = p_z_near; + far = p_z_far; + mode = PROJECTION_FRUSTUM; + force_change = false; + + VisualServer::get_singleton()->camera_set_frustum(camera, size, frustum_offset, near, far); + update_gizmo(); +} + void Camera::set_projection(Camera::Projection p_mode) { - if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL) { + if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) { mode = p_mode; _update_camera_mode(); _change_notify(); @@ -470,16 +493,19 @@ void Camera::_bind_methods() { ClassDB::bind_method(D_METHOD("project_position", "screen_point"), &Camera::project_position); ClassDB::bind_method(D_METHOD("set_perspective", "fov", "z_near", "z_far"), &Camera::set_perspective); ClassDB::bind_method(D_METHOD("set_orthogonal", "size", "z_near", "z_far"), &Camera::set_orthogonal); + ClassDB::bind_method(D_METHOD("set_frustum", "size", "offset", "z_near", "z_far"), &Camera::set_frustum); ClassDB::bind_method(D_METHOD("make_current"), &Camera::make_current); ClassDB::bind_method(D_METHOD("clear_current", "enable_next"), &Camera::clear_current, DEFVAL(true)); ClassDB::bind_method(D_METHOD("set_current"), &Camera::set_current); ClassDB::bind_method(D_METHOD("is_current"), &Camera::is_current); ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera::get_camera_transform); ClassDB::bind_method(D_METHOD("get_fov"), &Camera::get_fov); + ClassDB::bind_method(D_METHOD("get_frustum_offset"), &Camera::get_frustum_offset); ClassDB::bind_method(D_METHOD("get_size"), &Camera::get_size); ClassDB::bind_method(D_METHOD("get_zfar"), &Camera::get_zfar); ClassDB::bind_method(D_METHOD("get_znear"), &Camera::get_znear); ClassDB::bind_method(D_METHOD("set_fov"), &Camera::set_fov); + ClassDB::bind_method(D_METHOD("set_frustum_offset"), &Camera::set_frustum_offset); ClassDB::bind_method(D_METHOD("set_size"), &Camera::set_size); ClassDB::bind_method(D_METHOD("set_zfar"), &Camera::set_zfar); ClassDB::bind_method(D_METHOD("set_znear"), &Camera::set_znear); @@ -510,15 +536,17 @@ void Camera::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal"), "set_projection", "get_projection"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01,or_greater"), "set_znear", "get_znear"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar"); BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE); BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL); + BIND_ENUM_CONSTANT(PROJECTION_FRUSTUM); BIND_ENUM_CONSTANT(KEEP_WIDTH); BIND_ENUM_CONSTANT(KEEP_HEIGHT); @@ -543,6 +571,10 @@ float Camera::get_znear() const { return near; } +Vector2 Camera::get_frustum_offset() const { + return frustum_offset; +} + float Camera::get_zfar() const { return far; @@ -570,6 +602,11 @@ void Camera::set_znear(float p_znear) { _update_camera_mode(); } +void Camera::set_frustum_offset(Vector2 p_offset) { + frustum_offset = p_offset; + _update_camera_mode(); +} + void Camera::set_zfar(float p_zfar) { far = p_zfar; _update_camera_mode(); @@ -648,6 +685,7 @@ Camera::Camera() { camera = VisualServer::get_singleton()->camera_create(); size = 1; fov = 0; + frustum_offset = Vector2(); near = 0; far = 0; current = false; diff --git a/scene/3d/camera.h b/scene/3d/camera.h index a531324a85..fe8cb84f0d 100644 --- a/scene/3d/camera.h +++ b/scene/3d/camera.h @@ -46,7 +46,8 @@ public: enum Projection { PROJECTION_PERSPECTIVE, - PROJECTION_ORTHOGONAL + PROJECTION_ORTHOGONAL, + PROJECTION_FRUSTUM }; enum KeepAspect { @@ -68,6 +69,7 @@ private: float fov; float size; + Vector2 frustum_offset; float near, far; float v_offset; float h_offset; @@ -110,6 +112,7 @@ public: void set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far); void set_orthogonal(float p_size, float p_z_near, float p_z_far); + void set_frustum(float p_size, Vector2 p_offset, float p_near, float p_far); void set_projection(Camera::Projection p_mode); void make_current(); @@ -123,12 +126,15 @@ public: float get_size() const; float get_zfar() const; float get_znear() const; + Vector2 get_frustum_offset() const; + Projection get_projection() const; void set_fov(float p_fov); void set_size(float p_size); void set_zfar(float p_zfar); void set_znear(float p_znear); + void set_frustum_offset(Vector2 p_offset); virtual Transform get_camera_transform() const; diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp index c7f7b14a8f..fde135c972 100644 --- a/scene/3d/vehicle_body.cpp +++ b/scene/3d/vehicle_body.cpp @@ -716,7 +716,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) { real_t rollingFriction = 0.f; if (wheelInfo.m_raycastInfo.m_isInContact) { - if (engine_force != 0.f) { + if (engine_force != 0.f && wheelInfo.engine_traction != false) { rollingFriction = -engine_force * s->get_step(); } else { real_t defaultRollingFrictionImpulse = 0.f; diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index c7a815d8a4..f425972183 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -795,7 +795,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_font("bold_italics_font", "RichTextLabel", default_font); theme->set_font("mono_font", "RichTextLabel", default_font); - theme->set_color("default_color", "RichTextLabel", control_font_color); + theme->set_color("default_color", "RichTextLabel", Color(1, 1, 1)); theme->set_color("font_color_selected", "RichTextLabel", font_color_selection); theme->set_color("selection_color", "RichTextLabel", Color(0.1, 0.1, 1, 0.8)); diff --git a/scene/resources/multimesh.cpp b/scene/resources/multimesh.cpp index c9c9855945..be0b9f9ac3 100644 --- a/scene/resources/multimesh.cpp +++ b/scene/resources/multimesh.cpp @@ -288,7 +288,7 @@ void MultiMesh::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_format", PROPERTY_HINT_ENUM, "2D,3D"), "set_transform_format", "get_transform_format"); ADD_PROPERTY(PropertyInfo(Variant::INT, "custom_data_format", PROPERTY_HINT_ENUM, "None,Byte,Float"), "set_custom_data_format", "get_custom_data_format"); ADD_PROPERTY(PropertyInfo(Variant::INT, "instance_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"), "set_instance_count", "get_instance_count"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_instance_count", PROPERTY_HINT_RANGE, "-1,16384,1,or_greater"), "set_visible_instance_count_count", "get_visible_instance_count_count"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_instance_count", PROPERTY_HINT_RANGE, "-1,16384,1,or_greater"), "set_visible_instance_count", "get_visible_instance_count"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh"); ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR3_ARRAY, "transform_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_transform_array", "_get_transform_array"); ADD_PROPERTY(PropertyInfo(Variant::POOL_COLOR_ARRAY, "color_array", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_color_array", "_get_color_array"); diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index 791d59038a..d5e154a7fc 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -33,11 +33,9 @@ #include "visual_server_raster.h" #include "visual_server_viewport.h" -void VisualServerCanvas::_render_canvas_item_tree(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights) { +static const int z_range = VS::CANVAS_ITEM_Z_MAX - VS::CANVAS_ITEM_Z_MIN + 1; - static const int z_range = VS::CANVAS_ITEM_Z_MAX - VS::CANVAS_ITEM_Z_MIN + 1; - RasterizerCanvas::Item *z_list[z_range]; - RasterizerCanvas::Item *z_last_list[z_range]; +void VisualServerCanvas::_render_canvas_item_tree(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights) { memset(z_list, 0, z_range * sizeof(RasterizerCanvas::Item *)); memset(z_last_list, 0, z_range * sizeof(RasterizerCanvas::Item *)); @@ -1456,5 +1454,15 @@ bool VisualServerCanvas::free(RID p_rid) { } VisualServerCanvas::VisualServerCanvas() { + + z_list = (RasterizerCanvas::Item **)memalloc(z_range * sizeof(RasterizerCanvas::Item *)); + z_last_list = (RasterizerCanvas::Item **)memalloc(z_range * sizeof(RasterizerCanvas::Item *)); + disable_scale = false; } + +VisualServerCanvas::~VisualServerCanvas() { + + memfree(z_list); + memfree(z_last_list); +} diff --git a/servers/visual/visual_server_canvas.h b/servers/visual/visual_server_canvas.h index 7691d68639..4e99bb3676 100644 --- a/servers/visual/visual_server_canvas.h +++ b/servers/visual/visual_server_canvas.h @@ -160,6 +160,9 @@ private: void _render_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RasterizerCanvas::Item **z_list, RasterizerCanvas::Item **z_last_list, Item *p_canvas_clip, Item *p_material_owner); void _light_mask_canvas_items(int p_z, RasterizerCanvas::Item *p_canvas_item, RasterizerCanvas::Light *p_masked_lights); + RasterizerCanvas::Item **z_list; + RasterizerCanvas::Item **z_last_list; + public: void render_canvas(Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_masked_lights, const Rect2 &p_clip_rect); @@ -254,6 +257,7 @@ public: bool free(RID p_rid); VisualServerCanvas(); + ~VisualServerCanvas(); }; #endif // VISUALSERVERCANVAS_H diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index 9c7f474f44..efe2a99d2e 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -430,6 +430,7 @@ public: BIND0R(RID, camera_create) BIND4(camera_set_perspective, RID, float, float, float) BIND4(camera_set_orthogonal, RID, float, float, float) + BIND5(camera_set_frustum, RID, float, Vector2, float, float) BIND2(camera_set_transform, RID, const Transform &) BIND2(camera_set_cull_mask, RID, uint32_t) BIND2(camera_set_environment, RID, RID) diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index 4ee2824018..c1dc94daa2 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -61,6 +61,16 @@ void VisualServerScene::camera_set_orthogonal(RID p_camera, float p_size, float camera->zfar = p_z_far; } +void VisualServerScene::camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far) { + Camera *camera = camera_owner.get(p_camera); + ERR_FAIL_COND(!camera); + camera->type = Camera::FRUSTUM; + camera->size = p_size; + camera->offset = p_offset; + camera->znear = p_z_near; + camera->zfar = p_z_far; +} + void VisualServerScene::camera_set_transform(RID p_camera, const Transform &p_transform) { Camera *camera = camera_owner.get(p_camera); @@ -1730,6 +1740,17 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_view ortho = false; } break; + case Camera::FRUSTUM: { + + camera_matrix.set_frustum( + camera->size, + p_viewport_size.width / (float)p_viewport_size.height, + camera->offset, + camera->znear, + camera->zfar, + camera->vaspect); + ortho = false; + } break; } _prepare_scene(camera->transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID()); diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h index 7583acd88f..2c2ba7e15f 100644 --- a/servers/visual/visual_server_scene.h +++ b/servers/visual/visual_server_scene.h @@ -77,12 +77,14 @@ public: enum Type { PERSPECTIVE, - ORTHOGONAL + ORTHOGONAL, + FRUSTUM }; Type type; float fov; float znear, zfar; float size; + Vector2 offset; uint32_t visible_layers; bool vaspect; RID env; @@ -97,6 +99,7 @@ public: znear = 0.05; zfar = 100; size = 1.0; + offset = Vector2(); vaspect = false; } }; @@ -106,6 +109,7 @@ public: virtual RID camera_create(); virtual void camera_set_perspective(RID p_camera, float p_fovy_degrees, float p_z_near, float p_z_far); virtual void camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far); + virtual void camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far); virtual void camera_set_transform(RID p_camera, const Transform &p_transform); virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers); virtual void camera_set_environment(RID p_camera, RID p_env); diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index 9f23ad644e..3e451511cd 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -360,6 +360,7 @@ public: FUNCRID(camera) FUNC4(camera_set_perspective, RID, float, float, float) FUNC4(camera_set_orthogonal, RID, float, float, float) + FUNC5(camera_set_frustum, RID, float, Vector2, float, float) FUNC2(camera_set_transform, RID, const Transform &) FUNC2(camera_set_cull_mask, RID, uint32_t) FUNC2(camera_set_environment, RID, RID) diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index 19c6106502..041fb2b84f 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -1863,6 +1863,7 @@ void VisualServer::_bind_methods() { ClassDB::bind_method(D_METHOD("camera_create"), &VisualServer::camera_create); ClassDB::bind_method(D_METHOD("camera_set_perspective", "camera", "fovy_degrees", "z_near", "z_far"), &VisualServer::camera_set_perspective); ClassDB::bind_method(D_METHOD("camera_set_orthogonal", "camera", "size", "z_near", "z_far"), &VisualServer::camera_set_orthogonal); + ClassDB::bind_method(D_METHOD("camera_set_frustum", "camera", "size", "offset", "z_near", "z_far"), &VisualServer::camera_set_frustum); ClassDB::bind_method(D_METHOD("camera_set_transform", "camera", "transform"), &VisualServer::camera_set_transform); ClassDB::bind_method(D_METHOD("camera_set_cull_mask", "camera", "layers"), &VisualServer::camera_set_cull_mask); ClassDB::bind_method(D_METHOD("camera_set_environment", "camera", "env"), &VisualServer::camera_set_environment); diff --git a/servers/visual_server.h b/servers/visual_server.h index c98c5b39a9..7693faf42b 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -584,6 +584,7 @@ public: virtual RID camera_create() = 0; virtual void camera_set_perspective(RID p_camera, float p_fovy_degrees, float p_z_near, float p_z_far) = 0; virtual void camera_set_orthogonal(RID p_camera, float p_size, float p_z_near, float p_z_far) = 0; + virtual void camera_set_frustum(RID p_camera, float p_size, Vector2 p_offset, float p_z_near, float p_z_far) = 0; virtual void camera_set_transform(RID p_camera, const Transform &p_transform) = 0; virtual void camera_set_cull_mask(RID p_camera, uint32_t p_layers) = 0; virtual void camera_set_environment(RID p_camera, RID p_env) = 0; diff --git a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp index 782e9efaf1..b30ce03164 100644 --- a/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionDispatch/btCollisionWorld.cpp @@ -19,9 +19,10 @@ subject to the following restrictions: #include "BulletCollision/CollisionShapes/btCollisionShape.h" #include "BulletCollision/CollisionShapes/btConvexShape.h" #include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h" -#include "BulletCollision/CollisionShapes/btSphereShape.h" //for raycasting -#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" //for raycasting -#include "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h" //for raycasting +#include "BulletCollision/CollisionShapes/btSphereShape.h" //for raycasting +#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" //for raycasting +#include "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h" //for raycasting +#include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h" //for raycasting #include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.h" #include "BulletCollision/CollisionShapes/btCompoundShape.h" #include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" @@ -413,6 +414,18 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans, co rcb.m_hitFraction = resultCallback.m_closestHitFraction; triangleMesh->performRaycast(&rcb, rayFromLocalScaled, rayToLocalScaled); } + else if (collisionShape->getShapeType()==TERRAIN_SHAPE_PROXYTYPE) + { + ///optimized version for btHeightfieldTerrainShape + btHeightfieldTerrainShape* heightField = (btHeightfieldTerrainShape*)collisionShape; + btTransform worldTocollisionObject = colObjWorldTransform.inverse(); + btVector3 rayFromLocal = worldTocollisionObject * rayFromTrans.getOrigin(); + btVector3 rayToLocal = worldTocollisionObject * rayToTrans.getOrigin(); + + BridgeTriangleRaycastCallback rcb(rayFromLocal,rayToLocal,&resultCallback,collisionObjectWrap->getCollisionObject(),heightField,colObjWorldTransform); + rcb.m_hitFraction = resultCallback.m_closestHitFraction; + heightField->performRaycast(&rcb, rayFromLocal, rayToLocal); + } else { //generic (slower) case diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp b/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp index c85ce2498e..4adf27e6bb 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp @@ -73,6 +73,10 @@ void btHeightfieldTerrainShape::initialize( m_useZigzagSubdivision = false; m_upAxis = upAxis; m_localScaling.setValue(btScalar(1.), btScalar(1.), btScalar(1.)); + m_vboundsGrid = NULL; + m_vboundsChunkSize = 0; + m_vboundsGridWidth = 0; + m_vboundsGridLength = 0; // determine min/max axis-aligned bounding box (aabb) values switch (m_upAxis) @@ -108,6 +112,7 @@ void btHeightfieldTerrainShape::initialize( btHeightfieldTerrainShape::~btHeightfieldTerrainShape() { + clearAccelerator(); } void btHeightfieldTerrainShape::getAabb(const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const @@ -323,6 +328,8 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback } } + // TODO If m_vboundsGrid is available, use it to determine if we really need to process this area + for (int j = startJ; j < endJ; j++) { for (int x = startX; x < endX; x++) @@ -373,3 +380,416 @@ const btVector3& btHeightfieldTerrainShape::getLocalScaling() const { return m_localScaling; } + + + +struct GridRaycastState +{ + int x; // Next quad coords + int z; + int prev_x; // Previous quad coords + int prev_z; + btScalar param; // Exit param for previous quad + btScalar prevParam; // Enter param for previous quad + btScalar maxDistanceFlat; + btScalar maxDistance3d; +}; + + +// TODO Does it really need to take 3D vectors? +/// Iterates through a virtual 2D grid of unit-sized square cells, +/// and executes an action on each cell intersecting the given segment, ordered from begin to end. +/// Initially inspired by http://www.cse.yorku.ca/~amana/research/grid.pdf +template <typename Action_T> +void gridRaycast(Action_T &quadAction, const btVector3 &beginPos, const btVector3 &endPos) +{ + GridRaycastState rs; + rs.maxDistance3d = beginPos.distance(endPos); + if (rs.maxDistance3d < 0.0001) + // Consider the ray is too small to hit anything + return; + + btScalar rayDirectionFlatX = endPos[0] - beginPos[0]; + btScalar rayDirectionFlatZ = endPos[2] - beginPos[2]; + rs.maxDistanceFlat = btSqrt(rayDirectionFlatX * rayDirectionFlatX + rayDirectionFlatZ * rayDirectionFlatZ); + + if(rs.maxDistanceFlat < 0.0001) + { + // Consider the ray vertical + rayDirectionFlatX = 0; + rayDirectionFlatZ = 0; + } + else + { + rayDirectionFlatX /= rs.maxDistanceFlat; + rayDirectionFlatZ /= rs.maxDistanceFlat; + } + + const int xiStep = rayDirectionFlatX > 0 ? 1 : rayDirectionFlatX < 0 ? -1 : 0; + const int ziStep = rayDirectionFlatZ > 0 ? 1 : rayDirectionFlatZ < 0 ? -1 : 0; + + const float infinite = 9999999; + const btScalar paramDeltaX = xiStep != 0 ? 1.f / btFabs(rayDirectionFlatX) : infinite; + const btScalar paramDeltaZ = ziStep != 0 ? 1.f / btFabs(rayDirectionFlatZ) : infinite; + + // pos = param * dir + btScalar paramCrossX; // At which value of `param` we will cross a x-axis lane? + btScalar paramCrossZ; // At which value of `param` we will cross a z-axis lane? + + // paramCrossX and paramCrossZ are initialized as being the first cross + // X initialization + if (xiStep != 0) + { + if (xiStep == 1) + paramCrossX = (ceil(beginPos[0]) - beginPos[0]) * paramDeltaX; + else + paramCrossX = (beginPos[0] - floor(beginPos[0])) * paramDeltaX; + } + else + paramCrossX = infinite; // Will never cross on X + + // Z initialization + if (ziStep != 0) + { + if (ziStep == 1) + paramCrossZ = (ceil(beginPos[2]) - beginPos[2]) * paramDeltaZ; + else + paramCrossZ = (beginPos[2] - floor(beginPos[2])) * paramDeltaZ; + } + else + paramCrossZ = infinite; // Will never cross on Z + + rs.x = static_cast<int>(floor(beginPos[0])); + rs.z = static_cast<int>(floor(beginPos[2])); + + // Workaround cases where the ray starts at an integer position + if (paramCrossX == 0.0) + { + paramCrossX += paramDeltaX; + // If going backwards, we should ignore the position we would get by the above flooring, + // because the ray is not heading in that direction + if (xiStep == -1) + rs.x -= 1; + } + + if (paramCrossZ == 0.0) + { + paramCrossZ += paramDeltaZ; + if (ziStep == -1) + rs.z -= 1; + } + + rs.prev_x = rs.x; + rs.prev_z = rs.z; + rs.param = 0; + + while (true) + { + rs.prev_x = rs.x; + rs.prev_z = rs.z; + rs.prevParam = rs.param; + + if (paramCrossX < paramCrossZ) + { + // X lane + rs.x += xiStep; + // Assign before advancing the param, + // to be in sync with the initialization step + rs.param = paramCrossX; + paramCrossX += paramDeltaX; + } + else + { + // Z lane + rs.z += ziStep; + rs.param = paramCrossZ; + paramCrossZ += paramDeltaZ; + } + + if (rs.param > rs.maxDistanceFlat) + { + rs.param = rs.maxDistanceFlat; + quadAction(rs); + break; + } + else + quadAction(rs); + } +} + + +struct ProcessTrianglesAction +{ + const btHeightfieldTerrainShape *shape; + bool flipQuadEdges; + bool useDiamondSubdivision; + int width; + int length; + btTriangleCallback* callback; + + void exec(int x, int z) const + { + if(x < 0 || z < 0 || x >= width || z >= length) + return; + + btVector3 vertices[3]; + + // Check quad + if (flipQuadEdges || (useDiamondSubdivision && (((z + x) & 1) > 0))) + { + // First triangle + shape->getVertex(x, z, vertices[0]); + shape->getVertex(x + 1, z, vertices[1]); + shape->getVertex(x + 1, z + 1, vertices[2]); + callback->processTriangle(vertices, x, z); + + // Second triangle + shape->getVertex(x, z, vertices[0]); + shape->getVertex(x + 1, z + 1, vertices[1]); + shape->getVertex(x, z + 1, vertices[2]); + callback->processTriangle(vertices, x, z); + } + else + { + // First triangle + shape->getVertex(x, z, vertices[0]); + shape->getVertex(x, z + 1, vertices[1]); + shape->getVertex(x + 1, z, vertices[2]); + callback->processTriangle(vertices, x, z); + + // Second triangle + shape->getVertex(x + 1, z, vertices[0]); + shape->getVertex(x, z + 1, vertices[1]); + shape->getVertex(x + 1, z + 1, vertices[2]); + callback->processTriangle(vertices, x, z); + } + } + + void operator ()(const GridRaycastState &bs) const + { + exec(bs.prev_x, bs.prev_z); + } +}; + + +struct ProcessVBoundsAction +{ + const btHeightfieldTerrainShape::Range *vbounds; + int width; + int length; + int chunkSize; + + btVector3 rayBegin; + btVector3 rayEnd; + btVector3 rayDir; + + ProcessTrianglesAction processTriangles; + + void operator ()(const GridRaycastState &rs) const + { + int x = rs.prev_x; + int z = rs.prev_z; + + if(x < 0 || z < 0 || x >= width || z >= length) + return; + + const btHeightfieldTerrainShape::Range chunk = vbounds[x + z * width]; + + btVector3 enterPos; + btVector3 exitPos; + + if (rs.maxDistanceFlat > 0.0001) + { + btScalar flatTo3d = chunkSize * rs.maxDistance3d / rs.maxDistanceFlat; + btScalar enterParam3d = rs.prevParam * flatTo3d; + btScalar exitParam3d = rs.param * flatTo3d; + enterPos = rayBegin + rayDir * enterParam3d; + exitPos = rayBegin + rayDir * exitParam3d; + + // We did enter the flat projection of the AABB, + // but we have to check if we intersect it on the vertical axis + if (enterPos[1] > chunk.max && exitPos[1] > chunk.max) + return; + if (enterPos[1] < chunk.min && exitPos[1] < chunk.min) + return; + } + else + { + // Consider the ray vertical + // (though we shouldn't reach this often because there is an early check up-front) + enterPos = rayBegin; + exitPos = rayEnd; + } + + gridRaycast(processTriangles, enterPos, exitPos); + // Note: it could be possible to have more than one grid at different levels, + // to do this there would be a branch using a pointer to another ProcessVBoundsAction + } +}; + + +// TODO How do I interrupt the ray when there is a hit? `callback` does not return any result +/// Performs a raycast using a hierarchical Bresenham algorithm. +/// Does not allocate any memory by itself. +void btHeightfieldTerrainShape::performRaycast(btTriangleCallback* callback, const btVector3& raySource, const btVector3& rayTarget) const +{ + // Transform to cell-local + btVector3 beginPos = raySource / m_localScaling; + btVector3 endPos = rayTarget / m_localScaling; + beginPos += m_localOrigin; + endPos += m_localOrigin; + + ProcessTrianglesAction processTriangles; + processTriangles.shape = this; + processTriangles.flipQuadEdges = m_flipQuadEdges; + processTriangles.useDiamondSubdivision = m_useDiamondSubdivision; + processTriangles.callback = callback; + processTriangles.width = m_heightStickWidth - 1; + processTriangles.length = m_heightStickLength - 1; + + // TODO Transform vectors to account for m_upAxis + int iBeginX = static_cast<int>(floor(beginPos[0])); + int iBeginZ = static_cast<int>(floor(beginPos[2])); + int iEndX = static_cast<int>(floor(endPos[0])); + int iEndZ = static_cast<int>(floor(endPos[2])); + + if (iBeginX == iEndX && iBeginZ == iEndZ) + { + // The ray will never cross quads within the plane, + // so directly process triangles within one quad + // (typically, vertical rays should end up here) + processTriangles.exec(iBeginX, iEndZ); + return; + } + + if (m_vboundsGrid == NULL) + { + // Process all quads intersecting the flat projection of the ray + gridRaycast(processTriangles, beginPos, endPos); + } + else + { + btVector3 rayDiff = endPos - beginPos; + btScalar flatDistance2 = rayDiff[0] * rayDiff[0] + rayDiff[2] * rayDiff[2]; + if (flatDistance2 < m_vboundsChunkSize * m_vboundsChunkSize) + { + // Don't use chunks, the ray is too short in the plane + gridRaycast(processTriangles, beginPos, endPos); + } + + ProcessVBoundsAction processVBounds; + processVBounds.width = m_vboundsGridWidth; + processVBounds.length = m_vboundsGridLength; + processVBounds.vbounds = m_vboundsGrid; + processVBounds.rayBegin = beginPos; + processVBounds.rayEnd = endPos; + processVBounds.rayDir = rayDiff.normalized(); + processVBounds.processTriangles = processTriangles; + processVBounds.chunkSize = m_vboundsChunkSize; + // The ray is long, run raycast on a higher-level grid + gridRaycast(processVBounds, beginPos / m_vboundsChunkSize, endPos / m_vboundsChunkSize); + } +} + + +/// Builds a grid data structure storing the min and max heights of the terrain in chunks. +/// if chunkSize is zero, that accelerator is removed. +/// If you modify the heights, you need to rebuild this accelerator. +void btHeightfieldTerrainShape::buildAccelerator(int chunkSize) +{ + if (chunkSize <= 0) + { + clearAccelerator(); + return; + } + + m_vboundsChunkSize = chunkSize; + int nChunksX = m_heightStickWidth / chunkSize; + int nChunksZ = m_heightStickLength / chunkSize; + + if (m_heightStickWidth % chunkSize > 0) + ++nChunksX; // In case terrain size isn't dividable by chunk size + if (m_heightStickLength % chunkSize > 0) + ++nChunksZ; + + if(m_vboundsGridWidth != nChunksX || m_vboundsGridLength != nChunksZ) + { + clearAccelerator(); + m_vboundsGridWidth = nChunksX; + m_vboundsGridLength = nChunksZ; + } + + if (nChunksX == 0 || nChunksZ == 0) + return; + + // TODO What is the recommended way to allocate this? + // This data structure is only reallocated if the required size changed + if (m_vboundsGrid == NULL) + m_vboundsGrid = new Range[nChunksX * nChunksZ]; + + // Compute min and max height for all chunks + for (int cz = 0; cz < nChunksZ; ++cz) + { + int z0 = cz * chunkSize; + + for (int cx = 0; cx < nChunksX; ++cx) + { + int x0 = cx * chunkSize; + + Range r; + + r.min = getRawHeightFieldValue(x0, z0); + r.max = r.min; + + // Compute min and max height for this chunk. + // We have to include one extra cell to account for neighbors. + // Here is why: + // Say we have a flat terrain, and a plateau that fits a chunk perfectly. + // + // Left Right + // 0---0---0---1---1---1 + // | | | | | | + // 0---0---0---1---1---1 + // | | | | | | + // 0---0---0---1---1---1 + // x + // + // If the AABB for the Left chunk did not share vertices with the Right, + // then we would fail collision tests at x due to a gap. + // + for (int z = z0; z < z0 + chunkSize + 1; ++z) + { + if (z >= m_heightStickLength) + continue; + + for (int x = x0; x < x0 + chunkSize + 1; ++x) + { + if (x >= m_heightStickWidth) + continue; + + btScalar height = getRawHeightFieldValue(x, z); + + if (height < r.min) + r.min = height; + else if (height > r.max) + r.max = height; + } + } + + m_vboundsGrid[cx + cz * nChunksX] = r; + } + } +} + + +void btHeightfieldTerrainShape::clearAccelerator() +{ + if (m_vboundsGrid) + { + // TODO What is the recommended way to deallocate this? + delete[] m_vboundsGrid; + m_vboundsGrid = 0; + } +} + + diff --git a/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h b/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h index 8a50a57e31..e23b548cb2 100644 --- a/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h +++ b/thirdparty/bullet/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h @@ -18,6 +18,7 @@ subject to the following restrictions: #include "btConcaveShape.h" + ///btHeightfieldTerrainShape simulates a 2D heightfield terrain /** The caller is responsible for maintaining the heightfield array; this @@ -71,6 +72,12 @@ subject to the following restrictions: ATTRIBUTE_ALIGNED16(class) btHeightfieldTerrainShape : public btConcaveShape { +public: + struct Range { + btScalar min; + btScalar max; + }; + protected: btVector3 m_localAabbMin; btVector3 m_localAabbMax; @@ -100,9 +107,14 @@ protected: btVector3 m_localScaling; + // Accelerator + Range *m_vboundsGrid; + int m_vboundsGridWidth; + int m_vboundsGridLength; + int m_vboundsChunkSize; + virtual btScalar getRawHeightFieldValue(int x, int y) const; void quantizeWithClamp(int* out, const btVector3& point, int isMax) const; - void getVertex(int x, int y, btVector3& vertex) const; /// protected initialization /** @@ -154,6 +166,13 @@ public: virtual void setLocalScaling(const btVector3& scaling); virtual const btVector3& getLocalScaling() const; + + void getVertex(int x,int y,btVector3& vertex) const; + + void performRaycast (btTriangleCallback* callback, const btVector3& raySource, const btVector3& rayTarget) const; + + void buildAccelerator(int chunkSize=16); + void clearAccelerator(); //debugging virtual const char* getName() const { return "HEIGHTFIELD"; } |