diff options
145 files changed, 2089 insertions, 762 deletions
diff --git a/.travis.yml b/.travis.yml index 727567b8e7..b8e02a5be8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,9 +23,9 @@ matrix: addons: apt: sources: - - llvm-toolchain-xenial-6.0 + - llvm-toolchain-xenial-8 packages: - - clang-format-6.0 + - clang-format-8 - env: PLATFORM=x11 TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-mono-gcc-8 MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" EXTRA_ARGS="module_mono_enabled=yes mono_glue=no warnings=extra werror=yes" os: linux diff --git a/SConstruct b/SConstruct index 3ff6bb739a..88bb43fbc7 100644 --- a/SConstruct +++ b/SConstruct @@ -562,7 +562,7 @@ if 'env' in locals(): # (filename, size, weight). current_time = time.time() file_stat = [(x[0], x[1][0], (current_time - x[1][1])) for x in file_stat] - # Sort by the most resently accessed files (most sensible to keep) first + # Sort by the most recently accessed files (most sensible to keep) first file_stat.sort(key=lambda x: x[2]) # Search for the first entry where the storage limit is # reached diff --git a/core/error_macros.h b/core/error_macros.h index 78e29551d4..f72e987e23 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -41,7 +41,7 @@ */ /** - * Pointer to the error macro priting function. Reassign to any function to have errors printed + * Pointer to the error macro printing function. Reassign to any function to have errors printed */ /** Function used by the error macros */ diff --git a/core/image.cpp b/core/image.cpp index 99d5eab864..30af724de9 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -725,6 +725,131 @@ static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict } } +#define LANCZOS_TYPE 3 + +static float _lanczos(float p_x) { + return Math::abs(p_x) >= LANCZOS_TYPE ? 0 : Math::sincn(p_x) * Math::sincn(p_x / LANCZOS_TYPE); +} + +template <int CC, class T> +static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { + + int32_t src_width = p_src_width; + int32_t src_height = p_src_height; + int32_t dst_height = p_dst_height; + int32_t dst_width = p_dst_width; + + uint32_t buffer_size = src_height * dst_width * CC; + float *buffer = memnew_arr(float, buffer_size); // Store the first pass in a buffer + + { // FIRST PASS (horizontal) + + float x_scale = float(src_width) / float(dst_width); + + float scale_factor = MAX(x_scale, 1); // A larger kernel is required only when downscaling + int32_t half_kernel = LANCZOS_TYPE * scale_factor; + + float *kernel = memnew_arr(float, half_kernel * 2 - 1); + + for (int32_t buffer_x = 0; buffer_x < dst_width; buffer_x++) { + + float src_real_x = buffer_x * x_scale; + int32_t src_x = src_real_x; + + int32_t start_x = MAX(0, src_x - half_kernel + 1); + int32_t end_x = MIN(src_width - 1, src_x + half_kernel); + + // Create the kernel used by all the pixels of the column + for (int32_t target_x = start_x; target_x <= end_x; target_x++) + kernel[target_x - start_x] = _lanczos((src_real_x - target_x) / scale_factor); + + for (int32_t buffer_y = 0; buffer_y < src_height; buffer_y++) { + + float pixel[CC] = { 0 }; + float weight = 0; + + for (int32_t target_x = start_x; target_x <= end_x; target_x++) { + + float lanczos_val = kernel[target_x - start_x]; + weight += lanczos_val; + + const T *__restrict src_data = ((const T *)p_src) + (buffer_y * src_width + target_x) * CC; + + for (uint32_t i = 0; i < CC; i++) { + if (sizeof(T) == 2) //half float + pixel[i] += Math::half_to_float(src_data[i]) * lanczos_val; + else + pixel[i] += src_data[i] * lanczos_val; + } + } + + float *dst_data = ((float *)buffer) + (buffer_y * dst_width + buffer_x) * CC; + + for (uint32_t i = 0; i < CC; i++) + dst_data[i] = pixel[i] / weight; // Normalize the sum of all the samples + } + } + + memdelete_arr(kernel); + } // End of first pass + + { // SECOND PASS (vertical + result) + + float y_scale = float(src_height) / float(dst_height); + + float scale_factor = MAX(y_scale, 1); + int32_t half_kernel = LANCZOS_TYPE * scale_factor; + + float *kernel = memnew_arr(float, half_kernel * 2 - 1); + + for (int32_t dst_y = 0; dst_y < dst_height; dst_y++) { + + float buffer_real_y = dst_y * y_scale; + int32_t buffer_y = buffer_real_y; + + int32_t start_y = MAX(0, buffer_y - half_kernel + 1); + int32_t end_y = MIN(src_height - 1, buffer_y + half_kernel); + + for (int32_t target_y = start_y; target_y <= end_y; target_y++) + kernel[target_y - start_y] = _lanczos((buffer_real_y - target_y) / scale_factor); + + for (int32_t dst_x = 0; dst_x < dst_width; dst_x++) { + + float pixel[CC] = { 0 }; + float weight = 0; + + for (int32_t target_y = start_y; target_y <= end_y; target_y++) { + + float lanczos_val = kernel[target_y - start_y]; + weight += lanczos_val; + + float *buffer_data = ((float *)buffer) + (target_y * dst_width + dst_x) * CC; + + for (uint32_t i = 0; i < CC; i++) + pixel[i] += buffer_data[i] * lanczos_val; + } + + T *dst_data = ((T *)p_dst) + (dst_y * dst_width + dst_x) * CC; + + for (uint32_t i = 0; i < CC; i++) { + pixel[i] /= weight; + + if (sizeof(T) == 1) //byte + dst_data[i] = CLAMP(Math::fast_ftoi(pixel[i]), 0, 255); + else if (sizeof(T) == 2) //half float + dst_data[i] = Math::make_half_float(pixel[i]); + else // float + dst_data[i] = pixel[i]; + } + } + } + + memdelete_arr(kernel); + } // End of second pass + + memdelete_arr(buffer); +} + static void _overlay(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, float p_alpha, uint32_t p_width, uint32_t p_height, uint32_t p_pixel_size) { uint16_t alpha = CLAMP((uint16_t)(p_alpha * 256.0f), 0, 256); @@ -939,6 +1064,31 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { } } } break; + case INTERPOLATE_LANCZOS: { + + if (format >= FORMAT_L8 && format <= FORMAT_RGBA8) { + switch (get_format_pixel_size(format)) { + case 1: _scale_lanczos<1, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 2: _scale_lanczos<2, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 3: _scale_lanczos<3, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: _scale_lanczos<4, uint8_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + } else if (format >= FORMAT_RF && format <= FORMAT_RGBAF) { + switch (get_format_pixel_size(format)) { + case 4: _scale_lanczos<1, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 8: _scale_lanczos<2, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 12: _scale_lanczos<3, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 16: _scale_lanczos<4, float>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + } else if (format >= FORMAT_RH && format <= FORMAT_RGBAH) { + switch (get_format_pixel_size(format)) { + case 2: _scale_lanczos<1, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 4: _scale_lanczos<2, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 6: _scale_lanczos<3, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + case 8: _scale_lanczos<4, uint16_t>(r_ptr, w_ptr, width, height, p_width, p_height); break; + } + } + } break; } r = PoolVector<uint8_t>::Read(); @@ -2685,6 +2835,7 @@ void Image::_bind_methods() { BIND_ENUM_CONSTANT(INTERPOLATE_BILINEAR); BIND_ENUM_CONSTANT(INTERPOLATE_CUBIC); BIND_ENUM_CONSTANT(INTERPOLATE_TRILINEAR); + BIND_ENUM_CONSTANT(INTERPOLATE_LANCZOS); BIND_ENUM_CONSTANT(ALPHA_NONE); BIND_ENUM_CONSTANT(ALPHA_BIT); diff --git a/core/image.h b/core/image.h index 69a42f169a..752ef20208 100644 --- a/core/image.h +++ b/core/image.h @@ -109,6 +109,7 @@ public: INTERPOLATE_BILINEAR, INTERPOLATE_CUBIC, INTERPOLATE_TRILINEAR, + INTERPOLATE_LANCZOS, /* INTERPOLATE_TRICUBIC, */ /* INTERPOLATE GAUSS */ }; diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index e5c6d2a4f2..ce2054db36 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -429,7 +429,7 @@ Error HTTPClient::poll() { response_num = RESPONSE_OK; // Per the HTTP 1.1 spec, keep-alive is the default. - // Not following that specification breaks standard implemetations. + // Not following that specification breaks standard implementations. // Broken web servers should be fixed. bool keep_alive = true; diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index 038a34ed51..4a58d37ca5 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -301,8 +301,7 @@ String ResourceFormatImporter::get_import_group_file(const String &p_path) const bool valid = true; PathAndType pat; _get_path_and_type(p_path, pat, &valid); - return valid?pat.group_file:String(); - + return valid ? pat.group_file : String(); } bool ResourceFormatImporter::is_import_valid(const String &p_path) const { diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index bdbdde6df6..2e01989564 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -126,7 +126,7 @@ public: virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = NULL, Variant *r_metadata = NULL) = 0; - virtual Error import_group_file(const String& p_group_file,const Map<String,Map<StringName, Variant> >&p_source_file_options, const Map<String,String>& p_base_paths) { return ERR_UNAVAILABLE; } + virtual Error import_group_file(const String &p_group_file, const Map<String, Map<StringName, Variant> > &p_source_file_options, const Map<String, String> &p_base_paths) { return ERR_UNAVAILABLE; } virtual bool are_import_settings_valid(const String &p_path) const { return true; } virtual String get_import_settings_string() const { return String(); } }; diff --git a/core/io/zip_io.h b/core/io/zip_io.h index fb63878a4c..4eb1c8b46c 100644 --- a/core/io/zip_io.h +++ b/core/io/zip_io.h @@ -33,7 +33,7 @@ #include "core/os/file_access.h" -// Not direclty used in this header, but assumed available in downstream users +// Not directly used in this header, but assumed available in downstream users // like platform/*/export/export.cpp. Could be fixed, but probably better to have // thirdparty includes in as little headers as possible. #include "thirdparty/minizip/unzip.h" diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index e1388ad2ac..3d71e66f80 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -54,7 +54,8 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) { pt->pos = p_pos; pt->weight_scale = p_weight_scale; pt->prev_point = NULL; - pt->last_pass = 0; + pt->open_pass = 0; + pt->closed_pass = 0; pt->enabled = true; points[p_id] = pt; } else { @@ -246,86 +247,62 @@ bool AStar::_solve(Point *begin_point, Point *end_point) { if (!end_point->enabled) return false; - SelfList<Point>::List open_list; - bool found_route = false; - for (Set<Point *>::Element *E = begin_point->neighbours.front(); E; E = E->next()) { - - Point *n = E->get(); + Vector<Point *> open_list; + SortArray<Point *, SortPoints> sorter; - if (!n->enabled) - continue; + begin_point->g_score = 0; + begin_point->f_score = _estimate_cost(begin_point->id, end_point->id); - n->prev_point = begin_point; - n->distance = _compute_cost(begin_point->id, n->id) * n->weight_scale; - n->last_pass = pass; - open_list.add(&n->list); - } + open_list.push_back(begin_point); while (true) { - if (open_list.first() == NULL) { - // No path found + if (open_list.size() == 0) // No path found break; - } - // Check open list - - SelfList<Point> *least_cost_point = open_list.first(); - real_t least_cost = Math_INF; - // TODO: Cache previous results - for (SelfList<Point> *E = open_list.first(); E; E = E->next()) { - - Point *p = E->self(); - - real_t cost = p->distance; - cost += _estimate_cost(p->id, end_point->id); - - if (cost < least_cost) { - least_cost_point = E; - least_cost = cost; - } - } + Point *p = open_list[0]; // The currently processed point - Point *p = least_cost_point->self(); if (p == end_point) { found_route = true; break; } + sorter.pop_heap(0, open_list.size(), open_list.ptrw()); // Remove the current point from the open list + open_list.remove(open_list.size() - 1); + p->closed_pass = pass; // Mark the point as closed + for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) { - Point *e = E->get(); + Point *e = E->get(); // The neighbour point - if (!e->enabled) + if (!e->enabled || e->closed_pass == pass) continue; - real_t distance = _compute_cost(p->id, e->id) * e->weight_scale + p->distance; + real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id) * e->weight_scale; + + bool new_point = false; - if (e->last_pass == pass) { - // Already visited, is this cheaper? + if (e->open_pass != pass) { // The point wasn't inside the open list - if (e->distance > distance) { - e->prev_point = p; - e->distance = distance; - } - } else { - // Add to open neighbours + e->open_pass = pass; + open_list.push_back(e); + new_point = true; + } else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous - e->prev_point = p; - e->distance = distance; - e->last_pass = pass; // Mark as used - open_list.add(&e->list); + continue; } - } - open_list.remove(least_cost_point); - } + e->prev_point = p; + e->g_score = tentative_g_score; + e->f_score = e->g_score + _estimate_cost(e->id, end_point->id); - // Clear the openf list - while (open_list.first()) { - open_list.remove(open_list.first()); + if (new_point) // The position of the new points is already known + sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptrw()); + else + sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw()); + } } return found_route; @@ -352,8 +329,6 @@ PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) { ERR_FAIL_COND_V(!points.has(p_from_id), PoolVector<Vector3>()); ERR_FAIL_COND_V(!points.has(p_to_id), PoolVector<Vector3>()); - pass++; - Point *a = points[p_from_id]; Point *b = points[p_to_id]; @@ -403,8 +378,6 @@ PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) { ERR_FAIL_COND_V(!points.has(p_from_id), PoolVector<int>()); ERR_FAIL_COND_V(!points.has(p_to_id), PoolVector<int>()); - pass++; - Point *a = points[p_from_id]; Point *b = points[p_to_id]; diff --git a/core/math/a_star.h b/core/math/a_star.h index c63e1aa4dc..fac8a9d312 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -48,26 +48,34 @@ class AStar : public Reference { struct Point { - SelfList<Point> list; - int id; Vector3 pos; real_t weight_scale; - uint64_t last_pass; bool enabled; Set<Point *> neighbours; // Used for pathfinding Point *prev_point; - real_t distance; - - Point() : - list(this) {} + real_t g_score; + real_t f_score; + uint64_t open_pass; + uint64_t closed_pass; }; Map<int, Point *> points; + struct SortPoints { + _FORCE_INLINE_ bool operator()(const Point *A, const Point *B) const { // Returns true when the Point A is worse than Point B + if (A->f_score > B->f_score) + return true; + else if (A->f_score < B->f_score) + return false; + else + return A->g_score < B->g_score; // If the f_costs are the same then prioritize the points that are further away from the start + } + }; + struct Segment { union { struct { diff --git a/core/math/geometry.h b/core/math/geometry.h index f3a671aa9a..0b2adf9513 100644 --- a/core/math/geometry.h +++ b/core/math/geometry.h @@ -833,7 +833,7 @@ public: further_away_opposite.y = MIN(p[i].y, further_away_opposite.y); } - further_away += (further_away - further_away_opposite) * Vector2(1.221313, 1.512312); // make point outside that wont intersect with points in segment from p_point + further_away += (further_away - further_away_opposite) * Vector2(1.221313, 1.512312); // make point outside that won't intersect with points in segment from p_point int intersections = 0; for (int i = 0; i < c; i++) { diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index a75f2fb4ab..82b5b56c01 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -61,6 +61,12 @@ public: static _ALWAYS_INLINE_ double sinh(double p_x) { return ::sinh(p_x); } static _ALWAYS_INLINE_ float sinh(float p_x) { return ::sinhf(p_x); } + static _ALWAYS_INLINE_ float sinc(float p_x) { return p_x == 0 ? 1 : ::sin(p_x) / p_x; } + static _ALWAYS_INLINE_ double sinc(double p_x) { return p_x == 0 ? 1 : ::sin(p_x) / p_x; } + + static _ALWAYS_INLINE_ float sincn(float p_x) { return sinc(Math_PI * p_x); } + static _ALWAYS_INLINE_ double sincn(double p_x) { return sinc(Math_PI * p_x); } + static _ALWAYS_INLINE_ double cosh(double p_x) { return ::cosh(p_x); } static _ALWAYS_INLINE_ float cosh(float p_x) { return ::coshf(p_x); } diff --git a/core/object.cpp b/core/object.cpp index 937aa3c745..2a4ab93a6d 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -608,18 +608,16 @@ Variant Object::get_indexed(const Vector<StringName> &p_names, bool *r_valid) co } bool valid = false; - Variant current_value = get(p_names[0]); + Variant current_value = get(p_names[0], &valid); for (int i = 1; i < p_names.size(); i++) { current_value = current_value.get_named(p_names[i], &valid); - if (!valid) { - if (r_valid) - *r_valid = false; - return Variant(); - } + if (!valid) + break; } if (r_valid) - *r_valid = true; + *r_valid = valid; + return current_value; } diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp index 25a5c2afeb..a072017353 100644 --- a/core/os/input_event.cpp +++ b/core/os/input_event.cpp @@ -717,8 +717,17 @@ bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool * bool pressed = same_direction ? Math::abs(jm->get_axis_value()) >= p_deadzone : false; if (p_pressed != NULL) *p_pressed = pressed; - if (p_strength != NULL) - *p_strength = pressed ? CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, Math::abs(jm->get_axis_value())), 0.0f, 1.0f) : 0.0f; + if (p_strength != NULL) { + if (pressed) { + if (p_deadzone == 1.0f) { + *p_strength = 1.0f; + } else { + *p_strength = CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, Math::abs(jm->get_axis_value())), 0.0f, 1.0f); + } + } else { + *p_strength = 0.0f; + } + } } return match; } diff --git a/core/undo_redo.cpp b/core/undo_redo.cpp index 69581e4115..f7ca6d3bde 100644 --- a/core/undo_redo.cpp +++ b/core/undo_redo.cpp @@ -239,8 +239,8 @@ void UndoRedo::_pop_history_tail() { } } -bool UndoRedo::is_commiting_action() const { - return commiting > 0; +bool UndoRedo::is_committing_action() const { + return committing > 0; } void UndoRedo::commit_action() { @@ -255,9 +255,9 @@ void UndoRedo::commit_action() { merging = false; } - commiting++; + committing++; redo(); // perform action - commiting--; + committing--; if (callback && actions.size() > 0) { callback(callback_ud, actions[actions.size() - 1].name); } @@ -396,7 +396,7 @@ void UndoRedo::set_property_notify_callback(PropertyNotifyCallback p_property_ca UndoRedo::UndoRedo() { - commiting = 0; + committing = 0; version = 1; action_level = 0; current_action = -1; @@ -496,10 +496,8 @@ void UndoRedo::_bind_methods() { ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode"), &UndoRedo::create_action, DEFVAL(MERGE_DISABLE)); ClassDB::bind_method(D_METHOD("commit_action"), &UndoRedo::commit_action); - ClassDB::bind_method(D_METHOD("is_commiting_action"), &UndoRedo::is_commiting_action); - - //ClassDB::bind_method(D_METHOD("add_do_method","p_object", "p_method", "VARIANT_ARG_LIST"),&UndoRedo::add_do_method); - //ClassDB::bind_method(D_METHOD("add_undo_method","p_object", "p_method", "VARIANT_ARG_LIST"),&UndoRedo::add_undo_method); + // FIXME: Typo in "commiting", fix in 4.0 when breaking compat. + ClassDB::bind_method(D_METHOD("is_commiting_action"), &UndoRedo::is_committing_action); { MethodInfo mi; diff --git a/core/undo_redo.h b/core/undo_redo.h index 6293e77acc..e2cc6c659b 100644 --- a/core/undo_redo.h +++ b/core/undo_redo.h @@ -95,7 +95,7 @@ private: MethodNotifyCallback method_callback; PropertyNotifyCallback property_callback; - int commiting; + int committing; protected: static void _bind_methods(); @@ -110,7 +110,7 @@ public: void add_do_reference(Object *p_object); void add_undo_reference(Object *p_object); - bool is_commiting_action() const; + bool is_committing_action() const; void commit_action(); bool redo(); diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml index 2f7f05b2e4..63b9ef13fd 100644 --- a/doc/classes/@GDScript.xml +++ b/doc/classes/@GDScript.xml @@ -259,12 +259,12 @@ </description> </method> <method name="decimals"> - <return type="float"> + <return type="int"> </return> <argument index="0" name="step" type="float"> </argument> <description> - Deprecated alias for "[method step_decimals]". + Deprecated alias for "[method step_decimals]". </description> </method> <method name="dectime"> @@ -1020,7 +1020,7 @@ </description> </method> <method name="step_decimals"> - <return type="float"> + <return type="int"> </return> <argument index="0" name="step" type="float"> </argument> diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml index e61fe374fa..c1307be924 100644 --- a/doc/classes/AnimationNode.xml +++ b/doc/classes/AnimationNode.xml @@ -153,7 +153,7 @@ <argument index="0" name="path" type="NodePath"> </argument> <description> - Return [code]true[/code] wether a given path is filtered. + Return [code]true[/code] whether a given path is filtered. </description> </method> <method name="process" qualifiers="virtual"> diff --git a/doc/classes/AnimationNodeStateMachine.xml b/doc/classes/AnimationNodeStateMachine.xml index b80069095b..8f4e6dff69 100644 --- a/doc/classes/AnimationNodeStateMachine.xml +++ b/doc/classes/AnimationNodeStateMachine.xml @@ -3,11 +3,11 @@ <brief_description> </brief_description> <description> - Contains multiple root nodes as children in a graph. Each node is used as a state, and provides multiple functions to alternate between states. Retrieve the AnimationNodeStateMachinePlayback object from the [AnimationTree] node to control it programatically. + Contains multiple root nodes as children in a graph. Each node is used as a state, and provides multiple functions to alternate between states. Retrieve the AnimationNodeStateMachinePlayback object from the [AnimationTree] node to control it programmatically. [codeblock] var state_machine = anim_tree["parameters/StateMachine/playback"] state_machine.travel("SomeState") - [codeblock] + [/codeblock] </description> <tutorials> </tutorials> diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml index c65d545f3e..7bd346ed5a 100644 --- a/doc/classes/BaseButton.xml +++ b/doc/classes/BaseButton.xml @@ -57,6 +57,9 @@ <member name="group" type="ButtonGroup" setter="set_button_group" getter="get_button_group"> [ButtonGroup] associated to the button. </member> + <member name="keep_pressed_outside" type="bool" setter="set_keep_pressed_outside" getter="is_keep_pressed_outside"> + If [code]true[/code], the button stays pressed when moving the cursor outside the button while pressing it. Default value: [code]false[/code]. + </member> <member name="pressed" type="bool" setter="set_pressed" getter="is_pressed"> If [code]true[/code], the button's state is pressed. Means the button is pressed down or toggled (if toggle_mode is active). </member> @@ -69,9 +72,6 @@ <member name="toggle_mode" type="bool" setter="set_toggle_mode" getter="is_toggle_mode"> If [code]true[/code], the button is in toggle mode. Makes the button flip state between pressed and unpressed each time its area is clicked. </member> - <member name="keep_pressed_outside" type="bool" setter="set_keep_pressed_outside" getter="is_keep_pressed_outside"> - If [code]true[/code], the button stays pressed when moving the cursor outside the button while pressing it. Default value: [code]false[/code]. - </member> </members> <signals> <signal name="button_down"> diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 82a5679172..8ba3990933 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -4,10 +4,10 @@ Base class of anything 2D. </brief_description> <description> - Base class of anything 2D. Canvas items are laid out in a tree and children inherit and extend the transform of their parent. CanvasItem is extended by [Control], for anything GUI related, and by [Node2D] for anything 2D engine related. - Any CanvasItem can draw. For this, the "update" function must be called, then NOTIFICATION_DRAW will be received on idle time to request redraw. Because of this, canvas items don't need to be redraw on every frame, improving the performance significantly. Several functions for drawing on the CanvasItem are provided (see draw_* functions). They can only be used inside the notification, signal or _draw() overrides function, though. - Canvas items are draw in tree order. By default, children are on top of their parents so a root CanvasItem will be drawn behind everything (this can be changed per item though). - Canvas items can also be hidden (hiding also their subtree). They provide many means for changing standard parameters such as opacity (for it and the subtree) and self opacity, blend mode. + Base class of anything 2D. Canvas items are laid out in a tree; children inherit and extend their parent's transform. CanvasItem is extended by [Control] for anything GUI-related, and by [Node2D] for anything related to the 2D engine. + Any CanvasItem can draw. For this, [method update] must be called, then [constant NOTIFICATION_DRAW] will be received on idle time to request redraw. Because of this, canvas items don't need to be redrawn on every frame, improving the performance significantly. Several functions for drawing on the CanvasItem are provided (see [code]draw_*[/code] functions). However, they can only be used inside the [method Object._notification], signal or [method _draw] virtual functions. + Canvas items are drawn in tree order. By default, children are on top of their parents so a root CanvasItem will be drawn behind everything. This behavior can be changed on a per-item basis. + A CanvasItem can also be hidden, which will also hide its children. It provides many ways to change parameters such as modulation (for itself and its children) and self modulation (only for itself), as well as its blend mode. Ultimately, a transform notification can be requested, which will notify the node that its global position changed in case the parent tree changed. </description> <tutorials> @@ -36,7 +36,7 @@ <argument index="4" name="modulate" type="Color" default="Color( 1, 1, 1, 1 )"> </argument> <description> - Draws a string character using a custom font. Returns the advance, depending on the char width and kerning with an optional next char. + Draws a string character using a custom font. Returns the advance, depending on the character width and kerning with an optional next character. </description> </method> <method name="draw_circle"> @@ -116,7 +116,7 @@ <argument index="3" name="antialiased" type="bool" default="false"> </argument> <description> - Draws multiple, parallel lines with a uniform [code]color[/code] and [code]width[/code] and optional antialiasing. + Draws multiple, parallel lines with a uniform [code]color[/code]. [code]width[/code] and [code]antialiased[/code] are currently not implemented and have no effect. </description> </method> <method name="draw_multiline_colors"> @@ -211,7 +211,7 @@ <argument index="5" name="normal_map" type="Texture" default="null"> </argument> <description> - Draws a custom primitive, 1 point for a point, 2 points for a line, 3 points for a triangle and 4 points for a quad. + Draws a custom primitive. 1 point for a point, 2 points for a line, 3 points for a triangle and 4 points for a quad. </description> </method> <method name="draw_rect"> @@ -308,7 +308,7 @@ <argument index="5" name="normal_map" type="Texture" default="null"> </argument> <description> - Draws a textured rectangle at a given position, optionally modulated by a color. Transpose swaps the x and y coordinates when reading the texture. + Draws a textured rectangle at a given position, optionally modulated by a color. If [code]transpose[/code] is [code]true[/code], the texture will have its X and Y coordinates swapped. </description> </method> <method name="draw_texture_rect_region"> @@ -329,7 +329,7 @@ <argument index="6" name="clip_uv" type="bool" default="true"> </argument> <description> - Draws a textured rectangle region at a given position, optionally modulated by a color. Transpose swaps the x and y coordinates when reading the texture. + Draws a textured rectangle region at a given position, optionally modulated by a color. If [code]transpose[/code] is [code]true[/code], the texture will have its X and Y coordinates swapped. </description> </method> <method name="force_update_transform"> @@ -419,7 +419,7 @@ <return type="void"> </return> <description> - Hide the CanvasItem currently visible. + Hide the CanvasItem if it's currently visible. </description> </method> <method name="is_local_transform_notification_enabled" qualifiers="const"> @@ -433,7 +433,7 @@ <return type="bool"> </return> <description> - Return if set as toplevel. See [method set_as_toplevel]. + Returns [code]true[/code] if the node is set as top-level. See [method set_as_toplevel]. </description> </method> <method name="is_transform_notification_enabled" qualifiers="const"> @@ -474,7 +474,7 @@ <argument index="0" name="enable" type="bool"> </argument> <description> - Sets as top level. This means that it will not inherit transform from parent canvas items. + If [code]enable[/code] is [code]true[/code], the node won't inherit its transform from parent canvas items. </description> </method> <method name="set_notify_local_transform"> @@ -499,14 +499,14 @@ <return type="void"> </return> <description> - Show the CanvasItem currently hidden. For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple popup*() functions instead. + Show the CanvasItem if it's currently hidden. For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple [code]popup*()[/code] functions instead. </description> </method> <method name="update"> <return type="void"> </return> <description> - Queue the CanvasItem for update. [code]NOTIFICATION_DRAW[/code] will be called on idle time to request redraw. + Queue the CanvasItem for update. [constant NOTIFICATION_DRAW] will be called on idle time to request redraw. </description> </method> </methods> @@ -533,7 +533,7 @@ If [code]true[/code], the parent [CanvasItem]'s [member material] property is used as this one's material. Default value: [code]false[/code]. </member> <member name="visible" type="bool" setter="set_visible" getter="is_visible"> - If [code]true[/code], this [CanvasItem] is drawn. Default value: [code]true[/code]. For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple popup*() functions instead. + If [code]true[/code], this [CanvasItem] is drawn. Default value: [code]true[/code]. For controls that inherit [Popup], the correct way to make them visible is to call one of the multiple [code]popup*()[/code] functions instead. </member> </members> <signals> @@ -575,22 +575,22 @@ Mix blending mode. Colors are assumed to be premultiplied by the alpha (opacity) value. </constant> <constant name="BLEND_MODE_DISABLED" value="5" enum="BlendMode"> - Disable blending mode. Colors including alpha are written as is. Only applicable for render targets with a transparent background. No lighting will be applied. + Disable blending mode. Colors including alpha are written as-is. Only applicable for render targets with a transparent background. No lighting will be applied. </constant> <constant name="NOTIFICATION_TRANSFORM_CHANGED" value="2000"> - Canvas item transform has changed. Notification is only received if enabled by [method set_notify_transform] or [method set_notify_local_transform]. + The CanvasItem's transform has changed. This notification is only received if enabled by [method set_notify_transform] or [method set_notify_local_transform]. </constant> <constant name="NOTIFICATION_DRAW" value="30"> - CanvasItem is requested to draw. + The CanvasItem is requested to draw. </constant> <constant name="NOTIFICATION_VISIBILITY_CHANGED" value="31"> - Canvas item visibility has changed. + The CanvasItem's visibility has changed. </constant> <constant name="NOTIFICATION_ENTER_CANVAS" value="32"> - Canvas item has entered the canvas. + The CanvasItem has entered the canvas. </constant> <constant name="NOTIFICATION_EXIT_CANVAS" value="33"> - Canvas item has exited the canvas. + The CanvasItem has exited the canvas. </constant> </constants> </class> diff --git a/doc/classes/CheckButton.xml b/doc/classes/CheckButton.xml index 94e4eececd..04344c1834 100644 --- a/doc/classes/CheckButton.xml +++ b/doc/classes/CheckButton.xml @@ -41,8 +41,12 @@ </theme_item> <theme_item name="off" type="Texture"> </theme_item> + <theme_item name="off_disabled" type="Texture"> + </theme_item> <theme_item name="on" type="Texture"> </theme_item> + <theme_item name="on_disabled" type="Texture"> + </theme_item> <theme_item name="pressed" type="StyleBox"> </theme_item> </theme_items> diff --git a/doc/classes/EditorInspectorPlugin.xml b/doc/classes/EditorInspectorPlugin.xml index f2dcd2177c..da23218bdc 100644 --- a/doc/classes/EditorInspectorPlugin.xml +++ b/doc/classes/EditorInspectorPlugin.xml @@ -21,7 +21,7 @@ <argument index="0" name="control" type="Control"> </argument> <description> - Add a custom control, not necesarily a property editor. + Add a custom control, not necessarily a property editor. </description> </method> <method name="add_property_editor"> diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index 7981c90f86..5763d26561 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -471,6 +471,7 @@ <return type="int"> </return> <description> + Updates the overlays of the editor (2D/3D) viewport. </description> </method> </methods> diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml index bd189928b0..5792b539e0 100644 --- a/doc/classes/Font.xml +++ b/doc/classes/Font.xml @@ -77,6 +77,16 @@ Return the size of a string, taking kerning and advance into account. </description> </method> + <method name="get_wordwrap_string_size" qualifiers="const"> + <return type="Vector2"> + </return> + <argument index="0" name="string" type="String"> + </argument> + <argument index="1" name="p_width" type="float"> + </argument> + <description> + </description> + </method> <method name="has_outline" qualifiers="const"> <return type="bool"> </return> diff --git a/doc/classes/FuncRef.xml b/doc/classes/FuncRef.xml index 3f84316503..e35d7a68c5 100644 --- a/doc/classes/FuncRef.xml +++ b/doc/classes/FuncRef.xml @@ -17,6 +17,12 @@ Calls the referenced function previously set by [method set_function] or [method @GDScript.funcref]. </description> </method> + <method name="is_valid" qualifiers="const"> + <return type="bool"> + </return> + <description> + </description> + </method> <method name="set_function"> <return type="void"> </return> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 641bd64599..8dfabdd884 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -603,6 +603,8 @@ If the image does not have mipmaps, they will be generated and used internally, but no mipmaps will be generated on the resulting image. (Note that if you intend to scale multiple copies of the original image, it's better to call [code]generate_mipmaps[/code] on it in advance, to avoid wasting processing power in generating them again and again.) On the other hand, if the image already has mipmaps, they will be used, and a new set will be generated for the resulting image. </constant> + <constant name="INTERPOLATE_LANCZOS" value="4" enum="Interpolation"> + </constant> <constant name="ALPHA_NONE" value="0" enum="AlphaMode"> </constant> <constant name="ALPHA_BIT" value="1" enum="AlphaMode"> diff --git a/doc/classes/MultiMesh.xml b/doc/classes/MultiMesh.xml index 0e6b5ef1cb..1532fac206 100644 --- a/doc/classes/MultiMesh.xml +++ b/doc/classes/MultiMesh.xml @@ -59,7 +59,7 @@ <method name="set_as_bulk_array"> <return type="void"> </return> - <argument index="0" name="arg0" type="PoolRealArray"> + <argument index="0" name="array" type="PoolRealArray"> </argument> <description> Set all data related to the instances in one go. This is especially useful when loading the data from disk or preparing the data from GDNative. diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml index 51d2cbc867..06b4cb165e 100644 --- a/doc/classes/OptionButton.xml +++ b/doc/classes/OptionButton.xml @@ -202,14 +202,14 @@ </members> <signals> <signal name="item_focused"> - <argument index="0" name="ID" type="int"> + <argument index="0" name="id" type="int"> </argument> <description> This signal is emitted when user navigated to an item using [code]ui_up[/code] or [code]ui_down[/code] action. ID of the item selected is passed as argument. </description> </signal> <signal name="item_selected"> - <argument index="0" name="ID" type="int"> + <argument index="0" name="id" type="int"> </argument> <description> This signal is emitted when the current item was changed by the user. Index of the item selected is passed as argument. diff --git a/doc/classes/PhysicsBody.xml b/doc/classes/PhysicsBody.xml index 4d94c57ad1..fc4f2e18fd 100644 --- a/doc/classes/PhysicsBody.xml +++ b/doc/classes/PhysicsBody.xml @@ -32,7 +32,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> - Returns an individual bit on the collision mask. + Returns an individual bit on the [member collision_layer]. </description> </method> <method name="get_collision_mask_bit" qualifiers="const"> @@ -41,7 +41,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> - Returns an individual bit on the collision mask. + Returns an individual bit on the [member collision_mask]. </description> </method> <method name="remove_collision_exception_with"> @@ -61,7 +61,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> - Sets individual bits on the layer mask. Use this if you only need to change one layer's value. + Sets individual bits on the [member collision_layer] bitmask. Use this if you only need to change one layer's value. </description> </method> <method name="set_collision_mask_bit"> @@ -72,18 +72,20 @@ <argument index="1" name="value" type="bool"> </argument> <description> - Sets individual bits on the collision mask. Use this if you only need to change one layer's value. + Sets individual bits on the [member collision_mask] bitmask. Use this if you only need to change one layer's value. </description> </method> </methods> <members> <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer"> The physics layers this area is in. - Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property. + Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the [member collision_mask] property. A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. + Default value: 1 (the first layer/bit is enabled). </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> The physics layers this area scans for collisions. + Default value: 1 (the first layer/bit is enabled). </member> </members> <constants> diff --git a/doc/classes/PhysicsBody2D.xml b/doc/classes/PhysicsBody2D.xml index faa992ef7a..a5024c2432 100644 --- a/doc/classes/PhysicsBody2D.xml +++ b/doc/classes/PhysicsBody2D.xml @@ -32,7 +32,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> - Returns an individual bit on the collision mask. + Returns an individual bit on the [member collision_layer]. </description> </method> <method name="get_collision_mask_bit" qualifiers="const"> @@ -41,7 +41,7 @@ <argument index="0" name="bit" type="int"> </argument> <description> - Returns an individual bit on the collision mask. + Returns an individual bit on the [member collision_mask]. </description> </method> <method name="remove_collision_exception_with"> @@ -61,7 +61,7 @@ <argument index="1" name="value" type="bool"> </argument> <description> - Sets individual bits on the layer mask. Use this if you only need to change one layer's value. + Sets individual bits on the [member collision_layer] bitmask. Use this if you only need to change one layer's value. </description> </method> <method name="set_collision_mask_bit"> @@ -72,18 +72,20 @@ <argument index="1" name="value" type="bool"> </argument> <description> - Sets individual bits on the collision mask. Use this if you only need to change one layer's value. + Sets individual bits on the [member collision_mask] bitmask. Use this if you only need to change one layer's value. </description> </method> </methods> <members> <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer"> The physics layers this area is in. - Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property. + Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the [member collision_mask] property. A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. + Default value: 1 (the first layer/bit is enabled). </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask"> The physics layers this area scans for collisions. + Default value: 1 (the first layer/bit is enabled). </member> <member name="layers" type="int" setter="_set_layers" getter="_get_layers"> Both [member collision_layer] and [member collision_mask]. Returns [member collision_layer] when accessed. Updates [member collision_layer] and [member collision_mask] when modified. diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml index 4924175b6e..a91f765d41 100644 --- a/doc/classes/PopupMenu.xml +++ b/doc/classes/PopupMenu.xml @@ -527,14 +527,14 @@ </members> <signals> <signal name="id_focused"> - <argument index="0" name="ID" type="int"> + <argument index="0" name="id" type="int"> </argument> <description> This event is emitted when user navigated to an item of some id using [code]ui_up[/code] or [code]ui_down[/code] action. </description> </signal> <signal name="id_pressed"> - <argument index="0" name="ID" type="int"> + <argument index="0" name="id" type="int"> </argument> <description> This event is emitted when an item of some id is pressed or its accelerator is activated. diff --git a/doc/classes/PrismMesh.xml b/doc/classes/PrismMesh.xml index 0d0f330647..4c282c5e8d 100644 --- a/doc/classes/PrismMesh.xml +++ b/doc/classes/PrismMesh.xml @@ -12,7 +12,7 @@ </methods> <members> <member name="left_to_right" type="float" setter="set_left_to_right" getter="get_left_to_right"> - Displacement of the upper edge along the x-axis. 0.0 positions edge straight above the bottome left edge. Defaults to 0.5 (positioned on the midpoint). + Displacement of the upper edge along the x-axis. 0.0 positions edge straight above the bottom left edge. Defaults to 0.5 (positioned on the midpoint). </member> <member name="size" type="Vector3" setter="set_size" getter="get_size"> Size of the prism. Defaults to (2.0, 2.0, 2.0). diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 46a4a70a9b..4b5500d077 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -281,7 +281,7 @@ If [code]true[/code], enables warnings when using a property as if it was a function. </member> <member name="debug/gdscript/warnings/return_value_discarded" type="bool" setter="" getter=""> - If [code]true[/code], enables warnings when calling a function without using its return value (by assigning it to a variable or using it as a function argument). Such return values are sometimes used to denote possible errors using the [Error] type. + If [code]true[/code], enables warnings when calling a function without using its return value (by assigning it to a variable or using it as a function argument). Such return values are sometimes used to denote possible errors using the [enum Error] enum. </member> <member name="debug/gdscript/warnings/shadowed_variable" type="bool" setter="" getter=""> If [code]true[/code], enables warnings when defining a local or subclass member variable that would shadow a variable at an upper level (such as a member variable). @@ -717,7 +717,7 @@ Shaders have a time variable that constantly increases. At some point, it needs to be rolled back to zero to avoid precision errors on shader animations. This setting specifies when (in seconds). </member> <member name="rendering/quality/2d/gles2_use_nvidia_rect_flicker_workaround" type="bool" setter="" getter=""> - Some NVIDIA GPU drivers have a bug which produces flickering issues for the [code]draw_rect[/code] method, especially as used in [TileMap]. Refer to [url]https://github.com/godotengine/godot/issues/9913[/url] for details. + Some NVIDIA GPU drivers have a bug which produces flickering issues for the [code]draw_rect[/code] method, especially as used in [TileMap]. Refer to [url=https://github.com/godotengine/godot/issues/9913][/url] for details. If [code]true[/code], this option enables a "safe" code path for such NVIDIA GPUs at the cost of performance. This option only impacts the GLES2 rendering backend (so the bug stays if you use GLES3), and only desktop platforms. </member> <member name="rendering/quality/2d/use_pixel_snap" type="bool" setter="" getter=""> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index 526a9427dc..e06f0738b8 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -7,6 +7,7 @@ This is the built-in string class (and the one used by GDScript). It supports Unicode and provides all necessary means for string handling. Strings are reference counted and use a copy-on-write approach, so passing them around is cheap in resources. </description> <tutorials> + <link>https://docs.godotengine.org/en/stable/getting_started/scripting/gdscript/gdscript_format_string.html</link> </tutorials> <methods> <method name="String"> diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml index 0210815d75..dd0461f4db 100644 --- a/doc/classes/TextEdit.xml +++ b/doc/classes/TextEdit.xml @@ -385,6 +385,9 @@ <member name="draw_tabs" type="bool" setter="set_draw_tabs" getter="is_drawing_tabs"> If [code]true[/code], the "tab" character will have a visible representation. </member> + <member name="draw_spaces" type="bool" setter="set_draw_spaces" getter="is_drawing_spaces"> + If [code]true[/code], the "space" character will have a visible representation. + </member> <member name="fold_gutter" type="bool" setter="set_fold_gutter_enabled" getter="is_fold_gutter_enabled"> If [code]true[/code], the fold gutter is visible. This enables folding groups of indented lines. </member> diff --git a/doc/classes/UndoRedo.xml b/doc/classes/UndoRedo.xml index c0b73cd8e3..ce6405b96c 100644 --- a/doc/classes/UndoRedo.xml +++ b/doc/classes/UndoRedo.xml @@ -141,7 +141,7 @@ This is useful mostly to check if something changed from a saved version. </description> </method> - <method name="is_commiting_action" qualifiers="const"> + <method name="is_committing_action" qualifiers="const"> <return type="bool"> </return> <description> diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml index c85a2c4b38..dae120f09b 100644 --- a/doc/classes/VisualServer.xml +++ b/doc/classes/VisualServer.xml @@ -2003,17 +2003,6 @@ <description> </description> </method> - <method name="light_set_use_gi"> - <return type="void"> - </return> - <argument index="0" name="light" type="RID"> - </argument> - <argument index="1" name="enabled" type="bool"> - </argument> - <description> - Sets whether GI probes capture light information from this light. - </description> - </method> <method name="light_set_color"> <return type="void"> </return> @@ -2096,6 +2085,17 @@ <description> </description> </method> + <method name="light_set_use_gi"> + <return type="void"> + </return> + <argument index="0" name="light" type="RID"> + </argument> + <argument index="1" name="enabled" type="bool"> + </argument> + <description> + Sets whether GI probes capture light information from this light. + </description> + </method> <method name="lightmap_capture_create"> <return type="RID"> </return> diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py index e81b4db13e..c3e15b2f9a 100755 --- a/doc/tools/makerst.py +++ b/doc/tools/makerst.py @@ -683,10 +683,16 @@ def rstize_text(text, state): # type: (str, State) -> str # Handle [tags] inside_code = False + inside_url = False + url_has_name = False + url_link = "" pos = 0 tag_depth = 0 + previous_pos = 0 while True: pos = text.find('[', pos) + if inside_url and (pos > previous_pos): + url_has_name = True if pos == -1: break @@ -795,12 +801,16 @@ def rstize_text(text, state): # type: (str, State) -> str elif cmd.find('image=') == 0: tag_text = "" # '![](' + cmd[6:] + ')' elif cmd.find('url=') == 0: - tag_text = ':ref:`' + cmd[4:] + '<' + cmd[4:] + ">`" + url_link = cmd[4:] + tag_text = ':ref:`' tag_depth += 1 + url_has_name = False + inside_url = True elif cmd == '/url': - tag_text = '' + tag_text = ('' if url_has_name else url_link) + '<' + url_link + ">`" tag_depth -= 1 escape_post = True + inside_url = False elif cmd == 'center': tag_depth += 1 tag_text = '' @@ -871,6 +881,7 @@ def rstize_text(text, state): # type: (str, State) -> str text = pre_text + tag_text + post_text pos = len(pre_text) + len(tag_text) + previous_pos = pos if tag_depth > 0: print_error("Tag depth mismatch: too many/little open/close tags, file: {}".format(state.current_class), state) diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp index 55257ab1c5..2d9eec10d2 100644 --- a/drivers/gles2/rasterizer_scene_gles2.cpp +++ b/drivers/gles2/rasterizer_scene_gles2.cpp @@ -580,7 +580,7 @@ bool RasterizerSceneGLES2::reflection_probe_instance_begin_render(RID p_instance //the approach below is fatal for powervr - // Set the initial (empty) mipmaps, all need to be set for this to work in GLES2, even if later wont be used. + // Set the initial (empty) mipmaps, all need to be set for this to work in GLES2, even if they won't be used later. while (size >= 1) { for (int i = 0; i < 6; i++) { @@ -2469,6 +2469,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, if (p_env) { state.scene_shader.set_uniform(SceneShaderGLES2::BG_ENERGY, p_env->bg_energy); + state.scene_shader.set_uniform(SceneShaderGLES2::BG_COLOR, p_env->bg_color); state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_SKY_CONTRIBUTION, p_env->ambient_sky_contribution); state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_COLOR, p_env->ambient_color); @@ -2476,6 +2477,7 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, } else { state.scene_shader.set_uniform(SceneShaderGLES2::BG_ENERGY, 1.0); + state.scene_shader.set_uniform(SceneShaderGLES2::BG_COLOR, state.default_bg); state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_SKY_CONTRIBUTION, 1.0); state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_COLOR, state.default_ambient); state.scene_shader.set_uniform(SceneShaderGLES2::AMBIENT_ENERGY, 1.0); @@ -2578,7 +2580,6 @@ void RasterizerSceneGLES2::_render_render_list(RenderList::Element **p_elements, state.scene_shader.set_conditional(SceneShaderGLES2::USE_LIGHTMAP_CAPTURE, false); state.scene_shader.set_conditional(SceneShaderGLES2::FOG_DEPTH_ENABLED, false); state.scene_shader.set_conditional(SceneShaderGLES2::FOG_HEIGHT_ENABLED, false); - state.scene_shader.set_conditional(SceneShaderGLES2::USE_RADIANCE_MAP, false); state.scene_shader.set_conditional(SceneShaderGLES2::USE_DEPTH_PREPASS, false); } @@ -2829,6 +2830,7 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const } state.default_ambient = Color(clear_color.r, clear_color.g, clear_color.b, 1.0); + state.default_bg = Color(clear_color.r, clear_color.g, clear_color.b, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -2868,6 +2870,7 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const if (probe_interior) { env_radiance_tex = 0; //do not use radiance texture on interiors state.default_ambient = Color(0, 0, 0, 1); //black as default ambient for interior + state.default_bg = Color(0, 0, 0, 1); //black as default background for interior } // render opaque things first diff --git a/drivers/gles2/rasterizer_scene_gles2.h b/drivers/gles2/rasterizer_scene_gles2.h index e5f0bbcbbb..d805984dca 100644 --- a/drivers/gles2/rasterizer_scene_gles2.h +++ b/drivers/gles2/rasterizer_scene_gles2.h @@ -104,6 +104,7 @@ public: GLuint immediate_buffer; Color default_ambient; + Color default_bg; // ResolveShaderGLES3 resolve_shader; // ScreenSpaceReflectionShaderGLES3 ssr_shader; diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl index 7ba2856216..7dce784f52 100644 --- a/drivers/gles2/shaders/canvas.glsl +++ b/drivers/gles2/shaders/canvas.glsl @@ -347,7 +347,7 @@ void main() { vec4 color = color_interp; vec2 uv = uv_interp; #ifdef USE_FORCE_REPEAT - //needs to use this to workaround GLES2/WebGL1 forcing tiling that textures that dont support it + //needs to use this to workaround GLES2/WebGL1 forcing tiling that textures that don't support it uv = mod(uv, vec2(1.0, 1.0)); #endif diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl index 34f34f9961..0fa290e57c 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -914,6 +914,7 @@ uniform mat4 radiance_inverse_xform; #endif +uniform vec4 bg_color; uniform float bg_energy; uniform float ambient_sky_contribution; @@ -1561,7 +1562,6 @@ FRAGMENT_SHADER_CODE ref_vec.z *= -1.0; specular_light = textureCubeLod(radiance_map, ref_vec, roughness * RADIANCE_MAX_LOD).xyz * bg_energy; - { vec3 ambient_dir = normalize((radiance_inverse_xform * vec4(normal, 0.0)).xyz); vec3 env_ambient = textureCubeLod(radiance_map, ambient_dir, RADIANCE_MAX_LOD).xyz * bg_energy; @@ -1572,6 +1572,7 @@ FRAGMENT_SHADER_CODE #else ambient_light = ambient_color.rgb; + specular_light = bg_color.rgb * bg_energy; #endif @@ -1631,6 +1632,19 @@ FRAGMENT_SHADER_CODE #endif // defined(USE_REFLECTION_PROBE1) || defined(USE_REFLECTION_PROBE2) + // scales the specular reflections, needs to be be computed before lighting happens, + // but after environment and reflection probes are added + //TODO: this curve is not really designed for gammaspace, should be adjusted + const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); + vec4 r = roughness * c0 + c1; + float ndotv = clamp(dot(normal, eye_position), 0.0, 1.0); + float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y; + vec2 env = vec2(-1.04, 1.04) * a004 + r.zw; + + vec3 f0 = F0(metallic, specular, albedo); + specular_light *= env.x * f0 + env.y; + #ifdef USE_LIGHTMAP //ambient light will come entirely from lightmap is lightmap is used ambient_light = texture2D(lightmap, uv2_interp).rgb * lightmap_energy; @@ -2080,17 +2094,6 @@ FRAGMENT_SHADER_CODE #if defined(DIFFUSE_TOON) //simplify for toon, as specular_light *= specular * metallic * albedo * 2.0; -#else - //TODO: this curve is not really designed for gammaspace, should be adjusted - const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); - const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); - vec4 r = roughness * c0 + c1; - float ndotv = clamp(dot(normal, eye_position), 0.0, 1.0); - float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y; - vec2 env = vec2(-1.04, 1.04) * a004 + r.zw; - - vec3 f0 = F0(metallic, specular, albedo); - specular_light *= env.x * f0 + env.y; #endif } diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 630e1c2089..def924cd49 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1802,6 +1802,7 @@ FRAGMENT_SHADER_CODE ambient_light = vec3(0.0, 0.0, 0.0); #else ambient_light = ambient_light_color.rgb; + env_reflection_light = bg_color.rgb * bg_energy; #endif //AMBIENT_LIGHT_DISABLED #endif @@ -1813,6 +1814,20 @@ FRAGMENT_SHADER_CODE specular_blob_intensity *= specular * 2.0; #endif + // scales the specular reflections, needs to be be computed before lighting happens, + // but after environment and reflection probes are added + // Environment brdf approximation (Lazarov 2013) + // see https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile + const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); + vec4 r = roughness * c0 + c1; + float ndotv = clamp(dot(normal, eye_vec), 0.0, 1.0); + float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y; + vec2 env = vec2(-1.04, 1.04) * a004 + r.zw; + + vec3 f0 = F0(metallic, specular, albedo); + env_reflection_light *= env.x * f0 + env.y; + #if defined(USE_LIGHT_DIRECTIONAL) vec3 light_attenuation = vec3(1.0); @@ -2063,18 +2078,6 @@ FRAGMENT_SHADER_CODE #if defined(DIFFUSE_TOON) //simplify for toon, as specular_light *= specular * metallic * albedo * 2.0; -#else - // Environment brdf approximation (Lazarov 2013) - // see https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile - const vec4 c0 = vec4(-1.0, -0.0275, -0.572, 0.022); - const vec4 c1 = vec4(1.0, 0.0425, 1.04, -0.04); - vec4 r = roughness * c0 + c1; - float ndotv = clamp(dot(normal, eye_vec), 0.0, 1.0); - float a004 = min(r.x * r.x, exp2(-9.28 * ndotv)) * r.x + r.y; - vec2 env = vec2(-1.04, 1.04) * a004 + r.zw; - - vec3 f0 = F0(metallic, specular, albedo); - specular_light *= env.x * f0 + env.y; #endif } diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 61244ab78c..b2af11fab0 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -3520,7 +3520,7 @@ void AnimationTrackEditor::_update_tracks() { void AnimationTrackEditor::_animation_changed() { if (animation_changing_awaiting_update) { - return; //all will be updated, dont bother with anything + return; //all will be updated, don't bother with anything } if (key_edit && key_edit->setting) { diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index ec984e480a..7fd7b8dac3 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -755,6 +755,7 @@ void CodeTextEditor::update_editor_settings() { text_editor->set_indent_size(EditorSettings::get_singleton()->get("text_editor/indent/size")); text_editor->set_auto_indent(EditorSettings::get_singleton()->get("text_editor/indent/auto_indent")); text_editor->set_draw_tabs(EditorSettings::get_singleton()->get("text_editor/indent/draw_tabs")); + text_editor->set_draw_spaces(EditorSettings::get_singleton()->get("text_editor/indent/draw_spaces")); text_editor->set_show_line_numbers(EditorSettings::get_singleton()->get("text_editor/line_numbers/show_line_numbers")); text_editor->set_line_numbers_zero_padded(EditorSettings::get_singleton()->get("text_editor/line_numbers/line_numbers_zero_padded")); text_editor->set_show_line_length_guideline(EditorSettings::get_singleton()->get("text_editor/line_numbers/show_line_length_guideline")); diff --git a/editor/doc/doc_dump.cpp b/editor/doc/doc_dump.cpp index 6ccf0e26ea..866d57e054 100644 --- a/editor/doc/doc_dump.cpp +++ b/editor/doc/doc_dump.cpp @@ -111,7 +111,7 @@ void DocDump::dump(const String &p_file) { for (List<MethodInfo>::Element *E = method_list.front(); E; E = E->next()) { if (E->get().name == "" || E->get().name[0] == '_') - continue; //hiden + continue; //hidden MethodBind *m = ClassDB::get_method(name, E->get().name); diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index 2beb0153f4..9cd7d781a4 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -322,13 +322,13 @@ void EditorAudioBus::_volume_changed(float p_normalized) { float EditorAudioBus::_normalized_volume_to_scaled_db(float normalized) { /* There are three different formulas for the conversion from normalized - * values to relative decibal values. - * One formula is an exponential graph which intends to counteract - * the logorithmic nature of human hearing. This is an approximation - * of the behaviour of a 'logarithmic potentiometer' found on most - * musical instruments and also emulated in popular software. - * The other two equations are hand-tuned linear tapers that intend to - * try to ease the exponential equation in areas where it makes sense.*/ + * values to relative decibal values. + * One formula is an exponential graph which intends to counteract + * the logorithmic nature of human hearing. This is an approximation + * of the behaviour of a 'logarithmic potentiometer' found on most + * musical instruments and also emulated in popular software. + * The other two equations are hand-tuned linear tapers that intend to + * try to ease the exponential equation in areas where it makes sense.*/ if (normalized > 0.6f) { return 22.22f * normalized - 16.2f; @@ -341,16 +341,16 @@ float EditorAudioBus::_normalized_volume_to_scaled_db(float normalized) { float EditorAudioBus::_scaled_db_to_normalized_volume(float db) { /* Inversion of equations found in _normalized_volume_to_scaled_db. - * IMPORTANT: If one function changes, the other much change to reflect it. */ + * IMPORTANT: If one function changes, the other much change to reflect it. */ if (db > -2.88) { return (db + 16.2f) / 22.22f; } else if (db < -38.602f) { return (db + 80.00f) / 830.72f; } else { if (db < 0.0) { - /* To acommodate for NaN on negative numbers for root, we will mirror the - * results of the postive db range in order to get the desired numerical - * value on the negative side. */ + /* To accommodate for NaN on negative numbers for root, we will mirror the + * results of the positive db range in order to get the desired numerical + * value on the negative side. */ float positive_x = Math::pow(Math::abs(db) / 45.0f, 1.0f / 3.0f) + 1.0f; Vector2 translation = Vector2(1.0f, 0.0f) - Vector2(positive_x, Math::abs(db)); Vector2 reflected_position = Vector2(1.0, 0.0f) + translation; diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index dc43faeff1..6751e58bb2 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -335,7 +335,9 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa pd->file_ofs.push_back(sd); - pd->ep->step(TTR("Storing File:") + " " + p_path, 2 + p_file * 100 / p_total, false); + if (pd->ep->step(TTR("Storing File:") + " " + p_path, 2 + p_file * 100 / p_total, false)) { + return ERR_SKIP; + } return OK; } @@ -362,7 +364,9 @@ Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_pat zipWriteInFileInZip(zip, p_data.ptr(), p_data.size()); zipCloseFileInZip(zip); - zd->ep->step(TTR("Storing File:") + " " + p_path, 2 + p_file * 100 / p_total, false); + if (zd->ep->step(TTR("Storing File:") + " " + p_path, 2 + p_file * 100 / p_total, false)) { + return ERR_SKIP; + } return OK; } @@ -749,27 +753,37 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & this->resolve_platform_feature_priorities(p_preset, remap_features); } + err = OK; + for (List<String>::Element *F = remaps.front(); F; F = F->next()) { String remap = F->get(); if (remap == "path") { String remapped_path = config->get_value("remap", remap); Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path); - p_func(p_udata, remapped_path, array, idx, total); + err = p_func(p_udata, remapped_path, array, idx, total); } else if (remap.begins_with("path.")) { String feature = remap.get_slice(".", 1); if (remap_features.has(feature)) { String remapped_path = config->get_value("remap", remap); Vector<uint8_t> array = FileAccess::get_file_as_array(remapped_path); - p_func(p_udata, remapped_path, array, idx, total); + err = p_func(p_udata, remapped_path, array, idx, total); } } } + if (err != OK) { + return err; + } + //also save the .import file Vector<uint8_t> array = FileAccess::get_file_as_array(path + ".import"); - p_func(p_udata, path + ".import", array, idx, total); + err = p_func(p_udata, path + ".import", array, idx, total); + + if (err != OK) { + return err; + } } else { @@ -884,7 +898,7 @@ Error EditorExportPlatform::_add_shared_object(void *p_userdata, const SharedObj Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, const String &p_path, Vector<SharedObject> *p_so_files) { - EditorProgress ep("savepack", TTR("Packing"), 102); + EditorProgress ep("savepack", TTR("Packing"), 102, true); String tmppath = EditorSettings::get_singleton()->get_cache_dir().plus_file("packtmp"); FileAccess *ftmp = FileAccess::open(tmppath, FileAccess::WRITE); @@ -982,7 +996,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, c Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - EditorProgress ep("savezip", TTR("Packing"), 102); + EditorProgress ep("savezip", TTR("Packing"), 102, true); //FileAccess *tmp = FileAccess::open(tmppath,FileAccess::WRITE); @@ -995,7 +1009,7 @@ Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, co zd.zip = zip; Error err = export_project_files(p_preset, _save_zip_file, &zd); - if (err != OK) + if (err != OK && err != ERR_SKIP) ERR_PRINT("Failed to export project files"); zipClose(zip, NULL); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index abd3bea951..90af593166 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -218,7 +218,7 @@ void EditorFileSystem::_scan_filesystem() { if (first_scan) { // only use this on first scan, afterwards it gets ignored // this is so on first reimport we synchronize versions, then - // we dont care until editor restart. This is for usability mainly so + // we don't care until editor restart. This is for usability mainly so // your workflow is not killed after changing a setting by forceful reimporting // everything there is. filesystem_settings_version_for_import = l.strip_edges(); diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp index f6079624de..96653fec70 100644 --- a/editor/editor_folding.cpp +++ b/editor/editor_folding.cpp @@ -92,7 +92,7 @@ void EditorFolding::load_resource_folding(RES p_resource, const String &p_path) _set_unfolds(p_resource.ptr(), unfolds); } -void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Array& nodes_folded,Set<RES> &resources) { +void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Array &nodes_folded, Set<RES> &resources) { if (p_root != p_node) { if (!p_node->get_owner()) { return; //not owned, bye @@ -130,7 +130,7 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p } for (int i = 0; i < p_node->get_child_count(); i++) { - _fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, nodes_folded,resources); + _fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, nodes_folded, resources); } } void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path) { @@ -205,7 +205,7 @@ void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) { _set_unfolds(res.ptr(), unfolds2); } - for(int i=0;i<nodes_folded.size();i++) { + for (int i = 0; i < nodes_folded.size(); i++) { NodePath fold_path = nodes_folded[i]; if (p_scene->has_node(fold_path)) { Node *node = p_scene->get_node(fold_path); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index fed1f6b4fa..907d43b26f 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1015,8 +1015,7 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) { y = (img->get_height() - size) / 2; img->crop_from_point(x, y, size, size); - // We could get better pictures with better filters - img->resize(preview_size, preview_size, Image::INTERPOLATE_CUBIC); + img->resize(preview_size, preview_size, Image::INTERPOLATE_LANCZOS); } img->convert(Image::FORMAT_RGB8); @@ -1956,6 +1955,9 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { _scene_tab_closed(editor_data.get_edited_scene()); } + if (p_confirmed) + _menu_option_confirm(SCENE_TAB_CLOSE, true); + } break; case FILE_CLOSE_ALL_AND_QUIT: case FILE_CLOSE_ALL_AND_RUN_PROJECT_MANAGER: @@ -2288,10 +2290,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { project_settings->popup_project_settings(); } break; - case RUN_PROJECT_DATA_FOLDER: { - - OS::get_singleton()->shell_open(String("file://") + OS::get_singleton()->get_user_data_dir()); - } break; case FILE_INSTALL_ANDROID_SOURCE: { if (p_confirmed) { @@ -2528,6 +2526,9 @@ void EditorNode::_tool_menu_option(int p_idx) { case TOOLS_ORPHAN_RESOURCES: { orphan_resources->show(); } break; + case RUN_PROJECT_DATA_FOLDER: { + OS::get_singleton()->shell_open(String("file://") + OS::get_singleton()->get_user_data_dir()); + } break; case TOOLS_CUSTOM: { if (tool_menu->get_item_submenu(p_idx) == "") { Array params = tool_menu->get_item_metadata(p_idx); @@ -5997,7 +5998,6 @@ EditorNode::EditorNode() { node_dock = memnew(NodeDock); filesystem_dock = memnew(FileSystemDock(this)); - filesystem_dock->connect("open", this, "open_request"); filesystem_dock->connect("inherit", this, "_inherit_request"); filesystem_dock->connect("instance", this, "_instance_request"); filesystem_dock->connect("display_mode_changed", this, "_save_docks"); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index e3dc517a39..5f18959689 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -645,7 +645,7 @@ void EditorPropertyDictionary::update_property() { page->set_h_size_flags(SIZE_EXPAND_FILL); page->connect("value_changed", this, "_page_changed"); } else { - // Queue childs for deletion, delete immediately might cause errors. + // Queue children for deletion, deleting immediately might cause errors. for (int i = 1; i < vbox->get_child_count(); i++) { vbox->get_child(i)->queue_delete(); } diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 18531bc0dc..356964cd90 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -425,6 +425,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/indent/auto_indent", true); _initial_set("text_editor/indent/convert_indent_on_save", false); _initial_set("text_editor/indent/draw_tabs", true); + _initial_set("text_editor/indent/draw_spaces", false); // Line numbers _initial_set("text_editor/line_numbers/show_line_numbers", true); @@ -650,7 +651,7 @@ void EditorSettings::_load_default_text_editor_theme() { _initial_set("text_editor/highlighting/executing_line_color", Color(0.2, 0.8, 0.2, 0.4)); _initial_set("text_editor/highlighting/code_folding_color", Color(0.8, 0.8, 0.8, 0.8)); _initial_set("text_editor/highlighting/search_result_color", Color(0.05, 0.25, 0.05, 1)); - _initial_set("text_editor/highlighting/search_result_border_color", Color(0.1, 0.45, 0.1, 1)); + _initial_set("text_editor/highlighting/search_result_border_color", Color(0.41, 0.61, 0.91, 0.38)); } bool EditorSettings::_save_text_editor_theme(String p_file) { @@ -972,6 +973,9 @@ void EditorSettings::setup_network() { // link-local IPv6 addresses don't work, skipping them if (ip.begins_with("fe80:0:0:0:")) // fe80::/64 continue; + // Same goes for IPv4 link-local (APIPA) addresses. + if (ip.begins_with("169.254.")) // 169.254.0.0/16 + continue; if (ip == current) lip = current; //so it saves if (hint != "") diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index f74e913208..ba4dd9d7e2 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -833,6 +833,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_stylebox("read_only", "TextEdit", style_widget_disabled); theme->set_constant("side_margin", "TabContainer", 0); theme->set_icon("tab", "TextEdit", theme->get_icon("GuiTab", "EditorIcons")); + theme->set_icon("space", "TextEdit", theme->get_icon("GuiSpace", "EditorIcons")); theme->set_icon("folded", "TextEdit", theme->get_icon("GuiTreeArrowRight", "EditorIcons")); theme->set_icon("fold", "TextEdit", theme->get_icon("GuiTreeArrowDown", "EditorIcons")); theme->set_color("font_color", "TextEdit", font_color); @@ -1121,7 +1122,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { const Color executing_line_color = Color(0.2, 0.8, 0.2, 0.4); const Color code_folding_color = alpha3; const Color search_result_color = alpha1; - const Color search_result_border_color = alpha3; + const Color search_result_border_color = Color(0.41, 0.61, 0.91, 0.38); EditorSettings *setting = EditorSettings::get_singleton(); String text_editor_color_theme = setting->get("text_editor/theme/color_theme"); diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index ed6d6578ad..4e499021f9 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -566,7 +566,7 @@ Error ExportTemplateManager::install_android_template() { f->close(); } { - //add version, to ensure building wont work if template and Godot version are mismatch + //add version, to ensure building won't work if template and Godot version don't match FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::WRITE); ERR_FAIL_COND_V(!f, ERR_CANT_CREATE); f->store_line(VERSION_FULL_CONFIG); @@ -636,7 +636,7 @@ Error ExportTemplateManager::install_android_template() { FileAccess::set_unix_permissions(to_write, (info.external_fa >> 16) & 0x01FF); #endif } else { - ERR_PRINTS("Cant uncompress file: " + to_write); + ERR_PRINTS("Can't uncompress file: " + to_write); } } diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 5ce22154e0..b7e9d36d88 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -90,7 +90,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory String file_type = p_dir->get_file_type(i); if (_is_file_type_disabled_by_feature_profile(file_type)) { - //if type is disabled, file wont be displayed. + //if type is disabled, file won't be displayed. continue; } String file_name = p_dir->get_file(i); diff --git a/editor/icons/icon_GUI_space.svg b/editor/icons/icon_GUI_space.svg new file mode 100644 index 0000000000..caa4565f4a --- /dev/null +++ b/editor/icons/icon_GUI_space.svg @@ -0,0 +1,71 @@ +<?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="8" + height="8" + version="1.1" + viewBox="0 0 8 7.9999993" + id="svg98" + sodipodi:docname="icon_GUI_space.svg" + inkscape:version="0.92.4 (unknown)"> + <metadata + id="metadata104"> + <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="defs102"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 3.9999996 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="8 : 3.9999996 : 1" + inkscape:persp3d-origin="4 : 2.6666664 : 1" + id="perspective992" /> + </defs> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1853" + inkscape:window-height="1025" + id="namedview100" + showgrid="false" + inkscape:zoom="70.333333" + inkscape:cx="3.4905213" + inkscape:cy="6" + inkscape:window-x="67" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="svg98" + inkscape:pagecheckerboard="true" /> + <g + transform="matrix(0.5,0,0,-0.5,1,527.20001)" + id="g96" + style="fill:#ffffff;fill-opacity:0.19607843"> + <circle + cx="6" + cy="1046.4" + r="3" + id="circle94" + style="fill:#ffffff;fill-opacity:0.19607843" /> + </g> +</svg> diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index d72de3de48..5865ceb3af 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -81,7 +81,7 @@ void ResourceImporterTexture::_texture_reimport_normal(const Ref<StreamTexture> void ResourceImporterTexture::update_imports() { if (EditorFileSystem::get_singleton()->is_scanning() || EditorFileSystem::get_singleton()->is_importing()) { - return; // do nothing for noe + return; // do nothing for now } mutex->lock(); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 7c075b5635..0a9436952b 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -129,8 +129,10 @@ void AnimationPlayerEditor::_notification(int p_what) { autoplay_icon = get_icon("AutoPlay", "EditorIcons"); stop->set_icon(get_icon("Stop", "EditorIcons")); + onion_toggle->set_icon(get_icon("Onion", "EditorIcons")); + onion_skinning->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons")); + pin->set_icon(get_icon("Pin", "EditorIcons")); - onion_skinning->set_icon(get_icon("Onion", "EditorIcons")); tool_anim->add_style_override("normal", get_stylebox("normal", "Button")); track_editor->get_edit_menu()->add_style_override("normal", get_stylebox("normal", "Button")); @@ -1229,7 +1231,6 @@ void AnimationPlayerEditor::_onion_skinning_menu(int p_option) { case ONION_SKINNING_ENABLE: { onion.enabled = !onion.enabled; - menu->set_item_checked(idx, onion.enabled); if (onion.enabled) _start_onion_skinning(); @@ -1708,19 +1709,21 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay hb->add_child(track_editor->get_edit_menu()); + hb->add_child(memnew(VSeparator)); + + onion_toggle = memnew(ToolButton); + onion_toggle->set_toggle_mode(true); + onion_toggle->set_tooltip(TTR("Enable Onion Skinning")); + onion_toggle->connect("pressed", this, "_onion_skinning_menu", varray(ONION_SKINNING_ENABLE)); + hb->add_child(onion_toggle); + onion_skinning = memnew(MenuButton); - //onion_skinning->set_flat(false); - onion_skinning->set_tooltip(TTR("Onion Skinning")); - onion_skinning->get_popup()->add_check_shortcut(ED_SHORTCUT("animation_player_editor/onion_skinning", TTR("Enable Onion Skinning")), ONION_SKINNING_ENABLE); - onion_skinning->get_popup()->add_separator(); - onion_skinning->get_popup()->add_item(TTR("Directions"), -1); - onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true); + onion_skinning->set_tooltip(TTR("Onion Skinning Options")); + onion_skinning->get_popup()->add_separator(TTR("Directions")); onion_skinning->get_popup()->add_check_item(TTR("Past"), ONION_SKINNING_PAST); onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true); onion_skinning->get_popup()->add_check_item(TTR("Future"), ONION_SKINNING_FUTURE); - onion_skinning->get_popup()->add_separator(); - onion_skinning->get_popup()->add_item(TTR("Depth"), -1); - onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true); + onion_skinning->get_popup()->add_separator(TTR("Depth")); onion_skinning->get_popup()->add_radio_check_item(TTR("1 step"), ONION_SKINNING_1_STEP); onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true); onion_skinning->get_popup()->add_radio_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS); @@ -1731,6 +1734,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay onion_skinning->get_popup()->add_check_item(TTR("Include Gizmos (3D)"), ONION_SKINNING_INCLUDE_GIZMOS); hb->add_child(onion_skinning); + hb->add_child(memnew(VSeparator)); + pin = memnew(ToolButton); pin->set_toggle_mode(true); pin->set_tooltip(TTR("Pin AnimationPlayer")); diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 9085c70410..c6ab6c5e30 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -102,6 +102,7 @@ class AnimationPlayerEditor : public VBoxContainer { Button *autoplay; MenuButton *tool_anim; + ToolButton *onion_toggle; MenuButton *onion_skinning; ToolButton *pin; SpinBox *frame; diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 92579e5cef..828abef9a9 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3305,9 +3305,7 @@ void ScriptEditorPlugin::edit(Object *p_object) { } else { script_editor->edit(p_script); } - } - - if (Object::cast_to<TextFile>(p_object)) { + } else if (Object::cast_to<TextFile>(p_object)) { script_editor->edit(Object::cast_to<TextFile>(p_object)); } } diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index d40e67cc8c..eaba48fa05 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1266,7 +1266,8 @@ bool ScriptTextEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_ Dictionary d = p_data; if (d.has("type") && (String(d["type"]) == "resource" || String(d["type"]) == "files" || - String(d["type"]) == "nodes")) { + String(d["type"]) == "nodes" || + String(d["type"]) == "files_and_dirs")) { return true; } @@ -1328,7 +1329,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data te->insert_text_at_cursor(res->get_path()); } - if (d.has("type") && String(d["type"]) == "files") { + if (d.has("type") && (String(d["type"]) == "files" || String(d["type"]) == "files_and_dirs")) { Array files = d["files"]; diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 80e2e99685..85743c96d6 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -683,7 +683,6 @@ ThemeEditor::ThemeEditor() { 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); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 21470d81ed..93c6614841 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -1654,7 +1654,7 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { edited_collision_shape = _convex; _set_edited_shape_points(_get_collision_shape_points(concave)); } else { - // Shoudn't haphen + // Shouldn't happen } for (int i = 0; i < sd.size(); i++) { if (sd[i].get("shape") == previous_shape) { @@ -1892,7 +1892,7 @@ void TileSetEditor::_update_toggle_shape_button() { tools[SHAPE_TOGGLE_TYPE]->set_icon(get_icon("ConcavePolygonShape2D", "EditorIcons")); tools[SHAPE_TOGGLE_TYPE]->set_text("Make Concave"); } else { - // Shoudn't happen + // Shouldn't happen separator_shape_toggle->hide(); tools[SHAPE_TOGGLE_TYPE]->hide(); } diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index f3eb5d1483..ad698be0a3 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -1588,6 +1588,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Min", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the lesser of two values."), VisualShaderNodeScalarOp::OP_MIN, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Mix", "Scalar", "Functions", "VisualShaderNodeScalarInterp", TTR("Linear interpolation between two scalars."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Negate", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeScalarFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("OneMinus", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("1.0 - scalar"), VisualShaderNodeScalarFunc::FUNC_ONEMINUS, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Pow", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the value of the first parameter raised to the power of the second."), VisualShaderNodeScalarOp::OP_POW, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Radians", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Converts a quantity in degrees to radians."), VisualShaderNodeScalarFunc::FUNC_RADIANS, VisualShaderNode::PORT_TYPE_SCALAR)); add_options.push_back(AddOption("Reciprocal", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("1.0 / scalar"), VisualShaderNodeScalarFunc::FUNC_RECIPROCAL, VisualShaderNode::PORT_TYPE_SCALAR)); @@ -1677,6 +1678,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeVectorInterp", TTR("Linear interpolation between two vectors."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_VECTOR)); add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNode::PORT_TYPE_VECTOR)); add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), VisualShaderNodeVectorOp::OP_POW, VisualShaderNode::PORT_TYPE_VECTOR)); add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNode::PORT_TYPE_VECTOR)); add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNode::PORT_TYPE_VECTOR)); @@ -1708,6 +1710,8 @@ VisualShaderEditor::VisualShaderEditor() { // SPECIAL + add_options.push_back(AddOption("Fresnel", "Special", "", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ScalarDerivativeFunc", "Special", "Common", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) Scalar derivative function."), -1, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); add_options.push_back(AddOption("VectorDerivativeFunc", "Special", "Common", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) Vector derivative function."), -1, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); diff --git a/editor/project_export.cpp b/editor/project_export.cpp index b9cf7ec10a..e1950c666c 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -972,7 +972,7 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) { current->set_export_path(p_path); Error err = platform->export_project(current, export_debug->is_pressed(), p_path, 0); - if (err != OK) { + if (err != OK && err != ERR_SKIP) { if (err == ERR_FILE_NOT_FOUND) { error_dialog->set_text(vformat(TTR("Failed to export the project for platform '%s'.\nExport templates seem to be missing or invalid."), platform->get_name())); } else { // Assume misconfiguration. FIXME: Improve error handling and preset config validation. @@ -1001,7 +1001,7 @@ void ProjectExportDialog::_export_all_dialog_action(const String &p_str) { void ProjectExportDialog::_export_all(bool p_debug) { String mode = p_debug ? TTR("Debug") : TTR("Release"); - EditorProgress ep("exportall", TTR("Exporting All") + " " + mode, EditorExport::get_singleton()->get_export_preset_count()); + EditorProgress ep("exportall", TTR("Exporting All") + " " + mode, EditorExport::get_singleton()->get_export_preset_count(), true); for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i); @@ -1012,7 +1012,7 @@ void ProjectExportDialog::_export_all(bool p_debug) { ep.step(preset->get_name(), i); Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0); - if (err != OK) { + if (err != OK && err != ERR_SKIP) { if (err == ERR_FILE_BAD_PATH) { error_dialog->set_text(TTR("The given export path doesn't exist:") + "\n" + preset->get_export_path().get_base_dir()); } else { diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index a41f10607b..1f5300e351 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1602,6 +1602,8 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { Ref<Script> existing = E->get()->get_script(); editor_data->get_undo_redo().add_do_method(E->get(), "set_script", p_script.get_ref_ptr()); editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing); + editor_data->get_undo_redo().add_do_method(this, "_update_script_button"); + editor_data->get_undo_redo().add_undo_method(this, "_update_script_button"); } editor_data->get_undo_redo().commit_action(); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 62845bfb9b..76feaedb1a 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -49,7 +49,7 @@ Node *SceneTreeEditor::get_scene_node() { void SceneTreeEditor::_cell_button_pressed(Object *p_item, int p_column, int p_id) { if (connect_to_script_mode) { - return; //dont do anything in this mode + return; //don't do anything in this mode } TreeItem *item = Object::cast_to<TreeItem>(p_item); ERR_FAIL_COND(!item); diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index 1da8bf874c..a661c2cfc3 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -299,6 +299,7 @@ void ScriptEditorDebugger::_scene_tree_rmb_selected(const Vector2 &p_position) { item_menu->clear(); item_menu->add_icon_item(get_icon("CreateNewSceneFrom", "EditorIcons"), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE); + item_menu->add_icon_item(get_icon("CopyNodePath", "EditorIcons"), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH); item_menu->set_global_position(get_global_mouse_position()); item_menu->popup(); } @@ -396,6 +397,7 @@ Size2 ScriptEditorDebugger::get_minimum_size() const { ms.y = MAX(ms.y, 250 * EDSCALE); return ms; } + void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_data) { if (p_msg == "debug_enter") { @@ -1930,6 +1932,24 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) { file_dialog->popup_centered_ratio(); } break; + case ITEM_MENU_COPY_NODE_PATH: { + + TreeItem *ti = inspect_scene_tree->get_selected(); + String text = ti->get_text(0); + + if (ti->get_parent() == NULL) { + text = "."; + } else if (ti->get_parent()->get_parent() == NULL) { + text = "."; + } else { + while (ti->get_parent()->get_parent() != inspect_scene_tree->get_root()) { + ti = ti->get_parent(); + text = ti->get_text(0) + "/" + text; + } + } + + OS::get_singleton()->set_clipboard(text); + } break; } } diff --git a/editor/script_editor_debugger.h b/editor/script_editor_debugger.h index f7afe6bf72..05dcab0f80 100644 --- a/editor/script_editor_debugger.h +++ b/editor/script_editor_debugger.h @@ -66,6 +66,7 @@ class ScriptEditorDebugger : public Control { enum ItemMenu { ITEM_MENU_COPY_ERROR, ITEM_MENU_SAVE_REMOTE_NODE, + ITEM_MENU_COPY_NODE_PATH, }; AcceptDialog *msgdialog; diff --git a/editor/translations/editor.pot b/editor/translations/editor.pot index f9408cfbbc..218eef4fc1 100644 --- a/editor/translations/editor.pot +++ b/editor/translations/editor.pot @@ -9860,7 +9860,7 @@ msgstr "" msgid "" "Container by itself serves no purpose unless a script configures it's " "children placement behavior.\n" -"If you dont't intend to add a script, then please use a plain 'Control' node " +"If you don't intend to add a script, then please use a plain 'Control' node " "instead." msgstr "" diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj index 569033d93c..b375293ca6 100644 --- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj +++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj @@ -352,7 +352,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "$code_sign_identity_debug"; COPY_PHASE_STRIP = NO; ENABLE_BITCODE = NO; - "FRAMEWORK_SEARCH_PATHS[arch=*]" = "$binary"; + "FRAMEWORK_SEARCH_PATHS[arch=*]" = "$binary/**"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; @@ -395,7 +395,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "$code_sign_identity_release"; COPY_PHASE_STRIP = YES; ENABLE_BITCODE = NO; - "FRAMEWORK_SEARCH_PATHS[arch=*]" = "$binary"; + "FRAMEWORK_SEARCH_PATHS[arch=*]" = "$binary/**"; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; diff --git a/misc/travis/clang-format.sh b/misc/travis/clang-format.sh index ee4d0b2a10..5463a6cedf 100755 --- a/misc/travis/clang-format.sh +++ b/misc/travis/clang-format.sh @@ -1,6 +1,6 @@ #!/bin/sh -CLANG_FORMAT=clang-format-6.0 +CLANG_FORMAT=clang-format-8 if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then # Check the whole commit range against $TRAVIS_BRANCH, the base merge branch diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp index 8d82fb2eeb..4cf8782721 100644 --- a/modules/assimp/editor_scene_importer_assimp.cpp +++ b/modules/assimp/editor_scene_importer_assimp.cpp @@ -310,8 +310,8 @@ void EditorSceneImporterAssimp::_generate_bone_groups(ImportState &state, const const aiBone *bone = mesh->mBones[j]; String name = _assimp_get_string(bone->mName); ownership[name] = owned_by; - //store the actuall full path for the bone transform - //when skeleton finds it's place in the tree, it will be restored + //store the actual full path for the bone transform + //when skeleton finds its place in the tree, it will be restored bind_xforms[name] = mesh_offset * _assimp_matrix_transform(bone->mOffsetMatrix); } } @@ -1460,7 +1460,7 @@ void EditorSceneImporterAssimp::_generate_node(ImportState &state, const aiNode int mesh_index = p_assimp_node->mMeshes[i]; surface_indices.push_back(mesh_index); - //take the chane and attempt to find the skeleton from the bones + //take the chance and attempt to find the skeleton from the bones if (!skeleton) { aiMesh *ai_mesh = state.assimp_scene->mMeshes[p_assimp_node->mMeshes[i]]; for (uint32_t j = 0; j < ai_mesh->mNumBones; j++) { @@ -1598,7 +1598,7 @@ void EditorSceneImporterAssimp::_generate_node(ImportState &state, const aiNode skeleton->localize_rests(); node_name = "Skeleton"; //don't use the bone root name - node_transform = Transform(); //dont transform + node_transform = Transform(); //don't transform new_node = skeleton; } else { diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp index e5f70a0b34..733a900396 100644 --- a/modules/bullet/rigid_body_bullet.cpp +++ b/modules/bullet/rigid_body_bullet.cpp @@ -741,22 +741,20 @@ void RigidBodyBullet::set_continuous_collision_detection(bool p_enable) { if (p_enable) { // This threshold enable CCD if the object moves more than // 1 meter in one simulation frame - btBody->setCcdMotionThreshold(0.1); + btBody->setCcdMotionThreshold(1e-7); /// Calculate using the rule writte below the CCD swept sphere radius /// CCD works on an embedded sphere of radius, make sure this radius /// is embedded inside the convex objects, preferably smaller: /// for an object of dimensions 1 meter, try 0.2 - btScalar radius; + btScalar radius(1.0); if (btBody->getCollisionShape()) { btVector3 center; btBody->getCollisionShape()->getBoundingSphere(center, radius); - } else { - radius = 0; } btBody->setCcdSweptSphereRadius(radius * 0.2); } else { - btBody->setCcdMotionThreshold(0.); + btBody->setCcdMotionThreshold(10000.0); btBody->setCcdSweptSphereRadius(0.); } } @@ -834,7 +832,7 @@ void RigidBodyBullet::reload_shapes() { btBody->updateInertiaTensor(); reload_kinematic_shapes(); - + set_continuous_collision_detection(btBody->getCcdMotionThreshold() < 9998.0); reload_body(); } 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/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 6bfd98873e..738b415d16 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -1152,7 +1152,7 @@ public: bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result) { - // Calculate the cummulative AABB of all shapes of the kinematic body + // Calculate the cumulative AABB of all shapes of the kinematic body btVector3 aabb_min, aabb_max; bool shapes_found = false; @@ -1347,7 +1347,7 @@ int SpaceBullet::add_separation_result(PhysicsServer::SeparationResult *r_result int SpaceBullet::recover_from_penetration_ray(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, int p_result_max, btVector3 &r_delta_recover_movement, PhysicsServer::SeparationResult *r_results) { - // Calculate the cummulative AABB of all shapes of the kinematic body + // Calculate the cumulative AABB of all shapes of the kinematic body btVector3 aabb_min, aabb_max; bool shapes_found = false; diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index 7e1cc937cd..3a61afa023 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -611,7 +611,7 @@ void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, in { EdgeSort es; - es.angle = 0; //wont be checked here + es.angle = 0; //won't be checked here es.edge = p_edge; es.prev_point = p_from_point; es.edge_point = p_to_point; diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index e70773d914..1d27b9b6f4 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -2082,6 +2082,9 @@ CSGBrush *CSGPolygon::_build_brush() { for (int i = 0; i <= splits; i++) { float ofs = i * path_interval; + if (ofs > bl) { + ofs = bl; + } if (i == splits && path_joined) { ofs = 0.0; } diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml index 894c17c684..d3d1e58b7b 100644 --- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml +++ b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml @@ -110,7 +110,7 @@ Always use [code]TRANSFER_MODE_ORDERED[/code] in place of [code]TRANSFER_MODE_UNRELIABLE[/code]. This is the only way to use ordering with the RPC system. </member> <member name="channel_count" type="int" setter="set_channel_count" getter="get_channel_count"> - The number of channels to be used by ENet. Default: [code]3[/code]. Channels are used to separate different kinds of data. In realiable or ordered mode, for example, the packet delivery order is ensured on a per channel basis. + The number of channels to be used by ENet. Default: [code]3[/code]. Channels are used to separate different kinds of data. In reliable or ordered mode, for example, the packet delivery order is ensured on a per channel basis. </member> <member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode"> The compression method used for network packets. Default is no compression. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all. diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index 492b365128..18dfe08e85 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -231,7 +231,7 @@ void NetworkedMultiplayerENet::poll() { break; } - // A client joined with an invalid ID (neagtive values, 0, and 1 are reserved). + // A client joined with an invalid ID (negative values, 0, and 1 are reserved). // Probably trying to exploit us. if (server && ((int)event.data < 2 || peer_map.has((int)event.data))) { enet_peer_reset(event.peer); diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp index c8e17e8dc5..a27935bfe2 100644 --- a/modules/gdnative/gdnative.cpp +++ b/modules/gdnative/gdnative.cpp @@ -404,7 +404,7 @@ bool GDNative::terminate() { } else if (gdnatives->size() == 1) { // we're the last one, terminate! gdnatives->clear(); - // wew this looks scary, but all it does is remove the entry completely + // whew this looks scary, but all it does is remove the entry completely GDNativeLibrary::loaded_libraries->erase(GDNativeLibrary::loaded_libraries->find(library->get_current_library_path())); } } diff --git a/modules/gdnative/gdnative_library_singleton_editor.cpp b/modules/gdnative/gdnative_library_singleton_editor.cpp index 55bc16fccc..389b353a51 100644 --- a/modules/gdnative/gdnative_library_singleton_editor.cpp +++ b/modules/gdnative/gdnative_library_singleton_editor.cpp @@ -32,11 +32,16 @@ #include "gdnative_library_singleton_editor.h" #include "gdnative.h" -void GDNativeLibrarySingletonEditor::_find_gdnative_singletons(EditorFileSystemDirectory *p_dir, const Set<String> &enabled_list) { +#include "editor/editor_node.h" + +Set<String> GDNativeLibrarySingletonEditor::_find_singletons_recursive(EditorFileSystemDirectory *p_dir) { + + Set<String> file_paths; // check children for (int i = 0; i < p_dir->get_file_count(); i++) { + String file_name = p_dir->get_file(i); String file_type = p_dir->get_file_type(i); if (file_type != "GDNativeLibrary") { @@ -45,23 +50,57 @@ void GDNativeLibrarySingletonEditor::_find_gdnative_singletons(EditorFileSystemD Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i)); if (lib.is_valid() && lib->is_singleton()) { - String path = p_dir->get_file_path(i); - TreeItem *ti = libraries->create_item(libraries->get_root()); - ti->set_text(0, path.get_file()); - ti->set_tooltip(0, path); - ti->set_metadata(0, path); - ti->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); - ti->set_text(1, "Disabled,Enabled"); - bool enabled = enabled_list.has(path) ? true : false; - - ti->set_range(1, enabled ? 1 : 0); - ti->set_custom_color(1, enabled ? Color(0, 1, 0) : Color(1, 0, 0)); + file_paths.insert(p_dir->get_file_path(i)); } } // check subdirectories for (int i = 0; i < p_dir->get_subdir_count(); i++) { - _find_gdnative_singletons(p_dir->get_subdir(i), enabled_list); + Set<String> paths = _find_singletons_recursive(p_dir->get_subdir(i)); + + for (Set<String>::Element *E = paths.front(); E; E = E->next()) { + file_paths.insert(E->get()); + } + } + + return file_paths; +} + +void GDNativeLibrarySingletonEditor::_discover_singletons() { + + EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->get_filesystem(); + + Set<String> file_paths = _find_singletons_recursive(dir); + + bool changed = false; + Array current_files; + if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { + current_files = ProjectSettings::get_singleton()->get("gdnative/singletons"); + } + Array files; + for (Set<String>::Element *E = file_paths.front(); E; E = E->next()) { + if (!current_files.has(E->get())) { + changed = true; + } + files.append(E->get()); + } + + // Check for removed files + if (!changed) { + // Removed singleton + for (int j = 0; j < current_files.size(); j++) { + if (!files.has(current_files[j])) { + changed = true; + break; + } + } + } + + if (changed) { + + ProjectSettings::get_singleton()->set("gdnative/singletons", files); + _update_libraries(); // So singleton options (i.e. disabled) updates too + ProjectSettings::get_singleton()->save(); } } @@ -69,22 +108,40 @@ void GDNativeLibrarySingletonEditor::_update_libraries() { updating = true; libraries->clear(); - libraries->create_item(); //rppt + libraries->create_item(); // root item - Vector<String> enabled_paths; + Array singletons; if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { - enabled_paths = ProjectSettings::get_singleton()->get("gdnative/singletons"); + singletons = ProjectSettings::get_singleton()->get("gdnative/singletons"); } - Set<String> enabled_list; - for (int i = 0; i < enabled_paths.size(); i++) { - enabled_list.insert(enabled_paths[i]); + Array singletons_disabled; + if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons_disabled")) { + singletons_disabled = ProjectSettings::get_singleton()->get("gdnative/singletons_disabled"); } - EditorFileSystemDirectory *fs = EditorFileSystem::get_singleton()->get_filesystem(); - if (fs) { - _find_gdnative_singletons(fs, enabled_list); + Array updated_disabled; + for (int i = 0; i < singletons.size(); i++) { + bool enabled = true; + String path = singletons[i]; + if (singletons_disabled.has(path)) { + enabled = false; + updated_disabled.push_back(path); + } + TreeItem *ti = libraries->create_item(libraries->get_root()); + ti->set_text(0, path.get_file()); + ti->set_tooltip(0, path); + ti->set_metadata(0, path); + ti->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); + ti->set_text(1, "Disabled,Enabled"); + ti->set_range(1, enabled ? 1 : 0); + ti->set_custom_color(1, enabled ? Color(0, 1, 0) : Color(1, 0, 0)); + ti->set_editable(1, true); } + // The singletons list changed, we must update the settings + if (updated_disabled.size() != singletons_disabled.size()) + ProjectSettings::get_singleton()->set("gdnative/singletons_disabled", updated_disabled); + updating = false; } @@ -99,24 +156,29 @@ void GDNativeLibrarySingletonEditor::_item_edited() { bool enabled = item->get_range(1); String path = item->get_metadata(0); - Vector<String> enabled_paths; - if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { - enabled_paths = ProjectSettings::get_singleton()->get("gdnative/singletons"); + Array disabled_paths; + Array undo_paths; + if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons_disabled")) { + disabled_paths = ProjectSettings::get_singleton()->get("gdnative/singletons_disabled"); + // Duplicate so redo works (not a reference) + disabled_paths = disabled_paths.duplicate(); + // For undo, so we can reset the property. + undo_paths = disabled_paths.duplicate(); } if (enabled) { - if (enabled_paths.find(path) == -1) { - enabled_paths.push_back(path); - } + disabled_paths.erase(path); } else { - enabled_paths.erase(path); + if (disabled_paths.find(path) == -1) + disabled_paths.push_back(path); } - if (enabled_paths.size()) { - ProjectSettings::get_singleton()->set("gdnative/singletons", enabled_paths); - } else { - ProjectSettings::get_singleton()->set("gdnative/singletons", Variant()); - } + undo_redo->create_action(enabled ? TTR("Enabled GDNative Singleton") : TTR("Disabled GDNative Singleton")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "gdnative/singletons_disabled", disabled_paths); + undo_redo->add_do_method(this, "_update_libraries"); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "gdnative/singletons_disabled", undo_paths); + undo_redo->add_undo_method(this, "_update_libraries"); + undo_redo->commit_action(); } void GDNativeLibrarySingletonEditor::_notification(int p_what) { @@ -131,9 +193,12 @@ void GDNativeLibrarySingletonEditor::_notification(int p_what) { void GDNativeLibrarySingletonEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_item_edited"), &GDNativeLibrarySingletonEditor::_item_edited); + ClassDB::bind_method(D_METHOD("_discover_singletons"), &GDNativeLibrarySingletonEditor::_discover_singletons); + ClassDB::bind_method(D_METHOD("_update_libraries"), &GDNativeLibrarySingletonEditor::_update_libraries); } GDNativeLibrarySingletonEditor::GDNativeLibrarySingletonEditor() { + undo_redo = EditorNode::get_singleton()->get_undo_redo(); libraries = memnew(Tree); libraries->set_columns(2); libraries->set_column_titles_visible(true); @@ -143,6 +208,7 @@ GDNativeLibrarySingletonEditor::GDNativeLibrarySingletonEditor() { add_margin_child(TTR("Libraries: "), libraries, true); updating = false; libraries->connect("item_edited", this, "_item_edited"); + EditorFileSystem::get_singleton()->connect("filesystem_changed", this, "_discover_singletons"); } #endif // TOOLS_ENABLED diff --git a/modules/gdnative/gdnative_library_singleton_editor.h b/modules/gdnative/gdnative_library_singleton_editor.h index cf5ab23501..b43080dfdb 100644 --- a/modules/gdnative/gdnative_library_singleton_editor.h +++ b/modules/gdnative/gdnative_library_singleton_editor.h @@ -36,18 +36,24 @@ #include "editor/project_settings_editor.h" class GDNativeLibrarySingletonEditor : public VBoxContainer { + GDCLASS(GDNativeLibrarySingletonEditor, VBoxContainer); + +private: Tree *libraries; + UndoRedo *undo_redo; bool updating; - void _update_libraries(); - void _find_gdnative_singletons(EditorFileSystemDirectory *p_dir, const Set<String> &enabled_list); - void _item_edited(); + static Set<String> _find_singletons_recursive(EditorFileSystemDirectory *p_dir); protected: void _notification(int p_what); static void _bind_methods(); + void _discover_singletons(); + void _item_edited(); + void _update_libraries(); + public: GDNativeLibrarySingletonEditor(); }; diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp index 5cf144d4fe..c2aa8427b4 100644 --- a/modules/gdnative/nativescript/nativescript.cpp +++ b/modules/gdnative/nativescript/nativescript.cpp @@ -1309,7 +1309,7 @@ void NativeScriptLanguage::unregister_binding_functions(int p_idx) { for (Set<Vector<void *> *>::Element *E = binding_instances.front(); E; E = E->next()) { Vector<void *> &binding_data = *E->get(); - if (binding_data[p_idx] && binding_functions[p_idx].second.free_instance_binding_data) + if (p_idx < binding_data.size() && binding_data[p_idx] && binding_functions[p_idx].second.free_instance_binding_data) binding_functions[p_idx].second.free_instance_binding_data(binding_functions[p_idx].second.data, binding_data[p_idx]); } @@ -1345,7 +1345,7 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec if (!(*binding_data)[p_idx]) { - const void *global_type_tag = global_type_tags[p_idx].get(p_object->get_class_name()); + const void *global_type_tag = get_global_type_tag(p_idx, p_object->get_class_name()); // no binding data yet, soooooo alloc new one \o/ (*binding_data).write[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object); @@ -1454,6 +1454,9 @@ const void *NativeScriptLanguage::get_global_type_tag(int p_idx, StringName p_cl const HashMap<StringName, const void *> &tags = global_type_tags[p_idx]; + if (!tags.has(p_class_name)) + return NULL; + const void *tag = tags.get(p_class_name); return tag; diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h index b279fdad8b..381c334231 100644 --- a/modules/gdnative/pluginscript/pluginscript_instance.h +++ b/modules/gdnative/pluginscript/pluginscript_instance.h @@ -62,7 +62,7 @@ public: virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error); #if 0 // Rely on default implementations provided by ScriptInstance for the moment. - // Note that multilevel call could be removed in 3.0 release, so stay tunned + // Note that multilevel call could be removed in 3.0 release, so stay tuned // (see https://godotengine.org/qa/9244/can-override-the-_ready-and-_process-functions-child-classes) virtual void call_multilevel(const StringName& p_method,const Variant** p_args,int p_argcount); virtual void call_multilevel_reversed(const StringName& p_method,const Variant** p_args,int p_argcount); diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index 2094dca6e4..55d44ceec8 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -50,97 +50,6 @@ #include "editor/editor_node.h" #include "gdnative_library_editor_plugin.h" #include "gdnative_library_singleton_editor.h" -// Class used to discover singleton gdnative files - -static void actual_discoverer_handler(); - -class GDNativeSingletonDiscover : public Object { - // GDCLASS(GDNativeSingletonDiscover, Object) - - virtual String get_class() const { - // okay, this is a really dirty hack. - // We're overriding get_class so we can connect it to a signal - // This works because get_class is a virtual method, so we don't - // need to register a new class to ClassDB just for this one - // little signal. - - actual_discoverer_handler(); - - return "Object"; - } -}; - -static Set<String> get_gdnative_singletons(EditorFileSystemDirectory *p_dir) { - - Set<String> file_paths; - - // check children - - for (int i = 0; i < p_dir->get_file_count(); i++) { - String file_name = p_dir->get_file(i); - String file_type = p_dir->get_file_type(i); - - if (file_type != "GDNativeLibrary") { - continue; - } - - Ref<GDNativeLibrary> lib = ResourceLoader::load(p_dir->get_file_path(i)); - if (lib.is_valid() && lib->is_singleton()) { - file_paths.insert(p_dir->get_file_path(i)); - } - } - - // check subdirectories - for (int i = 0; i < p_dir->get_subdir_count(); i++) { - Set<String> paths = get_gdnative_singletons(p_dir->get_subdir(i)); - - for (Set<String>::Element *E = paths.front(); E; E = E->next()) { - file_paths.insert(E->get()); - } - } - - return file_paths; -} - -static void actual_discoverer_handler() { - - EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->get_filesystem(); - - Set<String> file_paths = get_gdnative_singletons(dir); - - bool changed = false; - Array current_files; - if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { - current_files = ProjectSettings::get_singleton()->get("gdnative/singletons"); - } - Array files; - files.resize(file_paths.size()); - int i = 0; - for (Set<String>::Element *E = file_paths.front(); E; i++, E = E->next()) { - if (!current_files.has(E->get())) { - changed = true; - } - files.set(i, E->get()); - } - - // Check for removed files - if (!changed) { - for (int j = 0; j < current_files.size(); j++) { - if (!file_paths.has(current_files[j])) { - changed = true; - break; - } - } - } - - if (changed) { - - ProjectSettings::get_singleton()->set("gdnative/singletons", files); - ProjectSettings::get_singleton()->save(); - } -} - -static GDNativeSingletonDiscover *discoverer = NULL; class GDNativeExportPlugin : public EditorExportPlugin { @@ -275,9 +184,6 @@ static void editor_init_callback() { library_editor->set_name(TTR("GDNative")); ProjectSettingsEditor::get_singleton()->get_tabs()->add_child(library_editor); - discoverer = memnew(GDNativeSingletonDiscover); - EditorFileSystem::get_singleton()->connect("filesystem_changed", discoverer, "get_class"); - Ref<GDNativeExportPlugin> export_plugin; export_plugin.instance(); @@ -335,30 +241,36 @@ void register_gdnative_types() { if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons")) { singletons = ProjectSettings::get_singleton()->get("gdnative/singletons"); } - - singleton_gdnatives.resize(singletons.size()); + Array excluded = Array(); + if (ProjectSettings::get_singleton()->has_setting("gdnative/singletons_disabled")) { + excluded = ProjectSettings::get_singleton()->get("gdnative/singletons_disabled"); + } for (int i = 0; i < singletons.size(); i++) { String path = singletons[i]; - Ref<GDNativeLibrary> lib = ResourceLoader::load(path); + if (excluded.has(path)) + continue; - singleton_gdnatives.write[i].instance(); - singleton_gdnatives.write[i]->set_library(lib); + Ref<GDNativeLibrary> lib = ResourceLoader::load(path); + Ref<GDNative> singleton; + singleton.instance(); + singleton->set_library(lib); - if (!singleton_gdnatives.write[i]->initialize()) { + if (!singleton->initialize()) { // Can't initialize. Don't make a native_call then continue; } void *proc_ptr; - Error err = singleton_gdnatives[i]->get_symbol( + Error err = singleton->get_symbol( lib->get_symbol_prefix() + "gdnative_singleton", proc_ptr); if (err != OK) { - ERR_PRINT((String("No godot_gdnative_singleton in \"" + singleton_gdnatives[i]->get_library()->get_current_library_path()) + "\" found").utf8().get_data()); + ERR_PRINT((String("No godot_gdnative_singleton in \"" + singleton->get_library()->get_current_library_path()) + "\" found").utf8().get_data()); } else { + singleton_gdnatives.push_back(singleton); ((void (*)())proc_ptr)(); } } @@ -388,12 +300,6 @@ void unregister_gdnative_types() { memdelete(GDNativeCallRegistry::singleton); -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint() && discoverer != NULL) { - memdelete(discoverer); - } -#endif - ResourceLoader::remove_resource_format_loader(resource_loader_gdnlib); resource_loader_gdnlib.unref(); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 33e99cb82f..d7a809eb5f 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1064,7 +1064,7 @@ Variant::Type GDScriptInstance::get_property_type(const StringName &p_name, bool } void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const { - // exported members, not doen yet! + // exported members, not done yet! const GDScript *sptr = script.ptr(); List<PropertyInfo> props; diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 903b3892d1..de15f939ce 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -7473,7 +7473,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { return; } - // Replace assignment with implict conversion + // Replace assignment with implicit conversion BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); convert->line = v.line; convert->function = GDScriptFunctions::TYPE_CONVERT; @@ -7837,14 +7837,14 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { if (_is_type_compatible(assign_type, lv->datatype)) { _mark_line_as_unsafe(lv->line); } else { - // Try implict conversion + // Try implicit conversion if (lv->datatype.kind != DataType::BUILTIN || !_is_type_compatible(lv->datatype, assign_type, true)) { _set_error("Assigned value type (" + assign_type.to_string() + ") doesn't match the variable's type (" + lv->datatype.to_string() + ").", lv->line); return; } - // Replace assignment with implict conversion + // Replace assignment with implicit conversion BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); convert->line = lv->line; convert->function = GDScriptFunctions::TYPE_CONVERT; @@ -7968,14 +7968,14 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { if (_is_type_compatible(rh_type, lh_type)) { _mark_line_as_unsafe(op->line); } else { - // Try implict conversion + // Try implicit conversion if (lh_type.kind != DataType::BUILTIN || !_is_type_compatible(lh_type, rh_type, true)) { _set_error("Assigned value type (" + rh_type.to_string() + ") doesn't match the variable's type (" + lh_type.to_string() + ").", op->line); return; } - // Replace assignment with implict conversion + // Replace assignment with implicit conversion BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); convert->line = op->line; convert->function = GDScriptFunctions::TYPE_CONVERT; diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index ef09e76d11..bfbd6ca80e 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -2020,7 +2020,7 @@ bool CSharpScript::_update_exports() { for (int i = fields.size() - 1; i >= 0; i--) { GDMonoField *field = fields[i]; - if (_get_member_export(top, field, prop_info, exported)) { + if (_get_member_export(field, prop_info, exported)) { StringName name = field->get_name(); if (exported) { @@ -2041,7 +2041,7 @@ bool CSharpScript::_update_exports() { for (int i = properties.size() - 1; i >= 0; i--) { GDMonoProperty *property = properties[i]; - if (_get_member_export(top, property, prop_info, exported)) { + if (_get_member_export(property, prop_info, exported)) { StringName name = property->get_name(); if (exported) { @@ -2168,17 +2168,19 @@ bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Ve * Returns false if there was an error, otherwise true. * If there was an error, r_prop_info and r_exported are not assigned any value. */ -bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { +bool CSharpScript::_get_member_export(IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) { - StringName name = p_member->get_name(); + // Goddammit, C++. All I wanted was some nested functions. +#define MEMBER_FULL_QUALIFIED_NAME(m_member) \ + (m_member->get_enclosing_class()->get_full_name() + "." + (String)m_member->get_name()) if (p_member->is_static()) { if (p_member->has_attribute(CACHED_CLASS(ExportAttribute))) - ERR_PRINTS("Cannot export member because it is static: " + p_class->get_full_name() + "." + name.operator String()); + ERR_PRINTS("Cannot export member because it is static: " + MEMBER_FULL_QUALIFIED_NAME(p_member)); return false; } - if (member_info.has(name)) + if (member_info.has(p_member->get_name())) return false; ManagedType type; @@ -2191,19 +2193,22 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_ CRASH_NOW(); } - GDMonoMarshal::ExportInfo export_info; - Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type, &export_info); + Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type); if (!p_member->has_attribute(CACHED_CLASS(ExportAttribute))) { - r_prop_info = PropertyInfo(variant_type, name.operator String(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); + r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE); r_exported = false; return true; } if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) { GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member); - if (!property->has_getter() || !property->has_setter()) { - ERR_PRINTS("Cannot export property because it does not provide a getter or a setter: " + p_class->get_full_name() + "." + name.operator String()); + if (!property->has_getter()) { + ERR_PRINTS("Read-only property cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member)); + return false; + } + if (!property->has_setter()) { + ERR_PRINTS("Set-only property (without getter) cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member)); return false; } } @@ -2214,16 +2219,38 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_ String hint_string; if (variant_type == Variant::NIL) { - ERR_PRINTS("Unknown type of exported member: " + p_class->get_full_name() + "." + name.operator String()); + ERR_PRINTS("Unknown exported member type: " + MEMBER_FULL_QUALIFIED_NAME(p_member)); return false; - } else if (variant_type == Variant::INT && type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(type.type_class->get_mono_ptr())) { - // TODO: Move to ExportInfo? - variant_type = Variant::INT; - hint = PROPERTY_HINT_ENUM; + } + + int hint_res = _try_get_member_export_hint(p_member, type, variant_type, /* allow_generics: */ true, hint, hint_string); + + if (hint_res == -1) { + ERR_EXPLAIN("Error while trying to determine information about the exported member: " + MEMBER_FULL_QUALIFIED_NAME(p_member)); + ERR_FAIL_V(false); + } + + if (hint_res == 0) { + hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)); + hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); + } + + r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE); + r_exported = true; + + return true; + +#undef MEMBER_FULL_QUALIFIED_NAME +} + +int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string) { + + if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) { + r_hint = PROPERTY_HINT_ENUM; - Vector<MonoClassField *> fields = type.type_class->get_enum_fields(); + Vector<MonoClassField *> fields = p_type.type_class->get_enum_fields(); - MonoType *enum_basetype = mono_class_enum_basetype(type.type_class->get_mono_ptr()); + MonoType *enum_basetype = mono_class_enum_basetype(p_type.type_class->get_mono_ptr()); String name_only_hint_string; @@ -2236,12 +2263,12 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_ MonoClassField *field = fields[i]; if (i > 0) { - hint_string += ","; + r_hint_string += ","; name_only_hint_string += ","; } String enum_field_name = mono_field_get_name(field); - hint_string += enum_field_name; + r_hint_string += enum_field_name; name_only_hint_string += enum_field_name; // TODO: @@ -2251,54 +2278,73 @@ bool CSharpScript::_get_member_export(GDMonoClass *p_class, IMonoClassMember *p_ MonoObject *val_obj = mono_field_get_value_object(mono_domain_get(), field, NULL); if (val_obj == NULL) { - ERR_PRINTS("Failed to get '" + enum_field_name + "' constant enum value of exported member: " + - p_class->get_full_name() + "." + name.operator String()); - return false; + ERR_EXPLAIN("Failed to get '" + enum_field_name + "' constant enum value"); + ERR_FAIL_V(-1); } bool r_error; uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error); if (r_error) { - ERR_PRINTS("Failed to unbox '" + enum_field_name + "' constant enum value of exported member: " + - p_class->get_full_name() + "." + name.operator String()); - return false; + ERR_EXPLAIN("Failed to unbox '" + enum_field_name + "' constant enum value"); + ERR_FAIL_V(-1); } if (val != (unsigned int)i) { uses_default_values = false; } - hint_string += ":"; - hint_string += String::num_uint64(val); + r_hint_string += ":"; + r_hint_string += String::num_uint64(val); } if (uses_default_values) { // If we use the format NAME:VAL, that's what the editor displays. // That's annoying if the user is not using custom values for the enum constants. // This may not be needed in the future if the editor is changed to not display values. - hint_string = name_only_hint_string; + r_hint_string = name_only_hint_string; } - } else if (variant_type == Variant::OBJECT && CACHED_CLASS(GodotReference)->is_assignable_from(type.type_class)) { - GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(type.type_class); + } else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(GodotResource)->is_assignable_from(p_type.type_class)) { + GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class); CRASH_COND(field_native_class == NULL); - hint = PROPERTY_HINT_RESOURCE_TYPE; - hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class); - } else if (variant_type == Variant::ARRAY && export_info.array.element_type != Variant::NIL) { - String elem_type_str = itos(export_info.array.element_type); - hint = PROPERTY_HINT_TYPE_STRING; - hint_string = elem_type_str + "/" + elem_type_str + ":" + export_info.array.element_native_name; - } else if (variant_type == Variant::DICTIONARY && export_info.dictionary.key_type != Variant::NIL && export_info.dictionary.value_type != Variant::NIL) { - // TODO: There is no hint for this yet + r_hint = PROPERTY_HINT_RESOURCE_TYPE; + r_hint_string = NATIVE_GDMONOCLASS_NAME(field_native_class); + } else if (p_allow_generics && p_variant_type == Variant::ARRAY) { + // Nested arrays are not supported in the inspector + + ManagedType elem_type; + + if (!GDMonoMarshal::try_get_array_element_type(p_type, elem_type)) + return 0; + + Variant::Type elem_variant_type = GDMonoMarshal::managed_to_variant_type(elem_type); + + PropertyHint elem_hint = PROPERTY_HINT_NONE; + String elem_hint_string; + + if (elem_variant_type == Variant::NIL) { + ERR_EXPLAIN("Unknown array element type"); + ERR_FAIL_V(-1); + } + + int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string); + + if (hint_res == -1) { + ERR_EXPLAIN("Error while trying to determine information about the array element type"); + ERR_FAIL_V(-1); + } + + // Format: type/hint:hint_string + r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string; + r_hint = PROPERTY_HINT_TYPE_STRING; + + } else if (p_allow_generics && p_variant_type == Variant::DICTIONARY) { + // TODO: Dictionaries are not supported in the inspector } else { - hint = PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)); - hint_string = CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr); + return 0; } - r_prop_info = PropertyInfo(variant_type, name.operator String(), hint, hint_string, PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE); - r_exported = true; - - return true; + return 1; } #endif diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index fe4eed2e24..298d55c4df 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -127,7 +127,8 @@ class CSharpScript : public Script { bool _update_exports(); #ifdef TOOLS_ENABLED - bool _get_member_export(GDMonoClass *p_class, IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported); + bool _get_member_export(IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported); + static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string); #endif CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Variant::CallError &r_error); diff --git a/modules/mono/glue/Managed/Files/DynamicObject.cs b/modules/mono/glue/Managed/Files/DynamicObject.cs index 9860feafdd..a0f105d55e 100644 --- a/modules/mono/glue/Managed/Files/DynamicObject.cs +++ b/modules/mono/glue/Managed/Files/DynamicObject.cs @@ -202,7 +202,7 @@ namespace Godot //public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes); //public override bool TryDeleteMember(DeleteMemberBinder binder); - // Invokation on the object itself, e.g.: obj(param) + // Invocation on the object itself, e.g.: obj(param) //public override bool TryInvoke(InvokeBinder binder, object[] args, out object result); // No unnary operations to handle diff --git a/modules/mono/glue/Managed/Files/MarshalUtils.cs b/modules/mono/glue/Managed/Files/MarshalUtils.cs index 7e72b0edb5..730a1e1585 100644 --- a/modules/mono/glue/Managed/Files/MarshalUtils.cs +++ b/modules/mono/glue/Managed/Files/MarshalUtils.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; namespace Godot { @@ -8,29 +9,113 @@ namespace Godot static class MarshalUtils { + /// <summary> + /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> + /// is <see cref="Godot.Collections.Array{T}"/>; otherwise returns <see langword="false"/>. + /// </summary> + /// <exception cref="System.InvalidOperationException"> + /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. + /// </exception> static bool TypeIsGenericArray(Type type) { return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>); } + /// <summary> + /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/> + /// is <see cref="Godot.Collections.Dictionary{T}"/>; otherwise returns <see langword="false"/>. + /// </summary> + /// <exception cref="System.InvalidOperationException"> + /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false. + /// </exception> static bool TypeIsGenericDictionary(Type type) { return type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>); } - static void ArrayGetElementType(Type type, out Type elementType) + static void ArrayGetElementType(Type arrayType, out Type elementType) { - elementType = type.GetGenericArguments()[0]; + elementType = arrayType.GetGenericArguments()[0]; } - static void DictionaryGetKeyValueTypes(Type type, out Type keyType, out Type valueType) + static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) { - var genericArgs = type.GetGenericArguments(); - + var genericArgs = dictionaryType.GetGenericArguments(); keyType = genericArgs[0]; valueType = genericArgs[1]; } + static bool GenericIEnumerableIsAssignableFromType(Type type, out Type elementType) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + elementType = type.GetGenericArguments()[0]; + return true; + } + + foreach (var interfaceType in type.GetInterfaces()) + { + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + elementType = interfaceType.GetGenericArguments()[0]; + return true; + } + } + + Type baseType = type.BaseType; + + if (baseType == null) + { + elementType = null; + return false; + } + + return GenericIEnumerableIsAssignableFromType(baseType, out elementType); + } + + static bool GenericIDictionaryIsAssignableFromType(Type type, out Type keyType, out Type valueType) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + { + var genericArgs = type.GetGenericArguments(); + keyType = genericArgs[0]; + valueType = genericArgs[1]; + return true; + } + + foreach (var interfaceType in type.GetInterfaces()) + { + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) + { + var genericArgs = interfaceType.GetGenericArguments(); + keyType = genericArgs[0]; + valueType = genericArgs[1]; + return true; + } + } + + Type baseType = type.BaseType; + + if (baseType == null) + { + keyType = null; + valueType = null; + return false; + } + + return GenericIDictionaryIsAssignableFromType(baseType, out keyType, out valueType); + } + + static Type MakeGenericArrayType(Type elemType) + { + return typeof(Godot.Collections.Array<>).MakeGenericType(elemType); + } + + static Type MakeGenericDictionaryType(Type keyType, Type valueType) + { + return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, valueType); + } + // TODO Add support for IEnumerable<T> and IDictionary<TKey, TValue> // TODO: EnumerableToArray and IDictionaryToDictionary can be optimized @@ -64,5 +149,26 @@ namespace Godot Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, entry.Key, entry.Value); } } + + internal static void GenericIDictionaryToDictionary(object dictionary, IntPtr godotDictionaryPtr) + { +#if DEBUG + if (!GenericIDictionaryIsAssignableFromType(dictionary.GetType())) + throw new InvalidOperationException("The type does not implement IDictionary<,>"); +#endif + + // TODO: Can we optimize this? + + var keys = ((IEnumerable)dictionary.GetType().GetProperty("Keys").GetValue(dictionary)).GetEnumerator(); + var values = ((IEnumerable)dictionary.GetType().GetProperty("Values").GetValue(dictionary)).GetEnumerator(); + + while (keys.MoveNext() && values.MoveNext()) + { + object key = keys.Current; + object value = values.Current; + + Dictionary.godot_icall_Dictionary_Add(godotDictionaryPtr, key, value); + } + } } } diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp index 9779797d1a..2e79f87625 100644 --- a/modules/mono/mono_gd/gd_mono_field.cpp +++ b/modules/mono/mono_gd/gd_mono_field.cpp @@ -313,12 +313,32 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type()); + + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), + GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary)); mono_field_set_value(p_object, mono_field, managed); break; } + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), + GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); mono_field_set_value(p_object, mono_field, managed); @@ -432,26 +452,24 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ case MONO_TYPE_GENERICINST: { MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type()); - MonoException *exc = NULL; - - GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary); - MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - - if (is_dict) { + if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class); mono_field_set_value(p_object, mono_field, managed); break; } - exc = NULL; + if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class); + mono_field_set_value(p_object, mono_field, managed); + break; + } - GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray); - MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + // The order in which we check the following interfaces is very important (dictionaries and generics first) - if (is_array) { - MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class); + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), + GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); mono_field_set_value(p_object, mono_field, managed); break; } @@ -462,6 +480,14 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_ break; } + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { + MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), + GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); + mono_field_set_value(p_object, mono_field, managed); + break; + } + if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array)); mono_field_set_value(p_object, mono_field, managed); diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h index e348583370..a7727ddf34 100644 --- a/modules/mono/mono_gd/gd_mono_field.h +++ b/modules/mono/mono_gd/gd_mono_field.h @@ -47,9 +47,11 @@ class GDMonoField : public IMonoClassMember { MonoCustomAttrInfo *attributes; public: - virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_FIELD; } + virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; } - virtual StringName get_name() GD_FINAL { return name; } + virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_FIELD; } + + virtual StringName get_name() const GD_FINAL { return name; } virtual bool is_static() GD_FINAL; virtual Visibility get_visibility() GD_FINAL; diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp index d586b73cf9..461dcf3ec0 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.cpp +++ b/modules/mono/mono_gd/gd_mono_marshal.cpp @@ -35,7 +35,7 @@ namespace GDMonoMarshal { -Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_export_info) { +Variant::Type managed_to_variant_type(const ManagedType &p_type) { switch (p_type.type_encoding) { case MONO_TYPE_BOOLEAN: return Variant::BOOL; @@ -157,10 +157,24 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_e return Variant::ARRAY; } + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type()); + + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { + return Variant::DICTIONARY; + } + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { return Variant::DICTIONARY; } + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { + return Variant::ARRAY; + } + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { return Variant::ARRAY; } @@ -169,71 +183,96 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_e case MONO_TYPE_GENERICINST: { MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); - MonoException *exc = NULL; - GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary); - MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - - if (is_dict) { - if (r_export_info) { - MonoReflectionType *key_reftype; - MonoReflectionType *value_reftype; + if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { + return Variant::DICTIONARY; + } - exc = NULL; - invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes), - reftype, &key_reftype, &value_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { + return Variant::ARRAY; + } - ManagedType key_type = ManagedType::from_reftype(key_reftype); - ManagedType value_type = ManagedType::from_reftype(value_reftype); + // The order in which we check the following interfaces is very important (dictionaries and generics first) - r_export_info->dictionary.key_type = managed_to_variant_type(key_type); - r_export_info->dictionary.key_native_name = NATIVE_GDMONOCLASS_NAME(key_type.type_class); - r_export_info->dictionary.value_type = managed_to_variant_type(value_type); - r_export_info->dictionary.value_native_name = NATIVE_GDMONOCLASS_NAME(value_type.type_class); - } + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) + return Variant::DICTIONARY; + if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { return Variant::DICTIONARY; } - exc = NULL; - GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray); - MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) + return Variant::ARRAY; - if (is_array) { - if (r_export_info) { - MonoReflectionType *elem_reftype; + if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { + return Variant::ARRAY; + } + } break; + + default: { + } break; + } - exc = NULL; - invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType), - reftype, &elem_reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); + // Unknown + return Variant::NIL; +} - ManagedType elem_type = ManagedType::from_reftype(elem_reftype); +bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) { + switch (p_array_type.type_encoding) { + case MONO_TYPE_GENERICINST: { + MonoReflectionType *array_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_array_type.type_class->get_mono_type()); - r_export_info->array.element_type = managed_to_variant_type(elem_type); - r_export_info->array.element_native_name = NATIVE_GDMONOCLASS_NAME(elem_type.type_class); - } + if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype)) { + MonoReflectionType *elem_reftype; - return Variant::ARRAY; - } + GDMonoUtils::Marshal::array_get_element_type(array_reftype, &elem_reftype); - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - return Variant::DICTIONARY; + r_elem_type = ManagedType::from_reftype(elem_reftype); + return true; } - if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - return Variant::ARRAY; + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(array_reftype, &elem_reftype)) { + r_elem_type = ManagedType::from_reftype(elem_reftype); + return true; } } break; + default: { + } break; + } + + return false; +} +bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type) { + switch (p_dictionary_type.type_encoding) { + case MONO_TYPE_GENERICINST: { + MonoReflectionType *dict_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_dictionary_type.type_class->get_mono_type()); + + if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype)) { + MonoReflectionType *key_reftype; + MonoReflectionType *value_reftype; + + GDMonoUtils::Marshal::dictionary_get_key_value_types(dict_reftype, &key_reftype, &value_reftype); + + r_key_type = ManagedType::from_reftype(key_reftype); + r_value_type = ManagedType::from_reftype(value_reftype); + return true; + } + + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(dict_reftype, &key_reftype, &value_reftype)) { + r_key_type = ManagedType::from_reftype(key_reftype); + r_value_type = ManagedType::from_reftype(value_reftype); + return true; + } + } break; default: { } break; } - // Unknown - return Variant::NIL; + return false; } String mono_to_utf8_string(MonoString *p_mono_string) { @@ -502,10 +541,26 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); } + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type()); + + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), + GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); + } + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); } + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), + GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); + } + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); } @@ -603,28 +658,32 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty case MONO_TYPE_GENERICINST: { MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type()); - MonoException *exc = NULL; - GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary); - MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - - if (is_dict) { + if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class); } - exc = NULL; - GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray); - MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - - if (is_array) { + if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class); } + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { + return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), + GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype)); + } + if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary)); } + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { + return GDMonoUtils::create_managed_from(p_var->operator Array(), + GDMonoUtils::Marshal::make_generic_array_type(elem_reftype)); + } + if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array)); } @@ -787,66 +846,64 @@ Variant mono_object_to_variant(MonoObject *p_obj) { return ptr ? Variant(*ptr) : Variant(); } + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type()); + + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { + return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj); + } + if (type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - Dictionary dict; - MonoException *exc = NULL; - invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary), p_obj, &dict, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - return dict; + return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj); + } + + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { + return GDMonoUtils::Marshal::enumerable_to_array(p_obj); } if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - Array array; - MonoException *exc = NULL; - invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray), p_obj, &array, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - return array; + return GDMonoUtils::Marshal::enumerable_to_array(p_obj); } } break; case MONO_TYPE_GENERICINST: { MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type()); - MonoException *exc = NULL; - - GDMonoUtils::TypeIsGenericDictionary type_is_dict = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary); - MonoBoolean is_dict = invoke_method_thunk(type_is_dict, reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - - if (is_dict) { - exc = NULL; + if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) { + MonoException *exc = NULL; MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); return *unbox<Dictionary *>(ret); } - exc = NULL; - - GDMonoUtils::TypeIsGenericArray type_is_array = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray); - MonoBoolean is_array = invoke_method_thunk(type_is_array, reftype, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - - if (is_array) { - exc = NULL; + if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) { + MonoException *exc = NULL; MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc); UNLIKELY_UNHANDLED_EXCEPTION(exc); return *unbox<Array *>(ret); } + // The order in which we check the following interfaces is very important (dictionaries and generics first) + + MonoReflectionType *key_reftype, *value_reftype; + if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) { + return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj); + } + if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IDictionary))) { - Dictionary dict; - exc = NULL; - invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary), p_obj, &dict, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - return dict; + return GDMonoUtils::Marshal::idictionary_to_dictionary(p_obj); + } + + MonoReflectionType *elem_reftype; + if (GDMonoUtils::Marshal::generic_ienumerable_is_assignable_from(reftype, &elem_reftype)) { + return GDMonoUtils::Marshal::enumerable_to_array(p_obj); } if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) { - Array array; - exc = NULL; - invoke_method_thunk(CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray), p_obj, &array, &exc); - UNLIKELY_UNHANDLED_EXCEPTION(exc); - return array; + return GDMonoUtils::Marshal::enumerable_to_array(p_obj); } } break; } @@ -1075,4 +1132,5 @@ PoolVector3Array mono_array_to_PoolVector3Array(MonoArray *p_array) { return ret; } + } // namespace GDMonoMarshal diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h index 8d3fd4b349..3fa958ac32 100644 --- a/modules/mono/mono_gd/gd_mono_marshal.h +++ b/modules/mono/mono_gd/gd_mono_marshal.h @@ -57,28 +57,10 @@ T unbox(MonoObject *p_obj) { #define BOX_PTR(x) mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(IntPtr), x) #define BOX_ENUM(m_enum_class, x) mono_value_box(mono_domain_get(), m_enum_class, &x) -// FIXME: Made this struct in a hurry. It could be done differently. -struct ExportInfo { - struct ArrayInfo { - Variant::Type element_type; - String element_native_name; - - ArrayInfo() : - element_type(Variant::NIL) {} - } array; - struct DictionaryInfo { - Variant::Type key_type; - String key_native_name; - Variant::Type value_type; - String value_native_name; - - DictionaryInfo() : - key_type(Variant::NIL), - value_type(Variant::NIL) {} - } dictionary; -}; +Variant::Type managed_to_variant_type(const ManagedType &p_type); -Variant::Type managed_to_variant_type(const ManagedType &p_type, ExportInfo *r_export_info = NULL); +bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type); +bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type); // String diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp index 7f11e4671d..f290c6c8ac 100644 --- a/modules/mono/mono_gd/gd_mono_method.cpp +++ b/modules/mono/mono_gd/gd_mono_method.cpp @@ -74,6 +74,10 @@ void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) { method_info = MethodInfo(); } +GDMonoClass *GDMonoMethod::get_enclosing_class() const { + return GDMono::get_singleton()->get_class(mono_method_get_class(mono_method)); +} + bool GDMonoMethod::is_static() { return mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_STATIC; } diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h index f74cef438d..2fc8628f27 100644 --- a/modules/mono/mono_gd/gd_mono_method.h +++ b/modules/mono/mono_gd/gd_mono_method.h @@ -57,9 +57,11 @@ class GDMonoMethod : public IMonoClassMember { MonoMethod *mono_method; public: - virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_METHOD; } + virtual GDMonoClass *get_enclosing_class() const GD_FINAL; - virtual StringName get_name() GD_FINAL { return name; } + virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_METHOD; } + + virtual StringName get_name() const GD_FINAL { return name; } virtual bool is_static() GD_FINAL; diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h index 2700c460b0..d6efa60412 100644 --- a/modules/mono/mono_gd/gd_mono_property.h +++ b/modules/mono/mono_gd/gd_mono_property.h @@ -47,9 +47,11 @@ class GDMonoProperty : public IMonoClassMember { MonoCustomAttrInfo *attributes; public: - virtual MemberType get_member_type() GD_FINAL { return MEMBER_TYPE_PROPERTY; } + virtual GDMonoClass *get_enclosing_class() const GD_FINAL { return owner; } - virtual StringName get_name() GD_FINAL { return name; } + virtual MemberType get_member_type() const GD_FINAL { return MEMBER_TYPE_PROPERTY; } + + virtual StringName get_name() const GD_FINAL { return name; } virtual bool is_static() GD_FINAL; virtual Visibility get_visibility() GD_FINAL; diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp index bcf5712d16..5236e43c90 100644 --- a/modules/mono/mono_gd/gd_mono_utils.cpp +++ b/modules/mono/mono_gd/gd_mono_utils.cpp @@ -109,7 +109,7 @@ void MonoCache::clear_members() { class_NodePath = NULL; class_RID = NULL; class_GodotObject = NULL; - class_GodotReference = NULL; + class_GodotResource = NULL; class_Node = NULL; class_Control = NULL; class_Spatial = NULL; @@ -151,12 +151,25 @@ void MonoCache::clear_members() { methodthunk_SignalAwaiter_FailureCallback = NULL; methodthunk_GodotTaskScheduler_Activate = NULL; + // Start of MarshalUtils methods + methodthunk_MarshalUtils_TypeIsGenericArray = NULL; methodthunk_MarshalUtils_TypeIsGenericDictionary = NULL; + methodthunk_MarshalUtils_ArrayGetElementType = NULL; methodthunk_MarshalUtils_DictionaryGetKeyValueTypes = NULL; + + methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType = NULL; + methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType = NULL; + + methodthunk_MarshalUtils_MakeGenericArrayType = NULL; + methodthunk_MarshalUtils_MakeGenericDictionaryType = NULL; + methodthunk_MarshalUtils_EnumerableToArray = NULL; methodthunk_MarshalUtils_IDictionaryToDictionary = NULL; + methodthunk_MarshalUtils_GenericIDictionaryToDictionary = NULL; + + // End of MarshalUtils methods task_scheduler_handle = Ref<MonoGCHandle>(); } @@ -217,7 +230,7 @@ void update_godot_api_cache() { CACHE_CLASS_AND_CHECK(NodePath, GODOT_API_CLASS(NodePath)); CACHE_CLASS_AND_CHECK(RID, GODOT_API_CLASS(RID)); CACHE_CLASS_AND_CHECK(GodotObject, GODOT_API_CLASS(Object)); - CACHE_CLASS_AND_CHECK(GodotReference, GODOT_API_CLASS(Reference)); + CACHE_CLASS_AND_CHECK(GodotResource, GODOT_API_CLASS(Resource)); CACHE_CLASS_AND_CHECK(Node, GODOT_API_CLASS(Node)); CACHE_CLASS_AND_CHECK(Control, GODOT_API_CLASS(Control)); CACHE_CLASS_AND_CHECK(Spatial, GODOT_API_CLASS(Spatial)); @@ -258,12 +271,28 @@ void update_godot_api_cache() { CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method_thunk("FailureCallback", 0)); CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method_thunk("Activate", 0)); + // Start of MarshalUtils methods + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericArray, (TypeIsGenericArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericArray", 1)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericDictionary, (TypeIsGenericDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("TypeIsGenericDictionary", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3)); + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, (ArrayGetElementType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("ArrayGetElementType", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, (DictionaryGetKeyValueTypes)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("DictionaryGetKeyValueTypes", 3)); + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIEnumerableIsAssignableFromType, (GenericIEnumerableIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIEnumerableIsAssignableFromType", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryIsAssignableFromType, (GenericIDictionaryIsAssignableFromType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryIsAssignableFromType", 3)); + + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericArrayType, (MakeGenericArrayType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericArrayType", 1)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, MakeGenericDictionaryType, (MakeGenericDictionaryType)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("MakeGenericDictionaryType", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, EnumerableToArray, (EnumerableToArray)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("EnumerableToArray", 2)); CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, IDictionaryToDictionary, (IDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("IDictionaryToDictionary", 2)); + CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GenericIDictionaryToDictionary, (GenericIDictionaryToDictionary)GODOT_API_CLASS(MarshalUtils)->get_method_thunk("GenericIDictionaryToDictionary", 2)); + + // End of MarshalUtils methods #ifdef DEBUG_ENABLED CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method_thunk("GetStackFrameInfo", 4)); @@ -727,4 +756,99 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc) { invoke_method_thunk(CACHED_METHOD_THUNK(GodotObject, Dispose), p_mono_object, r_exc); } +namespace Marshal { + +MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype) { + TypeIsGenericArray thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray); + MonoException *exc = NULL; + MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return res; +} + +MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype) { + TypeIsGenericDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary); + MonoException *exc = NULL; + MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return res; +} + +void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) { + ArrayGetElementType thunk = CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType); + MonoException *exc = NULL; + invoke_method_thunk(thunk, p_array_reftype, r_elem_reftype, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); +} + +void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { + DictionaryGetKeyValueTypes thunk = CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes); + MonoException *exc = NULL; + invoke_method_thunk(thunk, p_dict_reftype, r_key_reftype, r_value_reftype, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); +} + +MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) { + GenericIEnumerableIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType); + MonoException *exc = NULL; + MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_elem_reftype, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return res; +} + +MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) { + GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType); + MonoException *exc = NULL; + MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_key_reftype, r_value_reftype, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return res; +} + +Array enumerable_to_array(MonoObject *p_enumerable) { + Array result; + EnumerableToArray thunk = CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray); + MonoException *exc = NULL; + invoke_method_thunk(thunk, p_enumerable, &result, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return result; +} + +Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) { + Dictionary result; + IDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary); + MonoException *exc = NULL; + invoke_method_thunk(thunk, p_idictionary, &result, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return result; +} + +Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) { + Dictionary result; + GenericIDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary); + MonoException *exc = NULL; + invoke_method_thunk(thunk, p_generic_idictionary, &result, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return result; +} + +GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) { + MakeGenericArrayType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType); + MonoException *exc = NULL; + MonoReflectionType *reftype = invoke_method_thunk(thunk, p_elem_reftype, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype))); +} + +GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) { + MakeGenericDictionaryType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType); + MonoException *exc = NULL; + MonoReflectionType *reftype = invoke_method_thunk(thunk, p_key_reftype, p_value_reftype, &exc); + UNLIKELY_UNHANDLED_EXCEPTION(exc); + return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype))); +} + +} // namespace Marshal + +// namespace Marshal + } // namespace GDMonoUtils diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h index 87610e286c..081a8a9813 100644 --- a/modules/mono/mono_gd/gd_mono_utils.h +++ b/modules/mono/mono_gd/gd_mono_utils.h @@ -60,10 +60,41 @@ typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, Mo typedef MonoBoolean (*TypeIsGenericArray)(MonoReflectionType *, MonoException **); typedef MonoBoolean (*TypeIsGenericDictionary)(MonoReflectionType *, MonoException **); -typedef MonoBoolean (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **); -typedef MonoBoolean (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **); + +typedef void (*ArrayGetElementType)(MonoReflectionType *, MonoReflectionType **, MonoException **); +typedef void (*DictionaryGetKeyValueTypes)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **); + +typedef MonoBoolean (*GenericIEnumerableIsAssignableFromType)(MonoReflectionType *, MonoReflectionType **, MonoException **); +typedef MonoBoolean (*GenericIDictionaryIsAssignableFromType)(MonoReflectionType *, MonoReflectionType **, MonoReflectionType **, MonoException **); + +typedef MonoReflectionType *(*MakeGenericArrayType)(MonoReflectionType *, MonoException **); +typedef MonoReflectionType *(*MakeGenericDictionaryType)(MonoReflectionType *, MonoReflectionType *, MonoException **); + typedef void (*EnumerableToArray)(MonoObject *, Array *, MonoException **); typedef void (*IDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **); +typedef void (*GenericIDictionaryToDictionary)(MonoObject *, Dictionary *, MonoException **); + +namespace Marshal { + +MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype); +MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype); + +void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype); +void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype); + +MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype); +MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype); + +GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype); +GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype); + +Array enumerable_to_array(MonoObject *p_enumerable); +Dictionary idictionary_to_dictionary(MonoObject *p_idictionary); +Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary); + +} // namespace Marshal + +// End of MarshalUtils methods struct MonoCache { @@ -114,7 +145,7 @@ struct MonoCache { GDMonoClass *class_NodePath; GDMonoClass *class_RID; GDMonoClass *class_GodotObject; - GDMonoClass *class_GodotReference; + GDMonoClass *class_GodotResource; GDMonoClass *class_Node; GDMonoClass *class_Control; GDMonoClass *class_Spatial; @@ -156,12 +187,25 @@ struct MonoCache { SignalAwaiter_FailureCallback methodthunk_SignalAwaiter_FailureCallback; GodotTaskScheduler_Activate methodthunk_GodotTaskScheduler_Activate; + // Start of MarshalUtils methods + TypeIsGenericArray methodthunk_MarshalUtils_TypeIsGenericArray; TypeIsGenericDictionary methodthunk_MarshalUtils_TypeIsGenericDictionary; + ArrayGetElementType methodthunk_MarshalUtils_ArrayGetElementType; DictionaryGetKeyValueTypes methodthunk_MarshalUtils_DictionaryGetKeyValueTypes; + + GenericIEnumerableIsAssignableFromType methodthunk_MarshalUtils_GenericIEnumerableIsAssignableFromType; + GenericIDictionaryIsAssignableFromType methodthunk_MarshalUtils_GenericIDictionaryIsAssignableFromType; + + MakeGenericArrayType methodthunk_MarshalUtils_MakeGenericArrayType; + MakeGenericDictionaryType methodthunk_MarshalUtils_MakeGenericDictionaryType; + EnumerableToArray methodthunk_MarshalUtils_EnumerableToArray; IDictionaryToDictionary methodthunk_MarshalUtils_IDictionaryToDictionary; + GenericIDictionaryToDictionary methodthunk_MarshalUtils_GenericIDictionaryToDictionary; + + // End of MarshalUtils methods Ref<MonoGCHandle> task_scheduler_handle; diff --git a/modules/mono/mono_gd/i_mono_class_member.h b/modules/mono/mono_gd/i_mono_class_member.h index 553d9edc72..f4de4e3230 100644 --- a/modules/mono/mono_gd/i_mono_class_member.h +++ b/modules/mono/mono_gd/i_mono_class_member.h @@ -53,9 +53,11 @@ public: virtual ~IMonoClassMember() {} - virtual MemberType get_member_type() = 0; + virtual GDMonoClass *get_enclosing_class() const = 0; - virtual StringName get_name() = 0; + virtual MemberType get_member_type() const = 0; + + virtual StringName get_name() const = 0; virtual bool is_static() = 0; diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index f45644f1eb..b987b3aebd 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -617,7 +617,9 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { String dst_path = p_path.replace_first("res://", "assets/"); store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0); - ed->ep->step("File: " + p_path, 3 + p_file * 100 / p_total); + if (ed->ep->step("File: " + p_path, 3 + p_file * 100 / p_total)) { + return ERR_SKIP; + } return OK; } @@ -1255,7 +1257,9 @@ public: } //export_temp - ep.step("Exporting APK", 0); + if (ep.step("Exporting APK", 0)) { + return ERR_SKIP; + } const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); const bool use_reverse = devices[p_device].api_level >= 21; @@ -1278,7 +1282,9 @@ public: String package_name = p_preset->get("package/unique_name"); if (remove_prev) { - ep.step("Uninstalling...", 1); + if (ep.step("Uninstalling...", 1)) { + return ERR_SKIP; + } print_line("Uninstalling previous version: " + devices[p_device].name); @@ -1291,7 +1297,9 @@ public: } print_line("Installing to device (please wait...): " + devices[p_device].name); - ep.step("Installing to device (please wait...)", 2); + if (ep.step("Installing to device (please wait...)", 2)) { + return ERR_SKIP; + } args.clear(); args.push_back("-s"); @@ -1357,7 +1365,9 @@ public: } } - ep.step("Running on Device...", 3); + if (ep.step("Running on Device...", 3)) { + return ERR_SKIP; + } args.clear(); args.push_back("-s"); args.push_back(devices[p_device].id); @@ -1763,7 +1773,7 @@ public: String src_apk; - EditorProgress ep("export", "Exporting for Android", 105); + EditorProgress ep("export", "Exporting for Android", 105, true); if (bool(p_preset->get("custom_package/use_custom_build"))) { //custom build //re-generate build.gradle and AndroidManifest.xml @@ -1855,7 +1865,9 @@ public: FileAccess *src_f = NULL; zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - ep.step("Creating APK", 0); + if (ep.step("Creating APK", 0)) { + return ERR_SKIP; + } unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io); if (!pkg) { @@ -1997,7 +2009,9 @@ public: ret = unzGoToNextFile(pkg); } - ep.step("Adding Files...", 1); + if (ep.step("Adding Files...", 1)) { + return ERR_SKIP; + } Error err = OK; Vector<String> cl = cmdline.strip_edges().split(" "); for (int i = 0; i < cl.size(); i++) { @@ -2135,14 +2149,18 @@ public: user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); } - ep.step("Signing debug APK...", 103); + if (ep.step("Signing debug APK...", 103)) { + return ERR_SKIP; + } } else { keystore = release_keystore; password = release_password; user = release_username; - ep.step("Signing release APK...", 103); + if (ep.step("Signing release APK...", 103)) { + return ERR_SKIP; + } } if (!FileAccess::exists(keystore)) { @@ -2174,7 +2192,9 @@ public: return ERR_CANT_CREATE; } - ep.step("Verifying APK...", 104); + if (ep.step("Verifying APK...", 104)) { + return ERR_SKIP; + } args.clear(); args.push_back("-verify"); @@ -2194,7 +2214,9 @@ public: static const int ZIP_ALIGNMENT = 4; - ep.step("Aligning APK...", 105); + if (ep.step("Aligning APK...", 105)) { + return ERR_SKIP; + } unzFile tmp_unaligned = unzOpen2(unaligned_path.utf8().get_data(), &io); if (!tmp_unaligned) { diff --git a/platform/android/java/AndroidManifest.xml b/platform/android/java/AndroidManifest.xml index 613d24fbd2..9997950137 100644 --- a/platform/android/java/AndroidManifest.xml +++ b/platform/android/java/AndroidManifest.xml @@ -22,7 +22,7 @@ <!--Anything in this line after the icon will be erased when doing custom build. If you want to add tags manually, do before it.--> <application android:label="@string/godot_project_name_string" android:allowBackup="false" tools:ignore="GoogleAppIndexingWarning" android:icon="@drawable/icon"> -<!--The following values are replaced when Godot exports, modifying them here has no effect. Do theses changes in the--> +<!--The following values are replaced when Godot exports, modifying them here has no effect. Do these changes in the--> <!--export preset. Adding new ones is fine.--> <activity android:name="org.godotengine.godot.Godot" diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index 3a03294b08..f99935bf7c 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -35,7 +35,7 @@ #include <jni.h> // These functions can be called from within JAVA and are the means by which our JAVA implementation calls back into our C++ code. -// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes thats why we have the long names) +// See java/src/org/godotengine/godot/GodotLib.java for the JAVA side of this (yes that's why we have the long names) extern "C" { JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *env, jobject obj, jobject activity, jobject p_asset_manager, jboolean p_use_apk_expansion); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env); diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index e1083aeefc..ba405ab7ae 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -819,7 +819,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p String dest_dir = p_path.get_base_dir() + "/"; String binary_name = p_path.get_file().get_basename(); - EditorProgress ep("export", "Exporting for iOS", 5); + EditorProgress ep("export", "Exporting for iOS", 5, true); String team_id = p_preset->get("application/app_store_team_id"); ERR_EXPLAIN("App Store Team ID not specified - cannot configure the project."); @@ -868,14 +868,18 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p memdelete(da); } - ep.step("Making .pck", 0); + if (ep.step("Making .pck", 0)) { + return ERR_SKIP; + } String pack_path = dest_dir + binary_name + ".pck"; Vector<SharedObject> libraries; Error err = save_pack(p_preset, pack_path, &libraries); if (err) return err; - ep.step("Extracting and configuring Xcode project", 1); + if (ep.step("Extracting and configuring Xcode project", 1)) { + return ERR_SKIP; + } String library_to_use = "libgodot.iphone." + String(p_debug ? "debug" : "release") + ".fat.a"; @@ -1053,7 +1057,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p memdelete(f); #ifdef OSX_ENABLED - ep.step("Code-signing dylibs", 2); + if (ep.step("Code-signing dylibs", 2)) { + return ERR_SKIP; + } DirAccess *dylibs_dir = DirAccess::open(dest_dir + binary_name + "/dylibs"); ERR_FAIL_COND_V(!dylibs_dir, ERR_CANT_OPEN); CodesignData codesign_data(p_preset, p_debug); @@ -1061,7 +1067,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p memdelete(dylibs_dir); ERR_FAIL_COND_V(err, err); - ep.step("Making .xcarchive", 3); + if (ep.step("Making .xcarchive", 3)) { + return ERR_SKIP; + } String archive_path = p_path.get_basename() + ".xcarchive"; List<String> archive_args; archive_args.push_back("-project"); @@ -1080,7 +1088,9 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p err = OS::get_singleton()->execute("xcodebuild", archive_args, true); ERR_FAIL_COND_V(err, err); - ep.step("Making .ipa", 4); + if (ep.step("Making .ipa", 4)) { + return ERR_SKIP; + } List<String> export_args; export_args.push_back("-exportArchive"); export_args.push_back("-archivePath"); diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 34781ce365..2e3e10e222 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -1035,7 +1035,7 @@ void OS_JavaScript::finalize() { // Miscellaneous -Error OS_JavaScript::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) { +Error OS_JavaScript::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { ERR_EXPLAIN("OS::execute() is not available on the HTML5 platform"); ERR_FAIL_V(ERR_UNAVAILABLE); diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index a9f9e23463..f7ce28e660 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -137,7 +137,7 @@ public: void run_async(); bool main_loop_iterate(); - virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false); + virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL); virtual Error kill(const ProcessID &p_pid); virtual int get_process_id() const; diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index 89212b0e5e..9dabbb12fc 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -121,7 +121,7 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png,*.icns"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); @@ -409,7 +409,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p String src_pkg_name; - EditorProgress ep("export", "Exporting for OSX", 3); + EditorProgress ep("export", "Exporting for OSX", 3, true); if (p_debug) src_pkg_name = p_preset->get("custom_package/debug"); @@ -432,7 +432,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p FileAccess *src_f = NULL; zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - ep.step("Creating app", 0); + if (ep.step("Creating app", 0)) { + return ERR_SKIP; + } unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); if (!src_pkg_zip) { @@ -542,11 +544,21 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p iconpath = ProjectSettings::get_singleton()->get("application/config/icon"); if (iconpath != "") { - Ref<Image> icon; - icon.instance(); - icon->load(iconpath); - if (!icon->empty()) { - _make_icon(icon, data); + if (iconpath.get_extension() == "icns") { + FileAccess *icon = FileAccess::open(iconpath, FileAccess::READ); + if (icon) { + data.resize(icon->get_len()); + icon->get_buffer(&data.write[0], icon->get_len()); + icon->close(); + memdelete(icon); + } + } else { + Ref<Image> icon; + icon.instance(); + icon->load(iconpath); + if (!icon->empty()) { + _make_icon(icon, data); + } } } //bleh? @@ -616,7 +628,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p } if (err == OK) { - ep.step("Making PKG", 1); + if (ep.step("Making PKG", 1)) { + return ERR_SKIP; + } if (export_format == "dmg") { String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; @@ -638,7 +652,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p } if (err == OK && identity != "") { - ep.step("Code signing bundle", 2); + if (ep.step("Code signing bundle", 2)) { + return ERR_SKIP; + } // the order in which we code sign is important, this is a bit of a shame or we could do this in our loop that extracts the files from our ZIP @@ -663,7 +679,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p // and finally create a DMG if (err == OK) { - ep.step("Making DMG", 3); + if (ep.step("Making DMG", 3)) { + return ERR_SKIP; + } err = _create_dmg(p_path, pkg_name, tmp_app_path_name); } diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index a0ab398f89..cdcad33f6d 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -187,7 +187,7 @@ class AppxPackager { public: void set_progress_task(String p_task) { progress_task = p_task; } void init(FileAccess *p_fa); - void add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress = false); + Error add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress = false); void finish(); AppxPackager(); @@ -468,10 +468,12 @@ void AppxPackager::init(FileAccess *p_fa) { tmp_content_types_file_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpcontenttypes.xml"); } -void AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress) { +Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress) { if (p_file_no >= 1 && p_total_files >= 1) { - EditorNode::progress_task_step(progress_task, "File: " + p_file_name, (p_file_no * 100) / p_total_files); + if (EditorNode::progress_task_step(progress_task, "File: " + p_file_name, (p_file_no * 100) / p_total_files)) { + return ERR_SKIP; + } } FileMeta meta; @@ -584,6 +586,8 @@ void AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t package->store_buffer(file_buffer.ptr(), file_buffer.size()); file_metadata.push_back(meta); + + return OK; } void AppxPackager::finish() { @@ -1008,9 +1012,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform { AppxPackager *packager = (AppxPackager *)p_userdata; String dst_path = p_path.replace_first("res://", "game/"); - packager->add_file(dst_path, p_data.ptr(), p_data.size(), p_file, p_total, _should_compress_asset(p_path, p_data)); - - return OK; + return packager->add_file(dst_path, p_data.ptr(), p_data.size(), p_file, p_total, _should_compress_asset(p_path, p_data)); } public: @@ -1230,7 +1232,7 @@ public: String src_appx; - EditorProgress ep("export", "Exporting for Windows Universal", 7); + EditorProgress ep("export", "Exporting for Windows Universal", 7, true); if (p_debug) src_appx = p_preset->get("custom_template/debug"); @@ -1280,7 +1282,9 @@ public: FileAccess *src_f = NULL; zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - ep.step("Creating package...", 0); + if (ep.step("Creating package...", 0)) { + return ERR_SKIP; + } unzFile pkg = unzOpen2(src_appx.utf8().get_data(), &io); @@ -1292,7 +1296,9 @@ public: int ret = unzGoToFirstFile(pkg); - ep.step("Copying template files...", 1); + if (ep.step("Copying template files...", 1)) { + return ERR_SKIP; + } EditorNode::progress_add_task("template_files", "Template files", 100); packager.set_progress_task("template_files"); @@ -1341,14 +1347,19 @@ public: print_line("ADDING: " + path); - packager.add_file(path, data.ptr(), data.size(), template_file_no++, template_files_amount, _should_compress_asset(path, data)); + err = packager.add_file(path, data.ptr(), data.size(), template_file_no++, template_files_amount, _should_compress_asset(path, data)); + if (err != OK) { + return err; + } ret = unzGoToNextFile(pkg); } EditorNode::progress_end_task("template_files"); - ep.step("Creating command line...", 2); + if (ep.step("Creating command line...", 2)) { + return ERR_SKIP; + } Vector<String> cl = ((String)p_preset->get("command_line/extra_args")).strip_edges().split(" "); for (int i = 0; i < cl.size(); i++) { @@ -1382,9 +1393,14 @@ public: print_line(itos(i) + " param: " + cl[i]); } - packager.add_file("__cl__.cl", clf.ptr(), clf.size(), -1, -1, false); + err = packager.add_file("__cl__.cl", clf.ptr(), clf.size(), -1, -1, false); + if (err != OK) { + return err; + } - ep.step("Adding project files...", 3); + if (ep.step("Adding project files...", 3)) { + return ERR_SKIP; + } EditorNode::progress_add_task("project_files", "Project Files", 100); packager.set_progress_task("project_files"); @@ -1393,7 +1409,9 @@ public: EditorNode::progress_end_task("project_files"); - ep.step("Closing package...", 7); + if (ep.step("Closing package...", 7)) { + return ERR_SKIP; + } unzClose(pkg); diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index 82f09032f5..1678d351b3 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -713,7 +713,7 @@ void OS_UWP::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c // TODO } -Error OS_UWP::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) { +Error OS_UWP::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { return FAILED; }; diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index 00f79efb04..7b00224017 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -208,7 +208,7 @@ public: virtual void delay_usec(uint32_t p_usec) const; virtual uint64_t get_ticks_usec() const; - virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false); + virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL); virtual Error kill(const ProcessID &p_pid); virtual bool has_environment(const String &p_var) const; diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 0b61770d87..c386fed367 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -705,7 +705,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } } } else if (mouse_mode != MOUSE_MODE_CAPTURED) { - // for reasons unknown to mankind, wheel comes in screen cordinates + // for reasons unknown to mankind, wheel comes in screen coordinates POINT coords; coords.x = mb->get_position().x; coords.y = mb->get_position().y; diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp index 721b52edaa..ff27e0a29a 100644 --- a/scene/2d/cpu_particles_2d.cpp +++ b/scene/2d/cpu_particles_2d.cpp @@ -978,7 +978,7 @@ void CPUParticles2D::_notification(int p_what) { if (p_what == NOTIFICATION_DRAW) { if (!redraw) - return; // dont add to render list + return; // don't add to render list RID texrid; if (texture.is_valid()) { diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index b321bcf3ce..3dc0f46676 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -491,7 +491,7 @@ void TileMap::update_dirty_quadrants() { shape_idx++; #ifdef DEBUG_ENABLED } else { - print_error("The TileSet asigned to the TileMap " + get_name() + " has an invalid convex shape."); + print_error("The TileSet assigned to the TileMap " + get_name() + " has an invalid convex shape."); #endif } } diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp index 15c089ec10..e192e040f2 100644 --- a/scene/3d/skeleton.cpp +++ b/scene/3d/skeleton.cpp @@ -162,7 +162,7 @@ void Skeleton::_update_process_order() { //now check process order int pass_count = 0; while (pass_count < len * len) { - //using bubblesort because of simplicity, it wont run every frame though. + //using bubblesort because of simplicity, it won't run every frame though. //bublesort worst case is O(n^2), and this may be an infinite loop if cyclic bool swapped = false; for (int i = 0; i < len; i++) { diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index ead3516116..a5955d57f7 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -1200,7 +1200,7 @@ void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float // Animation reset BUT played backwards, set position to the end c.current.pos = c.current.from->animation->get_length(); } else if (!p_from_end && c.current.pos == c.current.from->animation->get_length()) { - // Animation resumed but already ended, set position to the beggining + // Animation resumed but already ended, set position to the beginning c.current.pos = 0; } } diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp index 4da3e6ee28..06d6806f03 100644 --- a/scene/animation/skeleton_ik.cpp +++ b/scene/animation/skeleton_ik.cpp @@ -37,20 +37,20 @@ #ifndef _3D_DISABLED FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) { - for (int i = childs.size() - 1; 0 <= i; --i) { - if (p_bone_id == childs[i].bone) { - return &childs.write[i]; + for (int i = children.size() - 1; 0 <= i; --i) { + if (p_bone_id == children[i].bone) { + return &children.write[i]; } } return NULL; } FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child(const BoneId p_bone_id) { - const int infant_child_id = childs.size(); - childs.resize(infant_child_id + 1); - childs.write[infant_child_id].bone = p_bone_id; - childs.write[infant_child_id].parent_item = this; - return &childs.write[infant_child_id]; + const int infant_child_id = children.size(); + children.resize(infant_child_id + 1); + children.write[infant_child_id].bone = p_bone_id; + children.write[infant_child_id].parent_item = this; + return &children.write[infant_child_id]; } /// Build a chain that starts from the root to tip @@ -144,8 +144,8 @@ void FabrikInverseKinematic::update_chain(const Skeleton *p_sk, ChainItem *p_cha p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone); p_chain_item->current_pos = p_chain_item->initial_transform.origin; - for (int i = p_chain_item->childs.size() - 1; 0 <= i; --i) { - update_chain(p_sk, &p_chain_item->childs.write[i]); + for (int i = p_chain_item->children.size() - 1; 0 <= i; --i) { + update_chain(p_sk, &p_chain_item->children.write[i]); } } @@ -210,9 +210,9 @@ void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_ while (sub_chain_root) { // Reach the tip sub_chain_root->current_pos = origin; - if (!sub_chain_root->childs.empty()) { + if (!sub_chain_root->children.empty()) { - ChainItem &child(sub_chain_root->childs.write[0]); + ChainItem &child(sub_chain_root->children.write[0]); // Is not tip // So calculate next origin location @@ -302,10 +302,10 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove Transform new_bone_pose(ci->initial_transform); new_bone_pose.origin = ci->current_pos; - if (!ci->childs.empty()) { + if (!ci->children.empty()) { /// Rotate basis - const Vector3 initial_ori((ci->childs[0].initial_transform.origin - ci->initial_transform.origin).normalized()); + const Vector3 initial_ori((ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized()); const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized()); if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) { @@ -322,8 +322,8 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove p_task->skeleton->set_bone_global_pose(ci->bone, new_bone_pose); - if (!ci->childs.empty()) - ci = &ci->childs.write[0]; + if (!ci->children.empty()) + ci = &ci->children.write[0]; else ci = NULL; } diff --git a/scene/animation/skeleton_ik.h b/scene/animation/skeleton_ik.h index 228184a2df..5d48b7be33 100644 --- a/scene/animation/skeleton_ik.h +++ b/scene/animation/skeleton_ik.h @@ -49,7 +49,7 @@ class FabrikInverseKinematic { struct ChainItem { - Vector<ChainItem> childs; + Vector<ChainItem> children; ChainItem *parent_item; // Bone info diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp index 144e58d8b9..2def9fe8fc 100644 --- a/scene/audio/audio_stream_player.cpp +++ b/scene/audio/audio_stream_player.cpp @@ -32,8 +32,7 @@ #include "core/engine.h" - -void AudioStreamPlayer::_mix_to_bus(const AudioFrame *p_frames,int p_amount) { +void AudioStreamPlayer::_mix_to_bus(const AudioFrame *p_frames, int p_amount) { int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); @@ -66,10 +65,8 @@ void AudioStreamPlayer::_mix_to_bus(const AudioFrame *p_frames,int p_amount) { } } - void AudioStreamPlayer::_mix_internal(bool p_fadeout) { - //get data AudioFrame *buffer = mix_buffer.ptrw(); int buffer_size = mix_buffer.size(); @@ -94,15 +91,14 @@ void AudioStreamPlayer::_mix_internal(bool p_fadeout) { //set volume for next mix mix_volume_db = target_volume; - _mix_to_bus(buffer,buffer_size); - + _mix_to_bus(buffer, buffer_size); } void AudioStreamPlayer::_mix_audio() { if (use_fadeout) { - _mix_to_bus(fadeout_buffer.ptr(),fadeout_buffer.size()); - use_fadeout=false; + _mix_to_bus(fadeout_buffer.ptr(), fadeout_buffer.size()); + use_fadeout = false; } if (!stream_playback.is_valid() || !active || @@ -121,7 +117,7 @@ void AudioStreamPlayer::_mix_audio() { if (setstop) { _mix_internal(true); stream_playback->stop(); - setstop=false; + setstop = false; } if (setseek >= 0.0 && !stop_has_priority) { @@ -154,7 +150,7 @@ void AudioStreamPlayer::_notification(int p_what) { if (p_what == NOTIFICATION_INTERNAL_PROCESS) { if (!active || (setseek < 0 && !stream_playback->is_playing())) { - active = false; + active = false; set_process_internal(false); emit_signal("finished"); } @@ -200,7 +196,7 @@ void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) { vol += vol_inc; } - use_fadeout=true; + use_fadeout = true; } mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size()); @@ -252,7 +248,7 @@ void AudioStreamPlayer::play(float p_from_pos) { if (stream_playback.is_valid()) { //mix_volume_db = volume_db; do not reset volume ramp here, can cause clicks setseek = p_from_pos; - stop_has_priority=false; + stop_has_priority = false; active = true; set_process_internal(true); } @@ -268,8 +264,8 @@ void AudioStreamPlayer::seek(float p_seconds) { void AudioStreamPlayer::stop() { if (stream_playback.is_valid() && active) { - setstop=true; - stop_has_priority=true; + setstop = true; + stop_has_priority = true; } } @@ -357,7 +353,7 @@ void AudioStreamPlayer::_validate_property(PropertyInfo &property) const { if (property.name == "bus") { String options; - for (int i = 0; i <AudioServer::get_singleton()->get_bus_count(); i++) { + for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { if (i > 0) options += ","; String name = AudioServer::get_singleton()->get_bus_name(i); @@ -442,8 +438,8 @@ AudioStreamPlayer::AudioStreamPlayer() { stream_paused_fade = false; mix_target = MIX_TARGET_STEREO; fadeout_buffer.resize(512); - setstop=false; - use_fadeout=false; + setstop = false; + use_fadeout = false; AudioServer::get_singleton()->connect("bus_layout_changed", this, "_bus_layout_changed"); } diff --git a/scene/audio/audio_stream_player.h b/scene/audio/audio_stream_player.h index 0b782b67e7..7987bd7e7d 100644 --- a/scene/audio/audio_stream_player.h +++ b/scene/audio/audio_stream_player.h @@ -112,7 +112,6 @@ public: Ref<AudioStreamPlayback> get_stream_playback(); - AudioStreamPlayer(); ~AudioStreamPlayer(); }; diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index 7f1ca58d58..1f9bfb9936 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -177,7 +177,7 @@ String Container::get_configuration_warning() const { if (warning != String()) { warning += "\n"; } - warning += TTR("Container by itself serves no purpose unless a script configures it's children placement behavior.\nIf you dont't intend to add a script, then please use a plain 'Control' node instead."); + warning += TTR("Container by itself serves no purpose unless a script configures it's children placement behavior.\nIf you don't intend to add a script, then please use a plain 'Control' node instead."); } return warning; } diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index d889c8d8b8..2d18a80833 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -613,9 +613,7 @@ void LineEdit::_notification(int p_what) { #endif case NOTIFICATION_RESIZED: { - if (expand_to_text_length) { - window_pos = 0; //force scroll back since it's expanding to text length - } + window_pos = 0; set_cursor_position(get_cursor_position()); } break; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 10bd9e2a8b..8acd28a2f4 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1072,10 +1072,7 @@ void TextEdit::_notification(int p_what) { } if ((char_ofs + char_margin + char_w) >= xmargin_end) { - if (syntax_coloring) - continue; - else - break; + break; } bool in_search_result = false; @@ -1255,6 +1252,11 @@ void TextEdit::_notification(int p_what) { cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color); } + if (draw_spaces && str[j] == ' ') { + int yofs = (get_row_height() - cache.space_icon->get_height()) / 2; + cache.space_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color); + } + char_ofs += char_w; if (line_wrap_index == line_wrap_amount && j == str.length() - 1 && is_folded(line)) { @@ -3601,7 +3603,7 @@ void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r op.chain_forward = false; op.chain_backward = false; - //see if it shold just be set as current op + //see if it should just be set as current op if (current_op.type != op.type) { op.prev_version = get_version(); _push_current_op(); @@ -3652,7 +3654,7 @@ void TextEdit::_remove_text(int p_from_line, int p_from_column, int p_to_line, i op.chain_forward = false; op.chain_backward = false; - //see if it shold just be set as current op + //see if it should just be set as current op if (current_op.type != op.type) { op.prev_version = get_version(); _push_current_op(); @@ -4513,6 +4515,7 @@ void TextEdit::_update_caches() { #endif cache.row_height = cache.font->get_height() + cache.line_spacing; cache.tab_icon = get_icon("tab"); + cache.space_icon = get_icon("space"); cache.folded_icon = get_icon("folded"); cache.can_fold_icon = get_icon("fold"); cache.folded_eol_icon = get_icon("GuiEllipsis", "EditorIcons"); @@ -5649,6 +5652,16 @@ bool TextEdit::is_drawing_tabs() const { return draw_tabs; } +void TextEdit::set_draw_spaces(bool p_draw) { + + draw_spaces = p_draw; +} + +bool TextEdit::is_drawing_spaces() const { + + return draw_spaces; +} + void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) { override_selected_font_color = p_override_selected_font_color; } @@ -6382,6 +6395,8 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_show_line_numbers_enabled"), &TextEdit::is_show_line_numbers_enabled); ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs); ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs); + ClassDB::bind_method(D_METHOD("set_draw_spaces"), &TextEdit::set_draw_spaces); + ClassDB::bind_method(D_METHOD("is_drawing_spaces"), &TextEdit::is_drawing_spaces); ClassDB::bind_method(D_METHOD("set_breakpoint_gutter_enabled", "enable"), &TextEdit::set_breakpoint_gutter_enabled); ClassDB::bind_method(D_METHOD("is_breakpoint_gutter_enabled"), &TextEdit::is_breakpoint_gutter_enabled); ClassDB::bind_method(D_METHOD("set_draw_fold_gutter"), &TextEdit::set_draw_fold_gutter); @@ -6433,6 +6448,7 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "syntax_highlighting"), "set_syntax_coloring", "is_syntax_coloring_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_line_numbers"), "set_show_line_numbers", "is_show_line_numbers_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "breakpoint_gutter"), "set_breakpoint_gutter_enabled", "is_breakpoint_gutter_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fold_gutter"), "set_draw_fold_gutter", "is_drawing_fold_gutter"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled"); @@ -6474,6 +6490,7 @@ TextEdit::TextEdit() { setting_row = false; draw_tabs = false; + draw_spaces = false; override_selected_font_color = false; draw_caret = true; max_chars = 0; diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 4badd85e07..eb9fb2cf57 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -163,6 +163,7 @@ private: struct Cache { Ref<Texture> tab_icon; + Ref<Texture> space_icon; Ref<Texture> can_fold_icon; Ref<Texture> folded_icon; Ref<Texture> folded_eol_icon; @@ -290,6 +291,7 @@ private: bool first_draw; bool setting_row; bool draw_tabs; + bool draw_spaces; bool override_selected_font_color; bool cursor_changed_dirty; bool text_changed_dirty; @@ -613,6 +615,8 @@ public: int get_indent_size(); void set_draw_tabs(bool p_draw); bool is_drawing_tabs() const; + void set_draw_spaces(bool p_draw); + bool is_drawing_spaces() const; void set_override_selected_font_color(bool p_override_selected_font_color); bool is_overriding_selected_font_color() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 47f5b152f0..8bfb50c68d 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -523,6 +523,7 @@ void register_scene_types() { ClassDB::register_class<VisualShaderNodeCubeMapUniform>(); ClassDB::register_class<VisualShaderNodeIf>(); ClassDB::register_class<VisualShaderNodeSwitch>(); + ClassDB::register_class<VisualShaderNodeFresnel>(); ClassDB::register_class<ShaderMaterial>(); ClassDB::register_virtual_class<CanvasItem>(); diff --git a/scene/resources/bit_map.cpp b/scene/resources/bit_map.cpp index a9d85be0dc..e4a64a1de1 100644 --- a/scene/resources/bit_map.cpp +++ b/scene/resources/bit_map.cpp @@ -595,16 +595,16 @@ Array BitMap::_opaque_to_polygons_bind(const Rect2 &p_rect, float p_epsilon) con return result_array; } -void BitMap::resize(const Size2& p_new_size) { +void BitMap::resize(const Size2 &p_new_size) { Ref<BitMap> new_bitmap; new_bitmap.instance(); new_bitmap->create(p_new_size); - int lw = MIN(width,p_new_size.width); - int lh = MIN(height,p_new_size.height); - for(int x=0;x<lw;x++) { - for(int y=0;y<lh;y++) { - new_bitmap->set_bit(Vector2(x,y),get_bit(Vector2(x,y))); + int lw = MIN(width, p_new_size.width); + int lh = MIN(height, p_new_size.height); + for (int x = 0; x < lw; x++) { + for (int y = 0; y < lh; y++) { + new_bitmap->set_bit(Vector2(x, y), get_bit(Vector2(x, y))); } } @@ -617,11 +617,11 @@ Ref<Image> BitMap::convert_to_image() const { Ref<Image> image; image.instance(); - image->create(width,height,false,Image::FORMAT_L8); + image->create(width, height, false, Image::FORMAT_L8); image->lock(); - for(int i=0;i<width;i++) { - for(int j=0;j<height;j++) { - image->set_pixel( i,j,get_bit(Point2(i,j)) ? Color(1,1,1) : Color(0,0,0)); + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + image->set_pixel(i, j, get_bit(Point2(i, j)) ? Color(1, 1, 1) : Color(0, 0, 0)); } } @@ -629,30 +629,28 @@ Ref<Image> BitMap::convert_to_image() const { return image; } -void BitMap::blit(const Vector2& p_pos,const Ref<BitMap>& p_bitmap) { +void BitMap::blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap) { int x = p_pos.x; int y = p_pos.y; int w = p_bitmap->get_size().width; int h = p_bitmap->get_size().height; - for(int i=0;i<w;i++) { - for (int j=0;j<h;j++) { - int px = x+i; - int py = y+j; - if (px<0 || px>=width) + for (int i = 0; i < w; i++) { + for (int j = 0; j < h; j++) { + int px = x + i; + int py = y + j; + if (px < 0 || px >= width) continue; - if (py<0 || py>=height) + if (py < 0 || py >= height) continue; - if (p_bitmap->get_bit(Vector2(i,j))) { - set_bit(Vector2(x,y),true); + if (p_bitmap->get_bit(Vector2(i, j))) { + set_bit(Vector2(x, y), true); } } } - } - void BitMap::_bind_methods() { ClassDB::bind_method(D_METHOD("create", "size"), &BitMap::create); diff --git a/scene/resources/bit_map.h b/scene/resources/bit_map.h index 6e1171b8a9..daf24affb1 100644 --- a/scene/resources/bit_map.h +++ b/scene/resources/bit_map.h @@ -64,11 +64,11 @@ public: int get_true_bit_count() const; Size2 get_size() const; - void resize(const Size2& p_new_size); + void resize(const Size2 &p_new_size); void grow_mask(int p_pixels, const Rect2 &p_rect); - void blit(const Vector2& p_pos,const Ref<BitMap>& p_bitmap); + void blit(const Vector2 &p_pos, const Ref<BitMap> &p_bitmap); Ref<Image> convert_to_image() const; Vector<Vector<Vector2> > clip_opaque_to_polygons(const Rect2 &p_rect, float p_epsilon = 2.0) const; diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index b3bd3f47e6..0b88763300 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -425,6 +425,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_stylebox("completion", "TextEdit", make_stylebox(tree_bg_png, 3, 3, 3, 3, 0, 0, 0, 0)); theme->set_icon("tab", "TextEdit", make_icon(tab_png)); + theme->set_icon("space", "TextEdit", make_icon(space_png)); theme->set_icon("folded", "TextEdit", make_icon(arrow_right_png)); theme->set_icon("fold", "TextEdit", make_icon(arrow_down_png)); diff --git a/scene/resources/default_theme/space.png b/scene/resources/default_theme/space.png Binary files differnew file mode 100644 index 0000000000..7e458a6c45 --- /dev/null +++ b/scene/resources/default_theme/space.png diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h index a09b811b37..87b8afd6a3 100644 --- a/scene/resources/default_theme/theme_data.h +++ b/scene/resources/default_theme/theme_data.h @@ -358,6 +358,10 @@ static const unsigned char selection_oof_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x0, 0x2d, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x2, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xfd, 0xfb, 0xff, 0xaf, 0xdf, 0x90, 0xa5, 0x0, 0x0, 0x0, 0xf, 0x74, 0x52, 0x4e, 0x53, 0xa, 0x1a, 0x26, 0x29, 0x2a, 0x48, 0x65, 0x6d, 0x6e, 0x66, 0x3, 0x20, 0x25, 0x16, 0xc, 0x1f, 0x74, 0xbf, 0x74, 0x0, 0x0, 0x0, 0x37, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x64, 0x54, 0x52, 0x64, 0x60, 0x60, 0x78, 0x77, 0x8f, 0x51, 0x34, 0x8, 0xcc, 0xb8, 0xcd, 0xa8, 0xd9, 0x4, 0x66, 0xdc, 0x60, 0x74, 0x2f, 0x33, 0x4, 0x32, 0xde, 0xce, 0x64, 0xf4, 0x68, 0x53, 0x0, 0x32, 0xfe, 0xcd, 0xa0, 0x90, 0x1, 0x37, 0x10, 0x6e, 0x5, 0xdc, 0x52, 0xb8, 0x33, 0x0, 0xcc, 0x7, 0x26, 0xff, 0x1f, 0x38, 0x23, 0x97, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; +static const unsigned char space_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x8, 0x6, 0x0, 0x0, 0x0, 0xc4, 0xf, 0xbe, 0x8b, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x2f, 0x49, 0x44, 0x41, 0x54, 0x18, 0x95, 0x63, 0x60, 0xa0, 0x2a, 0xf8, 0xff, 0xff, 0xbf, 0xe0, 0xff, 0xff, 0xff, 0x5, 0x91, 0xc5, 0x58, 0x90, 0x24, 0x85, 0x18, 0x18, 0x18, 0x14, 0xa0, 0x6c, 0x6, 0x46, 0x46, 0xc6, 0xf7, 0xc, 0xc, 0xc, 0xc, 0x4c, 0x14, 0x5b, 0x41, 0x39, 0x0, 0x0, 0x71, 0x1a, 0x13, 0x5d, 0x87, 0xfe, 0x9e, 0x7d, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; + static const unsigned char spinbox_updown_png[] = { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x8, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xfa, 0x37, 0xea, 0x0, 0x0, 0x0, 0x59, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x20, 0x11, 0xdc, 0x4f, 0x7f, 0x98, 0x86, 0x47, 0xfa, 0x81, 0xe5, 0x83, 0x1f, 0xf, 0x7e, 0x3d, 0xb2, 0xc5, 0xa5, 0x5b, 0xe2, 0xc1, 0x93, 0x7, 0xff, 0x81, 0xf0, 0xf9, 0x63, 0x69, 0x2c, 0xd2, 0x67, 0x58, 0xef, 0x1f, 0x2, 0x4a, 0x42, 0xe0, 0xf1, 0xdb, 0xec, 0x98, 0xfa, 0x67, 0x2, 0x25, 0xe0, 0xf0, 0xe1, 0x2, 0x86, 0x41, 0x7, 0x30, 0x1d, 0x39, 0x3, 0xbf, 0x37, 0x8f, 0xdd, 0x66, 0x27, 0x29, 0xa0, 0x10, 0x4a, 0x2c, 0xa0, 0x41, 0x8d, 0x1b, 0x3c, 0x4c, 0x3, 0x46, 0x16, 0x69, 0x0, 0x0, 0x87, 0x2a, 0x58, 0xb5, 0x18, 0xe9, 0x80, 0x2c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 }; diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index e9f90fc85f..6e7bb27e74 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -447,9 +447,8 @@ Error ResourceInteractiveLoaderText::poll() { resource_cache.push_back(res); #ifdef TOOLS_ENABLED //remember ID for saving - res->set_id_for_path(local_path,index); + res->set_id_for_path(local_path, index); #endif - } ExtResource er; @@ -1545,9 +1544,6 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } { - - - } #ifdef TOOLS_ENABLED @@ -1569,7 +1565,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r } int attempt = 1; //start from one, more readable format - while(cached_ids_found.has(attempt)) { + while (cached_ids_found.has(attempt)) { attempt++; } @@ -1577,7 +1573,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r E->get() = attempt; //update also in resource Ref<Resource> res = E->key(); - res->set_id_for_path(local_path,attempt); + res->set_id_for_path(local_path, attempt); } #else //make sure to start from one, as it makes format more readable @@ -1598,7 +1594,6 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r sorted_er.sort(); - for (int i = 0; i < sorted_er.size(); i++) { String p = sorted_er[i].resource->get_path(); diff --git a/scene/resources/resource_format_text.h b/scene/resources/resource_format_text.h index ab6f94986c..06c841229b 100644 --- a/scene/resources/resource_format_text.h +++ b/scene/resources/resource_format_text.h @@ -172,10 +172,9 @@ class ResourceFormatSaverTextInstance { struct ResourceSort { RES resource; int index; - bool operator<(const ResourceSort& p_right) const { + bool operator<(const ResourceSort &p_right) const { return index < p_right.index; } - }; void _find_resources(const Variant &p_variant, bool p_main = false); diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp index 3ba43006a3..8c0497e91a 100644 --- a/scene/resources/surface_tool.cpp +++ b/scene/resources/surface_tool.cpp @@ -115,7 +115,7 @@ void SurfaceTool::add_vertex(const Vector3 &p_vertex) { //ensure vertices are the expected amount ERR_FAIL_COND(vtx.weights.size() != vtx.bones.size()); if (vtx.weights.size() < expected_vertices) { - //less than requred, fill + //less than required, fill for (int i = vtx.weights.size(); i < expected_vertices; i++) { vtx.weights.push_back(0); vtx.bones.push_back(0); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 9b8037965b..a44471a365 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -1192,7 +1192,7 @@ String VisualShaderNodeScalarFunc::get_output_port_name(int p_port) const { String VisualShaderNodeScalarFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { - static const char *scalar_func_id[FUNC_TRUNC + 1] = { + static const char *scalar_func_id[FUNC_ONEMINUS + 1] = { "sin($)", "cos($)", "tan($)", @@ -1223,7 +1223,8 @@ String VisualShaderNodeScalarFunc::generate_code(Shader::Mode p_mode, VisualShad "radians($)", "1.0/($)", "roundEven($)", - "trunc($)" + "trunc($)", + "1.0-$" }; return "\t" + p_output_vars[0] + " = " + String(scalar_func_id[func]).replace("$", p_input_vars[0]) + ";\n"; @@ -1251,7 +1252,7 @@ void VisualShaderNodeScalarFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeScalarFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeScalarFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Frac,Saturate,Negate,ACosH,ASinH,ATanH,Degrees,Exp2,InverseSqrt,Log2,Radians,Reciprocal,RoundEven,Trunc"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Sin,Cos,Tan,ASin,ACos,ATan,SinH,CosH,TanH,Log,Exp,Sqrt,Abs,Sign,Floor,Round,Ceil,Frac,Saturate,Negate,ACosH,ASinH,ATanH,Degrees,Exp2,InverseSqrt,Log2,Radians,Reciprocal,RoundEven,Trunc,OneMinus"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_SIN); BIND_ENUM_CONSTANT(FUNC_COS); @@ -1284,6 +1285,7 @@ void VisualShaderNodeScalarFunc::_bind_methods() { BIND_ENUM_CONSTANT(FUNC_RECIPROCAL); BIND_ENUM_CONSTANT(FUNC_ROUNDEVEN); BIND_ENUM_CONSTANT(FUNC_TRUNC); + BIND_ENUM_CONSTANT(FUNC_ONEMINUS); } VisualShaderNodeScalarFunc::VisualShaderNodeScalarFunc() { @@ -1319,7 +1321,7 @@ String VisualShaderNodeVectorFunc::get_output_port_name(int p_port) const { String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { - static const char *vec_func_id[FUNC_TRUNC + 1] = { + static const char *vec_func_id[FUNC_ONEMINUS + 1] = { "normalize($)", "max(min($,vec3(1.0)),vec3(0.0))", "-($)", @@ -1353,7 +1355,8 @@ String VisualShaderNodeVectorFunc::generate_code(Shader::Mode p_mode, VisualShad "sqrt($)", "tan($)", "tanh($)", - "trunc($)" + "trunc($)", + "vec3(1.0, 1.0, 1.0)-$" }; String code; @@ -1405,7 +1408,7 @@ void VisualShaderNodeVectorFunc::_bind_methods() { ClassDB::bind_method(D_METHOD("set_function", "func"), &VisualShaderNodeVectorFunc::set_function); ClassDB::bind_method(D_METHOD("get_function"), &VisualShaderNodeVectorFunc::get_function); - ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc"), "set_function", "get_function"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "function", PROPERTY_HINT_ENUM, "Normalize,Saturate,Negate,Reciprocal,RGB2HSV,HSV2RGB,Abs,ACos,ACosH,ASin,ASinH,ATan,ATanH,Ceil,Cos,CosH,Degrees,Exp,Exp2,Floor,Frac,InverseSqrt,Log,Log2,Radians,Round,RoundEven,Sign,Sin,SinH,Sqrt,Tan,TanH,Trunc,OneMinus"), "set_function", "get_function"); BIND_ENUM_CONSTANT(FUNC_NORMALIZE); BIND_ENUM_CONSTANT(FUNC_SATURATE); @@ -1441,6 +1444,7 @@ void VisualShaderNodeVectorFunc::_bind_methods() { BIND_ENUM_CONSTANT(FUNC_TAN); BIND_ENUM_CONSTANT(FUNC_TANH); BIND_ENUM_CONSTANT(FUNC_TRUNC); + BIND_ENUM_CONSTANT(FUNC_ONEMINUS); } VisualShaderNodeVectorFunc::VisualShaderNodeVectorFunc() { @@ -3085,3 +3089,66 @@ VisualShaderNodeSwitch::VisualShaderNodeSwitch() { set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); set_input_port_default_value(2, Vector3(0.0, 0.0, 0.0)); } + +////////////// Fresnel + +String VisualShaderNodeFresnel::get_caption() const { + return "Fresnel"; +} + +int VisualShaderNodeFresnel::get_input_port_count() const { + return 4; +} + +VisualShaderNodeFresnel::PortType VisualShaderNodeFresnel::get_input_port_type(int p_port) const { + switch (p_port) { + case 0: + return PORT_TYPE_VECTOR; + case 1: + return PORT_TYPE_VECTOR; + case 2: + return PORT_TYPE_BOOLEAN; + case 3: + return PORT_TYPE_SCALAR; + default: + return PORT_TYPE_VECTOR; + } +} + +String VisualShaderNodeFresnel::get_input_port_name(int p_port) const { + switch (p_port) { + case 0: + return "normal"; + case 1: + return "view"; + case 2: + return "invert"; + case 3: + return "power"; + default: + return ""; + } +} + +int VisualShaderNodeFresnel::get_output_port_count() const { + return 1; +} + +VisualShaderNodeFresnel::PortType VisualShaderNodeFresnel::get_output_port_type(int p_port) const { + return PORT_TYPE_SCALAR; +} + +String VisualShaderNodeFresnel::get_output_port_name(int p_port) const { + return "result"; +} + +String VisualShaderNodeFresnel::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const { + return "\t" + p_output_vars[0] + " = " + p_input_vars[2] + " ? (pow(clamp(dot(" + p_input_vars[0] + ", " + p_input_vars[1] + "), 0.0, 1.0), " + p_input_vars[3] + ")) : (pow(1.0 - clamp(dot(" + p_input_vars[0] + ", " + p_input_vars[1] + "), 0.0, 1.0), " + p_input_vars[3] + "));"; +} + +VisualShaderNodeFresnel::VisualShaderNodeFresnel() { + set_input_port_default_value(0, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(1, Vector3(0.0, 0.0, 0.0)); + set_input_port_default_value(2, false); + set_input_port_default_value(3, 1.0); +} diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h index 90c479bd48..852248b9b4 100644 --- a/scene/resources/visual_shader_nodes.h +++ b/scene/resources/visual_shader_nodes.h @@ -562,7 +562,8 @@ public: FUNC_RADIANS, FUNC_RECIPROCAL, FUNC_ROUNDEVEN, - FUNC_TRUNC + FUNC_TRUNC, + FUNC_ONEMINUS }; protected: @@ -635,7 +636,8 @@ public: FUNC_SQRT, FUNC_TAN, FUNC_TANH, - FUNC_TRUNC + FUNC_TRUNC, + FUNC_ONEMINUS }; protected: @@ -1484,4 +1486,26 @@ public: VisualShaderNodeSwitch(); }; +/////////////////////////////////////// +/// FRESNEL +/////////////////////////////////////// + +class VisualShaderNodeFresnel : public VisualShaderNode { + GDCLASS(VisualShaderNodeFresnel, VisualShaderNode) +public: + virtual String get_caption() const; + + virtual int get_input_port_count() const; + virtual PortType get_input_port_type(int p_port) const; + virtual String get_input_port_name(int p_port) const; + + virtual int get_output_port_count() const; + virtual PortType get_output_port_type(int p_port) const; + virtual String get_output_port_name(int p_port) const; + + virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const; + + VisualShaderNodeFresnel(); +}; + #endif // VISUAL_SHADER_NODES_H diff --git a/servers/audio/effects/audio_effect_spectrum_analyzer.cpp b/servers/audio/effects/audio_effect_spectrum_analyzer.cpp index 46d92336f3..05cba416be 100644 --- a/servers/audio/effects/audio_effect_spectrum_analyzer.cpp +++ b/servers/audio/effects/audio_effect_spectrum_analyzer.cpp @@ -112,7 +112,7 @@ void AudioEffectSpectrumAnalyzerInstance::process(const AudioFrame *p_src_frames } //determine time of capture - double remainer_sec = (temporal_fft_pos / mix_rate); //substract remainder from mix time + double remainer_sec = (temporal_fft_pos / mix_rate); //subtract remainder from mix time last_fft_time = time - uint64_t(remainer_sec * 1000000.0); } diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp index f5acadd71c..e2b1bb9da4 100644 --- a/servers/physics_2d/space_2d_sw.cpp +++ b/servers/physics_2d/space_2d_sw.cpp @@ -795,7 +795,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co cbk.valid_dir = col_obj_shape_xform.get_axis(1).normalized(); float owc_margin = col_obj->get_shape_one_way_collision_margin(shape_idx); - cbk.valid_depth = MAX(owc_margin, p_margin); //user specified, but never less than actual margin or it wont work + cbk.valid_depth = MAX(owc_margin, p_margin); //user specified, but never less than actual margin or it won't work cbk.invalid_by_dir = 0; if (col_obj->get_type() == CollisionObject2DSW::TYPE_BODY) { diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 33714a79b2..cb3a744730 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -616,7 +616,7 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { else tk.type = TK_INT_CONSTANT; - tk.constant = str.to_double(); //wont work with hex + tk.constant = str.to_double(); //won't work with hex tk.line = tk_line; return tk; 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"; } |