summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/area_2d.cpp28
-rw-r--r--scene/2d/collision_object_2d.cpp24
-rw-r--r--scene/2d/joint_2d.cpp (renamed from scene/2d/joints_2d.cpp)4
-rw-r--r--scene/2d/joint_2d.h (renamed from scene/2d/joints_2d.h)8
-rw-r--r--scene/2d/physical_bone_2d.cpp2
-rw-r--r--scene/2d/physical_bone_2d.h4
-rw-r--r--scene/2d/physics_body_2d.cpp260
-rw-r--r--scene/2d/physics_body_2d.h40
-rw-r--r--scene/2d/tile_map.cpp402
-rw-r--r--scene/2d/tile_map.h13
-rw-r--r--scene/3d/area_3d.cpp28
-rw-r--r--scene/3d/camera_3d.cpp244
-rw-r--r--scene/3d/camera_3d.h64
-rw-r--r--scene/3d/collision_object_3d.cpp40
-rw-r--r--scene/3d/importer_mesh_instance_3d.cpp85
-rw-r--r--scene/3d/importer_mesh_instance_3d.h64
-rw-r--r--scene/3d/joint_3d.cpp (renamed from scene/3d/physics_joint_3d.cpp)4
-rw-r--r--scene/3d/joint_3d.h (renamed from scene/3d/physics_joint_3d.h)8
-rw-r--r--scene/3d/mesh_instance_3d.cpp6
-rw-r--r--scene/3d/physics_body_3d.cpp340
-rw-r--r--scene/3d/physics_body_3d.h44
-rw-r--r--scene/3d/skeleton_ik_3d.cpp12
-rw-r--r--scene/3d/soft_dynamic_body_3d.cpp8
-rw-r--r--scene/3d/spring_arm_3d.cpp32
-rw-r--r--scene/animation/animation_blend_tree.cpp54
-rw-r--r--scene/animation/animation_node_state_machine.cpp34
-rw-r--r--scene/animation/animation_player.cpp53
-rw-r--r--scene/debugger/scene_debugger.cpp16
-rw-r--r--scene/gui/code_edit.cpp45
-rw-r--r--scene/gui/color_picker.cpp53
-rw-r--r--scene/gui/color_picker.h1
-rw-r--r--scene/gui/control.cpp179
-rw-r--r--scene/gui/control.h12
-rw-r--r--scene/gui/dialogs.cpp6
-rw-r--r--scene/gui/gradient_edit.cpp41
-rw-r--r--scene/gui/gradient_edit.h7
-rw-r--r--scene/gui/graph_edit.cpp16
-rw-r--r--scene/gui/graph_node.cpp12
-rw-r--r--scene/gui/grid_container.cpp20
-rw-r--r--scene/gui/label.cpp78
-rw-r--r--scene/gui/line_edit.cpp111
-rw-r--r--scene/gui/rich_text_label.cpp29
-rw-r--r--scene/gui/tabs.cpp15
-rw-r--r--scene/gui/text_edit.cpp230
-rw-r--r--scene/gui/text_edit.h9
-rw-r--r--scene/gui/tree.cpp18
-rw-r--r--scene/gui/tree.h2
-rw-r--r--scene/main/canvas_item.cpp4
-rw-r--r--scene/main/canvas_item.h4
-rw-r--r--scene/main/node.cpp76
-rw-r--r--scene/main/node.h6
-rw-r--r--scene/main/resource_preloader.cpp8
-rw-r--r--scene/main/scene_tree.cpp2
-rw-r--r--scene/main/window.cpp16
-rw-r--r--scene/main/window.h4
-rw-r--r--scene/register_scene_types.cpp9
-rw-r--r--scene/resources/curve.cpp8
-rw-r--r--scene/resources/default_theme/default_theme.cpp13
-rw-r--r--scene/resources/font.cpp13
-rw-r--r--scene/resources/font.h8
-rw-r--r--scene/resources/importer_mesh.cpp1242
-rw-r--r--scene/resources/importer_mesh.h132
-rw-r--r--scene/resources/material.cpp20
-rw-r--r--scene/resources/material.h3
-rw-r--r--scene/resources/mesh.cpp4
-rw-r--r--scene/resources/mesh_library.cpp14
-rw-r--r--scene/resources/navigation_mesh.cpp10
-rw-r--r--scene/resources/packed_scene.cpp38
-rw-r--r--scene/resources/resource_format_text.cpp32
-rw-r--r--scene/resources/shader.cpp4
-rw-r--r--scene/resources/sky_material.cpp12
-rw-r--r--scene/resources/sky_material.h3
-rw-r--r--scene/resources/sprite_frames.cpp20
-rw-r--r--scene/resources/text_line.cpp18
-rw-r--r--scene/resources/text_line.h10
-rw-r--r--scene/resources/text_paragraph.cpp32
-rw-r--r--scene/resources/text_paragraph.h10
-rw-r--r--scene/resources/texture.cpp2
-rw-r--r--scene/resources/theme.cpp944
-rw-r--r--scene/resources/theme.h17
-rw-r--r--scene/resources/tile_set.cpp238
-rw-r--r--scene/resources/tile_set.h8
-rw-r--r--scene/resources/visual_shader.cpp52
83 files changed, 3771 insertions, 2070 deletions
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index d78b9847e6..33b1c7bcce 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -300,8 +300,8 @@ void Area2D::_clear_monitoring() {
body_map.clear();
//disconnect all monitored stuff
- for (Map<ObjectID, BodyState>::Element *E = bmcopy.front(); E; E = E->next()) {
- Object *obj = ObjectDB::get_instance(E->key());
+ for (const KeyValue<ObjectID, BodyState> &E : bmcopy) {
+ Object *obj = ObjectDB::get_instance(E.key);
Node *node = Object::cast_to<Node>(obj);
if (!node) { //node may have been deleted in previous frame or at other legitimate point
@@ -311,12 +311,12 @@ void Area2D::_clear_monitoring() {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree));
- if (!E->get().in_tree) {
+ if (!E.value.in_tree) {
continue;
}
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape);
+ for (int i = 0; i < E.value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E.value.rid, node, E.value.shapes[i].body_shape, E.value.shapes[i].area_shape);
}
emit_signal(SceneStringNames::get_singleton()->body_exited, obj);
@@ -328,8 +328,8 @@ void Area2D::_clear_monitoring() {
area_map.clear();
//disconnect all monitored stuff
- for (Map<ObjectID, AreaState>::Element *E = bmcopy.front(); E; E = E->next()) {
- Object *obj = ObjectDB::get_instance(E->key());
+ for (const KeyValue<ObjectID, AreaState> &E : bmcopy) {
+ Object *obj = ObjectDB::get_instance(E.key);
Node *node = Object::cast_to<Node>(obj);
if (!node) { //node may have been deleted in previous frame or at other legitimate point
@@ -339,12 +339,12 @@ void Area2D::_clear_monitoring() {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_area_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_area_exit_tree));
- if (!E->get().in_tree) {
+ if (!E.value.in_tree) {
continue;
}
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape);
+ for (int i = 0; i < E.value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E.value.rid, node, E.value.shapes[i].area_shape, E.value.shapes[i].self_shape);
}
emit_signal(SceneStringNames::get_singleton()->area_exited, obj);
@@ -404,8 +404,8 @@ TypedArray<Node2D> Area2D::get_overlapping_bodies() const {
TypedArray<Node2D> ret;
ret.resize(body_map.size());
int idx = 0;
- for (const Map<ObjectID, BodyState>::Element *E = body_map.front(); E; E = E->next()) {
- Object *obj = ObjectDB::get_instance(E->key());
+ for (const KeyValue<ObjectID, BodyState> &E : body_map) {
+ Object *obj = ObjectDB::get_instance(E.key);
if (!obj) {
ret.resize(ret.size() - 1); //ops
} else {
@@ -421,8 +421,8 @@ TypedArray<Area2D> Area2D::get_overlapping_areas() const {
TypedArray<Area2D> ret;
ret.resize(area_map.size());
int idx = 0;
- for (const Map<ObjectID, AreaState>::Element *E = area_map.front(); E; E = E->next()) {
- Object *obj = ObjectDB::get_instance(E->key());
+ for (const KeyValue<ObjectID, AreaState> &E : area_map) {
+ Object *obj = ObjectDB::get_instance(E.key);
if (!obj) {
ret.resize(ret.size() - 1); //ops
} else {
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index 5d3a538f60..6916f832d0 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -342,15 +342,15 @@ real_t CollisionObject2D::get_shape_owner_one_way_collision_margin(uint32_t p_ow
}
void CollisionObject2D::get_shape_owners(List<uint32_t> *r_owners) {
- for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
- r_owners->push_back(E->key());
+ for (const KeyValue<uint32_t, ShapeData> &E : shapes) {
+ r_owners->push_back(E.key);
}
}
Array CollisionObject2D::_get_shape_owners() {
Array ret;
- for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<uint32_t, ShapeData> &E : shapes) {
+ ret.push_back(E.key);
}
return ret;
@@ -434,10 +434,10 @@ void CollisionObject2D::shape_owner_remove_shape(uint32_t p_owner, int p_shape)
shapes[p_owner].shapes.remove(p_shape);
- for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().shapes.size(); i++) {
- if (E->get().shapes[i].index > index_to_remove) {
- E->get().shapes.write[i].index -= 1;
+ for (KeyValue<uint32_t, ShapeData> &E : shapes) {
+ for (int i = 0; i < E.value.shapes.size(); i++) {
+ if (E.value.shapes[i].index > index_to_remove) {
+ E.value.shapes.write[i].index -= 1;
}
}
}
@@ -456,10 +456,10 @@ void CollisionObject2D::shape_owner_clear_shapes(uint32_t p_owner) {
uint32_t CollisionObject2D::shape_find_owner(int p_shape_index) const {
ERR_FAIL_INDEX_V(p_shape_index, total_subshapes, 0);
- for (const Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().shapes.size(); i++) {
- if (E->get().shapes[i].index == p_shape_index) {
- return E->key();
+ for (const KeyValue<uint32_t, ShapeData> &E : shapes) {
+ for (int i = 0; i < E.value.shapes.size(); i++) {
+ if (E.value.shapes[i].index == p_shape_index) {
+ return E.key;
}
}
}
diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joint_2d.cpp
index 4a6606256e..3b371d4a07 100644
--- a/scene/2d/joints_2d.cpp
+++ b/scene/2d/joint_2d.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* joints_2d.cpp */
+/* joint_2d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "joints_2d.h"
+#include "joint_2d.h"
#include "physics_body_2d.h"
#include "scene/scene_string_names.h"
diff --git a/scene/2d/joints_2d.h b/scene/2d/joint_2d.h
index dc5a08f815..0c3956e463 100644
--- a/scene/2d/joints_2d.h
+++ b/scene/2d/joint_2d.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* joints_2d.h */
+/* joint_2d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef JOINTS_2D_H
-#define JOINTS_2D_H
+#ifndef JOINT_2D_H
+#define JOINT_2D_H
#include "node_2d.h"
@@ -148,4 +148,4 @@ public:
DampedSpringJoint2D();
};
-#endif // JOINTS_2D_H
+#endif // JOINT_2D_H
diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp
index 48817679bc..c1b0bc35dd 100644
--- a/scene/2d/physical_bone_2d.cpp
+++ b/scene/2d/physical_bone_2d.cpp
@@ -30,6 +30,8 @@
#include "physical_bone_2d.h"
+#include "scene/2d/joint_2d.h"
+
void PhysicalBone2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
diff --git a/scene/2d/physical_bone_2d.h b/scene/2d/physical_bone_2d.h
index a250d0aadd..8b41f75c3e 100644
--- a/scene/2d/physical_bone_2d.h
+++ b/scene/2d/physical_bone_2d.h
@@ -31,11 +31,11 @@
#ifndef PHYSICAL_BONE_2D_H
#define PHYSICAL_BONE_2D_H
-#include "scene/2d/joints_2d.h"
#include "scene/2d/physics_body_2d.h"
-
#include "scene/2d/skeleton_2d.h"
+class Joint2D;
+
class PhysicalBone2D : public RigidDynamicBody2D {
GDCLASS(PhysicalBone2D, RigidDynamicBody2D);
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index c07a999588..f493d97ceb 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -54,13 +54,14 @@ PhysicsBody2D::~PhysicsBody2D() {
}
}
-Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_test_only, real_t p_margin) {
- PhysicsServer2D::MotionResult result;
-
+Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_linear_velocity, bool p_test_only, real_t p_margin) {
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.
double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
- if (move_and_collide(p_motion * delta, result, p_margin, p_test_only)) {
+ PhysicsServer2D::MotionParameters parameters(get_global_transform(), p_linear_velocity * delta, p_margin);
+
+ PhysicsServer2D::MotionResult result;
+ if (move_and_collide(parameters, result, p_test_only)) {
// Create a new instance when the cached reference is invalid or still in use in script.
if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) {
motion_cache.instantiate();
@@ -74,18 +75,18 @@ Ref<KinematicCollision2D> PhysicsBody2D::_move(const Vector2 &p_motion, bool p_t
return Ref<KinematicCollision2D>();
}
-bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
+bool PhysicsBody2D::move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only, bool p_cancel_sliding) {
if (is_only_update_transform_changes_enabled()) {
ERR_PRINT("Move functions do not work together with 'sync to physics' option. Please read the documentation.");
}
- Transform2D gt = get_global_transform();
- bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_collide_separation_ray, p_exclude);
+
+ bool colliding = PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_parameters, &r_result);
// Restore direction of motion to be along original motion,
// in order to avoid sliding due to recovery,
// but only if collision depth is low enough to avoid tunneling.
if (p_cancel_sliding) {
- real_t motion_length = p_motion.length();
+ real_t motion_length = p_parameters.motion.length();
real_t precision = 0.001;
if (colliding) {
@@ -93,7 +94,7 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M
// so even in normal resting cases the depth can be a bit more than the margin.
precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction);
- if (r_result.collision_depth > (real_t)p_margin + precision) {
+ if (r_result.collision_depth > p_parameters.margin + precision) {
p_cancel_sliding = false;
}
}
@@ -102,7 +103,7 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M
// When motion is null, recovery is the resulting motion.
Vector2 motion_normal;
if (motion_length > CMP_EPSILON) {
- motion_normal = p_motion / motion_length;
+ motion_normal = p_parameters.motion / motion_length;
}
// Check depth of recovery.
@@ -111,15 +112,16 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M
real_t recovery_length = recovery.length();
// Fixes cases where canceling slide causes the motion to go too deep into the ground,
// because we're only taking rest information into account and not general recovery.
- if (recovery_length < (real_t)p_margin + precision) {
+ if (recovery_length < p_parameters.margin + precision) {
// Apply adjustment to motion.
r_result.travel = motion_normal * projected_length;
- r_result.remainder = p_motion - r_result.travel;
+ r_result.remainder = p_parameters.motion - r_result.travel;
}
}
}
if (!p_test_only) {
+ Transform2D gt = p_parameters.from;
gt.elements[2] += r_result.travel;
set_global_transform(gt);
}
@@ -127,7 +129,7 @@ bool PhysicsBody2D::move_and_collide(const Vector2 &p_motion, PhysicsServer2D::M
return colliding;
}
-bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) {
+bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_linear_velocity, const Ref<KinematicCollision2D> &r_collision, real_t p_margin) {
ERR_FAIL_COND_V(!is_inside_tree(), false);
PhysicsServer2D::MotionResult *r = nullptr;
@@ -139,7 +141,9 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky.
double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
- return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion * delta, p_margin, r);
+ PhysicsServer2D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin);
+
+ return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), parameters, r);
}
TypedArray<PhysicsBody2D> PhysicsBody2D::get_collision_exceptions() {
@@ -288,6 +292,12 @@ void AnimatableBody2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
last_valid_transform = get_global_transform();
+ _update_kinematic_motion();
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ set_only_update_transform_changes(false);
+ set_notify_local_transform(false);
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
@@ -313,7 +323,6 @@ void AnimatableBody2D::_bind_methods() {
AnimatableBody2D::AnimatableBody2D() :
StaticBody2D(PhysicsServer2D::BODY_MODE_KINEMATIC) {
- _update_kinematic_motion();
}
void RigidDynamicBody2D::_body_enter_tree(ObjectID p_id) {
@@ -457,9 +466,9 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state)
//untag all
int rc = 0;
- for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().shapes.size(); i++) {
- E->get().shapes[i].tagged = false;
+ for (KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) {
+ for (int i = 0; i < E.value.shapes.size(); i++) {
+ E.value.shapes[i].tagged = false;
rc++;
}
}
@@ -503,12 +512,12 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state)
//put the ones to remove
- for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().shapes.size(); i++) {
- if (!E->get().shapes[i].tagged) {
- toremove[toremove_count].rid = E->get().rid;
- toremove[toremove_count].body_id = E->key();
- toremove[toremove_count].pair = E->get().shapes[i];
+ for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) {
+ for (int i = 0; i < E.value.shapes.size(); i++) {
+ if (!E.value.shapes[i].tagged) {
+ toremove[toremove_count].rid = E.value.rid;
+ toremove[toremove_count].body_id = E.key;
+ toremove[toremove_count].pair = E.value.shapes[i];
toremove_count++;
}
}
@@ -815,8 +824,8 @@ TypedArray<Node2D> RigidDynamicBody2D::get_colliding_bodies() const {
TypedArray<Node2D> ret;
ret.resize(contact_monitor->body_map.size());
int idx = 0;
- for (const Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) {
- Object *obj = ObjectDB::get_instance(E->key());
+ for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) {
+ Object *obj = ObjectDB::get_instance(E.key);
if (!obj) {
ret.resize(ret.size() - 1); //ops
} else {
@@ -835,9 +844,9 @@ void RigidDynamicBody2D::set_contact_monitor(bool p_enabled) {
if (!p_enabled) {
ERR_FAIL_COND_MSG(contact_monitor->locked, "Can't disable contact monitoring during in/out callback. Use call_deferred(\"set_contact_monitor\", false) instead.");
- for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) {
+ for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) {
//clean up mess
- Object *obj = ObjectDB::get_instance(E->key());
+ Object *obj = ObjectDB::get_instance(E.key);
Node *node = Object::cast_to<Node>(obj);
if (node) {
@@ -1047,6 +1056,8 @@ bool CharacterBody2D::move_and_slide() {
double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
Vector2 current_platform_velocity = platform_velocity;
+ Transform2D gt = get_global_transform();
+ previous_position = gt.elements[2];
if ((on_floor || on_wall) && platform_rid.is_valid()) {
bool excluded = false;
@@ -1059,7 +1070,6 @@ bool CharacterBody2D::move_and_slide() {
//this approach makes sure there is less delay between the actual body velocity and the one we saved
PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid);
if (bs) {
- Transform2D gt = get_global_transform();
Vector2 local_position = gt.elements[2] - bs->get_transform().elements[2];
current_platform_velocity = bs->get_velocity_at_local_position(local_position);
}
@@ -1069,6 +1079,7 @@ bool CharacterBody2D::move_and_slide() {
}
motion_results.clear();
+ last_motion = Vector2();
bool was_on_floor = on_floor;
on_floor = false;
@@ -1076,10 +1087,14 @@ bool CharacterBody2D::move_and_slide() {
on_wall = false;
if (!current_platform_velocity.is_equal_approx(Vector2())) {
+ PhysicsServer2D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin);
+ parameters.exclude_bodies.insert(platform_rid);
+ if (platform_object_id.is_valid()) {
+ parameters.exclude_objects.insert(platform_object_id);
+ }
+
PhysicsServer2D::MotionResult floor_result;
- Set<RID> exclude;
- exclude.insert(platform_rid);
- if (move_and_collide(current_platform_velocity * delta, floor_result, margin, false, false, false, exclude)) {
+ if (move_and_collide(parameters, floor_result, false, false)) {
motion_results.push_back(floor_result);
_set_collision_direction(floor_result);
}
@@ -1091,23 +1106,33 @@ bool CharacterBody2D::move_and_slide() {
_move_and_slide_free(delta);
}
- if (!on_floor && !on_wall) {
+ // Compute real velocity.
+ real_velocity = get_position_delta() / delta;
+
+ if (moving_platform_apply_velocity_on_leave != PLATFORM_VEL_ON_LEAVE_NEVER) {
// Add last platform velocity when just left a moving platform.
- linear_velocity += current_platform_velocity;
+ if (!on_floor && !on_wall) {
+ if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) {
+ current_platform_velocity = current_platform_velocity.slide(up_direction);
+ }
+ motion_velocity += current_platform_velocity;
+ }
}
return motion_results.size() > 0;
}
void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity) {
- Vector2 motion = linear_velocity * p_delta;
+ Vector2 motion = motion_velocity * p_delta;
Vector2 motion_slide_up = motion.slide(up_direction);
Vector2 prev_floor_normal = floor_normal;
RID prev_platform_rid = platform_rid;
+ ObjectID prev_platform_object_id = platform_object_id;
int prev_platform_layer = platform_layer;
platform_rid = RID();
+ platform_object_id = ObjectID();
floor_normal = Vector2();
platform_velocity = Vector2();
@@ -1116,28 +1141,47 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
bool sliding_enabled = !floor_stop_on_slope;
// Constant speed can be applied only the first time sliding is enabled.
bool can_apply_constant_speed = sliding_enabled;
+ // If the platform's ceiling push down the body.
+ bool apply_ceiling_velocity = false;
bool first_slide = true;
- bool vel_dir_facing_up = linear_velocity.dot(up_direction) > 0;
+ bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0;
Vector2 last_travel;
for (int iteration = 0; iteration < max_slides; ++iteration) {
- PhysicsServer2D::MotionResult result;
+ PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin);
+
+ Vector2 prev_position = parameters.from.elements[2];
- Vector2 prev_position = get_global_transform().elements[2];
+ PhysicsServer2D::MotionResult result;
+ bool collided = move_and_collide(parameters, result, false, !sliding_enabled);
- bool collided = move_and_collide(motion, result, margin, false, !sliding_enabled);
+ last_motion = result.travel;
if (collided) {
motion_results.push_back(result);
_set_collision_direction(result);
- if (on_floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) {
+ // If we hit a ceiling platform, we set the vertical motion_velocity to at least the platform one.
+ if (on_ceiling && result.collider_velocity != Vector2() && result.collider_velocity.dot(up_direction) < 0) {
+ // If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward.
+ if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (result.collision_normal + up_direction).length() < 0.01) {
+ apply_ceiling_velocity = true;
+ Vector2 ceiling_vertical_velocity = up_direction * up_direction.dot(result.collider_velocity);
+ Vector2 motion_vertical_velocity = up_direction * up_direction.dot(motion_velocity);
+ if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) {
+ motion_velocity = ceiling_vertical_velocity + motion_velocity.slide(up_direction);
+ }
+ }
+ }
+
+ if (on_floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) {
Transform2D gt = get_global_transform();
if (result.travel.length() <= margin + CMP_EPSILON) {
gt.elements[2] -= result.travel;
}
set_global_transform(gt);
- linear_velocity = Vector2();
+ motion_velocity = Vector2();
+ last_motion = Vector2();
motion = Vector2();
break;
}
@@ -1160,10 +1204,12 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
}
on_floor = true;
platform_rid = prev_platform_rid;
+ platform_object_id = prev_platform_object_id;
platform_layer = prev_platform_layer;
platform_velocity = p_prev_platform_velocity;
floor_normal = prev_floor_normal;
- linear_velocity = Vector2();
+ motion_velocity = Vector2();
+ last_motion = Vector2();
motion = Vector2();
break;
}
@@ -1182,9 +1228,9 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
motion = motion_slide_norm * (motion_slide_up.length() - result.travel.slide(up_direction).length() - last_travel.slide(up_direction).length());
}
// Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling.
- else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up)) {
+ else if ((sliding_enabled || !on_floor) && (!on_ceiling || slide_on_ceiling || !vel_dir_facing_up) && !apply_ceiling_velocity) {
Vector2 slide_motion = result.remainder.slide(result.collision_normal);
- if (slide_motion.dot(linear_velocity) > 0.0) {
+ if (slide_motion.dot(motion_velocity) > 0.0) {
motion = slide_motion;
} else {
motion = Vector2();
@@ -1192,10 +1238,10 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
if (slide_on_ceiling && on_ceiling) {
// Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall.
if (vel_dir_facing_up) {
- linear_velocity = linear_velocity.slide(result.collision_normal);
+ motion_velocity = motion_velocity.slide(result.collision_normal);
} else {
// Avoid acceleration in slope when falling.
- linear_velocity = up_direction * up_direction.dot(linear_velocity);
+ motion_velocity = up_direction * up_direction.dot(motion_velocity);
}
}
}
@@ -1203,7 +1249,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
else {
motion = result.remainder;
if (on_ceiling && !slide_on_ceiling && vel_dir_facing_up) {
- linear_velocity = linear_velocity.slide(up_direction);
+ motion_velocity = motion_velocity.slide(up_direction);
motion = motion.slide(up_direction);
}
}
@@ -1237,28 +1283,37 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Reset the gravity accumulation when touching the ground.
if (on_floor && !vel_dir_facing_up) {
- linear_velocity = linear_velocity.slide(up_direction);
+ motion_velocity = motion_velocity.slide(up_direction);
}
}
void CharacterBody2D::_move_and_slide_free(double p_delta) {
- Vector2 motion = linear_velocity * p_delta;
+ Vector2 motion = motion_velocity * p_delta;
platform_rid = RID();
+ platform_object_id = ObjectID();
floor_normal = Vector2();
platform_velocity = Vector2();
bool first_slide = true;
for (int iteration = 0; iteration < max_slides; ++iteration) {
+ PhysicsServer2D::MotionParameters parameters(get_global_transform(), motion, margin);
+
PhysicsServer2D::MotionResult result;
+ bool collided = move_and_collide(parameters, result, false, false);
- bool collided = move_and_collide(motion, result, margin, false, false);
+ last_motion = result.travel;
if (collided) {
motion_results.push_back(result);
_set_collision_direction(result);
- if (free_mode_min_slide_angle != 0 && result.get_angle(-linear_velocity.normalized()) < free_mode_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
+ if (result.remainder.is_equal_approx(Vector2())) {
+ motion = Vector2();
+ break;
+ }
+
+ if (free_mode_min_slide_angle != 0 && result.get_angle(-motion_velocity.normalized()) < free_mode_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
motion = Vector2();
} else if (first_slide) {
Vector2 motion_slide_norm = result.remainder.slide(result.collision_normal).normalized();
@@ -1267,16 +1322,16 @@ void CharacterBody2D::_move_and_slide_free(double p_delta) {
motion = result.remainder.slide(result.collision_normal);
}
- if (motion.dot(linear_velocity) <= 0.0) {
+ if (motion.dot(motion_velocity) <= 0.0) {
motion = Vector2();
}
}
- first_slide = false;
-
if (!collided || motion.is_equal_approx(Vector2())) {
break;
}
+
+ first_slide = false;
}
}
@@ -1288,10 +1343,11 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
// Snap by at least collision margin to keep floor state consistent.
real_t length = MAX(floor_snap_length, margin);
- Transform2D gt = get_global_transform();
+ PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin);
+ parameters.collide_separation_ray = true;
+
PhysicsServer2D::MotionResult result;
- if (move_and_collide(-up_direction * length, result, margin, true, false, true)) {
- bool apply = true;
+ if (move_and_collide(parameters, result, true, false)) {
if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
on_floor = true;
floor_normal = result.collision_normal;
@@ -1306,13 +1362,9 @@ void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
result.travel = Vector2();
}
}
- } else {
- apply = false;
- }
- if (apply) {
- gt.elements[2] += result.travel;
- set_global_transform(gt);
+ parameters.from.elements[2] += result.travel;
+ set_global_transform(parameters.from);
}
}
}
@@ -1325,8 +1377,11 @@ bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facin
// Snap by at least collision margin to keep floor state consistent.
real_t length = MAX(floor_snap_length, margin);
+ PhysicsServer2D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin);
+ parameters.collide_separation_ray = true;
+
PhysicsServer2D::MotionResult result;
- if (move_and_collide(-up_direction * length, result, margin, true, false, true)) {
+ if (move_and_collide(parameters, result, true, false)) {
if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
return true;
}
@@ -1344,6 +1399,7 @@ void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResu
on_ceiling = true;
} else {
on_wall = true;
+ wall_normal = p_result.collision_normal;
// Don't apply wall velocity when the collider is a CharacterBody2D.
if (Object::cast_to<CharacterBody2D>(ObjectDB::get_instance(p_result.collider_id)) == nullptr) {
_set_platform_data(p_result);
@@ -1353,16 +1409,17 @@ void CharacterBody2D::_set_collision_direction(const PhysicsServer2D::MotionResu
void CharacterBody2D::_set_platform_data(const PhysicsServer2D::MotionResult &p_result) {
platform_rid = p_result.collider;
+ platform_object_id = p_result.collider_id;
platform_velocity = p_result.collider_velocity;
platform_layer = PhysicsServer2D::get_singleton()->body_get_collision_layer(platform_rid);
}
-const Vector2 &CharacterBody2D::get_linear_velocity() const {
- return linear_velocity;
+const Vector2 &CharacterBody2D::get_motion_velocity() const {
+ return motion_velocity;
}
-void CharacterBody2D::set_linear_velocity(const Vector2 &p_velocity) {
- linear_velocity = p_velocity;
+void CharacterBody2D::set_motion_velocity(const Vector2 &p_velocity) {
+ motion_velocity = p_velocity;
}
bool CharacterBody2D::is_on_floor() const {
@@ -1389,16 +1446,32 @@ bool CharacterBody2D::is_on_ceiling_only() const {
return on_ceiling && !on_floor && !on_wall;
}
-Vector2 CharacterBody2D::get_floor_normal() const {
+const Vector2 &CharacterBody2D::get_floor_normal() const {
return floor_normal;
}
+const Vector2 &CharacterBody2D::get_wall_normal() const {
+ return wall_normal;
+}
+
+const Vector2 &CharacterBody2D::get_last_motion() const {
+ return last_motion;
+}
+
+Vector2 CharacterBody2D::get_position_delta() const {
+ return get_global_transform().elements[2] - previous_position;
+}
+
+const Vector2 &CharacterBody2D::get_real_velocity() const {
+ return real_velocity;
+}
+
real_t CharacterBody2D::get_floor_angle(const Vector2 &p_up_direction) const {
ERR_FAIL_COND_V(p_up_direction == Vector2(), 0);
return Math::acos(floor_normal.dot(p_up_direction));
}
-Vector2 CharacterBody2D::get_platform_velocity() const {
+const Vector2 &CharacterBody2D::get_platform_velocity() const {
return platform_velocity;
}
@@ -1498,6 +1571,14 @@ CharacterBody2D::MotionMode CharacterBody2D::get_motion_mode() const {
return motion_mode;
}
+void CharacterBody2D::set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_apply_velocity) {
+ moving_platform_apply_velocity_on_leave = p_on_leave_apply_velocity;
+}
+
+CharacterBody2D::MovingPlatformApplyVelocityOnLeave CharacterBody2D::get_moving_platform_apply_velocity_on_leave() const {
+ return moving_platform_apply_velocity_on_leave;
+}
+
int CharacterBody2D::get_max_slides() const {
return max_slides;
}
@@ -1547,6 +1628,7 @@ void CharacterBody2D::_notification(int p_what) {
// Reset move_and_slide() data.
on_floor = false;
platform_rid = RID();
+ platform_object_id = ObjectID();
on_ceiling = false;
on_wall = false;
motion_results.clear();
@@ -1558,8 +1640,8 @@ void CharacterBody2D::_notification(int p_what) {
void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody2D::move_and_slide);
- ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &CharacterBody2D::set_linear_velocity);
- ClassDB::bind_method(D_METHOD("get_linear_velocity"), &CharacterBody2D::get_linear_velocity);
+ ClassDB::bind_method(D_METHOD("set_motion_velocity", "motion_velocity"), &CharacterBody2D::set_motion_velocity);
+ ClassDB::bind_method(D_METHOD("get_motion_velocity"), &CharacterBody2D::get_motion_velocity);
ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody2D::set_safe_margin);
ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody2D::get_safe_margin);
@@ -1589,6 +1671,8 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody2D::set_up_direction);
ClassDB::bind_method(D_METHOD("set_motion_mode", "mode"), &CharacterBody2D::set_motion_mode);
ClassDB::bind_method(D_METHOD("get_motion_mode"), &CharacterBody2D::get_motion_mode);
+ ClassDB::bind_method(D_METHOD("set_moving_platform_apply_velocity_on_leave", "on_leave_apply_velocity"), &CharacterBody2D::set_moving_platform_apply_velocity_on_leave);
+ ClassDB::bind_method(D_METHOD("get_moving_platform_apply_velocity_on_leave"), &CharacterBody2D::get_moving_platform_apply_velocity_on_leave);
ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody2D::is_on_floor);
ClassDB::bind_method(D_METHOD("is_on_floor_only"), &CharacterBody2D::is_on_floor_only);
@@ -1597,6 +1681,10 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_on_wall"), &CharacterBody2D::is_on_wall);
ClassDB::bind_method(D_METHOD("is_on_wall_only"), &CharacterBody2D::is_on_wall_only);
ClassDB::bind_method(D_METHOD("get_floor_normal"), &CharacterBody2D::get_floor_normal);
+ ClassDB::bind_method(D_METHOD("get_wall_normal"), &CharacterBody2D::get_wall_normal);
+ ClassDB::bind_method(D_METHOD("get_last_motion"), &CharacterBody2D::get_last_motion);
+ ClassDB::bind_method(D_METHOD("get_position_delta"), &CharacterBody2D::get_position_delta);
+ ClassDB::bind_method(D_METHOD("get_real_velocity"), &CharacterBody2D::get_real_velocity);
ClassDB::bind_method(D_METHOD("get_floor_angle", "up_direction"), &CharacterBody2D::get_floor_angle, DEFVAL(Vector2(0.0, -1.0)));
ClassDB::bind_method(D_METHOD("get_platform_velocity"), &CharacterBody2D::get_platform_velocity);
ClassDB::bind_method(D_METHOD("get_slide_collision_count"), &CharacterBody2D::get_slide_collision_count);
@@ -1604,10 +1692,11 @@ void CharacterBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody2D::_get_last_slide_collision);
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_motion_velocity", "get_motion_velocity");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "up_direction"), "set_up_direction", "get_up_direction");
+
ADD_GROUP("Free Mode", "free_mode_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "free_mode_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_free_mode_min_slide_angle", "get_free_mode_min_slide_angle");
ADD_GROUP("Floor", "floor_");
@@ -1617,12 +1706,17 @@ void CharacterBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_floor_snap_length", "get_floor_snap_length");
ADD_GROUP("Moving platform", "moving_platform");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_apply_velocity_on_leave", PROPERTY_HINT_ENUM, "Always,Upward Only,Never", PROPERTY_USAGE_DEFAULT), "set_moving_platform_apply_velocity_on_leave", "get_moving_platform_apply_velocity_on_leave");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers");
ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin");
BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED);
BIND_ENUM_CONSTANT(MOTION_MODE_FREE);
+
+ BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_ALWAYS);
+ BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY);
+ BIND_ENUM_CONSTANT(PLATFORM_VEL_ON_LEAVE_NEVER);
}
void CharacterBody2D::_validate_property(PropertyInfo &property) const {
@@ -1717,10 +1811,6 @@ Vector2 KinematicCollision2D::get_collider_velocity() const {
return result.collider_velocity;
}
-Variant KinematicCollision2D::get_collider_metadata() const {
- return Variant();
-}
-
void KinematicCollision2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_position"), &KinematicCollision2D::get_position);
ClassDB::bind_method(D_METHOD("get_normal"), &KinematicCollision2D::get_normal);
@@ -1734,18 +1824,4 @@ void KinematicCollision2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_collider_shape"), &KinematicCollision2D::get_collider_shape);
ClassDB::bind_method(D_METHOD("get_collider_shape_index"), &KinematicCollision2D::get_collider_shape_index);
ClassDB::bind_method(D_METHOD("get_collider_velocity"), &KinematicCollision2D::get_collider_velocity);
- ClassDB::bind_method(D_METHOD("get_collider_metadata"), &KinematicCollision2D::get_collider_metadata);
-
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position"), "", "get_position");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "normal"), "", "get_normal");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "travel"), "", "get_travel");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "remainder"), "", "get_remainder");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "local_shape"), "", "get_local_shape");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider"), "", "get_collider");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_id"), "", "get_collider_id");
- ADD_PROPERTY(PropertyInfo(Variant::RID, "collider_rid"), "", "get_collider_rid");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider_shape"), "", "get_collider_shape");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_shape_index"), "", "get_collider_shape_index");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "collider_velocity"), "", "get_collider_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::NIL, "collider_metadata", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "", "get_collider_metadata");
}
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index e789ac4cb7..d1f52b33f2 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -47,11 +47,11 @@ protected:
Ref<KinematicCollision2D> motion_cache;
- Ref<KinematicCollision2D> _move(const Vector2 &p_motion, bool p_test_only = false, real_t p_margin = 0.08);
+ Ref<KinematicCollision2D> _move(const Vector2 &p_linear_velocity, bool p_test_only = false, real_t p_margin = 0.08);
public:
- bool move_and_collide(const Vector2 &p_motion, PhysicsServer2D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>());
- bool test_move(const Transform2D &p_from, const Vector2 &p_motion, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08);
+ bool move_and_collide(const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true);
+ bool test_move(const Transform2D &p_from, const Vector2 &p_linear_velocity, const Ref<KinematicCollision2D> &r_collision = Ref<KinematicCollision2D>(), real_t p_margin = 0.08);
TypedArray<PhysicsBody2D> get_collision_exceptions();
void add_collision_exception_with(Node *p_node); //must be physicsbody
@@ -310,10 +310,15 @@ public:
MOTION_MODE_GROUNDED,
MOTION_MODE_FREE,
};
+ enum MovingPlatformApplyVelocityOnLeave {
+ PLATFORM_VEL_ON_LEAVE_ALWAYS,
+ PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY,
+ PLATFORM_VEL_ON_LEAVE_NEVER,
+ };
bool move_and_slide();
- const Vector2 &get_linear_velocity() const;
- void set_linear_velocity(const Vector2 &p_velocity);
+ const Vector2 &get_motion_velocity() const;
+ void set_motion_velocity(const Vector2 &p_velocity);
bool is_on_floor() const;
bool is_on_floor_only() const;
@@ -321,9 +326,14 @@ public:
bool is_on_wall_only() const;
bool is_on_ceiling() const;
bool is_on_ceiling_only() const;
- Vector2 get_floor_normal() const;
+ const Vector2 &get_last_motion() const;
+ Vector2 get_position_delta() const;
+ const Vector2 &get_floor_normal() const;
+ const Vector2 &get_wall_normal() const;
+ const Vector2 &get_real_velocity() const;
+
real_t get_floor_angle(const Vector2 &p_up_direction = Vector2(0.0, -1.0)) const;
- Vector2 get_platform_velocity() const;
+ const Vector2 &get_platform_velocity() const;
int get_slide_collision_count() const;
PhysicsServer2D::MotionResult get_slide_collision(int p_bounce) const;
@@ -334,24 +344,31 @@ public:
private:
real_t margin = 0.08;
MotionMode motion_mode = MOTION_MODE_GROUNDED;
+ MovingPlatformApplyVelocityOnLeave moving_platform_apply_velocity_on_leave = PLATFORM_VEL_ON_LEAVE_ALWAYS;
bool floor_constant_speed = false;
bool floor_stop_on_slope = true;
bool floor_block_on_wall = true;
bool slide_on_ceiling = true;
int max_slides = 4;
- int platform_layer;
+ int platform_layer = 0;
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
real_t floor_snap_length = 1;
real_t free_mode_min_slide_angle = Math::deg2rad((real_t)15.0);
Vector2 up_direction = Vector2(0.0, -1.0);
uint32_t moving_platform_floor_layers = UINT32_MAX;
uint32_t moving_platform_wall_layers = 0;
- Vector2 linear_velocity;
+ Vector2 motion_velocity;
Vector2 floor_normal;
Vector2 platform_velocity;
+ Vector2 wall_normal;
+ Vector2 last_motion;
+ Vector2 previous_position;
+ Vector2 real_velocity;
+
RID platform_rid;
+ ObjectID platform_object_id;
bool on_floor = false;
bool on_ceiling = false;
bool on_wall = false;
@@ -395,6 +412,9 @@ private:
void set_motion_mode(MotionMode p_mode);
MotionMode get_motion_mode() const;
+ void set_moving_platform_apply_velocity_on_leave(MovingPlatformApplyVelocityOnLeave p_on_leave_velocity);
+ MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const;
+
void _move_and_slide_free(double p_delta);
void _move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity);
@@ -414,6 +434,7 @@ protected:
};
VARIANT_ENUM_CAST(CharacterBody2D::MotionMode);
+VARIANT_ENUM_CAST(CharacterBody2D::MovingPlatformApplyVelocityOnLeave);
class KinematicCollision2D : public RefCounted {
GDCLASS(KinematicCollision2D, RefCounted);
@@ -439,7 +460,6 @@ public:
Object *get_collider_shape() const;
int get_collider_shape_index() const;
Vector2 get_collider_velocity() const;
- Variant get_collider_metadata() const;
};
#endif // PHYSICS_BODY_2D_H
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 929233e4e0..222ec986b0 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -51,8 +51,8 @@ void TileMapPattern::remove_cell(const Vector2i &p_coords, bool p_update_size) {
pattern.erase(p_coords);
if (p_update_size) {
size = Vector2i();
- for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) {
- size = size.max(E->key() + Vector2i(1, 1));
+ for (const KeyValue<Vector2i, TileMapCell> &E : pattern) {
+ size = size.max(E.key + Vector2i(1, 1));
}
}
}
@@ -80,8 +80,8 @@ TypedArray<Vector2i> TileMapPattern::get_used_cells() const {
TypedArray<Vector2i> a;
a.resize(pattern.size());
int i = 0;
- for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) {
- Vector2i p(E->key().x, E->key().y);
+ for (const KeyValue<Vector2i, TileMapCell> &E : pattern) {
+ Vector2i p(E.key.x, E.key.y);
a[i++] = p;
}
@@ -93,8 +93,8 @@ Vector2i TileMapPattern::get_size() const {
}
void TileMapPattern::set_size(const Vector2i &p_size) {
- for (Map<Vector2i, TileMapCell>::Element *E = pattern.front(); E; E = E->next()) {
- Vector2i coords = E->key();
+ for (const KeyValue<Vector2i, TileMapCell> &E : pattern) {
+ Vector2i coords = E.key;
if (p_size.x <= coords.x || p_size.y <= coords.y) {
ERR_FAIL_MSG(vformat("Cannot set pattern size to %s, it contains a tile at %s. Size can only be increased.", p_size, coords));
};
@@ -452,6 +452,19 @@ int TileMap::get_layer_z_index(int p_layer) const {
return layers[p_layer].z_index;
}
+void TileMap::set_collision_animatable(bool p_enabled) {
+ collision_animatable = p_enabled;
+ _clear_internals();
+ set_notify_local_transform(p_enabled);
+ set_physics_process_internal(p_enabled);
+ _recreate_internals();
+ emit_signal(SNAME("changed"));
+}
+
+bool TileMap::is_collision_animatable() const {
+ return collision_animatable;
+}
+
void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) {
collision_visibility_mode = p_show_collision;
_clear_internals();
@@ -508,7 +521,6 @@ Map<Vector2i, TileMapQuadrant>::Element *TileMap::_create_quadrant(int p_layer,
// Call the create_quadrant method on plugins
if (tile_set.is_valid()) {
_rendering_create_quadrant(&q);
- _physics_create_quadrant(&q);
}
return layers[p_layer].quadrant_map.insert(p_qk, q);
@@ -526,9 +538,9 @@ void TileMap::_make_quadrant_dirty(Map<Vector2i, TileMapQuadrant>::Element *Q) {
void TileMap::_make_all_quadrants_dirty() {
// Make all quandrants dirty, then trigger an update later.
for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
- if (!E->value().dirty_list_element.in_list()) {
- layers[layer].dirty_quadrant_list.add(&E->value().dirty_list_element);
+ for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
+ if (!E.value.dirty_list_element.in_list()) {
+ layers[layer].dirty_quadrant_list.add(&E.value.dirty_list_element);
}
}
}
@@ -610,8 +622,8 @@ void TileMap::_recreate_internals() {
// Recreate the quadrants.
const Map<Vector2i, TileMapCell> &tile_map = layers[layer].tile_map;
- for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
- Vector2i qk = _coords_to_quadrant_coords(layer, Vector2i(E->key().x, E->key().y));
+ for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
+ Vector2i qk = _coords_to_quadrant_coords(layer, Vector2i(E.key.x, E.key.y));
Map<Vector2i, TileMapQuadrant>::Element *Q = layers[layer].quadrant_map.find(qk);
if (!Q) {
@@ -619,7 +631,7 @@ void TileMap::_recreate_internals() {
layers[layer].dirty_quadrant_list.add(&Q->get().dirty_list_element);
}
- Vector2i pk = E->key();
+ Vector2i pk = E.key;
Q->get().cells.insert(pk);
_make_quadrant_dirty(Q);
@@ -688,7 +700,7 @@ void TileMap::_recompute_rect_cache() {
Rect2 r_total;
for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
+ for (const Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
Rect2 r;
r.position = map_to_world(E->key() * get_effective_quadrant_size(layer));
r.expand_to(map_to_world((E->key() + Vector2i(1, 0)) * get_effective_quadrant_size(layer)));
@@ -717,13 +729,13 @@ void TileMap::_rendering_notification(int p_what) {
case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: {
bool visible = is_visible_in_tree();
for (int layer = 0; layer < (int)layers.size(); layer++) {
- for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) {
- TileMapQuadrant &q = E_quadrant->get();
+ for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) {
+ TileMapQuadrant &q = E_quadrant.value;
// Update occluders transform.
- for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) {
+ for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) {
Transform2D xform;
- xform.set_origin(E_cell->key());
+ xform.set_origin(E_cell.key);
for (const RID &occluder : q.occluders) {
RS::get_singleton()->canvas_light_occluder_set_enabled(occluder, visible);
}
@@ -736,13 +748,13 @@ void TileMap::_rendering_notification(int p_what) {
return;
}
for (int layer = 0; layer < (int)layers.size(); layer++) {
- for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) {
- TileMapQuadrant &q = E_quadrant->get();
+ for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) {
+ TileMapQuadrant &q = E_quadrant.value;
// Update occluders transform.
- for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) {
+ for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) {
Transform2D xform;
- xform.set_origin(E_cell->key());
+ xform.set_origin(E_cell.key);
for (const RID &occluder : q.occluders) {
RS::get_singleton()->canvas_light_occluder_set_transform(occluder, get_global_transform() * xform);
}
@@ -821,8 +833,8 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
RID prev_canvas_item;
// Iterate over the cells of the quadrant.
- for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) {
- TileMapCell c = get_cell(q.layer, E_cell->value(), true);
+ for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) {
+ TileMapCell c = get_cell(q.layer, E_cell.value, true);
TileSetSource *source;
if (tile_set->has_source(c.source_id)) {
@@ -891,12 +903,12 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
modulate.a *= 0.3;
}
}
- draw_tile(canvas_item, E_cell->key() - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate);
+ draw_tile(canvas_item, E_cell.key - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate);
// --- Occluders ---
for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) {
Transform2D xform;
- xform.set_origin(E_cell->key());
+ xform.set_origin(E_cell.key);
if (tile_data->get_occluder(i).is_valid()) {
RID occluder_id = rs->canvas_light_occluder_create();
rs->canvas_light_occluder_set_enabled(occluder_id, visible);
@@ -922,13 +934,13 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
for (int layer = 0; layer < (int)layers.size(); layer++) {
// Sort the quadrants coords per world coordinates
Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator> world_to_map;
- for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
- world_to_map[map_to_world(E->key())] = E->key();
+ for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
+ world_to_map[map_to_world(E.key)] = E.key;
}
// Sort the quadrants
- for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E = world_to_map.front(); E; E = E->next()) {
- TileMapQuadrant &q = layers[layer].quadrant_map[E->value()];
+ for (const KeyValue<Vector2i, Vector2i> &E : world_to_map) {
+ TileMapQuadrant &q = layers[layer].quadrant_map[E.value];
for (const RID &ci : q.canvas_items) {
RS::get_singleton()->canvas_item_set_draw_index(ci, index++);
}
@@ -1092,24 +1104,67 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
void TileMap::_physics_notification(int p_what) {
switch (p_what) {
+ case CanvasItem::NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ bool in_editor = false;
+#ifdef TOOLS_ENABLED
+ in_editor = Engine::get_singleton()->is_editor_hint();
+#endif
+ if (is_inside_tree() && collision_animatable && !in_editor) {
+ // Update tranform on the physics tick when in animatable mode.
+ last_valid_transform = new_transform;
+ set_notify_local_transform(false);
+ set_global_transform(new_transform);
+ set_notify_local_transform(true);
+ }
+ } break;
case CanvasItem::NOTIFICATION_TRANSFORM_CHANGED: {
- // Update the bodies transforms.
- if (is_inside_tree()) {
+ bool in_editor = false;
+#ifdef TOOLS_ENABLED
+ in_editor = Engine::get_singleton()->is_editor_hint();
+#endif
+ if (is_inside_tree() && (!collision_animatable || in_editor)) {
+ // Update the new transform directly if we are not in animatable mode.
+ Transform2D global_transform = get_global_transform();
for (int layer = 0; layer < (int)layers.size(); layer++) {
- Transform2D global_transform = get_global_transform();
+ for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
+ TileMapQuadrant &q = E.value;
- for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
- TileMapQuadrant &q = E->get();
+ for (RID body : q.bodies) {
+ Transform2D xform;
+ xform.set_origin(map_to_world(bodies_coords[body]));
+ xform = global_transform * xform;
+ PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+ }
+ }
+ }
+ }
+ } break;
+ case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
+ bool in_editor = false;
+#ifdef TOOLS_ENABLED
+ in_editor = Engine::get_singleton()->is_editor_hint();
+#endif
+ if (is_inside_tree() && !in_editor && collision_animatable) {
+ // Only active when animatable. Send the new transform to the physics...
+ new_transform = get_global_transform();
+ for (int layer = 0; layer < (int)layers.size(); layer++) {
+ for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
+ TileMapQuadrant &q = E.value;
- Transform2D xform;
- xform.set_origin(map_to_world(E->key() * get_effective_quadrant_size(layer)));
- xform = global_transform * xform;
+ for (RID body : q.bodies) {
+ Transform2D xform;
+ xform.set_origin(map_to_world(bodies_coords[body]));
+ xform = new_transform * xform;
- for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
- PhysicsServer2D::get_singleton()->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+ PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
}
}
}
+
+ // ... but then revert changes.
+ set_notify_local_transform(false);
+ set_global_transform(last_valid_transform);
+ set_notify_local_transform(true);
}
} break;
}
@@ -1120,29 +1175,23 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
ERR_FAIL_COND(!tile_set.is_valid());
Transform2D global_transform = get_global_transform();
+ last_valid_transform = global_transform;
+ new_transform = global_transform;
PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
+ RID space = get_world_2d()->get_space();
SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
while (q_list_element) {
TileMapQuadrant &q = *q_list_element->self();
- Vector2 quadrant_pos = map_to_world(q.coords * get_effective_quadrant_size(q.layer));
-
- LocalVector<int> body_shape_count;
- body_shape_count.resize(q.bodies.size());
-
- // Clear shapes.
- for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
- ps->body_clear_shapes(q.bodies[body_index]);
- body_shape_count[body_index] = 0;
-
- // Position the bodies.
- Transform2D xform;
- xform.set_origin(quadrant_pos);
- xform = global_transform * xform;
- ps->body_set_state(q.bodies[body_index], PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+ // Clear bodies.
+ for (RID body : q.bodies) {
+ bodies_coords.erase(body);
+ ps->free(body);
}
+ q.bodies.clear();
+ // Recreate bodies and shapes.
for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) {
TileMapCell c = get_cell(q.layer, E_cell->get(), true);
@@ -1158,26 +1207,53 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
if (atlas_source) {
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
- for (int body_index = 0; body_index < q.bodies.size(); body_index++) {
- int &body_shape_index = body_shape_count[body_index];
+ for (int tile_set_physics_layer = 0; tile_set_physics_layer < tile_set->get_physics_layers_count(); tile_set_physics_layer++) {
+ Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer);
+ uint32_t physics_layer = tile_set->get_physics_layer_collision_layer(tile_set_physics_layer);
+ uint32_t physics_mask = tile_set->get_physics_layer_collision_mask(tile_set_physics_layer);
- // Add the shapes again.
- for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) {
- bool one_way_collision = tile_data->is_collision_polygon_one_way(body_index, polygon_index);
- float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(body_index, polygon_index);
+ // Create the body.
+ RID body = ps->body_create();
+ bodies_coords[body] = E_cell->get();
+ ps->body_set_mode(body, collision_animatable ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC);
+ ps->body_set_space(body, space);
- int shapes_count = tile_data->get_collision_polygon_shapes_count(body_index, polygon_index);
- for (int shape_index = 0; shape_index < shapes_count; shape_index++) {
- Transform2D xform = Transform2D();
- xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos);
+ Transform2D xform;
+ xform.set_origin(map_to_world(E_cell->get()));
+ xform = global_transform * xform;
+ ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
+
+ ps->body_attach_object_instance_id(body, get_instance_id());
+ ps->body_set_collision_layer(body, physics_layer);
+ ps->body_set_collision_mask(body, physics_mask);
+ ps->body_set_pickable(body, false);
+ ps->body_set_state(body, PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, tile_data->get_constant_linear_velocity(tile_set_physics_layer));
+ ps->body_set_state(body, PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, tile_data->get_constant_angular_velocity(tile_set_physics_layer));
+
+ if (!physics_material.is_valid()) {
+ ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
+ ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1);
+ } else {
+ ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce());
+ ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction());
+ }
+
+ q.bodies.push_back(body);
+ // Add the shapes to the body.
+ int body_shape_index = 0;
+ for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(tile_set_physics_layer); polygon_index++) {
+ // Iterate over the polygons.
+ bool one_way_collision = tile_data->is_collision_polygon_one_way(tile_set_physics_layer, polygon_index);
+ float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(tile_set_physics_layer, polygon_index);
+ int shapes_count = tile_data->get_collision_polygon_shapes_count(tile_set_physics_layer, polygon_index);
+ for (int shape_index = 0; shape_index < shapes_count; shape_index++) {
// Add decomposed convex shapes.
- Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(body_index, polygon_index, shape_index);
- ps->body_add_shape(q.bodies[body_index], shape->get_rid(), xform);
- ps->body_set_shape_metadata(q.bodies[body_index], body_shape_index, E_cell->get());
- ps->body_set_shape_as_one_way_collision(q.bodies[body_index], body_shape_index, one_way_collision, one_way_collision_margin);
+ Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(tile_set_physics_layer, polygon_index, shape_index);
+ ps->body_add_shape(body, shape->get_rid());
+ ps->body_set_shape_as_one_way_collision(body, body_shape_index, one_way_collision, one_way_collision_margin);
- ++body_shape_index;
+ body_shape_index++;
}
}
}
@@ -1189,54 +1265,11 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
}
}
-void TileMap::_physics_create_quadrant(TileMapQuadrant *p_quadrant) {
- ERR_FAIL_COND(!tile_set.is_valid());
-
- //Get the TileMap's gobla transform.
- Transform2D global_transform;
- if (is_inside_tree()) {
- global_transform = get_global_transform();
- }
-
- // Clear all bodies.
- p_quadrant->bodies.clear();
-
- // Create the body and set its parameters.
- for (int layer = 0; layer < tile_set->get_physics_layers_count(); layer++) {
- RID body = PhysicsServer2D::get_singleton()->body_create();
- PhysicsServer2D::get_singleton()->body_set_mode(body, PhysicsServer2D::BODY_MODE_STATIC);
-
- PhysicsServer2D::get_singleton()->body_attach_object_instance_id(body, get_instance_id());
- PhysicsServer2D::get_singleton()->body_set_collision_layer(body, tile_set->get_physics_layer_collision_layer(layer));
- PhysicsServer2D::get_singleton()->body_set_collision_mask(body, tile_set->get_physics_layer_collision_mask(layer));
-
- Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(layer);
- if (!physics_material.is_valid()) {
- PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0);
- PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1);
- } else {
- PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce());
- PhysicsServer2D::get_singleton()->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction());
- }
-
- if (is_inside_tree()) {
- RID space = get_world_2d()->get_space();
- PhysicsServer2D::get_singleton()->body_set_space(body, space);
-
- Transform2D xform;
- xform.set_origin(map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer)));
- xform = global_transform * xform;
- PhysicsServer2D::get_singleton()->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform);
- }
-
- p_quadrant->bodies.push_back(body);
- }
-}
-
void TileMap::_physics_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
// Remove a quadrant.
- for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) {
- PhysicsServer2D::get_singleton()->free(p_quadrant->bodies[body_index]);
+ for (RID body : p_quadrant->bodies) {
+ bodies_coords.erase(body);
+ PhysicsServer2D::get_singleton()->free(body);
}
p_quadrant->bodies.clear();
}
@@ -1252,7 +1285,7 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
bool show_collision = false;
switch (collision_visibility_mode) {
case TileMap::VISIBILITY_MODE_DEFAULT:
- show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_navigation_hint());
+ show_collision = !Engine::get_singleton()->is_editor_hint() && (get_tree() && get_tree()->is_debugging_collisions_hint());
break;
case TileMap::VISIBILITY_MODE_FORCE_HIDE:
show_collision = false;
@@ -1266,39 +1299,28 @@ void TileMap::_physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
}
RenderingServer *rs = RenderingServer::get_singleton();
-
- Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
+ PhysicsServer2D *ps = PhysicsServer2D::get_singleton();
Color debug_collision_color = get_tree()->get_debug_collisions_color();
- for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) {
- TileMapCell c = get_cell(p_quadrant->layer, E_cell->get(), true);
-
- Transform2D xform;
- xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos);
- rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+ Vector<Color> color;
+ color.push_back(debug_collision_color);
- if (tile_set->has_source(c.source_id)) {
- TileSetSource *source = *tile_set->get_source(c.source_id);
-
- if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
- continue;
- }
-
- TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ Vector2 quadrant_pos = map_to_world(p_quadrant->coords * get_effective_quadrant_size(p_quadrant->layer));
+ Transform2D qudrant_xform;
+ qudrant_xform.set_origin(quadrant_pos);
+ Transform2D global_transform_inv = (get_global_transform() * qudrant_xform).affine_inverse();
- for (int body_index = 0; body_index < p_quadrant->bodies.size(); body_index++) {
- for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(body_index); polygon_index++) {
- // Draw the debug polygon.
- Vector<Vector2> polygon = tile_data->get_collision_polygon_points(body_index, polygon_index);
- if (polygon.size() >= 3) {
- Vector<Color> color;
- color.push_back(debug_collision_color);
- rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color);
- }
- }
- }
+ for (RID body : p_quadrant->bodies) {
+ Transform2D xform = Transform2D(ps->body_get_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM)) * global_transform_inv;
+ rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, xform);
+ for (int shape_index = 0; shape_index < ps->body_get_shape_count(body); shape_index++) {
+ const RID &shape = ps->body_get_shape(body, shape_index);
+ PhysicsServer2D::ShapeType type = ps->shape_get_type(shape);
+ if (type == PhysicsServer2D::SHAPE_CONVEX_POLYGON) {
+ Vector<Vector2> polygon = ps->shape_get_data(shape);
+ rs->canvas_item_add_polygon(p_quadrant->debug_canvas_item, polygon, color);
+ } else {
+ WARN_PRINT("Wrong shape type for a tile, should be SHAPE_CONVEX_POLYGON.");
}
}
rs->canvas_item_add_set_transform(p_quadrant->debug_canvas_item, Transform2D());
@@ -1313,16 +1335,16 @@ void TileMap::_navigation_notification(int p_what) {
if (is_inside_tree()) {
for (int layer = 0; layer < (int)layers.size(); layer++) {
Transform2D tilemap_xform = get_global_transform();
- for (Map<Vector2i, TileMapQuadrant>::Element *E_quadrant = layers[layer].quadrant_map.front(); E_quadrant; E_quadrant = E_quadrant->next()) {
- TileMapQuadrant &q = E_quadrant->get();
- for (Map<Vector2i, Vector<RID>>::Element *E_region = q.navigation_regions.front(); E_region; E_region = E_region->next()) {
- for (int layer_index = 0; layer_index < E_region->get().size(); layer_index++) {
- RID region = E_region->get()[layer_index];
+ for (KeyValue<Vector2i, TileMapQuadrant> &E_quadrant : layers[layer].quadrant_map) {
+ TileMapQuadrant &q = E_quadrant.value;
+ for (const KeyValue<Vector2i, Vector<RID>> &E_region : q.navigation_regions) {
+ for (int layer_index = 0; layer_index < E_region.value.size(); layer_index++) {
+ RID region = E_region.value[layer_index];
if (!region.is_valid()) {
continue;
}
Transform2D tile_transform;
- tile_transform.set_origin(map_to_world(E_region->key()));
+ tile_transform.set_origin(map_to_world(E_region.key));
NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform);
}
}
@@ -1351,9 +1373,9 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
TileMapQuadrant &q = *q_list_element->self();
// Clear navigation shapes in the quadrant.
- for (Map<Vector2i, Vector<RID>>::Element *E = q.navigation_regions.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().size(); i++) {
- RID region = E->get()[i];
+ for (const KeyValue<Vector2i, Vector<RID>> &E : q.navigation_regions) {
+ for (int i = 0; i < E.value.size(); i++) {
+ RID region = E.value[i];
if (!region.is_valid()) {
continue;
}
@@ -1404,9 +1426,9 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
void TileMap::_navigation_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
// Clear navigation shapes in the quadrant.
- for (Map<Vector2i, Vector<RID>>::Element *E = p_quadrant->navigation_regions.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().size(); i++) {
- RID region = E->get()[i];
+ for (const KeyValue<Vector2i, Vector<RID>> &E : p_quadrant->navigation_regions) {
+ for (int i = 0; i < E.value.size(); i++) {
+ RID region = E.value[i];
if (!region.is_valid()) {
continue;
}
@@ -1507,8 +1529,8 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_
TileMapQuadrant &q = *q_list_element->self();
// Clear the scenes.
- for (Map<Vector2i, String>::Element *E = q.scenes.front(); E; E = E->next()) {
- Node *node = get_node(E->get());
+ for (const KeyValue<Vector2i, String> &E : q.scenes) {
+ Node *node = get_node(E.value);
if (node) {
node->queue_delete();
}
@@ -1555,8 +1577,8 @@ void TileMap::_scenes_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_
void TileMap::_scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant) {
// Clear the scenes.
- for (Map<Vector2i, String>::Element *E = p_quadrant->scenes.front(); E; E = E->next()) {
- Node *node = get_node(E->get());
+ for (const KeyValue<Vector2i, String> &E : p_quadrant->scenes) {
+ Node *node = get_node(E.value);
if (node) {
node->queue_delete();
}
@@ -1858,16 +1880,21 @@ Map<Vector2i, TileMapQuadrant> *TileMap::get_quadrant_map(int p_layer) {
return &layers[p_layer].quadrant_map;
}
+Vector2i TileMap::get_coords_for_body_rid(RID p_physics_body) {
+ ERR_FAIL_COND_V_MSG(!bodies_coords.has(p_physics_body), Vector2i(), vformat("No tiles for the given body RID %d.", p_physics_body));
+ return bodies_coords[p_physics_body];
+}
+
void TileMap::fix_invalid_tiles() {
ERR_FAIL_COND_MSG(tile_set.is_null(), "Cannot fix invalid tiles if Tileset is not open.");
for (unsigned int i = 0; i < layers.size(); i++) {
const Map<Vector2i, TileMapCell> &tile_map = layers[i].tile_map;
Set<Vector2i> coords;
- for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
- TileSetSource *source = *tile_set->get_source(E->get().source_id);
- if (!source || !source->has_tile(E->get().get_atlas_coords()) || !source->has_alternative_tile(E->get().get_atlas_coords(), E->get().alternative_tile)) {
- coords.insert(E->key());
+ for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
+ TileSetSource *source = *tile_set->get_source(E.value.source_id);
+ if (!source || !source->has_tile(E.value.get_atlas_coords()) || !source->has_alternative_tile(E.value.get_atlas_coords(), E.value.alternative_tile)) {
+ coords.insert(E.key);
}
}
for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) {
@@ -1990,14 +2017,14 @@ Vector<int> TileMap::_get_tile_data(int p_layer) const {
// Save in highest format
int idx = 0;
- for (const Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
+ for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
uint8_t *ptr = (uint8_t *)&w[idx];
- encode_uint16((int16_t)(E->key().x), &ptr[0]);
- encode_uint16((int16_t)(E->key().y), &ptr[2]);
- encode_uint16(E->get().source_id, &ptr[4]);
- encode_uint16(E->get().coord_x, &ptr[6]);
- encode_uint16(E->get().coord_y, &ptr[8]);
- encode_uint16(E->get().alternative_tile, &ptr[10]);
+ encode_uint16((int16_t)(E.key.x), &ptr[0]);
+ encode_uint16((int16_t)(E.key.y), &ptr[2]);
+ encode_uint16(E.value.source_id, &ptr[4]);
+ encode_uint16(E.value.coord_x, &ptr[6]);
+ encode_uint16(E.value.coord_y, &ptr[8]);
+ encode_uint16(E.value.alternative_tile, &ptr[10]);
idx += 3;
}
@@ -2751,8 +2778,8 @@ TypedArray<Vector2i> TileMap::get_used_cells(int p_layer) const {
TypedArray<Vector2i> a;
a.resize(layers[p_layer].tile_map.size());
int i = 0;
- for (Map<Vector2i, TileMapCell>::Element *E = layers[p_layer].tile_map.front(); E; E = E->next()) {
- Vector2i p(E->key().x, E->key().y);
+ for (const KeyValue<Vector2i, TileMapCell> &E : layers[p_layer].tile_map) {
+ Vector2i p(E.key.x, E.key.y);
a[i++] = p;
}
@@ -2773,8 +2800,8 @@ Rect2 TileMap::get_used_rect() { // Not const because of cache
first = false;
}
- for (Map<Vector2i, TileMapCell>::Element *E = tile_map.front(); E; E = E->next()) {
- used_rect_cache.expand_to(Vector2i(E->key().x, E->key().y));
+ for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
+ used_rect_cache.expand_to(Vector2i(E.key.x, E.key.y));
}
}
}
@@ -2794,8 +2821,8 @@ void TileMap::set_light_mask(int p_light_mask) {
// Occlusion: set light mask.
CanvasItem::set_light_mask(p_light_mask);
for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
- for (const RID &ci : E->get().canvas_items) {
+ for (const KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
+ for (const RID &ci : E.value.canvas_items) {
RenderingServer::get_singleton()->canvas_item_set_light_mask(ci, get_light_mask());
}
}
@@ -2809,8 +2836,8 @@ void TileMap::set_material(const Ref<Material> &p_material) {
// Update material for the whole tilemap.
for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
- TileMapQuadrant &q = E->get();
+ for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
+ TileMapQuadrant &q = E.value;
for (const RID &ci : q.canvas_items) {
RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid());
}
@@ -2825,8 +2852,8 @@ void TileMap::set_use_parent_material(bool p_use_parent_material) {
// Update use_parent_material for the whole tilemap.
for (unsigned int layer = 0; layer < layers.size(); layer++) {
- for (Map<Vector2i, TileMapQuadrant>::Element *E = layers[layer].quadrant_map.front(); E; E = E->next()) {
- TileMapQuadrant &q = E->get();
+ for (KeyValue<Vector2i, TileMapQuadrant> &E : layers[layer].quadrant_map) {
+ TileMapQuadrant &q = E.value;
for (const RID &ci : q.canvas_items) {
RS::get_singleton()->canvas_item_set_use_parent_material(ci, get_use_parent_material() || get_material().is_valid());
}
@@ -3007,6 +3034,8 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layer_z_index", "layer", "z_index"), &TileMap::set_layer_z_index);
ClassDB::bind_method(D_METHOD("get_layer_z_index", "layer"), &TileMap::get_layer_z_index);
+ ClassDB::bind_method(D_METHOD("set_collision_animatable", "enabled"), &TileMap::set_collision_animatable);
+ ClassDB::bind_method(D_METHOD("is_collision_animatable"), &TileMap::is_collision_animatable);
ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "collision_visibility_mode"), &TileMap::set_collision_visibility_mode);
ClassDB::bind_method(D_METHOD("get_collision_visibility_mode"), &TileMap::get_collision_visibility_mode);
@@ -3018,10 +3047,14 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "layer", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords);
ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile);
+ ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &TileMap::get_coords_for_body_rid);
+
ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles);
- ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles);
+ ClassDB::bind_method(D_METHOD("clear_layer", "layer"), &TileMap::clear_layer);
ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear);
+ ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles);
+
ClassDB::bind_method(D_METHOD("get_used_cells", "layer"), &TileMap::get_used_cells);
ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMap::get_used_rect);
@@ -3037,6 +3070,7 @@ void TileMap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_animatable"), "set_collision_animatable", "is_collision_animatable");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_visibility_mode", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode");
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index ca38baa550..2faede2445 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -202,6 +202,7 @@ private:
// Properties.
Ref<TileSet> tile_set;
int quadrant_size = 16;
+ bool collision_animatable = false;
VisibilityMode collision_visibility_mode = VISIBILITY_MODE_DEFAULT;
VisibilityMode navigation_visibility_mode = VISIBILITY_MODE_DEFAULT;
@@ -229,6 +230,9 @@ private:
LocalVector<TileMapLayer> layers;
int selected_layer = -1;
+ // Mapping for RID to coords.
+ Map<RID, Vector2i> bodies_coords;
+
// Quadrants and internals management.
Vector2i _coords_to_quadrant_coords(int p_layer, const Vector2i &p_coords) const;
@@ -259,9 +263,10 @@ private:
void _rendering_cleanup_quadrant(TileMapQuadrant *p_quadrant);
void _rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
+ Transform2D last_valid_transform;
+ Transform2D new_transform;
void _physics_notification(int p_what);
void _physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
- void _physics_create_quadrant(TileMapQuadrant *p_quadrant);
void _physics_cleanup_quadrant(TileMapQuadrant *p_quadrant);
void _physics_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
@@ -325,6 +330,9 @@ public:
void set_selected_layer(int p_layer_id); // For editor use.
int get_selected_layer() const;
+ void set_collision_animatable(bool p_enabled);
+ bool is_collision_animatable() const;
+
void set_collision_visibility_mode(VisibilityMode p_show_collision);
VisibilityMode get_collision_visibility_mode();
@@ -364,6 +372,9 @@ public:
virtual void set_texture_filter(CanvasItem::TextureFilter p_texture_filter) override;
virtual void set_texture_repeat(CanvasItem::TextureRepeat p_texture_repeat) override;
+ // For finding tiles from collision.
+ Vector2i get_coords_for_body_rid(RID p_physics_body);
+
// Fixing a nclearing methods.
void fix_invalid_tiles();
diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp
index 943586f43c..7e4c40ca0e 100644
--- a/scene/3d/area_3d.cpp
+++ b/scene/3d/area_3d.cpp
@@ -262,8 +262,8 @@ void Area3D::_clear_monitoring() {
body_map.clear();
//disconnect all monitored stuff
- for (Map<ObjectID, BodyState>::Element *E = bmcopy.front(); E; E = E->next()) {
- Object *obj = ObjectDB::get_instance(E->key());
+ for (const KeyValue<ObjectID, BodyState> &E : bmcopy) {
+ Object *obj = ObjectDB::get_instance(E.key);
Node *node = Object::cast_to<Node>(obj);
if (!node) { //node may have been deleted in previous frame or at other legitimate point
@@ -274,12 +274,12 @@ void Area3D::_clear_monitoring() {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree));
- if (!E->get().in_tree) {
+ if (!E.value.in_tree) {
continue;
}
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape);
+ for (int i = 0; i < E.value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E.value.rid, node, E.value.shapes[i].body_shape, E.value.shapes[i].area_shape);
}
emit_signal(SceneStringNames::get_singleton()->body_exited, node);
@@ -291,8 +291,8 @@ void Area3D::_clear_monitoring() {
area_map.clear();
//disconnect all monitored stuff
- for (Map<ObjectID, AreaState>::Element *E = bmcopy.front(); E; E = E->next()) {
- Object *obj = ObjectDB::get_instance(E->key());
+ for (const KeyValue<ObjectID, AreaState> &E : bmcopy) {
+ Object *obj = ObjectDB::get_instance(E.key);
Node *node = Object::cast_to<Node>(obj);
if (!node) { //node may have been deleted in previous frame or at other legitimate point
@@ -303,12 +303,12 @@ void Area3D::_clear_monitoring() {
node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree));
node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree));
- if (!E->get().in_tree) {
+ if (!E.value.in_tree) {
continue;
}
- for (int i = 0; i < E->get().shapes.size(); i++) {
- emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape);
+ for (int i = 0; i < E.value.shapes.size(); i++) {
+ emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E.value.rid, node, E.value.shapes[i].area_shape, E.value.shapes[i].self_shape);
}
emit_signal(SceneStringNames::get_singleton()->area_exited, obj);
@@ -446,8 +446,8 @@ TypedArray<Node3D> Area3D::get_overlapping_bodies() const {
Array ret;
ret.resize(body_map.size());
int idx = 0;
- for (const Map<ObjectID, BodyState>::Element *E = body_map.front(); E; E = E->next()) {
- Object *obj = ObjectDB::get_instance(E->key());
+ for (const KeyValue<ObjectID, BodyState> &E : body_map) {
+ Object *obj = ObjectDB::get_instance(E.key);
if (!obj) {
ret.resize(ret.size() - 1); //ops
} else {
@@ -479,8 +479,8 @@ TypedArray<Area3D> Area3D::get_overlapping_areas() const {
Array ret;
ret.resize(area_map.size());
int idx = 0;
- for (const Map<ObjectID, AreaState>::Element *E = area_map.front(); E; E = E->next()) {
- Object *obj = ObjectDB::get_instance(E->key());
+ for (const KeyValue<ObjectID, AreaState> &E : area_map) {
+ Object *obj = ObjectDB::get_instance(E.key);
if (!obj) {
ret.resize(ret.size() - 1); //ops
} else {
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index 9aad338d15..61d73ff1e2 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -492,6 +492,7 @@ void Camera3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_frustum"), &Camera3D::get_frustum);
ClassDB::bind_method(D_METHOD("is_position_in_frustum", "world_point"), &Camera3D::is_position_in_frustum);
ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera3D::get_camera);
+ ClassDB::bind_method(D_METHOD("get_pyramid_shape_rid"), &Camera3D::get_pyramid_shape_rid);
ClassDB::bind_method(D_METHOD("set_cull_mask_value", "layer_number", "value"), &Camera3D::set_cull_mask_value);
ClassDB::bind_method(D_METHOD("get_cull_mask_value", "layer_number"), &Camera3D::get_cull_mask_value);
@@ -654,233 +655,46 @@ Vector3 Camera3D::get_doppler_tracked_velocity() const {
}
}
-Camera3D::Camera3D() {
- camera = RenderingServer::get_singleton()->camera_create();
- set_perspective(75.0, 0.05, 4000.0);
- RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers);
- //active=false;
- velocity_tracker.instantiate();
- set_notify_transform(true);
- set_disable_scale(true);
-}
-
-Camera3D::~Camera3D() {
- RenderingServer::get_singleton()->free(camera);
-}
+RID Camera3D::get_pyramid_shape_rid() {
+ if (pyramid_shape == RID()) {
+ pyramid_shape_points = get_near_plane_points();
+ pyramid_shape = PhysicsServer3D::get_singleton()->convex_polygon_shape_create();
+ PhysicsServer3D::get_singleton()->shape_set_data(pyramid_shape, pyramid_shape_points);
-////////////////////////////////////////
+ } else { //check if points changed
+ Vector<Vector3> local_points = get_near_plane_points();
-void ClippedCamera3D::set_margin(real_t p_margin) {
- margin = p_margin;
-}
-
-real_t ClippedCamera3D::get_margin() const {
- return margin;
-}
-
-void ClippedCamera3D::set_process_callback(ClipProcessCallback p_mode) {
- if (process_callback == p_mode) {
- return;
- }
- process_callback = p_mode;
- set_process_internal(process_callback == CLIP_PROCESS_IDLE);
- set_physics_process_internal(process_callback == CLIP_PROCESS_PHYSICS);
-}
-
-ClippedCamera3D::ClipProcessCallback ClippedCamera3D::get_process_callback() const {
- return process_callback;
-}
-
-Transform3D ClippedCamera3D::get_camera_transform() const {
- Transform3D t = Camera3D::get_camera_transform();
- t.origin += -t.basis.get_axis(Vector3::AXIS_Z).normalized() * clip_offset;
- return t;
-}
-
-void ClippedCamera3D::_notification(int p_what) {
- if (p_what == NOTIFICATION_INTERNAL_PROCESS || p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
- Node3D *parent = Object::cast_to<Node3D>(get_parent());
- if (!parent) {
- return;
- }
+ bool all_equal = true;
- PhysicsDirectSpaceState3D *dspace = get_world_3d()->get_direct_space_state();
- ERR_FAIL_COND(!dspace); // most likely physics set to threads
-
- Vector3 cam_fw = -get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized();
- Vector3 cam_pos = get_global_transform().origin;
- Vector3 parent_pos = parent->get_global_transform().origin;
-
- Plane parent_plane(parent_pos, cam_fw);
-
- if (parent_plane.is_point_over(cam_pos)) {
- //cam is beyond parent plane
- return;
- }
-
- Vector3 ray_from = parent_plane.project(cam_pos);
-
- clip_offset = 0; //reset by default
-
- { //check if points changed
- Vector<Vector3> local_points = get_near_plane_points();
-
- bool all_equal = true;
-
- for (int i = 0; i < 5; i++) {
- if (points[i] != local_points[i]) {
- all_equal = false;
- break;
- }
- }
-
- if (!all_equal) {
- PhysicsServer3D::get_singleton()->shape_set_data(pyramid_shape, local_points);
- points = local_points;
+ for (int i = 0; i < 5; i++) {
+ if (local_points[i] != pyramid_shape_points[i]) {
+ all_equal = false;
+ break;
}
}
- Transform3D xf = get_global_transform();
- xf.origin = ray_from;
- xf.orthonormalize();
-
- real_t closest_safe = 1.0f, closest_unsafe = 1.0f;
- if (dspace->cast_motion(pyramid_shape, xf, cam_pos - ray_from, margin, closest_safe, closest_unsafe, exclude, collision_mask, clip_to_bodies, clip_to_areas)) {
- clip_offset = cam_pos.distance_to(ray_from + (cam_pos - ray_from) * closest_safe);
+ if (!all_equal) {
+ PhysicsServer3D::get_singleton()->shape_set_data(pyramid_shape, local_points);
+ pyramid_shape_points = local_points;
}
-
- _update_camera();
- }
-
- if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
- update_gizmos();
}
-}
-
-void ClippedCamera3D::set_collision_mask(uint32_t p_mask) {
- collision_mask = p_mask;
-}
-uint32_t ClippedCamera3D::get_collision_mask() const {
- return collision_mask;
+ return pyramid_shape;
}
-void ClippedCamera3D::set_collision_mask_value(int p_layer_number, bool p_value) {
- ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
- ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
- uint32_t mask = get_collision_mask();
- if (p_value) {
- mask |= 1 << (p_layer_number - 1);
- } else {
- mask &= ~(1 << (p_layer_number - 1));
- }
- set_collision_mask(mask);
-}
-
-bool ClippedCamera3D::get_collision_mask_value(int p_layer_number) const {
- ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
- ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
- return get_collision_mask() & (1 << (p_layer_number - 1));
-}
-
-void ClippedCamera3D::add_exception_rid(const RID &p_rid) {
- exclude.insert(p_rid);
-}
-
-void ClippedCamera3D::add_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object);
- if (!co) {
- return;
- }
- add_exception_rid(co->get_rid());
-}
-
-void ClippedCamera3D::remove_exception_rid(const RID &p_rid) {
- exclude.erase(p_rid);
+Camera3D::Camera3D() {
+ camera = RenderingServer::get_singleton()->camera_create();
+ set_perspective(75.0, 0.05, 4000.0);
+ RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers);
+ //active=false;
+ velocity_tracker.instantiate();
+ set_notify_transform(true);
+ set_disable_scale(true);
}
-void ClippedCamera3D::remove_exception(const Object *p_object) {
- ERR_FAIL_NULL(p_object);
- const CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_object);
- if (!co) {
- return;
+Camera3D::~Camera3D() {
+ RenderingServer::get_singleton()->free(camera);
+ if (pyramid_shape.is_valid()) {
+ PhysicsServer3D::get_singleton()->free(pyramid_shape);
}
- remove_exception_rid(co->get_rid());
-}
-
-void ClippedCamera3D::clear_exceptions() {
- exclude.clear();
-}
-
-real_t ClippedCamera3D::get_clip_offset() const {
- return clip_offset;
-}
-
-void ClippedCamera3D::set_clip_to_areas(bool p_clip) {
- clip_to_areas = p_clip;
-}
-
-bool ClippedCamera3D::is_clip_to_areas_enabled() const {
- return clip_to_areas;
-}
-
-void ClippedCamera3D::set_clip_to_bodies(bool p_clip) {
- clip_to_bodies = p_clip;
-}
-
-bool ClippedCamera3D::is_clip_to_bodies_enabled() const {
- return clip_to_bodies;
-}
-
-void ClippedCamera3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_margin", "margin"), &ClippedCamera3D::set_margin);
- ClassDB::bind_method(D_METHOD("get_margin"), &ClippedCamera3D::get_margin);
-
- ClassDB::bind_method(D_METHOD("set_process_callback", "process_callback"), &ClippedCamera3D::set_process_callback);
- ClassDB::bind_method(D_METHOD("get_process_callback"), &ClippedCamera3D::get_process_callback);
-
- ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ClippedCamera3D::set_collision_mask);
- ClassDB::bind_method(D_METHOD("get_collision_mask"), &ClippedCamera3D::get_collision_mask);
-
- ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &ClippedCamera3D::set_collision_mask_value);
- ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &ClippedCamera3D::get_collision_mask_value);
-
- ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ClippedCamera3D::add_exception_rid);
- ClassDB::bind_method(D_METHOD("add_exception", "node"), &ClippedCamera3D::add_exception);
-
- ClassDB::bind_method(D_METHOD("remove_exception_rid", "rid"), &ClippedCamera3D::remove_exception_rid);
- ClassDB::bind_method(D_METHOD("remove_exception", "node"), &ClippedCamera3D::remove_exception);
-
- ClassDB::bind_method(D_METHOD("set_clip_to_areas", "enable"), &ClippedCamera3D::set_clip_to_areas);
- ClassDB::bind_method(D_METHOD("is_clip_to_areas_enabled"), &ClippedCamera3D::is_clip_to_areas_enabled);
-
- ClassDB::bind_method(D_METHOD("get_clip_offset"), &ClippedCamera3D::get_clip_offset);
-
- ClassDB::bind_method(D_METHOD("set_clip_to_bodies", "enable"), &ClippedCamera3D::set_clip_to_bodies);
- ClassDB::bind_method(D_METHOD("is_clip_to_bodies_enabled"), &ClippedCamera3D::is_clip_to_bodies_enabled);
-
- ClassDB::bind_method(D_METHOD("clear_exceptions"), &ClippedCamera3D::clear_exceptions);
-
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_margin", "get_margin");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_callback", "get_process_callback");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
-
- ADD_GROUP("Clip To", "clip_to");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_to_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_clip_to_areas", "is_clip_to_areas_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_to_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_clip_to_bodies", "is_clip_to_bodies_enabled");
-
- BIND_ENUM_CONSTANT(CLIP_PROCESS_PHYSICS);
- BIND_ENUM_CONSTANT(CLIP_PROCESS_IDLE);
-}
-
-ClippedCamera3D::ClippedCamera3D() {
- set_physics_process_internal(true);
- set_notify_local_transform(Engine::get_singleton()->is_editor_hint());
- points.resize(5);
- pyramid_shape = PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_CONVEX_POLYGON);
-}
-
-ClippedCamera3D::~ClippedCamera3D() {
- PhysicsServer3D::get_singleton()->free(pyramid_shape);
}
diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h
index c1af7fa4f7..6006a2ea3f 100644
--- a/scene/3d/camera_3d.h
+++ b/scene/3d/camera_3d.h
@@ -86,6 +86,9 @@ private:
DopplerTracking doppler_tracking = DOPPLER_TRACKING_DISABLED;
Ref<VelocityTracker3D> velocity_tracker;
+ RID pyramid_shape;
+ Vector<Vector3> pyramid_shape_points;
+
protected:
void _update_camera();
virtual void _request_camera_update();
@@ -168,6 +171,8 @@ public:
Vector3 get_doppler_tracked_velocity() const;
+ RID get_pyramid_shape_rid();
+
Camera3D();
~Camera3D();
};
@@ -176,63 +181,4 @@ VARIANT_ENUM_CAST(Camera3D::Projection);
VARIANT_ENUM_CAST(Camera3D::KeepAspect);
VARIANT_ENUM_CAST(Camera3D::DopplerTracking);
-class ClippedCamera3D : public Camera3D {
- GDCLASS(ClippedCamera3D, Camera3D);
-
-public:
- enum ClipProcessCallback {
- CLIP_PROCESS_PHYSICS,
- CLIP_PROCESS_IDLE,
- };
-
-private:
- ClipProcessCallback process_callback = CLIP_PROCESS_PHYSICS;
- RID pyramid_shape;
- real_t margin = 0.0;
- real_t clip_offset = 0.0;
- uint32_t collision_mask = 1;
- bool clip_to_areas = false;
- bool clip_to_bodies = true;
-
- Set<RID> exclude;
-
- Vector<Vector3> points;
-
-protected:
- void _notification(int p_what);
- static void _bind_methods();
- virtual Transform3D get_camera_transform() const override;
-
-public:
- void set_clip_to_areas(bool p_clip);
- bool is_clip_to_areas_enabled() const;
-
- void set_clip_to_bodies(bool p_clip);
- bool is_clip_to_bodies_enabled() const;
-
- void set_margin(real_t p_margin);
- real_t get_margin() const;
-
- void set_process_callback(ClipProcessCallback p_mode);
- ClipProcessCallback get_process_callback() const;
-
- void set_collision_mask(uint32_t p_mask);
- uint32_t get_collision_mask() const;
-
- void set_collision_mask_value(int p_layer_number, bool p_value);
- bool get_collision_mask_value(int p_layer_number) const;
-
- void add_exception_rid(const RID &p_rid);
- void add_exception(const Object *p_object);
- void remove_exception_rid(const RID &p_rid);
- void remove_exception(const Object *p_object);
- void clear_exceptions();
-
- real_t get_clip_offset() const;
-
- ClippedCamera3D();
- ~ClippedCamera3D();
-};
-
-VARIANT_ENUM_CAST(ClippedCamera3D::ClipProcessCallback);
#endif
diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp
index e2f953974a..814ed5c2a7 100644
--- a/scene/3d/collision_object_3d.cpp
+++ b/scene/3d/collision_object_3d.cpp
@@ -37,8 +37,8 @@ void CollisionObject3D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
if (_are_collision_shapes_visible()) {
debug_shape_old_transform = get_global_transform();
- for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
- debug_shapes_to_update.insert(E->key());
+ for (const KeyValue<uint32_t, ShapeData> &E : shapes) {
+ debug_shapes_to_update.insert(E.key);
}
_update_debug_shapes();
}
@@ -324,8 +324,8 @@ void CollisionObject3D::_update_shape_data(uint32_t p_owner) {
}
void CollisionObject3D::_shape_changed(const Ref<Shape3D> &p_shape) {
- for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
- ShapeData &shapedata = E->get();
+ for (KeyValue<uint32_t, ShapeData> &E : shapes) {
+ ShapeData &shapedata = E.value;
ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw();
for (int i = 0; i < shapedata.shapes.size(); i++) {
ShapeData::ShapeBase &s = shapes[i];
@@ -380,8 +380,8 @@ void CollisionObject3D::_update_debug_shapes() {
}
void CollisionObject3D::_clear_debug_shapes() {
- for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
- ShapeData &shapedata = E->get();
+ for (KeyValue<uint32_t, ShapeData> &E : shapes) {
+ ShapeData &shapedata = E.value;
ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw();
for (int i = 0; i < shapedata.shapes.size(); i++) {
ShapeData::ShapeBase &s = shapes[i];
@@ -400,8 +400,8 @@ void CollisionObject3D::_clear_debug_shapes() {
void CollisionObject3D::_on_transform_changed() {
if (debug_shapes_count > 0 && !debug_shape_old_transform.is_equal_approx(get_global_transform())) {
debug_shape_old_transform = get_global_transform();
- for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
- ShapeData &shapedata = E->get();
+ for (KeyValue<uint32_t, ShapeData> &E : shapes) {
+ ShapeData &shapedata = E.value;
const ShapeData::ShapeBase *shapes = shapedata.shapes.ptr();
for (int i = 0; i < shapedata.shapes.size(); i++) {
RS::get_singleton()->instance_set_transform(shapes[i].debug_shape, debug_shape_old_transform * shapedata.xform);
@@ -523,15 +523,15 @@ bool CollisionObject3D::is_shape_owner_disabled(uint32_t p_owner) const {
}
void CollisionObject3D::get_shape_owners(List<uint32_t> *r_owners) {
- for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
- r_owners->push_back(E->key());
+ for (const KeyValue<uint32_t, ShapeData> &E : shapes) {
+ r_owners->push_back(E.key);
}
}
Array CollisionObject3D::_get_shape_owners() {
Array ret;
- for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<uint32_t, ShapeData> &E : shapes) {
+ ret.push_back(E.key);
}
return ret;
@@ -628,10 +628,10 @@ void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape)
shapes[p_owner].shapes.remove(p_shape);
- for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().shapes.size(); i++) {
- if (E->get().shapes[i].index > index_to_remove) {
- E->get().shapes.write[i].index -= 1;
+ for (KeyValue<uint32_t, ShapeData> &E : shapes) {
+ for (int i = 0; i < E.value.shapes.size(); i++) {
+ if (E.value.shapes[i].index > index_to_remove) {
+ E.value.shapes.write[i].index -= 1;
}
}
}
@@ -650,10 +650,10 @@ void CollisionObject3D::shape_owner_clear_shapes(uint32_t p_owner) {
uint32_t CollisionObject3D::shape_find_owner(int p_shape_index) const {
ERR_FAIL_INDEX_V(p_shape_index, total_subshapes, 0);
- for (const Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().shapes.size(); i++) {
- if (E->get().shapes[i].index == p_shape_index) {
- return E->key();
+ for (const KeyValue<uint32_t, ShapeData> &E : shapes) {
+ for (int i = 0; i < E.value.shapes.size(); i++) {
+ if (E.value.shapes[i].index == p_shape_index) {
+ return E.key;
}
}
}
diff --git a/scene/3d/importer_mesh_instance_3d.cpp b/scene/3d/importer_mesh_instance_3d.cpp
new file mode 100644
index 0000000000..748a2e5092
--- /dev/null
+++ b/scene/3d/importer_mesh_instance_3d.cpp
@@ -0,0 +1,85 @@
+/*************************************************************************/
+/* importer_mesh_instance_3d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "importer_mesh_instance_3d.h"
+
+#include "scene/resources/importer_mesh.h"
+
+void ImporterMeshInstance3D::set_mesh(const Ref<ImporterMesh> &p_mesh) {
+ mesh = p_mesh;
+}
+Ref<ImporterMesh> ImporterMeshInstance3D::get_mesh() const {
+ return mesh;
+}
+
+void ImporterMeshInstance3D::set_skin(const Ref<Skin> &p_skin) {
+ skin = p_skin;
+}
+Ref<Skin> ImporterMeshInstance3D::get_skin() const {
+ return skin;
+}
+
+void ImporterMeshInstance3D::set_surface_material(int p_idx, const Ref<Material> &p_material) {
+ ERR_FAIL_COND(p_idx < 0);
+ if (p_idx >= surface_materials.size()) {
+ surface_materials.resize(p_idx + 1);
+ }
+
+ surface_materials.write[p_idx] = p_material;
+}
+Ref<Material> ImporterMeshInstance3D::get_surface_material(int p_idx) const {
+ ERR_FAIL_COND_V(p_idx < 0, Ref<Material>());
+ if (p_idx >= surface_materials.size()) {
+ return Ref<Material>();
+ }
+ return surface_materials[p_idx];
+}
+
+void ImporterMeshInstance3D::set_skeleton_path(const NodePath &p_path) {
+ skeleton_path = p_path;
+}
+NodePath ImporterMeshInstance3D::get_skeleton_path() const {
+ return skeleton_path;
+}
+
+void ImporterMeshInstance3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &ImporterMeshInstance3D::set_mesh);
+ ClassDB::bind_method(D_METHOD("get_mesh"), &ImporterMeshInstance3D::get_mesh);
+
+ ClassDB::bind_method(D_METHOD("set_skin", "skin"), &ImporterMeshInstance3D::set_skin);
+ ClassDB::bind_method(D_METHOD("get_skin"), &ImporterMeshInstance3D::get_skin);
+
+ ClassDB::bind_method(D_METHOD("set_skeleton_path", "skeleton_path"), &ImporterMeshInstance3D::set_skeleton_path);
+ ClassDB::bind_method(D_METHOD("get_skeleton_path"), &ImporterMeshInstance3D::get_skeleton_path);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "ImporterMesh"), "set_mesh", "get_mesh");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path");
+}
diff --git a/scene/3d/importer_mesh_instance_3d.h b/scene/3d/importer_mesh_instance_3d.h
new file mode 100644
index 0000000000..0cf7dbe86b
--- /dev/null
+++ b/scene/3d/importer_mesh_instance_3d.h
@@ -0,0 +1,64 @@
+/*************************************************************************/
+/* importer_mesh_instance_3d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_IMPORTER_MESH_INSTANCE_3D_H
+#define SCENE_IMPORTER_MESH_INSTANCE_3D_H
+
+#include "scene/3d/node_3d.h"
+#include "scene/resources/immediate_mesh.h"
+#include "scene/resources/skin.h"
+
+class ImporterMesh;
+
+class ImporterMeshInstance3D : public Node3D {
+ GDCLASS(ImporterMeshInstance3D, Node3D)
+
+ Ref<ImporterMesh> mesh;
+ Ref<Skin> skin;
+ NodePath skeleton_path;
+ Vector<Ref<Material>> surface_materials;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_mesh(const Ref<ImporterMesh> &p_mesh);
+ Ref<ImporterMesh> get_mesh() const;
+
+ void set_skin(const Ref<Skin> &p_skin);
+ Ref<Skin> get_skin() const;
+
+ void set_surface_material(int p_idx, const Ref<Material> &p_material);
+ Ref<Material> get_surface_material(int p_idx) const;
+
+ void set_skeleton_path(const NodePath &p_path);
+ NodePath get_skeleton_path() const;
+};
+#endif
diff --git a/scene/3d/physics_joint_3d.cpp b/scene/3d/joint_3d.cpp
index 12938946a0..aa5ca85bdf 100644
--- a/scene/3d/physics_joint_3d.cpp
+++ b/scene/3d/joint_3d.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* physics_joint_3d.cpp */
+/* joint_3d.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "physics_joint_3d.h"
+#include "joint_3d.h"
#include "scene/scene_string_names.h"
diff --git a/scene/3d/physics_joint_3d.h b/scene/3d/joint_3d.h
index 3e0ea38a5c..211cf8e071 100644
--- a/scene/3d/physics_joint_3d.h
+++ b/scene/3d/joint_3d.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* physics_joint_3d.h */
+/* joint_3d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef PHYSICS_JOINT_H
-#define PHYSICS_JOINT_H
+#ifndef JOINT_3D_H
+#define JOINT_3D_H
#include "scene/3d/node_3d.h"
#include "scene/3d/physics_body_3d.h"
@@ -334,4 +334,4 @@ public:
VARIANT_ENUM_CAST(Generic6DOFJoint3D::Param);
VARIANT_ENUM_CAST(Generic6DOFJoint3D::Flag);
-#endif // PHYSICS_JOINT_H
+#endif // JOINT_3D_H
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index 7e7db57af3..67f4a88228 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -86,8 +86,8 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const {
void MeshInstance3D::_get_property_list(List<PropertyInfo> *p_list) const {
List<String> ls;
- for (const Map<StringName, BlendShapeTrack>::Element *E = blend_shape_tracks.front(); E; E = E->next()) {
- ls.push_back(E->key());
+ for (const KeyValue<StringName, BlendShapeTrack> &E : blend_shape_tracks) {
+ ls.push_back(E.key);
}
ls.sort();
@@ -369,6 +369,8 @@ void MeshInstance3D::create_debug_tangents() {
for (int i = 0; i < mesh->get_surface_count(); i++) {
Array arrays = mesh->surface_get_arrays(i);
+ ERR_CONTINUE(arrays.size() != Mesh::ARRAY_MAX);
+
Vector<Vector3> verts = arrays[Mesh::ARRAY_VERTEX];
Vector<Vector3> norms = arrays[Mesh::ARRAY_NORMAL];
if (norms.size() == 0) {
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index bbf2c81a92..976bff4fbc 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -33,10 +33,6 @@
#include "core/core_string_names.h"
#include "scene/scene_string_names.h"
-#ifdef TOOLS_ENABLED
-#include "editor/plugins/node_3d_editor_plugin.h"
-#endif
-
void PhysicsBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1));
ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1));
@@ -95,12 +91,15 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) {
PhysicsServer3D::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid());
}
-Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_test_only, real_t p_margin, int p_max_collisions) {
- PhysicsServer3D::MotionResult result;
+Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_linear_velocity, bool p_test_only, real_t p_margin, int p_max_collisions) {
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
- if (move_and_collide(p_motion * delta, result, p_margin, p_test_only, p_max_collisions)) {
+ PhysicsServer3D::MotionParameters parameters(get_global_transform(), p_linear_velocity * delta, p_margin);
+ parameters.max_collisions = p_max_collisions;
+
+ PhysicsServer3D::MotionResult result;
+ if (move_and_collide(parameters, result, p_test_only)) {
// Create a new instance when the cached reference is invalid or still in use in script.
if (motion_cache.is_null() || motion_cache->reference_get_count() > 1) {
motion_cache.instantiate();
@@ -115,23 +114,22 @@ Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_t
return Ref<KinematicCollision3D>();
}
-bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only, int p_max_collisions, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) {
- Transform3D gt = get_global_transform();
- bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_max_collisions, p_collide_separation_ray, p_exclude);
+bool PhysicsBody3D::move_and_collide(const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult &r_result, bool p_test_only, bool p_cancel_sliding) {
+ bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_parameters, &r_result);
// Restore direction of motion to be along original motion,
// in order to avoid sliding due to recovery,
// but only if collision depth is low enough to avoid tunneling.
if (p_cancel_sliding) {
- real_t motion_length = p_motion.length();
+ real_t motion_length = p_parameters.motion.length();
real_t precision = 0.001;
if (colliding) {
// Can't just use margin as a threshold because collision depth is calculated on unsafe motion,
// so even in normal resting cases the depth can be a bit more than the margin.
- precision += motion_length * (r_result.unsafe_fraction - r_result.safe_fraction);
+ precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction);
- if (r_result.collisions[0].depth > (real_t)p_margin + precision) {
+ if (r_result.collisions[0].depth > p_parameters.margin + precision) {
p_cancel_sliding = false;
}
}
@@ -140,7 +138,7 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M
// When motion is null, recovery is the resulting motion.
Vector3 motion_normal;
if (motion_length > CMP_EPSILON) {
- motion_normal = p_motion / motion_length;
+ motion_normal = p_parameters.motion / motion_length;
}
// Check depth of recovery.
@@ -149,10 +147,10 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M
real_t recovery_length = recovery.length();
// Fixes cases where canceling slide causes the motion to go too deep into the ground,
// because we're only taking rest information into account and not general recovery.
- if (recovery_length < (real_t)p_margin + precision) {
+ if (recovery_length < p_parameters.margin + precision) {
// Apply adjustment to motion.
r_result.travel = motion_normal * projected_length;
- r_result.remainder = p_motion - r_result.travel;
+ r_result.remainder = p_parameters.motion - r_result.travel;
}
}
}
@@ -164,6 +162,7 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M
}
if (!p_test_only) {
+ Transform3D gt = p_parameters.from;
gt.origin += r_result.travel;
set_global_transform(gt);
}
@@ -171,7 +170,7 @@ bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::M
return colliding;
}
-bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, int p_max_collisions) {
+bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_linear_velocity, const Ref<KinematicCollision3D> &r_collision, real_t p_margin, int p_max_collisions) {
ERR_FAIL_COND_V(!is_inside_tree(), false);
PhysicsServer3D::MotionResult *r = nullptr;
@@ -183,7 +182,9 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
- return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion * delta, p_margin, r, p_max_collisions);
+ PhysicsServer3D::MotionParameters parameters(p_from, p_linear_velocity * delta, p_margin);
+
+ return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), parameters, r);
}
void PhysicsBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) {
@@ -340,6 +341,12 @@ void AnimatableBody3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
last_valid_transform = get_global_transform();
+ _update_kinematic_motion();
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ set_only_update_transform_changes(false);
+ set_notify_local_transform(false);
} break;
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
@@ -367,8 +374,6 @@ void AnimatableBody3D::_bind_methods() {
AnimatableBody3D::AnimatableBody3D() :
StaticBody3D(PhysicsServer3D::BODY_MODE_KINEMATIC) {
PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback);
-
- _update_kinematic_motion();
}
void RigidDynamicBody3D::_body_enter_tree(ObjectID p_id) {
@@ -512,9 +517,9 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state)
//untag all
int rc = 0;
- for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().shapes.size(); i++) {
- E->get().shapes[i].tagged = false;
+ for (KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) {
+ for (int i = 0; i < E.value.shapes.size(); i++) {
+ E.value.shapes[i].tagged = false;
rc++;
}
}
@@ -560,12 +565,12 @@ void RigidDynamicBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state)
//put the ones to remove
- for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().shapes.size(); i++) {
- if (!E->get().shapes[i].tagged) {
- toremove[toremove_count].rid = E->get().rid;
- toremove[toremove_count].body_id = E->key();
- toremove[toremove_count].pair = E->get().shapes[i];
+ for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) {
+ for (int i = 0; i < E.value.shapes.size(); i++) {
+ if (!E.value.shapes[i].tagged) {
+ toremove[toremove_count].rid = E.value.rid;
+ toremove[toremove_count].body_id = E.key;
+ toremove[toremove_count].pair = E.value.shapes[i];
toremove_count++;
}
}
@@ -885,9 +890,9 @@ void RigidDynamicBody3D::set_contact_monitor(bool p_enabled) {
if (!p_enabled) {
ERR_FAIL_COND_MSG(contact_monitor->locked, "Can't disable contact monitoring during in/out callback. Use call_deferred(\"set_contact_monitor\", false) instead.");
- for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) {
+ for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) {
//clean up mess
- Object *obj = ObjectDB::get_instance(E->key());
+ Object *obj = ObjectDB::get_instance(E.key);
Node *node = Object::cast_to<Node>(obj);
if (node) {
@@ -914,8 +919,8 @@ Array RigidDynamicBody3D::get_colliding_bodies() const {
Array ret;
ret.resize(contact_monitor->body_map.size());
int idx = 0;
- for (const Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) {
- Object *obj = ObjectDB::get_instance(E->key());
+ for (const KeyValue<ObjectID, BodyState> &E : contact_monitor->body_map) {
+ Object *obj = ObjectDB::get_instance(E.key);
if (!obj) {
ret.resize(ret.size() - 1); //ops
} else {
@@ -1085,13 +1090,16 @@ void RigidDynamicBody3D::_reload_physics_characteristics() {
bool CharacterBody3D::move_and_slide() {
// Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky
double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time();
- previous_position = get_global_transform().origin;
+
for (int i = 0; i < 3; i++) {
if (locked_axis & (1 << i)) {
- linear_velocity[i] = 0.0;
+ motion_velocity[i] = 0.0;
}
}
+ Transform3D gt = get_global_transform();
+ previous_position = gt.origin;
+
Vector3 current_platform_velocity = platform_velocity;
if ((collision_state.floor || collision_state.wall) && platform_rid.is_valid()) {
@@ -1105,7 +1113,6 @@ bool CharacterBody3D::move_and_slide() {
//this approach makes sure there is less delay between the actual body velocity and the one we saved
PhysicsDirectBodyState3D *bs = PhysicsServer3D::get_singleton()->body_get_direct_state(platform_rid);
if (bs) {
- Transform3D gt = get_global_transform();
Vector3 local_position = gt.origin - bs->get_transform().origin;
current_platform_velocity = bs->get_velocity_at_local_position(local_position);
}
@@ -1119,11 +1126,17 @@ bool CharacterBody3D::move_and_slide() {
bool was_on_floor = collision_state.floor;
collision_state.state = 0;
+ last_motion = Vector3();
+
if (!current_platform_velocity.is_equal_approx(Vector3())) {
+ PhysicsServer3D::MotionParameters parameters(get_global_transform(), current_platform_velocity * delta, margin);
+ parameters.exclude_bodies.insert(platform_rid);
+ if (platform_object_id.is_valid()) {
+ parameters.exclude_objects.insert(platform_object_id);
+ }
+
PhysicsServer3D::MotionResult floor_result;
- Set<RID> exclude;
- exclude.insert(platform_rid);
- if (move_and_collide(current_platform_velocity * delta, floor_result, margin, false, 1, false, false, exclude)) {
+ if (move_and_collide(parameters, floor_result, false, false)) {
motion_results.push_back(floor_result);
CollisionState result_state;
@@ -1146,40 +1159,45 @@ bool CharacterBody3D::move_and_slide() {
if (moving_platform_apply_velocity_on_leave == PLATFORM_VEL_ON_LEAVE_UPWARD_ONLY && current_platform_velocity.dot(up_direction) < 0) {
current_platform_velocity = current_platform_velocity.slide(up_direction);
}
- linear_velocity += current_platform_velocity;
+ motion_velocity += current_platform_velocity;
}
}
- // Reset the gravity accumulation when touching the ground.
- if (collision_state.floor && linear_velocity.dot(up_direction) <= 0) {
- linear_velocity = linear_velocity.slide(up_direction);
- }
-
return motion_results.size() > 0;
}
void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) {
- Vector3 motion = linear_velocity * p_delta;
+ Vector3 motion = motion_velocity * p_delta;
Vector3 motion_slide_up = motion.slide(up_direction);
Vector3 prev_floor_normal = floor_normal;
platform_rid = RID();
+ platform_object_id = ObjectID();
platform_velocity = Vector3();
+ platform_ceiling_velocity = Vector3();
floor_normal = Vector3();
wall_normal = Vector3();
+ ceiling_normal = Vector3();
// No sliding on first attempt to keep floor motion stable when possible,
// When stop on slope is enabled or when there is no up direction.
bool sliding_enabled = !floor_stop_on_slope;
// Constant speed can be applied only the first time sliding is enabled.
bool can_apply_constant_speed = sliding_enabled;
+ // If the platform's ceiling push down the body.
+ bool apply_ceiling_velocity = false;
bool first_slide = true;
- bool vel_dir_facing_up = linear_velocity.dot(up_direction) > 0;
+ bool vel_dir_facing_up = motion_velocity.dot(up_direction) > 0;
Vector3 total_travel;
for (int iteration = 0; iteration < max_slides; ++iteration) {
+ PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin);
+ parameters.max_collisions = 4;
+
PhysicsServer3D::MotionResult result;
- bool collided = move_and_collide(motion, result, margin, false, 4, !sliding_enabled);
+ bool collided = move_and_collide(parameters, result, false, !sliding_enabled);
+
+ last_motion = result.travel;
if (collided) {
motion_results.push_back(result);
@@ -1189,20 +1207,33 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
CollisionState result_state;
_set_collision_direction(result, result_state);
- if (collision_state.floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) {
+ // If we hit a ceiling platform, we set the vertical motion_velocity to at least the platform one.
+ if (collision_state.ceiling && platform_ceiling_velocity != Vector3() && platform_ceiling_velocity.dot(up_direction) < 0) {
+ // If ceiling sliding is on, only apply when the ceiling is flat or when the motion is upward.
+ if (!slide_on_ceiling || motion.dot(up_direction) < 0 || (ceiling_normal + up_direction).length() < 0.01) {
+ apply_ceiling_velocity = true;
+ Vector3 ceiling_vertical_velocity = up_direction * up_direction.dot(platform_ceiling_velocity);
+ Vector3 motion_vertical_velocity = up_direction * up_direction.dot(motion_velocity);
+ if (motion_vertical_velocity.dot(up_direction) > 0 || ceiling_vertical_velocity.length_squared() > motion_vertical_velocity.length_squared()) {
+ motion_velocity = ceiling_vertical_velocity + motion_velocity.slide(up_direction);
+ }
+ }
+ }
+
+ if (collision_state.floor && floor_stop_on_slope && (motion_velocity.normalized() + up_direction).length() < 0.01) {
Transform3D gt = get_global_transform();
if (result.travel.length() <= margin + CMP_EPSILON) {
gt.origin -= result.travel;
}
set_global_transform(gt);
- linear_velocity = Vector3();
+ motion_velocity = Vector3();
motion = Vector3();
+ last_motion = Vector3();
break;
}
if (result.remainder.is_equal_approx(Vector3())) {
motion = Vector3();
- last_motion = result.travel;
break;
}
@@ -1251,7 +1282,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
Vector3 forward = wall_normal.slide(up_direction).normalized();
motion = motion.slide(forward);
// Avoid accelerating when you jump on the wall and smooth falling.
- linear_velocity = linear_velocity.slide(forward);
+ motion_velocity = motion_velocity.slide(forward);
// Allow only lateral motion along previous floor when already on floor.
// Fixes slowing down when moving in diagonal against an inclined wall.
@@ -1280,7 +1311,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
if (stop_all_motion) {
motion = Vector3();
- linear_velocity = Vector3();
+ motion_velocity = Vector3();
}
}
}
@@ -1291,7 +1322,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
real_t motion_angle = Math::abs(Math::acos(-horizontal_normal.dot(motion_slide_up.normalized())));
if (motion_angle < wall_min_slide_angle) {
motion = up_direction * motion.dot(up_direction);
- linear_velocity = up_direction * linear_velocity.dot(up_direction);
+ motion_velocity = up_direction * motion_velocity.dot(up_direction);
apply_default_sliding = false;
}
@@ -1300,21 +1331,35 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
if (apply_default_sliding) {
// Regular sliding, the last part of the test handle the case when you don't want to slide on the ceiling.
- if ((sliding_enabled || !collision_state.floor) && (!collision_state.ceiling || slide_on_ceiling || !vel_dir_facing_up)) {
+ if ((sliding_enabled || !collision_state.floor) && (!collision_state.ceiling || slide_on_ceiling || !vel_dir_facing_up) && !apply_ceiling_velocity) {
const PhysicsServer3D::MotionCollision &collision = result.collisions[0];
+
Vector3 slide_motion = result.remainder.slide(collision.normal);
- if (slide_motion.dot(linear_velocity) > 0.0) {
+ if (collision_state.floor && !collision_state.wall) {
+ // Slide using the intersection between the motion plane and the floor plane,
+ // in order to keep the direction intact.
+ real_t motion_length = slide_motion.length();
+ slide_motion = up_direction.cross(result.remainder).cross(floor_normal);
+
+ // Keep the length from default slide to change speed in slopes by default,
+ // when constant speed is not enabled.
+ slide_motion.normalize();
+ slide_motion *= motion_length;
+ }
+
+ if (slide_motion.dot(motion_velocity) > 0.0) {
motion = slide_motion;
} else {
motion = Vector3();
}
+
if (slide_on_ceiling && result_state.ceiling) {
// Apply slide only in the direction of the input motion, otherwise just stop to avoid jittering when moving against a wall.
if (vel_dir_facing_up) {
- linear_velocity = linear_velocity.slide(collision.normal);
+ motion_velocity = motion_velocity.slide(collision.normal);
} else {
// Avoid acceleration in slope when falling.
- linear_velocity = up_direction * up_direction.dot(linear_velocity);
+ motion_velocity = up_direction * up_direction.dot(motion_velocity);
}
}
}
@@ -1322,18 +1367,19 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
else {
motion = result.remainder;
if (result_state.ceiling && !slide_on_ceiling && vel_dir_facing_up) {
- linear_velocity = linear_velocity.slide(up_direction);
+ motion_velocity = motion_velocity.slide(up_direction);
motion = motion.slide(up_direction);
}
}
}
+ total_travel += result.travel;
+
// Apply Constant Speed.
- if (p_was_on_floor && floor_constant_speed && collision_state.floor && !motion.is_equal_approx(Vector3())) {
- motion = motion.normalized() * MAX(0, (motion_slide_up.length() - result.travel.slide(up_direction).length() - total_travel.slide(up_direction).length()));
+ if (p_was_on_floor && floor_constant_speed && can_apply_constant_speed && collision_state.floor && !motion.is_equal_approx(Vector3())) {
+ Vector3 travel_slide_up = total_travel.slide(up_direction);
+ motion = motion.normalized() * MAX(0, (motion_slide_up.length() - travel_slide_up.length()));
}
-
- total_travel += result.travel;
}
// When you move forward in a downward slope you don’t collide because you will be in the air.
// This test ensures that constant speed is applied, only if the player is still on the ground after the snap is applied.
@@ -1344,44 +1390,48 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
gt.origin = gt.origin - result.travel;
set_global_transform(gt);
- Vector3 motion_slide_norm = motion.slide(prev_floor_normal).normalized();
+ // Slide using the intersection between the motion plane and the floor plane,
+ // in order to keep the direction intact.
+ Vector3 motion_slide_norm = up_direction.cross(motion).cross(prev_floor_normal);
+ motion_slide_norm.normalize();
+
motion = motion_slide_norm * (motion_slide_up.length());
collided = true;
}
- can_apply_constant_speed = !can_apply_constant_speed && !sliding_enabled;
- sliding_enabled = true;
- first_slide = false;
-
- if (!motion.is_equal_approx(Vector3())) {
- last_motion = motion;
- }
-
if (!collided || motion.is_equal_approx(Vector3())) {
break;
}
+
+ can_apply_constant_speed = !can_apply_constant_speed && !sliding_enabled;
+ sliding_enabled = true;
+ first_slide = false;
}
_snap_on_floor(p_was_on_floor, vel_dir_facing_up);
// Reset the gravity accumulation when touching the ground.
if (collision_state.floor && !vel_dir_facing_up) {
- linear_velocity = linear_velocity.slide(up_direction);
+ motion_velocity = motion_velocity.slide(up_direction);
}
}
void CharacterBody3D::_move_and_slide_free(double p_delta) {
- Vector3 motion = linear_velocity * p_delta;
+ Vector3 motion = motion_velocity * p_delta;
platform_rid = RID();
+ platform_object_id = ObjectID();
floor_normal = Vector3();
platform_velocity = Vector3();
bool first_slide = true;
for (int iteration = 0; iteration < max_slides; ++iteration) {
+ PhysicsServer3D::MotionParameters parameters(get_global_transform(), motion, margin);
+
PhysicsServer3D::MotionResult result;
+ bool collided = move_and_collide(parameters, result, false, false);
- bool collided = move_and_collide(motion, result, margin, false, 1, false);
+ last_motion = result.travel;
if (collided) {
motion_results.push_back(result);
@@ -1389,7 +1439,12 @@ void CharacterBody3D::_move_and_slide_free(double p_delta) {
CollisionState result_state;
_set_collision_direction(result, result_state);
- if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-linear_velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
+ if (result.remainder.is_equal_approx(Vector3())) {
+ motion = Vector3();
+ break;
+ }
+
+ if (wall_min_slide_angle != 0 && Math::acos(wall_normal.dot(-motion_velocity.normalized())) < wall_min_slide_angle + FLOOR_ANGLE_THRESHOLD) {
motion = Vector3();
if (result.travel.length() < margin + CMP_EPSILON) {
Transform3D gt = get_global_transform();
@@ -1403,16 +1458,16 @@ void CharacterBody3D::_move_and_slide_free(double p_delta) {
motion = result.remainder.slide(wall_normal);
}
- if (motion.dot(linear_velocity) <= 0.0) {
+ if (motion.dot(motion_velocity) <= 0.0) {
motion = Vector3();
}
}
- first_slide = false;
-
if (!collided || motion.is_equal_approx(Vector3())) {
break;
}
+
+ first_slide = false;
}
}
@@ -1424,9 +1479,12 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
// Snap by at least collision margin to keep floor state consistent.
real_t length = MAX(floor_snap_length, margin);
- Transform3D gt = get_global_transform();
+ PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin);
+ parameters.max_collisions = 4;
+ parameters.collide_separation_ray = true;
+
PhysicsServer3D::MotionResult result;
- if (move_and_collide(-up_direction * length, result, margin, true, 4, false, true)) {
+ if (move_and_collide(parameters, result, true, false)) {
CollisionState result_state;
// Apply direction for floor only.
_set_collision_direction(result, result_state, CollisionState(true, false, false));
@@ -1442,8 +1500,8 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
}
}
- gt.origin += result.travel;
- set_global_transform(gt);
+ parameters.from.origin += result.travel;
+ set_global_transform(parameters.from);
}
}
}
@@ -1456,8 +1514,12 @@ bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facin
// Snap by at least collision margin to keep floor state consistent.
real_t length = MAX(floor_snap_length, margin);
+ PhysicsServer3D::MotionParameters parameters(get_global_transform(), -up_direction * length, margin);
+ parameters.max_collisions = 4;
+ parameters.collide_separation_ray = true;
+
PhysicsServer3D::MotionResult result;
- if (move_and_collide(-up_direction * length, result, margin, true, 4, false, true)) {
+ if (move_and_collide(parameters, result, true, false)) {
CollisionState result_state;
// Don't apply direction for any type.
_set_collision_direction(result, result_state, CollisionState());
@@ -1501,6 +1563,8 @@ void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResu
if (ceiling_angle <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) {
r_state.ceiling = true;
if (p_apply_state.ceiling) {
+ platform_ceiling_velocity = collision.collider_velocity;
+ ceiling_normal = collision.normal;
collision_state.ceiling = true;
}
continue;
@@ -1552,6 +1616,7 @@ void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResu
void CharacterBody3D::_set_platform_data(const PhysicsServer3D::MotionCollision &p_collision) {
platform_rid = p_collision.collider;
+ platform_object_id = p_collision.collider_id;
platform_velocity = p_collision.collider_velocity;
platform_layer = PhysicsServer3D::get_singleton()->body_get_collision_layer(platform_rid);
}
@@ -1564,12 +1629,12 @@ real_t CharacterBody3D::get_safe_margin() const {
return margin;
}
-Vector3 CharacterBody3D::get_linear_velocity() const {
- return linear_velocity;
+const Vector3 &CharacterBody3D::get_motion_velocity() const {
+ return motion_velocity;
}
-void CharacterBody3D::set_linear_velocity(const Vector3 &p_velocity) {
- linear_velocity = p_velocity;
+void CharacterBody3D::set_motion_velocity(const Vector3 &p_velocity) {
+ motion_velocity = p_velocity;
}
bool CharacterBody3D::is_on_floor() const {
@@ -1596,15 +1661,15 @@ bool CharacterBody3D::is_on_ceiling_only() const {
return collision_state.ceiling && !collision_state.floor && !collision_state.wall;
}
-Vector3 CharacterBody3D::get_floor_normal() const {
+const Vector3 &CharacterBody3D::get_floor_normal() const {
return floor_normal;
}
-Vector3 CharacterBody3D::get_wall_normal() const {
+const Vector3 &CharacterBody3D::get_wall_normal() const {
return wall_normal;
}
-Vector3 CharacterBody3D::get_last_motion() const {
+const Vector3 &CharacterBody3D::get_last_motion() const {
return last_motion;
}
@@ -1612,19 +1677,23 @@ Vector3 CharacterBody3D::get_position_delta() const {
return get_transform().origin - previous_position;
}
-Vector3 CharacterBody3D::get_real_velocity() const {
+const Vector3 &CharacterBody3D::get_real_velocity() const {
return real_velocity;
-};
+}
real_t CharacterBody3D::get_floor_angle(const Vector3 &p_up_direction) const {
ERR_FAIL_COND_V(p_up_direction == Vector3(), 0);
return Math::acos(floor_normal.dot(p_up_direction));
}
-Vector3 CharacterBody3D::get_platform_velocity() const {
+const Vector3 &CharacterBody3D::get_platform_velocity() const {
return platform_velocity;
}
+Vector3 CharacterBody3D::get_linear_velocity() const {
+ return get_real_velocity();
+}
+
int CharacterBody3D::get_slide_collision_count() const {
return motion_results.size();
}
@@ -1770,6 +1839,7 @@ void CharacterBody3D::_notification(int p_what) {
// Reset move_and_slide() data.
collision_state.state = 0;
platform_rid = RID();
+ platform_object_id = ObjectID();
motion_results.clear();
platform_velocity = Vector3();
} break;
@@ -1779,8 +1849,8 @@ void CharacterBody3D::_notification(int p_what) {
void CharacterBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody3D::move_and_slide);
- ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &CharacterBody3D::set_linear_velocity);
- ClassDB::bind_method(D_METHOD("get_linear_velocity"), &CharacterBody3D::get_linear_velocity);
+ ClassDB::bind_method(D_METHOD("set_motion_velocity", "motion_velocity"), &CharacterBody3D::set_motion_velocity);
+ ClassDB::bind_method(D_METHOD("get_motion_velocity"), &CharacterBody3D::get_motion_velocity);
ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody3D::set_safe_margin);
ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody3D::get_safe_margin);
@@ -1833,7 +1903,7 @@ void CharacterBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_mode", PROPERTY_HINT_ENUM, "Grounded,Free", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_motion_mode", "get_motion_mode");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "slide_on_ceiling"), "set_slide_on_ceiling_enabled", "is_slide_on_ceiling_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_linear_velocity", "get_linear_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "motion_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_motion_velocity", "get_motion_velocity");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides");
ADD_GROUP("Free Mode", "free_mode_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wall_min_slide_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians", PROPERTY_USAGE_DEFAULT), "set_wall_min_slide_angle", "get_wall_min_slide_angle");
@@ -1958,51 +2028,6 @@ Vector3 KinematicCollision3D::get_collider_velocity(int p_collision_index) const
return result.collisions[p_collision_index].collider_velocity;
}
-Variant KinematicCollision3D::get_collider_metadata(int p_collision_index) const {
- ERR_FAIL_INDEX_V(p_collision_index, result.collision_count, Variant());
- return Variant();
-}
-
-Vector3 KinematicCollision3D::get_best_position() const {
- return result.collision_count ? get_position() : Vector3();
-}
-
-Vector3 KinematicCollision3D::get_best_normal() const {
- return result.collision_count ? get_normal() : Vector3();
-}
-
-Object *KinematicCollision3D::get_best_local_shape() const {
- return result.collision_count ? get_local_shape() : nullptr;
-}
-
-Object *KinematicCollision3D::get_best_collider() const {
- return result.collision_count ? get_collider() : nullptr;
-}
-
-ObjectID KinematicCollision3D::get_best_collider_id() const {
- return result.collision_count ? get_collider_id() : ObjectID();
-}
-
-RID KinematicCollision3D::get_best_collider_rid() const {
- return result.collision_count ? get_collider_rid() : RID();
-}
-
-Object *KinematicCollision3D::get_best_collider_shape() const {
- return result.collision_count ? get_collider_shape() : nullptr;
-}
-
-int KinematicCollision3D::get_best_collider_shape_index() const {
- return result.collision_count ? get_collider_shape_index() : 0;
-}
-
-Vector3 KinematicCollision3D::get_best_collider_velocity() const {
- return result.collision_count ? get_collider_velocity() : Vector3();
-}
-
-Variant KinematicCollision3D::get_best_collider_metadata() const {
- return result.collision_count ? get_collider_metadata() : Variant();
-}
-
void KinematicCollision3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_travel"), &KinematicCollision3D::get_travel);
ClassDB::bind_method(D_METHOD("get_remainder"), &KinematicCollision3D::get_remainder);
@@ -2017,32 +2042,6 @@ void KinematicCollision3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_collider_shape", "collision_index"), &KinematicCollision3D::get_collider_shape, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_collider_shape_index", "collision_index"), &KinematicCollision3D::get_collider_shape_index, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_collider_velocity", "collision_index"), &KinematicCollision3D::get_collider_velocity, DEFVAL(0));
- ClassDB::bind_method(D_METHOD("get_collider_metadata", "collision_index"), &KinematicCollision3D::get_collider_metadata, DEFVAL(0));
-
- ClassDB::bind_method(D_METHOD("get_best_position"), &KinematicCollision3D::get_best_position);
- ClassDB::bind_method(D_METHOD("get_best_normal"), &KinematicCollision3D::get_best_normal);
- ClassDB::bind_method(D_METHOD("get_best_local_shape"), &KinematicCollision3D::get_best_local_shape);
- ClassDB::bind_method(D_METHOD("get_best_collider"), &KinematicCollision3D::get_best_collider);
- ClassDB::bind_method(D_METHOD("get_best_collider_id"), &KinematicCollision3D::get_best_collider_id);
- ClassDB::bind_method(D_METHOD("get_best_collider_rid"), &KinematicCollision3D::get_best_collider_rid);
- ClassDB::bind_method(D_METHOD("get_best_collider_shape"), &KinematicCollision3D::get_best_collider_shape);
- ClassDB::bind_method(D_METHOD("get_best_collider_shape_index"), &KinematicCollision3D::get_best_collider_shape_index);
- ClassDB::bind_method(D_METHOD("get_best_collider_velocity"), &KinematicCollision3D::get_best_collider_velocity);
- ClassDB::bind_method(D_METHOD("get_best_collider_metadata"), &KinematicCollision3D::get_best_collider_metadata);
-
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "travel"), "", "get_travel");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "remainder"), "", "get_remainder");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_count"), "", "get_collision_count");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position"), "", "get_best_position");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "normal"), "", "get_best_normal");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "local_shape"), "", "get_best_local_shape");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider"), "", "get_best_collider");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_id"), "", "get_best_collider_id");
- ADD_PROPERTY(PropertyInfo(Variant::RID, "collider_rid"), "", "get_best_collider_rid");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider_shape"), "", "get_best_collider_shape");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_shape_index"), "", "get_best_collider_shape_index");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "collider_velocity"), "", "get_best_collider_velocity");
- ADD_PROPERTY(PropertyInfo(Variant::NIL, "collider_metadata", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "", "get_best_collider_metadata");
}
///////////////////////////////////////
@@ -2981,14 +2980,11 @@ void PhysicalBone3D::_on_bone_parent_changed() {
_reload_joint();
}
-void PhysicalBone3D::_set_gizmo_move_joint(bool p_move_joint) {
#ifdef TOOLS_ENABLED
+void PhysicalBone3D::_set_gizmo_move_joint(bool p_move_joint) {
gizmo_move_joint = p_move_joint;
- Node3DEditor::get_singleton()->update_transform_gizmo();
-#endif
}
-#ifdef TOOLS_ENABLED
Transform3D PhysicalBone3D::get_global_gizmo_transform() const {
return gizmo_move_joint ? get_global_transform() * joint_offset : get_global_transform();
}
diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h
index a53147cb8f..5677df730c 100644
--- a/scene/3d/physics_body_3d.h
+++ b/scene/3d/physics_body_3d.h
@@ -50,11 +50,11 @@ protected:
uint16_t locked_axis = 0;
- Ref<KinematicCollision3D> _move(const Vector3 &p_motion, bool p_test_only = false, real_t p_margin = 0.001, int p_max_collisions = 1);
+ Ref<KinematicCollision3D> _move(const Vector3 &p_linear_velocity, bool p_test_only = false, real_t p_margin = 0.001, int p_max_collisions = 1);
public:
- bool move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, int p_max_collisions = 1, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>());
- bool test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, int p_max_collisions = 1);
+ bool move_and_collide(const PhysicsServer3D::MotionParameters &p_parameters, PhysicsServer3D::MotionResult &r_result, bool p_test_only = false, bool p_cancel_sliding = true);
+ bool test_move(const Transform3D &p_from, const Vector3 &p_linear_velocity, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001, int p_max_collisions = 1);
void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock);
bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const;
@@ -328,8 +328,8 @@ public:
};
bool move_and_slide();
- virtual Vector3 get_linear_velocity() const override;
- void set_linear_velocity(const Vector3 &p_velocity);
+ const Vector3 &get_motion_velocity() const;
+ void set_motion_velocity(const Vector3 &p_velocity);
bool is_on_floor() const;
bool is_on_floor_only() const;
@@ -337,13 +337,15 @@ public:
bool is_on_wall_only() const;
bool is_on_ceiling() const;
bool is_on_ceiling_only() const;
- Vector3 get_last_motion() const;
+ const Vector3 &get_last_motion() const;
Vector3 get_position_delta() const;
- Vector3 get_floor_normal() const;
- Vector3 get_wall_normal() const;
- Vector3 get_real_velocity() const;
+ const Vector3 &get_floor_normal() const;
+ const Vector3 &get_wall_normal() const;
+ const Vector3 &get_real_velocity() const;
real_t get_floor_angle(const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const;
- Vector3 get_platform_velocity() const;
+ const Vector3 &get_platform_velocity() const;
+
+ virtual Vector3 get_linear_velocity() const override;
int get_slide_collision_count() const;
PhysicsServer3D::MotionResult get_slide_collision(int p_bounce) const;
@@ -379,19 +381,22 @@ private:
bool floor_block_on_wall = true;
bool slide_on_ceiling = true;
int max_slides = 6;
- int platform_layer;
+ int platform_layer = 0;
RID platform_rid;
+ ObjectID platform_object_id;
uint32_t moving_platform_floor_layers = UINT32_MAX;
uint32_t moving_platform_wall_layers = 0;
real_t floor_snap_length = 0.1;
real_t floor_max_angle = Math::deg2rad((real_t)45.0);
real_t wall_min_slide_angle = Math::deg2rad((real_t)15.0);
Vector3 up_direction = Vector3(0.0, 1.0, 0.0);
- Vector3 linear_velocity;
+ Vector3 motion_velocity;
Vector3 floor_normal;
Vector3 wall_normal;
+ Vector3 ceiling_normal;
Vector3 last_motion;
Vector3 platform_velocity;
+ Vector3 platform_ceiling_velocity;
Vector3 previous_position;
Vector3 real_velocity;
@@ -483,18 +488,6 @@ public:
Object *get_collider_shape(int p_collision_index = 0) const;
int get_collider_shape_index(int p_collision_index = 0) const;
Vector3 get_collider_velocity(int p_collision_index = 0) const;
- Variant get_collider_metadata(int p_collision_index = 0) const;
-
- Vector3 get_best_position() const;
- Vector3 get_best_normal() const;
- Object *get_best_local_shape() const;
- Object *get_best_collider() const;
- ObjectID get_best_collider_id() const;
- RID get_best_collider_rid() const;
- Object *get_best_collider_shape() const;
- int get_best_collider_shape_index() const;
- Vector3 get_best_collider_velocity() const;
- Variant get_best_collider_metadata() const;
};
class PhysicalBone3D : public PhysicsBody3D {
@@ -662,10 +655,9 @@ private:
public:
void _on_bone_parent_changed();
- void _set_gizmo_move_joint(bool p_move_joint);
-public:
#ifdef TOOLS_ENABLED
+ void _set_gizmo_move_joint(bool p_move_joint);
virtual Transform3D get_global_gizmo_transform() const override;
virtual Transform3D get_local_gizmo_transform() const override;
#endif
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index 8d90aabfac..2e788051f4 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -291,14 +291,10 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
new_bone_pose.origin = ci->current_pos;
if (!ci->children.is_empty()) {
- /// Rotate basis
- 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) {
- const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1)));
- new_bone_pose.basis.rotate(rot_axis, rot_angle);
- }
+ p_task->skeleton->update_bone_rest_forward_vector(ci->bone);
+ Vector3 forward_vector = p_task->skeleton->get_bone_axis_forward_vector(ci->bone);
+ // Rotate the bone towards the next bone in the chain:
+ new_bone_pose.basis.rotate_to_align(forward_vector, new_bone_pose.origin.direction_to(ci->children[0].current_pos));
} else {
// Set target orientation to tip
diff --git a/scene/3d/soft_dynamic_body_3d.cpp b/scene/3d/soft_dynamic_body_3d.cpp
index a886c61263..21f9b0a35d 100644
--- a/scene/3d/soft_dynamic_body_3d.cpp
+++ b/scene/3d/soft_dynamic_body_3d.cpp
@@ -433,9 +433,9 @@ void SoftDynamicBody3D::prepare_physics_server() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
if (get_mesh().is_valid()) {
- PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh());
+ PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()->get_rid());
} else {
- PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, nullptr);
+ PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, RID());
}
return;
@@ -444,10 +444,10 @@ void SoftDynamicBody3D::prepare_physics_server() {
if (get_mesh().is_valid() && (is_enabled() || (disable_mode != DISABLE_MODE_REMOVE))) {
become_mesh_owner();
- PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh());
+ PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()->get_rid());
RS::get_singleton()->connect("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh));
} else {
- PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, nullptr);
+ PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, RID());
if (RS::get_singleton()->is_connected("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh))) {
RS::get_singleton()->disconnect("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh));
}
diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp
index 4748a9d889..116cab19b1 100644
--- a/scene/3d/spring_arm_3d.cpp
+++ b/scene/3d/spring_arm_3d.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "spring_arm_3d.h"
+#include "scene/3d/camera_3d.h"
void SpringArm3D::_notification(int p_what) {
switch (p_what) {
@@ -133,17 +134,32 @@ void SpringArm3D::process_spring() {
Vector3 motion;
const Vector3 cast_direction(get_global_transform().basis.xform(Vector3(0, 0, 1)));
+ motion = Vector3(cast_direction * (spring_length));
+
if (shape.is_null()) {
- motion = Vector3(cast_direction * (spring_length));
- PhysicsDirectSpaceState3D::RayResult r;
- bool intersected = get_world_3d()->get_direct_space_state()->intersect_ray(get_global_transform().origin, get_global_transform().origin + motion, r, excluded_objects, mask);
- if (intersected) {
- real_t dist = get_global_transform().origin.distance_to(r.position);
- dist -= margin;
- motion_delta = dist / (spring_length);
+ Camera3D *camera = nullptr;
+ for (int i = get_child_count() - 1; 0 <= i; --i) {
+ camera = Object::cast_to<Camera3D>(get_child(i));
+ if (camera) {
+ break;
+ }
+ }
+
+ if (camera != nullptr) {
+ //use camera rotation, but spring arm position
+ Transform3D base_transform = camera->get_global_transform();
+ base_transform.origin = get_global_transform().origin;
+ get_world_3d()->get_direct_space_state()->cast_motion(camera->get_pyramid_shape_rid(), base_transform, motion, 0, motion_delta, motion_delta_unsafe, excluded_objects, mask);
+ } else {
+ PhysicsDirectSpaceState3D::RayResult r;
+ bool intersected = get_world_3d()->get_direct_space_state()->intersect_ray(get_global_transform().origin, get_global_transform().origin + motion, r, excluded_objects, mask);
+ if (intersected) {
+ real_t dist = get_global_transform().origin.distance_to(r.position);
+ dist -= margin;
+ motion_delta = dist / (spring_length);
+ }
}
} else {
- motion = Vector3(cast_direction * spring_length);
get_world_3d()->get_direct_space_state()->cast_motion(shape->get_rid(), get_global_transform(), motion, 0, motion_delta, motion_delta_unsafe, excluded_objects, mask);
}
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index af186072ac..10a66386eb 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -828,9 +828,9 @@ Ref<AnimationNode> AnimationNodeBlendTree::get_node(const StringName &p_name) co
}
StringName AnimationNodeBlendTree::get_node_name(const Ref<AnimationNode> &p_node) const {
- for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) {
- if (E->get().node == p_node) {
- return E->key();
+ for (const KeyValue<StringName, Node> &E : nodes) {
+ if (E.value.node == p_node) {
+ return E.key;
}
}
@@ -850,8 +850,8 @@ Vector2 AnimationNodeBlendTree::get_node_position(const StringName &p_node) cons
void AnimationNodeBlendTree::get_child_nodes(List<ChildNode> *r_child_nodes) {
Vector<StringName> ns;
- for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) {
- ns.push_back(E->key());
+ for (const KeyValue<StringName, Node> &E : nodes) {
+ ns.push_back(E.key);
}
ns.sort_custom<StringName::AlphCompare>();
@@ -886,10 +886,10 @@ void AnimationNodeBlendTree::remove_node(const StringName &p_name) {
nodes.erase(p_name);
//erase connections to name
- for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().connections.size(); i++) {
- if (E->get().connections[i] == p_name) {
- E->get().connections.write[i] = StringName();
+ for (KeyValue<StringName, Node> &E : nodes) {
+ for (int i = 0; i < E.value.connections.size(); i++) {
+ if (E.value.connections[i] == p_name) {
+ E.value.connections.write[i] = StringName();
}
}
}
@@ -910,10 +910,10 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN
nodes.erase(p_name);
//rename connections
- for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().connections.size(); i++) {
- if (E->get().connections[i] == p_name) {
- E->get().connections.write[i] = p_new_name;
+ for (KeyValue<StringName, Node> &E : nodes) {
+ for (int i = 0; i < E.value.connections.size(); i++) {
+ if (E.value.connections[i] == p_name) {
+ E.value.connections.write[i] = p_new_name;
}
}
}
@@ -932,9 +932,9 @@ void AnimationNodeBlendTree::connect_node(const StringName &p_input_node, int p_
Ref<AnimationNode> input = nodes[p_input_node].node;
ERR_FAIL_INDEX(p_input_index, nodes[p_input_node].connections.size());
- for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().connections.size(); i++) {
- StringName output = E->get().connections[i];
+ for (KeyValue<StringName, Node> &E : nodes) {
+ for (int i = 0; i < E.value.connections.size(); i++) {
+ StringName output = E.value.connections[i];
ERR_FAIL_COND(output == p_output_node);
}
}
@@ -976,9 +976,9 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node
return CONNECTION_ERROR_CONNECTION_EXISTS;
}
- for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().connections.size(); i++) {
- StringName output = E->get().connections[i];
+ for (const KeyValue<StringName, Node> &E : nodes) {
+ for (int i = 0; i < E.value.connections.size(); i++) {
+ const StringName output = E.value.connections[i];
if (output == p_output_node) {
return CONNECTION_ERROR_CONNECTION_EXISTS;
}
@@ -988,12 +988,12 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node
}
void AnimationNodeBlendTree::get_node_connections(List<NodeConnection> *r_connections) const {
- for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) {
- for (int i = 0; i < E->get().connections.size(); i++) {
- StringName output = E->get().connections[i];
+ for (const KeyValue<StringName, Node> &E : nodes) {
+ for (int i = 0; i < E.value.connections.size(); i++) {
+ const StringName output = E.value.connections[i];
if (output != StringName()) {
NodeConnection nc;
- nc.input_node = E->key();
+ nc.input_node = E.key;
nc.input_index = i;
nc.output_node = output;
r_connections->push_back(nc);
@@ -1012,8 +1012,8 @@ double AnimationNodeBlendTree::process(double p_time, bool p_seek) {
}
void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) {
- for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) {
- r_list->push_back(E->key());
+ for (const KeyValue<StringName, Node> &E : nodes) {
+ r_list->push_back(E.key);
}
}
@@ -1104,8 +1104,8 @@ bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) cons
void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) const {
List<StringName> names;
- for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) {
- names.push_back(E->key());
+ for (const KeyValue<StringName, Node> &E : nodes) {
+ names.push_back(E.key);
}
names.sort_custom<StringName::AlphCompare>();
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 9fc1dbd0c6..81ecb50ea0 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -329,11 +329,17 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
} else {
// teleport to start
- path.clear();
- current = start_request;
- playing = true;
- play_start = true;
- start_request = StringName(); //clear start request
+ if (p_state_machine->states.has(start_request)) {
+ path.clear();
+ current = start_request;
+ playing = true;
+ play_start = true;
+ start_request = StringName(); //clear start request
+ } else {
+ StringName node = start_request;
+ start_request = StringName(); //clear start request
+ ERR_FAIL_V_MSG(0, "No such node: '" + node + "'");
+ }
}
}
@@ -571,9 +577,9 @@ Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name)
}
StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_node) const {
- for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) {
- if (E->get().node == p_node) {
- return E->key();
+ for (const KeyValue<StringName, State> &E : states) {
+ if (E.value.node == p_node) {
+ return E.key;
}
}
@@ -583,8 +589,8 @@ StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_
void AnimationNodeStateMachine::get_child_nodes(List<ChildNode> *r_child_nodes) {
Vector<StringName> nodes;
- for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) {
- nodes.push_back(E->key());
+ for (const KeyValue<StringName, State> &E : states) {
+ nodes.push_back(E.key);
}
nodes.sort_custom<StringName::AlphCompare>();
@@ -674,8 +680,8 @@ void AnimationNodeStateMachine::rename_node(const StringName &p_name, const Stri
void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const {
List<StringName> nodes;
- for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) {
- nodes.push_back(E->key());
+ for (const KeyValue<StringName, State> &E : states) {
+ nodes.push_back(E.key);
}
nodes.sort_custom<StringName::AlphCompare>();
@@ -897,8 +903,8 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c
void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) const {
List<StringName> names;
- for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) {
- names.push_back(E->key());
+ for (const KeyValue<StringName, State> &E : states) {
+ names.push_back(E.key);
}
names.sort_custom<StringName::AlphCompare>();
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 5825a35030..2c8c4ee788 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -37,7 +37,6 @@
#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"
-#include "editor/editor_settings.h"
#include "scene/2d/skeleton_2d.h"
void AnimatedValuesBackup::update_skeletons() {
@@ -124,8 +123,8 @@ bool AnimationPlayer::_get(const StringName &p_name, Variant &r_ret) const {
} else if (name == "blend_times") {
Vector<BlendKey> keys;
- for (Map<BlendKey, float>::Element *E = blend_times.front(); E; E = E->next()) {
- keys.ordered_insert(E->key());
+ for (const KeyValue<BlendKey, float> &E : blend_times) {
+ keys.ordered_insert(E.key);
}
Array array;
@@ -147,8 +146,8 @@ void AnimationPlayer::_validate_property(PropertyInfo &property) const {
if (property.name == "current_animation") {
List<String> names;
- for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) {
- names.push_back(E->key());
+ for (const KeyValue<StringName, AnimationData> &E : animation_set) {
+ names.push_back(E.key);
}
names.sort();
names.push_front("[stop]");
@@ -167,10 +166,10 @@ void AnimationPlayer::_validate_property(PropertyInfo &property) const {
void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
List<PropertyInfo> anim_names;
- for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) {
- anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E->key()), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
- if (E->get().next != StringName()) {
- anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ for (const KeyValue<StringName, AnimationData> &E : animation_set) {
+ anim_names.push_back(PropertyInfo(Variant::OBJECT, "anims/" + String(E.key), PROPERTY_HINT_RESOURCE_TYPE, "Animation", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
+ if (E.value.next != StringName()) {
+ anim_names.push_back(PropertyInfo(Variant::STRING, "next/" + String(E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
}
}
@@ -1019,8 +1018,8 @@ void AnimationPlayer::rename_animation(const StringName &p_name, const StringNam
List<BlendKey> to_erase;
Map<BlendKey, float> to_insert;
- for (Map<BlendKey, float>::Element *E = blend_times.front(); E; E = E->next()) {
- BlendKey bk = E->key();
+ for (const KeyValue<BlendKey, float> &E : blend_times) {
+ BlendKey bk = E.key;
BlendKey new_bk = bk;
bool erase = false;
if (bk.from == p_name) {
@@ -1034,7 +1033,7 @@ void AnimationPlayer::rename_animation(const StringName &p_name, const StringNam
if (erase) {
to_erase.push_back(bk);
- to_insert[new_bk] = E->get();
+ to_insert[new_bk] = E.value;
}
}
@@ -1071,8 +1070,8 @@ Ref<Animation> AnimationPlayer::get_animation(const StringName &p_name) const {
void AnimationPlayer::get_animation_list(List<StringName> *p_animations) const {
List<String> anims;
- for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) {
- anims.push_back(E->key());
+ for (const KeyValue<StringName, AnimationData> &E : animation_set) {
+ anims.push_back(E.key);
}
anims.sort();
@@ -1365,8 +1364,8 @@ void AnimationPlayer::clear_caches() {
node_cache_map.clear();
- for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) {
- E->get().node_cache.clear();
+ for (KeyValue<StringName, AnimationData> &E : animation_set) {
+ E.value.node_cache.clear();
}
cache_update_size = 0;
@@ -1388,9 +1387,9 @@ bool AnimationPlayer::is_active() const {
}
StringName AnimationPlayer::find_animation(const Ref<Animation> &p_animation) const {
- for (Map<StringName, AnimationData>::Element *E = animation_set.front(); E; E = E->next()) {
- if (E->get().animation == p_animation) {
- return E->key();
+ for (const KeyValue<StringName, AnimationData> &E : animation_set) {
+ if (E.value.animation == p_animation) {
+ return E.key;
}
}
@@ -1493,18 +1492,12 @@ NodePath AnimationPlayer::get_root() const {
}
void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
-#ifdef TOOLS_ENABLED
- const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
-#else
- const String quote_style = "\"";
-#endif
-
String pf = p_function;
if (p_idx == 0 && (p_function == "play" || p_function == "play_backwards" || p_function == "remove_animation" || p_function == "has_animation" || p_function == "queue")) {
List<StringName> al;
get_animation_list(&al);
for (const StringName &name : al) {
- r_options->push_back(String(name).quote(quote_style));
+ r_options->push_back(String(name).quote());
}
}
Node::get_argument_options(p_function, p_idx, r_options);
@@ -1545,12 +1538,12 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o
entry.bone_idx = -1;
backup->entries.push_back(entry);
} else {
- for (Map<StringName, TrackNodeCache::PropertyAnim>::Element *E = nc->property_anim.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, TrackNodeCache::PropertyAnim> &E : nc->property_anim) {
AnimatedValuesBackup::Entry entry;
- entry.object = E->value().object;
- entry.subpath = E->value().subpath;
+ entry.object = E.value.object;
+ entry.subpath = E.value.subpath;
bool valid;
- entry.value = E->value().object->get_indexed(E->value().subpath, &valid);
+ entry.value = E.value.object->get_indexed(E.value.subpath, &valid);
entry.bone_idx = -1;
if (valid) {
backup->entries.push_back(entry);
diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp
index f4e477b613..3c8949ddfb 100644
--- a/scene/debugger/scene_debugger.cpp
+++ b/scene/debugger/scene_debugger.cpp
@@ -249,8 +249,8 @@ void SceneDebugger::remove_from_cache(const String &p_filename, Node *p_node) {
Map<Node *, Map<ObjectID, Node *>> &remove_list = debugger->live_edit_remove_list;
Map<Node *, Map<ObjectID, Node *>>::Element *F = remove_list.find(p_node);
if (F) {
- for (Map<ObjectID, Node *>::Element *G = F->get().front(); G; G = G->next()) {
- memdelete(G->get());
+ for (const KeyValue<ObjectID, Node *> &G : F->get()) {
+ memdelete(G.value);
}
remove_list.erase(F);
}
@@ -339,15 +339,15 @@ void SceneDebuggerObject::_parse_script_properties(Script *p_script, ScriptInsta
}
// Constants
for (ScriptConstantsMap::Element *sc = constants.front(); sc; sc = sc->next()) {
- for (Map<StringName, Variant>::Element *E = sc->get().front(); E; E = E->next()) {
+ for (const KeyValue<StringName, Variant> &E : sc->get()) {
String script_path = sc->key() == p_script ? "" : sc->key()->get_path().get_file() + "/";
- if (E->value().get_type() == Variant::OBJECT) {
- Variant id = ((Object *)E->value())->get_instance_id();
- PropertyInfo pi(id.get_type(), "Constants/" + E->key(), PROPERTY_HINT_OBJECT_ID, "Object");
+ if (E.value.get_type() == Variant::OBJECT) {
+ Variant id = ((Object *)E.value)->get_instance_id();
+ PropertyInfo pi(id.get_type(), "Constants/" + E.key, PROPERTY_HINT_OBJECT_ID, "Object");
properties.push_back(SceneDebuggerProperty(pi, id));
} else {
- PropertyInfo pi(E->value().get_type(), "Constants/" + script_path + E->key());
- properties.push_back(SceneDebuggerProperty(pi, E->value()));
+ PropertyInfo pi(E.value.get_type(), "Constants/" + script_path + E.key);
+ properties.push_back(SceneDebuggerProperty(pi, E.value));
}
}
}
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index c7d1d7ef82..b816f12bc3 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -614,6 +614,11 @@ void CodeEdit::_backspace_internal() {
return;
}
+ if (has_selection()) {
+ delete_selection();
+ return;
+ }
+
int cc = get_caret_column();
int cl = get_caret_line();
@@ -621,11 +626,6 @@ void CodeEdit::_backspace_internal() {
return;
}
- if (has_selection()) {
- delete_selection();
- return;
- }
-
if (cl > 0 && _is_line_hidden(cl - 1)) {
unfold_line(get_caret_line() - 1);
}
@@ -1146,13 +1146,20 @@ bool CodeEdit::is_drawing_executing_lines_gutter() const {
}
void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) {
- if (draw_breakpoints && is_line_breakpointed(p_line)) {
- int padding = p_region.size.x / 6;
+ if (draw_breakpoints) {
+ bool hovering = p_region.has_point(get_local_mouse_pos());
+ bool breakpointed = is_line_breakpointed(p_line);
+
+ if (breakpointed || (hovering && !is_dragging_cursor())) {
+ int padding = p_region.size.x / 6;
+ Rect2 icon_region = p_region;
+ icon_region.position += Point2(padding, padding);
+ icon_region.size -= Point2(padding, padding) * 2;
- Rect2 breakpoint_region = p_region;
- breakpoint_region.position += Point2(padding, padding);
- breakpoint_region.size -= Point2(padding, padding) * 2;
- breakpoint_icon->draw_rect(get_canvas_item(), breakpoint_region, false, breakpoint_color);
+ // Darken icon when hovering & not yet breakpointed.
+ Color use_color = hovering && !breakpointed ? breakpoint_color.darkened(0.4) : breakpoint_color;
+ breakpoint_icon->draw_rect(get_canvas_item(), icon_region, false, use_color);
+ }
}
if (draw_bookmarks && is_line_bookmarked(p_line)) {
@@ -1613,12 +1620,12 @@ Point2 CodeEdit::get_delimiter_start_position(int p_line, int p_column) const {
bool in_region = ((p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->value()) != -1;
/* Check the keys for this line. */
- for (Map<int, int>::Element *E = delimiter_cache[p_line].front(); E; E = E->next()) {
- if (E->key() > p_column) {
+ for (const KeyValue<int, int> &E : delimiter_cache[p_line]) {
+ if (E.key > p_column) {
break;
}
- in_region = E->value() != -1;
- start_position.x = in_region ? E->key() : -1;
+ in_region = E.value != -1;
+ start_position.x = in_region ? E.key : -1;
}
/* Region was found on this line and is not a multiline continuation. */
@@ -1664,12 +1671,12 @@ Point2 CodeEdit::get_delimiter_end_position(int p_line, int p_column) const {
int region = (p_line <= 0 || delimiter_cache[p_line - 1].size() < 1) ? -1 : delimiter_cache[p_line - 1].back()->value();
/* Check the keys for this line. */
- for (Map<int, int>::Element *E = delimiter_cache[p_line].front(); E; E = E->next()) {
- end_position.x = (E->value() == -1) ? E->key() : -1;
- if (E->key() > p_column) {
+ for (const KeyValue<int, int> &E : delimiter_cache[p_line]) {
+ end_position.x = (E.value == -1) ? E.key : -1;
+ if (E.key > p_column) {
break;
}
- region = E->value();
+ region = E.value;
}
/* Region was found on this line and is not a multiline continuation. */
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 611035fff9..54548e1941 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -35,7 +35,6 @@
#include "core/os/os.h"
#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#endif
#include "scene/main/window.h"
@@ -44,17 +43,7 @@ List<Color> ColorPicker::preset_cache;
void ColorPicker::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_THEME_CHANGED: {
- btn_pick->set_icon(get_theme_icon(SNAME("screen_picker"), SNAME("ColorPicker")));
- btn_add_preset->set_icon(get_theme_icon(SNAME("add_preset")));
- _update_presets();
- _update_controls();
- } break;
case NOTIFICATION_ENTER_TREE: {
- btn_pick->set_icon(get_theme_icon(SNAME("screen_picker"), SNAME("ColorPicker")));
- btn_add_preset->set_icon(get_theme_icon(SNAME("add_preset")));
-
- _update_controls();
_update_color();
#ifdef TOOLS_ENABLED
@@ -71,18 +60,39 @@ void ColorPicker::_notification(int p_what) {
}
}
#endif
- } break;
- case NOTIFICATION_PARENTED: {
+ [[fallthrough]];
+ }
+ case NOTIFICATION_THEME_CHANGED: {
+ btn_pick->set_icon(get_theme_icon(SNAME("screen_picker"), SNAME("ColorPicker")));
+ btn_add_preset->set_icon(get_theme_icon(SNAME("add_preset")));
+
+ uv_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height"))));
+ w_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("h_width")), 0));
+
+ wheel_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height"))));
+ wheel_margin->add_theme_constant_override("margin_bottom", 8 * get_theme_default_base_scale());
+
for (int i = 0; i < 4; i++) {
+ labels[i]->set_custom_minimum_size(Size2(get_theme_constant(SNAME("label_width")), 0));
set_offset((Side)i, get_offset((Side)i) + get_theme_constant(SNAME("margin")));
}
+
+ if (Engine::get_singleton()->is_editor_hint()) {
+ // Adjust for the width of the "Script" icon.
+ text_type->set_custom_minimum_size(Size2(28 * get_theme_default_base_scale(), 0));
+ }
+
+ _update_presets();
+ _update_controls();
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
Popup *p = Object::cast_to<Popup>(get_parent());
if (p) {
p->set_size(Size2(get_combined_minimum_size().width + get_theme_constant(SNAME("margin")) * 2, get_combined_minimum_size().height + get_theme_constant(SNAME("margin")) * 2));
}
} break;
+
case NOTIFICATION_WM_CLOSE_REQUEST: {
if (screen != nullptr && screen->is_visible()) {
screen->hide();
@@ -762,11 +772,7 @@ void ColorPicker::_slider_draw(int p_which) {
Size2 size = scroll[p_which]->get_size();
Color left_color;
Color right_color;
-#ifdef TOOLS_ENABLED
- const real_t margin = 4 * EDSCALE;
-#else
- const real_t margin = 4;
-#endif
+ const real_t margin = 4 * get_theme_default_base_scale();
if (p_which == 3) {
scroll[p_which]->draw_texture_rect(get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true);
@@ -1147,7 +1153,6 @@ ColorPicker::ColorPicker() :
uv_edit->set_mouse_filter(MOUSE_FILTER_PASS);
uv_edit->set_h_size_flags(SIZE_EXPAND_FILL);
uv_edit->set_v_size_flags(SIZE_EXPAND_FILL);
- uv_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height"))));
uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, uv_edit));
HBoxContainer *hb_smpl = memnew(HBoxContainer);
@@ -1219,9 +1224,6 @@ ColorPicker::ColorPicker() :
text_type->set_text("#");
text_type->set_tooltip(TTR("Switch between hexadecimal and code values."));
if (Engine::get_singleton()->is_editor_hint()) {
-#ifdef TOOLS_ENABLED
- text_type->set_custom_minimum_size(Size2(28 * EDSCALE, 0)); // Adjust for the width of the "Script" icon.
-#endif
text_type->connect("pressed", callable_mp(this, &ColorPicker::_text_type_toggled));
} else {
text_type->set_flat(true);
@@ -1236,7 +1238,6 @@ ColorPicker::ColorPicker() :
wheel_edit->set_h_size_flags(SIZE_EXPAND_FILL);
wheel_edit->set_v_size_flags(SIZE_EXPAND_FILL);
- wheel_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height"))));
hb_edit->add_child(wheel_edit);
wheel_mat.instantiate();
@@ -1244,12 +1245,7 @@ ColorPicker::ColorPicker() :
circle_mat.instantiate();
circle_mat->set_shader(circle_shader);
- MarginContainer *wheel_margin(memnew(MarginContainer));
-#ifdef TOOLS_ENABLED
- wheel_margin->add_theme_constant_override("margin_bottom", 8 * EDSCALE);
-#else
wheel_margin->add_theme_constant_override("margin_bottom", 8);
-#endif
wheel_edit->add_child(wheel_margin);
wheel_margin->add_child(wheel);
@@ -1261,7 +1257,6 @@ ColorPicker::ColorPicker() :
wheel_uv->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, wheel_uv));
hb_edit->add_child(w_edit);
- w_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("h_width")), 0));
w_edit->set_h_size_flags(SIZE_FILL);
w_edit->set_v_size_flags(SIZE_EXPAND_FILL);
w_edit->connect("gui_input", callable_mp(this, &ColorPicker::_w_input));
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 67ca007eb5..ad4f5ad5b1 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -81,6 +81,7 @@ private:
Control *uv_edit = memnew(Control);
Control *w_edit = memnew(Control);
AspectRatioContainer *wheel_edit = memnew(AspectRatioContainer);
+ MarginContainer *wheel_margin = memnew(MarginContainer);
Ref<ShaderMaterial> wheel_mat;
Ref<ShaderMaterial> circle_mat;
Control *wheel = memnew(Control);
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 81411d5844..38da40a402 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -38,7 +38,6 @@
#include "core/os/os.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
-
#include "scene/gui/label.h"
#include "scene/gui/panel.h"
#include "scene/main/canvas_layer.h"
@@ -48,7 +47,6 @@
#include "servers/text_server.h"
#ifdef TOOLS_ENABLED
-#include "editor/editor_settings.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#endif
@@ -742,7 +740,7 @@ bool Control::has_point(const Point2 &p_point) const {
return Rect2(Point2(), get_size()).has_point(p_point);
}
-void Control::set_drag_forwarding(Control *p_target) {
+void Control::set_drag_forwarding(Node *p_target) {
if (p_target) {
data.drag_owner = p_target->get_instance_id();
} else {
@@ -754,8 +752,7 @@ Variant Control::get_drag_data(const Point2 &p_point) {
if (data.drag_owner.is_valid()) {
Object *obj = ObjectDB::get_instance(data.drag_owner);
if (obj) {
- Control *c = Object::cast_to<Control>(obj);
- return c->call("_get_drag_data_fw", p_point, this);
+ return obj->call("_get_drag_data_fw", p_point, this);
}
}
@@ -771,8 +768,7 @@ bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const
if (data.drag_owner.is_valid()) {
Object *obj = ObjectDB::get_instance(data.drag_owner);
if (obj) {
- Control *c = Object::cast_to<Control>(obj);
- return c->call("_can_drop_data_fw", p_point, p_data, this);
+ return obj->call("_can_drop_data_fw", p_point, p_data, this);
}
}
@@ -787,8 +783,7 @@ void Control::drop_data(const Point2 &p_point, const Variant &p_data) {
if (data.drag_owner.is_valid()) {
Object *obj = ObjectDB::get_instance(data.drag_owner);
if (obj) {
- Control *c = Object::cast_to<Control>(obj);
- c->call("_drop_data_fw", p_point, p_data, this);
+ obj->call("_drop_data_fw", p_point, p_data, this);
return;
}
}
@@ -836,11 +831,12 @@ T Control::get_theme_item_in_types(Control *p_theme_owner, Window *p_theme_owner
ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, T(), "At least one theme type must be specified.");
// First, look through each control or window node in the branch, until no valid parent can be found.
- // For each control iterate through its inheritance chain and see if p_name exists in any of them.
+ // Only nodes with a theme resource attached are considered.
Control *theme_owner = p_theme_owner;
Window *theme_owner_window = p_theme_owner_window;
while (theme_owner || theme_owner_window) {
+ // For each theme resource check the theme types provided and see if p_name exists with any of them.
for (const StringName &E : p_theme_types) {
if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) {
return theme_owner->data.theme->get_theme_item(p_data_type, p_name, E);
@@ -891,11 +887,12 @@ bool Control::has_theme_item_in_types(Control *p_theme_owner, Window *p_theme_ow
ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified.");
// First, look through each control or window node in the branch, until no valid parent can be found.
- // For each control iterate through its inheritance chain and see if p_name exists in any of them.
+ // Only nodes with a theme resource attached are considered.
Control *theme_owner = p_theme_owner;
Window *theme_owner_window = p_theme_owner_window;
while (theme_owner || theme_owner_window) {
+ // For each theme resource check the theme types provided and see if p_name exists with any of them.
for (const StringName &E : p_theme_types) {
if (theme_owner && theme_owner->data.theme->has_theme_item(p_data_type, p_name, E)) {
return true;
@@ -1133,6 +1130,150 @@ bool Control::has_theme_constant(const StringName &p_name, const StringName &p_t
return has_theme_item_in_types(data.theme_owner, data.theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
}
+float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_theme_owner_window) {
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ // For each theme resource see if their assigned theme has the default value defined and valid.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
+
+ while (theme_owner || theme_owner_window) {
+ if (theme_owner && theme_owner->data.theme->has_default_theme_base_scale()) {
+ return theme_owner->data.theme->get_default_theme_base_scale();
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_default_theme_base_scale()) {
+ return theme_owner_window->theme->get_default_theme_base_scale();
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
+ }
+ }
+ }
+
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_default_theme_base_scale()) {
+ return Theme::get_project_default()->get_default_theme_base_scale();
+ }
+ }
+
+ // Lastly, fall back on the default Theme.
+ return Theme::get_default()->get_default_theme_base_scale();
+}
+
+float Control::get_theme_default_base_scale() const {
+ return fetch_theme_default_base_scale(data.theme_owner, data.theme_owner_window);
+}
+
+Ref<Font> Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_theme_owner_window) {
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ // For each theme resource see if their assigned theme has the default value defined and valid.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
+
+ while (theme_owner || theme_owner_window) {
+ if (theme_owner && theme_owner->data.theme->has_default_theme_font()) {
+ return theme_owner->data.theme->get_default_theme_font();
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_default_theme_font()) {
+ return theme_owner_window->theme->get_default_theme_font();
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
+ }
+ }
+ }
+
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_default_theme_font()) {
+ return Theme::get_project_default()->get_default_theme_font();
+ }
+ }
+
+ // Lastly, fall back on the default Theme.
+ return Theme::get_default()->get_default_theme_font();
+}
+
+Ref<Font> Control::get_theme_default_font() const {
+ return fetch_theme_default_font(data.theme_owner, data.theme_owner_window);
+}
+
+int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_theme_owner_window) {
+ // First, look through each control or window node in the branch, until no valid parent can be found.
+ // Only nodes with a theme resource attached are considered.
+ // For each theme resource see if their assigned theme has the default value defined and valid.
+ Control *theme_owner = p_theme_owner;
+ Window *theme_owner_window = p_theme_owner_window;
+
+ while (theme_owner || theme_owner_window) {
+ if (theme_owner && theme_owner->data.theme->has_default_theme_font_size()) {
+ return theme_owner->data.theme->get_default_theme_font_size();
+ }
+
+ if (theme_owner_window && theme_owner_window->theme->has_default_theme_font_size()) {
+ return theme_owner_window->theme->get_default_theme_font_size();
+ }
+
+ Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
+ Control *parent_c = Object::cast_to<Control>(parent);
+ if (parent_c) {
+ theme_owner = parent_c->data.theme_owner;
+ theme_owner_window = parent_c->data.theme_owner_window;
+ } else {
+ Window *parent_w = Object::cast_to<Window>(parent);
+ if (parent_w) {
+ theme_owner = parent_w->theme_owner;
+ theme_owner_window = parent_w->theme_owner_window;
+ } else {
+ theme_owner = nullptr;
+ theme_owner_window = nullptr;
+ }
+ }
+ }
+
+ // Secondly, check the project-defined Theme resource.
+ if (Theme::get_project_default().is_valid()) {
+ if (Theme::get_project_default()->has_default_theme_font_size()) {
+ return Theme::get_project_default()->get_default_theme_font_size();
+ }
+ }
+
+ // Lastly, fall back on the default Theme.
+ return Theme::get_default()->get_default_theme_font_size();
+}
+
+int Control::get_theme_default_font_size() const {
+ return fetch_theme_default_font_size(data.theme_owner, data.theme_owner_window);
+}
+
Rect2 Control::get_parent_anchorable_rect() const {
if (!is_inside_tree()) {
return Rect2();
@@ -2452,8 +2593,8 @@ bool Control::is_text_field() const {
return false;
}
-Vector<Vector2i> Control::structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const {
- Vector<Vector2i> ret;
+Array Control::structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const {
+ Array ret;
switch (p_theme_type) {
case STRUCTURED_TEXT_URI: {
int prev = 0;
@@ -2619,12 +2760,6 @@ bool Control::is_visibility_clip_disabled() const {
}
void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
-#ifdef TOOLS_ENABLED
- const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
-#else
- const String quote_style = "\"";
-#endif
-
Node::get_argument_options(p_function, p_idx, r_options);
if (p_idx == 0) {
@@ -2644,7 +2779,7 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List
sn.sort_custom<StringName::AlphCompare>();
for (const StringName &name : sn) {
- r_options->push_back(String(name).quote(quote_style));
+ r_options->push_back(String(name).quote());
}
}
}
@@ -2792,6 +2927,10 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Control::has_theme_color, DEFVAL(""));
ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Control::has_theme_constant, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("get_theme_default_base_scale"), &Control::get_theme_default_base_scale);
+ ClassDB::bind_method(D_METHOD("get_theme_default_font"), &Control::get_theme_default_font);
+ ClassDB::bind_method(D_METHOD("get_theme_default_font_size"), &Control::get_theme_default_font_size);
+
ClassDB::bind_method(D_METHOD("get_parent_control"), &Control::get_parent_control);
ClassDB::bind_method(D_METHOD("set_h_grow_direction", "direction"), &Control::set_h_grow_direction);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 9cec5d6e8d..bdc06319ea 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -283,7 +283,7 @@ protected:
//virtual void _window_gui_input(InputEvent p_event);
- virtual Vector<Vector2i> structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const;
+ virtual Array structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const;
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -355,7 +355,7 @@ public:
virtual Size2 get_minimum_size() const;
virtual Size2 get_combined_minimum_size() const;
virtual bool has_point(const Point2 &p_point) const;
- virtual void set_drag_forwarding(Control *p_target);
+ virtual void set_drag_forwarding(Node *p_target);
virtual Variant get_drag_data(const Point2 &p_point);
virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
virtual void drop_data(const Point2 &p_point, const Variant &p_data);
@@ -509,6 +509,14 @@ public:
bool has_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
bool has_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
+ static float fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_theme_owner_window);
+ static Ref<Font> fetch_theme_default_font(Control *p_theme_owner, Window *p_theme_owner_window);
+ static int fetch_theme_default_font_size(Control *p_theme_owner, Window *p_theme_owner_window);
+
+ float get_theme_default_base_scale() const;
+ Ref<Font> get_theme_default_font() const;
+ int get_theme_default_font_size() const;
+
/* TOOLTIP */
void set_tooltip(const String &p_tooltip);
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 5d98aaa698..71d2778cc3 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -37,7 +37,6 @@
#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"
-#include "editor/editor_scale.h"
#include "scene/main/window.h" // Only used to check for more modals when dimming the editor.
#endif
@@ -363,8 +362,7 @@ Button *ConfirmationDialog::get_cancel_button() {
ConfirmationDialog::ConfirmationDialog() {
set_title(TTRC("Please Confirm..."));
-#ifdef TOOLS_ENABLED
- set_min_size(Size2(200, 70) * EDSCALE);
-#endif
+ set_min_size(Size2(200, 70));
+
cancel = add_cancel_button();
}
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index 56b8a936e1..5d024d3be7 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -32,15 +32,6 @@
#include "core/os/keyboard.h"
-#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
-#define SPACING (3 * EDSCALE)
-#define POINT_WIDTH (8 * EDSCALE)
-#else
-#define SPACING 3
-#define POINT_WIDTH 8
-#endif
-
GradientEdit::GradientEdit() {
set_focus_mode(FOCUS_ALL);
@@ -53,12 +44,12 @@ GradientEdit::GradientEdit() {
int GradientEdit::_get_point_from_pos(int x) {
int result = -1;
- int total_w = get_size().width - get_size().height - SPACING;
+ int total_w = get_size().width - get_size().height - draw_spacing;
float min_distance = 1e20;
for (int i = 0; i < points.size(); i++) {
//Check if we clicked at point
float distance = ABS(x - points[i].offset * total_w);
- float min = (POINT_WIDTH / 2 * 1.7); //make it easier to grab
+ float min = (draw_point_width / 2 * 1.7); //make it easier to grab
if (distance <= min && distance < min_distance) {
result = i;
min_distance = distance;
@@ -129,7 +120,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
grabbed = _get_point_from_pos(x);
if (grabbed != -1) {
- int total_w = get_size().width - get_size().height - SPACING;
+ int total_w = get_size().width - get_size().height - draw_spacing;
Gradient::Point newPoint = points[grabbed];
newPoint.offset = CLAMP(x / float(total_w), 0, 1);
@@ -151,10 +142,10 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
update();
int x = mb->get_position().x;
- int total_w = get_size().width - get_size().height - SPACING;
+ int total_w = get_size().width - get_size().height - draw_spacing;
//Check if color selector was clicked.
- if (x > total_w + SPACING) {
+ if (x > total_w + draw_spacing) {
_show_color_picker();
return;
}
@@ -225,7 +216,7 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid() && grabbing) {
- int total_w = get_size().width - get_size().height - SPACING;
+ int total_w = get_size().width - get_size().height - draw_spacing;
int x = mm->get_position().x;
@@ -297,6 +288,12 @@ void GradientEdit::_notification(int p_what) {
picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed));
}
}
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ draw_spacing = BASE_SPACING * get_theme_default_base_scale();
+ draw_point_width = BASE_POINT_WIDTH * get_theme_default_base_scale();
+ }
+
if (p_what == NOTIFICATION_DRAW) {
int w = get_size().x;
int h = get_size().y;
@@ -305,7 +302,7 @@ void GradientEdit::_notification(int p_what) {
return; //Safety check. We have division by 'h'. And in any case there is nothing to draw with such size
}
- int total_w = get_size().width - get_size().height - SPACING;
+ int total_w = get_size().width - get_size().height - draw_spacing;
//Draw checker pattern for ramp
draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true);
@@ -358,7 +355,7 @@ void GradientEdit::_notification(int p_what) {
col.a = 0.9;
draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col);
- Rect2 rect = Rect2(points[i].offset * total_w - POINT_WIDTH / 2, h / 2, POINT_WIDTH, h / 2);
+ Rect2 rect = Rect2(points[i].offset * total_w - draw_point_width / 2, h / 2, draw_point_width, h / 2);
draw_rect(rect, points[i].color, true);
draw_rect(rect, col, false);
if (grabbed == i) {
@@ -375,15 +372,15 @@ void GradientEdit::_notification(int p_what) {
}
//Draw "button" for color selector
- draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + SPACING, 0, h, h), true);
+ draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + draw_spacing, 0, h, h), true);
if (grabbed != -1) {
//Draw with selection color
- draw_rect(Rect2(total_w + SPACING, 0, h, h), points[grabbed].color);
+ draw_rect(Rect2(total_w + draw_spacing, 0, h, h), points[grabbed].color);
} else {
//if no color selected draw grey color with 'X' on top.
- draw_rect(Rect2(total_w + SPACING, 0, h, h), Color(0.5, 0.5, 0.5, 1));
- draw_line(Vector2(total_w + SPACING, 0), Vector2(total_w + SPACING + h, h), Color(1, 1, 1, 0.6));
- draw_line(Vector2(total_w + SPACING, h), Vector2(total_w + SPACING + h, 0), Color(1, 1, 1, 0.6));
+ draw_rect(Rect2(total_w + draw_spacing, 0, h, h), Color(0.5, 0.5, 0.5, 1));
+ draw_line(Vector2(total_w + draw_spacing, 0), Vector2(total_w + draw_spacing + h, h), Color(1, 1, 1, 0.6));
+ draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6));
}
//Draw borders around color ramp if in focus
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index a173631963..f3a39daaf6 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -46,6 +46,13 @@ class GradientEdit : public Control {
int grabbed = -1;
Vector<Gradient::Point> points;
+ // Make sure to use the scaled value below.
+ const int BASE_SPACING = 3;
+ const int BASE_POINT_WIDTH = 8;
+
+ int draw_spacing = BASE_SPACING;
+ int draw_point_width = BASE_POINT_WIDTH;
+
void _draw_checker(int x, int y, int w, int h);
void _color_changed(const Color &p_color);
int _get_point_from_pos(int x);
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index d9c08ec272..35e31be9af 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -36,10 +36,6 @@
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
-#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
-#endif
-
constexpr int MINIMAP_OFFSET = 12;
constexpr int MINIMAP_PADDING = 5;
@@ -436,6 +432,8 @@ void GraphEdit::_notification(int p_what) {
snap_button->set_icon(get_theme_icon(SNAME("snap")));
minimap_button->set_icon(get_theme_icon(SNAME("minimap")));
layout_button->set_icon(get_theme_icon(SNAME("layout")));
+
+ zoom_label->set_custom_minimum_size(Size2(48, 0) * get_theme_default_base_scale());
}
if (p_what == NOTIFICATION_READY) {
Size2 hmin = h_scroll->get_combined_minimum_size();
@@ -816,11 +814,7 @@ void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from
scaled_points.push_back(points[i] * p_zoom);
}
-#ifdef TOOLS_ENABLED
- p_where->draw_polyline_colors(scaled_points, colors, Math::floor(p_width * EDSCALE), lines_antialiased);
-#else
- p_where->draw_polyline_colors(scaled_points, colors, p_width, lines_antialiased);
-#endif
+ p_where->draw_polyline_colors(scaled_points, colors, Math::floor(p_width * get_theme_default_base_scale()), lines_antialiased);
}
void GraphEdit::_connections_layer_draw() {
@@ -2272,11 +2266,7 @@ GraphEdit::GraphEdit() {
zoom_label->set_visible(false);
zoom_label->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
zoom_label->set_align(Label::ALIGN_CENTER);
-#ifdef TOOLS_ENABLED
- zoom_label->set_custom_minimum_size(Size2(48, 0) * EDSCALE);
-#else
zoom_label->set_custom_minimum_size(Size2(48, 0));
-#endif
_update_zoom_label();
zoom_minus = memnew(Button);
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index ecf735d7f5..8462fd259e 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -403,28 +403,28 @@ void GraphNode::_notification(int p_what) {
close_rect = Rect2();
}
- for (Map<int, Slot>::Element *E = slot_info.front(); E; E = E->next()) {
- if (E->key() < 0 || E->key() >= cache_y.size()) {
+ for (const KeyValue<int, Slot> &E : slot_info) {
+ if (E.key < 0 || E.key >= cache_y.size()) {
continue;
}
- if (!slot_info.has(E->key())) {
+ if (!slot_info.has(E.key)) {
continue;
}
- const Slot &s = slot_info[E->key()];
+ const Slot &s = slot_info[E.key];
//left
if (s.enable_left) {
Ref<Texture2D> p = port;
if (s.custom_slot_left.is_valid()) {
p = s.custom_slot_left;
}
- p->draw(get_canvas_item(), icofs + Point2(edgeofs, cache_y[E->key()]), s.color_left);
+ p->draw(get_canvas_item(), icofs + Point2(edgeofs, cache_y[E.key]), s.color_left);
}
if (s.enable_right) {
Ref<Texture2D> p = port;
if (s.custom_slot_right.is_valid()) {
p = s.custom_slot_right;
}
- p->draw(get_canvas_item(), icofs + Point2(get_size().x - edgeofs, cache_y[E->key()]), s.color_right);
+ p->draw(get_canvas_item(), icofs + Point2(get_size().x - edgeofs, cache_y[E.key]), s.color_right);
}
}
diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp
index 1107e3a4af..2beb2624d2 100644
--- a/scene/gui/grid_container.cpp
+++ b/scene/gui/grid_container.cpp
@@ -82,15 +82,15 @@ void GridContainer::_notification(int p_what) {
// Evaluate the remaining space for expanded columns/rows.
Size2 remaining_space = get_size();
- for (Map<int, int>::Element *E = col_minw.front(); E; E = E->next()) {
- if (!col_expanded.has(E->key())) {
- remaining_space.width -= E->get();
+ for (const KeyValue<int, int> &E : col_minw) {
+ if (!col_expanded.has(E.key)) {
+ remaining_space.width -= E.value;
}
}
- for (Map<int, int>::Element *E = row_minh.front(); E; E = E->next()) {
- if (!row_expanded.has(E->key())) {
- remaining_space.height -= E->get();
+ for (const KeyValue<int, int> &E : row_minh) {
+ if (!row_expanded.has(E.key)) {
+ remaining_space.height -= E.value;
}
}
remaining_space.height -= vsep * MAX(max_row - 1, 0);
@@ -247,12 +247,12 @@ Size2 GridContainer::get_minimum_size() const {
Size2 ms;
- for (Map<int, int>::Element *E = col_minw.front(); E; E = E->next()) {
- ms.width += E->get();
+ for (const KeyValue<int, int> &E : col_minw) {
+ ms.width += E.value;
}
- for (Map<int, int>::Element *E = row_minh.front(); E; E = E->next()) {
- ms.height += E->get();
+ for (const KeyValue<int, int> &E : row_minh) {
+ ms.height += E.value;
}
ms.height += vsep * max_row;
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 18cde25e55..b8cb618171 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -108,7 +108,7 @@ void Label::_shape() {
}
lines_rid.clear();
- uint8_t autowrap_flags = TextServer::BREAK_MANDATORY;
+ uint16_t autowrap_flags = TextServer::BREAK_MANDATORY;
switch (autowrap_mode) {
case AUTOWRAP_WORD_SMART:
autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY;
@@ -122,10 +122,10 @@ void Label::_shape() {
case AUTOWRAP_OFF:
break;
}
- Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags);
+ PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags);
- for (int i = 0; i < line_breaks.size(); i++) {
- RID line = TS->shaped_text_substr(text_rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x);
+ for (int i = 0; i < line_breaks.size(); i = i + 2) {
+ RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
lines_rid.push_back(line);
}
}
@@ -145,7 +145,7 @@ void Label::_shape() {
}
if (lines_dirty) {
- uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
+ uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
switch (overrun_behavior) {
case OVERRUN_TRIM_WORD_ELLIPSIS:
overrun_flags |= TextServer::OVERRUN_TRIM;
@@ -231,7 +231,7 @@ void Label::_update_visible() {
}
}
-inline void draw_glyph(const TextServer::Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Vector2 &p_ofs) {
+inline void draw_glyph(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Vector2 &p_ofs) {
if (p_gl.font_rid != RID()) {
TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_color);
} else {
@@ -239,7 +239,7 @@ inline void draw_glyph(const TextServer::Glyph &p_gl, const RID &p_canvas, const
}
}
-inline void draw_glyph_outline(const TextServer::Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Color &p_font_shadow_color, const Color &p_font_outline_color, const int &p_shadow_outline_size, const int &p_outline_size, const Vector2 &p_ofs, const Vector2 &shadow_ofs) {
+inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Color &p_font_shadow_color, const Color &p_font_outline_color, const int &p_shadow_outline_size, const int &p_outline_size, const Vector2 &p_ofs, const Vector2 &shadow_ofs) {
if (p_gl.font_rid != RID()) {
if (p_font_shadow_color.a > 0) {
TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color);
@@ -387,21 +387,25 @@ void Label::_notification(int p_what) {
} break;
}
- const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(lines_rid[i]);
- const TextServer::Glyph *glyphs = visual.ptr();
- int gl_size = visual.size();
- TextServer::TrimData trim_data = TS->shaped_text_get_trim_data(lines_rid[i]);
+ const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);
+ int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]);
+
+ int ellipsis_pos = TS->shaped_text_get_ellipsis_pos(lines_rid[i]);
+ int trim_pos = TS->shaped_text_get_trim_pos(lines_rid[i]);
+
+ const Glyph *ellipsis_glyphs = TS->shaped_text_get_ellipsis_glyphs(lines_rid[i]);
+ int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]);
// Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps.
if (font_shadow_color.a > 0 || (font_outline_color.a != 0.0 && outline_size > 0)) {
Vector2 offset = ofs;
// Draw RTL ellipsis string when necessary.
- if (rtl && trim_data.ellipsis_pos >= 0) {
- for (int gl_idx = trim_data.ellipsis_glyph_buf.size() - 1; gl_idx >= 0; gl_idx--) {
- for (int j = 0; j < trim_data.ellipsis_glyph_buf[gl_idx].repeat; j++) {
+ if (rtl && ellipsis_pos >= 0) {
+ for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) {
+ for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
//Draw glyph outlines and shadow.
- draw_glyph_outline(trim_data.ellipsis_glyph_buf[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
- offset.x += trim_data.ellipsis_glyph_buf[gl_idx].advance;
+ draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ offset.x += ellipsis_glyphs[gl_idx].advance;
}
}
}
@@ -410,13 +414,13 @@ void Label::_notification(int p_what) {
for (int j = 0; j < gl_size; j++) {
for (int k = 0; k < glyphs[j].repeat; k++) {
// Trim when necessary.
- if (trim_data.trim_pos >= 0) {
+ if (trim_pos >= 0) {
if (rtl) {
- if (j < trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ if (j < trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
continue;
}
} else {
- if (j >= trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ if (j >= trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
break;
}
}
@@ -428,12 +432,12 @@ void Label::_notification(int p_what) {
}
}
// Draw LTR ellipsis string when necessary.
- if (!rtl && trim_data.ellipsis_pos >= 0) {
- for (int gl_idx = 0; gl_idx < trim_data.ellipsis_glyph_buf.size(); gl_idx++) {
- for (int j = 0; j < trim_data.ellipsis_glyph_buf[gl_idx].repeat; j++) {
+ if (!rtl && ellipsis_pos >= 0) {
+ for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) {
+ for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
//Draw glyph outlines and shadow.
- draw_glyph_outline(trim_data.ellipsis_glyph_buf[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
- offset.x += trim_data.ellipsis_glyph_buf[gl_idx].advance;
+ draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ offset.x += ellipsis_glyphs[gl_idx].advance;
}
}
}
@@ -442,12 +446,12 @@ void Label::_notification(int p_what) {
// Draw main text. Note: Do not merge this into the single loop with the outline, to prevent overlaps.
// Draw RTL ellipsis string when necessary.
- if (rtl && trim_data.ellipsis_pos >= 0) {
- for (int gl_idx = trim_data.ellipsis_glyph_buf.size() - 1; gl_idx >= 0; gl_idx--) {
- for (int j = 0; j < trim_data.ellipsis_glyph_buf[gl_idx].repeat; j++) {
+ if (rtl && ellipsis_pos >= 0) {
+ for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) {
+ for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
//Draw glyph outlines and shadow.
- draw_glyph(trim_data.ellipsis_glyph_buf[gl_idx], ci, font_color, ofs);
- ofs.x += trim_data.ellipsis_glyph_buf[gl_idx].advance;
+ draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
+ ofs.x += ellipsis_glyphs[gl_idx].advance;
}
}
}
@@ -456,13 +460,13 @@ void Label::_notification(int p_what) {
for (int j = 0; j < gl_size; j++) {
for (int k = 0; k < glyphs[j].repeat; k++) {
// Trim when necessary.
- if (trim_data.trim_pos >= 0) {
+ if (trim_pos >= 0) {
if (rtl) {
- if (j < trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ if (j < trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
continue;
}
} else {
- if (j >= trim_data.trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ if (j >= trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
break;
}
}
@@ -474,12 +478,12 @@ void Label::_notification(int p_what) {
}
}
// Draw LTR ellipsis string when necessary.
- if (!rtl && trim_data.ellipsis_pos >= 0) {
- for (int gl_idx = 0; gl_idx < trim_data.ellipsis_glyph_buf.size(); gl_idx++) {
- for (int j = 0; j < trim_data.ellipsis_glyph_buf[gl_idx].repeat; j++) {
+ if (!rtl && ellipsis_pos >= 0) {
+ for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) {
+ for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
//Draw glyph outlines and shadow.
- draw_glyph(trim_data.ellipsis_glyph_buf[gl_idx], ci, font_color, ofs);
- ofs.x += trim_data.ellipsis_glyph_buf[gl_idx].advance;
+ draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
+ ofs.x += ellipsis_glyphs[gl_idx].advance;
}
}
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 89fd9bfc88..653885aa08 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -40,7 +40,6 @@
#include "servers/display_server.h"
#include "servers/text_server.h"
#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
#endif
#include "scene/main/window.h"
@@ -67,10 +66,10 @@ void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) {
if (p_move_by_word) {
int cc = caret_column;
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = words.size() - 1; i >= 0; i--) {
- if (words[i].x < cc) {
- cc = words[i].x;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
+ for (int i = words.size() - 2; i >= 0; i = i - 2) {
+ if (words[i] < cc) {
+ cc = words[i];
break;
}
}
@@ -99,10 +98,10 @@ void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
if (p_move_by_word) {
int cc = caret_column;
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = 0; i < words.size(); i++) {
- if (words[i].y > cc) {
- cc = words[i].y;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
+ for (int i = 1; i < words.size(); i = i + 2) {
+ if (words[i] > cc) {
+ cc = words[i];
break;
}
}
@@ -151,10 +150,10 @@ void LineEdit::_backspace(bool p_word, bool p_all_to_left) {
if (p_word) {
int cc = caret_column;
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = words.size() - 1; i >= 0; i--) {
- if (words[i].x < cc) {
- cc = words[i].x;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
+ for (int i = words.size() - 2; i >= 0; i = i - 2) {
+ if (words[i] < cc) {
+ cc = words[i];
break;
}
}
@@ -194,10 +193,10 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) {
if (p_word) {
int cc = caret_column;
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = 0; i < words.size(); i++) {
- if (words[i].y > cc) {
- cc = words[i].y;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
+ for (int i = 1; i < words.size(); i = i + 2) {
+ if (words[i] > cc) {
+ cc = words[i];
break;
}
}
@@ -276,12 +275,12 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
// Double-click select word.
last_dblclk = OS::get_singleton()->get_ticks_msec();
last_dblclk_pos = b->get_position();
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text_rid);
- for (int i = 0; i < words.size(); i++) {
- if ((words[i].x < caret_column && words[i].y > caret_column) || (i == words.size() - 1 && caret_column == words[i].y)) {
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text_rid);
+ for (int i = 0; i < words.size(); i = i + 2) {
+ if ((words[i] < caret_column && words[i + 1] > caret_column) || (i == words.size() - 2 && caret_column == words[i + 1])) {
selection.enabled = true;
- selection.begin = words[i].x;
- selection.end = words[i].y;
+ selection.begin = words[i];
+ selection.end = words[i + 1];
selection.double_click = true;
caret_column = selection.end;
break;
@@ -713,11 +712,7 @@ void LineEdit::_notification(int p_what) {
ofs_max -= r_icon->get_width();
}
-#ifdef TOOLS_ENABLED
- int caret_width = Math::round(EDSCALE);
-#else
- int caret_width = 1;
-#endif
+ int caret_width = Math::round(1 * get_theme_default_base_scale());
// Draw selections rects.
Vector2 ofs = Point2(x_ofs + scroll_offset, y_ofs);
@@ -737,9 +732,8 @@ void LineEdit::_notification(int p_what) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_color);
}
}
- const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(text_rid);
- const TextServer::Glyph *glyphs = visual.ptr();
- int gl_size = visual.size();
+ const Glyph *glyphs = TS->shaped_text_get_glyphs(text_rid);
+ int gl_size = TS->shaped_text_get_glyph_count(text_rid);
// Draw text.
ofs.y += TS->shaped_text_get_ascent(text_rid);
@@ -783,38 +777,36 @@ void LineEdit::_notification(int p_what) {
if (draw_caret) {
if (ime_text.length() == 0) {
// Normal caret.
- Rect2 l_caret, t_caret;
- TextServer::Direction l_dir, t_dir;
- TS->shaped_text_get_carets(text_rid, caret_column, l_caret, l_dir, t_caret, t_dir);
+ CaretInfo caret = TS->shaped_text_get_carets(text_rid, caret_column);
- if (l_caret == Rect2() && t_caret == Rect2()) {
+ if (caret.l_caret == Rect2() && caret.t_caret == Rect2()) {
// No carets, add one at the start.
int h = get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size")));
int y = style->get_offset().y + (y_area - h) / 2;
if (rtl) {
- l_dir = TextServer::DIRECTION_RTL;
- l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h));
+ caret.l_dir = TextServer::DIRECTION_RTL;
+ caret.l_caret = Rect2(Vector2(ofs_max, y), Size2(caret_width, h));
} else {
- l_dir = TextServer::DIRECTION_LTR;
- l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h));
+ caret.l_dir = TextServer::DIRECTION_LTR;
+ caret.l_caret = Rect2(Vector2(x_ofs, y), Size2(caret_width, h));
}
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, caret_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.l_caret, caret_color);
} else {
- if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) {
+ if (caret.l_caret != Rect2() && caret.l_dir == TextServer::DIRECTION_AUTO) {
// Draw extra marker on top of mid caret.
- Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width);
+ Rect2 trect = Rect2(caret.l_caret.position.x - 3 * caret_width, caret.l_caret.position.y, 6 * caret_width, caret_width);
trect.position += ofs;
RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color);
}
- l_caret.position += ofs;
- l_caret.size.x = caret_width;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, l_caret, caret_color);
+ caret.l_caret.position += ofs;
+ caret.l_caret.size.x = caret_width;
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.l_caret, caret_color);
- t_caret.position += ofs;
- t_caret.size.x = caret_width;
+ caret.t_caret.position += ofs;
+ caret.t_caret.size.x = caret_width;
- RenderingServer::get_singleton()->canvas_item_add_rect(ci, t_caret, caret_color);
+ RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.t_caret, caret_color);
}
} else {
{
@@ -1114,32 +1106,31 @@ Vector2i LineEdit::get_caret_pixel_pos() {
}
Vector2i ret;
- Rect2 l_caret, t_caret;
- TextServer::Direction l_dir, t_dir;
+ CaretInfo caret;
// Get position of the start of caret.
if (ime_text.length() != 0 && ime_selection.x != 0) {
- TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x, l_caret, l_dir, t_caret, t_dir);
+ caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x);
} else {
- TS->shaped_text_get_carets(text_rid, caret_column, l_caret, l_dir, t_caret, t_dir);
+ caret = TS->shaped_text_get_carets(text_rid, caret_column);
}
- if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) {
- ret.x = x_ofs + l_caret.position.x + scroll_offset;
+ if ((caret.l_caret != Rect2() && (caret.l_dir == TextServer::DIRECTION_AUTO || caret.l_dir == (TextServer::Direction)input_direction)) || (caret.t_caret == Rect2())) {
+ ret.x = x_ofs + caret.l_caret.position.x + scroll_offset;
} else {
- ret.x = x_ofs + t_caret.position.x + scroll_offset;
+ ret.x = x_ofs + caret.t_caret.position.x + scroll_offset;
}
// Get position of the end of caret.
if (ime_text.length() != 0) {
if (ime_selection.y != 0) {
- TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x + ime_selection.y, l_caret, l_dir, t_caret, t_dir);
+ caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_selection.x + ime_selection.y);
} else {
- TS->shaped_text_get_carets(text_rid, caret_column + ime_text.size(), l_caret, l_dir, t_caret, t_dir);
+ caret = TS->shaped_text_get_carets(text_rid, caret_column + ime_text.size());
}
- if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) {
- ret.y = x_ofs + l_caret.position.x + scroll_offset;
+ if ((caret.l_caret != Rect2() && (caret.l_dir == TextServer::DIRECTION_AUTO || caret.l_dir == (TextServer::Direction)input_direction)) || (caret.t_caret == Rect2())) {
+ ret.y = x_ofs + caret.l_caret.position.x + scroll_offset;
} else {
- ret.y = x_ofs + t_caret.position.x + scroll_offset;
+ ret.y = x_ofs + caret.t_caret.position.x + scroll_offset;
}
} else {
ret.y = ret.x;
@@ -1502,7 +1493,7 @@ void LineEdit::insert_text_at_caret(String p_text) {
String post = text.substr(caret_column, text.length() - caret_column);
text = pre + p_text + post;
_shape();
- TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text_rid, caret_column, caret_column + p_text.length());
+ TextServer::Direction dir = TS->shaped_text_get_dominant_direction_in_range(text_rid, caret_column, caret_column + p_text.length());
if (dir != TextServer::DIRECTION_AUTO) {
input_direction = (TextDirection)dir;
}
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 277026cc28..4588966d88 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -41,10 +41,6 @@
#include "modules/regex/regex.h"
#endif
-#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
-#endif
-
RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) const {
if (p_free) {
if (p_item->subitems.size()) {
@@ -819,9 +815,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
}
- const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid);
- const TextServer::Glyph *glyphs = visual.ptr();
- int gl_size = visual.size();
+ const Glyph *glyphs = TS->shaped_text_get_glyphs(rid);
+ int gl_size = TS->shaped_text_get_glyph_count(rid);
Vector2 gloff = off;
// Draw oulines and shadow.
@@ -996,19 +991,13 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Color uc = font_color;
uc.a *= 0.5;
float y_off = TS->shaped_text_get_underline_position(rid);
- float underline_width = TS->shaped_text_get_underline_thickness(rid);
-#ifdef TOOLS_ENABLED
- underline_width *= EDSCALE;
-#endif
+ float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width);
} else if (_find_strikethrough(it)) {
Color uc = font_color;
uc.a *= 0.5;
float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
- float underline_width = TS->shaped_text_get_underline_thickness(rid);
-#ifdef TOOLS_ENABLED
- underline_width *= EDSCALE;
-#endif
+ float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width);
}
@@ -1593,18 +1582,18 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
if (c_frame) {
const Line &l = c_frame->lines[c_line];
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(l.text_buf->get_rid());
- for (int i = 0; i < words.size(); i++) {
- if (c_index >= words[i].x && c_index < words[i].y) {
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(l.text_buf->get_rid());
+ for (int i = 0; i < words.size(); i = i + 2) {
+ if (c_index >= words[i] && c_index < words[i + 1]) {
selection.from_frame = c_frame;
selection.from_line = c_line;
selection.from_item = c_item;
- selection.from_char = words[i].x;
+ selection.from_char = words[i];
selection.to_frame = c_frame;
selection.to_line = c_line;
selection.to_item = c_item;
- selection.to_char = words[i].y;
+ selection.to_char = words[i + 1];
selection.active = true;
update();
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index ef34bec347..f305bf7013 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -156,6 +156,7 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) {
if (scrolling_enabled && buttons_visible) {
if (missing_right) {
offset++;
+ _ensure_no_over_offset(); // Avoid overreaching when scrolling fast.
update();
}
}
@@ -164,7 +165,7 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) {
if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (rb_hover != -1) {
//pressed
- emit_signal(SNAME("right_button_pressed"), rb_hover);
+ emit_signal(SNAME("tab_rmb_clicked"), rb_hover);
}
rb_pressing = false;
@@ -401,7 +402,7 @@ void Tabs::_notification(int p_what) {
w += tabs[i].size_text;
if (tabs[i].right_button.is_valid()) {
- Ref<StyleBox> style = get_theme_stylebox(SNAME("button"));
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
Ref<Texture2D> rb = tabs[i].right_button;
w += get_theme_constant(SNAME("hseparation"));
@@ -433,7 +434,7 @@ void Tabs::_notification(int p_what) {
}
if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
- Ref<StyleBox> style = get_theme_stylebox(SNAME("button"));
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
Ref<Texture2D> cb = close;
w += get_theme_constant(SNAME("hseparation"));
@@ -449,7 +450,7 @@ void Tabs::_notification(int p_what) {
if (!tabs[i].disabled && cb_hover == i) {
if (cb_pressing) {
- get_theme_stylebox(SNAME("button_pressed"))->draw(ci, cb_rect);
+ get_theme_stylebox(SNAME("close_bg_pressed"))->draw(ci, cb_rect);
} else {
style->draw(ci, cb_rect);
}
@@ -886,7 +887,7 @@ void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) {
hover_now = get_tab_count() - 1;
}
move_tab(tab_from_id, hover_now);
- emit_signal(SNAME("reposition_active_tab_request"), hover_now);
+ emit_signal(SNAME("active_tab_rearranged"), hover_now);
set_current_tab(hover_now);
} else if (get_tabs_rearrange_group() != -1) {
// drag and drop between Tabs
@@ -1165,10 +1166,10 @@ void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &Tabs::get_select_with_rmb);
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
- ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_rmb_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_closed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab")));
- ADD_SIGNAL(MethodInfo("reposition_active_tab_request", PropertyInfo(Variant::INT, "idx_to")));
+ ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to")));
ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 5b13e1da0b..57bcbb7c2d 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -42,10 +42,6 @@
#include "scene/main/window.h"
-#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
-#endif
-
static bool _is_text_char(char32_t c) {
return !is_symbol(c);
}
@@ -186,7 +182,7 @@ void TextEdit::Text::_calculate_max_line_width() {
max_width = width;
}
-void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Vector<Vector2i> &p_bidi_override) {
+void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Array &p_bidi_override) {
ERR_FAIL_INDEX(p_line, text.size());
if (font.is_null() || font_size <= 0) {
@@ -278,14 +274,14 @@ void TextEdit::Text::invalidate_all() {
void TextEdit::Text::clear() {
text.clear();
- insert(0, "", Vector<Vector2i>());
+ insert(0, "", Array());
}
int TextEdit::Text::get_max_width() const {
return max_width;
}
-void TextEdit::Text::set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override) {
+void TextEdit::Text::set(int p_line, const String &p_text, const Array &p_bidi_override) {
ERR_FAIL_INDEX(p_line, text.size());
text.write[p_line].data = p_text;
@@ -293,7 +289,7 @@ void TextEdit::Text::set(int p_line, const String &p_text, const Vector<Vector2i
invalidate_cache(p_line);
}
-void TextEdit::Text::insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override) {
+void TextEdit::Text::insert(int p_at, const String &p_text, const Array &p_bidi_override) {
Line line;
line.gutters.resize(gutter_count);
line.hidden = false;
@@ -1076,9 +1072,8 @@ void TextEdit::_notification(int p_what) {
ofs_y += (row_height - text_height) / 2;
- const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(rid);
- const TextServer::Glyph *glyphs = visual.ptr();
- int gl_size = visual.size();
+ const Glyph *glyphs = TS->shaped_text_get_glyphs(rid);
+ int gl_size = TS->shaped_text_get_glyph_count(rid);
ofs_y += ldata->get_line_ascent(line_wrap_index);
int char_ofs = 0;
@@ -1174,38 +1169,33 @@ void TextEdit::_notification(int p_what) {
}
}
- // Carets
-#ifdef TOOLS_ENABLED
- int caret_width = Math::round(EDSCALE);
-#else
- int caret_width = 1;
-#endif
+ // Carets.
+ int caret_width = Math::round(1 * get_theme_default_base_scale());
if (!clipped && caret.line == line && line_wrap_index == caret_wrap_index) {
caret.draw_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index);
if (ime_text.length() == 0) {
- Rect2 l_caret, t_caret;
- TextServer::Direction l_dir, t_dir;
+ CaretInfo ts_caret;
if (str.length() != 0) {
// Get carets.
- TS->shaped_text_get_carets(rid, caret.column, l_caret, l_dir, t_caret, t_dir);
+ ts_caret = TS->shaped_text_get_carets(rid, caret.column);
} else {
// No carets, add one at the start.
int h = font->get_height(font_size);
if (rtl) {
- l_dir = TextServer::DIRECTION_RTL;
- l_caret = Rect2(Vector2(xmargin_end - char_margin + ofs_x, -h / 2), Size2(caret_width * 4, h));
+ ts_caret.l_dir = TextServer::DIRECTION_RTL;
+ ts_caret.l_caret = Rect2(Vector2(xmargin_end - char_margin + ofs_x, -h / 2), Size2(caret_width * 4, h));
} else {
- l_dir = TextServer::DIRECTION_LTR;
- l_caret = Rect2(Vector2(char_ofs, -h / 2), Size2(caret_width * 4, h));
+ ts_caret.l_dir = TextServer::DIRECTION_LTR;
+ ts_caret.l_caret = Rect2(Vector2(char_ofs, -h / 2), Size2(caret_width * 4, h));
}
}
- if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) {
- caret.draw_pos.x = char_margin + ofs_x + l_caret.position.x;
+ if ((ts_caret.l_caret != Rect2() && (ts_caret.l_dir == TextServer::DIRECTION_AUTO || ts_caret.l_dir == (TextServer::Direction)input_direction)) || (ts_caret.t_caret == Rect2())) {
+ caret.draw_pos.x = char_margin + ofs_x + ts_caret.l_caret.position.x;
} else {
- caret.draw_pos.x = char_margin + ofs_x + t_caret.position.x;
+ caret.draw_pos.x = char_margin + ofs_x + ts_caret.t_caret.position.x;
}
if (caret.draw_pos.x >= xmargin_beg && caret.draw_pos.x < xmargin_end) {
@@ -1215,64 +1205,64 @@ void TextEdit::_notification(int p_what) {
//Block or underline caret, draw trailing carets at full height.
int h = font->get_height(font_size);
- if (t_caret != Rect2()) {
+ if (ts_caret.t_caret != Rect2()) {
if (overtype_mode) {
- t_caret.position.y = TS->shaped_text_get_descent(rid);
- t_caret.size.y = caret_width;
+ ts_caret.t_caret.position.y = TS->shaped_text_get_descent(rid);
+ ts_caret.t_caret.size.y = caret_width;
} else {
- t_caret.position.y = -TS->shaped_text_get_ascent(rid);
- t_caret.size.y = h;
+ ts_caret.t_caret.position.y = -TS->shaped_text_get_ascent(rid);
+ ts_caret.t_caret.size.y = h;
}
- t_caret.position += Vector2(char_margin + ofs_x, ofs_y);
- draw_rect(t_caret, caret_color, overtype_mode);
+ ts_caret.t_caret.position += Vector2(char_margin + ofs_x, ofs_y);
+ draw_rect(ts_caret.t_caret, caret_color, overtype_mode);
- if (l_caret != Rect2() && l_dir != t_dir) {
- l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
- l_caret.size.x = caret_width;
- draw_rect(l_caret, caret_color * Color(1, 1, 1, 0.5));
+ if (ts_caret.l_caret != Rect2() && ts_caret.l_dir != ts_caret.t_dir) {
+ ts_caret.l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
+ ts_caret.l_caret.size.x = caret_width;
+ draw_rect(ts_caret.l_caret, caret_color * Color(1, 1, 1, 0.5));
}
} else { // End of the line.
if (gl_size > 0) {
// Adjust for actual line dimensions.
if (overtype_mode) {
- l_caret.position.y = TS->shaped_text_get_descent(rid);
- l_caret.size.y = caret_width;
+ ts_caret.l_caret.position.y = TS->shaped_text_get_descent(rid);
+ ts_caret.l_caret.size.y = caret_width;
} else {
- l_caret.position.y = -TS->shaped_text_get_ascent(rid);
- l_caret.size.y = h;
+ ts_caret.l_caret.position.y = -TS->shaped_text_get_ascent(rid);
+ ts_caret.l_caret.size.y = h;
}
} else if (overtype_mode) {
- l_caret.position.y += l_caret.size.y;
- l_caret.size.y = caret_width;
+ ts_caret.l_caret.position.y += ts_caret.l_caret.size.y;
+ ts_caret.l_caret.size.y = caret_width;
}
- if (l_caret.position.x >= TS->shaped_text_get_size(rid).x) {
- l_caret.size.x = font->get_char_size('m', 0, font_size).x;
+ if (ts_caret.l_caret.position.x >= TS->shaped_text_get_size(rid).x) {
+ ts_caret.l_caret.size.x = font->get_char_size('m', 0, font_size).x;
} else {
- l_caret.size.x = 3 * caret_width;
+ ts_caret.l_caret.size.x = 3 * caret_width;
}
- l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
- if (l_dir == TextServer::DIRECTION_RTL) {
- l_caret.position.x -= l_caret.size.x;
+ ts_caret.l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
+ if (ts_caret.l_dir == TextServer::DIRECTION_RTL) {
+ ts_caret.l_caret.position.x -= ts_caret.l_caret.size.x;
}
- draw_rect(l_caret, caret_color, overtype_mode);
+ draw_rect(ts_caret.l_caret, caret_color, overtype_mode);
}
} else {
// Normal caret.
- if (l_caret != Rect2() && l_dir == TextServer::DIRECTION_AUTO) {
+ if (ts_caret.l_caret != Rect2() && ts_caret.l_dir == TextServer::DIRECTION_AUTO) {
// Draw extra marker on top of mid caret.
- Rect2 trect = Rect2(l_caret.position.x - 3 * caret_width, l_caret.position.y, 6 * caret_width, caret_width);
+ Rect2 trect = Rect2(ts_caret.l_caret.position.x - 3 * caret_width, ts_caret.l_caret.position.y, 6 * caret_width, caret_width);
trect.position += Vector2(char_margin + ofs_x, ofs_y);
RenderingServer::get_singleton()->canvas_item_add_rect(ci, trect, caret_color);
}
- l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
- l_caret.size.x = caret_width;
+ ts_caret.l_caret.position += Vector2(char_margin + ofs_x, ofs_y);
+ ts_caret.l_caret.size.x = caret_width;
- draw_rect(l_caret, caret_color);
+ draw_rect(ts_caret.l_caret, caret_color);
- t_caret.position += Vector2(char_margin + ofs_x, ofs_y);
- t_caret.size.x = caret_width;
+ ts_caret.t_caret.position += Vector2(char_margin + ofs_x, ofs_y);
+ ts_caret.t_caret.size.x = caret_width;
- draw_rect(t_caret, caret_color);
+ draw_rect(ts_caret.t_caret, caret_color);
}
}
}
@@ -1636,6 +1626,32 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
}
+
+ // Check if user is hovering a different gutter, and update if yes.
+ Vector2i current_hovered_gutter = Vector2i(-1, -1);
+
+ int left_margin = style_normal->get_margin(SIDE_LEFT);
+ if (mpos.x <= left_margin + gutters_width + gutter_padding) {
+ int hovered_row = get_line_column_at_pos(mpos).y;
+ for (int i = 0; i < gutters.size(); i++) {
+ if (!gutters[i].draw || gutters[i].width <= 0) {
+ continue;
+ }
+
+ if (mpos.x > left_margin && mpos.x <= (left_margin + gutters[i].width) - 3) {
+ // We are in this gutter i's horizontal area.
+ current_hovered_gutter = Vector2i(i, hovered_row);
+ break;
+ }
+
+ left_margin += gutters[i].width;
+ }
+ }
+
+ if (current_hovered_gutter != hovered_gutter) {
+ hovered_gutter = current_hovered_gutter;
+ update();
+ }
}
if (draw_minimap && !dragging_selection) {
@@ -1942,10 +1958,10 @@ void TextEdit::_move_caret_left(bool p_select, bool p_move_by_word) {
set_caret_line(caret.line - 1);
set_caret_column(text[caret.line].length());
} else {
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
- for (int i = words.size() - 1; i >= 0; i--) {
- if (words[i].x < cc) {
- cc = words[i].x;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
+ for (int i = words.size() - 2; i >= 0; i = i - 2) {
+ if (words[i] < cc) {
+ cc = words[i];
break;
}
}
@@ -1993,10 +2009,10 @@ void TextEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
set_caret_line(caret.line + 1);
set_caret_column(0);
} else {
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
- for (int i = 0; i < words.size(); i++) {
- if (words[i].y > cc) {
- cc = words[i].y;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
+ for (int i = 1; i < words.size(); i = i + 2) {
+ if (words[i] > cc) {
+ cc = words[i];
break;
}
}
@@ -2188,10 +2204,10 @@ void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) {
int line = caret.line;
int column = caret.column;
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
- for (int i = words.size() - 1; i >= 0; i--) {
- if (words[i].x < column) {
- column = words[i].x;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
+ for (int i = words.size() - 2; i >= 0; i = i - 2) {
+ if (words[i] < column) {
+ column = words[i];
break;
}
}
@@ -2231,10 +2247,10 @@ void TextEdit::_delete(bool p_word, bool p_all_to_right) {
int line = caret.line;
int column = caret.column;
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
- for (int i = 0; i < words.size(); i++) {
- if (words[i].y > column) {
- column = words[i].y;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
+ for (int i = 1; i < words.size(); i = i + 2) {
+ if (words[i] > column) {
+ column = words[i];
break;
}
}
@@ -2731,7 +2747,10 @@ void TextEdit::insert_line_at(int p_at, const String &p_text) {
}
void TextEdit::insert_text_at_caret(const String &p_text) {
- begin_complex_operation();
+ bool had_selection = has_selection();
+ if (had_selection) {
+ begin_complex_operation();
+ }
delete_selection();
@@ -2743,7 +2762,9 @@ void TextEdit::insert_text_at_caret(const String &p_text) {
set_caret_column(new_column);
update();
- end_complex_operation();
+ if (had_selection) {
+ end_complex_operation();
+ }
}
void TextEdit::remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) {
@@ -3600,10 +3621,10 @@ int TextEdit::get_caret_wrap_index() const {
}
String TextEdit::get_word_under_caret() const {
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
- for (int i = 0; i < words.size(); i++) {
- if (words[i].x <= caret.column && words[i].y > caret.column) {
- return text[caret.line].substr(words[i].x, words[i].y - words[i].x);
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
+ for (int i = 0; i < words.size(); i = i + 2) {
+ if (words[i] <= caret.column && words[i + 1] > caret.column) {
+ return text[caret.line].substr(words[i], words[i + 1] - words[i]);
}
}
return "";
@@ -3687,11 +3708,11 @@ void TextEdit::select_word_under_caret() {
int begin = 0;
int end = 0;
- const Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
- for (int i = 0; i < words.size(); i++) {
- if ((words[i].x < caret.column && words[i].y > caret.column) || (i == words.size() - 1 && caret.column == words[i].y)) {
- begin = words[i].x;
- end = words[i].y;
+ const PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(caret.line)->get_rid());
+ for (int i = 0; i < words.size(); i = i + 2) {
+ if ((words[i] < caret.column && words[i + 1] > caret.column) || (i == words.size() - 2 && caret.column == words[i + 1])) {
+ begin = words[i];
+ end = words[i + 1];
break;
}
}
@@ -5051,10 +5072,12 @@ void TextEdit::_cut_internal() {
}
int cl = get_caret_line();
+ int cc = get_caret_column();
+ int indent_level = get_indent_level(cl);
+ double hscroll = get_h_scroll();
String clipboard = text[cl];
DisplayServer::get_singleton()->clipboard_set(clipboard);
- set_caret_line(cl);
set_caret_column(0);
if (cl == 0 && get_line_count() > 1) {
@@ -5065,6 +5088,17 @@ void TextEdit::_cut_internal() {
set_caret_line(get_caret_line() + 1);
}
+ // Correct the visualy perceived caret column taking care of identation level of the lines.
+ int diff_indent = indent_level - get_indent_level(get_caret_line());
+ cc += diff_indent;
+ if (diff_indent != 0) {
+ cc += diff_indent > 0 ? -1 : 1;
+ }
+
+ // Restore horizontal scroll and caret column modified by the backspace() call.
+ set_h_scroll(hscroll);
+ set_caret_column(cc);
+
cut_copy_line = clipboard;
}
@@ -5345,14 +5379,12 @@ int TextEdit::_get_column_x_offset_for_line(int p_char, int p_line) const {
}
}
- Rect2 l_caret, t_caret;
- TextServer::Direction l_dir, t_dir;
RID text_rid = text.get_line_data(p_line)->get_line_rid(row);
- TS->shaped_text_get_carets(text_rid, caret.column, l_caret, l_dir, t_caret, t_dir);
- if ((l_caret != Rect2() && (l_dir == TextServer::DIRECTION_AUTO || l_dir == (TextServer::Direction)input_direction)) || (t_caret == Rect2())) {
- return l_caret.position.x;
+ CaretInfo ts_caret = TS->shaped_text_get_carets(text_rid, caret.column);
+ if ((ts_caret.l_caret != Rect2() && (ts_caret.l_dir == TextServer::DIRECTION_AUTO || ts_caret.l_dir == (TextServer::Direction)input_direction)) || (ts_caret.t_caret == Rect2())) {
+ return ts_caret.l_caret.position.x;
} else {
- return t_caret.position.x;
+ return ts_caret.t_caret.position.x;
}
}
@@ -5409,11 +5441,11 @@ void TextEdit::_update_selection_mode_word() {
int caret_pos = CLAMP(col, 0, text[line].length());
int beg = caret_pos;
int end = beg;
- Vector<Vector2i> words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
- for (int i = 0; i < words.size(); i++) {
- if ((words[i].x < caret_pos && words[i].y > caret_pos) || (i == words.size() - 1 && caret_pos == words[i].y)) {
- beg = words[i].x;
- end = words[i].y;
+ PackedInt32Array words = TS->shaped_text_get_word_breaks(text.get_line_data(line)->get_rid());
+ for (int i = 0; i < words.size(); i = i + 2) {
+ if ((words[i] < caret_pos && words[i + 1] > caret_pos) || (i == words.size() - 2 && caret_pos == words[i + 1])) {
+ beg = words[i];
+ end = words[i + 1];
break;
}
}
@@ -6001,7 +6033,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
r_end_line = p_line + substrings.size() - 1;
r_end_column = text[r_end_line].length() - postinsert_text.length();
- TextServer::Direction dir = TS->shaped_text_get_dominant_direciton_in_range(text.get_line_data(r_end_line)->get_rid(), (r_end_line == p_line) ? caret.column : 0, r_end_column);
+ TextServer::Direction dir = TS->shaped_text_get_dominant_direction_in_range(text.get_line_data(r_end_line)->get_rid(), (r_end_line == p_line) ? caret.column : 0, r_end_column);
if (dir != TextServer::DIRECTION_AUTO) {
input_direction = (TextDirection)dir;
}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index b1226f2aff..07e585847a 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -139,7 +139,7 @@ private:
Vector<Gutter> gutters;
String data;
- Vector<Vector2i> bidi_override;
+ Array bidi_override;
Ref<TextParagraph> data_buf;
Color background_color = Color(0, 0, 0, 0);
@@ -194,7 +194,7 @@ private:
Vector<Vector2i> get_line_wrap_ranges(int p_line) const;
const Ref<TextParagraph> get_line_data(int p_line) const;
- void set(int p_line, const String &p_text, const Vector<Vector2i> &p_bidi_override);
+ void set(int p_line, const String &p_text, const Array &p_bidi_override);
void set_hidden(int p_line, bool p_hidden) {
text.write[p_line].hidden = p_hidden;
if (!p_hidden && text[p_line].width > max_width) {
@@ -204,12 +204,12 @@ private:
}
}
bool is_hidden(int p_line) const { return text[p_line].hidden; }
- void insert(int p_at, const String &p_text, const Vector<Vector2i> &p_bidi_override);
+ void insert(int p_at, const String &p_text, const Array &p_bidi_override);
void remove(int p_at);
int size() const { return text.size(); }
void clear();
- void invalidate_cache(int p_line, int p_column = -1, const String &p_ime_text = String(), const Vector<Vector2i> &p_bidi_override = Vector<Vector2i>());
+ void invalidate_cache(int p_line, int p_column = -1, const String &p_ime_text = String(), const Array &p_bidi_override = Array());
void invalidate_all();
void invalidate_all_lines();
@@ -477,6 +477,7 @@ private:
Vector<GutterInfo> gutters;
int gutters_width = 0;
int gutter_padding = 0;
+ Vector2i hovered_gutter = Vector2i(-1, -1); // X = gutter index, Y = row.
void _update_gutter_width();
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 7d7596635c..3f041bf65a 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -41,10 +41,6 @@
#include "box_container.h"
-#ifdef TOOLS_ENABLED
-#include "editor/editor_scale.h"
-#endif
-
#include <limits.h>
Size2 TreeItem::Cell::get_icon_size() const {
@@ -1377,6 +1373,8 @@ void Tree::update_cache() {
cache.title_button_hover = get_theme_stylebox(SNAME("title_button_hover"));
cache.title_button_color = get_theme_color(SNAME("title_button_color"));
+ cache.base_scale = get_theme_default_base_scale();
+
v_scroll->set_custom_step(cache.font->get_height(cache.font_size));
}
@@ -2046,15 +2044,9 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
root_pos -= Point2i(cache.arrow->get_width(), 0);
}
- float line_width = cache.relationship_line_width;
- float parent_line_width = cache.parent_hl_line_width;
- float children_line_width = cache.children_hl_line_width;
-
-#ifdef TOOLS_ENABLED
- line_width *= Math::round(EDSCALE);
- parent_line_width *= Math::round(EDSCALE);
- children_line_width *= Math::round(EDSCALE);
-#endif
+ float line_width = cache.relationship_line_width * Math::round(cache.base_scale);
+ float parent_line_width = cache.parent_hl_line_width * Math::round(cache.base_scale);
+ float children_line_width = cache.children_hl_line_width * Math::round(cache.base_scale);
Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs;
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index c4a6b6b058..6ca9458e9b 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -516,6 +516,8 @@ private:
Color custom_button_font_highlight;
Color font_outline_color;
+ float base_scale = 1.0;
+
int hseparation = 0;
int vseparation = 0;
int item_margin = 0;
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 64b169b1fb..916833c9a7 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -647,13 +647,13 @@ void CanvasItem::draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Tex
RenderingServer::get_singleton()->canvas_item_add_multimesh(canvas_item, p_multimesh->get_rid(), texture_rid);
}
-void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
+void CanvasItem::draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND(p_font.is_null());
p_font->draw_string(canvas_item, p_pos, p_text, p_align, p_width, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags);
}
-void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
+void CanvasItem::draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
ERR_FAIL_COND(p_font.is_null());
p_font->draw_multiline_string(canvas_item, p_pos, p_text, p_align, p_width, p_max_lines, p_size, p_modulate, p_outline_size, p_outline_modulate, p_flags);
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index 01ed47d4dc..ba9f47119d 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -235,8 +235,8 @@ public:
void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1));
void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture);
- void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
real_t draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
void draw_set_transform(const Point2 &p_offset, real_t p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0));
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index f5d2d2f2a2..0876c30dd1 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -41,10 +41,6 @@
#include "scene/scene_string_names.h"
#include "viewport.h"
-#ifdef TOOLS_ENABLED
-#include "editor/editor_settings.h"
-#endif
-
#include <stdint.h>
VARIANT_ENUM_CAST(Node::ProcessMode);
@@ -114,8 +110,8 @@ void Node::_notification(int p_notification) {
memdelete(data.path_cache);
data.path_cache = nullptr;
}
- if (data.filename.length()) {
- get_multiplayer()->scene_enter_exit_notify(data.filename, this, false);
+ if (data.scene_file_path.length()) {
+ get_multiplayer()->scene_enter_exit_notify(data.scene_file_path, this, false);
}
} break;
case NOTIFICATION_PATH_CHANGED: {
@@ -146,9 +142,9 @@ void Node::_notification(int p_notification) {
GDVIRTUAL_CALL(_ready);
- if (data.filename.length()) {
+ if (data.scene_file_path.length()) {
ERR_FAIL_COND(!is_inside_tree());
- get_multiplayer()->scene_enter_exit_notify(data.filename, this, true);
+ get_multiplayer()->scene_enter_exit_notify(data.scene_file_path, this, true);
}
} break;
@@ -211,8 +207,8 @@ void Node::_propagate_enter_tree() {
data.inside_tree = true;
- for (Map<StringName, GroupData>::Element *E = data.grouped.front(); E; E = E->next()) {
- E->get().group = data.tree->add_to_group(E->key(), this);
+ for (KeyValue<StringName, GroupData> &E : data.grouped) {
+ E.value.group = data.tree->add_to_group(E.key, this);
}
notification(NOTIFICATION_ENTER_TREE);
@@ -235,7 +231,7 @@ void Node::_propagate_enter_tree() {
data.blocked--;
#ifdef DEBUG_ENABLED
- SceneDebugger::add_to_cache(data.filename, this);
+ SceneDebugger::add_to_cache(data.scene_file_path, this);
#endif
// enter groups
}
@@ -253,7 +249,7 @@ void Node::_propagate_exit_tree() {
//block while removing children
#ifdef DEBUG_ENABLED
- SceneDebugger::remove_from_cache(data.filename, this);
+ SceneDebugger::remove_from_cache(data.scene_file_path, this);
#endif
data.blocked++;
@@ -274,9 +270,9 @@ void Node::_propagate_exit_tree() {
// exit groups
- for (Map<StringName, GroupData>::Element *E = data.grouped.front(); E; E = E->next()) {
- data.tree->remove_from_group(E->key(), this);
- E->get().group = nullptr;
+ for (KeyValue<StringName, GroupData> &E : data.grouped) {
+ data.tree->remove_from_group(E.key, this);
+ E.value.group = nullptr;
}
data.viewport = nullptr;
@@ -353,9 +349,9 @@ void Node::_move_child(Node *p_child, int p_pos, bool p_ignore_end) {
for (int i = motion_from; i <= motion_to; i++) {
data.children[i]->notification(NOTIFICATION_MOVED_IN_PARENT);
}
- for (const Map<StringName, GroupData>::Element *E = p_child->data.grouped.front(); E; E = E->next()) {
- if (E->get().group) {
- E->get().group->changed = true;
+ for (const KeyValue<StringName, GroupData> &E : p_child->data.grouped) {
+ if (E.value.group) {
+ E.value.group->changed = true;
}
}
@@ -1678,10 +1674,10 @@ Array Node::_get_groups() const {
}
void Node::get_groups(List<GroupInfo> *p_groups) const {
- for (const Map<StringName, GroupData>::Element *E = data.grouped.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, GroupData> &E : data.grouped) {
GroupInfo gi;
- gi.name = E->key();
- gi.persistent = E->get().persistent;
+ gi.name = E.key;
+ gi.persistent = E.value.persistent;
p_groups->push_back(gi);
}
}
@@ -1689,8 +1685,8 @@ void Node::get_groups(List<GroupInfo> *p_groups) const {
int Node::get_persistent_group_count() const {
int count = 0;
- for (const Map<StringName, GroupData>::Element *E = data.grouped.front(); E; E = E->next()) {
- if (E->get().persistent) {
+ for (const KeyValue<StringName, GroupData> &E : data.grouped) {
+ if (E.value.persistent) {
count += 1;
}
}
@@ -1846,12 +1842,12 @@ void Node::remove_and_skip() {
data.parent->remove_child(this);
}
-void Node::set_filename(const String &p_filename) {
- data.filename = p_filename;
+void Node::set_scene_file_path(const String &p_scene_file_path) {
+ data.scene_file_path = p_scene_file_path;
}
-String Node::get_filename() const {
- return data.filename;
+String Node::get_scene_file_path() const {
+ return data.scene_file_path;
}
void Node::set_editor_description(const String &p_editor_description) {
@@ -1948,8 +1944,8 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
nip->set_instance_path(ip->get_instance_path());
node = nip;
- } else if ((p_flags & DUPLICATE_USE_INSTANCING) && get_filename() != String()) {
- Ref<PackedScene> res = ResourceLoader::load(get_filename());
+ } else if ((p_flags & DUPLICATE_USE_INSTANCING) && get_scene_file_path() != String()) {
+ Ref<PackedScene> res = ResourceLoader::load(get_scene_file_path());
ERR_FAIL_COND_V(res.is_null(), nullptr);
PackedScene::GenEditState ges = PackedScene::GEN_EDIT_STATE_DISABLED;
#ifdef TOOLS_ENABLED
@@ -1972,8 +1968,8 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
ERR_FAIL_COND_V(!node, nullptr);
}
- if (get_filename() != "") { //an instance
- node->set_filename(get_filename());
+ if (get_scene_file_path() != "") { //an instance
+ node->set_scene_file_path(get_scene_file_path());
node->data.editable_instance = data.editable_instance;
}
@@ -2004,7 +2000,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
node_tree.push_back(descendant);
- if (descendant->get_filename() != "" && instance_roots.has(descendant->get_owner())) {
+ if (descendant->get_scene_file_path() != "" && instance_roots.has(descendant->get_owner())) {
instance_roots.push_back(descendant);
}
}
@@ -2313,7 +2309,7 @@ void Node::replace_by(Node *p_node, bool p_keep_groups) {
owned_by_owner[i]->set_owner(owner);
}
- p_node->set_filename(get_filename());
+ p_node->set_scene_file_path(get_scene_file_path());
}
void Node::_replace_connections_target(Node *p_new_target) {
@@ -2536,17 +2532,11 @@ NodePath Node::get_import_path() const {
}
static void _add_nodes_to_options(const Node *p_base, const Node *p_node, List<String> *r_options) {
-#ifdef TOOLS_ENABLED
- const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
-#else
- const String quote_style = "\"";
-#endif
-
if (p_node != p_base && !p_node->get_owner()) {
return;
}
String n = p_base->get_path_to(p_node);
- r_options->push_back(n.quote(quote_style));
+ r_options->push_back(n.quote());
for (int i = 0; i < p_node->get_child_count(); i++) {
_add_nodes_to_options(p_base, p_node->get_child(i), r_options);
}
@@ -2693,8 +2683,8 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_index", "include_internal"), &Node::get_index, DEFVAL(false));
ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree);
ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty);
- ClassDB::bind_method(D_METHOD("set_filename", "filename"), &Node::set_filename);
- ClassDB::bind_method(D_METHOD("get_filename"), &Node::get_filename);
+ ClassDB::bind_method(D_METHOD("set_scene_file_path", "scene_file_path"), &Node::set_scene_file_path);
+ ClassDB::bind_method(D_METHOD("get_scene_file_path"), &Node::get_scene_file_path);
ClassDB::bind_method(D_METHOD("propagate_notification", "what"), &Node::propagate_notification);
ClassDB::bind_method(D_METHOD("propagate_call", "method", "args", "parent_first"), &Node::propagate_call, DEFVAL(Array()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_physics_process", "enable"), &Node::set_physics_process);
@@ -2839,7 +2829,7 @@ void Node::_bind_methods() {
ADD_SIGNAL(MethodInfo("tree_exited"));
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_name", "get_name");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_filename", "get_filename");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_file_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_scene_file_path", "get_scene_file_path");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_owner", "get_owner");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "", "get_multiplayer");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", PROPERTY_USAGE_NONE), "set_custom_multiplayer", "get_custom_multiplayer");
diff --git a/scene/main/node.h b/scene/main/node.h
index 198501eeac..7d4c79cfba 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -96,7 +96,7 @@ private:
};
struct Data {
- String filename;
+ String scene_file_path;
Ref<SceneState> instance_state;
Ref<SceneState> inherited_state;
@@ -353,8 +353,8 @@ public:
void print_tree();
void print_tree_pretty();
- void set_filename(const String &p_filename);
- String get_filename() const;
+ void set_scene_file_path(const String &p_scene_file_path);
+ String get_scene_file_path() const;
void set_editor_description(const String &p_editor_description);
String get_editor_description() const;
diff --git a/scene/main/resource_preloader.cpp b/scene/main/resource_preloader.cpp
index cd9560db61..f4c90ee668 100644
--- a/scene/main/resource_preloader.cpp
+++ b/scene/main/resource_preloader.cpp
@@ -57,8 +57,8 @@ Array ResourcePreloader::_get_resources() const {
Set<String> sorted_names;
- for (Map<StringName, RES>::Element *E = resources.front(); E; E = E->next()) {
- sorted_names.insert(E->key());
+ for (const KeyValue<StringName, RES> &E : resources) {
+ sorted_names.insert(E.key);
}
int i = 0;
@@ -131,8 +131,8 @@ Vector<String> ResourcePreloader::_get_resource_list() const {
}
void ResourcePreloader::get_resource_list(List<StringName> *p_list) {
- for (Map<StringName, RES>::Element *E = resources.front(); E; E = E->next()) {
- p_list->push_back(E->key());
+ for (const KeyValue<StringName, RES> &E : resources) {
+ p_list->push_back(E.key);
}
}
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 6e78193717..3d07e4473d 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1105,7 +1105,7 @@ Error SceneTree::change_scene_to(const Ref<PackedScene> &p_scene) {
Error SceneTree::reload_current_scene() {
ERR_FAIL_COND_V(!current_scene, ERR_UNCONFIGURED);
- String fname = current_scene->get_filename();
+ String fname = current_scene->get_scene_file_path();
return change_scene(fname);
}
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index ca5a3915d0..a0f62c853f 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -1266,6 +1266,18 @@ bool Window::has_theme_constant(const StringName &p_name, const StringName &p_th
return Control::has_theme_item_in_types(theme_owner, theme_owner_window, Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
}
+float Window::get_theme_default_base_scale() const {
+ return Control::fetch_theme_default_base_scale(theme_owner, theme_owner_window);
+}
+
+Ref<Font> Window::get_theme_default_font() const {
+ return Control::fetch_theme_default_font(theme_owner, theme_owner_window);
+}
+
+int Window::get_theme_default_font_size() const {
+ return Control::fetch_theme_default_font_size(theme_owner, theme_owner_window);
+}
+
Rect2i Window::get_parent_rect() const {
ERR_FAIL_COND_V(!is_inside_tree(), Rect2i());
if (is_embedded()) {
@@ -1480,6 +1492,10 @@ void Window::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_theme_color", "name", "theme_type"), &Window::has_theme_color, DEFVAL(""));
ClassDB::bind_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Window::has_theme_constant, DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("get_theme_default_base_scale"), &Window::get_theme_default_base_scale);
+ ClassDB::bind_method(D_METHOD("get_theme_default_font"), &Window::get_theme_default_font);
+ ClassDB::bind_method(D_METHOD("get_theme_default_font_size"), &Window::get_theme_default_font_size);
+
ClassDB::bind_method(D_METHOD("set_layout_direction", "direction"), &Window::set_layout_direction);
ClassDB::bind_method(D_METHOD("get_layout_direction"), &Window::get_layout_direction);
ClassDB::bind_method(D_METHOD("is_layout_rtl"), &Window::is_layout_rtl);
diff --git a/scene/main/window.h b/scene/main/window.h
index 4f31d9cd1f..def6eab7b8 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -280,6 +280,10 @@ public:
bool has_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
bool has_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
+ float get_theme_default_base_scale() const;
+ Ref<Font> get_theme_default_font() const;
+ int get_theme_default_font_size() const;
+
Rect2i get_parent_rect() const;
virtual DisplayServer::WindowID get_window_id() const override;
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 1e89b17044..bf8f7291be 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -46,7 +46,7 @@
#include "scene/2d/collision_shape_2d.h"
#include "scene/2d/cpu_particles_2d.h"
#include "scene/2d/gpu_particles_2d.h"
-#include "scene/2d/joints_2d.h"
+#include "scene/2d/joint_2d.h"
#include "scene/2d/light_2d.h"
#include "scene/2d/light_occluder_2d.h"
#include "scene/2d/line_2d.h"
@@ -215,6 +215,8 @@
#include "scene/3d/decal.h"
#include "scene/3d/gpu_particles_3d.h"
#include "scene/3d/gpu_particles_collision_3d.h"
+#include "scene/3d/importer_mesh_instance_3d.h"
+#include "scene/3d/joint_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/lightmap_gi.h"
#include "scene/3d/lightmap_probe.h"
@@ -227,7 +229,6 @@
#include "scene/3d/occluder_instance_3d.h"
#include "scene/3d/path_3d.h"
#include "scene/3d/physics_body_3d.h"
-#include "scene/3d/physics_joint_3d.h"
#include "scene/3d/position_3d.h"
#include "scene/3d/proximity_group_3d.h"
#include "scene/3d/ray_cast_3d.h"
@@ -244,6 +245,7 @@
#include "scene/3d/world_environment.h"
#include "scene/3d/xr_nodes.h"
#include "scene/resources/environment.h"
+#include "scene/resources/importer_mesh.h"
#include "scene/resources/mesh_library.h"
#endif
@@ -441,10 +443,11 @@ void register_scene_types() {
GDREGISTER_CLASS(Skin);
GDREGISTER_VIRTUAL_CLASS(SkinReference);
GDREGISTER_CLASS(Skeleton3D);
+ GDREGISTER_CLASS(ImporterMesh);
+ GDREGISTER_CLASS(ImporterMeshInstance3D);
GDREGISTER_VIRTUAL_CLASS(VisualInstance3D);
GDREGISTER_VIRTUAL_CLASS(GeometryInstance3D);
GDREGISTER_CLASS(Camera3D);
- GDREGISTER_CLASS(ClippedCamera3D);
GDREGISTER_CLASS(AudioListener3D);
GDREGISTER_CLASS(XRCamera3D);
GDREGISTER_CLASS(XRController3D);
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index a364a27e80..9dc76dcf44 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -966,9 +966,9 @@ PackedVector2Array Curve2D::tessellate(int p_max_stages, float p_tolerance) cons
int pidx = 0;
for (int i = 0; i < points.size() - 1; i++) {
- for (Map<float, Vector2>::Element *E = midpoints[i].front(); E; E = E->next()) {
+ for (const KeyValue<float, Vector2> &E : midpoints[i]) {
pidx++;
- bpw[pidx] = E->get();
+ bpw[pidx] = E.value;
}
pidx++;
@@ -1652,9 +1652,9 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, float p_tolerance) cons
int pidx = 0;
for (int i = 0; i < points.size() - 1; i++) {
- for (Map<float, Vector3>::Element *E = midpoints[i].front(); E; E = E->next()) {
+ for (const KeyValue<float, Vector3> &E : midpoints[i]) {
pidx++;
- bpw[pidx] = E->get();
+ bpw[pidx] = E.value;
}
pidx++;
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 492e0512e4..54bb7a82cf 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -790,8 +790,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_stylebox("tab_selected", "Tabs", sb_expand(make_stylebox(tab_current_png, 4, 3, 4, 1, 16, 3, 16, 2), 2, 2, 2, 2));
theme->set_stylebox("tab_unselected", "Tabs", sb_expand(make_stylebox(tab_behind_png, 5, 4, 5, 1, 16, 5, 16, 2), 3, 3, 3, 3));
theme->set_stylebox("tab_disabled", "Tabs", sb_expand(make_stylebox(tab_disabled_png, 5, 5, 5, 1, 16, 6, 16, 4), 3, 0, 3, 3));
- theme->set_stylebox("button_pressed", "Tabs", make_stylebox(button_pressed_png, 4, 4, 4, 4));
- theme->set_stylebox("button", "Tabs", make_stylebox(button_normal_png, 4, 4, 4, 4));
+ theme->set_stylebox("close_bg_pressed", "Tabs", make_stylebox(button_pressed_png, 4, 4, 4, 4));
+ theme->set_stylebox("close_bg_highlight", "Tabs", make_stylebox(button_normal_png, 4, 4, 4, 4));
theme->set_icon("increment", "Tabs", make_icon(scroll_button_right_png));
theme->set_icon("increment_highlight", "Tabs", make_icon(scroll_button_right_hl_png));
@@ -1036,9 +1036,16 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
}
Ref<Font> large_font = default_font;
- fill_default_theme(t, default_font, large_font, default_icon, default_style, p_hidpi ? 2.0 : 1.0);
+
+ float default_scale = 1.0;
+ if (p_hidpi) {
+ default_scale = 2.0;
+ }
+
+ fill_default_theme(t, default_font, large_font, default_icon, default_style, default_scale);
Theme::set_default(t);
+ Theme::set_default_base_scale(default_scale);
Theme::set_default_icon(default_icon);
Theme::set_default_style(default_style);
Theme::set_default_font(default_font);
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 9b403a18f0..c1d42dff09 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -502,6 +502,11 @@ void FontData::set_data(const PackedByteArray &p_data) {
}
PackedByteArray FontData::get_data() const {
+ if (unlikely((size_t)data.size() != data_size)) {
+ PackedByteArray *data_w = const_cast<PackedByteArray *>(&data);
+ data_w->resize(data_size);
+ memcpy(data_w->ptrw(), data_ptr, data_size);
+ }
return data;
}
@@ -1399,7 +1404,7 @@ real_t Font::get_underline_thickness(int p_size) const {
return ret;
}
-Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint8_t p_flags) const {
+Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint16_t p_flags) const {
ERR_FAIL_COND_V(data.is_empty(), Size2());
int size = (p_size <= 0) ? base_size : p_size;
@@ -1426,7 +1431,7 @@ Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, re
return buffer->get_size();
}
-Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint8_t p_flags) const {
+Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint16_t p_flags) const {
ERR_FAIL_COND_V(data.is_empty(), Size2());
int size = (p_size <= 0) ? base_size : p_size;
@@ -1465,7 +1470,7 @@ Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int
return ret;
}
-void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
+void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
ERR_FAIL_COND(data.is_empty());
int size = (p_size <= 0) ? base_size : p_size;
@@ -1507,7 +1512,7 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
buffer->draw(p_canvas_item, ofs, p_modulate);
}
-void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint8_t p_flags) const {
+void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
ERR_FAIL_COND(data.is_empty());
int size = (p_size <= 0) ? base_size : p_size;
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 9a34edce64..e65fdb0751 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -266,11 +266,11 @@ public:
virtual real_t get_underline_thickness(int p_size = -1) const;
// Drawing string.
- virtual Size2 get_string_size(const String &p_text, int p_size = -1, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = -1, uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
+ virtual Size2 get_string_size(const String &p_text, int p_size = -1, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = -1, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
- virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
// Helper functions.
virtual bool has_char(char32_t p_char) const;
diff --git a/scene/resources/importer_mesh.cpp b/scene/resources/importer_mesh.cpp
new file mode 100644
index 0000000000..af69b799cc
--- /dev/null
+++ b/scene/resources/importer_mesh.cpp
@@ -0,0 +1,1242 @@
+/*************************************************************************/
+/* importer_mesh.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "importer_mesh.h"
+
+#include "core/math/random_pcg.h"
+#include "core/math/static_raycaster.h"
+#include "scene/resources/surface_tool.h"
+
+#include <cstdint>
+
+void ImporterMesh::Surface::split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) {
+ ERR_FAIL_COND(arrays.size() != RS::ARRAY_MAX);
+
+ const PackedVector3Array &vertices = arrays[RS::ARRAY_VERTEX];
+ int current_vertex_count = vertices.size();
+ int new_vertex_count = p_indices.size();
+ int final_vertex_count = current_vertex_count + new_vertex_count;
+ const int *indices_ptr = p_indices.ptr();
+
+ for (int i = 0; i < arrays.size(); i++) {
+ if (i == RS::ARRAY_INDEX) {
+ continue;
+ }
+
+ if (arrays[i].get_type() == Variant::NIL) {
+ continue;
+ }
+
+ switch (arrays[i].get_type()) {
+ case Variant::PACKED_VECTOR3_ARRAY: {
+ PackedVector3Array data = arrays[i];
+ data.resize(final_vertex_count);
+ Vector3 *data_ptr = data.ptrw();
+ if (i == RS::ARRAY_NORMAL) {
+ const Vector3 *normals_ptr = p_normals.ptr();
+ memcpy(&data_ptr[current_vertex_count], normals_ptr, sizeof(Vector3) * new_vertex_count);
+ } else {
+ for (int j = 0; j < new_vertex_count; j++) {
+ data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
+ }
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_VECTOR2_ARRAY: {
+ PackedVector2Array data = arrays[i];
+ data.resize(final_vertex_count);
+ Vector2 *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+ PackedFloat32Array data = arrays[i];
+ int elements = data.size() / current_vertex_count;
+ data.resize(final_vertex_count * elements);
+ float *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(float) * elements);
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ PackedInt32Array data = arrays[i];
+ int elements = data.size() / current_vertex_count;
+ data.resize(final_vertex_count * elements);
+ int32_t *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(int32_t) * elements);
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ PackedByteArray data = arrays[i];
+ int elements = data.size() / current_vertex_count;
+ data.resize(final_vertex_count * elements);
+ uint8_t *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ memcpy(&data_ptr[(current_vertex_count + j) * elements], &data_ptr[indices_ptr[j] * elements], sizeof(uint8_t) * elements);
+ }
+ arrays[i] = data;
+ } break;
+ case Variant::PACKED_COLOR_ARRAY: {
+ PackedColorArray data = arrays[i];
+ data.resize(final_vertex_count);
+ Color *data_ptr = data.ptrw();
+ for (int j = 0; j < new_vertex_count; j++) {
+ data_ptr[current_vertex_count + j] = data_ptr[indices_ptr[j]];
+ }
+ arrays[i] = data;
+ } break;
+ default: {
+ ERR_FAIL_MSG("Unhandled array type.");
+ } break;
+ }
+ }
+}
+
+void ImporterMesh::add_blend_shape(const String &p_name) {
+ ERR_FAIL_COND(surfaces.size() > 0);
+ blend_shapes.push_back(p_name);
+}
+
+int ImporterMesh::get_blend_shape_count() const {
+ return blend_shapes.size();
+}
+
+String ImporterMesh::get_blend_shape_name(int p_blend_shape) const {
+ ERR_FAIL_INDEX_V(p_blend_shape, blend_shapes.size(), String());
+ return blend_shapes[p_blend_shape];
+}
+
+void ImporterMesh::set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode) {
+ blend_shape_mode = p_blend_shape_mode;
+}
+
+Mesh::BlendShapeMode ImporterMesh::get_blend_shape_mode() const {
+ return blend_shape_mode;
+}
+
+void ImporterMesh::add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes, const Dictionary &p_lods, const Ref<Material> &p_material, const String &p_name, const uint32_t p_flags) {
+ ERR_FAIL_COND(p_blend_shapes.size() != blend_shapes.size());
+ ERR_FAIL_COND(p_arrays.size() != Mesh::ARRAY_MAX);
+ Surface s;
+ s.primitive = p_primitive;
+ s.arrays = p_arrays;
+ s.name = p_name;
+ s.flags = p_flags;
+
+ Vector<Vector3> vertex_array = p_arrays[Mesh::ARRAY_VERTEX];
+ int vertex_count = vertex_array.size();
+ ERR_FAIL_COND(vertex_count == 0);
+
+ for (int i = 0; i < blend_shapes.size(); i++) {
+ Array bsdata = p_blend_shapes[i];
+ ERR_FAIL_COND(bsdata.size() != Mesh::ARRAY_MAX);
+ Vector<Vector3> vertex_data = bsdata[Mesh::ARRAY_VERTEX];
+ ERR_FAIL_COND(vertex_data.size() != vertex_count);
+ Surface::BlendShape bs;
+ bs.arrays = bsdata;
+ s.blend_shape_data.push_back(bs);
+ }
+
+ List<Variant> lods;
+ p_lods.get_key_list(&lods);
+ for (const Variant &E : lods) {
+ ERR_CONTINUE(!E.is_num());
+ Surface::LOD lod;
+ lod.distance = E;
+ lod.indices = p_lods[E];
+ ERR_CONTINUE(lod.indices.size() == 0);
+ s.lods.push_back(lod);
+ }
+
+ s.material = p_material;
+
+ surfaces.push_back(s);
+ mesh.unref();
+}
+
+int ImporterMesh::get_surface_count() const {
+ return surfaces.size();
+}
+
+Mesh::PrimitiveType ImporterMesh::get_surface_primitive_type(int p_surface) {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Mesh::PRIMITIVE_MAX);
+ return surfaces[p_surface].primitive;
+}
+Array ImporterMesh::get_surface_arrays(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Array());
+ return surfaces[p_surface].arrays;
+}
+String ImporterMesh::get_surface_name(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), String());
+ return surfaces[p_surface].name;
+}
+void ImporterMesh::set_surface_name(int p_surface, const String &p_name) {
+ ERR_FAIL_INDEX(p_surface, surfaces.size());
+ surfaces.write[p_surface].name = p_name;
+ mesh.unref();
+}
+
+Array ImporterMesh::get_surface_blend_shape_arrays(int p_surface, int p_blend_shape) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Array());
+ ERR_FAIL_INDEX_V(p_blend_shape, surfaces[p_surface].blend_shape_data.size(), Array());
+ return surfaces[p_surface].blend_shape_data[p_blend_shape].arrays;
+}
+int ImporterMesh::get_surface_lod_count(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0);
+ return surfaces[p_surface].lods.size();
+}
+Vector<int> ImporterMesh::get_surface_lod_indices(int p_surface, int p_lod) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Vector<int>());
+ ERR_FAIL_INDEX_V(p_lod, surfaces[p_surface].lods.size(), Vector<int>());
+
+ return surfaces[p_surface].lods[p_lod].indices;
+}
+
+float ImporterMesh::get_surface_lod_size(int p_surface, int p_lod) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0);
+ ERR_FAIL_INDEX_V(p_lod, surfaces[p_surface].lods.size(), 0);
+ return surfaces[p_surface].lods[p_lod].distance;
+}
+
+uint32_t ImporterMesh::get_surface_format(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), 0);
+ return surfaces[p_surface].flags;
+}
+
+Ref<Material> ImporterMesh::get_surface_material(int p_surface) const {
+ ERR_FAIL_INDEX_V(p_surface, surfaces.size(), Ref<Material>());
+ return surfaces[p_surface].material;
+}
+
+void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_material) {
+ ERR_FAIL_INDEX(p_surface, surfaces.size());
+ surfaces.write[p_surface].material = p_material;
+ mesh.unref();
+}
+
+void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_split_angle) {
+ if (!SurfaceTool::simplify_scale_func) {
+ return;
+ }
+ if (!SurfaceTool::simplify_with_attrib_func) {
+ return;
+ }
+ if (!SurfaceTool::optimize_vertex_cache_func) {
+ return;
+ }
+
+ for (int i = 0; i < surfaces.size(); i++) {
+ if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) {
+ continue;
+ }
+ if (get_blend_shape_count()) {
+ continue;
+ }
+
+ surfaces.write[i].lods.clear();
+ Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
+ PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX];
+ Vector<Vector3> normals = surfaces[i].arrays[RS::ARRAY_NORMAL];
+ Vector<Vector2> uvs = surfaces[i].arrays[RS::ARRAY_TEX_UV];
+
+ unsigned int index_count = indices.size();
+ unsigned int vertex_count = vertices.size();
+
+ if (index_count == 0) {
+ continue; //no lods if no indices
+ }
+
+ const Vector3 *vertices_ptr = vertices.ptr();
+ const int *indices_ptr = indices.ptr();
+
+ if (normals.is_empty()) {
+ normals.resize(vertices.size());
+ Vector3 *n_ptr = normals.ptrw();
+ for (unsigned int j = 0; j < index_count; j += 3) {
+ const Vector3 &v0 = vertices_ptr[indices_ptr[j + 0]];
+ const Vector3 &v1 = vertices_ptr[indices_ptr[j + 1]];
+ const Vector3 &v2 = vertices_ptr[indices_ptr[j + 2]];
+ Vector3 n = vec3_cross(v0 - v2, v0 - v1).normalized();
+ n_ptr[j + 0] = n;
+ n_ptr[j + 1] = n;
+ n_ptr[j + 2] = n;
+ }
+ }
+
+ float normal_merge_threshold = Math::cos(Math::deg2rad(p_normal_merge_angle));
+ float normal_pre_split_threshold = Math::cos(Math::deg2rad(MIN(180.0f, p_normal_split_angle * 2.0f)));
+ float normal_split_threshold = Math::cos(Math::deg2rad(p_normal_split_angle));
+ const Vector3 *normals_ptr = normals.ptr();
+
+ Map<Vector3, LocalVector<Pair<int, int>>> unique_vertices;
+
+ LocalVector<int> vertex_remap;
+ LocalVector<int> vertex_inverse_remap;
+ LocalVector<Vector3> merged_vertices;
+ LocalVector<Vector3> merged_normals;
+ LocalVector<int> merged_normals_counts;
+ const Vector2 *uvs_ptr = uvs.ptr();
+
+ for (unsigned int j = 0; j < vertex_count; j++) {
+ const Vector3 &v = vertices_ptr[j];
+ const Vector3 &n = normals_ptr[j];
+
+ Map<Vector3, LocalVector<Pair<int, int>>>::Element *E = unique_vertices.find(v);
+
+ if (E) {
+ const LocalVector<Pair<int, int>> &close_verts = E->get();
+
+ bool found = false;
+ for (unsigned int k = 0; k < close_verts.size(); k++) {
+ const Pair<int, int> &idx = close_verts[k];
+
+ // TODO check more attributes?
+ if ((!uvs_ptr || uvs_ptr[j].distance_squared_to(uvs_ptr[idx.second]) < CMP_EPSILON2) && normals[idx.second].dot(n) > normal_merge_threshold) {
+ vertex_remap.push_back(idx.first);
+ merged_normals[idx.first] += normals[idx.second];
+ merged_normals_counts[idx.first]++;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ int vcount = merged_vertices.size();
+ unique_vertices[v].push_back(Pair<int, int>(vcount, j));
+ vertex_inverse_remap.push_back(j);
+ merged_vertices.push_back(v);
+ vertex_remap.push_back(vcount);
+ merged_normals.push_back(normals_ptr[j]);
+ merged_normals_counts.push_back(1);
+ }
+ } else {
+ int vcount = merged_vertices.size();
+ unique_vertices[v] = LocalVector<Pair<int, int>>();
+ unique_vertices[v].push_back(Pair<int, int>(vcount, j));
+ vertex_inverse_remap.push_back(j);
+ merged_vertices.push_back(v);
+ vertex_remap.push_back(vcount);
+ merged_normals.push_back(normals_ptr[j]);
+ merged_normals_counts.push_back(1);
+ }
+ }
+
+ LocalVector<int> merged_indices;
+ merged_indices.resize(index_count);
+ for (unsigned int j = 0; j < index_count; j++) {
+ merged_indices[j] = vertex_remap[indices[j]];
+ }
+
+ unsigned int merged_vertex_count = merged_vertices.size();
+ const Vector3 *merged_vertices_ptr = merged_vertices.ptr();
+ const int32_t *merged_indices_ptr = merged_indices.ptr();
+
+ {
+ const int *counts_ptr = merged_normals_counts.ptr();
+ Vector3 *merged_normals_ptrw = merged_normals.ptr();
+ for (unsigned int j = 0; j < merged_vertex_count; j++) {
+ merged_normals_ptrw[j] /= counts_ptr[j];
+ }
+ }
+
+ LocalVector<float> normal_weights;
+ normal_weights.resize(merged_vertex_count);
+ for (unsigned int j = 0; j < merged_vertex_count; j++) {
+ normal_weights[j] = 2.0; // Give some weight to normal preservation, may be worth exposing as an import setting
+ }
+
+ const float max_mesh_error = FLT_MAX; // We don't want to limit by error, just by index target
+ float scale = SurfaceTool::simplify_scale_func((const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3));
+ float mesh_error = 0.0f;
+
+ unsigned int index_target = 12; // Start with the smallest target, 4 triangles
+ unsigned int last_index_count = 0;
+
+ int split_vertex_count = vertex_count;
+ LocalVector<Vector3> split_vertex_normals;
+ LocalVector<int> split_vertex_indices;
+ split_vertex_normals.reserve(index_count / 3);
+ split_vertex_indices.reserve(index_count / 3);
+
+ RandomPCG pcg;
+ pcg.seed(123456789); // Keep seed constant across imports
+
+ Ref<StaticRaycaster> raycaster = StaticRaycaster::create();
+ if (raycaster.is_valid()) {
+ raycaster->add_mesh(vertices, indices, 0);
+ raycaster->commit();
+ }
+
+ while (index_target < index_count) {
+ PackedInt32Array new_indices;
+ new_indices.resize(index_count);
+
+ size_t new_index_count = SurfaceTool::simplify_with_attrib_func((unsigned int *)new_indices.ptrw(), (const uint32_t *)merged_indices_ptr, index_count, (const float *)merged_vertices_ptr, merged_vertex_count, sizeof(Vector3), index_target, max_mesh_error, &mesh_error, (float *)merged_normals.ptr(), normal_weights.ptr(), 3);
+
+ if (new_index_count < last_index_count * 1.5f) {
+ index_target = index_target * 1.5f;
+ continue;
+ }
+
+ if (new_index_count <= 0 || (new_index_count >= (index_count * 0.75f))) {
+ break;
+ }
+
+ new_indices.resize(new_index_count);
+
+ LocalVector<LocalVector<int>> vertex_corners;
+ vertex_corners.resize(vertex_count);
+ {
+ int *ptrw = new_indices.ptrw();
+ for (unsigned int j = 0; j < new_index_count; j++) {
+ const int &remapped = vertex_inverse_remap[ptrw[j]];
+ vertex_corners[remapped].push_back(j);
+ ptrw[j] = remapped;
+ }
+ }
+
+ if (raycaster.is_valid()) {
+ float error_factor = 1.0f / (scale * MAX(mesh_error, 0.15));
+ const float ray_bias = 0.05;
+ float ray_length = ray_bias + mesh_error * scale * 3.0f;
+
+ Vector<StaticRaycaster::Ray> rays;
+ LocalVector<Vector2> ray_uvs;
+
+ int32_t *new_indices_ptr = new_indices.ptrw();
+
+ int current_ray_count = 0;
+ for (unsigned int j = 0; j < new_index_count; j += 3) {
+ const Vector3 &v0 = vertices_ptr[new_indices_ptr[j + 0]];
+ const Vector3 &v1 = vertices_ptr[new_indices_ptr[j + 1]];
+ const Vector3 &v2 = vertices_ptr[new_indices_ptr[j + 2]];
+ Vector3 face_normal = vec3_cross(v0 - v2, v0 - v1);
+ float face_area = face_normal.length(); // Actually twice the face area, since it's the same error_factor on all faces, we don't care
+
+ Vector3 dir = face_normal / face_area;
+ int ray_count = CLAMP(5.0 * face_area * error_factor, 16, 64);
+
+ rays.resize(current_ray_count + ray_count);
+ StaticRaycaster::Ray *rays_ptr = rays.ptrw();
+
+ ray_uvs.resize(current_ray_count + ray_count);
+ Vector2 *ray_uvs_ptr = ray_uvs.ptr();
+
+ for (int k = 0; k < ray_count; k++) {
+ float u = pcg.randf();
+ float v = pcg.randf();
+
+ if (u + v >= 1.0f) {
+ u = 1.0f - u;
+ v = 1.0f - v;
+ }
+
+ u = 0.9f * u + 0.05f / 3.0f; // Give barycentric coordinates some padding, we don't want to sample right on the edge
+ v = 0.9f * v + 0.05f / 3.0f; // v = (v - one_third) * 0.95f + one_third;
+ float w = 1.0f - u - v;
+
+ Vector3 org = v0 * w + v1 * u + v2 * v;
+ org -= dir * ray_bias;
+ rays_ptr[current_ray_count + k] = StaticRaycaster::Ray(org, dir, 0.0f, ray_length);
+ rays_ptr[current_ray_count + k].id = j / 3;
+ ray_uvs_ptr[current_ray_count + k] = Vector2(u, v);
+ }
+
+ current_ray_count += ray_count;
+ }
+
+ raycaster->intersect(rays);
+
+ LocalVector<Vector3> ray_normals;
+ LocalVector<float> ray_normal_weights;
+
+ ray_normals.resize(new_index_count);
+ ray_normal_weights.resize(new_index_count);
+
+ for (unsigned int j = 0; j < new_index_count; j++) {
+ ray_normal_weights[j] = 0.0f;
+ }
+
+ const StaticRaycaster::Ray *rp = rays.ptr();
+ for (int j = 0; j < rays.size(); j++) {
+ if (rp[j].geomID != 0) { // Ray missed
+ continue;
+ }
+
+ if (rp[j].normal.normalized().dot(rp[j].dir) > 0.0f) { // Hit a back face.
+ continue;
+ }
+
+ const float &u = rp[j].u;
+ const float &v = rp[j].v;
+ const float w = 1.0f - u - v;
+
+ const unsigned int &hit_tri_id = rp[j].primID;
+ const unsigned int &orig_tri_id = rp[j].id;
+
+ const Vector3 &n0 = normals_ptr[indices_ptr[hit_tri_id * 3 + 0]];
+ const Vector3 &n1 = normals_ptr[indices_ptr[hit_tri_id * 3 + 1]];
+ const Vector3 &n2 = normals_ptr[indices_ptr[hit_tri_id * 3 + 2]];
+ Vector3 normal = n0 * w + n1 * u + n2 * v;
+
+ Vector2 orig_uv = ray_uvs[j];
+ float orig_bary[3] = { 1.0f - orig_uv.x - orig_uv.y, orig_uv.x, orig_uv.y };
+ for (int k = 0; k < 3; k++) {
+ int idx = orig_tri_id * 3 + k;
+ float weight = orig_bary[k];
+ ray_normals[idx] += normal * weight;
+ ray_normal_weights[idx] += weight;
+ }
+ }
+
+ for (unsigned int j = 0; j < new_index_count; j++) {
+ if (ray_normal_weights[j] < 1.0f) { // Not enough data, the new normal would be just a bad guess
+ ray_normals[j] = Vector3();
+ } else {
+ ray_normals[j] /= ray_normal_weights[j];
+ }
+ }
+
+ LocalVector<LocalVector<int>> normal_group_indices;
+ LocalVector<Vector3> normal_group_averages;
+ normal_group_indices.reserve(24);
+ normal_group_averages.reserve(24);
+
+ for (unsigned int j = 0; j < vertex_count; j++) {
+ const LocalVector<int> &corners = vertex_corners[j];
+ const Vector3 &vertex_normal = normals_ptr[j];
+
+ for (unsigned int k = 0; k < corners.size(); k++) {
+ const int &corner_idx = corners[k];
+ const Vector3 &ray_normal = ray_normals[corner_idx];
+
+ if (ray_normal.length_squared() < CMP_EPSILON2) {
+ continue;
+ }
+
+ bool found = false;
+ for (unsigned int l = 0; l < normal_group_indices.size(); l++) {
+ LocalVector<int> &group_indices = normal_group_indices[l];
+ Vector3 n = normal_group_averages[l] / group_indices.size();
+ if (n.dot(ray_normal) > normal_pre_split_threshold) {
+ found = true;
+ group_indices.push_back(corner_idx);
+ normal_group_averages[l] += ray_normal;
+ break;
+ }
+ }
+
+ if (!found) {
+ LocalVector<int> new_group;
+ new_group.push_back(corner_idx);
+ normal_group_indices.push_back(new_group);
+ normal_group_averages.push_back(ray_normal);
+ }
+ }
+
+ for (unsigned int k = 0; k < normal_group_indices.size(); k++) {
+ LocalVector<int> &group_indices = normal_group_indices[k];
+ Vector3 n = normal_group_averages[k] / group_indices.size();
+
+ if (vertex_normal.dot(n) < normal_split_threshold) {
+ split_vertex_indices.push_back(j);
+ split_vertex_normals.push_back(n);
+ int new_idx = split_vertex_count++;
+ for (unsigned int l = 0; l < group_indices.size(); l++) {
+ new_indices_ptr[group_indices[l]] = new_idx;
+ }
+ }
+ }
+
+ normal_group_indices.clear();
+ normal_group_averages.clear();
+ }
+ }
+
+ Surface::LOD lod;
+ lod.distance = MAX(mesh_error * scale, CMP_EPSILON2);
+ lod.indices = new_indices;
+ surfaces.write[i].lods.push_back(lod);
+ index_target = MAX(new_index_count, index_target) * 2;
+ last_index_count = new_index_count;
+
+ if (mesh_error == 0.0f) {
+ break;
+ }
+ }
+
+ surfaces.write[i].split_normals(split_vertex_indices, split_vertex_normals);
+ surfaces.write[i].lods.sort_custom<Surface::LODComparator>();
+
+ for (int j = 0; j < surfaces.write[i].lods.size(); j++) {
+ Surface::LOD &lod = surfaces.write[i].lods.write[j];
+ unsigned int *lod_indices_ptr = (unsigned int *)lod.indices.ptrw();
+ SurfaceTool::optimize_vertex_cache_func(lod_indices_ptr, lod_indices_ptr, lod.indices.size(), split_vertex_count);
+ }
+ }
+}
+
+bool ImporterMesh::has_mesh() const {
+ return mesh.is_valid();
+}
+
+Ref<ArrayMesh> ImporterMesh::get_mesh(const Ref<ArrayMesh> &p_base) {
+ ERR_FAIL_COND_V(surfaces.size() == 0, Ref<ArrayMesh>());
+
+ if (mesh.is_null()) {
+ if (p_base.is_valid()) {
+ mesh = p_base;
+ }
+ if (mesh.is_null()) {
+ mesh.instantiate();
+ }
+ mesh->set_name(get_name());
+ if (has_meta("import_id")) {
+ mesh->set_meta("import_id", get_meta("import_id"));
+ }
+ for (int i = 0; i < blend_shapes.size(); i++) {
+ mesh->add_blend_shape(blend_shapes[i]);
+ }
+ mesh->set_blend_shape_mode(blend_shape_mode);
+ for (int i = 0; i < surfaces.size(); i++) {
+ Array bs_data;
+ if (surfaces[i].blend_shape_data.size()) {
+ for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) {
+ bs_data.push_back(surfaces[i].blend_shape_data[j].arrays);
+ }
+ }
+ Dictionary lods;
+ if (surfaces[i].lods.size()) {
+ for (int j = 0; j < surfaces[i].lods.size(); j++) {
+ lods[surfaces[i].lods[j].distance] = surfaces[i].lods[j].indices;
+ }
+ }
+
+ mesh->add_surface_from_arrays(surfaces[i].primitive, surfaces[i].arrays, bs_data, lods, surfaces[i].flags);
+ if (surfaces[i].material.is_valid()) {
+ mesh->surface_set_material(mesh->get_surface_count() - 1, surfaces[i].material);
+ }
+ if (surfaces[i].name != String()) {
+ mesh->surface_set_name(mesh->get_surface_count() - 1, surfaces[i].name);
+ }
+ }
+
+ mesh->set_lightmap_size_hint(lightmap_size_hint);
+
+ if (shadow_mesh.is_valid()) {
+ Ref<ArrayMesh> shadow = shadow_mesh->get_mesh();
+ mesh->set_shadow_mesh(shadow);
+ }
+ }
+
+ return mesh;
+}
+
+void ImporterMesh::clear() {
+ surfaces.clear();
+ blend_shapes.clear();
+ mesh.unref();
+}
+
+void ImporterMesh::create_shadow_mesh() {
+ if (shadow_mesh.is_valid()) {
+ shadow_mesh.unref();
+ }
+
+ //no shadow mesh for blendshapes
+ if (blend_shapes.size() > 0) {
+ return;
+ }
+ //no shadow mesh for skeletons
+ for (int i = 0; i < surfaces.size(); i++) {
+ if (surfaces[i].arrays[RS::ARRAY_BONES].get_type() != Variant::NIL) {
+ return;
+ }
+ if (surfaces[i].arrays[RS::ARRAY_WEIGHTS].get_type() != Variant::NIL) {
+ return;
+ }
+ }
+
+ shadow_mesh.instantiate();
+
+ for (int i = 0; i < surfaces.size(); i++) {
+ LocalVector<int> vertex_remap;
+ Vector<Vector3> new_vertices;
+ Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
+ int vertex_count = vertices.size();
+ {
+ Map<Vector3, int> unique_vertices;
+ const Vector3 *vptr = vertices.ptr();
+ for (int j = 0; j < vertex_count; j++) {
+ const Vector3 &v = vptr[j];
+
+ Map<Vector3, int>::Element *E = unique_vertices.find(v);
+
+ if (E) {
+ vertex_remap.push_back(E->get());
+ } else {
+ int vcount = unique_vertices.size();
+ unique_vertices[v] = vcount;
+ vertex_remap.push_back(vcount);
+ new_vertices.push_back(v);
+ }
+ }
+ }
+
+ Array new_surface;
+ new_surface.resize(RS::ARRAY_MAX);
+ Dictionary lods;
+
+ // print_line("original vertex count: " + itos(vertices.size()) + " new vertex count: " + itos(new_vertices.size()));
+
+ new_surface[RS::ARRAY_VERTEX] = new_vertices;
+
+ Vector<int> indices = surfaces[i].arrays[RS::ARRAY_INDEX];
+ if (indices.size()) {
+ int index_count = indices.size();
+ const int *index_rptr = indices.ptr();
+ Vector<int> new_indices;
+ new_indices.resize(indices.size());
+ int *index_wptr = new_indices.ptrw();
+
+ for (int j = 0; j < index_count; j++) {
+ int index = index_rptr[j];
+ ERR_FAIL_INDEX(index, vertex_count);
+ index_wptr[j] = vertex_remap[index];
+ }
+
+ new_surface[RS::ARRAY_INDEX] = new_indices;
+
+ // Make sure the same LODs as the full version are used.
+ // This makes it more coherent between rendered model and its shadows.
+ for (int j = 0; j < surfaces[i].lods.size(); j++) {
+ indices = surfaces[i].lods[j].indices;
+
+ index_count = indices.size();
+ index_rptr = indices.ptr();
+ new_indices.resize(indices.size());
+ index_wptr = new_indices.ptrw();
+
+ for (int k = 0; k < index_count; k++) {
+ int index = index_rptr[k];
+ ERR_FAIL_INDEX(index, vertex_count);
+ index_wptr[k] = vertex_remap[index];
+ }
+
+ lods[surfaces[i].lods[j].distance] = new_indices;
+ }
+ }
+
+ shadow_mesh->add_surface(surfaces[i].primitive, new_surface, Array(), lods, Ref<Material>(), surfaces[i].name, surfaces[i].flags);
+ }
+}
+
+Ref<ImporterMesh> ImporterMesh::get_shadow_mesh() const {
+ return shadow_mesh;
+}
+
+void ImporterMesh::_set_data(const Dictionary &p_data) {
+ clear();
+ if (p_data.has("blend_shape_names")) {
+ blend_shapes = p_data["blend_shape_names"];
+ }
+ if (p_data.has("surfaces")) {
+ Array surface_arr = p_data["surfaces"];
+ for (int i = 0; i < surface_arr.size(); i++) {
+ Dictionary s = surface_arr[i];
+ ERR_CONTINUE(!s.has("primitive"));
+ ERR_CONTINUE(!s.has("arrays"));
+ Mesh::PrimitiveType prim = Mesh::PrimitiveType(int(s["primitive"]));
+ ERR_CONTINUE(prim >= Mesh::PRIMITIVE_MAX);
+ Array arr = s["arrays"];
+ Dictionary lods;
+ String name;
+ if (s.has("name")) {
+ name = s["name"];
+ }
+ if (s.has("lods")) {
+ lods = s["lods"];
+ }
+ Array b_shapes;
+ if (s.has("b_shapes")) {
+ b_shapes = s["b_shapes"];
+ }
+ Ref<Material> material;
+ if (s.has("material")) {
+ material = s["material"];
+ }
+ uint32_t flags = 0;
+ if (s.has("flags")) {
+ flags = s["flags"];
+ }
+ add_surface(prim, arr, b_shapes, lods, material, name, flags);
+ }
+ }
+}
+Dictionary ImporterMesh::_get_data() const {
+ Dictionary data;
+ if (blend_shapes.size()) {
+ data["blend_shape_names"] = blend_shapes;
+ }
+ Array surface_arr;
+ for (int i = 0; i < surfaces.size(); i++) {
+ Dictionary d;
+ d["primitive"] = surfaces[i].primitive;
+ d["arrays"] = surfaces[i].arrays;
+ if (surfaces[i].blend_shape_data.size()) {
+ Array bs_data;
+ for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) {
+ bs_data.push_back(surfaces[i].blend_shape_data[j].arrays);
+ }
+ d["blend_shapes"] = bs_data;
+ }
+ if (surfaces[i].lods.size()) {
+ Dictionary lods;
+ for (int j = 0; j < surfaces[i].lods.size(); j++) {
+ lods[surfaces[i].lods[j].distance] = surfaces[i].lods[j].indices;
+ }
+ d["lods"] = lods;
+ }
+
+ if (surfaces[i].material.is_valid()) {
+ d["material"] = surfaces[i].material;
+ }
+
+ if (surfaces[i].name != String()) {
+ d["name"] = surfaces[i].name;
+ }
+
+ if (surfaces[i].flags != 0) {
+ d["flags"] = surfaces[i].flags;
+ }
+
+ surface_arr.push_back(d);
+ }
+ data["surfaces"] = surface_arr;
+ return data;
+}
+
+Vector<Face3> ImporterMesh::get_faces() const {
+ Vector<Face3> faces;
+ for (int i = 0; i < surfaces.size(); i++) {
+ if (surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) {
+ Vector<Vector3> vertices = surfaces[i].arrays[Mesh::ARRAY_VERTEX];
+ Vector<int> indices = surfaces[i].arrays[Mesh::ARRAY_INDEX];
+ if (indices.size()) {
+ for (int j = 0; j < indices.size(); j += 3) {
+ Face3 f;
+ f.vertex[0] = vertices[indices[j + 0]];
+ f.vertex[1] = vertices[indices[j + 1]];
+ f.vertex[2] = vertices[indices[j + 2]];
+ faces.push_back(f);
+ }
+ } else {
+ for (int j = 0; j < vertices.size(); j += 3) {
+ Face3 f;
+ f.vertex[0] = vertices[j + 0];
+ f.vertex[1] = vertices[j + 1];
+ f.vertex[2] = vertices[j + 2];
+ faces.push_back(f);
+ }
+ }
+ }
+ }
+
+ return faces;
+}
+
+Vector<Ref<Shape3D>> ImporterMesh::convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const {
+ ERR_FAIL_COND_V(!Mesh::convex_decomposition_function, Vector<Ref<Shape3D>>());
+
+ const Vector<Face3> faces = get_faces();
+ int face_count = faces.size();
+
+ Vector<Vector3> vertices;
+ uint32_t vertex_count = 0;
+ vertices.resize(face_count * 3);
+ Vector<uint32_t> indices;
+ indices.resize(face_count * 3);
+ {
+ Map<Vector3, uint32_t> vertex_map;
+ Vector3 *vertex_w = vertices.ptrw();
+ uint32_t *index_w = indices.ptrw();
+ for (int i = 0; i < face_count; i++) {
+ for (int j = 0; j < 3; j++) {
+ const Vector3 &vertex = faces[i].vertex[j];
+ Map<Vector3, uint32_t>::Element *found_vertex = vertex_map.find(vertex);
+ uint32_t index;
+ if (found_vertex) {
+ index = found_vertex->get();
+ } else {
+ index = ++vertex_count;
+ vertex_map[vertex] = index;
+ vertex_w[index] = vertex;
+ }
+ index_w[i * 3 + j] = index;
+ }
+ }
+ }
+ vertices.resize(vertex_count);
+
+ Vector<Vector<Vector3>> decomposed = Mesh::convex_decomposition_function((real_t *)vertices.ptr(), vertex_count, indices.ptr(), face_count, p_settings, nullptr);
+
+ Vector<Ref<Shape3D>> ret;
+
+ for (int i = 0; i < decomposed.size(); i++) {
+ Ref<ConvexPolygonShape3D> shape;
+ shape.instantiate();
+ shape->set_points(decomposed[i]);
+ ret.push_back(shape);
+ }
+
+ return ret;
+}
+
+Ref<Shape3D> ImporterMesh::create_trimesh_shape() const {
+ Vector<Face3> faces = get_faces();
+ if (faces.size() == 0) {
+ return Ref<Shape3D>();
+ }
+
+ Vector<Vector3> face_points;
+ face_points.resize(faces.size() * 3);
+
+ for (int i = 0; i < face_points.size(); i += 3) {
+ Face3 f = faces.get(i / 3);
+ face_points.set(i, f.vertex[0]);
+ face_points.set(i + 1, f.vertex[1]);
+ face_points.set(i + 2, f.vertex[2]);
+ }
+
+ Ref<ConcavePolygonShape3D> shape = memnew(ConcavePolygonShape3D);
+ shape->set_faces(face_points);
+ return shape;
+}
+
+Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() {
+ Vector<Face3> faces = get_faces();
+ if (faces.size() == 0) {
+ return Ref<NavigationMesh>();
+ }
+
+ Map<Vector3, int> unique_vertices;
+ LocalVector<int> face_indices;
+
+ for (int i = 0; i < faces.size(); i++) {
+ for (int j = 0; j < 3; j++) {
+ Vector3 v = faces[i].vertex[j];
+ int idx;
+ if (unique_vertices.has(v)) {
+ idx = unique_vertices[v];
+ } else {
+ idx = unique_vertices.size();
+ unique_vertices[v] = idx;
+ }
+ face_indices.push_back(idx);
+ }
+ }
+
+ Vector<Vector3> vertices;
+ vertices.resize(unique_vertices.size());
+ for (const KeyValue<Vector3, int> &E : unique_vertices) {
+ vertices.write[E.value] = E.key;
+ }
+
+ Ref<NavigationMesh> nm;
+ nm.instantiate();
+ nm->set_vertices(vertices);
+
+ Vector<int> v3;
+ v3.resize(3);
+ for (uint32_t i = 0; i < face_indices.size(); i += 3) {
+ v3.write[0] = face_indices[i + 0];
+ v3.write[1] = face_indices[i + 1];
+ v3.write[2] = face_indices[i + 2];
+ nm->add_polygon(v3);
+ }
+
+ return nm;
+}
+
+extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y);
+
+struct EditorSceneImporterMeshLightmapSurface {
+ Ref<Material> material;
+ LocalVector<SurfaceTool::Vertex> vertices;
+ Mesh::PrimitiveType primitive = Mesh::PrimitiveType::PRIMITIVE_MAX;
+ uint32_t format = 0;
+ String name;
+};
+
+Error ImporterMesh::lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache) {
+ ERR_FAIL_COND_V(!array_mesh_lightmap_unwrap_callback, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V_MSG(blend_shapes.size() != 0, ERR_UNAVAILABLE, "Can't unwrap mesh with blend shapes.");
+
+ LocalVector<float> vertices;
+ LocalVector<float> normals;
+ LocalVector<int> indices;
+ LocalVector<float> uv;
+ LocalVector<Pair<int, int>> uv_indices;
+
+ Vector<EditorSceneImporterMeshLightmapSurface> lightmap_surfaces;
+
+ // Keep only the scale
+ Basis basis = p_base_transform.get_basis();
+ Vector3 scale = Vector3(basis.get_axis(0).length(), basis.get_axis(1).length(), basis.get_axis(2).length());
+
+ Transform3D transform;
+ transform.scale(scale);
+
+ Basis normal_basis = transform.basis.inverse().transposed();
+
+ for (int i = 0; i < get_surface_count(); i++) {
+ EditorSceneImporterMeshLightmapSurface s;
+ s.primitive = get_surface_primitive_type(i);
+
+ ERR_FAIL_COND_V_MSG(s.primitive != Mesh::PRIMITIVE_TRIANGLES, ERR_UNAVAILABLE, "Only triangles are supported for lightmap unwrap.");
+ Array arrays = get_surface_arrays(i);
+ s.material = get_surface_material(i);
+ s.name = get_surface_name(i);
+
+ SurfaceTool::create_vertex_array_from_triangle_arrays(arrays, s.vertices, &s.format);
+
+ PackedVector3Array rvertices = arrays[Mesh::ARRAY_VERTEX];
+ int vc = rvertices.size();
+
+ PackedVector3Array rnormals = arrays[Mesh::ARRAY_NORMAL];
+
+ int vertex_ofs = vertices.size() / 3;
+
+ vertices.resize((vertex_ofs + vc) * 3);
+ normals.resize((vertex_ofs + vc) * 3);
+ uv_indices.resize(vertex_ofs + vc);
+
+ for (int j = 0; j < vc; j++) {
+ Vector3 v = transform.xform(rvertices[j]);
+ Vector3 n = normal_basis.xform(rnormals[j]).normalized();
+
+ vertices[(j + vertex_ofs) * 3 + 0] = v.x;
+ vertices[(j + vertex_ofs) * 3 + 1] = v.y;
+ vertices[(j + vertex_ofs) * 3 + 2] = v.z;
+ normals[(j + vertex_ofs) * 3 + 0] = n.x;
+ normals[(j + vertex_ofs) * 3 + 1] = n.y;
+ normals[(j + vertex_ofs) * 3 + 2] = n.z;
+ uv_indices[j + vertex_ofs] = Pair<int, int>(i, j);
+ }
+
+ PackedInt32Array rindices = arrays[Mesh::ARRAY_INDEX];
+ int ic = rindices.size();
+
+ float eps = 1.19209290e-7F; // Taken from xatlas.h
+ if (ic == 0) {
+ for (int j = 0; j < vc / 3; j++) {
+ Vector3 p0 = transform.xform(rvertices[j * 3 + 0]);
+ Vector3 p1 = transform.xform(rvertices[j * 3 + 1]);
+ Vector3 p2 = transform.xform(rvertices[j * 3 + 2]);
+
+ if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
+ continue;
+ }
+
+ indices.push_back(vertex_ofs + j * 3 + 0);
+ indices.push_back(vertex_ofs + j * 3 + 1);
+ indices.push_back(vertex_ofs + j * 3 + 2);
+ }
+
+ } else {
+ for (int j = 0; j < ic / 3; j++) {
+ Vector3 p0 = transform.xform(rvertices[rindices[j * 3 + 0]]);
+ Vector3 p1 = transform.xform(rvertices[rindices[j * 3 + 1]]);
+ Vector3 p2 = transform.xform(rvertices[rindices[j * 3 + 2]]);
+
+ if ((p0 - p1).length_squared() < eps || (p1 - p2).length_squared() < eps || (p2 - p0).length_squared() < eps) {
+ continue;
+ }
+
+ indices.push_back(vertex_ofs + rindices[j * 3 + 0]);
+ indices.push_back(vertex_ofs + rindices[j * 3 + 1]);
+ indices.push_back(vertex_ofs + rindices[j * 3 + 2]);
+ }
+ }
+
+ lightmap_surfaces.push_back(s);
+ }
+
+ //unwrap
+
+ bool use_cache = true; // Used to request cache generation and to know if cache was used
+ uint8_t *gen_cache;
+ int gen_cache_size;
+ float *gen_uvs;
+ int *gen_vertices;
+ int *gen_indices;
+ int gen_vertex_count;
+ int gen_index_count;
+ int size_x;
+ int size_y;
+
+ bool ok = array_mesh_lightmap_unwrap_callback(p_texel_size, vertices.ptr(), normals.ptr(), vertices.size() / 3, indices.ptr(), indices.size(), p_src_cache.ptr(), &use_cache, &gen_cache, &gen_cache_size, &gen_uvs, &gen_vertices, &gen_vertex_count, &gen_indices, &gen_index_count, &size_x, &size_y);
+
+ if (!ok) {
+ return ERR_CANT_CREATE;
+ }
+
+ //remove surfaces
+ clear();
+
+ //create surfacetools for each surface..
+ LocalVector<Ref<SurfaceTool>> surfaces_tools;
+
+ for (int i = 0; i < lightmap_surfaces.size(); i++) {
+ Ref<SurfaceTool> st;
+ st.instantiate();
+ st->begin(Mesh::PRIMITIVE_TRIANGLES);
+ st->set_material(lightmap_surfaces[i].material);
+ st->set_meta("name", lightmap_surfaces[i].name);
+ surfaces_tools.push_back(st); //stay there
+ }
+
+ print_verbose("Mesh: Gen indices: " + itos(gen_index_count));
+
+ //go through all indices
+ for (int i = 0; i < gen_index_count; i += 3) {
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 0]], (int)uv_indices.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 1]], (int)uv_indices.size(), ERR_BUG);
+ ERR_FAIL_INDEX_V(gen_vertices[gen_indices[i + 2]], (int)uv_indices.size(), ERR_BUG);
+
+ ERR_FAIL_COND_V(uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 1]]].first || uv_indices[gen_vertices[gen_indices[i + 0]]].first != uv_indices[gen_vertices[gen_indices[i + 2]]].first, ERR_BUG);
+
+ int surface = uv_indices[gen_vertices[gen_indices[i + 0]]].first;
+
+ for (int j = 0; j < 3; j++) {
+ SurfaceTool::Vertex v = lightmap_surfaces[surface].vertices[uv_indices[gen_vertices[gen_indices[i + j]]].second];
+
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_COLOR) {
+ surfaces_tools[surface]->set_color(v.color);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TEX_UV) {
+ surfaces_tools[surface]->set_uv(v.uv);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_NORMAL) {
+ surfaces_tools[surface]->set_normal(v.normal);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_TANGENT) {
+ Plane t;
+ t.normal = v.tangent;
+ t.d = v.binormal.dot(v.normal.cross(v.tangent)) < 0 ? -1 : 1;
+ surfaces_tools[surface]->set_tangent(t);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_BONES) {
+ surfaces_tools[surface]->set_bones(v.bones);
+ }
+ if (lightmap_surfaces[surface].format & Mesh::ARRAY_FORMAT_WEIGHTS) {
+ surfaces_tools[surface]->set_weights(v.weights);
+ }
+
+ Vector2 uv2(gen_uvs[gen_indices[i + j] * 2 + 0], gen_uvs[gen_indices[i + j] * 2 + 1]);
+ surfaces_tools[surface]->set_uv2(uv2);
+
+ surfaces_tools[surface]->add_vertex(v.vertex);
+ }
+ }
+
+ //generate surfaces
+ for (unsigned int i = 0; i < surfaces_tools.size(); i++) {
+ surfaces_tools[i]->index();
+ Array arrays = surfaces_tools[i]->commit_to_arrays();
+ add_surface(surfaces_tools[i]->get_primitive(), arrays, Array(), Dictionary(), surfaces_tools[i]->get_material(), surfaces_tools[i]->get_meta("name"));
+ }
+
+ set_lightmap_size_hint(Size2(size_x, size_y));
+
+ if (gen_cache_size > 0) {
+ r_dst_cache.resize(gen_cache_size);
+ memcpy(r_dst_cache.ptrw(), gen_cache, gen_cache_size);
+ memfree(gen_cache);
+ }
+
+ if (!use_cache) {
+ // Cache was not used, free the buffers
+ memfree(gen_vertices);
+ memfree(gen_indices);
+ memfree(gen_uvs);
+ }
+
+ return OK;
+}
+
+void ImporterMesh::set_lightmap_size_hint(const Size2i &p_size) {
+ lightmap_size_hint = p_size;
+}
+
+Size2i ImporterMesh::get_lightmap_size_hint() const {
+ return lightmap_size_hint;
+}
+
+void ImporterMesh::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_blend_shape", "name"), &ImporterMesh::add_blend_shape);
+ ClassDB::bind_method(D_METHOD("get_blend_shape_count"), &ImporterMesh::get_blend_shape_count);
+ ClassDB::bind_method(D_METHOD("get_blend_shape_name", "blend_shape_idx"), &ImporterMesh::get_blend_shape_name);
+
+ ClassDB::bind_method(D_METHOD("set_blend_shape_mode", "mode"), &ImporterMesh::set_blend_shape_mode);
+ ClassDB::bind_method(D_METHOD("get_blend_shape_mode"), &ImporterMesh::get_blend_shape_mode);
+
+ ClassDB::bind_method(D_METHOD("add_surface", "primitive", "arrays", "blend_shapes", "lods", "material", "name", "flags"), &ImporterMesh::add_surface, DEFVAL(Array()), DEFVAL(Dictionary()), DEFVAL(Ref<Material>()), DEFVAL(String()), DEFVAL(0));
+
+ ClassDB::bind_method(D_METHOD("get_surface_count"), &ImporterMesh::get_surface_count);
+ ClassDB::bind_method(D_METHOD("get_surface_primitive_type", "surface_idx"), &ImporterMesh::get_surface_primitive_type);
+ ClassDB::bind_method(D_METHOD("get_surface_name", "surface_idx"), &ImporterMesh::get_surface_name);
+ ClassDB::bind_method(D_METHOD("get_surface_arrays", "surface_idx"), &ImporterMesh::get_surface_arrays);
+ ClassDB::bind_method(D_METHOD("get_surface_blend_shape_arrays", "surface_idx", "blend_shape_idx"), &ImporterMesh::get_surface_blend_shape_arrays);
+ ClassDB::bind_method(D_METHOD("get_surface_lod_count", "surface_idx"), &ImporterMesh::get_surface_lod_count);
+ ClassDB::bind_method(D_METHOD("get_surface_lod_size", "surface_idx", "lod_idx"), &ImporterMesh::get_surface_lod_size);
+ ClassDB::bind_method(D_METHOD("get_surface_lod_indices", "surface_idx", "lod_idx"), &ImporterMesh::get_surface_lod_indices);
+ ClassDB::bind_method(D_METHOD("get_surface_material", "surface_idx"), &ImporterMesh::get_surface_material);
+ ClassDB::bind_method(D_METHOD("get_surface_format", "surface_idx"), &ImporterMesh::get_surface_format);
+
+ ClassDB::bind_method(D_METHOD("set_surface_name", "surface_idx", "name"), &ImporterMesh::set_surface_name);
+ ClassDB::bind_method(D_METHOD("set_surface_material", "surface_idx", "material"), &ImporterMesh::set_surface_material);
+
+ ClassDB::bind_method(D_METHOD("get_mesh", "base_mesh"), &ImporterMesh::get_mesh, DEFVAL(Ref<ArrayMesh>()));
+ ClassDB::bind_method(D_METHOD("clear"), &ImporterMesh::clear);
+
+ ClassDB::bind_method(D_METHOD("_set_data", "data"), &ImporterMesh::_set_data);
+ ClassDB::bind_method(D_METHOD("_get_data"), &ImporterMesh::_get_data);
+
+ ClassDB::bind_method(D_METHOD("set_lightmap_size_hint", "size"), &ImporterMesh::set_lightmap_size_hint);
+ ClassDB::bind_method(D_METHOD("get_lightmap_size_hint"), &ImporterMesh::get_lightmap_size_hint);
+
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_data", "_get_data");
+}
diff --git a/scene/resources/importer_mesh.h b/scene/resources/importer_mesh.h
new file mode 100644
index 0000000000..89909f17f0
--- /dev/null
+++ b/scene/resources/importer_mesh.h
@@ -0,0 +1,132 @@
+/*************************************************************************/
+/* importer_mesh.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef SCENE_IMPORTER_MESH_H
+#define SCENE_IMPORTER_MESH_H
+
+#include "core/io/resource.h"
+#include "core/templates/local_vector.h"
+#include "scene/resources/concave_polygon_shape_3d.h"
+#include "scene/resources/convex_polygon_shape_3d.h"
+#include "scene/resources/mesh.h"
+#include "scene/resources/navigation_mesh.h"
+
+#include <cstdint>
+
+// The following classes are used by importers instead of ArrayMesh and MeshInstance3D
+// so the data is not registered (hence, quality loss), importing happens faster and
+// its easier to modify before saving
+
+class ImporterMesh : public Resource {
+ GDCLASS(ImporterMesh, Resource)
+
+ struct Surface {
+ Mesh::PrimitiveType primitive;
+ Array arrays;
+ struct BlendShape {
+ Array arrays;
+ };
+ Vector<BlendShape> blend_shape_data;
+ struct LOD {
+ Vector<int> indices;
+ float distance = 0.0f;
+ };
+ Vector<LOD> lods;
+ Ref<Material> material;
+ String name;
+ uint32_t flags = 0;
+
+ struct LODComparator {
+ _FORCE_INLINE_ bool operator()(const LOD &l, const LOD &r) const {
+ return l.distance < r.distance;
+ }
+ };
+
+ void split_normals(const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals);
+ };
+ Vector<Surface> surfaces;
+ Vector<String> blend_shapes;
+ Mesh::BlendShapeMode blend_shape_mode = Mesh::BLEND_SHAPE_MODE_NORMALIZED;
+
+ Ref<ArrayMesh> mesh;
+
+ Ref<ImporterMesh> shadow_mesh;
+
+ Size2i lightmap_size_hint;
+
+protected:
+ void _set_data(const Dictionary &p_data);
+ Dictionary _get_data() const;
+
+ static void _bind_methods();
+
+public:
+ void add_blend_shape(const String &p_name);
+ int get_blend_shape_count() const;
+ String get_blend_shape_name(int p_blend_shape) const;
+
+ void add_surface(Mesh::PrimitiveType p_primitive, const Array &p_arrays, const Array &p_blend_shapes = Array(), const Dictionary &p_lods = Dictionary(), const Ref<Material> &p_material = Ref<Material>(), const String &p_name = String(), const uint32_t p_flags = 0);
+ int get_surface_count() const;
+
+ void set_blend_shape_mode(Mesh::BlendShapeMode p_blend_shape_mode);
+ Mesh::BlendShapeMode get_blend_shape_mode() const;
+
+ Mesh::PrimitiveType get_surface_primitive_type(int p_surface);
+ String get_surface_name(int p_surface) const;
+ void set_surface_name(int p_surface, const String &p_name);
+ Array get_surface_arrays(int p_surface) const;
+ Array get_surface_blend_shape_arrays(int p_surface, int p_blend_shape) const;
+ int get_surface_lod_count(int p_surface) const;
+ Vector<int> get_surface_lod_indices(int p_surface, int p_lod) const;
+ float get_surface_lod_size(int p_surface, int p_lod) const;
+ Ref<Material> get_surface_material(int p_surface) const;
+ uint32_t get_surface_format(int p_surface) const;
+
+ void set_surface_material(int p_surface, const Ref<Material> &p_material);
+
+ void generate_lods(float p_normal_merge_angle, float p_normal_split_angle);
+
+ void create_shadow_mesh();
+ Ref<ImporterMesh> get_shadow_mesh() const;
+
+ Vector<Face3> get_faces() const;
+ Vector<Ref<Shape3D>> convex_decompose(const Mesh::ConvexDecompositionSettings &p_settings) const;
+ Ref<Shape3D> create_trimesh_shape() const;
+ Ref<NavigationMesh> create_navigation_mesh();
+ Error lightmap_unwrap_cached(const Transform3D &p_base_transform, float p_texel_size, const Vector<uint8_t> &p_src_cache, Vector<uint8_t> &r_dst_cache);
+
+ void set_lightmap_size_hint(const Size2i &p_size);
+ Size2i get_lightmap_size_hint() const;
+
+ bool has_mesh() const;
+ Ref<ArrayMesh> get_mesh(const Ref<ArrayMesh> &p_base = Ref<ArrayMesh>());
+ void clear();
+};
+#endif // SCENE_IMPORTER_MESH_H
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 3a6af3afb0..66f04f0292 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -32,11 +32,6 @@
#include "core/config/engine.h"
#include "core/version.h"
-
-#ifdef TOOLS_ENABLED
-#include "editor/editor_settings.h"
-#endif
-
#include "scene/main/scene_tree.h"
#include "scene/scene_string_names.h"
@@ -80,6 +75,9 @@ void Material::_validate_property(PropertyInfo &property) const {
if (!_can_do_next_pass() && property.name == "next_pass") {
property.usage = PROPERTY_USAGE_NONE;
}
+ if (!_can_use_render_priority() && property.name == "render_priority") {
+ property.usage = PROPERTY_USAGE_NONE;
+ }
}
void Material::inspect_native_shader_code() {
@@ -268,19 +266,13 @@ void ShaderMaterial::_bind_methods() {
}
void ShaderMaterial::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
-#ifdef TOOLS_ENABLED
- const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
-#else
- const String quote_style = "\"";
-#endif
-
String f = p_function.operator String();
if ((f == "get_shader_param" || f == "set_shader_param") && p_idx == 0) {
if (shader.is_valid()) {
List<PropertyInfo> pl;
shader->get_param_list(&pl);
for (const PropertyInfo &E : pl) {
- r_options->push_back(E.name.replace_first("shader_param/", "").quote(quote_style));
+ r_options->push_back(E.name.replace_first("shader_param/", "").quote());
}
}
}
@@ -291,6 +283,10 @@ bool ShaderMaterial::_can_do_next_pass() const {
return shader.is_valid() && shader->get_mode() == Shader::MODE_SPATIAL;
}
+bool ShaderMaterial::_can_use_render_priority() const {
+ return shader.is_valid() && shader->get_mode() == Shader::MODE_SPATIAL;
+}
+
Shader::Mode ShaderMaterial::get_shader_mode() const {
if (shader.is_valid()) {
return shader->get_mode();
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 5d7a5324ca..798f7568df 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -53,6 +53,7 @@ protected:
_FORCE_INLINE_ RID _get_material() const { return material; }
static void _bind_methods();
virtual bool _can_do_next_pass() const { return false; }
+ virtual bool _can_use_render_priority() const { return false; }
void _validate_property(PropertyInfo &property) const override;
@@ -93,6 +94,7 @@ protected:
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
virtual bool _can_do_next_pass() const override;
+ virtual bool _can_use_render_priority() const override;
void _shader_changed();
@@ -535,6 +537,7 @@ protected:
static void _bind_methods();
void _validate_property(PropertyInfo &property) const override;
virtual bool _can_do_next_pass() const override { return true; }
+ virtual bool _can_use_render_priority() const override { return true; }
public:
void set_albedo(const Color &p_albedo);
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 67db8c1a10..18e6a51118 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -373,8 +373,8 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
//normalize
- for (Map<Vector3, Vector3>::Element *E = normal_accum.front(); E; E = E->next()) {
- E->get().normalize();
+ for (KeyValue<Vector3, Vector3> &E : normal_accum) {
+ E.value.normalize();
}
//displace normals
diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp
index cfb7c3e037..309670e0b1 100644
--- a/scene/resources/mesh_library.cpp
+++ b/scene/resources/mesh_library.cpp
@@ -97,8 +97,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const {
}
void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
- for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) {
- String name = "item/" + itos(E->key()) + "/";
+ for (const KeyValue<int, Item> &E : item_map) {
+ String name = "item/" + itos(E.key) + "/";
p_list->push_back(PropertyInfo(Variant::STRING, name + "name"));
p_list->push_back(PropertyInfo(Variant::OBJECT, name + "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"));
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, name + "mesh_transform"));
@@ -230,17 +230,17 @@ Vector<int> MeshLibrary::get_item_list() const {
Vector<int> ret;
ret.resize(item_map.size());
int idx = 0;
- for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) {
- ret.write[idx++] = E->key();
+ for (const KeyValue<int, Item> &E : item_map) {
+ ret.write[idx++] = E.key;
}
return ret;
}
int MeshLibrary::find_item_by_name(const String &p_name) const {
- for (Map<int, Item>::Element *E = item_map.front(); E; E = E->next()) {
- if (E->get().name == p_name) {
- return E->key();
+ for (const KeyValue<int, Item> &E : item_map) {
+ if (E.value.name == p_name) {
+ return E.key;
}
}
return -1;
diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index 00cee9269b..d87056f55d 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -41,6 +41,8 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
continue;
}
Array arr = p_mesh->surface_get_arrays(i);
+ ERR_CONTINUE(arr.size() != Mesh::ARRAY_MAX);
+
Vector<Vector3> varr = arr[Mesh::ARRAY_VERTEX];
Vector<int> iarr = arr[Mesh::ARRAY_INDEX];
if (varr.size() == 0 || iarr.size() == 0) {
@@ -367,10 +369,10 @@ Ref<Mesh> NavigationMesh::get_debug_mesh() {
}
List<Vector3> lines;
- for (Map<_EdgeKey, bool>::Element *E = edge_map.front(); E; E = E->next()) {
- if (E->get()) {
- lines.push_back(E->key().from);
- lines.push_back(E->key().to);
+ for (const KeyValue<_EdgeKey, bool> &E : edge_map) {
+ if (E.value) {
+ lines.push_back(E.key.from);
+ lines.push_back(E.key.to);
}
}
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 59faa50114..c8d3ea5e37 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -299,8 +299,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
}
- for (Map<Ref<Resource>, Ref<Resource>>::Element *E = resources_local_to_scene.front(); E; E = E->next()) {
- E->get()->setup_local_to_scene();
+ for (KeyValue<Ref<Resource>, Ref<Resource>> &E : resources_local_to_scene) {
+ E.value->setup_local_to_scene();
}
//do connections
@@ -384,7 +384,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
// save the child instantiated scenes that are chosen as editable, so they can be restored
// upon load back
- if (p_node != p_owner && p_node->get_filename() != String() && p_owner->is_editable_instance(p_node)) {
+ if (p_node != p_owner && p_node->get_scene_file_path() != String() && p_owner->is_editable_instance(p_node)) {
editable_instances.push_back(p_owner->get_path_to(p_node));
// Node is the root of an editable instance.
is_editable_instance = true;
@@ -437,14 +437,14 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
}
}
- if (p_node->get_filename() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) {
+ if (p_node->get_scene_file_path() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) {
if (p_node->get_scene_instance_load_placeholder()) {
//it's a placeholder, use the placeholder path
- nd.instance = _vm_get_variant(p_node->get_filename(), variant_map);
+ nd.instance = _vm_get_variant(p_node->get_scene_file_path(), variant_map);
nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER;
} else {
//must instance ourselves
- Ref<PackedScene> instance = ResourceLoader::load(p_node->get_filename());
+ Ref<PackedScene> instance = ResourceLoader::load(p_node->get_scene_file_path());
if (!instance.is_valid()) {
return ERR_CANT_OPEN;
}
@@ -454,7 +454,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map
}
n = nullptr;
} else {
- if (n->get_filename() != String()) {
+ if (n->get_scene_file_path() != String()) {
//is an instance
Ref<SceneState> state = n->get_scene_instance_state();
if (state.is_valid()) {
@@ -714,7 +714,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName
ERR_CONTINUE(!common_parent);
- if (common_parent != p_owner && common_parent->get_filename() == String()) {
+ if (common_parent != p_owner && common_parent->get_scene_file_path() == String()) {
common_parent = common_parent->get_owner();
}
@@ -774,7 +774,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName
nl = nullptr;
} else {
- if (nl->get_filename() != String()) {
+ if (nl->get_scene_file_path() != String()) {
//is an instance
Ref<SceneState> state = nl->get_scene_instance_state();
if (state.is_valid()) {
@@ -887,8 +887,8 @@ Error SceneState::pack(Node *p_scene) {
names.resize(name_map.size());
- for (Map<StringName, int>::Element *E = name_map.front(); E; E = E->next()) {
- names.write[E->get()] = E->key();
+ for (const KeyValue<StringName, int> &E : name_map) {
+ names.write[E.value] = E.key;
}
variants.resize(variant_map.size());
@@ -899,14 +899,14 @@ Error SceneState::pack(Node *p_scene) {
}
node_paths.resize(nodepath_map.size());
- for (Map<Node *, int>::Element *E = nodepath_map.front(); E; E = E->next()) {
- node_paths.write[E->get()] = scene->get_path_to(E->key());
+ for (const KeyValue<Node *, int> &E : nodepath_map) {
+ node_paths.write[E.value] = scene->get_path_to(E.key);
}
if (Engine::get_singleton()->is_editor_hint()) {
// Build node path cache
- for (Map<Node *, int>::Element *E = node_map.front(); E; E = E->next()) {
- node_path_cache[scene->get_path_to(E->key())] = E->get();
+ for (const KeyValue<Node *, int> &E : node_map) {
+ node_path_cache[scene->get_path_to(E.key)] = E.value;
}
}
@@ -977,9 +977,9 @@ int SceneState::find_node_by_path(const NodePath &p_node) const {
}
int SceneState::_find_base_scene_node_remap_key(int p_idx) const {
- for (Map<int, int>::Element *E = base_scene_node_remap.front(); E; E = E->next()) {
- if (E->value() == p_idx) {
- return E->key();
+ for (const KeyValue<int, int> &E : base_scene_node_remap) {
+ if (E.value == p_idx) {
+ return E.key;
}
}
return -1;
@@ -1652,7 +1652,7 @@ Node *PackedScene::instantiate(GenEditState p_edit_state) const {
}
if (get_path() != "" && get_path().find("::") == -1) {
- s->set_filename(get_path());
+ s->set_scene_file_path(get_path());
}
s->notification(Node::NOTIFICATION_INSTANCED);
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 341ce22185..77d915aef9 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1653,55 +1653,55 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
#ifdef TOOLS_ENABLED
// Keep order from cached ids.
Set<String> cached_ids_found;
- for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) {
- String cached_id = E->key()->get_id_for_path(local_path);
+ for (KeyValue<RES, String> &E : external_resources) {
+ String cached_id = E.key->get_id_for_path(local_path);
if (cached_id == "" || cached_ids_found.has(cached_id)) {
- int sep_pos = E->get().find("_");
+ int sep_pos = E.value.find("_");
if (sep_pos != -1) {
- E->get() = E->get().substr(0, sep_pos + 1); // Keep the order found, for improved thread loading performance.
+ E.value = E.value.substr(0, sep_pos + 1); // Keep the order found, for improved thread loading performance.
} else {
- E->get() = "";
+ E.value = "";
}
} else {
- E->get() = cached_id;
+ E.value = cached_id;
cached_ids_found.insert(cached_id);
}
}
// Create IDs for non cached resources.
- for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) {
- if (cached_ids_found.has(E->get())) { // Already cached, go on.
+ for (KeyValue<RES, String> &E : external_resources) {
+ if (cached_ids_found.has(E.value)) { // Already cached, go on.
continue;
}
String attempt;
while (true) {
- attempt = E->get() + Resource::generate_scene_unique_id();
+ attempt = E.value + Resource::generate_scene_unique_id();
if (!cached_ids_found.has(attempt)) {
break;
}
}
cached_ids_found.insert(attempt);
- E->get() = attempt;
+ E.value = attempt;
// Update also in resource.
- Ref<Resource> res = E->key();
+ Ref<Resource> res = E.key;
res->set_id_for_path(local_path, attempt);
}
#else
// Make sure to start from one, as it makes format more readable.
int counter = 1;
- for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) {
- E->get() = itos(counter++);
+ for (KeyValue<RES, String> &E : external_resources) {
+ E.value = itos(counter++);
}
#endif
Vector<ResourceSort> sorted_er;
- for (Map<RES, String>::Element *E = external_resources.front(); E; E = E->next()) {
+ for (const KeyValue<RES, String> &E : external_resources) {
ResourceSort rs;
- rs.resource = E->key();
- rs.id = E->get();
+ rs.resource = E.key;
+ rs.id = E.value;
sorted_er.push_back(rs);
}
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 44d524f142..242e20f3b0 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -116,8 +116,8 @@ Ref<Texture2D> Shader::get_default_texture_param(const StringName &p_param) cons
}
void Shader::get_default_texture_param_list(List<StringName> *r_textures) const {
- for (const Map<StringName, Ref<Texture2D>>::Element *E = default_textures.front(); E; E = E->next()) {
- r_textures->push_back(E->key());
+ for (const KeyValue<StringName, Ref<Texture2D>> &E : default_textures) {
+ r_textures->push_back(E.key);
}
}
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index 39082b6f7a..b6d3c96cb7 100644
--- a/scene/resources/sky_material.cpp
+++ b/scene/resources/sky_material.cpp
@@ -125,10 +125,6 @@ float ProceduralSkyMaterial::get_sun_curve() const {
return sun_curve;
}
-bool ProceduralSkyMaterial::_can_do_next_pass() const {
- return false;
-}
-
Shader::Mode ProceduralSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
@@ -312,10 +308,6 @@ Ref<Texture2D> PanoramaSkyMaterial::get_panorama() const {
return panorama;
}
-bool PanoramaSkyMaterial::_can_do_next_pass() const {
- return false;
-}
-
Shader::Mode PanoramaSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
@@ -482,10 +474,6 @@ Ref<Texture2D> PhysicalSkyMaterial::get_night_sky() const {
return night_sky;
}
-bool PhysicalSkyMaterial::_can_do_next_pass() const {
- return false;
-}
-
Shader::Mode PhysicalSkyMaterial::get_shader_mode() const {
return Shader::MODE_SKY;
}
diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h
index 63e730617b..daeda212d4 100644
--- a/scene/resources/sky_material.h
+++ b/scene/resources/sky_material.h
@@ -58,7 +58,6 @@ private:
protected:
static void _bind_methods();
- virtual bool _can_do_next_pass() const override;
public:
void set_sky_top_color(const Color &p_sky_top);
@@ -117,7 +116,6 @@ private:
protected:
static void _bind_methods();
- virtual bool _can_do_next_pass() const override;
public:
void set_panorama(const Ref<Texture2D> &p_panorama);
@@ -159,7 +157,6 @@ private:
protected:
static void _bind_methods();
- virtual bool _can_do_next_pass() const override;
public:
void set_rayleigh_coefficient(float p_rayleigh);
diff --git a/scene/resources/sprite_frames.cpp b/scene/resources/sprite_frames.cpp
index 140c6f821f..01afb00283 100644
--- a/scene/resources/sprite_frames.cpp
+++ b/scene/resources/sprite_frames.cpp
@@ -108,15 +108,15 @@ Vector<String> SpriteFrames::_get_animation_list() const {
}
void SpriteFrames::get_animation_list(List<StringName> *r_animations) const {
- for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) {
- r_animations->push_back(E->key());
+ for (const KeyValue<StringName, Anim> &E : animations) {
+ r_animations->push_back(E.key);
}
}
Vector<String> SpriteFrames::get_animation_names() const {
Vector<String> names;
- for (const Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) {
- names.push_back(E->key());
+ for (const KeyValue<StringName, Anim> &E : animations) {
+ names.push_back(E.key);
}
names.sort();
return names;
@@ -164,14 +164,14 @@ Array SpriteFrames::_get_frames() const {
Array SpriteFrames::_get_animations() const {
Array anims;
- for (Map<StringName, Anim>::Element *E = animations.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, Anim> &E : animations) {
Dictionary d;
- d["name"] = E->key();
- d["speed"] = E->get().speed;
- d["loop"] = E->get().loop;
+ d["name"] = E.key;
+ d["speed"] = E.value.speed;
+ d["loop"] = E.value.loop;
Array frames;
- for (int i = 0; i < E->get().frames.size(); i++) {
- frames.push_back(E->get().frames[i]);
+ for (int i = 0; i < E.value.frames.size(); i++) {
+ frames.push_back(E.value.frames[i]);
}
d["frames"] = frames;
anims.push_back(d);
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
index d2f38ba836..0094518967 100644
--- a/scene/resources/text_line.cpp
+++ b/scene/resources/text_line.cpp
@@ -53,7 +53,7 @@ void TextLine::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control");
- ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::_set_bidi_override);
+ ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::set_bidi_override);
ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""));
ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGN_CENTER), DEFVAL(1));
@@ -112,7 +112,7 @@ void TextLine::_shape() {
TS->shaped_text_tab_align(rid, tab_stops);
}
- uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
+ uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
if (overrun_behavior != OVERRUN_NO_TRIMMING) {
switch (overrun_behavior) {
case OVERRUN_TRIM_WORD_ELLIPSIS:
@@ -195,15 +195,7 @@ TextServer::Orientation TextLine::get_orientation() const {
return TS->shaped_text_get_orientation(rid);
}
-void TextLine::_set_bidi_override(const Array &p_override) {
- Vector<Vector2i> overrides;
- for (int i = 0; i < p_override.size(); i++) {
- overrides.push_back(p_override[i]);
- }
- set_bidi_override(overrides);
-}
-
-void TextLine::set_bidi_override(const Vector<Vector2i> &p_override) {
+void TextLine::set_bidi_override(const Array &p_override) {
TS->shaped_text_set_bidi_override(rid, p_override);
dirty = true;
}
@@ -256,14 +248,14 @@ void TextLine::tab_align(const Vector<float> &p_tab_stops) {
dirty = true;
}
-void TextLine::set_flags(uint8_t p_flags) {
+void TextLine::set_flags(uint16_t p_flags) {
if (flags != p_flags) {
flags = p_flags;
dirty = true;
}
}
-uint8_t TextLine::get_flags() const {
+uint16_t TextLine::get_flags() const {
return flags;
}
diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h
index 9ed9c2f177..43739f27ec 100644
--- a/scene/resources/text_line.h
+++ b/scene/resources/text_line.h
@@ -56,7 +56,7 @@ private:
bool dirty = true;
float width = -1.0;
- uint8_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
+ uint16_t flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
HAlign align = HALIGN_LEFT;
OverrunBehavior overrun_behavior = OVERRUN_TRIM_ELLIPSIS;
@@ -75,7 +75,7 @@ public:
void set_direction(TextServer::Direction p_direction);
TextServer::Direction get_direction() const;
- void set_bidi_override(const Vector<Vector2i> &p_override);
+ void set_bidi_override(const Array &p_override);
void set_orientation(TextServer::Orientation p_orientation);
TextServer::Orientation get_orientation() const;
@@ -95,8 +95,8 @@ public:
void tab_align(const Vector<float> &p_tab_stops);
- void set_flags(uint8_t p_flags);
- uint8_t get_flags() const;
+ void set_flags(uint16_t p_flags);
+ uint16_t get_flags() const;
void set_text_overrun_behavior(OverrunBehavior p_behavior);
OverrunBehavior get_text_overrun_behavior() const;
@@ -120,8 +120,6 @@ public:
int hit_test(float p_coords) const;
- void _set_bidi_override(const Array &p_override);
-
TextLine(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL);
TextLine();
~TextLine();
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 62949b9b98..b2e18e2451 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -53,7 +53,7 @@ void TextParagraph::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control");
- ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::_set_bidi_override);
+ ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::set_bidi_override);
ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL(""));
ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap);
@@ -158,9 +158,9 @@ void TextParagraph::_shape_lines() {
if (h_offset > 0) {
// Dropcap, flow around.
- Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags);
- for (int i = 0; i < line_breaks.size(); i++) {
- RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x);
+ PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags);
+ for (int i = 0; i < line_breaks.size(); i = i + 2) {
+ RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x;
if (v_offset < h) {
TS->free(line);
@@ -171,21 +171,21 @@ void TextParagraph::_shape_lines() {
}
dropcap_lines++;
v_offset -= h;
- start = line_breaks[i].y;
+ start = line_breaks[i + 1];
lines_rid.push_back(line);
}
}
// Use fixed for the rest of lines.
- Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags);
- for (int i = 0; i < line_breaks.size(); i++) {
- RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x);
+ PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags);
+ for (int i = 0; i < line_breaks.size(); i = i + 2) {
+ RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
if (!tab_stops.is_empty()) {
TS->shaped_text_tab_align(line, tab_stops);
}
lines_rid.push_back(line);
}
- uint8_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
+ uint16_t overrun_flags = TextServer::OVERRUN_NO_TRIMMING;
if (overrun_behavior != OVERRUN_NO_TRIMMING) {
switch (overrun_behavior) {
case OVERRUN_TRIM_WORD_ELLIPSIS:
@@ -347,15 +347,7 @@ int TextParagraph::get_spacing_bottom() const {
return spacing_bottom;
}
-void TextParagraph::_set_bidi_override(const Array &p_override) {
- Vector<Vector2i> overrides;
- for (int i = 0; i < p_override.size(); i++) {
- overrides.push_back(p_override[i]);
- }
- set_bidi_override(overrides);
-}
-
-void TextParagraph::set_bidi_override(const Vector<Vector2i> &p_override) {
+void TextParagraph::set_bidi_override(const Array &p_override) {
TS->shaped_text_set_bidi_override(rid, p_override);
lines_dirty = true;
}
@@ -392,14 +384,14 @@ void TextParagraph::tab_align(const Vector<float> &p_tab_stops) {
lines_dirty = true;
}
-void TextParagraph::set_flags(uint8_t p_flags) {
+void TextParagraph::set_flags(uint16_t p_flags) {
if (flags != p_flags) {
flags = p_flags;
lines_dirty = true;
}
}
-uint8_t TextParagraph::get_flags() const {
+uint16_t TextParagraph::get_flags() const {
return flags;
}
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
index ee7bbab9c5..69c50559df 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -63,7 +63,7 @@ private:
float width = -1.0;
int max_lines_visible = -1;
- uint8_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
+ uint16_t flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA;
OverrunBehavior overrun_behavior = OVERRUN_NO_TRIMMING;
HAlign align = HALIGN_LEFT;
@@ -94,7 +94,7 @@ public:
void set_preserve_control(bool p_enabled);
bool get_preserve_control() const;
- void set_bidi_override(const Vector<Vector2i> &p_override);
+ void set_bidi_override(const Array &p_override);
bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
void clear_dropcap();
@@ -108,8 +108,8 @@ public:
void tab_align(const Vector<float> &p_tab_stops);
- void set_flags(uint8_t p_flags);
- uint8_t get_flags() const;
+ void set_flags(uint16_t p_flags);
+ uint16_t get_flags() const;
void set_text_overrun_behavior(OverrunBehavior p_behavior);
OverrunBehavior get_text_overrun_behavior() const;
@@ -153,8 +153,6 @@ public:
int hit_test(const Point2 &p_coords) const;
- void _set_bidi_override(const Array &p_override);
-
TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", float p_width = -1.f, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL);
TextParagraph();
~TextParagraph();
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 80cab99373..1b3a7a5f2a 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -1249,7 +1249,7 @@ bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const {
}
Ref<Image> AtlasTexture::get_image() const {
- if (!atlas.is_valid()) {
+ if (!atlas.is_valid() || !atlas->get_image().is_valid()) {
return Ref<Image>();
}
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index e49d883ba4..65cdc1e24e 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -29,270 +29,20 @@
/*************************************************************************/
#include "theme.h"
-#include "core/io/file_access.h"
#include "core/string/print_string.h"
-void Theme::_emit_theme_changed() {
- if (no_change_propagation) {
- return;
- }
-
- notify_property_list_changed();
- emit_changed();
-}
-
-Vector<String> Theme::_get_icon_list(const String &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_icon_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_icon_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_icon_type_list(&il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_stylebox_list(const String &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_stylebox_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_stylebox_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_stylebox_type_list(&il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_font_list(const String &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_font_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_font_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_font_type_list(&il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_font_size_list(const String &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_font_size_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_font_size_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_font_size_type_list(&il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_color_list(const String &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_color_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_color_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_color_type_list(&il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_constant_list(const String &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_constant_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_constant_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_constant_type_list(&il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const {
- switch (p_data_type) {
- case DATA_TYPE_COLOR:
- return _get_color_list(p_theme_type);
- case DATA_TYPE_CONSTANT:
- return _get_constant_list(p_theme_type);
- case DATA_TYPE_FONT:
- return _get_font_list(p_theme_type);
- case DATA_TYPE_FONT_SIZE:
- return _get_font_size_list(p_theme_type);
- case DATA_TYPE_ICON:
- return _get_icon_list(p_theme_type);
- case DATA_TYPE_STYLEBOX:
- return _get_stylebox_list(p_theme_type);
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-
- return Vector<String>();
-}
-
-Vector<String> Theme::_get_theme_item_type_list(DataType p_data_type) const {
- switch (p_data_type) {
- case DATA_TYPE_COLOR:
- return _get_color_type_list();
- case DATA_TYPE_CONSTANT:
- return _get_constant_type_list();
- case DATA_TYPE_FONT:
- return _get_font_type_list();
- case DATA_TYPE_FONT_SIZE:
- return _get_font_size_type_list();
- case DATA_TYPE_ICON:
- return _get_icon_type_list();
- case DATA_TYPE_STYLEBOX:
- return _get_stylebox_type_list();
- case DATA_TYPE_MAX:
- break; // Can't happen, but silences warning.
- }
-
- return Vector<String>();
-}
-
-Vector<String> Theme::_get_type_variation_list(const StringName &p_theme_type) const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_type_variation_list(p_theme_type, &il);
- ilret.resize(il.size());
-
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
-
-Vector<String> Theme::_get_type_list() const {
- Vector<String> ilret;
- List<StringName> il;
-
- get_type_list(&il);
- ilret.resize(il.size());
+// Universal Theme resources used when no other theme has the item.
+Ref<Theme> Theme::default_theme;
+Ref<Theme> Theme::project_default_theme;
- int i = 0;
- String *w = ilret.ptrw();
- for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
- w[i] = E->get();
- }
- return ilret;
-}
+// Universal default values, final fallback for every theme.
+float Theme::default_base_scale = 1.0;
+Ref<Texture2D> Theme::default_icon;
+Ref<StyleBox> Theme::default_style;
+Ref<Font> Theme::default_font;
+int Theme::default_font_size = 16;
+// Dynamic properties.
bool Theme::_set(const StringName &p_name, const Variant &p_value) {
String sname = p_name;
@@ -452,6 +202,63 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
+// Universal fallback Theme resources.
+Ref<Theme> Theme::get_default() {
+ return default_theme;
+}
+
+void Theme::set_default(const Ref<Theme> &p_default) {
+ default_theme = p_default;
+}
+
+Ref<Theme> Theme::get_project_default() {
+ return project_default_theme;
+}
+
+void Theme::set_project_default(const Ref<Theme> &p_project_default) {
+ project_default_theme = p_project_default;
+}
+
+// Universal fallback values for theme item types.
+void Theme::set_default_base_scale(float p_base_scale) {
+ default_base_scale = p_base_scale;
+}
+
+void Theme::set_default_icon(const Ref<Texture2D> &p_icon) {
+ default_icon = p_icon;
+}
+
+void Theme::set_default_style(const Ref<StyleBox> &p_style) {
+ default_style = p_style;
+}
+
+void Theme::set_default_font(const Ref<Font> &p_font) {
+ default_font = p_font;
+}
+
+void Theme::set_default_font_size(int p_font_size) {
+ default_font_size = p_font_size;
+}
+
+// Fallback values for theme item types, configurable per theme.
+void Theme::set_default_theme_base_scale(float p_base_scale) {
+ if (default_theme_base_scale == p_base_scale) {
+ return;
+ }
+
+ default_theme_base_scale = p_base_scale;
+
+ _emit_theme_changed();
+}
+
+float Theme::get_default_theme_base_scale() const {
+ return default_theme_base_scale;
+}
+
+bool Theme::has_default_theme_base_scale() const {
+ return default_theme_base_scale > 0.0;
+}
+
void Theme::set_default_theme_font(const Ref<Font> &p_default_font) {
if (default_theme_font == p_default_font) {
return;
@@ -464,7 +271,7 @@ void Theme::set_default_theme_font(const Ref<Font> &p_default_font) {
default_theme_font = p_default_font;
if (default_theme_font.is_valid()) {
- default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ default_theme_font->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
}
_emit_theme_changed();
@@ -474,6 +281,10 @@ Ref<Font> Theme::get_default_theme_font() const {
return default_theme_font;
}
+bool Theme::has_default_theme_font() const {
+ return default_theme_font.is_valid();
+}
+
void Theme::set_default_theme_font_size(int p_font_size) {
if (default_theme_font_size == p_font_size) {
return;
@@ -488,57 +299,25 @@ int Theme::get_default_theme_font_size() const {
return default_theme_font_size;
}
-Ref<Theme> Theme::project_default_theme;
-Ref<Theme> Theme::default_theme;
-Ref<Texture2D> Theme::default_icon;
-Ref<StyleBox> Theme::default_style;
-Ref<Font> Theme::default_font;
-int Theme::default_font_size = 16;
-
-Ref<Theme> Theme::get_default() {
- return default_theme;
-}
-
-void Theme::set_default(const Ref<Theme> &p_default) {
- default_theme = p_default;
-}
-
-Ref<Theme> Theme::get_project_default() {
- return project_default_theme;
-}
-
-void Theme::set_project_default(const Ref<Theme> &p_project_default) {
- project_default_theme = p_project_default;
-}
-
-void Theme::set_default_icon(const Ref<Texture2D> &p_icon) {
- default_icon = p_icon;
-}
-
-void Theme::set_default_style(const Ref<StyleBox> &p_style) {
- default_style = p_style;
-}
-
-void Theme::set_default_font(const Ref<Font> &p_font) {
- default_font = p_font;
-}
-
-void Theme::set_default_font_size(int p_font_size) {
- default_font_size = p_font_size;
+bool Theme::has_default_theme_font_size() const {
+ return default_theme_font_size > 0;
}
+// Icons.
void Theme::set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon) {
+ bool existing = false;
if (icon_map[p_theme_type].has(p_name) && icon_map[p_theme_type][p_name].is_valid()) {
+ existing = true;
icon_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
icon_map[p_theme_type][p_name] = p_icon;
if (p_icon.is_valid()) {
- icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ icon_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
}
- _emit_theme_changed();
+ _emit_theme_changed(!existing);
}
Ref<Texture2D> Theme::get_icon(const StringName &p_name, const StringName &p_theme_type) const {
@@ -565,7 +344,7 @@ void Theme::rename_icon(const StringName &p_old_name, const StringName &p_name,
icon_map[p_theme_type][p_name] = icon_map[p_theme_type][p_old_name];
icon_map[p_theme_type].erase(p_old_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) {
@@ -578,7 +357,7 @@ void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type)
icon_map[p_theme_type].erase(p_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) const {
@@ -611,18 +390,21 @@ void Theme::get_icon_type_list(List<StringName> *p_list) const {
}
}
+// Styleboxes.
void Theme::set_stylebox(const StringName &p_name, const StringName &p_theme_type, const Ref<StyleBox> &p_style) {
+ bool existing = false;
if (style_map[p_theme_type].has(p_name) && style_map[p_theme_type][p_name].is_valid()) {
+ existing = true;
style_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
style_map[p_theme_type][p_name] = p_style;
if (p_style.is_valid()) {
- style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ style_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
}
- _emit_theme_changed();
+ _emit_theme_changed(!existing);
}
Ref<StyleBox> Theme::get_stylebox(const StringName &p_name, const StringName &p_theme_type) const {
@@ -649,7 +431,7 @@ void Theme::rename_stylebox(const StringName &p_old_name, const StringName &p_na
style_map[p_theme_type][p_name] = style_map[p_theme_type][p_old_name];
style_map[p_theme_type].erase(p_old_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_type) {
@@ -662,7 +444,7 @@ void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_t
style_map[p_theme_type].erase(p_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const {
@@ -695,24 +477,27 @@ void Theme::get_stylebox_type_list(List<StringName> *p_list) const {
}
}
+// Fonts.
void Theme::set_font(const StringName &p_name, const StringName &p_theme_type, const Ref<Font> &p_font) {
+ bool existing = false;
if (font_map[p_theme_type][p_name].is_valid()) {
+ existing = true;
font_map[p_theme_type][p_name]->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
}
font_map[p_theme_type][p_name] = p_font;
if (p_font.is_valid()) {
- font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(), CONNECT_REFERENCE_COUNTED);
+ font_map[p_theme_type][p_name]->connect("changed", callable_mp(this, &Theme::_emit_theme_changed), varray(false), CONNECT_REFERENCE_COUNTED);
}
- _emit_theme_changed();
+ _emit_theme_changed(!existing);
}
Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_type) const {
if (font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) {
return font_map[p_theme_type][p_name];
- } else if (default_theme_font.is_valid()) {
+ } else if (has_default_theme_font()) {
return default_theme_font;
} else {
return default_font;
@@ -720,7 +505,7 @@ Ref<Font> Theme::get_font(const StringName &p_name, const StringName &p_theme_ty
}
bool Theme::has_font(const StringName &p_name, const StringName &p_theme_type) const {
- return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || default_theme_font.is_valid());
+ return ((font_map.has(p_theme_type) && font_map[p_theme_type].has(p_name) && font_map[p_theme_type][p_name].is_valid()) || has_default_theme_font());
}
bool Theme::has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
@@ -735,7 +520,7 @@ void Theme::rename_font(const StringName &p_old_name, const StringName &p_name,
font_map[p_theme_type][p_name] = font_map[p_theme_type][p_old_name];
font_map[p_theme_type].erase(p_old_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) {
@@ -748,7 +533,7 @@ void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type)
font_map[p_theme_type].erase(p_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) const {
@@ -781,16 +566,18 @@ void Theme::get_font_type_list(List<StringName> *p_list) const {
}
}
+// Font sizes.
void Theme::set_font_size(const StringName &p_name, const StringName &p_theme_type, int p_font_size) {
+ bool existing = has_font_size_nocheck(p_name, p_theme_type);
font_size_map[p_theme_type][p_name] = p_font_size;
- _emit_theme_changed();
+ _emit_theme_changed(!existing);
}
int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_type) const {
if (font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) {
return font_size_map[p_theme_type][p_name];
- } else if (default_theme_font_size > 0) {
+ } else if (has_default_theme_font_size()) {
return default_theme_font_size;
} else {
return default_font_size;
@@ -798,7 +585,7 @@ int Theme::get_font_size(const StringName &p_name, const StringName &p_theme_typ
}
bool Theme::has_font_size(const StringName &p_name, const StringName &p_theme_type) const {
- return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || (default_theme_font_size > 0));
+ return ((font_size_map.has(p_theme_type) && font_size_map[p_theme_type].has(p_name) && (font_size_map[p_theme_type][p_name] > 0)) || has_default_theme_font_size());
}
bool Theme::has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const {
@@ -813,7 +600,7 @@ void Theme::rename_font_size(const StringName &p_old_name, const StringName &p_n
font_size_map[p_theme_type][p_name] = font_size_map[p_theme_type][p_old_name];
font_size_map[p_theme_type].erase(p_old_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_type) {
@@ -822,7 +609,7 @@ void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_
font_size_map[p_theme_type].erase(p_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const {
@@ -855,10 +642,12 @@ void Theme::get_font_size_type_list(List<StringName> *p_list) const {
}
}
+// Colors.
void Theme::set_color(const StringName &p_name, const StringName &p_theme_type, const Color &p_color) {
+ bool existing = has_color_nocheck(p_name, p_theme_type);
color_map[p_theme_type][p_name] = p_color;
- _emit_theme_changed();
+ _emit_theme_changed(!existing);
}
Color Theme::get_color(const StringName &p_name, const StringName &p_theme_type) const {
@@ -885,7 +674,7 @@ void Theme::rename_color(const StringName &p_old_name, const StringName &p_name,
color_map[p_theme_type][p_name] = color_map[p_theme_type][p_old_name];
color_map[p_theme_type].erase(p_old_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type) {
@@ -894,7 +683,7 @@ void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type
color_map[p_theme_type].erase(p_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) const {
@@ -927,10 +716,12 @@ void Theme::get_color_type_list(List<StringName> *p_list) const {
}
}
+// Theme constants.
void Theme::set_constant(const StringName &p_name, const StringName &p_theme_type, int p_constant) {
+ bool existing = has_constant_nocheck(p_name, p_theme_type);
constant_map[p_theme_type][p_name] = p_constant;
- _emit_theme_changed();
+ _emit_theme_changed(!existing);
}
int Theme::get_constant(const StringName &p_name, const StringName &p_theme_type) const {
@@ -957,7 +748,7 @@ void Theme::rename_constant(const StringName &p_old_name, const StringName &p_na
constant_map[p_theme_type][p_name] = constant_map[p_theme_type][p_old_name];
constant_map[p_theme_type].erase(p_old_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_type) {
@@ -966,7 +757,7 @@ void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_t
constant_map[p_theme_type].erase(p_name);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) const {
@@ -999,6 +790,7 @@ void Theme::get_constant_type_list(List<StringName> *p_list) const {
}
}
+// Generic methods for managing theme items.
void Theme::set_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type, const Variant &p_value) {
switch (p_data_type) {
case DATA_TYPE_COLOR: {
@@ -1230,6 +1022,7 @@ void Theme::get_theme_item_type_list(DataType p_data_type, List<StringName> *p_l
}
}
+// Theme type variations.
void Theme::set_type_variation(const StringName &p_theme_type, const StringName &p_base_type) {
ERR_FAIL_COND_MSG(p_theme_type == StringName(), "An empty theme type cannot be marked as a variation of another type.");
ERR_FAIL_COND_MSG(ClassDB::class_exists(p_theme_type), "A type associated with a built-in class cannot be marked as a variation of another type.");
@@ -1243,7 +1036,7 @@ void Theme::set_type_variation(const StringName &p_theme_type, const StringName
variation_map[p_theme_type] = p_base_type;
variation_base_map[p_base_type].push_back(p_theme_type);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
bool Theme::is_type_variation(const StringName &p_theme_type, const StringName &p_base_type) const {
@@ -1257,7 +1050,7 @@ void Theme::clear_type_variation(const StringName &p_theme_type) {
variation_base_map[base_type].erase(p_theme_type);
variation_map.erase(p_theme_type);
- _emit_theme_changed();
+ _emit_theme_changed(true);
}
StringName Theme::get_type_variation_base(const StringName &p_theme_type) const {
@@ -1287,67 +1080,355 @@ void Theme::get_type_variation_list(const StringName &p_base_type, List<StringNa
}
}
-void Theme::_freeze_change_propagation() {
- no_change_propagation = true;
-}
+// Theme types.
+void Theme::get_type_list(List<StringName> *p_list) const {
+ ERR_FAIL_NULL(p_list);
-void Theme::_unfreeze_and_propagate_changes() {
- no_change_propagation = false;
- _emit_theme_changed();
+ Set<StringName> types;
+ const StringName *key = nullptr;
+
+ // Icons.
+ while ((key = icon_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // StyleBoxes.
+ while ((key = style_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Fonts.
+ while ((key = font_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Font sizes.
+ while ((key = font_size_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Colors.
+ while ((key = color_map.next(key))) {
+ types.insert(*key);
+ }
+
+ key = nullptr;
+
+ // Constants.
+ while ((key = constant_map.next(key))) {
+ types.insert(*key);
+ }
+
+ for (Set<StringName>::Element *E = types.front(); E; E = E->next()) {
+ p_list->push_back(E->get());
+ }
}
-void Theme::clear() {
- // These items need disconnecting.
- {
- const StringName *K = nullptr;
- while ((K = icon_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = icon_map[*K].next(L))) {
- Ref<Texture2D> icon = icon_map[*K][*L];
- if (icon.is_valid()) {
- icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
+void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) {
+ ERR_FAIL_NULL(p_list);
+
+ // Build the dependency chain for type variations.
+ if (p_type_variation != StringName()) {
+ StringName variation_name = p_type_variation;
+ while (variation_name != StringName()) {
+ p_list->push_back(variation_name);
+ variation_name = get_type_variation_base(variation_name);
+
+ // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme).
+ if (variation_name == p_base_type) {
+ break;
}
}
}
- {
- const StringName *K = nullptr;
- while ((K = style_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = style_map[*K].next(L))) {
- Ref<StyleBox> style = style_map[*K][*L];
- if (style.is_valid()) {
- style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
- }
- }
+ // Continue building the chain using native class hierarchy.
+ StringName class_name = p_base_type;
+ while (class_name != StringName()) {
+ p_list->push_back(class_name);
+ class_name = ClassDB::get_parent_class_nocheck(class_name);
}
+}
- {
- const StringName *K = nullptr;
- while ((K = font_map.next(K))) {
- const StringName *L = nullptr;
- while ((L = font_map[*K].next(L))) {
- Ref<Font> font = font_map[*K][*L];
- if (font.is_valid()) {
- font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
- }
- }
- }
+// Internal methods for getting lists as a Vector of String (compatible with public API).
+Vector<String> Theme::_get_icon_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_icon_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
}
+ return ilret;
+}
- icon_map.clear();
- style_map.clear();
- font_map.clear();
- font_size_map.clear();
- color_map.clear();
- constant_map.clear();
+Vector<String> Theme::_get_icon_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
- variation_map.clear();
- variation_base_map.clear();
+ get_icon_type_list(&il);
+ ilret.resize(il.size());
- _emit_theme_changed();
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_stylebox_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_stylebox_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_stylebox_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_stylebox_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_font_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_font_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_font_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_font_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_font_size_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_font_size_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_font_size_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_font_size_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_color_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_color_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_color_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_color_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_constant_list(const String &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_constant_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_constant_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_constant_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_theme_item_list(DataType p_data_type, const String &p_theme_type) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ return _get_color_list(p_theme_type);
+ case DATA_TYPE_CONSTANT:
+ return _get_constant_list(p_theme_type);
+ case DATA_TYPE_FONT:
+ return _get_font_list(p_theme_type);
+ case DATA_TYPE_FONT_SIZE:
+ return _get_font_size_list(p_theme_type);
+ case DATA_TYPE_ICON:
+ return _get_icon_list(p_theme_type);
+ case DATA_TYPE_STYLEBOX:
+ return _get_stylebox_list(p_theme_type);
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+
+ return Vector<String>();
+}
+
+Vector<String> Theme::_get_theme_item_type_list(DataType p_data_type) const {
+ switch (p_data_type) {
+ case DATA_TYPE_COLOR:
+ return _get_color_type_list();
+ case DATA_TYPE_CONSTANT:
+ return _get_constant_type_list();
+ case DATA_TYPE_FONT:
+ return _get_font_type_list();
+ case DATA_TYPE_FONT_SIZE:
+ return _get_font_size_type_list();
+ case DATA_TYPE_ICON:
+ return _get_icon_type_list();
+ case DATA_TYPE_STYLEBOX:
+ return _get_stylebox_type_list();
+ case DATA_TYPE_MAX:
+ break; // Can't happen, but silences warning.
+ }
+
+ return Vector<String>();
+}
+
+Vector<String> Theme::_get_type_variation_list(const StringName &p_theme_type) const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_type_variation_list(p_theme_type, &il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+Vector<String> Theme::_get_type_list() const {
+ Vector<String> ilret;
+ List<StringName> il;
+
+ get_type_list(&il);
+ ilret.resize(il.size());
+
+ int i = 0;
+ String *w = ilret.ptrw();
+ for (List<StringName>::Element *E = il.front(); E; E = E->next(), i++) {
+ w[i] = E->get();
+ }
+ return ilret;
+}
+
+// Theme bulk manipulations.
+void Theme::_emit_theme_changed(bool p_notify_list_changed) {
+ if (no_change_propagation) {
+ return;
+ }
+
+ if (p_notify_list_changed) {
+ notify_property_list_changed();
+ }
+ emit_changed();
+}
+
+void Theme::_freeze_change_propagation() {
+ no_change_propagation = true;
+}
+
+void Theme::_unfreeze_and_propagate_changes() {
+ no_change_propagation = false;
+ _emit_theme_changed(true);
}
void Theme::merge_with(const Ref<Theme> &p_other) {
@@ -1434,80 +1515,58 @@ void Theme::merge_with(const Ref<Theme> &p_other) {
_unfreeze_and_propagate_changes();
}
-void Theme::get_type_list(List<StringName> *p_list) const {
- ERR_FAIL_NULL(p_list);
-
- Set<StringName> types;
- const StringName *key = nullptr;
-
- // Icons.
- while ((key = icon_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // StyleBoxes.
- while ((key = style_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Fonts.
- while ((key = font_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Font sizes.
- while ((key = font_size_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Colors.
- while ((key = color_map.next(key))) {
- types.insert(*key);
- }
-
- key = nullptr;
-
- // Constants.
- while ((key = constant_map.next(key))) {
- types.insert(*key);
+void Theme::clear() {
+ // These items need disconnecting.
+ {
+ const StringName *K = nullptr;
+ while ((K = icon_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = icon_map[*K].next(L))) {
+ Ref<Texture2D> icon = icon_map[*K][*L];
+ if (icon.is_valid()) {
+ icon->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+ }
+ }
}
- for (Set<StringName>::Element *E = types.front(); E; E = E->next()) {
- p_list->push_back(E->get());
+ {
+ const StringName *K = nullptr;
+ while ((K = style_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = style_map[*K].next(L))) {
+ Ref<StyleBox> style = style_map[*K][*L];
+ if (style.is_valid()) {
+ style->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
+ }
+ }
}
-}
-void Theme::get_type_dependencies(const StringName &p_base_type, const StringName &p_type_variation, List<StringName> *p_list) {
- ERR_FAIL_NULL(p_list);
-
- // Build the dependency chain for type variations.
- if (p_type_variation != StringName()) {
- StringName variation_name = p_type_variation;
- while (variation_name != StringName()) {
- p_list->push_back(variation_name);
- variation_name = get_type_variation_base(variation_name);
-
- // If we have reached the base type dependency, it's safe to stop (assuming no funny business was done to the Theme).
- if (variation_name == p_base_type) {
- break;
+ {
+ const StringName *K = nullptr;
+ while ((K = font_map.next(K))) {
+ const StringName *L = nullptr;
+ while ((L = font_map[*K].next(L))) {
+ Ref<Font> font = font_map[*K][*L];
+ if (font.is_valid()) {
+ font->disconnect("changed", callable_mp(this, &Theme::_emit_theme_changed));
+ }
}
}
}
- // Continue building the chain using native class hierarchy.
- StringName class_name = p_base_type;
- while (class_name != StringName()) {
- p_list->push_back(class_name);
- class_name = ClassDB::get_parent_class_nocheck(class_name);
- }
+ icon_map.clear();
+ style_map.clear();
+ font_map.clear();
+ font_size_map.clear();
+ color_map.clear();
+ constant_map.clear();
+
+ variation_map.clear();
+ variation_base_map.clear();
+
+ _emit_theme_changed(true);
}
void Theme::reset_state() {
@@ -1563,11 +1622,17 @@ void Theme::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_constant_list", "theme_type"), &Theme::_get_constant_list);
ClassDB::bind_method(D_METHOD("get_constant_type_list"), &Theme::_get_constant_type_list);
+ ClassDB::bind_method(D_METHOD("set_default_base_scale", "font_size"), &Theme::set_default_theme_base_scale);
+ ClassDB::bind_method(D_METHOD("get_default_base_scale"), &Theme::get_default_theme_base_scale);
+ ClassDB::bind_method(D_METHOD("has_default_base_scale"), &Theme::has_default_theme_base_scale);
+
ClassDB::bind_method(D_METHOD("set_default_font", "font"), &Theme::set_default_theme_font);
ClassDB::bind_method(D_METHOD("get_default_font"), &Theme::get_default_theme_font);
+ ClassDB::bind_method(D_METHOD("has_default_font"), &Theme::has_default_theme_font);
ClassDB::bind_method(D_METHOD("set_default_font_size", "font_size"), &Theme::set_default_theme_font_size);
ClassDB::bind_method(D_METHOD("get_default_font_size"), &Theme::get_default_theme_font_size);
+ ClassDB::bind_method(D_METHOD("has_default_font_size"), &Theme::has_default_theme_font_size);
ClassDB::bind_method(D_METHOD("set_theme_item", "data_type", "name", "theme_type", "value"), &Theme::set_theme_item);
ClassDB::bind_method(D_METHOD("get_theme_item", "data_type", "name", "theme_type"), &Theme::get_theme_item);
@@ -1588,6 +1653,7 @@ void Theme::_bind_methods() {
ClassDB::bind_method(D_METHOD("merge_with", "other"), &Theme::merge_with);
ClassDB::bind_method(D_METHOD("clear"), &Theme::clear);
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "default_base_scale", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,or_greater"), "set_default_base_scale", "get_default_base_scale");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font");
ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size"), "set_default_font_size", "get_default_font_size");
diff --git a/scene/resources/theme.h b/scene/resources/theme.h
index 15f21b91b8..d170d53ae3 100644
--- a/scene/resources/theme.h
+++ b/scene/resources/theme.h
@@ -61,7 +61,7 @@ public:
private:
bool no_change_propagation = false;
- void _emit_theme_changed();
+ void _emit_theme_changed(bool p_notify_list_changed = false);
HashMap<StringName, HashMap<StringName, Ref<Texture2D>>> icon_map;
HashMap<StringName, HashMap<StringName, Ref<StyleBox>>> style_map;
@@ -96,13 +96,19 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
- static Ref<Theme> project_default_theme;
+ // Universal Theme resources used when no other theme has the item.
static Ref<Theme> default_theme;
+ static Ref<Theme> project_default_theme;
+
+ // Universal default values, final fallback for every theme.
+ static float default_base_scale;
static Ref<Texture2D> default_icon;
static Ref<StyleBox> default_style;
static Ref<Font> default_font;
static int default_font_size;
+ // Default values configurable for each individual theme.
+ float default_theme_base_scale = 0.0;
Ref<Font> default_theme_font;
int default_theme_font_size = -1;
@@ -120,16 +126,23 @@ public:
static Ref<Theme> get_project_default();
static void set_project_default(const Ref<Theme> &p_project_default);
+ static void set_default_base_scale(float p_base_scale);
static void set_default_icon(const Ref<Texture2D> &p_icon);
static void set_default_style(const Ref<StyleBox> &p_style);
static void set_default_font(const Ref<Font> &p_font);
static void set_default_font_size(int p_font_size);
+ void set_default_theme_base_scale(float p_base_scale);
+ float get_default_theme_base_scale() const;
+ bool has_default_theme_base_scale() const;
+
void set_default_theme_font(const Ref<Font> &p_default_font);
Ref<Font> get_default_theme_font() const;
+ bool has_default_theme_font() const;
void set_default_theme_font_size(int p_font_size);
int get_default_theme_font_size() const;
+ bool has_default_theme_font_size() const;
void set_icon(const StringName &p_name, const StringName &p_theme_type, const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_icon(const StringName &p_name, const StringName &p_theme_type) const;
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 5a8c5b3782..40eedb63c1 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -66,8 +66,8 @@ const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = {
void TileSet::set_tile_shape(TileSet::TileShape p_shape) {
tile_shape = p_shape;
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) {
+ E_source.value->notify_tile_data_properties_should_change();
}
terrain_bits_meshes_dirty = true;
@@ -89,8 +89,8 @@ TileSet::TileLayout TileSet::get_tile_layout() const {
void TileSet::set_tile_offset_axis(TileSet::TileOffsetAxis p_alignment) {
tile_offset_axis = p_alignment;
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) {
+ E_source.value->notify_tile_data_properties_should_change();
}
terrain_bits_meshes_dirty = true;
@@ -386,8 +386,8 @@ void TileSet::remove_terrain_set(int p_index) {
void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode) {
ERR_FAIL_INDEX(p_terrain_set, terrain_sets.size());
terrain_sets.write[p_terrain_set].mode = p_terrain_mode;
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) {
+ E_source.value->notify_tile_data_properties_should_change();
}
notify_property_list_changed();
@@ -719,8 +719,8 @@ void TileSet::set_custom_data_type(int p_layer_id, Variant::Type p_value) {
ERR_FAIL_INDEX(p_layer_id, custom_data_layers.size());
custom_data_layers.write[p_layer_id].type = p_value;
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- E_source->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<int, Ref<TileSetSource>> &E_source : sources) {
+ E_source.value->notify_tile_data_properties_should_change();
}
emit_changed();
@@ -858,10 +858,10 @@ void TileSet::remove_alternative_level_tile_proxy(int p_source_from, Vector2i p_
Array TileSet::get_source_level_tile_proxies() const {
Array output;
- for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) {
+ for (const KeyValue<int, int> &E : source_level_proxies) {
Array proxy;
- proxy.push_back(E->key());
- proxy.push_back(E->get());
+ proxy.push_back(E.key);
+ proxy.push_back(E.value);
output.push_back(proxy);
}
return output;
@@ -869,10 +869,10 @@ Array TileSet::get_source_level_tile_proxies() const {
Array TileSet::get_coords_level_tile_proxies() const {
Array output;
- for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) {
+ for (const KeyValue<Array, Array> &E : coords_level_proxies) {
Array proxy;
- proxy.append_array(E->key());
- proxy.append_array(E->get());
+ proxy.append_array(E.key);
+ proxy.append_array(E.value);
output.push_back(proxy);
}
return output;
@@ -880,10 +880,10 @@ Array TileSet::get_coords_level_tile_proxies() const {
Array TileSet::get_alternative_level_tile_proxies() const {
Array output;
- for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) {
+ for (const KeyValue<Array, Array> &E : alternative_level_proxies) {
Array proxy;
- proxy.append_array(E->key());
- proxy.append_array(E->get());
+ proxy.append_array(E.key);
+ proxy.append_array(E.value);
output.push_back(proxy);
}
return output;
@@ -935,9 +935,9 @@ Array TileSet::map_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_a
void TileSet::cleanup_invalid_tile_proxies() {
// Source level.
Vector<int> source_to_remove;
- for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) {
- if (has_source(E->key())) {
- source_to_remove.append(E->key());
+ for (const KeyValue<int, int> &E : source_level_proxies) {
+ if (has_source(E.key)) {
+ source_to_remove.append(E.key);
}
}
for (int i = 0; i < source_to_remove.size(); i++) {
@@ -946,8 +946,8 @@ void TileSet::cleanup_invalid_tile_proxies() {
// Coords level.
Vector<Array> coords_to_remove;
- for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) {
- Array a = E->key();
+ for (const KeyValue<Array, Array> &E : coords_level_proxies) {
+ Array a = E.key;
if (has_source(a[0]) && get_source(a[0])->has_tile(a[1])) {
coords_to_remove.append(a);
}
@@ -959,8 +959,8 @@ void TileSet::cleanup_invalid_tile_proxies() {
// Alternative level.
Vector<Array> alternative_to_remove;
- for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) {
- Array a = E->key();
+ for (const KeyValue<Array, Array> &E : alternative_level_proxies) {
+ Array a = E.key;
if (has_source(a[0]) && get_source(a[0])->has_tile(a[1]) && get_source(a[0])->has_alternative_tile(a[1], a[2])) {
alternative_to_remove.append(a);
}
@@ -1911,8 +1911,8 @@ const int TileSetSource::INVALID_TILE_ALTERNATIVE = -1;
#ifndef DISABLE_DEPRECATED
void TileSet::_compatibility_conversion() {
- for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
- CompatibilityTileData *ctd = E->value();
+ for (KeyValue<int, CompatibilityTileData *> &E : compatibility_data) {
+ CompatibilityTileData *ctd = E.value;
// Add the texture
TileSetAtlasSource *atlas_source = memnew(TileSetAtlasSource);
@@ -1950,11 +1950,11 @@ void TileSet::_compatibility_conversion() {
value_array.push_back(coords);
value_array.push_back(alternative_tile);
- if (!compatibility_tilemap_mapping.has(E->key())) {
- compatibility_tilemap_mapping[E->key()] = Map<Array, Array>();
+ if (!compatibility_tilemap_mapping.has(E.key)) {
+ compatibility_tilemap_mapping[E.key] = Map<Array, Array>();
}
- compatibility_tilemap_mapping[E->key()][key_array] = value_array;
- compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_SINGLE_TILE;
+ compatibility_tilemap_mapping[E.key][key_array] = value_array;
+ compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_SINGLE_TILE;
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile));
@@ -2042,11 +2042,11 @@ void TileSet::_compatibility_conversion() {
value_array.push_back(coords);
value_array.push_back(alternative_tile);
- if (!compatibility_tilemap_mapping.has(E->key())) {
- compatibility_tilemap_mapping[E->key()] = Map<Array, Array>();
+ if (!compatibility_tilemap_mapping.has(E.key)) {
+ compatibility_tilemap_mapping[E.key] = Map<Array, Array>();
}
- compatibility_tilemap_mapping[E->key()][key_array] = value_array;
- compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_ATLAS_TILE;
+ compatibility_tilemap_mapping[E.key][key_array] = value_array;
+ compatibility_tilemap_mapping_tile_modes[E.key] = COMPATIBILITY_TILE_MODE_ATLAS_TILE;
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile));
@@ -2128,8 +2128,8 @@ void TileSet::_compatibility_conversion() {
}
// Reset compatibility data
- for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) {
+ memdelete(E.value);
}
compatibility_data = Map<int, CompatibilityTileData *>();
}
@@ -2579,25 +2579,25 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
} else if (components.size() == 2 && components[0] == "tile_proxies") {
if (components[1] == "source_level") {
Array a;
- for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) {
- a.push_back(E->key());
- a.push_back(E->get());
+ for (const KeyValue<int, int> &E : source_level_proxies) {
+ a.push_back(E.key);
+ a.push_back(E.value);
}
r_ret = a;
return true;
} else if (components[1] == "coords_level") {
Array a;
- for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) {
- a.push_back(E->key());
- a.push_back(E->get());
+ for (const KeyValue<Array, Array> &E : coords_level_proxies) {
+ a.push_back(E.key);
+ a.push_back(E.value);
}
r_ret = a;
return true;
} else if (components[1] == "alternative_level") {
Array a;
- for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) {
- a.push_back(E->key());
- a.push_back(E->get());
+ for (const KeyValue<Array, Array> &E : alternative_level_proxies) {
+ a.push_back(E.key);
+ a.push_back(E.value);
}
r_ret = a;
return true;
@@ -2673,8 +2673,8 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
// Sources.
// Note: sources have to be listed in at the end as some TileData rely on the TileSet properties being initialized first.
- for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) {
- p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ for (const KeyValue<int, Ref<TileSetSource>> &E_source : sources) {
+ p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
// Tile Proxies.
@@ -2850,8 +2850,8 @@ TileSet::TileSet() {
TileSet::~TileSet() {
#ifndef DISABLE_DEPRECATED
- for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<int, CompatibilityTileData *> &E : compatibility_data) {
+ memdelete(E.value);
}
#endif // DISABLE_DEPRECATED
while (!source_ids.is_empty()) {
@@ -2883,18 +2883,18 @@ void TileSetAtlasSource::set_tile_set(const TileSet *p_tile_set) {
tile_set = p_tile_set;
// Set the TileSet on all TileData.
- for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
- for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
- E_alternative->get()->set_tile_set(tile_set);
+ for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) {
+ for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->set_tile_set(tile_set);
}
}
}
void TileSetAtlasSource::notify_tile_data_properties_should_change() {
// Set the TileSet on all TileData.
- for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
- for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
- E_alternative->get()->notify_tile_data_properties_should_change();
+ for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) {
+ for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->notify_tile_data_properties_should_change();
}
}
}
@@ -3045,9 +3045,9 @@ void TileSetAtlasSource::remove_custom_data_layer(int p_index) {
void TileSetAtlasSource::reset_state() {
// Reset all TileData.
- for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
- for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
- E_alternative->get()->reset_state();
+ for (KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) {
+ for (KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
+ E_alternative.value->reset_state();
}
}
}
@@ -3124,7 +3124,7 @@ Vector2i TileSetAtlasSource::get_atlas_grid_size() const {
}
bool TileSetAtlasSource::_set(const StringName &p_name, const Variant &p_value) {
- Vector<String> components = String(p_name).split("/", true, 3);
+ Vector<String> components = String(p_name).split("/", true, 2);
// Compute the vector2i if we have coordinates.
Vector<String> coords_split = components[0].split(":");
@@ -3260,40 +3260,40 @@ bool TileSetAtlasSource::_get(const StringName &p_name, Variant &r_ret) const {
void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
// Atlases data.
PropertyInfo property_info;
- for (Map<Vector2i, TileAlternativesData>::Element *E_tile = tiles.front(); E_tile; E_tile = E_tile->next()) {
+ for (const KeyValue<Vector2i, TileAlternativesData> &E_tile : tiles) {
List<PropertyInfo> tile_property_list;
// size_in_atlas
property_info = PropertyInfo(Variant::VECTOR2I, "size_in_atlas", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
- if (E_tile->get().size_in_atlas == Vector2i(1, 1)) {
+ if (E_tile.value.size_in_atlas == Vector2i(1, 1)) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// next_alternative_id
property_info = PropertyInfo(Variant::INT, "next_alternative_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
- if (E_tile->get().next_alternative_id == 1) {
+ if (E_tile.value.next_alternative_id == 1) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// animation_columns.
property_info = PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
- if (E_tile->get().animation_columns == 0) {
+ if (E_tile.value.animation_columns == 0) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// animation_separation.
property_info = PropertyInfo(Variant::INT, "animation_separation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
- if (E_tile->get().animation_separation == Vector2i()) {
+ if (E_tile.value.animation_separation == Vector2i()) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
// animation_speed.
property_info = PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
- if (E_tile->get().animation_speed == 1.0) {
+ if (E_tile.value.animation_speed == 1.0) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
tile_property_list.push_back(property_info);
@@ -3302,8 +3302,8 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
tile_property_list.push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NETWORK));
// animation_frame_*.
- bool store_durations = tiles[E_tile->key()].animation_frames_durations.size() >= 2;
- for (int i = 0; i < (int)tiles[E_tile->key()].animation_frames_durations.size(); i++) {
+ bool store_durations = tiles[E_tile.key].animation_frames_durations.size() >= 2;
+ for (int i = 0; i < (int)tiles[E_tile.key].animation_frames_durations.size(); i++) {
property_info = PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR);
if (!store_durations) {
property_info.usage ^= PROPERTY_USAGE_STORAGE;
@@ -3311,27 +3311,27 @@ void TileSetAtlasSource::_get_property_list(List<PropertyInfo> *p_list) const {
tile_property_list.push_back(property_info);
}
- for (Map<int, TileData *>::Element *E_alternative = E_tile->get().alternatives.front(); E_alternative; E_alternative = E_alternative->next()) {
+ for (const KeyValue<int, TileData *> &E_alternative : E_tile.value.alternatives) {
// Add a dummy property to show the alternative exists.
- tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ tile_property_list.push_back(PropertyInfo(Variant::INT, vformat("%d", E_alternative.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
// Get the alternative tile's properties and append them to the list of properties.
List<PropertyInfo> alternative_property_list;
- E_alternative->get()->get_property_list(&alternative_property_list);
+ E_alternative.value->get_property_list(&alternative_property_list);
for (PropertyInfo &alternative_property_info : alternative_property_list) {
Variant default_value = ClassDB::class_get_default_property_value("TileData", alternative_property_info.name);
- Variant value = E_alternative->get()->get(alternative_property_info.name);
+ Variant value = E_alternative.value->get(alternative_property_info.name);
if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value))) {
alternative_property_info.usage ^= PROPERTY_USAGE_STORAGE;
}
- alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative->key()), alternative_property_info.name);
+ alternative_property_info.name = vformat("%s/%s", vformat("%d", E_alternative.key), alternative_property_info.name);
tile_property_list.push_back(alternative_property_info);
}
}
// Add all alternative.
for (PropertyInfo &tile_property_info : tile_property_list) {
- tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile->key().x, E_tile->key().y), tile_property_info.name);
+ tile_property_info.name = vformat("%s/%s", vformat("%d:%d", E_tile.key.x, E_tile.key.y), tile_property_info.name);
p_list->push_back(tile_property_info);
}
}
@@ -3373,8 +3373,8 @@ void TileSetAtlasSource::remove_tile(Vector2i p_atlas_coords) {
_clear_coords_mapping_cache(p_atlas_coords);
// Free tile data.
- for (Map<int, TileData *>::Element *E_tile_data = tiles[p_atlas_coords].alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) {
- memdelete(E_tile_data->get());
+ for (const KeyValue<int, TileData *> &E_tile_data : tiles[p_atlas_coords].alternatives) {
+ memdelete(E_tile_data.value);
}
// Delete the tile
@@ -3619,8 +3619,8 @@ bool TileSetAtlasSource::has_tiles_outside_texture() {
Vector2i grid_size = get_atlas_grid_size();
Vector<Vector2i> to_remove;
- for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) {
- if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) {
+ for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
+ if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) {
return true;
}
}
@@ -3632,9 +3632,9 @@ void TileSetAtlasSource::clear_tiles_outside_texture() {
Vector2i grid_size = get_atlas_grid_size();
Vector<Vector2i> to_remove;
- for (Map<Vector2i, TileSetAtlasSource::TileAlternativesData>::Element *E = tiles.front(); E; E = E->next()) {
- if (E->key().x >= grid_size.x || E->key().y >= grid_size.y) {
- to_remove.append(E->key());
+ for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
+ if (E.key.x >= grid_size.x || E.key.y >= grid_size.y) {
+ to_remove.append(E.key);
}
}
@@ -3775,9 +3775,9 @@ void TileSetAtlasSource::_bind_methods() {
TileSetAtlasSource::~TileSetAtlasSource() {
// Free everything needed.
- for (Map<Vector2i, TileAlternativesData>::Element *E_alternatives = tiles.front(); E_alternatives; E_alternatives = E_alternatives->next()) {
- for (Map<int, TileData *>::Element *E_tile_data = E_alternatives->get().alternatives.front(); E_tile_data; E_tile_data = E_tile_data->next()) {
- memdelete(E_tile_data->get());
+ for (KeyValue<Vector2i, TileAlternativesData> &E_alternatives : tiles) {
+ for (KeyValue<int, TileData *> &E_tile_data : E_alternatives.value.alternatives) {
+ memdelete(E_tile_data.value);
}
}
}
@@ -4316,9 +4316,26 @@ Ref<OccluderPolygon2D> TileData::get_occluder(int p_layer_id) const {
}
// Physics
-int TileData::get_collision_polygons_count(int p_layer_id) const {
- ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
- return physics[p_layer_id].polygons.size();
+void TileData::set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ physics.write[p_layer_id].linear_velocity = p_velocity;
+ emit_signal(SNAME("changed"));
+}
+
+Vector2 TileData::get_constant_linear_velocity(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), Vector2());
+ return physics[p_layer_id].linear_velocity;
+}
+
+void TileData::set_constant_angular_velocity(int p_layer_id, real_t p_velocity) {
+ ERR_FAIL_INDEX(p_layer_id, physics.size());
+ physics.write[p_layer_id].angular_velocity = p_velocity;
+ emit_signal(SNAME("changed"));
+}
+
+real_t TileData::get_constant_angular_velocity(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0.0);
+ return physics[p_layer_id].angular_velocity;
}
void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count) {
@@ -4329,6 +4346,11 @@ void TileData::set_collision_polygons_count(int p_layer_id, int p_polygons_count
emit_signal(SNAME("changed"));
}
+int TileData::get_collision_polygons_count(int p_layer_id) const {
+ ERR_FAIL_INDEX_V(p_layer_id, physics.size(), 0);
+ return physics[p_layer_id].polygons.size();
+}
+
void TileData::add_collision_polygon(int p_layer_id) {
ERR_FAIL_INDEX(p_layer_id, physics.size());
physics.write[p_layer_id].polygons.push_back(PhysicsLayerTileData::PolygonShapeTileData());
@@ -4530,11 +4552,7 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
// Physics layers.
int layer_index = components[0].trim_prefix("physics_layer_").to_int();
ERR_FAIL_COND_V(layer_index < 0, false);
- if (components.size() == 2 && components[1] == "polygons_count") {
- if (p_value.get_type() != Variant::INT) {
- return false;
- }
-
+ if (components.size() == 2) {
if (layer_index >= physics.size()) {
if (tile_set) {
return false;
@@ -4542,8 +4560,19 @@ bool TileData::_set(const StringName &p_name, const Variant &p_value) {
physics.resize(layer_index + 1);
}
}
- set_collision_polygons_count(layer_index, p_value);
- return true;
+ if (components[1] == "linear_velocity") {
+ set_constant_linear_velocity(layer_index, p_value);
+ return true;
+ } else if (components[1] == "angular_velocity") {
+ set_constant_angular_velocity(layer_index, p_value);
+ return true;
+ } else if (components[1] == "polygons_count") {
+ if (p_value.get_type() != Variant::INT) {
+ return false;
+ }
+ set_collision_polygons_count(layer_index, p_value);
+ return true;
+ }
} else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) {
int polygon_index = components[1].trim_prefix("polygon_").to_int();
ERR_FAIL_COND_V(polygon_index < 0, false);
@@ -4645,9 +4674,18 @@ bool TileData::_get(const StringName &p_name, Variant &r_ret) const {
if (layer_index >= physics.size()) {
return false;
}
- if (components.size() == 2 && components[1] == "polygons_count") {
- r_ret = get_collision_polygons_count(layer_index);
- return true;
+
+ if (components.size() == 2) {
+ if (components[1] == "linear_velocity") {
+ r_ret = get_constant_linear_velocity(layer_index);
+ return true;
+ } else if (components[1] == "angular_velocity") {
+ r_ret = get_constant_angular_velocity(layer_index);
+ return true;
+ } else if (components[1] == "polygons_count") {
+ r_ret = get_collision_polygons_count(layer_index);
+ return true;
+ }
} else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) {
int polygon_index = components[1].trim_prefix("polygon_").to_int();
ERR_FAIL_COND_V(polygon_index < 0, false);
@@ -4718,6 +4756,8 @@ void TileData::_get_property_list(List<PropertyInfo> *p_list) const {
// Physics layers.
p_list->push_back(PropertyInfo(Variant::NIL, "Physics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
for (int i = 0; i < physics.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::VECTOR2, vformat("physics_layer_%d/linear_velocity", i), PROPERTY_HINT_NONE));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("physics_layer_%d/angular_velocity", i), PROPERTY_HINT_NONE));
p_list->push_back(PropertyInfo(Variant::INT, vformat("physics_layer_%d/polygons_count", i), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
for (int j = 0; j < physics[i].polygons.size(); j++) {
@@ -4807,8 +4847,12 @@ void TileData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_occluder", "layer_id"), &TileData::get_occluder);
// Physics.
- ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count);
+ ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "layer_id", "velocity"), &TileData::set_constant_linear_velocity);
+ ClassDB::bind_method(D_METHOD("get_constant_linear_velocity", "layer_id"), &TileData::get_constant_linear_velocity);
+ ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "layer_id", "velocity"), &TileData::set_constant_angular_velocity);
+ ClassDB::bind_method(D_METHOD("get_constant_angular_velocity", "layer_id"), &TileData::get_constant_angular_velocity);
ClassDB::bind_method(D_METHOD("set_collision_polygons_count", "layer_id", "polygons_count"), &TileData::set_collision_polygons_count);
+ ClassDB::bind_method(D_METHOD("get_collision_polygons_count", "layer_id"), &TileData::get_collision_polygons_count);
ClassDB::bind_method(D_METHOD("add_collision_polygon", "layer_id"), &TileData::add_collision_polygon);
ClassDB::bind_method(D_METHOD("remove_collision_polygon", "layer_id", "polygon_index"), &TileData::remove_collision_polygon);
ClassDB::bind_method(D_METHOD("set_collision_polygon_points", "layer_id", "polygon_index", "polygon"), &TileData::set_collision_polygon_points);
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 46cb1fb36d..2b4929b3df 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -645,6 +645,8 @@ private:
float one_way_margin = 1.0;
};
+ Vector2 linear_velocity;
+ float angular_velocity = 0.0;
Vector<PolygonShapeTileData> polygons;
};
Vector<PhysicsLayerTileData> physics;
@@ -718,8 +720,12 @@ public:
Ref<OccluderPolygon2D> get_occluder(int p_layer_id) const;
// Physics
- int get_collision_polygons_count(int p_layer_id) const;
+ void set_constant_linear_velocity(int p_layer_id, const Vector2 &p_velocity);
+ Vector2 get_constant_linear_velocity(int p_layer_id) const;
+ void set_constant_angular_velocity(int p_layer_id, real_t p_velocity);
+ real_t get_constant_angular_velocity(int p_layer_id) const;
void set_collision_polygons_count(int p_layer_id, int p_shapes_count);
+ int get_collision_polygons_count(int p_layer_id) const;
void add_collision_polygon(int p_layer_id);
void remove_collision_polygon(int p_layer_id, int p_polygon_index);
void set_collision_polygon_points(int p_layer_id, int p_polygon_index, Vector<Vector2> p_polygon);
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index e8fe3ff3cd..934d16bd7e 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -197,9 +197,9 @@ Vector<StringName> VisualShaderNode::get_editable_properties() const {
Array VisualShaderNode::get_default_input_values() const {
Array ret;
- for (Map<int, Variant>::Element *E = default_input_values.front(); E; E = E->next()) {
- ret.push_back(E->key());
- ret.push_back(E->get());
+ for (const KeyValue<int, Variant> &E : default_input_values) {
+ ret.push_back(E.key);
+ ret.push_back(E.value);
}
return ret;
}
@@ -460,14 +460,14 @@ Dictionary VisualShader::get_engine_version() const {
void VisualShader::update_engine_version(const Dictionary &p_new_version) {
if (engine_version.is_empty()) { // before 4.0
for (int i = 0; i < TYPE_MAX; i++) {
- for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
- Ref<VisualShaderNodeInput> input = Object::cast_to<VisualShaderNodeInput>(E->get().node.ptr());
+ for (KeyValue<int, Node> &E : graph[i].nodes) {
+ Ref<VisualShaderNodeInput> input = Object::cast_to<VisualShaderNodeInput>(E.value.node.ptr());
if (input.is_valid()) {
if (input->get_input_name() == "side") {
input->set_input_name("front_facing");
}
}
- Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr());
+ Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(E.value.node.ptr());
if (expression.is_valid()) {
for (int j = 0; j < expression->get_input_port_count(); j++) {
int type = expression->get_input_port_type(j);
@@ -484,7 +484,7 @@ void VisualShader::update_engine_version(const Dictionary &p_new_version) {
expression->set_output_port_type(j, type);
}
}
- Ref<VisualShaderNodeCompare> compare = Object::cast_to<VisualShaderNodeCompare>(E->get().node.ptr());
+ Ref<VisualShaderNodeCompare> compare = Object::cast_to<VisualShaderNodeCompare>(E.value.node.ptr());
if (compare.is_valid()) {
int ctype = int(compare->get_comparison_type());
if (int(ctype) > 0) { // + PORT_TYPE_SCALAR_INT
@@ -561,8 +561,8 @@ Vector<int> VisualShader::get_node_list(Type p_type) const {
const Graph *g = &graph[p_type];
Vector<int> ret;
- for (Map<int, Node>::Element *E = g->nodes.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<int, Node> &E : g->nodes) {
+ ret.push_back(E.key);
}
return ret;
@@ -575,9 +575,9 @@ int VisualShader::get_valid_node_id(Type p_type) const {
}
int VisualShader::find_node_id(Type p_type, const Ref<VisualShaderNode> &p_node) const {
- for (const Map<int, Node>::Element *E = graph[p_type].nodes.front(); E; E = E->next()) {
- if (E->get().node == p_node) {
- return E->key();
+ for (const KeyValue<int, Node> &E : graph[p_type].nodes) {
+ if (E.value.node == p_node) {
+ return E.key;
}
}
@@ -819,8 +819,8 @@ void VisualShader::set_mode(Mode p_mode) {
flags.clear();
shader_mode = p_mode;
for (int i = 0; i < TYPE_MAX; i++) {
- for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
- Ref<VisualShaderNodeInput> input = E->get().node;
+ for (KeyValue<int, Node> &E : graph[i].nodes) {
+ Ref<VisualShaderNodeInput> input = E.value.node;
if (input.is_valid()) {
input->shader_mode = shader_mode;
//input->input_index = 0;
@@ -1041,8 +1041,8 @@ String VisualShader::validate_uniform_name(const String &p_name, const Ref<Visua
while (true) {
bool exists = false;
for (int i = 0; i < TYPE_MAX; i++) {
- for (const Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
- Ref<VisualShaderNodeUniform> node = E->get().node;
+ for (const KeyValue<int, Node> &E : graph[i].nodes) {
+ Ref<VisualShaderNodeUniform> node = E.value.node;
if (node == p_uniform) { //do not test on self
continue;
}
@@ -1271,8 +1271,8 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
- for (Map<String, String>::Element *E = blend_mode_enums.front(); E; E = E->next()) {
- p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E->key(), PROPERTY_HINT_ENUM, E->get()));
+ for (const KeyValue<String, String> &E : blend_mode_enums) {
+ p_list->push_back(PropertyInfo(Variant::INT, "modes/" + E.key, PROPERTY_HINT_ENUM, E.value));
}
for (Set<String>::Element *E = toggles.front(); E; E = E->next()) {
@@ -1280,22 +1280,22 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
}
for (int i = 0; i < TYPE_MAX; i++) {
- for (Map<int, Node>::Element *E = graph[i].nodes.front(); E; E = E->next()) {
+ for (const KeyValue<int, Node> &E : graph[i].nodes) {
String prop_name = "nodes/";
prop_name += type_string[i];
- prop_name += "/" + itos(E->key());
+ prop_name += "/" + itos(E.key);
- if (E->key() != NODE_ID_OUTPUT) {
+ if (E.key != NODE_ID_OUTPUT) {
p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "VisualShaderNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
}
p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- if (Object::cast_to<VisualShaderNodeGroupBase>(E->get().node.ptr()) != nullptr) {
+ if (Object::cast_to<VisualShaderNodeGroupBase>(E.value.node.ptr()) != nullptr) {
p_list->push_back(PropertyInfo(Variant::VECTOR2, prop_name + "/size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/input_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
- if (Object::cast_to<VisualShaderNodeExpression>(E->get().node.ptr()) != nullptr) {
+ if (Object::cast_to<VisualShaderNodeExpression>(E.value.node.ptr()) != nullptr) {
p_list->push_back(PropertyInfo(Variant::STRING, prop_name + "/expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
}
@@ -1716,9 +1716,9 @@ void VisualShader::_update_shader() const {
emitters.insert(i, List<int>());
}
- for (Map<int, Node>::Element *M = graph[i].nodes.front(); M; M = M->next()) {
- if (M->get().node == emit_particle.ptr()) {
- emitters[i].push_back(M->key());
+ for (const KeyValue<int, Node> &M : graph[i].nodes) {
+ if (M.value.node == emit_particle.ptr()) {
+ emitters[i].push_back(M.key);
break;
}
}