summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml10
-rw-r--r--core/io/config_file.cpp2
-rw-r--r--core/math/a_star.cpp239
-rw-r--r--core/math/a_star.h26
-rw-r--r--core/math/delaunay.h4
-rw-r--r--core/oa_hash_map.h34
-rw-r--r--doc/classes/AnimationPlayer.xml1
-rw-r--r--doc/classes/ClassDB.xml4
-rw-r--r--doc/classes/Directory.xml4
-rw-r--r--doc/classes/GeometryInstance.xml12
-rw-r--r--doc/classes/MainLoop.xml2
-rw-r--r--drivers/gles3/shaders/copy.glsl4
-rw-r--r--drivers/unix/file_access_unix.cpp13
-rw-r--r--drivers/windows/file_access_windows.cpp14
-rw-r--r--editor/editor_file_dialog.cpp6
-rw-r--r--editor/editor_help_search.cpp2
-rw-r--r--editor/editor_node.cpp14
-rw-r--r--editor/editor_plugin.cpp7
-rw-r--r--editor/editor_plugin.h5
-rw-r--r--editor/editor_settings.cpp2
-rw-r--r--editor/editor_themes.cpp3
-rw-r--r--editor/filesystem_dock.cpp10
-rw-r--r--editor/icons/icon_editor_curve_handle.svg1
-rw-r--r--editor/icons/icon_editor_path_sharp_handle.svg1
-rw-r--r--editor/icons/icon_editor_path_smooth_handle.svg1
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp3
-rw-r--r--editor/plugins/path_2d_editor_plugin.cpp42
-rw-r--r--editor/plugins/path_editor_plugin.cpp3
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp7
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp12
-rw-r--r--editor/project_manager.cpp123
-rw-r--r--editor/project_manager.h1
-rw-r--r--editor/spatial_editor_gizmos.cpp24
-rw-r--r--misc/ide/jetbrains/build.gradle2
-rw-r--r--modules/csg/csg.cpp10
-rw-r--r--modules/websocket/websocket_multiplayer_peer.cpp15
-rw-r--r--platform/android/java/src/org/godotengine/godot/Godot.java4
-rw-r--r--scene/2d/navigation_2d.cpp4
-rw-r--r--scene/2d/path_2d.cpp2
-rw-r--r--scene/3d/collision_polygon.cpp2
-rw-r--r--scene/3d/ray_cast.cpp2
-rw-r--r--scene/animation/animation_player.cpp4
-rw-r--r--scene/gui/file_dialog.cpp2
-rw-r--r--scene/gui/text_edit.cpp28
-rw-r--r--scene/resources/animation.cpp8
-rw-r--r--scene/resources/default_theme/default_theme.cpp1
-rw-r--r--scene/resources/visual_shader.cpp19
49 files changed, 458 insertions, 284 deletions
diff --git a/.gitignore b/.gitignore
index f43f68f25f..0ee2a8b382 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# Godot auto generated files
*.gen.*
+.import/
# Documentation generated by doxygen or from classes.xml
doc/_build/
diff --git a/.travis.yml b/.travis.yml
index e16f1ca8a7..305544d821 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -84,9 +84,9 @@ matrix:
os: osx
compiler: clang
- - name: Linux headless editor (release_debug, GCC 9)
+ - name: Linux headless editor (release_debug, GCC 9, testing project exporting and script running)
stage: build
- env: PLATFORM=server TOOLS=yes TARGET=release_debug CACHE_NAME=${PLATFORM}-tools-gcc-9 MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" EXTRA_ARGS="warnings=extra werror=yes"
+ env: PLATFORM=server TOOLS=yes TARGET=release_debug CACHE_NAME=${PLATFORM}-tools-gcc-9 MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" EXTRA_ARGS="warnings=extra werror=yes" TEST_PROJECT=yes
os: linux
compiler: gcc-9
addons:
@@ -136,4 +136,10 @@ script:
sh ./misc/travis/clang-format.sh;
else
scons -j2 CC=$CC CXX=$CXX platform=$PLATFORM tools=$TOOLS target=$TARGET $OPTIONS $EXTRA_ARGS;
+
+ if [ "$TEST_PROJECT" = "yes" ]; then
+ git clone --depth 1 "https://github.com/godotengine/godot-tests.git";
+ sed -i "s:custom_template/release=\"\":custom_template/release=\"$(readlink -e bin/godot_server.x11.opt.tools.64)\":" godot-tests/tests/project_export/export_presets.cfg;
+ godot-tests/tests/project_export/test_project.sh "bin/godot_server.x11.opt.tools.64";
+ fi
fi
diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp
index 70bb816acd..9063e028be 100644
--- a/core/io/config_file.cpp
+++ b/core/io/config_file.cpp
@@ -201,7 +201,7 @@ Error ConfigFile::load(const String &p_path) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
if (!f)
- return ERR_CANT_OPEN;
+ return err;
return _internal_load(p_path, f);
}
diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp
index b61119d8df..aea42a1edf 100644
--- a/core/math/a_star.cpp
+++ b/core/math/a_star.cpp
@@ -40,7 +40,17 @@ int AStar::get_available_point_id() const {
return 1;
}
- return points.back()->key() + 1;
+ // calculate our new next available point id if bigger than before or next id already contained in set of points.
+ if (points.has(last_free_id)) {
+ int cur_new_id = last_free_id;
+ while (points.has(cur_new_id)) {
+ cur_new_id++;
+ }
+ int &non_const = const_cast<int &>(last_free_id);
+ non_const = cur_new_id;
+ }
+
+ return last_free_id;
}
void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
@@ -48,7 +58,10 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
ERR_FAIL_COND(p_id < 0);
ERR_FAIL_COND(p_weight_scale < 1);
- if (!points.has(p_id)) {
+ Point *found_pt;
+ bool p_exists = points.lookup(p_id, found_pt);
+
+ if (!p_exists) {
Point *pt = memnew(Point);
pt->id = p_id;
pt->pos = p_pos;
@@ -57,84 +70,98 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
pt->open_pass = 0;
pt->closed_pass = 0;
pt->enabled = true;
- points[p_id] = pt;
+ points.set(p_id, pt);
} else {
- points[p_id]->pos = p_pos;
- points[p_id]->weight_scale = p_weight_scale;
+ found_pt->pos = p_pos;
+ found_pt->weight_scale = p_weight_scale;
}
}
Vector3 AStar::get_point_position(int p_id) const {
- ERR_FAIL_COND_V(!points.has(p_id), Vector3());
+ Point *p;
+ bool p_exists = points.lookup(p_id, p);
+ ERR_FAIL_COND_V(!p_exists, Vector3());
- return points[p_id]->pos;
+ return p->pos;
}
void AStar::set_point_position(int p_id, const Vector3 &p_pos) {
- ERR_FAIL_COND(!points.has(p_id));
+ Point *p;
+ bool p_exists = points.lookup(p_id, p);
+ ERR_FAIL_COND(!p_exists);
- points[p_id]->pos = p_pos;
+ p->pos = p_pos;
}
real_t AStar::get_point_weight_scale(int p_id) const {
- ERR_FAIL_COND_V(!points.has(p_id), 0);
+ Point *p;
+ bool p_exists = points.lookup(p_id, p);
+ ERR_FAIL_COND_V(!p_exists, 0);
- return points[p_id]->weight_scale;
+ return p->weight_scale;
}
void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) {
- ERR_FAIL_COND(!points.has(p_id));
+ Point *p;
+ bool p_exists = points.lookup(p_id, p);
+ ERR_FAIL_COND(!p_exists);
ERR_FAIL_COND(p_weight_scale < 1);
- points[p_id]->weight_scale = p_weight_scale;
+ p->weight_scale = p_weight_scale;
}
void AStar::remove_point(int p_id) {
- ERR_FAIL_COND(!points.has(p_id));
-
- Point *p = points[p_id];
+ Point *p;
+ bool p_exists = points.lookup(p_id, p);
+ ERR_FAIL_COND(!p_exists);
- for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
+ for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
- Segment s(p_id, E->get()->id);
+ Segment s(p_id, (*it.key));
segments.erase(s);
- E->get()->neighbours.erase(p);
- E->get()->unlinked_neighbours.erase(p);
+ (*it.value)->neighbours.remove(p->id);
+ (*it.value)->unlinked_neighbours.remove(p->id);
}
- for (Set<Point *>::Element *E = p->unlinked_neighbours.front(); E; E = E->next()) {
+ for (OAHashMap<int, Point *>::Iterator it = p->unlinked_neighbours.iter(); it.valid; it = p->unlinked_neighbours.next_iter(it)) {
- Segment s(p_id, E->get()->id);
+ Segment s(p_id, (*it.key));
segments.erase(s);
- E->get()->neighbours.erase(p);
- E->get()->unlinked_neighbours.erase(p);
+ (*it.value)->neighbours.remove(p->id);
+ (*it.value)->unlinked_neighbours.remove(p->id);
}
memdelete(p);
- points.erase(p_id);
+ points.remove(p_id);
+ last_free_id = p_id;
}
void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {
- ERR_FAIL_COND(!points.has(p_id));
- ERR_FAIL_COND(!points.has(p_with_id));
ERR_FAIL_COND(p_id == p_with_id);
- Point *a = points[p_id];
- Point *b = points[p_with_id];
- a->neighbours.insert(b);
+ Point *a;
+ bool from_exists = points.lookup(p_id, a);
+ ERR_FAIL_COND(!from_exists);
- if (bidirectional)
- b->neighbours.insert(a);
- else
- b->unlinked_neighbours.insert(a);
+ Point *b;
+ bool to_exists = points.lookup(p_with_id, b);
+ ERR_FAIL_COND(!to_exists);
+
+ a->neighbours.set(b->id, b);
+
+ if (bidirectional) {
+ b->neighbours.set(a->id, a);
+ } else {
+ b->unlinked_neighbours.set(a->id, a);
+ }
Segment s(p_id, p_with_id);
if (s.from == p_id) {
@@ -147,6 +174,7 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {
segments.insert(s);
}
+
void AStar::disconnect_points(int p_id, int p_with_id) {
Segment s(p_id, p_with_id);
@@ -154,12 +182,18 @@ void AStar::disconnect_points(int p_id, int p_with_id) {
segments.erase(s);
- Point *a = points[p_id];
- Point *b = points[p_with_id];
- a->neighbours.erase(b);
- a->unlinked_neighbours.erase(b);
- b->neighbours.erase(a);
- b->unlinked_neighbours.erase(a);
+ Point *a;
+ bool a_exists = points.lookup(p_id, a);
+ CRASH_COND(!a_exists);
+
+ Point *b;
+ bool b_exists = points.lookup(p_with_id, b);
+ CRASH_COND(!b_exists);
+
+ a->neighbours.remove(b->id);
+ a->unlinked_neighbours.remove(b->id);
+ b->neighbours.remove(a->id);
+ b->unlinked_neighbours.remove(a->id);
}
bool AStar::has_point(int p_id) const {
@@ -171,8 +205,8 @@ Array AStar::get_points() {
Array point_list;
- for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) {
- point_list.push_back(E->key());
+ for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) {
+ point_list.push_back(*(it.key));
}
return point_list;
@@ -180,14 +214,14 @@ Array AStar::get_points() {
PoolVector<int> AStar::get_point_connections(int p_id) {
- ERR_FAIL_COND_V(!points.has(p_id), PoolVector<int>());
+ Point *p;
+ bool p_exists = points.lookup(p_id, p);
+ ERR_FAIL_COND_V(!p_exists, PoolVector<int>());
PoolVector<int> point_list;
- Point *p = points[p_id];
-
- for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
- point_list.push_back(E->get()->id);
+ for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
+ point_list.push_back((*it.key));
}
return point_list;
@@ -201,9 +235,9 @@ bool AStar::are_points_connected(int p_id, int p_with_id) const {
void AStar::clear() {
- for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) {
-
- memdelete(E->get());
+ last_free_id = 0;
+ for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) {
+ memdelete(*(it.value));
}
segments.clear();
points.clear();
@@ -214,14 +248,14 @@ int AStar::get_closest_point(const Vector3 &p_point) const {
int closest_id = -1;
real_t closest_dist = 1e20;
- for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) {
+ for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) {
+
+ if (!(*it.value)->enabled) continue; // Disabled points should not be considered.
- if (!E->get()->enabled)
- continue; //Disabled points should not be considered
- real_t d = p_point.distance_squared_to(E->get()->pos);
+ real_t d = p_point.distance_squared_to((*it.value)->pos);
if (closest_id < 0 || d < closest_dist) {
closest_dist = d;
- closest_id = E->key();
+ closest_id = *(it.key);
}
}
@@ -230,8 +264,8 @@ int AStar::get_closest_point(const Vector3 &p_point) const {
Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const {
- real_t closest_dist = 1e20;
bool found = false;
+ real_t closest_dist = 1e20;
Vector3 closest_point;
for (const Set<Segment>::Element *E = segments.front(); E; E = E->next()) {
@@ -262,8 +296,7 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
pass++;
- if (!end_point->enabled)
- return false;
+ if (!end_point->enabled) return false;
bool found_route = false;
@@ -272,13 +305,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
begin_point->g_score = 0;
begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
-
open_list.push_back(begin_point);
- while (true) {
-
- if (open_list.size() == 0) // No path found
- break;
+ while (!open_list.empty()) {
Point *p = open_list[0]; // The currently processed point
@@ -291,24 +320,23 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
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()) {
+ for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
- Point *e = E->get(); // The neighbour point
+ Point *e = *(it.value); // The neighbour point
- if (!e->enabled || e->closed_pass == pass)
+ if (!e->enabled || e->closed_pass == pass) {
continue;
+ }
real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id) * e->weight_scale;
bool new_point = false;
- if (e->open_pass != pass) { // The point wasn't inside the open list
-
+ if (e->open_pass != pass) { // The point wasn't inside the open list.
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
-
+ } else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous.
continue;
}
@@ -316,10 +344,11 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
e->g_score = tentative_g_score;
e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
- if (new_point) // The position of the new points is already known
+ 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
+ } else {
sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw());
+ }
}
}
@@ -331,7 +360,15 @@ float AStar::_estimate_cost(int p_from_id, int p_to_id) {
if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost))
return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id);
- return points[p_from_id]->pos.distance_to(points[p_to_id]->pos);
+ Point *from_point;
+ bool from_exists = points.lookup(p_from_id, from_point);
+ CRASH_COND(!from_exists);
+
+ Point *to_point;
+ bool to_exists = points.lookup(p_to_id, to_point);
+ CRASH_COND(!to_exists);
+
+ return from_point->pos.distance_to(to_point->pos);
}
float AStar::_compute_cost(int p_from_id, int p_to_id) {
@@ -339,16 +376,26 @@ float AStar::_compute_cost(int p_from_id, int p_to_id) {
if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost))
return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id);
- return points[p_from_id]->pos.distance_to(points[p_to_id]->pos);
+ Point *from_point;
+ bool from_exists = points.lookup(p_from_id, from_point);
+ CRASH_COND(!from_exists);
+
+ Point *to_point;
+ bool to_exists = points.lookup(p_to_id, to_point);
+ CRASH_COND(!to_exists);
+
+ return from_point->pos.distance_to(to_point->pos);
}
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>());
+ Point *a;
+ bool from_exists = points.lookup(p_from_id, a);
+ ERR_FAIL_COND_V(!from_exists, PoolVector<Vector3>());
- Point *a = points[p_from_id];
- Point *b = points[p_to_id];
+ Point *b;
+ bool to_exists = points.lookup(p_to_id, b);
+ ERR_FAIL_COND_V(!to_exists, PoolVector<Vector3>());
if (a == b) {
PoolVector<Vector3> ret;
@@ -360,11 +407,8 @@ PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {
Point *end_point = b;
bool found_route = _solve(begin_point, end_point);
+ if (!found_route) return PoolVector<Vector3>();
- if (!found_route)
- return PoolVector<Vector3>();
-
- // Midpoints
Point *p = end_point;
int pc = 1; // Begin point
while (p != begin_point) {
@@ -393,11 +437,13 @@ PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {
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>());
+ Point *a;
+ bool from_exists = points.lookup(p_from_id, a);
+ ERR_FAIL_COND_V(!from_exists, PoolVector<int>());
- Point *a = points[p_from_id];
- Point *b = points[p_to_id];
+ Point *b;
+ bool to_exists = points.lookup(p_to_id, b);
+ ERR_FAIL_COND_V(!to_exists, PoolVector<int>());
if (a == b) {
PoolVector<int> ret;
@@ -409,11 +455,8 @@ PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) {
Point *end_point = b;
bool found_route = _solve(begin_point, end_point);
+ if (!found_route) return PoolVector<int>();
- if (!found_route)
- return PoolVector<int>();
-
- // Midpoints
Point *p = end_point;
int pc = 1; // Begin point
while (p != begin_point) {
@@ -442,16 +485,20 @@ PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) {
void AStar::set_point_disabled(int p_id, bool p_disabled) {
- ERR_FAIL_COND(!points.has(p_id));
+ Point *p;
+ bool p_exists = points.lookup(p_id, p);
+ ERR_FAIL_COND(!p_exists);
- points[p_id]->enabled = !p_disabled;
+ p->enabled = !p_disabled;
}
bool AStar::is_point_disabled(int p_id) const {
- ERR_FAIL_COND_V(!points.has(p_id), false);
+ Point *p;
+ bool p_exists = points.lookup(p_id, p);
+ ERR_FAIL_COND_V(!p_exists, false);
- return !points[p_id]->enabled;
+ return !p->enabled;
}
void AStar::_bind_methods() {
@@ -487,13 +534,11 @@ void AStar::_bind_methods() {
}
AStar::AStar() {
-
+ last_free_id = 0;
pass = 1;
}
AStar::~AStar() {
-
- pass = 1;
clear();
}
diff --git a/core/math/a_star.h b/core/math/a_star.h
index cbabcce974..53aaaa1f6c 100644
--- a/core/math/a_star.h
+++ b/core/math/a_star.h
@@ -31,6 +31,7 @@
#ifndef ASTAR_H
#define ASTAR_H
+#include "core/oa_hash_map.h"
#include "core/reference.h"
/**
@@ -43,8 +44,6 @@ class AStar : public Reference {
GDCLASS(AStar, Reference);
- uint64_t pass;
-
struct Point {
int id;
@@ -52,10 +51,10 @@ class AStar : public Reference {
real_t weight_scale;
bool enabled;
- Set<Point *> neighbours;
- Set<Point *> unlinked_neighbours;
+ OAHashMap<int, Point *> neighbours;
+ OAHashMap<int, Point *> unlinked_neighbours;
- // Used for pathfinding
+ // Used for pathfinding.
Point *prev_point;
real_t g_score;
real_t f_score;
@@ -63,16 +62,15 @@ class AStar : public Reference {
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)
+ _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)
+ } 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
+ } 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.
+ }
}
};
@@ -100,6 +98,10 @@ class AStar : public Reference {
}
};
+ int last_free_id;
+ uint64_t pass;
+
+ OAHashMap<int, Point *> points;
Set<Segment> segments;
bool _solve(Point *begin_point, Point *end_point);
diff --git a/core/math/delaunay.h b/core/math/delaunay.h
index ed52c506db..3f8013a3e6 100644
--- a/core/math/delaunay.h
+++ b/core/math/delaunay.h
@@ -80,11 +80,11 @@ public:
}
static bool edge_compare(const Vector<Vector2> &p_vertices, const Edge &p_a, const Edge &p_b) {
- if (Math::is_zero_approx(p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[0]])) && Math::is_zero_approx(p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[1]]))) {
+ if (p_vertices[p_a.edge[0]] == p_vertices[p_b.edge[0]] && p_vertices[p_a.edge[1]] == p_vertices[p_b.edge[1]]) {
return true;
}
- if (Math::is_zero_approx(p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[1]])) && Math::is_zero_approx(p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[0]]))) {
+ if (p_vertices[p_a.edge[0]] == p_vertices[p_b.edge[1]] && p_vertices[p_a.edge[1]] == p_vertices[p_b.edge[0]]) {
return true;
}
diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h
index e52d36a859..83621bec14 100644
--- a/core/oa_hash_map.h
+++ b/core/oa_hash_map.h
@@ -62,7 +62,7 @@ private:
static const uint32_t EMPTY_HASH = 0;
static const uint32_t DELETED_HASH_BIT = 1 << 31;
- _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) {
+ _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const {
uint32_t hash = Hasher::hash(p_key);
if (hash == EMPTY_HASH) {
@@ -74,7 +74,7 @@ private:
return hash;
}
- _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) {
+ _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) const {
p_hash = p_hash & ~DELETED_HASH_BIT; // we don't care if it was deleted or not
uint32_t original_pos = p_hash % capacity;
@@ -90,7 +90,7 @@ private:
num_elements++;
}
- bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) {
+ bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const {
uint32_t hash = _hash(p_key);
uint32_t pos = hash % capacity;
uint32_t distance = 0;
@@ -151,6 +151,7 @@ private:
distance++;
}
}
+
void _resize_and_rehash() {
TKey *old_keys = keys;
@@ -190,6 +191,26 @@ public:
_FORCE_INLINE_ uint32_t get_capacity() const { return capacity; }
_FORCE_INLINE_ uint32_t get_num_elements() const { return num_elements; }
+ bool empty() const {
+ return num_elements == 0;
+ }
+
+ void clear() {
+
+ for (uint32_t i = 0; i < capacity; i++) {
+
+ if (hashes[i] & DELETED_HASH_BIT) {
+ continue;
+ }
+
+ hashes[i] |= DELETED_HASH_BIT;
+ values[i].~TValue();
+ keys[i].~TKey();
+ }
+
+ num_elements = 0;
+ }
+
void insert(const TKey &p_key, const TValue &p_value) {
if ((float)num_elements / (float)capacity > 0.9) {
@@ -219,7 +240,7 @@ public:
* if r_data is not NULL then the value will be written to the object
* it points to.
*/
- bool lookup(const TKey &p_key, TValue &r_data) {
+ bool lookup(const TKey &p_key, TValue &r_data) const {
uint32_t pos = 0;
bool exists = _lookup_pos(p_key, pos);
@@ -232,7 +253,7 @@ public:
return false;
}
- _FORCE_INLINE_ bool has(const TKey &p_key) {
+ _FORCE_INLINE_ bool has(const TKey &p_key) const {
uint32_t _pos = 0;
return _lookup_pos(p_key, _pos);
}
@@ -302,6 +323,9 @@ public:
return it;
}
+ OAHashMap(const OAHashMap &) = delete; // Delete the copy constructor so we don't get unexpected copies and dangling pointers.
+ OAHashMap &operator=(const OAHashMap &) = delete; // Same for assignment operator.
+
OAHashMap(uint32_t p_initial_capacity = 64) {
capacity = p_initial_capacity;
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index b4c44fe8eb..e510603281 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -165,6 +165,7 @@
</argument>
<description>
Queues an animation for playback once the current one is done.
+ [b]Note:[/b] If a looped animation is currently playing, the queued animation will never play unless the looped animation is stopped somehow.
</description>
</method>
<method name="remove_animation">
diff --git a/doc/classes/ClassDB.xml b/doc/classes/ClassDB.xml
index b7b77bc02a..fd08643dd5 100644
--- a/doc/classes/ClassDB.xml
+++ b/doc/classes/ClassDB.xml
@@ -134,7 +134,7 @@
<argument index="2" name="no_inheritance" type="bool" default="false">
</argument>
<description>
- Returns whether [code]class[/code] (or its ancestry if [code]no_inheritance[/code] is false) has a method called [code]method[/code] or not.
+ Returns whether [code]class[/code] (or its ancestry if [code]no_inheritance[/code] is [code]false[/code]) has a method called [code]method[/code] or not.
</description>
</method>
<method name="class_has_signal" qualifiers="const">
@@ -201,7 +201,7 @@
<argument index="0" name="class" type="String">
</argument>
<description>
- Returns whether this class is enabled or not.
+ Returns whether this [code]class[/code] is enabled or not.
</description>
</method>
<method name="is_parent_class" qualifiers="const">
diff --git a/doc/classes/Directory.xml b/doc/classes/Directory.xml
index 9294a515d2..8aae85563a 100644
--- a/doc/classes/Directory.xml
+++ b/doc/classes/Directory.xml
@@ -127,8 +127,8 @@
</argument>
<description>
Initializes the stream used to list all files and directories using the [method get_next] function, closing the current opened stream if needed. Once the stream has been processed, it should typically be closed with [method list_dir_end].
- If you pass [code]skip_navigational[/code], then [code].[/code] and [code]..[/code] would be filtered out.
- If you pass [code]skip_hidden[/code], then hidden files would be filtered out.
+ If [code]skip_navigational[/code] is [code]true[/code], [code].[/code] and [code]..[/code] are filtered out.
+ If [code]skip_hidden[/code] is [code]true[/code], hidden files are filtered out.
</description>
</method>
<method name="list_dir_end">
diff --git a/doc/classes/GeometryInstance.xml b/doc/classes/GeometryInstance.xml
index b108e1be7c..02f2c27043 100644
--- a/doc/classes/GeometryInstance.xml
+++ b/doc/classes/GeometryInstance.xml
@@ -46,22 +46,26 @@
</member>
<member name="lod_max_distance" type="float" setter="set_lod_max_distance" getter="get_lod_max_distance" default="0.0">
The GeometryInstance's max LOD distance.
+ [b]Note:[/b] This property currently has no effect.
</member>
<member name="lod_max_hysteresis" type="float" setter="set_lod_max_hysteresis" getter="get_lod_max_hysteresis" default="0.0">
The GeometryInstance's max LOD margin.
+ [b]Note:[/b] This property currently has no effect.
</member>
<member name="lod_min_distance" type="float" setter="set_lod_min_distance" getter="get_lod_min_distance" default="0.0">
The GeometryInstance's min LOD distance.
+ [b]Note:[/b] This property currently has no effect.
</member>
<member name="lod_min_hysteresis" type="float" setter="set_lod_min_hysteresis" getter="get_lod_min_hysteresis" default="0.0">
The GeometryInstance's min LOD margin.
+ [b]Note:[/b] This property currently has no effect.
</member>
<member name="material_override" type="Material" setter="set_material_override" getter="get_material_override">
The material override for the whole geometry.
- If there is a material in [code]material_override[/code], it will be used instead of any material set in any material slot of the mesh.
+ If a material is assigned to this property, it will be used instead of any material set in any material slot of the mesh.
</member>
<member name="use_in_baked_light" type="bool" setter="set_flag" getter="get_flag" default="false">
- If [code]true[/code], this GeometryInstance will be used when baking lights using a [GIProbe] and/or any other form of baked lighting.
+ If [code]true[/code], this GeometryInstance will be used when baking lights using a [GIProbe] or [BakedLightmap].
</member>
</members>
<constants>
@@ -78,10 +82,10 @@
</constant>
<constant name="SHADOW_CASTING_SETTING_SHADOWS_ONLY" value="3" enum="ShadowCastingSetting">
Will only show the shadows casted from this object.
- In other words: The actual mesh will not be visible, only the shadows casted from the mesh.
+ In other words, the actual mesh will not be visible, only the shadows casted from the mesh will be.
</constant>
<constant name="FLAG_USE_BAKED_LIGHT" value="0" enum="Flags">
- Will allow the GeometryInstance to be used when baking lights using a [GIProbe] and/or any other form of baked lighting.
+ Will allow the GeometryInstance to be used when baking lights using a [GIProbe] or [BakedLightmap].
</constant>
<constant name="FLAG_DRAW_NEXT_FRAME_IF_VISIBLE" value="1" enum="Flags">
Unused in this class, exposed for consistency with [enum VisualServer.InstanceFlags].
diff --git a/doc/classes/MainLoop.xml b/doc/classes/MainLoop.xml
index f5bf12a876..fedf77bfd2 100644
--- a/doc/classes/MainLoop.xml
+++ b/doc/classes/MainLoop.xml
@@ -4,7 +4,7 @@
Abstract base class for the game's main loop.
</brief_description>
<description>
- [MainLoop] is the abstract base class for a Godot project's game loop. It in inherited by [SceneTree], which is the default game loop implementation used in Godot projects, though it is also possible to write and use one's own [MainLoop] subclass instead of the scene tree.
+ [MainLoop] is the abstract base class for a Godot project's game loop. It is inherited by [SceneTree], which is the default game loop implementation used in Godot projects, though it is also possible to write and use one's own [MainLoop] subclass instead of the scene tree.
Upon the application start, a [MainLoop] implementation must be provided to the OS; otherwise, the application will exit. This happens automatically (and a [SceneTree] is created) unless a main [Script] is provided from the command line (with e.g. [code]godot -s my_loop.gd[/code], which should then be a [MainLoop] implementation.
Here is an example script implementing a simple [MainLoop]:
[codeblock]
diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl
index 232b9ce7c0..1952e201aa 100644
--- a/drivers/gles3/shaders/copy.glsl
+++ b/drivers/gles3/shaders/copy.glsl
@@ -165,11 +165,11 @@ void main() {
#elif defined(USE_ASYM_PANO)
// When an asymmetrical projection matrix is used (applicable for stereoscopic rendering i.e. VR) we need to do this calculation per fragment to get a perspective correct result.
- // Note that we're ignoring the x-offset for IPD, with Z sufficiently in the distance it becomes neglectible, as a result we could probably just set cube_normal.z to -1.
+ // Asymmetrical projection means the center of projection is no longer in the center of the screen but shifted.
// The Matrix[2][0] (= asym_proj.x) and Matrix[2][1] (= asym_proj.z) values are what provide the right shift in the image.
vec3 cube_normal;
- cube_normal.z = -1000000.0;
+ cube_normal.z = -1.0;
cube_normal.x = (cube_normal.z * (-uv_interp.x - asym_proj.x)) / asym_proj.y;
cube_normal.y = (cube_normal.z * (-uv_interp.y - asym_proj.z)) / asym_proj.a;
cube_normal = mat3(sky_transform) * mat3(pano_transform) * cube_normal;
diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp
index 5820a59019..071734eb48 100644
--- a/drivers/unix/file_access_unix.cpp
+++ b/drivers/unix/file_access_unix.cpp
@@ -38,6 +38,8 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <errno.h>
+
#if defined(UNIX_ENABLED)
#include <unistd.h>
#endif
@@ -112,8 +114,15 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) {
f = fopen(path.utf8().get_data(), mode_string);
if (f == NULL) {
- last_error = ERR_FILE_CANT_OPEN;
- return ERR_FILE_CANT_OPEN;
+ switch (errno) {
+ case ENOENT: {
+ last_error = ERR_FILE_NOT_FOUND;
+ } break;
+ default: {
+ last_error = ERR_FILE_CANT_OPEN;
+ } break;
+ }
+ return last_error;
} else {
last_error = OK;
flags = p_mode_flags;
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index c12a2d75a8..fb21c0c5a1 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -38,6 +38,7 @@
#include <shlwapi.h>
#include <windows.h>
+#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <tchar.h>
@@ -114,11 +115,18 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) {
path = path + ".tmp";
}
- _wfopen_s(&f, path.c_str(), mode_string);
+ errno_t errcode = _wfopen_s(&f, path.c_str(), mode_string);
if (f == NULL) {
- last_error = ERR_FILE_CANT_OPEN;
- return ERR_FILE_CANT_OPEN;
+ switch (errcode) {
+ case ENOENT: {
+ last_error = ERR_FILE_NOT_FOUND;
+ } break;
+ default: {
+ last_error = ERR_FILE_CANT_OPEN;
+ } break;
+ }
+ return last_error;
} else {
last_error = OK;
flags = p_mode_flags;
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index 1f43740858..8c9aedc7b7 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -257,6 +257,7 @@ void EditorFileDialog::_post_popup() {
if (is_visible_in_tree()) {
Ref<Texture> folder = get_icon("folder", "FileDialog");
+ const Color folder_color = get_color("folder", "FileDialog");
recent->clear();
bool res = access == ACCESS_RESOURCES;
@@ -274,6 +275,7 @@ void EditorFileDialog::_post_popup() {
recent->add_item(name, folder);
recent->set_item_metadata(recent->get_item_count() - 1, recentd[i]);
+ recent->set_item_icon_modulate(recent->get_item_count() - 1, folder_color);
}
local_history.clear();
@@ -734,6 +736,7 @@ void EditorFileDialog::update_file_list() {
dir_access->list_dir_begin();
Ref<Texture> folder = get_icon("folder", "FileDialog");
+ const Color folder_color = get_color("folder", "FileDialog");
List<String> files;
List<String> dirs;
@@ -774,6 +777,7 @@ void EditorFileDialog::update_file_list() {
d["dir"] = true;
item_list->set_item_metadata(item_list->get_item_count() - 1, d);
+ item_list->set_item_icon_modulate(item_list->get_item_count() - 1, folder_color);
dirs.pop_front();
}
@@ -1200,6 +1204,7 @@ void EditorFileDialog::_update_favorites() {
String current = get_current_dir();
Ref<Texture> folder_icon = get_icon("Folder", "EditorIcons");
+ const Color folder_color = get_color("folder", "FileDialog");
favorites->clear();
favorite->set_pressed(false);
@@ -1230,6 +1235,7 @@ void EditorFileDialog::_update_favorites() {
}
favorites->set_item_metadata(favorites->get_item_count() - 1, favorited[i]);
+ favorites->set_item_icon_modulate(favorites->get_item_count() - 1, folder_color);
if (setthis) {
favorite->set_pressed(true);
diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp
index fbfc999dbf..af79c21f85 100644
--- a/editor/editor_help_search.cpp
+++ b/editor/editor_help_search.cpp
@@ -354,7 +354,7 @@ bool EditorHelpSearch::Runner::_phase_match_classes() {
match.constants.push_back(const_cast<DocData::ConstantDoc *>(&class_doc.constants[i]));
if (search_flags & SEARCH_PROPERTIES)
for (int i = 0; i < class_doc.properties.size(); i++)
- if (_match_string(term, class_doc.properties[i].name))
+ if (_match_string(term, class_doc.properties[i].name) || _match_string(term, class_doc.properties[i].getter) || _match_string(term, class_doc.properties[i].setter))
match.properties.push_back(const_cast<DocData::PropertyDoc *>(&class_doc.properties[i]));
if (search_flags & SEARCH_THEME_ITEMS)
for (int i = 0; i < class_doc.theme_properties.size(); i++)
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 311f8b295b..3e97dbd96c 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -2954,7 +2954,6 @@ void EditorNode::set_addon_plugin_enabled(const String &p_addon, bool p_enabled,
EditorPlugin *ep = memnew(EditorPlugin);
ep->set_script(script.get_ref_ptr());
- ep->set_dir_cache(p_addon);
plugin_addons[p_addon] = ep;
add_editor_plugin(ep, p_config_changed);
@@ -3828,9 +3827,13 @@ void EditorNode::show_accept(const String &p_text, const String &p_title) {
void EditorNode::show_warning(const String &p_text, const String &p_title) {
- warning->set_text(p_text);
- warning->set_title(p_title);
- warning->popup_centered_minsize();
+ if (warning->is_inside_tree()) {
+ warning->set_text(p_text);
+ warning->set_title(p_title);
+ warning->popup_centered_minsize();
+ } else {
+ WARN_PRINTS(p_title + " " + p_text);
+ }
}
void EditorNode::_copy_warning(const String &p_str) {
@@ -5512,6 +5515,9 @@ EditorNode::EditorNode() {
}
}
+ // Define a minimum window size to prevent UI elements from overlapping or being cut off
+ OS::get_singleton()->set_min_window_size(Size2(1024, 600) * EDSCALE);
+
ResourceLoader::set_abort_on_missing_resources(false);
FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files"));
EditorFileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files"));
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 4b6afcbb86..e27f1ab9eb 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -324,13 +324,6 @@ void EditorPlugin::remove_autoload_singleton(const String &p_name) {
EditorNode::get_singleton()->get_project_settings()->get_autoload_settings()->autoload_remove(p_name);
}
-Ref<ConfigFile> EditorPlugin::get_config() {
- Ref<ConfigFile> cf = memnew(ConfigFile);
- Error err = cf->load(_dir_cache.plus_file("plugin.cfg"));
- ERR_FAIL_COND_V(err != OK, cf);
- return cf;
-}
-
ToolButton *EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title) {
ERR_FAIL_NULL_V(p_control, NULL);
return EditorNode::get_singleton()->add_bottom_panel_item(p_title, p_control);
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 7b6f55e93d..8941dfa28c 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -117,7 +117,6 @@ class EditorPlugin : public Node {
bool force_draw_over_forwarding_enabled;
String last_main_screen_name;
- String _dir_cache;
protected:
static void _bind_methods();
@@ -236,10 +235,6 @@ public:
void add_autoload_singleton(const String &p_name, const String &p_path);
void remove_autoload_singleton(const String &p_name);
- void set_dir_cache(const String &p_dir) { _dir_cache = p_dir; }
- String get_dir_cache() { return _dir_cache; }
- Ref<ConfigFile> get_config();
-
void enable_plugin();
void disable_plugin();
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 2925e7a496..e342b784c9 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -1224,7 +1224,7 @@ void EditorSettings::set_project_metadata(const String &p_section, const String
String path = get_project_settings_dir().plus_file("project_metadata.cfg");
Error err;
err = cf->load(path);
- ERR_FAIL_COND(err != OK);
+ ERR_FAIL_COND(err != OK && err != ERR_FILE_NOT_FOUND);
cf->set_value(p_section, p_key, p_data);
err = cf->save(path);
ERR_FAIL_COND(err != OK);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 9c1e919824..d962001b38 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -1066,6 +1066,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// FileDialog
theme->set_icon("folder", "FileDialog", theme->get_icon("Folder", "EditorIcons"));
+ // Use a different color for folder icons to make them easier to distinguish from files.
+ // On a light theme, the icon will be dark, so we need to lighten it before blending it with the accent color.
+ theme->set_color("folder", "FileDialog", (dark_theme ? Color(1, 1, 1) : Color(5, 5, 5)).linear_interpolate(accent_color, 0.7));
theme->set_color("files_disabled", "FileDialog", font_color_disabled);
// color picker
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index e4823c7745..83904800e9 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -64,6 +64,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
subdirectory_item->set_text(0, dname);
subdirectory_item->set_icon(0, get_icon("Folder", "EditorIcons"));
+ subdirectory_item->set_icon_color(0, get_color("folder", "FileDialog"));
subdirectory_item->set_selectable(0, true);
String lpath = p_dir->get_path();
subdirectory_item->set_metadata(0, lpath);
@@ -186,15 +187,19 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo
continue;
Ref<Texture> folder_icon = get_icon("Folder", "EditorIcons");
+ const Color folder_color = get_color("folder", "FileDialog");
String text;
Ref<Texture> icon;
+ Color color;
if (fave == "res://") {
text = "/";
icon = folder_icon;
+ color = folder_color;
} else if (fave.ends_with("/")) {
text = fave.substr(0, fave.length() - 1).get_file();
icon = folder_icon;
+ color = folder_color;
} else {
text = fave.get_file();
int index;
@@ -204,12 +209,14 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo
} else {
icon = get_icon("File", "EditorIcons");
}
+ color = Color(1, 1, 1);
}
if (searched_string.length() == 0 || text.to_lower().find(searched_string) >= 0) {
TreeItem *ti = tree->create_item(favorites);
ti->set_text(0, text);
ti->set_icon(0, icon);
+ ti->set_icon_color(0, color);
ti->set_tooltip(0, fave);
ti->set_selectable(0, true);
ti->set_metadata(0, fave);
@@ -639,6 +646,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
}
Ref<Texture> folder_icon = (use_thumbnails) ? folder_thumbnail : get_icon("folder", "FileDialog");
+ const Color folder_color = get_color("folder", "FileDialog");
// Build the FileInfo list
List<FileInfo> filelist;
@@ -716,6 +724,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
files->set_item_metadata(files->get_item_count() - 1, bd);
files->set_item_selectable(files->get_item_count() - 1, false);
+ files->set_item_icon_modulate(files->get_item_count() - 1, folder_color);
}
for (int i = 0; i < efd->get_subdir_count(); i++) {
@@ -724,6 +733,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
files->add_item(dname, folder_icon, true);
files->set_item_metadata(files->get_item_count() - 1, directory.plus_file(dname) + "/");
+ files->set_item_icon_modulate(files->get_item_count() - 1, folder_color);
if (cselection.has(dname)) {
files->select(files->get_item_count() - 1, false);
diff --git a/editor/icons/icon_editor_curve_handle.svg b/editor/icons/icon_editor_curve_handle.svg
new file mode 100644
index 0000000000..c405ceab9d
--- /dev/null
+++ b/editor/icons/icon_editor_curve_handle.svg
@@ -0,0 +1 @@
+<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><circle cx="5" cy="5" fill="#fefefe" r="2.75" stroke="#000" stroke-linecap="square"/></svg> \ No newline at end of file
diff --git a/editor/icons/icon_editor_path_sharp_handle.svg b/editor/icons/icon_editor_path_sharp_handle.svg
new file mode 100644
index 0000000000..db160dfeae
--- /dev/null
+++ b/editor/icons/icon_editor_path_sharp_handle.svg
@@ -0,0 +1 @@
+<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><path d="m-3.035534-10.106602h6.071068v6.071068h-6.071068z" fill="#fefefe" stroke="#000" stroke-linecap="square" transform="matrix(-.70710678 .70710678 -.70710678 -.70710678 0 0)"/></svg> \ No newline at end of file
diff --git a/editor/icons/icon_editor_path_smooth_handle.svg b/editor/icons/icon_editor_path_smooth_handle.svg
new file mode 100644
index 0000000000..34f3d290bd
--- /dev/null
+++ b/editor/icons/icon_editor_path_smooth_handle.svg
@@ -0,0 +1 @@
+<svg height="10" viewBox="0 0 10 10" width="10" xmlns="http://www.w3.org/2000/svg"><path d="m1.5-8.5h7v7h-7z" fill="#fefefe" stroke="#000" stroke-linecap="square" transform="rotate(90)"/></svg> \ No newline at end of file
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp
index 574b47d770..7f023af848 100644
--- a/editor/plugins/abstract_polygon_2d_editor.cpp
+++ b/editor/plugins/abstract_polygon_2d_editor.cpp
@@ -571,7 +571,8 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl
return;
Transform2D xform = canvas_item_editor->get_canvas_transform() * _get_node()->get_global_transform();
- const Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons");
+ // All polygon points are sharp, so use the sharp handle icon
+ const Ref<Texture> handle = get_icon("EditorPathSharpHandle", "EditorIcons");
const Vertex active_point = get_active_point();
const int n_polygons = _get_polygon_count();
diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp
index b87bd29cbd..f02dc0bd6d 100644
--- a/editor/plugins/path_2d_editor_plugin.cpp
+++ b/editor/plugins/path_2d_editor_plugin.cpp
@@ -367,18 +367,18 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
void Path2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
- if (!node)
+ if (!node || !node->is_visible_in_tree() || !node->get_curve().is_valid())
return;
- if (!node->is_visible_in_tree())
- return;
+ Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
- if (!node->get_curve().is_valid())
- return;
+ const Ref<Texture> path_sharp_handle = get_icon("EditorPathSharpHandle", "EditorIcons");
+ const Ref<Texture> path_smooth_handle = get_icon("EditorPathSmoothHandle", "EditorIcons");
+ // Both handle icons must be of the same size
+ const Size2 handle_size = path_sharp_handle->get_size();
- Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
- Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons");
- Size2 handle_size = handle->get_size();
+ const Ref<Texture> curve_handle = get_icon("EditorCurveHandle", "EditorIcons");
+ const Size2 curve_handle_size = curve_handle->get_size();
Ref<Curve2D> curve = node->get_curve();
@@ -387,19 +387,35 @@ void Path2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
for (int i = 0; i < len; i++) {
Vector2 point = xform.xform(curve->get_point_position(i));
- vpc->draw_texture_rect(handle, Rect2(point - handle_size * 0.5, handle_size), false, Color(1, 1, 1, 1));
+ // Determines the point icon to be used
+ bool smooth = false;
if (i < len - 1) {
Vector2 pointout = xform.xform(curve->get_point_position(i) + curve->get_point_out(i));
- vpc->draw_line(point, pointout, Color(0.5, 0.5, 1.0, 0.8), 1.0);
- vpc->draw_texture_rect(handle, Rect2(pointout - handle_size * 0.5, handle_size), false, Color(1, 0.5, 1, 0.3));
+ if (point != pointout) {
+ smooth = true;
+ // Draw the line with a dark and light color to be visible on all backgrounds
+ vpc->draw_line(point, pointout, Color(0, 0, 0, 0.5), Math::round(EDSCALE), true);
+ vpc->draw_line(point, pointout, Color(1, 1, 1, 0.5), Math::round(EDSCALE), true);
+ vpc->draw_texture_rect(curve_handle, Rect2(pointout - curve_handle_size * 0.5, curve_handle_size), false, Color(1, 1, 1, 0.75));
+ }
}
if (i > 0) {
Vector2 pointin = xform.xform(curve->get_point_position(i) + curve->get_point_in(i));
- vpc->draw_line(point, pointin, Color(0.5, 0.5, 1.0, 0.8), 1.0);
- vpc->draw_texture_rect(handle, Rect2(pointin - handle_size * 0.5, handle_size), false, Color(1, 0.5, 1, 0.3));
+ if (point != pointin) {
+ smooth = true;
+ // Draw the line with a dark and light color to be visible on all backgrounds
+ vpc->draw_line(point, pointin, Color(0, 0, 0, 0.5), Math::round(EDSCALE), true);
+ vpc->draw_line(point, pointin, Color(1, 1, 1, 0.5), Math::round(EDSCALE), true);
+ vpc->draw_texture_rect(curve_handle, Rect2(pointin - curve_handle_size * 0.5, curve_handle_size), false, Color(1, 1, 1, 0.75));
+ }
}
+
+ vpc->draw_texture_rect(
+ smooth ? path_smooth_handle : path_sharp_handle,
+ Rect2(point - handle_size * 0.5, handle_size),
+ false);
}
if (on_edge) {
diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp
index 1ae5acc5ff..2493380585 100644
--- a/editor/plugins/path_editor_plugin.cpp
+++ b/editor/plugins/path_editor_plugin.cpp
@@ -652,7 +652,6 @@ PathSpatialGizmoPlugin::PathSpatialGizmoPlugin() {
Color path_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/path", Color(0.5, 0.5, 1.0, 0.8));
create_material("path_material", path_color);
- path_color.a = 0.4;
- create_material("path_thin_material", path_color);
+ create_material("path_thin_material", Color(0.5, 0.5, 0.5));
create_handle_material("handles");
}
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index 59004a08c0..bd532a6418 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -1045,8 +1045,8 @@ void Polygon2DEditor::_uv_draw() {
}
}
- Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons");
- Ref<Texture> internal_handle = get_icon("EditorInternalHandle", "EditorIcons");
+ // All UV points are sharp, so use the sharp handle icon
+ Ref<Texture> handle = get_icon("EditorPathSharpHandle", "EditorIcons");
Color poly_line_color = Color(0.9, 0.5, 0.5);
if (polygons.size() || polygon_create.size()) {
@@ -1120,7 +1120,8 @@ void Polygon2DEditor::_uv_draw() {
if (i < uv_draw_max) {
uv_edit_draw->draw_texture(handle, mtx.xform(uvs[i]) - handle->get_size() * 0.5);
} else {
- uv_edit_draw->draw_texture(internal_handle, mtx.xform(uvs[i]) - internal_handle->get_size() * 0.5);
+ // Internal vertex
+ uv_edit_draw->draw_texture(handle, mtx.xform(uvs[i]) - handle->get_size() * 0.5, Color(0.6, 0.8, 1));
}
}
}
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index b2f06ca41f..b4cce9745e 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -2006,7 +2006,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
// Tools
paint_button = memnew(ToolButton);
paint_button->set_shortcut(ED_SHORTCUT("tile_map_editor/paint_tile", TTR("Paint Tile"), KEY_P));
- paint_button->set_tooltip(TTR("Shift+RMB: Line Draw\nShift+Ctrl+RMB: Rectangle Paint"));
+ paint_button->set_tooltip(TTR("Shift+LMB: Line Draw\nShift+Ctrl+LMB: Rectangle Paint"));
paint_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_NONE));
paint_button->set_toggle_mode(true);
toolbar->add_child(paint_button);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 619fb15a68..589964f620 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -2487,8 +2487,8 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Sin", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_SIN, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("SinH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the hyperbolic sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_SINH, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Sqrt", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the square root of the parameter."), VisualShaderNodeScalarFunc::FUNC_SQRT, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("SmoothStep", "Scalar", "Functions", "VisualShaderNodeScalarSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge0' and 1.0 if x is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
- add_options.push_back(AddOption("Step", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Step function( scalar(edge), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge' and otherwise 1.0."), VisualShaderNodeScalarOp::OP_STEP, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("SmoothStep", "Scalar", "Functions", "VisualShaderNodeScalarSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if x is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("Step", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Step function( scalar(edge), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), VisualShaderNodeScalarOp::OP_STEP, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Tan", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_TAN, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("TanH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the hyperbolic tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_TANH, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Trunc", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Finds the truncated value of the parameter."), VisualShaderNodeScalarFunc::FUNC_TRUNC, VisualShaderNode::PORT_TYPE_SCALAR));
@@ -2581,10 +2581,10 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeVectorSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeVectorScalarSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge' and otherwise 1.0."), VisualShaderNodeVectorOp::OP_STEP, VisualShaderNode::PORT_TYPE_VECTOR));
- add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeVectorScalarStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge' and otherwise 1.0."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeVectorSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeVectorScalarSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), VisualShaderNodeVectorOp::OP_STEP, VisualShaderNode::PORT_TYPE_VECTOR));
+ add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeVectorScalarStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller than 'edge' and otherwise 1.0."), -1, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the hyperbolic tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNode::PORT_TYPE_VECTOR));
add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the truncated value of the parameter."), VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNode::PORT_TYPE_VECTOR));
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 0f3c4c924a..a418773131 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -997,7 +997,6 @@ public:
void load_projects();
void set_search_term(String p_search_term);
- void set_filter_option(ProjectListFilter::FilterOption p_option);
void set_order_option(ProjectListFilter::FilterOption p_option);
void sort_projects();
int get_project_count() const;
@@ -1030,7 +1029,6 @@ private:
static void load_project_data(const String &p_property_key, Item &p_item, bool p_favorite);
String _search_term;
- ProjectListFilter::FilterOption _filter_option;
ProjectListFilter::FilterOption _order_option;
Set<String> _selected_project_keys;
String _last_clicked; // Project key
@@ -1063,7 +1061,6 @@ struct ProjectListComparator {
};
ProjectList::ProjectList() {
- _filter_option = ProjectListFilter::FILTER_NAME;
_order_option = ProjectListFilter::FILTER_MODIFIED;
_scroll_children = memnew(VBoxContainer);
@@ -1303,12 +1300,6 @@ void ProjectList::set_search_term(String p_search_term) {
_search_term = p_search_term;
}
-void ProjectList::set_filter_option(ProjectListFilter::FilterOption p_option) {
- if (_filter_option != p_option) {
- _filter_option = p_option;
- }
-}
-
void ProjectList::set_order_option(ProjectListFilter::FilterOption p_option) {
if (_order_option != p_option) {
_order_option = p_option;
@@ -1328,11 +1319,18 @@ void ProjectList::sort_projects() {
bool visible = true;
if (_search_term != "") {
- if (_filter_option == ProjectListFilter::FILTER_PATH) {
- visible = item.path.findn(_search_term) != -1;
- } else if (_filter_option == ProjectListFilter::FILTER_NAME) {
- visible = item.project_name.findn(_search_term) != -1;
+
+ String search_path;
+ if (_search_term.find("/") != -1) {
+ // Search path will match the whole path
+ search_path = item.path;
+ } else {
+ // Search path will only match the last path component to make searching more strict
+ search_path = item.path.get_file();
}
+
+ // When searching, display projects whose name or path contain the search term
+ visible = item.project_name.findn(_search_term) != -1 || search_path.findn(_search_term) != -1;
}
item.control->set_visible(visible);
@@ -1694,7 +1692,6 @@ void ProjectList::_bind_methods() {
ClassDB::bind_method("_panel_input", &ProjectList::_panel_input);
ClassDB::bind_method("_favorite_pressed", &ProjectList::_favorite_pressed);
ClassDB::bind_method("_show_project", &ProjectList::_show_project);
- //ClassDB::bind_method("_unhandled_input", &ProjectList::_unhandled_input);
ADD_SIGNAL(MethodInfo(SIGNAL_SELECTION_CHANGED));
ADD_SIGNAL(MethodInfo(SIGNAL_PROJECT_ASK_OPEN));
@@ -1754,8 +1751,19 @@ void ProjectManager::_unhandled_input(const Ref<InputEvent> &p_ev) {
if (k.is_valid()) {
- if (!k->is_pressed())
+ if (!k->is_pressed()) {
return;
+ }
+
+ // Pressing Command + Q quits the Project Manager
+ // This is handled by the platform implementation on macOS,
+ // so only define the shortcut on other platforms
+#ifndef OSX_ENABLED
+ if (k->get_scancode_with_modifiers() == (KEY_MASK_CMD | KEY_Q)) {
+ _dim_window();
+ get_tree()->quit();
+ }
+#endif
if (tabs->get_current_tab() != 0)
return;
@@ -1834,7 +1842,6 @@ void ProjectManager::_unhandled_input(const Ref<InputEvent> &p_ev) {
void ProjectManager::_load_recent_projects() {
- _project_list->set_filter_option(project_filter->get_filter_option());
_project_list->set_order_option(project_order_filter->get_filter_option());
_project_list->set_search_term(project_filter->get_search_term());
_project_list->load_projects();
@@ -2198,7 +2205,6 @@ void ProjectManager::_on_order_option_changed() {
}
void ProjectManager::_on_filter_option_changed() {
- _project_list->set_filter_option(project_filter->get_filter_option());
_project_list->set_search_term(project_filter->get_search_term());
_project_list->sort_projects();
}
@@ -2270,6 +2276,9 @@ ProjectManager::ProjectManager() {
} break;
}
+ // Define a minimum window size to prevent UI elements from overlapping or being cut off
+ OS::get_singleton()->set_min_window_size(Size2(750, 420) * EDSCALE);
+
#ifndef OSX_ENABLED
// The macOS platform implementation uses its own hiDPI window resizing code
// TODO: Resize windows on hiDPI displays on Windows and Linux and remove the line below
@@ -2295,26 +2304,11 @@ ProjectManager::ProjectManager() {
VBoxContainer *vb = memnew(VBoxContainer);
panel->add_child(vb);
vb->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 8 * EDSCALE);
- vb->add_constant_override("separation", 8 * EDSCALE);
String cp;
cp += 0xA9;
OS::get_singleton()->set_window_title(VERSION_NAME + String(" - ") + TTR("Project Manager") + " - " + cp + " 2007-2019 Juan Linietsky, Ariel Manzur & Godot Contributors");
- HBoxContainer *top_hb = memnew(HBoxContainer);
- vb->add_child(top_hb);
- Label *l = memnew(Label);
- l->set_text(VERSION_NAME + String(" - ") + TTR("Project Manager"));
- top_hb->add_child(l);
- top_hb->add_spacer();
- l = memnew(Label);
- String hash = String(VERSION_HASH);
- if (hash.length() != 0)
- hash = "." + hash.left(9);
- l->set_text("v" VERSION_FULL_BUILD "" + hash);
- l->set_align(Label::ALIGN_CENTER);
- top_hb->add_child(l);
-
Control *center_box = memnew(Control);
center_box->set_v_size_flags(SIZE_EXPAND_FILL);
vb->add_child(center_box);
@@ -2322,11 +2316,12 @@ ProjectManager::ProjectManager() {
tabs = memnew(TabContainer);
center_box->add_child(tabs);
tabs->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+ tabs->set_tab_align(TabContainer::ALIGN_LEFT);
HBoxContainer *tree_hb = memnew(HBoxContainer);
projects_hb = tree_hb;
- projects_hb->set_name(TTR("Project List"));
+ projects_hb->set_name(TTR("Projects"));
tabs->add_child(tree_hb);
@@ -2343,6 +2338,7 @@ ProjectManager::ProjectManager() {
sort_filter_titles.push_back("Path");
sort_filter_titles.push_back("Last Modified");
project_order_filter = memnew(ProjectListFilter);
+ project_order_filter->add_filter_option();
project_order_filter->_setup_filters(sort_filter_titles);
project_order_filter->set_filter_size(150);
sort_filters->add_child(project_order_filter);
@@ -2353,21 +2349,12 @@ ProjectManager::ProjectManager() {
project_order_filter->set_filter_option((ProjectListFilter::FilterOption)projects_sorting_order);
sort_filters->add_spacer(true);
- Label *search_label = memnew(Label);
- search_label->set_text(TTR("Search:"));
- sort_filters->add_child(search_label);
-
- HBoxContainer *search_filters = memnew(HBoxContainer);
- Vector<String> vec2;
- vec2.push_back("Name");
- vec2.push_back("Path");
+
project_filter = memnew(ProjectListFilter);
- project_filter->_setup_filters(vec2);
project_filter->add_search_box();
- search_filters->add_child(project_filter);
project_filter->connect("filter_changed", this, "_on_filter_option_changed");
project_filter->set_custom_minimum_size(Size2(280, 10) * EDSCALE);
- sort_filters->add_child(search_filters);
+ sort_filters->add_child(project_filter);
search_tree_vb->add_child(sort_filters);
@@ -2457,6 +2444,17 @@ ProjectManager::ProjectManager() {
settings_hb->set_alignment(BoxContainer::ALIGN_END);
settings_hb->set_h_grow_direction(Control::GROW_DIRECTION_BEGIN);
+ Label *version_label = memnew(Label);
+ String hash = String(VERSION_HASH);
+ if (hash.length() != 0) {
+ hash = "." + hash.left(9);
+ }
+ version_label->set_text("v" VERSION_FULL_BUILD "" + hash);
+ // Fade out the version label to be less prominent, but still readable
+ version_label->set_self_modulate(Color(1, 1, 1, 0.6));
+ version_label->set_align(Label::ALIGN_CENTER);
+ settings_hb->add_child(version_label);
+
language_btn = memnew(OptionButton);
language_btn->set_flat(true);
language_btn->set_focus_mode(Control::FOCUS_NONE);
@@ -2489,27 +2487,6 @@ ProjectManager::ProjectManager() {
center_box->add_child(settings_hb);
settings_hb->set_anchors_and_margins_preset(Control::PRESET_TOP_RIGHT);
- CenterContainer *cc = memnew(CenterContainer);
- Button *cancel = memnew(Button);
- cancel->set_text(TTR("Exit"));
- cancel->set_custom_minimum_size(Size2(100, 1) * EDSCALE);
-
-#ifndef OSX_ENABLED
- // Pressing Command + Q quits the Project Manager
- // This is handled by the platform implementation on macOS,
- // so only define the shortcut on other platforms
- InputEventKey *quit_key = memnew(InputEventKey);
- quit_key->set_command(true);
- quit_key->set_scancode(KEY_Q);
- ShortCut *quit_shortcut = memnew(ShortCut);
- quit_shortcut->set_shortcut(quit_key);
- cancel->set_shortcut(quit_shortcut);
-#endif
-
- cc->add_child(cancel);
- cancel->connect("pressed", this, "_exit_dialog");
- vb->add_child(cc);
-
//////////////////////////////////////////////////////////////
language_restart_ask = memnew(ConfirmationDialog);
@@ -2630,11 +2607,20 @@ void ProjectListFilter::_bind_methods() {
ADD_SIGNAL(MethodInfo("filter_changed"));
}
+void ProjectListFilter::add_filter_option() {
+ filter_option = memnew(OptionButton);
+ filter_option->set_clip_text(true);
+ filter_option->connect("item_selected", this, "_filter_option_selected");
+ add_child(filter_option);
+}
+
void ProjectListFilter::add_search_box() {
search_box = memnew(LineEdit);
+ search_box->set_placeholder(TTR("Search"));
search_box->connect("text_changed", this, "_search_text_changed");
search_box->set_h_size_flags(SIZE_EXPAND_FILL);
add_child(search_box);
+
has_search_box = true;
}
@@ -2645,13 +2631,6 @@ void ProjectListFilter::set_filter_size(int h_size) {
ProjectListFilter::ProjectListFilter() {
_current_filter = FILTER_NAME;
-
- filter_option = memnew(OptionButton);
- set_filter_size(80);
- filter_option->set_clip_text(true);
- filter_option->connect("item_selected", this, "_filter_option_selected");
- add_child(filter_option);
-
has_search_box = false;
}
diff --git a/editor/project_manager.h b/editor/project_manager.h
index 4ccb99d6bd..2a5fd02892 100644
--- a/editor/project_manager.h
+++ b/editor/project_manager.h
@@ -152,6 +152,7 @@ protected:
public:
void _setup_filters(Vector<String> options);
+ void add_filter_option();
void add_search_box();
void set_filter_size(int h_size);
String get_search_term();
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index f7ba5e5e3c..4c5371769f 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -2037,8 +2037,11 @@ PortalSpatialGizmo::PortalSpatialGizmo(Portal *p_portal) {
RayCastSpatialGizmoPlugin::RayCastSpatialGizmoPlugin() {
- Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
+ const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
create_material("shape_material", gizmo_color);
+ const float gizmo_value = gizmo_color.get_v();
+ const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65);
+ create_material("shape_material_disabled", gizmo_color_disabled);
}
bool RayCastSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) {
@@ -2064,7 +2067,8 @@ void RayCastSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
lines.push_back(Vector3());
lines.push_back(raycast->get_cast_to());
- Ref<SpatialMaterial> material = get_material("shape_material", p_gizmo);
+ const Ref<SpatialMaterial> material =
+ get_material(raycast->is_enabled() ? "shape_material" : "shape_material_disabled", p_gizmo);
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
@@ -3108,8 +3112,11 @@ void BakedIndirectLightGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
////
CollisionShapeSpatialGizmoPlugin::CollisionShapeSpatialGizmoPlugin() {
- Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
+ const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
create_material("shape_material", gizmo_color);
+ const float gizmo_value = gizmo_color.get_v();
+ const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65);
+ create_material("shape_material_disabled", gizmo_color_disabled);
create_handle_material("handles");
}
@@ -3432,7 +3439,8 @@ void CollisionShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
if (s.is_null())
return;
- Ref<Material> material = get_material("shape_material", p_gizmo);
+ const Ref<Material> material =
+ get_material(!cs->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo);
Ref<Material> handles_material = get_material("handles");
if (Object::cast_to<SphereShape>(*s)) {
@@ -3733,8 +3741,11 @@ void CollisionShapeSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
/////
CollisionPolygonSpatialGizmoPlugin::CollisionPolygonSpatialGizmoPlugin() {
- Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
+ const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
create_material("shape_material", gizmo_color);
+ const float gizmo_value = gizmo_color.get_v();
+ const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65);
+ create_material("shape_material_disabled", gizmo_color_disabled);
}
bool CollisionPolygonSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) {
@@ -3770,7 +3781,8 @@ void CollisionPolygonSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
lines.push_back(Vector3(points[i].x, points[i].y, -depth));
}
- Ref<Material> material = get_material("shape_material", p_gizmo);
+ const Ref<Material> material =
+ get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo);
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
diff --git a/misc/ide/jetbrains/build.gradle b/misc/ide/jetbrains/build.gradle
index eb2fbc0e69..dea81b4ea3 100644
--- a/misc/ide/jetbrains/build.gradle
+++ b/misc/ide/jetbrains/build.gradle
@@ -4,7 +4,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.3.2'
+ classpath 'com.android.tools.build:gradle:3.4.2'
}
}
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index fd0d36eddf..f1b3fa2ac6 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -242,7 +242,7 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_
//check if edge and poly share a vertex, of so, assign it to segment_idx
for (int i = 0; i < points.size(); i++) {
for (int j = 0; j < 2; j++) {
- if (Math::is_zero_approx(segment[j].distance_to(points[i].point))) {
+ if (segment[j] == points[i].point) {
segment_idx[j] = i;
inserted_points.push_back(i);
break;
@@ -310,7 +310,7 @@ void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_
Vector2 edgeseg[2] = { points[edges[i].points[0]].point, points[edges[i].points[1]].point };
Vector2 closest = Geometry::get_closest_point_to_segment_2d(segment[j], edgeseg);
- if (Math::is_zero_approx(closest.distance_to(segment[j]))) {
+ if (closest == segment[j]) {
//point rest of this edge
res = closest;
found = true;
@@ -439,7 +439,7 @@ void CSGBrushOperation::BuildPoly::clip(const CSGBrush *p_brush, int p_face, Mes
//transform A points to 2D
- if (Math::is_zero_approx(segment[0].distance_to(segment[1])))
+ if (segment[0] == segment[1])
return; //too small
_clip_segment(p_brush, p_face, segment, mesh_merge, p_for_B);
@@ -461,10 +461,10 @@ void CSGBrushOperation::_collision_callback(const CSGBrush *A, int p_face_a, Map
{
//check if either is a degenerate
- if (Math::is_zero_approx(va[0].distance_to(va[1])) || Math::is_zero_approx(va[0].distance_to(va[2])) || Math::is_zero_approx(va[1].distance_to(va[2])))
+ if (va[0] == va[1] || va[0] == va[2] || va[1] == va[2])
return;
- if (Math::is_zero_approx(vb[0].distance_to(vb[1])) || Math::is_zero_approx(vb[0].distance_to(vb[2])) || Math::is_zero_approx(vb[1].distance_to(vb[2])))
+ if (vb[0] == vb[1] || vb[0] == vb[2] || vb[1] == vb[2])
return;
}
diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp
index 62f09bfbf9..dd86c758bf 100644
--- a/modules/websocket/websocket_multiplayer_peer.cpp
+++ b/modules/websocket/websocket_multiplayer_peer.cpp
@@ -265,7 +265,10 @@ Error WebSocketMultiplayerPeer::_server_relay(int32_t p_from, int32_t p_to, cons
ERR_FAIL_COND_V(p_to == p_from, FAILED);
- return get_peer(p_to)->put_packet(p_buffer, p_buffer_size); // Sending to specific peer
+ Ref<WebSocketPeer> peer_to = get_peer(p_to);
+ ERR_FAIL_COND_V(peer_to.is_null(), FAILED);
+
+ return peer_to->put_packet(p_buffer, p_buffer_size); // Sending to specific peer
}
}
@@ -296,8 +299,6 @@ void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, u
ERR_FAIL_COND(type != SYS_NONE); // Only server sends sys messages
ERR_FAIL_COND(from != p_peer_id); // Someone is cheating
- _server_relay(from, to, in_buffer, size); // Relay if needed
-
if (to == 1) { // This is for the server
_store_pkt(from, to, in_buffer, data_size);
@@ -312,13 +313,9 @@ void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, u
// All but one, for us if not excluded
if (_peer_id != -(int32_t)p_peer_id)
_store_pkt(from, to, in_buffer, data_size);
-
- } else {
-
- // Send to specific peer
- ERR_FAIL_COND(!_peer_map.has(to));
- get_peer(to)->put_packet(in_buffer, size);
}
+ // Relay if needed (i.e. "to" includes a peer that is not the server)
+ _server_relay(from, to, in_buffer, size);
} else {
diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java
index 6e1841fa8b..bfc65a3b78 100644
--- a/platform/android/java/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/src/org/godotengine/godot/Godot.java
@@ -625,6 +625,10 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
GodotLib.ondestroy(this);
super.onDestroy();
+
+ // TODO: This is a temp solution. The proper fix will involve tracking down and properly shutting down each
+ // native Godot components that is started in Godot#onVideoInit.
+ forceQuit();
}
@Override
diff --git a/scene/2d/navigation_2d.cpp b/scene/2d/navigation_2d.cpp
index f644db462b..5cf28d6c89 100644
--- a/scene/2d/navigation_2d.cpp
+++ b/scene/2d/navigation_2d.cpp
@@ -551,7 +551,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
left_poly = p;
portal_left = apex_point;
portal_right = apex_point;
- if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_to(apex_point)))
+ if (!path.size() || path[path.size() - 1] != apex_point)
path.push_back(apex_point);
skip = true;
}
@@ -569,7 +569,7 @@ Vector<Vector2> Navigation2D::get_simple_path(const Vector2 &p_start, const Vect
right_poly = p;
portal_right = apex_point;
portal_left = apex_point;
- if (!path.size() || !Math::is_zero_approx(path[path.size() - 1].distance_to(apex_point)))
+ if (!path.size() || path[path.size() - 1] != apex_point)
path.push_back(apex_point);
}
}
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index f2f53d4354..55c8c7f229 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -110,7 +110,7 @@ void Path2D::_notification(int p_what) {
real_t frac = j / 8.0;
Vector2 p = curve->interpolate(i, frac);
- draw_line(prev_p, p, color, line_width);
+ draw_line(prev_p, p, color, line_width, true);
prev_p = p;
}
}
diff --git a/scene/3d/collision_polygon.cpp b/scene/3d/collision_polygon.cpp
index db07059b32..37aa95fb43 100644
--- a/scene/3d/collision_polygon.cpp
+++ b/scene/3d/collision_polygon.cpp
@@ -151,6 +151,8 @@ float CollisionPolygon::get_depth() const {
void CollisionPolygon::set_disabled(bool p_disabled) {
disabled = p_disabled;
+ update_gizmo();
+
if (parent) {
parent->shape_owner_set_disabled(owner_id, p_disabled);
}
diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp
index 10f92058e0..30eed8f1a7 100644
--- a/scene/3d/ray_cast.cpp
+++ b/scene/3d/ray_cast.cpp
@@ -102,6 +102,8 @@ Vector3 RayCast::get_collision_normal() const {
void RayCast::set_enabled(bool p_enabled) {
enabled = p_enabled;
+ update_gizmo();
+
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint())
set_physics_process_internal(p_enabled);
if (!p_enabled)
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 30aeebfdac..051f832882 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -1200,7 +1200,9 @@ void AnimationPlayer::play(const StringName &p_name, float p_custom_blend, float
}
}
- _stop_playing_caches();
+ if (get_current_animation() != p_name) {
+ _stop_playing_caches();
+ }
c.current.from = &animation_set[name];
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index f1bdbb5ff5..7305d7459c 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -400,6 +400,7 @@ void FileDialog::update_file_list() {
TreeItem *root = tree->create_item();
Ref<Texture> folder = get_icon("folder");
+ const Color folder_color = get_color("folder");
List<String> files;
List<String> dirs;
@@ -429,6 +430,7 @@ void FileDialog::update_file_list() {
TreeItem *ti = tree->create_item(root);
ti->set_text(0, dir_name);
ti->set_icon(0, folder);
+ ti->set_icon_color(0, folder_color);
Dictionary d;
d["name"] = dir_name;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index a771794f25..f137b2618b 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -6112,10 +6112,30 @@ void TextEdit::query_code_comple() {
c--;
}
- if (ofs > 0 && (inquote || _is_completable(l[ofs - 1]) || completion_prefixes.has(String::chr(l[ofs - 1]))))
- emit_signal("request_completion");
- else if (ofs > 1 && l[ofs - 1] == ' ' && completion_prefixes.has(String::chr(l[ofs - 2]))) // Make it work with a space too, it's good enough.
- emit_signal("request_completion");
+ bool ignored = completion_active && !completion_options.empty();
+ if (ignored) {
+ ScriptCodeCompletionOption::Kind kind = ScriptCodeCompletionOption::KIND_PLAIN_TEXT;
+ const ScriptCodeCompletionOption *previous_option = NULL;
+ for (int i = 0; i < completion_options.size(); i++) {
+ const ScriptCodeCompletionOption &current_option = completion_options[i];
+ if (!previous_option) {
+ previous_option = &current_option;
+ kind = current_option.kind;
+ }
+ if (previous_option->kind != current_option.kind) {
+ ignored = false;
+ break;
+ }
+ }
+ ignored = ignored && (kind == ScriptCodeCompletionOption::KIND_FILE_PATH || kind == ScriptCodeCompletionOption::KIND_NODE_PATH || kind == ScriptCodeCompletionOption::KIND_SIGNAL);
+ }
+
+ if (!ignored) {
+ if (ofs > 0 && (inquote || _is_completable(l[ofs - 1]) || completion_prefixes.has(String::chr(l[ofs - 1]))))
+ emit_signal("request_completion");
+ else if (ofs > 1 && l[ofs - 1] == ' ' && completion_prefixes.has(String::chr(l[ofs - 2]))) // Make it work with a space too, it's good enough.
+ emit_signal("request_completion");
+ }
}
void TextEdit::set_code_hint(const String &p_hint) {
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 8c8552ac54..985b38f913 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -2870,9 +2870,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
const Vector3 &v1 = t1.value.loc;
const Vector3 &v2 = t2.value.loc;
- if (Math::is_zero_approx(v0.distance_to(v2))) {
+ if (v0 == v2) {
//0 and 2 are close, let's see if 1 is close
- if (!Math::is_zero_approx(v0.distance_to(v1))) {
+ if (v0 != v1) {
//not close, not optimizable
return false;
}
@@ -2959,9 +2959,9 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
const Vector3 &v1 = t1.value.scale;
const Vector3 &v2 = t2.value.scale;
- if (Math::is_zero_approx(v0.distance_to(v2))) {
+ if (v0 == v2) {
//0 and 2 are close, let's see if 1 is close
- if (!Math::is_zero_approx(v0.distance_to(v1))) {
+ if (v0 != v1) {
//not close, not optimizable
return false;
}
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index fb0fb4f8e3..90787780d3 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -760,6 +760,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// FileDialog
theme->set_icon("folder", "FileDialog", make_icon(icon_folder_png));
+ theme->set_color("folder", "FileDialog", Color(1, 1, 1));
theme->set_color("files_disabled", "FileDialog", Color(0, 0, 0, 0.7));
// colorPicker
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index f7d7c2d1bc..08ce47692c 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -612,6 +612,25 @@ String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port
global_code += String() + "shader_type canvas_item;\n";
+ String global_expressions;
+ for (int i = 0, index = 0; i < TYPE_MAX; i++) {
+ for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
+ Ref<VisualShaderNodeGlobalExpression> global_expression = Object::cast_to<VisualShaderNodeGlobalExpression>(E->get().node.ptr());
+ if (global_expression.is_valid()) {
+
+ String expr = "";
+ expr += "// " + global_expression->get_caption() + ":" + itos(index++) + "\n";
+ expr += global_expression->generate_global(get_mode(), Type(i), -1);
+ expr = expr.replace("\n", "\n\t");
+ expr += "\n";
+ global_expressions += expr;
+ }
+ }
+ }
+
+ global_code += "\n";
+ global_code += global_expressions;
+
//make it faster to go around through shader
VMap<ConnectionKey, const List<Connection>::Element *> input_connections;
VMap<ConnectionKey, const List<Connection>::Element *> output_connections;