summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite_2d.cpp5
-rw-r--r--scene/2d/area_2d.cpp28
-rw-r--r--scene/2d/audio_stream_player_2d.cpp2
-rw-r--r--scene/2d/collision_object_2d.cpp24
-rw-r--r--scene/2d/gpu_particles_2d.cpp4
-rw-r--r--scene/2d/joint_2d.cpp (renamed from scene/2d/joints_2d.cpp)9
-rw-r--r--scene/2d/joint_2d.h (renamed from scene/2d/joints_2d.h)8
-rw-r--r--scene/2d/parallax_layer.cpp4
-rw-r--r--scene/2d/physical_bone_2d.cpp22
-rw-r--r--scene/2d/physical_bone_2d.h4
-rw-r--r--scene/2d/physics_body_2d.cpp379
-rw-r--r--scene/2d/physics_body_2d.h72
-rw-r--r--scene/2d/tile_map.cpp477
-rw-r--r--scene/2d/tile_map.h18
-rw-r--r--scene/3d/area_3d.cpp28
-rw-r--r--scene/3d/audio_stream_player_3d.cpp2
-rw-r--r--scene/3d/bone_attachment_3d.cpp8
-rw-r--r--scene/3d/bone_attachment_3d.h1
-rw-r--r--scene/3d/camera_3d.cpp263
-rw-r--r--scene/3d/camera_3d.h72
-rw-r--r--scene/3d/collision_object_3d.cpp40
-rw-r--r--scene/3d/collision_shape_3d.cpp3
-rw-r--r--scene/3d/gpu_particles_3d.cpp2
-rw-r--r--scene/3d/gpu_particles_collision_3d.cpp2
-rw-r--r--scene/3d/importer_mesh_instance_3d.cpp85
-rw-r--r--scene/3d/importer_mesh_instance_3d.h (renamed from scene/animation/animation_cache.h)64
-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.cpp10
-rw-r--r--scene/3d/node_3d.cpp13
-rw-r--r--scene/3d/node_3d.h4
-rw-r--r--scene/3d/physics_body_3d.cpp455
-rw-r--r--scene/3d/physics_body_3d.h84
-rw-r--r--scene/3d/proximity_group_3d.cpp10
-rw-r--r--scene/3d/skeleton_3d.cpp360
-rw-r--r--scene/3d/skeleton_3d.h44
-rw-r--r--scene/3d/skeleton_ik_3d.cpp16
-rw-r--r--scene/3d/soft_dynamic_body_3d.cpp13
-rw-r--r--scene/3d/soft_dynamic_body_3d.h2
-rw-r--r--scene/3d/spring_arm_3d.cpp32
-rw-r--r--scene/3d/sprite_3d.cpp14
-rw-r--r--scene/3d/vehicle_body_3d.cpp10
-rw-r--r--scene/3d/voxel_gi.cpp2
-rw-r--r--scene/3d/voxelizer.cpp3
-rw-r--r--scene/animation/animation_blend_tree.cpp54
-rw-r--r--scene/animation/animation_cache.cpp314
-rw-r--r--scene/animation/animation_node_state_machine.cpp34
-rw-r--r--scene/animation/animation_player.cpp221
-rw-r--r--scene/animation/animation_player.h7
-rw-r--r--scene/animation/animation_tree.cpp253
-rw-r--r--scene/animation/animation_tree.h5
-rw-r--r--scene/audio/audio_stream_player.cpp2
-rw-r--r--scene/debugger/scene_debugger.cpp16
-rw-r--r--scene/gui/button.cpp20
-rw-r--r--scene/gui/button.h6
-rw-r--r--scene/gui/code_edit.cpp54
-rw-r--r--scene/gui/code_edit.h1
-rw-r--r--scene/gui/color_picker.cpp57
-rw-r--r--scene/gui/color_picker.h1
-rw-r--r--scene/gui/container.cpp6
-rw-r--r--scene/gui/container.h3
-rw-r--r--scene/gui/control.cpp179
-rw-r--r--scene/gui/control.h14
-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.cpp21
-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.cpp143
-rw-r--r--scene/gui/line_edit.h7
-rw-r--r--scene/gui/rich_text_label.cpp29
-rw-r--r--scene/gui/spin_box.cpp18
-rw-r--r--scene/gui/spin_box.h4
-rw-r--r--scene/gui/tabs.cpp47
-rw-r--r--scene/gui/text_edit.cpp298
-rw-r--r--scene/gui/text_edit.h29
-rw-r--r--scene/gui/texture_progress_bar.cpp12
-rw-r--r--scene/gui/tree.cpp140
-rw-r--r--scene/gui/tree.h8
-rw-r--r--scene/main/canvas_item.cpp4
-rw-r--r--scene/main/canvas_item.h4
-rw-r--r--scene/main/http_request.cpp10
-rw-r--r--scene/main/node.cpp82
-rw-r--r--scene/main/node.h8
-rw-r--r--scene/main/resource_preloader.cpp8
-rw-r--r--scene/main/scene_tree.cpp8
-rw-r--r--scene/main/timer.cpp11
-rw-r--r--scene/main/timer.h2
-rw-r--r--scene/main/viewport.cpp60
-rw-r--r--scene/main/viewport.h15
-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/animation.cpp977
-rw-r--r--scene/resources/animation.h74
-rw-r--r--scene/resources/canvas_item_material.cpp2
-rw-r--r--scene/resources/curve.cpp8
-rw-r--r--scene/resources/default_theme/default_theme.cpp13
-rw-r--r--scene/resources/environment.cpp21
-rw-r--r--scene/resources/environment.h3
-rw-r--r--scene/resources/font.cpp19
-rw-r--r--scene/resources/font.h8
-rw-r--r--scene/resources/importer_mesh.cpp1247
-rw-r--r--scene/resources/importer_mesh.h133
-rw-r--r--scene/resources/material.cpp22
-rw-r--r--scene/resources/material.h3
-rw-r--r--scene/resources/mesh.cpp6
-rw-r--r--scene/resources/mesh_library.cpp14
-rw-r--r--scene/resources/navigation_mesh.cpp10
-rw-r--r--scene/resources/packed_scene.cpp40
-rw-r--r--scene/resources/primitive_meshes.cpp8
-rw-r--r--scene/resources/primitive_meshes.h2
-rw-r--r--scene/resources/resource_format_text.cpp32
-rw-r--r--scene/resources/shader.cpp4
-rw-r--r--scene/resources/skeleton_modification_2d.cpp42
-rw-r--r--scene/resources/skeleton_modification_2d_fabrik.cpp4
-rw-r--r--scene/resources/skeleton_modification_3d.cpp40
-rw-r--r--scene/resources/skeleton_modification_3d_fabrik.cpp6
-rw-r--r--scene/resources/skeleton_modification_3d_twoboneik.cpp2
-rw-r--r--scene/resources/skeleton_modification_stack_2d.cpp2
-rw-r--r--scene/resources/skeleton_modification_stack_3d.cpp2
-rw-r--r--scene/resources/sky_material.cpp26
-rw-r--r--scene/resources/sky_material.h3
-rw-r--r--scene/resources/sprite_frames.cpp20
-rw-r--r--scene/resources/surface_tool.cpp1
-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.cpp8
-rw-r--r--scene/resources/texture.h2
-rw-r--r--scene/resources/theme.cpp944
-rw-r--r--scene/resources/theme.h17
-rw-r--r--scene/resources/tile_set.cpp352
-rw-r--r--scene/resources/tile_set.h17
-rw-r--r--scene/resources/visual_shader.cpp104
-rw-r--r--scene/resources/visual_shader.h2
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp20
-rw-r--r--scene/resources/visual_shader_particle_nodes.h5
-rw-r--r--scene/scene_string_names.cpp4
-rw-r--r--scene/scene_string_names.h4
143 files changed, 5891 insertions, 3570 deletions
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp
index 96a3134691..fad4784d51 100644
--- a/scene/2d/animated_sprite_2d.cpp
+++ b/scene/2d/animated_sprite_2d.cpp
@@ -94,7 +94,7 @@ Rect2 AnimatedSprite2D::_get_rect() const {
Point2 ofs = offset;
if (centered) {
- ofs -= Size2(s) / 2;
+ ofs -= s / 2;
}
if (s == Size2(0, 0)) {
@@ -228,8 +228,7 @@ void AnimatedSprite2D::_notification(int p_what) {
RID ci = get_canvas_item();
- Size2i s;
- s = texture->get_size();
+ Size2 s = texture->get_size();
Point2 ofs = offset;
if (centered) {
ofs -= s / 2;
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index e497ea48c4..fff9c47d4d 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/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index bddc342c1a..18a50ae098 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -68,7 +68,7 @@ void AudioStreamPlayer2D::_notification(int p_what) {
active.set();
Ref<AudioStreamPlayback> new_playback = stream->instance_playback();
ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
- AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get());
+ AudioServer::get_singleton()->start_playback_stream(new_playback, _get_actual_bus(), volume_vector, setplay.get(), pitch_scale);
stream_playbacks.push_back(new_playback);
setplay.set(-1);
}
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/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index 5bce705dd5..3637594e1b 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -163,8 +163,8 @@ void GPUParticles2D::set_trail_sections(int p_sections) {
}
void GPUParticles2D::set_trail_section_subdivisions(int p_subdivisions) {
- ERR_FAIL_COND(trail_section_subdivisions < 1);
- ERR_FAIL_COND(trail_section_subdivisions > 1024);
+ ERR_FAIL_COND(p_subdivisions < 1);
+ ERR_FAIL_COND(p_subdivisions > 1024);
trail_section_subdivisions = p_subdivisions;
update();
diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joint_2d.cpp
index 4a6606256e..8a528151cf 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"
@@ -74,6 +74,8 @@ void Joint2D::_update_joint(bool p_only_free) {
PhysicsBody2D *body_a = Object::cast_to<PhysicsBody2D>(node_a);
PhysicsBody2D *body_b = Object::cast_to<PhysicsBody2D>(node_b);
+ bool valid = false;
+
if (node_a && !body_a && node_b && !body_b) {
warning = TTR("Node A and Node B must be PhysicsBody2Ds");
} else if (node_a && !body_a) {
@@ -86,11 +88,12 @@ void Joint2D::_update_joint(bool p_only_free) {
warning = TTR("Node A and Node B must be different PhysicsBody2Ds");
} else {
warning = String();
+ valid = true;
}
update_configuration_warnings();
- if (!warning.is_empty()) {
+ if (!valid) {
PhysicsServer2D::get_singleton()->joint_clear(joint);
return;
}
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/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index 67e35cc7a3..797e2e59cb 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -123,12 +123,12 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, real_t p_s
Point2 new_ofs = (screen_offset + (p_offset - screen_offset) * motion_scale) + motion_offset * p_scale + orig_offset * p_scale;
if (mirroring.x) {
- double den = mirroring.x * p_scale;
+ real_t den = mirroring.x * p_scale;
new_ofs.x -= den * ceil(new_ofs.x / den);
}
if (mirroring.y) {
- double den = mirroring.y * p_scale;
+ real_t den = mirroring.y * p_scale;
new_ofs.y -= den * ceil(new_ofs.y / den);
}
diff --git a/scene/2d/physical_bone_2d.cpp b/scene/2d/physical_bone_2d.cpp
index c4b2608812..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: {
@@ -150,27 +152,15 @@ void PhysicalBone2D::_start_physics_simulation() {
return;
}
- // Reset to Bone2D position
+ // Reset to Bone2D position.
_position_at_bone2d();
- // Apply the layers and masks
+ // Apply the layers and masks.
PhysicsServer2D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer());
PhysicsServer2D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask());
- // Apply the correct mode
- RigidDynamicBody2D::Mode rigid_mode = get_mode();
- if (rigid_mode == RigidDynamicBody2D::MODE_STATIC) {
- set_body_mode(PhysicsServer2D::BODY_MODE_STATIC);
- } else if (rigid_mode == RigidDynamicBody2D::MODE_DYNAMIC) {
- set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC);
- } else if (rigid_mode == RigidDynamicBody2D::MODE_KINEMATIC) {
- set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC);
- } else if (rigid_mode == RigidDynamicBody2D::MODE_DYNAMIC_LOCKED) {
- set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED);
- } else {
- // Default to Dynamic.
- set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC);
- }
+ // Apply the correct mode.
+ _apply_body_mode();
_internal_simulate_physics = true;
set_physics_process_internal(true);
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 1ca5a9c0b6..41288d646f 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -34,8 +34,8 @@
#include "scene/scene_string_names.h"
void PhysicsBody2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08));
- ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08));
+ ClassDB::bind_method(D_METHOD("move_and_collide", "linear_velocity", "test_only", "safe_margin"), &PhysicsBody2D::_move, DEFVAL(false), DEFVAL(0.08));
+ ClassDB::bind_method(D_METHOD("test_move", "from", "linear_velocity", "collision", "safe_margin"), &PhysicsBody2D::test_move, DEFVAL(Variant()), DEFVAL(0.08));
ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody2D::get_collision_exceptions);
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody2D::add_collision_exception_with);
@@ -54,10 +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, 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();
@@ -71,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) {
@@ -90,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;
}
}
@@ -99,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.
@@ -108,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);
}
@@ -124,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;
@@ -133,7 +138,12 @@ bool PhysicsBody2D::test_move(const Transform2D &p_from, const Vector2 &p_motion
r = const_cast<PhysicsServer2D::MotionResult *>(&r_collision->result);
}
- return PhysicsServer2D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_margin, r);
+ // 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();
+
+ 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() {
@@ -282,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: {
@@ -307,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) {
@@ -430,7 +445,7 @@ void RigidDynamicBody2D::_body_state_changed_callback(void *p_instance, PhysicsD
void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) {
set_block_transform_notify(true); // don't want notify (would feedback loop)
- if (mode != MODE_KINEMATIC) {
+ if (!freeze || freeze_mode != FREEZE_MODE_KINEMATIC) {
set_global_transform(p_state->get_transform());
}
@@ -451,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++;
}
}
@@ -497,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++;
}
}
@@ -524,29 +539,60 @@ void RigidDynamicBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state)
}
}
-void RigidDynamicBody2D::set_mode(Mode p_mode) {
- mode = p_mode;
- switch (p_mode) {
- case MODE_DYNAMIC: {
- set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC);
- } break;
- case MODE_STATIC: {
- set_body_mode(PhysicsServer2D::BODY_MODE_STATIC);
+void RigidDynamicBody2D::_apply_body_mode() {
+ if (freeze) {
+ switch (freeze_mode) {
+ case FREEZE_MODE_STATIC: {
+ set_body_mode(PhysicsServer2D::BODY_MODE_STATIC);
+ } break;
+ case FREEZE_MODE_KINEMATIC: {
+ set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC);
+ } break;
+ }
+ } else if (lock_rotation) {
+ set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LINEAR);
+ } else {
+ set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC);
+ }
+}
- } break;
- case MODE_KINEMATIC: {
- set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC);
+void RigidDynamicBody2D::set_lock_rotation_enabled(bool p_lock_rotation) {
+ if (p_lock_rotation == lock_rotation) {
+ return;
+ }
- } break;
- case MODE_DYNAMIC_LOCKED: {
- set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED);
+ lock_rotation = p_lock_rotation;
+ _apply_body_mode();
+}
- } break;
+bool RigidDynamicBody2D::is_lock_rotation_enabled() const {
+ return lock_rotation;
+}
+
+void RigidDynamicBody2D::set_freeze_enabled(bool p_freeze) {
+ if (p_freeze == freeze) {
+ return;
+ }
+
+ freeze = p_freeze;
+ _apply_body_mode();
+}
+
+bool RigidDynamicBody2D::is_freeze_enabled() const {
+ return freeze;
+}
+
+void RigidDynamicBody2D::set_freeze_mode(FreezeMode p_freeze_mode) {
+ if (p_freeze_mode == freeze_mode) {
+ return;
}
+
+ freeze_mode = p_freeze_mode;
+ _apply_body_mode();
}
-RigidDynamicBody2D::Mode RigidDynamicBody2D::get_mode() const {
- return mode;
+RigidDynamicBody2D::FreezeMode RigidDynamicBody2D::get_freeze_mode() const {
+ return freeze_mode;
}
void RigidDynamicBody2D::set_mass(real_t p_mass) {
@@ -778,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 {
@@ -798,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) {
@@ -844,17 +890,14 @@ TypedArray<String> RigidDynamicBody2D::get_configuration_warnings() const {
TypedArray<String> warnings = CollisionObject2D::get_configuration_warnings();
- if ((get_mode() == MODE_DYNAMIC || get_mode() == MODE_DYNAMIC_LOCKED) && (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05)) {
- warnings.push_back(TTR("Size changes to RigidDynamicBody2D (in dynamic modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
+ if (ABS(t.elements[0].length() - 1.0) > 0.05 || ABS(t.elements[1].length() - 1.0) > 0.05) {
+ warnings.push_back(TTR("Size changes to RigidDynamicBody2D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
}
return warnings;
}
void RigidDynamicBody2D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidDynamicBody2D::set_mode);
- ClassDB::bind_method(D_METHOD("get_mode"), &RigidDynamicBody2D::get_mode);
-
ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody2D::set_mass);
ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody2D::get_mass);
@@ -918,11 +961,19 @@ void RigidDynamicBody2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidDynamicBody2D::set_can_sleep);
ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidDynamicBody2D::is_able_to_sleep);
+ ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidDynamicBody2D::set_lock_rotation_enabled);
+ ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidDynamicBody2D::is_lock_rotation_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidDynamicBody2D::set_freeze_enabled);
+ ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidDynamicBody2D::is_freeze_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidDynamicBody2D::set_freeze_mode);
+ ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidDynamicBody2D::get_freeze_mode);
+
ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidDynamicBody2D::get_colliding_bodies);
GDVIRTUAL_BIND(_integrate_forces, "state");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia");
ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode");
@@ -936,6 +987,9 @@ void RigidDynamicBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lock_rotation"), "set_lock_rotation_enabled", "is_lock_rotation_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "freeze"), "set_freeze_enabled", "is_freeze_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode");
ADD_GROUP("Linear", "linear_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
@@ -952,10 +1006,8 @@ void RigidDynamicBody2D::_bind_methods() {
ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("sleeping_state_changed"));
- BIND_ENUM_CONSTANT(MODE_DYNAMIC);
- BIND_ENUM_CONSTANT(MODE_STATIC);
- BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED);
- BIND_ENUM_CONSTANT(MODE_KINEMATIC);
+ BIND_ENUM_CONSTANT(FREEZE_MODE_STATIC);
+ BIND_ENUM_CONSTANT(FREEZE_MODE_KINEMATIC);
BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO);
BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM);
@@ -1004,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;
@@ -1016,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);
}
@@ -1026,6 +1079,7 @@ bool CharacterBody2D::move_and_slide() {
}
motion_results.clear();
+ last_motion = Vector2();
bool was_on_floor = on_floor;
on_floor = false;
@@ -1033,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);
}
@@ -1048,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();
@@ -1073,30 +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) {
- gt.elements[2] -= result.travel.slide(up_direction);
- } else {
+ 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;
}
@@ -1111,7 +1196,7 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
// Avoid to move forward on a wall if floor_block_on_wall is true.
if (p_was_on_floor && !on_floor && !vel_dir_facing_up) {
// If the movement is large the body can be prevented from reaching the walls.
- if (result.travel.length() <= margin) {
+ if (result.travel.length() <= margin + CMP_EPSILON) {
// Cancels the motion.
Transform2D gt = get_global_transform();
gt.elements[2] -= result.travel;
@@ -1119,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;
}
@@ -1141,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();
@@ -1151,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);
}
}
}
@@ -1162,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);
}
}
@@ -1196,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();
@@ -1226,28 +1322,32 @@ 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;
}
}
void CharacterBody2D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up) {
- if (Math::is_equal_approx(floor_snap_length, 0) || on_floor || !was_on_floor || vel_dir_facing_up) {
+ if (on_floor || !was_on_floor || vel_dir_facing_up) {
return;
}
- Transform2D gt = get_global_transform();
+ // 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 * -floor_snap_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;
@@ -1262,24 +1362,26 @@ 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);
}
}
}
bool CharacterBody2D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) {
- if (Math::is_equal_approx(floor_snap_length, 0) || up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) {
+ if (up_direction == Vector2() || on_floor || !was_on_floor || vel_dir_facing_up) {
return false;
}
+ // 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 * -floor_snap_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;
}
@@ -1297,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);
@@ -1306,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 {
@@ -1342,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;
}
@@ -1451,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;
}
@@ -1500,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();
@@ -1511,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);
@@ -1542,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);
@@ -1550,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);
@@ -1557,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_");
@@ -1568,14 +1704,19 @@ void CharacterBody2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_constant_speed"), "set_floor_constant_speed_enabled", "is_floor_constant_speed_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_block_on_wall"), "set_floor_block_on_wall_enabled", "is_floor_block_on_wall_enabled");
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,1000,0.1"), "set_floor_snap_length", "get_floor_snap_length");
+ 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 {
@@ -1670,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);
@@ -1687,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 5d0d98a2df..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
@@ -92,7 +92,7 @@ class AnimatableBody2D : public StaticBody2D {
GDCLASS(AnimatableBody2D, StaticBody2D);
private:
- bool sync_to_physics = false;
+ bool sync_to_physics = true;
Transform2D last_valid_transform;
@@ -117,11 +117,9 @@ class RigidDynamicBody2D : public PhysicsBody2D {
GDCLASS(RigidDynamicBody2D, PhysicsBody2D);
public:
- enum Mode {
- MODE_DYNAMIC,
- MODE_STATIC,
- MODE_DYNAMIC_LOCKED,
- MODE_KINEMATIC,
+ enum FreezeMode {
+ FREEZE_MODE_STATIC,
+ FREEZE_MODE_KINEMATIC,
};
enum CenterOfMassMode {
@@ -137,7 +135,9 @@ public:
private:
bool can_sleep = true;
- Mode mode = MODE_DYNAMIC;
+ bool lock_rotation = false;
+ bool freeze = false;
+ FreezeMode freeze_mode = FREEZE_MODE_STATIC;
real_t mass = 1.0;
real_t inertia = 0.0;
@@ -211,9 +211,17 @@ protected:
GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState2D *)
+ void _apply_body_mode();
+
public:
- void set_mode(Mode p_mode);
- Mode get_mode() const;
+ void set_lock_rotation_enabled(bool p_lock_rotation);
+ bool is_lock_rotation_enabled() const;
+
+ void set_freeze_enabled(bool p_freeze);
+ bool is_freeze_enabled() const;
+
+ void set_freeze_mode(FreezeMode p_freeze_mode);
+ FreezeMode get_freeze_mode() const;
void set_mass(real_t p_mass);
real_t get_mass() const;
@@ -290,7 +298,7 @@ private:
void _reload_physics_characteristics();
};
-VARIANT_ENUM_CAST(RigidDynamicBody2D::Mode);
+VARIANT_ENUM_CAST(RigidDynamicBody2D::FreezeMode);
VARIANT_ENUM_CAST(RigidDynamicBody2D::CenterOfMassMode);
VARIANT_ENUM_CAST(RigidDynamicBody2D::CCDMode);
@@ -302,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;
@@ -313,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;
@@ -326,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_stop_on_slope = false;
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 = 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;
@@ -387,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);
@@ -406,6 +434,7 @@ protected:
};
VARIANT_ENUM_CAST(CharacterBody2D::MotionMode);
+VARIANT_ENUM_CAST(CharacterBody2D::MovingPlatformApplyVelocityOnLeave);
class KinematicCollision2D : public RefCounted {
GDCLASS(KinematicCollision2D, RefCounted);
@@ -431,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 03db9c0d32..c2f150ce00 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));
};
@@ -409,6 +409,19 @@ bool TileMap::is_layer_enabled(int p_layer) const {
return layers[p_layer].enabled;
}
+void TileMap::set_layer_modulate(int p_layer, Color p_modulate) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ layers[p_layer].modulate = p_modulate;
+ _clear_internals();
+ _recreate_internals();
+ emit_signal(SNAME("changed"));
+}
+
+Color TileMap::get_layer_modulate(int p_layer) const {
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Color());
+ return layers[p_layer].modulate;
+}
+
void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) {
ERR_FAIL_INDEX(p_layer, (int)layers.size());
layers[p_layer].y_sort_enabled = p_y_sort_enabled;
@@ -452,6 +465,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 +534,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 +551,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 +635,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 +644,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 +713,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 +742,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 +761,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);
}
@@ -786,8 +811,9 @@ void TileMap::_rendering_cleanup_layer(int p_layer) {
ERR_FAIL_INDEX(p_layer, (int)layers.size());
RenderingServer *rs = RenderingServer::get_singleton();
- if (!layers[p_layer].canvas_item.is_valid()) {
+ if (layers[p_layer].canvas_item.is_valid()) {
rs->free(layers[p_layer].canvas_item);
+ layers[p_layer].canvas_item = RID();
}
}
@@ -820,9 +846,22 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
int prev_z_index = 0;
RID prev_canvas_item;
+ Color modulate = get_self_modulate();
+ modulate *= get_layer_modulate(q.layer);
+ if (selected_layer >= 0) {
+ int z1 = get_layer_z_index(q.layer);
+ int z2 = get_layer_z_index(selected_layer);
+ if (z1 < z2 || (z1 == z2 && q.layer < selected_layer)) {
+ modulate = modulate.darkened(0.5);
+ } else if (z1 > z2 || (z1 == z2 && q.layer > selected_layer)) {
+ modulate = modulate.darkened(0.5);
+ modulate.a *= 0.3;
+ }
+ }
+
// 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)) {
@@ -882,21 +921,12 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
}
// Drawing the tile in the canvas item.
- Color modulate = get_self_modulate();
- if (selected_layer >= 0) {
- if (q.layer < selected_layer) {
- modulate = modulate.darkened(0.5);
- } else if (q.layer > selected_layer) {
- modulate = modulate.darkened(0.5);
- 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 +952,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 +1122,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 +1193,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 +1225,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 +1283,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 +1303,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 +1317,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);
-
- 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;
- }
+ Vector<Color> color;
+ color.push_back(debug_collision_color);
- 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 +1353,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 +1391,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 +1444,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 +1547,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 +1595,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 +1898,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 +2035,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;
}
@@ -2034,16 +2079,31 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
return false;
} else if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) {
int index = components[0].trim_prefix("layer_").to_int();
- if (index < 0 || index >= (int)layers.size()) {
+ if (index < 0) {
return false;
}
+ if (index >= (int)layers.size()) {
+ _clear_internals();
+ while (index >= (int)layers.size()) {
+ layers.push_back(TileMapLayer());
+ }
+ _recreate_internals();
+
+ notify_property_list_changed();
+ emit_signal(SNAME("changed"));
+ update_configuration_warnings();
+ }
+
if (components[1] == "name") {
set_layer_name(index, p_value);
return true;
} else if (components[1] == "enabled") {
set_layer_enabled(index, p_value);
return true;
+ } else if (components[1] == "modulate") {
+ set_layer_modulate(index, p_value);
+ return true;
} else if (components[1] == "y_sort_enabled") {
set_layer_y_sort_enabled(index, p_value);
return true;
@@ -2080,6 +2140,9 @@ bool TileMap::_get(const StringName &p_name, Variant &r_ret) const {
} else if (components[1] == "enabled") {
r_ret = is_layer_enabled(index);
return true;
+ } else if (components[1] == "modulate") {
+ r_ret = get_layer_modulate(index);
+ return true;
} else if (components[1] == "y_sort_enabled") {
r_ret = is_layer_y_sort_enabled(index);
return true;
@@ -2105,6 +2168,7 @@ void TileMap::_get_property_list(List<PropertyInfo> *p_list) const {
for (unsigned int i = 0; i < layers.size(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING, vformat("layer_%d/name", i), PROPERTY_HINT_NONE));
p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/enabled", i), PROPERTY_HINT_NONE));
+ p_list->push_back(PropertyInfo(Variant::COLOR, vformat("layer_%d/modulate", i), PROPERTY_HINT_NONE));
p_list->push_back(PropertyInfo(Variant::BOOL, vformat("layer_%d/y_sort_enabled", i), PROPERTY_HINT_NONE));
p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/y_sort_origin", i), PROPERTY_HINT_NONE));
p_list->push_back(PropertyInfo(Variant::INT, vformat("layer_%d/z_index", i), PROPERTY_HINT_NONE));
@@ -2739,8 +2803,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;
}
@@ -2761,8 +2825,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));
}
}
}
@@ -2782,8 +2846,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());
}
}
@@ -2797,8 +2861,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());
}
@@ -2813,8 +2877,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());
}
@@ -2988,6 +3052,8 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_layer_name", "layer"), &TileMap::get_layer_name);
ClassDB::bind_method(D_METHOD("set_layer_enabled", "layer", "enabled"), &TileMap::set_layer_enabled);
ClassDB::bind_method(D_METHOD("is_layer_enabled", "layer"), &TileMap::is_layer_enabled);
+ ClassDB::bind_method(D_METHOD("set_layer_modulate", "layer", "enabled"), &TileMap::set_layer_modulate);
+ ClassDB::bind_method(D_METHOD("get_layer_modulate", "layer"), &TileMap::get_layer_modulate);
ClassDB::bind_method(D_METHOD("set_layer_y_sort_enabled", "layer", "y_sort_enabled"), &TileMap::set_layer_y_sort_enabled);
ClassDB::bind_method(D_METHOD("is_layer_y_sort_enabled", "layer"), &TileMap::is_layer_y_sort_enabled);
ClassDB::bind_method(D_METHOD("set_layer_y_sort_origin", "layer", "y_sort_origin"), &TileMap::set_layer_y_sort_origin);
@@ -2995,6 +3061,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);
@@ -3006,10 +3074,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);
@@ -3023,8 +3095,11 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_tile_data", "layer"), &TileMap::_set_tile_data);
ClassDB::bind_method(D_METHOD("_get_tile_data", "layer"), &TileMap::_get_tile_data);
+ ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileMap::_tile_set_changed_deferred_update);
+
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");
@@ -3041,8 +3116,16 @@ void TileMap::_bind_methods() {
void TileMap::_tile_set_changed() {
emit_signal(SNAME("changed"));
- _clear_internals();
- _recreate_internals();
+ _tile_set_changed_deferred_update_needed = true;
+ call_deferred("_tile_set_changed_deferred_update");
+}
+
+void TileMap::_tile_set_changed_deferred_update() {
+ if (_tile_set_changed_deferred_update_needed) {
+ _clear_internals();
+ _recreate_internals();
+ _tile_set_changed_deferred_update_needed = false;
+ }
}
TileMap::TileMap() {
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index ca38baa550..e5809deabb 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;
@@ -218,6 +219,7 @@ private:
struct TileMapLayer {
String name;
bool enabled = true;
+ Color modulate = Color(1, 1, 1, 1);
bool y_sort_enabled = false;
int y_sort_origin = 0;
int z_index = 0;
@@ -229,6 +231,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 +264,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);
@@ -279,6 +285,8 @@ private:
Vector<int> _get_tile_data(int p_layer) const;
void _tile_set_changed();
+ bool _tile_set_changed_deferred_update_needed = false;
+ void _tile_set_changed_deferred_update();
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -316,6 +324,8 @@ public:
String get_layer_name(int p_layer) const;
void set_layer_enabled(int p_layer, bool p_visible);
bool is_layer_enabled(int p_layer) const;
+ void set_layer_modulate(int p_layer, Color p_modulate);
+ Color get_layer_modulate(int p_layer) const;
void set_layer_y_sort_enabled(int p_layer, bool p_enabled);
bool is_layer_y_sort_enabled(int p_layer) const;
void set_layer_y_sort_origin(int p_layer, int p_y_sort_origin);
@@ -325,6 +335,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 +377,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 8f4bb67a42..d411525707 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/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index a54b10ba70..c422070480 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -282,7 +282,7 @@ void AudioStreamPlayer3D::_notification(int p_what) {
ERR_FAIL_COND_MSG(new_playback.is_null(), "Failed to instantiate playback.");
Map<StringName, Vector<AudioFrame>> bus_map;
bus_map[_get_actual_bus()] = volume_vector;
- AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), linear_attenuation, attenuation_filter_cutoff_hz, actual_pitch_scale);
+ AudioServer::get_singleton()->start_playback_stream(new_playback, bus_map, setplay.get(), actual_pitch_scale, linear_attenuation, attenuation_filter_cutoff_hz);
stream_playbacks.push_back(new_playback);
setplay.set(-1);
}
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index c34c150145..8e89f4fc54 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -110,7 +110,7 @@ TypedArray<String> BoneAttachment3D::get_configuration_warnings() const {
} else {
Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent());
if (!parent) {
- warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an extenral Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node."));
+ warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an external Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node."));
}
}
@@ -215,8 +215,6 @@ void BoneAttachment3D::_transform_changed() {
sk->set_bone_global_pose_override(bone_idx, our_trans, 1.0, true);
} else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) {
sk->set_bone_local_pose_override(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans), 1.0, true);
- } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) {
- sk->set_bone_custom_pose(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans));
}
}
}
@@ -273,8 +271,6 @@ void BoneAttachment3D::set_override_pose(bool p_override) {
sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false);
} else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) {
sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false);
- } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) {
- sk->set_bone_custom_pose(bone_idx, Transform3D());
}
}
_transform_changed();
@@ -294,8 +290,6 @@ void BoneAttachment3D::set_override_mode(int p_mode) {
sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false);
} else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) {
sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false);
- } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) {
- sk->set_bone_custom_pose(bone_idx, Transform3D());
}
}
override_mode = p_mode;
diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h
index cf681cace8..57b9854e0e 100644
--- a/scene/3d/bone_attachment_3d.h
+++ b/scene/3d/bone_attachment_3d.h
@@ -47,7 +47,6 @@ class BoneAttachment3D : public Node3D {
enum OVERRIDE_MODES {
MODE_GLOBAL_POSE,
MODE_LOCAL_POSE,
- MODE_CUSTOM_POSE
};
bool use_external_skeleton = false;
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index 9aad338d15..588d2b5018 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -238,8 +238,8 @@ void Camera3D::clear_current(bool p_enable_next) {
}
}
-void Camera3D::set_current(bool p_current) {
- if (p_current) {
+void Camera3D::set_current(bool p_enabled) {
+ if (p_enabled) {
make_current();
} else {
clear_current();
@@ -460,7 +460,7 @@ void Camera3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_frustum", "size", "offset", "z_near", "z_far"), &Camera3D::set_frustum);
ClassDB::bind_method(D_METHOD("make_current"), &Camera3D::make_current);
ClassDB::bind_method(D_METHOD("clear_current", "enable_next"), &Camera3D::clear_current, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("set_current"), &Camera3D::set_current);
+ ClassDB::bind_method(D_METHOD("set_current", "enabled"), &Camera3D::set_current);
ClassDB::bind_method(D_METHOD("is_current"), &Camera3D::is_current);
ClassDB::bind_method(D_METHOD("get_camera_transform"), &Camera3D::get_camera_transform);
ClassDB::bind_method(D_METHOD("get_fov"), &Camera3D::get_fov);
@@ -468,13 +468,13 @@ void Camera3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_size"), &Camera3D::get_size);
ClassDB::bind_method(D_METHOD("get_far"), &Camera3D::get_far);
ClassDB::bind_method(D_METHOD("get_near"), &Camera3D::get_near);
- ClassDB::bind_method(D_METHOD("set_fov"), &Camera3D::set_fov);
- ClassDB::bind_method(D_METHOD("set_frustum_offset"), &Camera3D::set_frustum_offset);
- ClassDB::bind_method(D_METHOD("set_size"), &Camera3D::set_size);
- ClassDB::bind_method(D_METHOD("set_far"), &Camera3D::set_far);
- ClassDB::bind_method(D_METHOD("set_near"), &Camera3D::set_near);
+ ClassDB::bind_method(D_METHOD("set_fov", "fov"), &Camera3D::set_fov);
+ ClassDB::bind_method(D_METHOD("set_frustum_offset", "offset"), &Camera3D::set_frustum_offset);
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &Camera3D::set_size);
+ ClassDB::bind_method(D_METHOD("set_far", "far"), &Camera3D::set_far);
+ ClassDB::bind_method(D_METHOD("set_near", "near"), &Camera3D::set_near);
ClassDB::bind_method(D_METHOD("get_projection"), &Camera3D::get_projection);
- ClassDB::bind_method(D_METHOD("set_projection"), &Camera3D::set_projection);
+ ClassDB::bind_method(D_METHOD("set_projection", "mode"), &Camera3D::set_projection);
ClassDB::bind_method(D_METHOD("set_h_offset", "ofs"), &Camera3D::set_h_offset);
ClassDB::bind_method(D_METHOD("get_h_offset"), &Camera3D::get_h_offset);
ClassDB::bind_method(D_METHOD("set_v_offset", "ofs"), &Camera3D::set_v_offset);
@@ -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,47 @@ 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() {
+ ERR_FAIL_COND_V_MSG(!is_inside_tree(), RID(), "Camera is not inside scene.");
+ 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..73126611d5 100644
--- a/scene/3d/camera_3d.h
+++ b/scene/3d/camera_3d.h
@@ -44,8 +44,10 @@ public:
PROJECTION_FRUSTUM
};
- enum KeepAspect { KEEP_WIDTH,
- KEEP_HEIGHT };
+ enum KeepAspect {
+ KEEP_WIDTH,
+ KEEP_HEIGHT
+ };
enum DopplerTracking {
DOPPLER_TRACKING_DISABLED,
@@ -86,6 +88,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();
@@ -109,7 +114,7 @@ public:
void make_current();
void clear_current(bool p_enable_next = true);
- void set_current(bool p_current);
+ void set_current(bool p_enabled);
bool is_current() const;
RID get_camera() const;
@@ -168,6 +173,8 @@ public:
Vector3 get_doppler_tracked_velocity() const;
+ RID get_pyramid_shape_rid();
+
Camera3D();
~Camera3D();
};
@@ -176,63 +183,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/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp
index c79f956642..4e496fba47 100644
--- a/scene/3d/collision_shape_3d.cpp
+++ b/scene/3d/collision_shape_3d.cpp
@@ -124,8 +124,7 @@ TypedArray<String> CollisionShape3D::get_configuration_warnings() const {
if (shape.is_valid() &&
Object::cast_to<RigidDynamicBody3D>(get_parent()) &&
- Object::cast_to<ConcavePolygonShape3D>(*shape) &&
- Object::cast_to<RigidDynamicBody3D>(get_parent())->get_mode() != RigidDynamicBody3D::MODE_STATIC) {
+ Object::cast_to<ConcavePolygonShape3D>(*shape)) {
warnings.push_back(TTR("ConcavePolygonShape3D doesn't support RigidDynamicBody3D in another mode than static."));
}
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index baf28ae102..32a62d8c7e 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -469,7 +469,7 @@ void GPUParticles3D::_skinning_changed() {
if (draw_pass.is_valid() && draw_pass->get_builtin_bind_pose_count() > 0) {
xforms.resize(draw_pass->get_builtin_bind_pose_count());
for (int j = 0; j < draw_pass->get_builtin_bind_pose_count(); j++) {
- xforms.write[i] = draw_pass->get_builtin_bind_pose(j);
+ xforms.write[j] = draw_pass->get_builtin_bind_pose(j);
}
break;
}
diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp
index 4fa34615bf..9127168c58 100644
--- a/scene/3d/gpu_particles_collision_3d.cpp
+++ b/scene/3d/gpu_particles_collision_3d.cpp
@@ -475,7 +475,7 @@ Ref<Image> GPUParticlesCollisionSDF::bake() {
_create_bvh(bvh, face_pos.ptr(), face_pos.size(), faces.ptr(), th);
Vector<uint8_t> data;
- data.resize(sdf_size.z * sdf_size.y * sdf_size.x * sizeof(float));
+ data.resize(sdf_size.z * sdf_size.y * sdf_size.x * (int)sizeof(float));
if (bake_step_function) {
bake_step_function(0, "Baking SDF");
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/animation/animation_cache.h b/scene/3d/importer_mesh_instance_3d.h
index c856e644f7..0cf7dbe86b 100644
--- a/scene/animation/animation_cache.h
+++ b/scene/3d/importer_mesh_instance_3d.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* animation_cache.h */
+/* importer_mesh_instance_3d.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,57 +28,37 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef ANIMATION_CACHE_H
-#define ANIMATION_CACHE_H
+#ifndef SCENE_IMPORTER_MESH_INSTANCE_3D_H
+#define SCENE_IMPORTER_MESH_INSTANCE_3D_H
-#include "scene/3d/skeleton_3d.h"
-#include "scene/resources/animation.h"
+#include "scene/3d/node_3d.h"
+#include "scene/resources/immediate_mesh.h"
+#include "scene/resources/skin.h"
-class AnimationCache : public Object {
- GDCLASS(AnimationCache, Object);
+class ImporterMesh;
- struct Path {
- RES resource;
- Object *object = nullptr;
-#ifndef _3D_DISABLED
- Skeleton3D *skeleton = nullptr;
- Node3D *node_3d = nullptr;
-#endif // _3D_DISABLED
- Node *node = nullptr;
+class ImporterMeshInstance3D : public Node3D {
+ GDCLASS(ImporterMeshInstance3D, Node3D)
- int bone_idx = -1;
- Vector<StringName> subpath;
- bool valid = false;
- };
-
- Set<Node *> connected_nodes;
- Vector<Path> path_cache;
-
- Node *root = nullptr;
- Ref<Animation> animation;
- bool cache_dirty = true;
- bool cache_valid = false;
-
- void _node_exit_tree(Node *p_node);
-
- void _clear_cache();
- void _update_cache();
- void _animation_changed();
+ Ref<ImporterMesh> mesh;
+ Ref<Skin> skin;
+ NodePath skeleton_path;
+ Vector<Ref<Material>> surface_materials;
protected:
static void _bind_methods();
public:
- void set_track_transform(int p_idx, const Transform3D &p_transform);
- void set_track_value(int p_idx, const Variant &p_value);
- void call_track(int p_idx, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ void set_mesh(const Ref<ImporterMesh> &p_mesh);
+ Ref<ImporterMesh> get_mesh() const;
- void set_all(float p_time, float p_delta = 0);
+ void set_skin(const Ref<Skin> &p_skin);
+ Ref<Skin> get_skin() const;
- void set_animation(const Ref<Animation> &p_animation);
- void set_root(Node *p_root);
+ void set_surface_material(int p_idx, const Ref<Material> &p_material);
+ Ref<Material> get_surface_material(int p_idx) const;
- AnimationCache();
+ void set_skeleton_path(const NodePath &p_path);
+ NodePath get_skeleton_path() const;
};
-
-#endif // ANIMATION_CACHE_H
+#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..cfd90e5da0 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();
@@ -146,11 +146,13 @@ void MeshInstance3D::_resolve_skeleton_path() {
if (!skeleton_path.is_empty()) {
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(get_node(skeleton_path));
if (skeleton) {
- new_skin_reference = skeleton->register_skin(skin_internal);
if (skin_internal.is_null()) {
+ new_skin_reference = skeleton->register_skin(skeleton->create_skin_from_rest_transforms());
//a skin was created for us
skin_internal = new_skin_reference->get_skin();
notify_property_list_changed();
+ } else {
+ new_skin_reference = skeleton->register_skin(skin_internal);
}
}
}
@@ -369,6 +371,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/node_3d.cpp b/scene/3d/node_3d.cpp
index 12470939f5..272a06bce4 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -376,6 +376,18 @@ void Node3D::update_gizmos() {
#endif
}
+void Node3D::set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform) {
+#ifdef TOOLS_ENABLED
+ if (!is_inside_world()) {
+ return;
+ }
+
+ if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) {
+ get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_set_subgizmo_selection, this, p_gizmo, p_id, p_transform);
+ }
+#endif
+}
+
void Node3D::clear_subgizmo_selection() {
#ifdef TOOLS_ENABLED
if (!is_inside_world()) {
@@ -792,6 +804,7 @@ void Node3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_gizmo", "gizmo"), &Node3D::add_gizmo);
ClassDB::bind_method(D_METHOD("get_gizmos"), &Node3D::get_gizmos_bind);
ClassDB::bind_method(D_METHOD("clear_gizmos"), &Node3D::clear_gizmos);
+ ClassDB::bind_method(D_METHOD("set_subgizmo_selection", "gizmo", "id", "transform"), &Node3D::set_subgizmo_selection);
ClassDB::bind_method(D_METHOD("clear_subgizmo_selection"), &Node3D::clear_subgizmo_selection);
ClassDB::bind_method(D_METHOD("set_visible", "visible"), &Node3D::set_visible);
diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h
index 0fd0c4e205..d6dcdd96fe 100644
--- a/scene/3d/node_3d.h
+++ b/scene/3d/node_3d.h
@@ -92,6 +92,7 @@ class Node3D : public Node {
Vector<Ref<Node3DGizmo>> gizmos;
bool gizmos_disabled = false;
bool gizmos_dirty = false;
+ bool transform_gizmo_visible = true;
#endif
} data;
@@ -145,6 +146,8 @@ public:
#ifdef TOOLS_ENABLED
virtual Transform3D get_global_gizmo_transform() const;
virtual Transform3D get_local_gizmo_transform() const;
+ virtual void set_transform_gizmo_visible(bool p_enabled) { data.transform_gizmo_visible = p_enabled; };
+ virtual bool is_transform_gizmo_visible() const { return data.transform_gizmo_visible; };
#endif
void set_as_top_level(bool p_enabled);
@@ -155,6 +158,7 @@ public:
void set_disable_gizmos(bool p_enabled);
void update_gizmos();
+ void set_subgizmo_selection(Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform = Transform3D());
void clear_subgizmo_selection();
Vector<Ref<Node3DGizmo>> get_gizmos() const;
Array get_gizmos_bind() const;
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 918d1bb61f..8cb348d6e3 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -33,13 +33,9 @@
#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", "rel_vec", "test_only", "safe_margin", "max_collisions"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001), DEFVAL(1));
- ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "collision", "safe_margin", "max_collisions"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001), DEFVAL(1));
+ 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));
ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &PhysicsBody3D::set_axis_lock);
ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &PhysicsBody3D::get_axis_lock);
@@ -95,9 +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) {
+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();
+
+ 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(p_motion, result, p_margin, p_test_only, p_max_collisions)) {
+ 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();
@@ -112,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;
}
}
@@ -137,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.
@@ -146,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;
}
}
}
@@ -161,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);
}
@@ -168,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;
@@ -177,7 +179,12 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion
r = const_cast<PhysicsServer3D::MotionResult *>(&r_collision->result);
}
- return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_margin, r, 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();
+
+ 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) {
@@ -334,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: {
@@ -361,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) {
@@ -506,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++;
}
}
@@ -554,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++;
}
}
@@ -599,27 +610,60 @@ void RigidDynamicBody3D::_notification(int p_what) {
#endif
}
-void RigidDynamicBody3D::set_mode(Mode p_mode) {
- mode = p_mode;
- switch (p_mode) {
- case MODE_DYNAMIC: {
- set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC);
- } break;
- case MODE_STATIC: {
- set_body_mode(PhysicsServer3D::BODY_MODE_STATIC);
- } break;
- case MODE_DYNAMIC_LOCKED: {
- set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED);
- } break;
- case MODE_KINEMATIC: {
- set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC);
- } break;
+void RigidDynamicBody3D::_apply_body_mode() {
+ if (freeze) {
+ switch (freeze_mode) {
+ case FREEZE_MODE_STATIC: {
+ set_body_mode(PhysicsServer3D::BODY_MODE_STATIC);
+ } break;
+ case FREEZE_MODE_KINEMATIC: {
+ set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC);
+ } break;
+ }
+ } else if (lock_rotation) {
+ set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC_LINEAR);
+ } else {
+ set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC);
}
- update_configuration_warnings();
}
-RigidDynamicBody3D::Mode RigidDynamicBody3D::get_mode() const {
- return mode;
+void RigidDynamicBody3D::set_lock_rotation_enabled(bool p_lock_rotation) {
+ if (p_lock_rotation == lock_rotation) {
+ return;
+ }
+
+ lock_rotation = p_lock_rotation;
+ _apply_body_mode();
+}
+
+bool RigidDynamicBody3D::is_lock_rotation_enabled() const {
+ return lock_rotation;
+}
+
+void RigidDynamicBody3D::set_freeze_enabled(bool p_freeze) {
+ if (p_freeze == freeze) {
+ return;
+ }
+
+ freeze = p_freeze;
+ _apply_body_mode();
+}
+
+bool RigidDynamicBody3D::is_freeze_enabled() const {
+ return freeze;
+}
+
+void RigidDynamicBody3D::set_freeze_mode(FreezeMode p_freeze_mode) {
+ if (p_freeze_mode == freeze_mode) {
+ return;
+ }
+
+ freeze_mode = p_freeze_mode;
+ _apply_body_mode();
+}
+
+RigidDynamicBody3D::FreezeMode RigidDynamicBody3D::get_freeze_mode() const {
+ return freeze_mode;
}
void RigidDynamicBody3D::set_mass(real_t p_mass) {
@@ -846,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) {
@@ -875,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 {
@@ -892,17 +936,14 @@ TypedArray<String> RigidDynamicBody3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
- if ((get_mode() == MODE_DYNAMIC || get_mode() == MODE_DYNAMIC_LOCKED) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) {
- warnings.push_back(TTR("Size changes to RigidDynamicBody (in dynamic modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
+ if (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05) {
+ warnings.push_back(TTR("Size changes to RigidDynamicBody will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."));
}
return warnings;
}
void RigidDynamicBody3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_mode", "mode"), &RigidDynamicBody3D::set_mode);
- ClassDB::bind_method(D_METHOD("get_mode"), &RigidDynamicBody3D::get_mode);
-
ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidDynamicBody3D::set_mass);
ClassDB::bind_method(D_METHOD("get_mass"), &RigidDynamicBody3D::get_mass);
@@ -963,11 +1004,19 @@ void RigidDynamicBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidDynamicBody3D::set_can_sleep);
ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidDynamicBody3D::is_able_to_sleep);
+ ClassDB::bind_method(D_METHOD("set_lock_rotation_enabled", "lock_rotation"), &RigidDynamicBody3D::set_lock_rotation_enabled);
+ ClassDB::bind_method(D_METHOD("is_lock_rotation_enabled"), &RigidDynamicBody3D::is_lock_rotation_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_freeze_enabled", "freeze_mode"), &RigidDynamicBody3D::set_freeze_enabled);
+ ClassDB::bind_method(D_METHOD("is_freeze_enabled"), &RigidDynamicBody3D::is_freeze_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_freeze_mode", "freeze_mode"), &RigidDynamicBody3D::set_freeze_mode);
+ ClassDB::bind_method(D_METHOD("get_freeze_mode"), &RigidDynamicBody3D::get_freeze_mode);
+
ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidDynamicBody3D::get_colliding_bodies);
GDVIRTUAL_BIND(_integrate_forces, "state");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,1000,0.01,or_greater,exp"), "set_mass", "get_mass");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "inertia", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater,exp"), "set_inertia", "get_inertia");
ADD_PROPERTY(PropertyInfo(Variant::INT, "center_of_mass_mode", PROPERTY_HINT_ENUM, "Auto,Custom", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_center_of_mass_mode", "get_center_of_mass_mode");
@@ -981,6 +1030,9 @@ void RigidDynamicBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "lock_rotation"), "set_lock_rotation_enabled", "is_lock_rotation_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "freeze"), "set_freeze_enabled", "is_freeze_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "freeze_mode", PROPERTY_HINT_ENUM, "Static,Kinematic"), "set_freeze_mode", "get_freeze_mode");
ADD_GROUP("Linear", "linear_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp");
@@ -994,10 +1046,8 @@ void RigidDynamicBody3D::_bind_methods() {
ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("sleeping_state_changed"));
- BIND_ENUM_CONSTANT(MODE_DYNAMIC);
- BIND_ENUM_CONSTANT(MODE_STATIC);
- BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED);
- BIND_ENUM_CONSTANT(MODE_KINEMATIC);
+ BIND_ENUM_CONSTANT(FREEZE_MODE_STATIC);
+ BIND_ENUM_CONSTANT(FREEZE_MODE_KINEMATIC);
BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_AUTO);
BIND_ENUM_CONSTANT(CENTER_OF_MASS_MODE_CUSTOM);
@@ -1040,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()) {
@@ -1060,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);
}
@@ -1074,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;
@@ -1101,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);
@@ -1144,21 +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();
- real_t travel_total = result.travel.length();
- if (travel_total <= margin + CMP_EPSILON) {
+ 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;
}
@@ -1186,7 +1261,7 @@ void CharacterBody3D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
Transform3D gt = get_global_transform();
real_t travel_total = result.travel.length();
real_t cancel_dist_max = MIN(0.1, margin * 20);
- if (travel_total < margin + CMP_EPSILON) {
+ if (travel_total <= margin + CMP_EPSILON) {
gt.origin -= result.travel;
} else if (travel_total < cancel_dist_max) { // If the movement is large the body can be prevented from reaching the walls.
gt.origin -= result.travel.slide(up_direction);
@@ -1207,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.
@@ -1236,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();
}
}
}
@@ -1247,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;
}
@@ -1256,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);
}
}
}
@@ -1278,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.
@@ -1300,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);
@@ -1345,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();
@@ -1359,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;
}
}
@@ -1377,10 +1476,15 @@ void CharacterBody3D::_snap_on_floor(bool was_on_floor, bool vel_dir_facing_up)
return;
}
+ // 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));
@@ -1396,19 +1500,26 @@ 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);
}
}
}
bool CharacterBody3D::_on_floor_if_snapped(bool was_on_floor, bool vel_dir_facing_up) {
- if (Math::is_zero_approx(floor_snap_length) || up_direction == Vector3() || collision_state.floor || !was_on_floor || vel_dir_facing_up) {
+ if (up_direction == Vector3() || collision_state.floor || !was_on_floor || vel_dir_facing_up) {
return false;
}
+ // 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 * floor_snap_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());
@@ -1452,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;
@@ -1503,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);
}
@@ -1515,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 {
@@ -1547,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;
}
@@ -1563,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();
}
@@ -1721,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;
@@ -1730,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);
@@ -1784,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");
@@ -1909,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);
@@ -1968,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");
}
///////////////////////////////////////
@@ -2932,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 96f3d7d747..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;
@@ -105,7 +105,7 @@ private:
Vector3 linear_velocity;
Vector3 angular_velocity;
- bool sync_to_physics = false;
+ bool sync_to_physics = true;
Transform3D last_valid_transform;
@@ -133,11 +133,9 @@ class RigidDynamicBody3D : public PhysicsBody3D {
GDCLASS(RigidDynamicBody3D, PhysicsBody3D);
public:
- enum Mode {
- MODE_DYNAMIC,
- MODE_STATIC,
- MODE_DYNAMIC_LOCKED,
- MODE_KINEMATIC,
+ enum FreezeMode {
+ FREEZE_MODE_STATIC,
+ FREEZE_MODE_KINEMATIC,
};
enum CenterOfMassMode {
@@ -145,11 +143,11 @@ public:
CENTER_OF_MASS_MODE_CUSTOM,
};
- GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *)
-
-protected:
+private:
bool can_sleep = true;
- Mode mode = MODE_DYNAMIC;
+ bool lock_rotation = false;
+ bool freeze = false;
+ FreezeMode freeze_mode = FREEZE_MODE_STATIC;
real_t mass = 1.0;
Vector3 inertia;
@@ -214,16 +212,28 @@ protected:
void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape);
static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state);
- virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state);
+protected:
void _notification(int p_what);
static void _bind_methods();
virtual void _validate_property(PropertyInfo &property) const override;
+ GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *)
+
+ virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state);
+
+ void _apply_body_mode();
+
public:
- void set_mode(Mode p_mode);
- Mode get_mode() const;
+ void set_lock_rotation_enabled(bool p_lock_rotation);
+ bool is_lock_rotation_enabled() const;
+
+ void set_freeze_enabled(bool p_freeze);
+ bool is_freeze_enabled() const;
+
+ void set_freeze_mode(FreezeMode p_freeze_mode);
+ FreezeMode get_freeze_mode() const;
void set_mass(real_t p_mass);
real_t get_mass() const;
@@ -298,7 +308,7 @@ private:
void _reload_physics_characteristics();
};
-VARIANT_ENUM_CAST(RigidDynamicBody3D::Mode);
+VARIANT_ENUM_CAST(RigidDynamicBody3D::FreezeMode);
VARIANT_ENUM_CAST(RigidDynamicBody3D::CenterOfMassMode);
class KinematicCollision3D;
@@ -318,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;
@@ -327,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;
@@ -364,24 +376,27 @@ private:
};
CollisionState collision_state;
- bool floor_stop_on_slope = false;
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 = 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;
@@ -473,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 {
@@ -652,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/proximity_group_3d.cpp b/scene/3d/proximity_group_3d.cpp
index c8c61a9f00..23df00c1f6 100644
--- a/scene/3d/proximity_group_3d.cpp
+++ b/scene/3d/proximity_group_3d.cpp
@@ -34,9 +34,9 @@
void ProximityGroup3D::_clear_groups() {
Map<StringName, uint32_t>::Element *E;
+ const int size = 16;
- {
- const int size = 16;
+ do {
StringName remove_list[size];
E = groups.front();
int num = 0;
@@ -50,11 +50,7 @@ void ProximityGroup3D::_clear_groups() {
for (int i = 0; i < num; i++) {
groups.erase(remove_list[i]);
}
- }
-
- if (E) {
- _clear_groups(); // call until we go through the whole list
- }
+ } while (E);
}
void ProximityGroup3D::_update_groups() {
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index b7a79a2645..14506fd9bd 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -32,6 +32,7 @@
#include "core/object/message_queue.h"
#include "core/variant/type_info.h"
+#include "editor/plugins/skeleton_3d_editor_plugin.h"
#include "scene/3d/physics_body_3d.h"
#include "scene/resources/skeleton_modification_3d.h"
#include "scene/resources/surface_tool.h"
@@ -98,8 +99,12 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
set_bone_rest(which, p_value);
} else if (what == "enabled") {
set_bone_enabled(which, p_value);
- } else if (what == "pose") {
- set_bone_pose(which, p_value);
+ } else if (what == "position") {
+ set_bone_pose_position(which, p_value);
+ } else if (what == "rotation") {
+ set_bone_pose_rotation(which, p_value);
+ } else if (what == "scale") {
+ set_bone_pose_scale(which, p_value);
} else {
return false;
}
@@ -134,8 +139,12 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
r_ret = get_bone_rest(which);
} else if (what == "enabled") {
r_ret = is_bone_enabled(which);
- } else if (what == "pose") {
- r_ret = get_bone_pose(which);
+ } else if (what == "position") {
+ r_ret = get_bone_pose_position(which);
+ } else if (what == "rotation") {
+ r_ret = get_bone_pose_rotation(which);
+ } else if (what == "scale") {
+ r_ret = get_bone_pose_scale(which);
} else {
return false;
}
@@ -150,7 +159,9 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
- p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
#ifndef _3D_DISABLED
@@ -160,6 +171,45 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
"SkeletonModificationStack3D",
PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE));
#endif //_3D_DISABLED
+
+ for (PropertyInfo &E : *p_list) {
+ _validate_property(E);
+ }
+}
+
+void Skeleton3D::_validate_property(PropertyInfo &property) const {
+ PackedStringArray spr = property.name.split("/");
+ if (spr.size() == 3 && spr[0] == "bones") {
+ if (spr[2] == "rest") {
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+ if (is_show_rest_only()) {
+ if (spr[2] == "enabled") {
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+ if (spr[2] == "position") {
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+ if (spr[2] == "rotation") {
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+ if (spr[2] == "scale") {
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+ } else if (!is_bone_enabled(spr[1].to_int())) {
+ if (spr[2] == "position") {
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+ if (spr[2] == "rotation") {
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+ if (spr[2] == "scale") {
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+ }
+ }
+
+ Node3D::_validate_property(property);
}
void Skeleton3D::_update_process_order() {
@@ -178,7 +228,7 @@ void Skeleton3D::_update_process_order() {
for (int i = 0; i < len; i++) {
if (bonesptr[i].parent >= len) {
- //validate this just in case
+ // Validate this just in case.
ERR_PRINT("Bone " + itos(i) + " has invalid parent: " + itos(bonesptr[i].parent));
bonesptr[i].parent = -1;
}
@@ -186,9 +236,9 @@ void Skeleton3D::_update_process_order() {
if (bonesptr[i].parent != -1) {
int parent_bone_idx = bonesptr[i].parent;
- // Check to see if this node is already added to the parent:
+ // Check to see if this node is already added to the parent.
if (bonesptr[parent_bone_idx].child_bones.find(i) < 0) {
- // Add the child node
+ // Add the child node.
bonesptr[parent_bone_idx].child_bones.push_back(i);
} else {
ERR_PRINT("Skeleton3D parenthood graph is cyclic");
@@ -210,10 +260,10 @@ void Skeleton3D::_notification(int p_what) {
int len = bones.size();
dirty = false;
- // Update bone transforms
+ // Update bone transforms.
force_update_all_bone_transforms();
- //update skins
+ // Update skins.
for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
const Skin *skin = E->get()->skin.operator->();
RID skeleton = E->get()->skeleton;
@@ -231,7 +281,7 @@ void Skeleton3D::_notification(int p_what) {
StringName bind_name = skin->get_bind_name(i);
if (bind_name != StringName()) {
- //bind name used, use this
+ // Bind name used, use this.
bool found = false;
for (int j = 0; j < len; j++) {
if (bonesptr[j].name == bind_name) {
@@ -453,7 +503,8 @@ int Skeleton3D::get_bone_axis_forward_enum(int p_bone) {
return bones[p_bone].rest_bone_forward_axis;
}
-// skeleton creation api
+// Skeleton creation api
+
void Skeleton3D::add_bone(const String &p_name) {
ERR_FAIL_COND(p_name == "" || p_name.find(":") != -1 || p_name.find("/") != -1);
@@ -545,18 +596,6 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) {
_make_dirty();
}
-void Skeleton3D::set_bone_disable_rest(int p_bone, bool p_disable) {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].disable_rest = p_disable;
-}
-
-bool Skeleton3D::is_bone_rest_disabled(int p_bone) const {
- const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone, bone_size, false);
- return bones[p_bone].disable_rest;
-}
-
int Skeleton3D::get_bone_parent(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, -1);
@@ -626,6 +665,7 @@ void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) {
ERR_FAIL_INDEX(p_bone, bone_size);
bones.write[p_bone].enabled = p_enabled;
+ emit_signal(SceneStringNames::get_singleton()->bone_enabled_changed, p_bone);
_make_dirty();
}
@@ -635,6 +675,16 @@ bool Skeleton3D::is_bone_enabled(int p_bone) const {
return bones[p_bone].enabled;
}
+void Skeleton3D::set_show_rest_only(bool p_enabled) {
+ show_rest_only = p_enabled;
+ emit_signal(SceneStringNames::get_singleton()->show_rest_only_changed);
+ _make_dirty();
+}
+
+bool Skeleton3D::is_show_rest_only() const {
+ return show_rest_only;
+}
+
void Skeleton3D::clear_bones() {
bones.clear();
process_order_dirty = true;
@@ -642,38 +692,62 @@ void Skeleton3D::clear_bones() {
_make_dirty();
}
-// posing api
+// Posing api
-void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) {
+void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- bones.write[p_bone].pose = p_pose;
+ bones.write[p_bone].pose_position = p_position;
+ bones.write[p_bone].pose_cache_dirty = true;
if (is_inside_tree()) {
_make_dirty();
}
}
-Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
+void Skeleton3D::set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation) {
const int bone_size = bones.size();
- ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- return bones[p_bone].pose;
-}
+ ERR_FAIL_INDEX(p_bone, bone_size);
-void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose) {
+ bones.write[p_bone].pose_rotation = p_rotation;
+ bones.write[p_bone].pose_cache_dirty = true;
+ if (is_inside_tree()) {
+ _make_dirty();
+ }
+}
+void Skeleton3D::set_bone_pose_scale(int p_bone, const Vector3 &p_scale) {
const int bone_size = bones.size();
ERR_FAIL_INDEX(p_bone, bone_size);
- //ERR_FAIL_COND( !is_inside_scene() );
- bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform3D());
- bones.write[p_bone].custom_pose = p_custom_pose;
+ bones.write[p_bone].pose_scale = p_scale;
+ bones.write[p_bone].pose_cache_dirty = true;
+ if (is_inside_tree()) {
+ _make_dirty();
+ }
+}
- _make_dirty();
+Vector3 Skeleton3D::get_bone_pose_position(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3());
+ return bones[p_bone].pose_position;
+}
+
+Quaternion Skeleton3D::get_bone_pose_rotation(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Quaternion());
+ return bones[p_bone].pose_rotation;
+}
+
+Vector3 Skeleton3D::get_bone_pose_scale(int p_bone) const {
+ const int bone_size = bones.size();
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3());
+ return bones[p_bone].pose_scale;
}
-Transform3D Skeleton3D::get_bone_custom_pose(int p_bone) const {
+Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
const int bone_size = bones.size();
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
- return bones[p_bone].custom_pose;
+ ((Skeleton3D *)this)->bones.write[p_bone].update_pose_cache();
+ return bones[p_bone].pose_cache;
}
void Skeleton3D::_make_dirty() {
@@ -697,7 +771,7 @@ void Skeleton3D::localize_rests() {
set_bone_rest(current_bone_idx, bones[bones[current_bone_idx].parent].rest.affine_inverse() * bones[current_bone_idx].rest);
}
- // Add the bone's children to the list of bones to be processed
+ // Add the bone's children to the list of bones to be processed.
int child_bone_size = bones[current_bone_idx].child_bones.size();
for (int i = 0; i < child_bone_size; i++) {
bones_to_process.push_back(bones[current_bone_idx].child_bones[i]);
@@ -705,8 +779,8 @@ void Skeleton3D::localize_rests() {
}
}
-void Skeleton3D::set_animate_physical_bones(bool p_animate) {
- animate_physical_bones = p_animate;
+void Skeleton3D::set_animate_physical_bones(bool p_enabled) {
+ animate_physical_bones = p_enabled;
if (Engine::get_singleton()->is_editor_hint() == false) {
bool sim = false;
@@ -718,7 +792,7 @@ void Skeleton3D::set_animate_physical_bones(bool p_animate) {
}
}
}
- set_physics_process_internal(sim == false && p_animate);
+ set_physics_process_internal(sim == false && p_enabled);
}
}
@@ -831,7 +905,7 @@ void Skeleton3D::physical_bones_start_simulation_on(const TypedArray<StringName>
Vector<int> sim_bones;
if (p_bones.size() <= 0) {
- sim_bones.push_back(0); // if no bones is specified, activate ragdoll on full body
+ sim_bones.push_back(0); // If no bones is specified, activate ragdoll on full body.
} else {
sim_bones.resize(p_bones.size());
int c = 0;
@@ -874,59 +948,57 @@ void Skeleton3D::_skin_changed() {
_make_dirty();
}
-Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
- for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
- if (E->get()->skin == p_skin) {
- return Ref<SkinReference>(E->get());
+Ref<Skin> Skeleton3D::create_skin_from_rest_transforms() {
+ Ref<Skin> skin;
+
+ skin.instantiate();
+ skin->set_bind_count(bones.size());
+ _update_process_order(); // Just in case.
+
+ // Pose changed, rebuild cache of inverses.
+ const Bone *bonesptr = bones.ptr();
+ int len = bones.size();
+
+ // Calculate global rests and invert them.
+ LocalVector<int> bones_to_process;
+ bones_to_process = get_parentless_bones();
+ while (bones_to_process.size() > 0) {
+ int current_bone_idx = bones_to_process[0];
+ const Bone &b = bonesptr[current_bone_idx];
+ bones_to_process.erase(current_bone_idx);
+ LocalVector<int> child_bones_vector;
+ child_bones_vector = get_bone_children(current_bone_idx);
+ int child_bones_size = child_bones_vector.size();
+ if (b.parent < 0) {
+ skin->set_bind_pose(current_bone_idx, b.rest);
+ }
+ for (int i = 0; i < child_bones_size; i++) {
+ int child_bone_idx = child_bones_vector[i];
+ const Bone &cb = bonesptr[child_bone_idx];
+ skin->set_bind_pose(child_bone_idx, skin->get_bind_pose(current_bone_idx) * cb.rest);
+ // Add the bone's children to the list of bones to be processed.
+ bones_to_process.push_back(child_bones_vector[i]);
}
}
- Ref<Skin> skin = p_skin;
-
- if (skin.is_null()) {
- //need to create one from existing code, this is for compatibility only
- //when skeletons did not support skins. It is also used by gizmo
- //to display the skeleton.
-
- skin.instantiate();
- skin->set_bind_count(bones.size());
- _update_process_order(); //just in case
-
- // pose changed, rebuild cache of inverses
- const Bone *bonesptr = bones.ptr();
- int len = bones.size();
-
- // calculate global rests and invert them
- LocalVector<int> bones_to_process;
- bones_to_process = get_parentless_bones();
- while (bones_to_process.size() > 0) {
- int current_bone_idx = bones_to_process[0];
- const Bone &b = bonesptr[current_bone_idx];
- bones_to_process.erase(current_bone_idx);
- LocalVector<int> child_bones_vector;
- child_bones_vector = get_bone_children(current_bone_idx);
- int child_bones_size = child_bones_vector.size();
- if (b.parent < 0) {
- skin->set_bind_pose(current_bone_idx, b.rest);
- }
- for (int i = 0; i < child_bones_size; i++) {
- int child_bone_idx = child_bones_vector[i];
- const Bone &cb = bonesptr[child_bone_idx];
- skin->set_bind_pose(child_bone_idx, skin->get_bind_pose(current_bone_idx) * cb.rest);
- // Add the bone's children to the list of bones to be processed.
- bones_to_process.push_back(child_bones_vector[i]);
- }
- }
+ for (int i = 0; i < len; i++) {
+ // The inverse is what is actually required.
+ skin->set_bind_bone(i, i);
+ skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse());
+ }
+
+ return skin;
+}
+
+Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
+ ERR_FAIL_COND_V(p_skin.is_null(), Ref<SkinReference>());
- for (int i = 0; i < len; i++) {
- //the inverse is what is actually required
- skin->set_bind_bone(i, i);
- skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse());
+ for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
+ if (E->get()->skin == p_skin) {
+ return Ref<SkinReference>(E->get());
}
}
- ERR_FAIL_COND_V(skin.is_null(), Ref<SkinReference>());
-
Ref<SkinReference> skin_ref;
skin_ref.instantiate();
@@ -934,17 +1006,23 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
skin_ref->bind_count = 0;
skin_ref->skeleton = RenderingServer::get_singleton()->skeleton_create();
skin_ref->skeleton_node = this;
- skin_ref->skin = skin;
+ skin_ref->skin = p_skin;
skin_bindings.insert(skin_ref.operator->());
- skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed"));
+ skin_ref->skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed"));
- _make_dirty(); //skin needs to be updated, so update skeleton
+ _make_dirty(); // Skin needs to be updated, so update skeleton.
return skin_ref;
}
+void Skeleton3D::force_update_all_dirty_bones() {
+ if (dirty) {
+ const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
+ }
+}
+
void Skeleton3D::force_update_all_bone_transforms() {
_update_process_order();
@@ -966,60 +1044,35 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
bones_to_process.erase(current_bone_idx);
Bone &b = bonesptr[current_bone_idx];
+ bool bone_enabled = b.enabled && !show_rest_only;
- if (b.disable_rest) {
- if (b.enabled) {
- Transform3D pose = b.pose;
- if (b.custom_pose_enable) {
- pose = b.custom_pose * pose;
- }
- if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global * pose;
- b.pose_global_no_override = b.pose_global;
- } else {
- b.pose_global = pose;
- b.pose_global_no_override = b.pose_global;
- }
+ if (bone_enabled) {
+ b.update_pose_cache();
+ Transform3D pose = b.pose_cache;
+
+ if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global * pose;
+ b.pose_global_no_override = b.pose_global;
} else {
- if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global;
- b.pose_global_no_override = b.pose_global;
- } else {
- b.pose_global = Transform3D();
- b.pose_global_no_override = b.pose_global;
- }
+ b.pose_global = pose;
+ b.pose_global_no_override = b.pose_global;
}
-
} else {
- if (b.enabled) {
- Transform3D pose = b.pose;
- if (b.custom_pose_enable) {
- pose = b.custom_pose * pose;
- }
- if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose);
- b.pose_global_no_override = b.pose_global;
- } else {
- b.pose_global = b.rest * pose;
- b.pose_global_no_override = b.pose_global;
- }
+ if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global * b.rest;
+ b.pose_global_no_override = b.pose_global;
} else {
- if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global * b.rest;
- b.pose_global_no_override = b.pose_global;
- } else {
- b.pose_global = b.rest;
- b.pose_global_no_override = b.pose_global;
- }
+ b.pose_global = b.rest;
+ b.pose_global_no_override = b.pose_global;
}
}
if (b.local_pose_override_amount >= CMP_EPSILON) {
Transform3D override_local_pose;
if (b.parent >= 0) {
- override_local_pose = bonesptr[b.parent].pose_global * (b.rest * b.local_pose_override);
+ override_local_pose = bonesptr[b.parent].pose_global * b.local_pose_override;
} else {
- override_local_pose = (b.rest * b.local_pose_override);
+ override_local_pose = b.local_pose_override;
}
b.pose_global = b.pose_global.interpolate_with(override_local_pose, b.local_pose_override_amount);
}
@@ -1035,7 +1088,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
b.global_pose_override_amount = 0.0;
}
- // Add the bone's children to the list of bones to be processed
+ // Add the bone's children to the list of bones to be processed.
int child_bone_size = b.child_bones.size();
for (int i = 0; i < child_bone_size; i++) {
bones_to_process.push_back(b.child_bones[i]);
@@ -1045,7 +1098,7 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
}
}
-// helper functions
+// Helper functions
Transform3D Skeleton3D::global_pose_to_world_transform(Transform3D p_global_pose) {
return get_global_transform() * p_global_pose;
@@ -1060,8 +1113,8 @@ Transform3D Skeleton3D::global_pose_to_local_pose(int p_bone_idx, Transform3D p_
ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D());
if (bones[p_bone_idx].parent >= 0) {
int parent_bone_idx = bones[p_bone_idx].parent;
- Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest);
- return conversion_transform.affine_inverse() * p_global_pose;
+ Transform3D conversion_transform = bones[parent_bone_idx].pose_global.affine_inverse();
+ return conversion_transform * p_global_pose;
} else {
return p_global_pose;
}
@@ -1072,8 +1125,7 @@ Transform3D Skeleton3D::local_pose_to_global_pose(int p_bone_idx, Transform3D p_
ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D());
if (bones[p_bone_idx].parent >= 0) {
int parent_bone_idx = bones[p_bone_idx].parent;
- Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest);
- return conversion_transform * p_local_pose;
+ return bones[parent_bone_idx].pose_global * p_local_pose;
} else {
return p_local_pose;
}
@@ -1163,17 +1215,24 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton3D::get_bone_rest);
ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton3D::set_bone_rest);
+ ClassDB::bind_method(D_METHOD("create_skin_from_rest_transforms"), &Skeleton3D::create_skin_from_rest_transforms);
ClassDB::bind_method(D_METHOD("register_skin", "skin"), &Skeleton3D::register_skin);
ClassDB::bind_method(D_METHOD("localize_rests"), &Skeleton3D::localize_rests);
- ClassDB::bind_method(D_METHOD("set_bone_disable_rest", "bone_idx", "disable"), &Skeleton3D::set_bone_disable_rest);
- ClassDB::bind_method(D_METHOD("is_bone_rest_disabled", "bone_idx"), &Skeleton3D::is_bone_rest_disabled);
-
ClassDB::bind_method(D_METHOD("clear_bones"), &Skeleton3D::clear_bones);
ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose);
- ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_pose);
+ ClassDB::bind_method(D_METHOD("set_bone_pose_position", "bone_idx", "position"), &Skeleton3D::set_bone_pose_position);
+ ClassDB::bind_method(D_METHOD("set_bone_pose_rotation", "bone_idx", "rotation"), &Skeleton3D::set_bone_pose_rotation);
+ ClassDB::bind_method(D_METHOD("set_bone_pose_scale", "bone_idx", "scale"), &Skeleton3D::set_bone_pose_scale);
+
+ ClassDB::bind_method(D_METHOD("get_bone_pose_position", "bone_idx"), &Skeleton3D::get_bone_pose_position);
+ ClassDB::bind_method(D_METHOD("get_bone_pose_rotation", "bone_idx"), &Skeleton3D::get_bone_pose_rotation);
+ ClassDB::bind_method(D_METHOD("get_bone_pose_scale", "bone_idx"), &Skeleton3D::get_bone_pose_scale);
+
+ ClassDB::bind_method(D_METHOD("is_bone_enabled", "bone_idx"), &Skeleton3D::is_bone_enabled);
+ ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true));
ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override);
ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false));
@@ -1185,9 +1244,6 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_local_pose_override, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton3D::get_bone_local_pose_override);
- ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton3D::get_bone_custom_pose);
- ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton3D::set_bone_custom_pose);
-
ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms);
ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms);
@@ -1198,7 +1254,10 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("local_pose_to_global_pose", "bone_idx", "local_pose"), &Skeleton3D::local_pose_to_global_pose);
ClassDB::bind_method(D_METHOD("global_pose_z_forward_to_bone_forward", "bone_idx", "basis"), &Skeleton3D::global_pose_z_forward_to_bone_forward);
- ClassDB::bind_method(D_METHOD("set_animate_physical_bones"), &Skeleton3D::set_animate_physical_bones);
+ ClassDB::bind_method(D_METHOD("set_show_rest_only", "enabled"), &Skeleton3D::set_show_rest_only);
+ ClassDB::bind_method(D_METHOD("is_show_rest_only"), &Skeleton3D::is_show_rest_only);
+
+ ClassDB::bind_method(D_METHOD("set_animate_physical_bones", "enabled"), &Skeleton3D::set_animate_physical_bones);
ClassDB::bind_method(D_METHOD("get_animate_physical_bones"), &Skeleton3D::get_animate_physical_bones);
ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton3D::physical_bones_stop_simulation);
@@ -1212,6 +1271,7 @@ void Skeleton3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("execute_modifications", "delta", "execution_mode"), &Skeleton3D::execute_modifications);
#ifndef _3D_DISABLED
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_rest_only"), "set_show_rest_only", "is_show_rest_only");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones");
#endif // _3D_DISABLED
@@ -1220,6 +1280,8 @@ void Skeleton3D::_bind_methods() {
#endif // TOOLS_ENABLED
ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx")));
+ ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx")));
+ ADD_SIGNAL(MethodInfo("show_rest_only_changed"));
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
}
@@ -1228,7 +1290,7 @@ Skeleton3D::Skeleton3D() {
}
Skeleton3D::~Skeleton3D() {
- //some skins may remain bound
+ // Some skins may remain bound.
for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
E->get()->skeleton_node = nullptr;
}
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index c8a19db813..f7bc3df94e 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -76,16 +76,24 @@ private:
bool enabled;
int parent;
- bool disable_rest = false;
Transform3D rest;
- Transform3D pose;
+ _FORCE_INLINE_ void update_pose_cache() {
+ if (pose_cache_dirty) {
+ pose_cache.basis.set_quaternion_scale(pose_rotation, pose_scale);
+ pose_cache.origin = pose_position;
+ pose_cache_dirty = false;
+ }
+ }
+ bool pose_cache_dirty = true;
+ Transform3D pose_cache;
+ Vector3 pose_position;
+ Quaternion pose_rotation;
+ Vector3 pose_scale = Vector3(1, 1, 1);
+
Transform3D pose_global;
Transform3D pose_global_no_override;
- bool custom_pose_enable = false;
- Transform3D custom_pose;
-
real_t global_pose_override_amount = 0.0;
bool global_pose_override_reset = false;
Transform3D global_pose_override;
@@ -107,8 +115,6 @@ private:
Bone() {
parent = -1;
enabled = true;
- disable_rest = false;
- custom_pose_enable = false;
global_pose_override_amount = 0;
global_pose_override_reset = false;
#ifndef _3D_DISABLED
@@ -137,6 +143,8 @@ private:
void _make_dirty();
bool dirty = false;
+ bool show_rest_only = false;
+
uint64_t version = 1;
void _update_process_order();
@@ -145,6 +153,7 @@ protected:
bool _get(const StringName &p_path, Variant &r_ret) const;
bool _set(const StringName &p_path, const Variant &p_value);
void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
void _notification(int p_what);
static void _bind_methods();
@@ -185,9 +194,6 @@ public:
void remove_bone_child(int p_bone, int p_child);
Vector<int> get_parentless_bones();
- void set_bone_disable_rest(int p_bone, bool p_disable);
- bool is_bone_rest_disabled(int p_bone) const;
-
int get_bone_count() const;
void set_bone_rest(int p_bone, const Transform3D &p_rest);
@@ -197,15 +203,22 @@ public:
void set_bone_enabled(int p_bone, bool p_enabled);
bool is_bone_enabled(int p_bone) const;
+
+ void set_show_rest_only(bool p_enabled);
+ bool is_show_rest_only() const;
void clear_bones();
// posing api
- void set_bone_pose(int p_bone, const Transform3D &p_pose);
+ void set_bone_pose_position(int p_bone, const Vector3 &p_position);
+ void set_bone_pose_rotation(int p_bone, const Quaternion &p_rotation);
+ void set_bone_pose_scale(int p_bone, const Vector3 &p_scale);
+
Transform3D get_bone_pose(int p_bone) const;
- void set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose);
- Transform3D get_bone_custom_pose(int p_bone) const;
+ Vector3 get_bone_pose_position(int p_bone) const;
+ Quaternion get_bone_pose_rotation(int p_bone) const;
+ Vector3 get_bone_pose_scale(int p_bone) const;
void clear_bones_global_pose_override();
Transform3D get_bone_global_pose_override(int p_bone) const;
@@ -217,8 +230,11 @@ public:
void localize_rests(); // used for loaders and tools
+ Ref<Skin> create_skin_from_rest_transforms();
+
Ref<SkinReference> register_skin(const Ref<Skin> &p_skin);
+ void force_update_all_dirty_bones();
void force_update_all_bone_transforms();
void force_update_bone_children_transforms(int bone_idx);
@@ -244,7 +260,7 @@ public:
// Physical bone API
- void set_animate_physical_bones(bool p_animate);
+ void set_animate_physical_bones(bool p_enabled);
bool get_animate_physical_bones() const;
void bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone);
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index 466f67afb8..2e788051f4 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -99,7 +99,7 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain
child_ci->current_pos = child_ci->initial_transform.origin;
if (child_ci->parent_item) {
- child_ci->length = (child_ci->current_pos - child_ci->parent_item->current_pos).length();
+ child_ci->length = child_ci->parent_item->current_pos.distance_to(child_ci->current_pos);
}
}
@@ -140,7 +140,7 @@ void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet, Vec
solve_simple_backwards(p_task->chain, p_solve_magnet);
solve_simple_forwards(p_task->chain, p_solve_magnet, p_origin_pos);
- distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length();
+ distance_to_goal = p_task->chain.tips[0].end_effector->goal_transform.origin.distance_to(p_task->chain.tips[0].chain_item->current_pos);
}
}
@@ -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..9fceb21790 100644
--- a/scene/3d/soft_dynamic_body_3d.cpp
+++ b/scene/3d/soft_dynamic_body_3d.cpp
@@ -411,8 +411,9 @@ void SoftDynamicBody3D::_draw_soft_mesh() {
return;
}
- if (!rendering_server_handler.is_ready()) {
- rendering_server_handler.prepare(get_mesh()->get_rid(), 0);
+ const RID mesh_rid = get_mesh()->get_rid();
+ if (!rendering_server_handler.is_ready(mesh_rid)) {
+ rendering_server_handler.prepare(mesh_rid, 0);
/// Necessary in order to render the mesh correctly (Soft body nodes are in global space)
simulation_started = true;
@@ -433,9 +434,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 +445,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/soft_dynamic_body_3d.h b/scene/3d/soft_dynamic_body_3d.h
index 0b4b3021cd..5e7fbfe29e 100644
--- a/scene/3d/soft_dynamic_body_3d.h
+++ b/scene/3d/soft_dynamic_body_3d.h
@@ -50,7 +50,7 @@ class SoftDynamicBodyRenderingServerHandler : public RenderingServerHandler {
private:
SoftDynamicBodyRenderingServerHandler();
- bool is_ready() { return mesh.is_valid(); }
+ bool is_ready(RID p_mesh_rid) const { return mesh.is_valid() && mesh == p_mesh_rid; }
void prepare(RID p_mesh_rid, int p_surface);
void clear();
void open();
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/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index b9a2736918..349a534680 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -712,7 +712,7 @@ Rect2 Sprite3D::get_item_rect() const {
return CanvasItem::get_item_rect();
*/
- Size2i s;
+ Size2 s;
if (region) {
s = region_rect.size;
@@ -807,22 +807,20 @@ void AnimatedSprite3D::_draw() {
set_base(RID());
return; //no texuture no life
}
- Vector2 tsize = texture->get_size();
+ Size2 tsize = texture->get_size();
if (tsize.x == 0 || tsize.y == 0) {
return;
}
- Size2i s = tsize;
Rect2 src_rect;
-
- src_rect.size = s;
+ src_rect.size = tsize;
Point2 ofs = get_offset();
if (is_centered()) {
- ofs -= s / 2;
+ ofs -= tsize / 2;
}
- Rect2 dst_rect(ofs, s);
+ Rect2 dst_rect(ofs, tsize);
Rect2 final_rect;
Rect2 final_src_rect;
@@ -1133,7 +1131,7 @@ Rect2 AnimatedSprite3D::get_item_rect() const {
if (t.is_null()) {
return Rect2(0, 0, 1, 1);
}
- Size2i s = t->get_size();
+ Size2 s = t->get_size();
Point2 ofs = get_offset();
if (centered) {
diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp
index bc3bb81ed4..6761fdd944 100644
--- a/scene/3d/vehicle_body_3d.cpp
+++ b/scene/3d/vehicle_body_3d.cpp
@@ -275,7 +275,7 @@ void VehicleWheel3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_steering"), &VehicleWheel3D::get_steering);
ADD_GROUP("Per-Wheel Motion", "");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "-1024,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering");
ADD_GROUP("VehicleBody3D Motion", "");
@@ -470,7 +470,7 @@ real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) {
}
void VehicleBody3D::_update_suspension(PhysicsDirectBodyState3D *s) {
- real_t chassisMass = mass;
+ real_t chassisMass = get_mass();
for (int w_it = 0; w_it < wheels.size(); w_it++) {
VehicleWheel3D &wheel_info = *wheels[w_it];
@@ -558,7 +558,7 @@ void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const
rel_pos2,
normal,
s->get_inverse_inertia_tensor().get_main_diagonal(),
- 1.0 / mass,
+ 1.0 / get_mass(),
b2invinertia,
b2invmass);
@@ -584,7 +584,7 @@ void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const
#define ONLY_USE_LINEAR_MASS
#ifdef ONLY_USE_LINEAR_MASS
- real_t massTerm = real_t(1.) / ((1.0 / mass) + b2invmass);
+ real_t massTerm = real_t(1.) / ((1.0 / get_mass()) + b2invmass);
impulse = -contactDamping * rel_vel * massTerm;
#else
real_t velocityImpulse = -contactDamping * rel_vel * jacDiagABInv;
@@ -914,7 +914,7 @@ void VehicleBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_steering"), &VehicleBody3D::get_steering);
ADD_GROUP("Motion", "");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "engine_force", PROPERTY_HINT_RANGE, "-1024,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering");
}
diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp
index d3d12d94e9..377abd5b38 100644
--- a/scene/3d/voxel_gi.cpp
+++ b/scene/3d/voxel_gi.cpp
@@ -398,7 +398,7 @@ void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) {
baker.end_bake();
- //create the data for visual server
+ //create the data for rendering server
if (p_create_visual_debug) {
MultiMeshInstance3D *mmi = memnew(MultiMeshInstance3D);
diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp
index 04f371f4b2..aa1236521d 100644
--- a/scene/3d/voxelizer.cpp
+++ b/scene/3d/voxelizer.cpp
@@ -618,7 +618,6 @@ void Voxelizer::begin_bake(int p_subdiv, const AABB &p_bounds) {
bake_cells.resize(1);
material_cache.clear();
- print_line("subdiv: " + itos(p_subdiv));
//find out the actual real bounds, power of 2, which gets the highest subdivision
po2_bounds = p_bounds;
int longest_axis = po2_bounds.get_longest_axis_index();
@@ -661,7 +660,7 @@ void Voxelizer::end_bake() {
_fixup_plot(0, 0);
}
-//create the data for visual server
+//create the data for rendering server
int Voxelizer::get_voxel_gi_octree_depth() const {
return cell_subdiv;
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_cache.cpp b/scene/animation/animation_cache.cpp
deleted file mode 100644
index 56743007e4..0000000000
--- a/scene/animation/animation_cache.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-/*************************************************************************/
-/* animation_cache.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 "animation_cache.h"
-
-void AnimationCache::_node_exit_tree(Node *p_node) {
- //it is one shot, so it disconnects upon arrival
-
- ERR_FAIL_COND(!connected_nodes.has(p_node));
-
- connected_nodes.erase(p_node);
-
- for (int i = 0; i < path_cache.size(); i++) {
- if (path_cache[i].node != p_node) {
- continue;
- }
-
- path_cache.write[i].valid = false; //invalidate path cache
- }
-}
-
-void AnimationCache::_animation_changed() {
- _clear_cache();
-}
-
-void AnimationCache::_clear_cache() {
- while (connected_nodes.size()) {
- connected_nodes.front()->get()->disconnect("tree_exiting", callable_mp(this, &AnimationCache::_node_exit_tree));
- connected_nodes.erase(connected_nodes.front());
- }
- path_cache.clear();
- cache_valid = false;
- cache_dirty = true;
-}
-
-void AnimationCache::_update_cache() {
- cache_valid = false;
-
- ERR_FAIL_COND(!root);
- ERR_FAIL_COND(!root->is_inside_tree());
- ERR_FAIL_COND(animation.is_null());
-
- for (int i = 0; i < animation->get_track_count(); i++) {
- NodePath np = animation->track_get_path(i);
-
- Node *node = root->get_node(np);
- if (!node) {
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(!node, "Invalid track path in animation '" + np + "'.");
- }
-
- Path path;
-
- Ref<Resource> res;
-
- if (animation->track_get_type(i) == Animation::TYPE_TRANSFORM3D) {
-#ifndef _3D_DISABLED
- if (np.get_subname_count() > 1) {
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(animation->track_get_type(i) == Animation::TYPE_TRANSFORM3D, "Transform tracks can't have a subpath '" + np + "'.");
- }
-
- Node3D *sp = Object::cast_to<Node3D>(node);
-
- if (!sp) {
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(!sp, "Transform track not of type Node3D '" + np + "'.");
- }
-
- if (np.get_subname_count() == 1) {
- StringName property = np.get_subname(0);
- String ps = property;
-
- Skeleton3D *sk = Object::cast_to<Skeleton3D>(node);
- if (!sk) {
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(!sk, "Property defined in Transform track, but not a Skeleton! '" + np + "'.");
- }
-
- int idx = sk->find_bone(ps);
- if (idx == -1) {
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(idx == -1, "Property defined in Transform track, but not a Skeleton Bone! '" + np + "'.");
- }
-
- path.bone_idx = idx;
- path.skeleton = sk;
- }
-
- path.node_3d = sp;
-#endif // _3D_DISABLED
- } else {
- if (np.get_subname_count() > 0) {
- RES res2;
- Vector<StringName> leftover_subpath;
-
- // We don't want to cache the last resource unless it is a method call
- bool is_method = animation->track_get_type(i) == Animation::TYPE_METHOD;
- root->get_node_and_resource(np, res2, leftover_subpath, is_method);
-
- if (res2.is_valid()) {
- path.resource = res2;
- } else {
- path.node = node;
- }
- path.object = res2.is_valid() ? res2.ptr() : (Object *)node;
- path.subpath = leftover_subpath;
-
- } else {
- path.node = node;
- path.object = node;
- path.subpath = np.get_subnames();
- }
- }
-
- if (animation->track_get_type(i) == Animation::TYPE_VALUE) {
- if (np.get_subname_count() == 0) {
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(np.get_subname_count() == 0, "Value Track lacks property: " + np + ".");
- }
-
- } else if (animation->track_get_type(i) == Animation::TYPE_METHOD) {
- if (path.subpath.size() != 0) { // Trying to call a method of a non-resource
-
- path_cache.push_back(Path());
- ERR_CONTINUE_MSG(path.subpath.size() != 0, "Method Track has property: " + np + ".");
- }
- }
-
- path.valid = true;
-
- path_cache.push_back(path);
-
- if (!connected_nodes.has(path.node)) {
- connected_nodes.insert(path.node);
- path.node->connect("tree_exiting", callable_mp(this, &AnimationCache::_node_exit_tree), Node::make_binds(path.node), CONNECT_ONESHOT);
- }
- }
-
- cache_dirty = false;
- cache_valid = true;
-}
-
-void AnimationCache::set_track_transform(int p_idx, const Transform3D &p_transform) {
- if (cache_dirty) {
- _update_cache();
- }
-
- ERR_FAIL_COND(!cache_valid);
- ERR_FAIL_INDEX(p_idx, path_cache.size());
- Path &p = path_cache.write[p_idx];
- if (!p.valid) {
- return;
- }
-
-#ifndef _3D_DISABLED
- ERR_FAIL_COND(!p.node);
- ERR_FAIL_COND(!p.node_3d);
-
- if (p.skeleton) {
- p.skeleton->set_bone_pose(p.bone_idx, p_transform);
- } else {
- p.node_3d->set_transform(p_transform);
- }
-#endif // _3D_DISABLED
-}
-
-void AnimationCache::set_track_value(int p_idx, const Variant &p_value) {
- if (cache_dirty) {
- _update_cache();
- }
-
- ERR_FAIL_COND(!cache_valid);
- ERR_FAIL_INDEX(p_idx, path_cache.size());
- Path &p = path_cache.write[p_idx];
- if (!p.valid) {
- return;
- }
-
- ERR_FAIL_COND(!p.object);
- p.object->set_indexed(p.subpath, p_value);
-}
-
-void AnimationCache::call_track(int p_idx, const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- if (cache_dirty) {
- _update_cache();
- }
-
- ERR_FAIL_COND(!cache_valid);
- ERR_FAIL_INDEX(p_idx, path_cache.size());
- Path &p = path_cache.write[p_idx];
- if (!p.valid) {
- return;
- }
-
- ERR_FAIL_COND(!p.object);
- p.object->call(p_method, p_args, p_argcount, r_error);
-}
-
-void AnimationCache::set_all(float p_time, float p_delta) {
- if (cache_dirty) {
- _update_cache();
- }
-
- ERR_FAIL_COND(!cache_valid);
-
- int tc = animation->get_track_count();
- for (int i = 0; i < tc; i++) {
- switch (animation->track_get_type(i)) {
- case Animation::TYPE_TRANSFORM3D: {
- Vector3 loc, scale;
- Quaternion rot;
- animation->transform_track_interpolate(i, p_time, &loc, &rot, &scale);
- Transform3D tr(Basis(rot), loc);
- tr.basis.scale(scale);
-
- set_track_transform(i, tr);
-
- } break;
- case Animation::TYPE_VALUE: {
- if (animation->value_track_get_update_mode(i) == Animation::UPDATE_CONTINUOUS || (animation->value_track_get_update_mode(i) == Animation::UPDATE_DISCRETE && p_delta == 0)) {
- Variant v = animation->value_track_interpolate(i, p_time);
- set_track_value(i, v);
- } else {
- List<int> indices;
- animation->value_track_get_key_indices(i, p_time, p_delta, &indices);
-
- for (int &E : indices) {
- Variant v = animation->track_get_key_value(i, E);
- set_track_value(i, v);
- }
- }
-
- } break;
- case Animation::TYPE_METHOD: {
- List<int> indices;
- animation->method_track_get_key_indices(i, p_time, p_delta, &indices);
-
- for (int &E : indices) {
- Vector<Variant> args = animation->method_track_get_params(i, E);
- StringName name = animation->method_track_get_name(i, E);
- Callable::CallError err;
-
- if (!args.size()) {
- call_track(i, name, nullptr, 0, err);
- } else {
- Vector<const Variant *> argptrs;
- argptrs.resize(args.size());
- for (int j = 0; j < args.size(); j++) {
- argptrs.write[j] = &args.write[j];
- }
-
- call_track(i, name, (const Variant **)&argptrs[0], args.size(), err);
- }
- }
-
- } break;
- default: {
- }
- }
- }
-}
-
-void AnimationCache::set_animation(const Ref<Animation> &p_animation) {
- _clear_cache();
-
- if (animation.is_valid()) {
- animation->disconnect("changed", callable_mp(this, &AnimationCache::_animation_changed));
- }
-
- animation = p_animation;
-
- if (animation.is_valid()) {
- animation->connect("changed", callable_mp(this, &AnimationCache::_animation_changed));
- }
-}
-
-void AnimationCache::_bind_methods() {
-}
-
-void AnimationCache::set_root(Node *p_root) {
- _clear_cache();
- root = p_root;
-}
-
-AnimationCache::AnimationCache() {
-}
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..8407e0dd99 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() {
@@ -61,7 +60,12 @@ void AnimatedValuesBackup::restore() const {
if (entry->bone_idx == -1) {
entry->object->set_indexed(entry->subpath, entry->value);
} else {
- Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose(entry->bone_idx, entry->value);
+ Array arr = entry->value;
+ if (arr.size() == 3) {
+ Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_position(entry->bone_idx, arr[0]);
+ Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_rotation(entry->bone_idx, arr[1]);
+ Object::cast_to<Skeleton3D>(entry->object)->set_bone_pose_scale(entry->bone_idx, arr[0]);
+ }
}
}
}
@@ -124,8 +128,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 +151,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 +171,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));
}
}
@@ -243,6 +247,8 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
p_anim->node_cache.resize(a->get_track_count());
+ setup_pass++;
+
for (int i = 0; i < a->get_track_count(); i++) {
p_anim->node_cache.write[i] = nullptr;
RES resource;
@@ -276,46 +282,68 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
node_cache_map[key] = TrackNodeCache();
}
- p_anim->node_cache.write[i] = &node_cache_map[key];
- p_anim->node_cache[i]->path = a->track_get_path(i);
- p_anim->node_cache[i]->node = child;
- p_anim->node_cache[i]->resource = resource;
- p_anim->node_cache[i]->node_2d = Object::cast_to<Node2D>(child);
+ TrackNodeCache *node_cache = &node_cache_map[key];
+ p_anim->node_cache.write[i] = node_cache;
+
+ node_cache->path = a->track_get_path(i);
+ node_cache->node = child;
+ node_cache->resource = resource;
+ node_cache->node_2d = Object::cast_to<Node2D>(child);
#ifndef _3D_DISABLED
- if (a->track_get_type(i) == Animation::TYPE_TRANSFORM3D) {
+ if (a->track_get_type(i) == Animation::TYPE_POSITION_3D || a->track_get_type(i) == Animation::TYPE_ROTATION_3D || a->track_get_type(i) == Animation::TYPE_SCALE_3D) {
// special cases and caches for transform tracks
+ if (node_cache->last_setup_pass != setup_pass) {
+ node_cache->loc_used = false;
+ node_cache->rot_used = false;
+ node_cache->scale_used = false;
+ }
+
// cache node_3d
- p_anim->node_cache[i]->node_3d = Object::cast_to<Node3D>(child);
+ node_cache->node_3d = Object::cast_to<Node3D>(child);
// cache skeleton
- p_anim->node_cache[i]->skeleton = Object::cast_to<Skeleton3D>(child);
- if (p_anim->node_cache[i]->skeleton) {
+ node_cache->skeleton = Object::cast_to<Skeleton3D>(child);
+ if (node_cache->skeleton) {
if (a->track_get_path(i).get_subname_count() == 1) {
StringName bone_name = a->track_get_path(i).get_subname(0);
- p_anim->node_cache[i]->bone_idx = p_anim->node_cache[i]->skeleton->find_bone(bone_name);
- if (p_anim->node_cache[i]->bone_idx < 0) {
+ node_cache->bone_idx = node_cache->skeleton->find_bone(bone_name);
+ if (node_cache->bone_idx < 0) {
// broken track (nonexistent bone)
- p_anim->node_cache[i]->skeleton = nullptr;
- p_anim->node_cache[i]->node_3d = nullptr;
- ERR_CONTINUE(p_anim->node_cache[i]->bone_idx < 0);
+ node_cache->skeleton = nullptr;
+ node_cache->node_3d = nullptr;
+ ERR_CONTINUE(node_cache->bone_idx < 0);
}
} else {
// no property, just use spatialnode
- p_anim->node_cache[i]->skeleton = nullptr;
+ node_cache->skeleton = nullptr;
+ }
+ }
+
+ switch (a->track_get_type(i)) {
+ case Animation::TYPE_POSITION_3D: {
+ node_cache->loc_used = true;
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ node_cache->rot_used = true;
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+ node_cache->scale_used = true;
+ } break;
+ default: {
}
}
}
#endif // _3D_DISABLED
if (a->track_get_type(i) == Animation::TYPE_VALUE) {
- if (!p_anim->node_cache[i]->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
+ if (!node_cache->property_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
TrackNodeCache::PropertyAnim pa;
pa.subpath = leftover_path;
pa.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
pa.special = SP_NONE;
pa.owner = p_anim->node_cache[i];
- if (false && p_anim->node_cache[i]->node_2d) {
+ if (false && node_cache->node_2d) {
if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_pos) {
pa.special = SP_NODE2D_POS;
} else if (leftover_path.size() == 1 && leftover_path[0] == SceneStringNames::get_singleton()->transform_rot) {
@@ -324,20 +352,22 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
pa.special = SP_NODE2D_SCALE;
}
}
- p_anim->node_cache[i]->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa;
+ node_cache->property_anim[a->track_get_path(i).get_concatenated_subnames()] = pa;
}
}
if (a->track_get_type(i) == Animation::TYPE_BEZIER && leftover_path.size()) {
- if (!p_anim->node_cache[i]->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
+ if (!node_cache->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) {
TrackNodeCache::BezierAnim ba;
ba.bezier_property = leftover_path;
ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child;
ba.owner = p_anim->node_cache[i];
- p_anim->node_cache[i]->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba;
+ node_cache->bezier_anim[a->track_get_path(i).get_concatenated_subnames()] = ba;
}
}
+
+ node_cache->last_setup_pass = setup_pass;
}
}
@@ -370,17 +400,15 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
}
switch (a->track_get_type(i)) {
- case Animation::TYPE_TRANSFORM3D: {
+ case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
if (!nc->node_3d) {
continue;
}
Vector3 loc;
- Quaternion rot;
- Vector3 scale;
- Error err = a->transform_track_interpolate(i, p_time, &loc, &rot, &scale);
+ Error err = a->position_track_interpolate(i, p_time, &loc);
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
if (err != OK) {
@@ -392,12 +420,63 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
cache_update[cache_update_size++] = nc;
nc->accum_pass = accum_pass;
nc->loc_accum = loc;
- nc->rot_accum = rot;
- nc->scale_accum = scale;
-
+ nc->rot_accum = Quaternion();
+ nc->scale_accum = Vector3();
} else {
nc->loc_accum = nc->loc_accum.lerp(loc, p_interp);
+ }
+#endif // _3D_DISABLED
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+#ifndef _3D_DISABLED
+ if (!nc->node_3d) {
+ continue;
+ }
+
+ Quaternion rot;
+
+ Error err = a->rotation_track_interpolate(i, p_time, &rot);
+ //ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+ if (err != OK) {
+ continue;
+ }
+
+ if (nc->accum_pass != accum_pass) {
+ ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
+ cache_update[cache_update_size++] = nc;
+ nc->accum_pass = accum_pass;
+ nc->loc_accum = Vector3();
+ nc->rot_accum = rot;
+ nc->scale_accum = Vector3();
+ } else {
nc->rot_accum = nc->rot_accum.slerp(rot, p_interp);
+ }
+#endif // _3D_DISABLED
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+#ifndef _3D_DISABLED
+ if (!nc->node_3d) {
+ continue;
+ }
+
+ Vector3 scale;
+
+ Error err = a->scale_track_interpolate(i, p_time, &scale);
+ //ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+ if (err != OK) {
+ continue;
+ }
+
+ if (nc->accum_pass != accum_pass) {
+ ERR_CONTINUE(cache_update_size >= NODE_CACHE_UPDATE_MAX);
+ cache_update[cache_update_size++] = nc;
+ nc->accum_pass = accum_pass;
+ nc->loc_accum = Vector3();
+ nc->rot_accum = Quaternion();
+ nc->scale_accum = scale;
+ } else {
nc->scale_accum = nc->scale_accum.lerp(scale, p_interp);
}
#endif // _3D_DISABLED
@@ -856,15 +935,30 @@ void AnimationPlayer::_animation_update_transforms() {
TrackNodeCache *nc = cache_update[i];
ERR_CONTINUE(nc->accum_pass != accum_pass);
-
- t.origin = nc->loc_accum;
- t.basis.set_quaternion_scale(nc->rot_accum, nc->scale_accum);
#ifndef _3D_DISABLED
if (nc->skeleton && nc->bone_idx >= 0) {
- nc->skeleton->set_bone_pose(nc->bone_idx, t);
+ if (nc->loc_used) {
+ nc->skeleton->set_bone_pose_position(nc->bone_idx, nc->loc_accum);
+ }
+ if (nc->rot_used) {
+ nc->skeleton->set_bone_pose_rotation(nc->bone_idx, nc->rot_accum);
+ }
+ if (nc->scale_used) {
+ nc->skeleton->set_bone_pose_scale(nc->bone_idx, nc->scale_accum);
+ }
+
} else if (nc->node_3d) {
- nc->node_3d->set_transform(t);
+ if (nc->loc_used) {
+ nc->node_3d->set_position(nc->loc_accum);
+ }
+ if (nc->rot_used) {
+ nc->node_3d->set_rotation(nc->rot_accum.get_euler());
+ }
+ if (nc->scale_used) {
+ nc->node_3d->set_scale(nc->scale_accum);
+ }
}
+
#endif // _3D_DISABLED
}
}
@@ -1019,8 +1113,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 +1128,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 +1165,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 +1459,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 +1482,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 +1587,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);
@@ -1534,7 +1622,12 @@ Ref<AnimatedValuesBackup> AnimationPlayer::backup_animated_values(Node *p_root_o
AnimatedValuesBackup::Entry entry;
entry.object = nc->skeleton;
entry.bone_idx = nc->bone_idx;
- entry.value = nc->skeleton->get_bone_pose(nc->bone_idx);
+ Array arr;
+ arr.resize(3);
+ arr[0] = nc->skeleton->get_bone_pose_position(nc->bone_idx);
+ arr[1] = nc->skeleton->get_bone_pose_rotation(nc->bone_idx);
+ arr[2] = nc->skeleton->get_bone_pose_scale(nc->bone_idx);
+ entry.value = nc;
backup->entries.push_back(entry);
} else {
if (nc->node_3d) {
@@ -1545,12 +1638,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/animation/animation_player.h b/scene/animation/animation_player.h
index b693e29bdf..f8e72a67b6 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -88,6 +88,8 @@ private:
SP_NODE2D_SCALE,
};
+ uint32_t setup_pass = 1;
+
struct TrackNodeCache {
NodePath path;
uint32_t id = 0;
@@ -101,6 +103,10 @@ private:
int bone_idx = -1;
// accumulated transforms
+ bool loc_used = false;
+ bool rot_used = false;
+ bool scale_used = false;
+
Vector3 loc_accum;
Quaternion rot_accum;
Vector3 scale_accum;
@@ -134,6 +140,7 @@ private:
Map<StringName, BezierAnim> bezier_anim;
+ uint32_t last_setup_pass = 0;
TrackNodeCache() {}
};
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 9ca8d478b1..dd5fe46223 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -544,13 +544,18 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
NodePath path = anim->track_get_path(i);
Animation::TrackType track_type = anim->track_get_type(i);
+ Animation::TrackType track_cache_type = track_type;
+ if (track_cache_type == Animation::TYPE_POSITION_3D || track_cache_type == Animation::TYPE_ROTATION_3D || track_cache_type == Animation::TYPE_SCALE_3D) {
+ track_cache_type = Animation::TYPE_POSITION_3D; //reference them as position3D tracks, even if they modify rotation or scale
+ }
+
TrackCache *track = nullptr;
if (track_cache.has(path)) {
track = track_cache.get(path);
}
//if not valid, delete track
- if (track && (track->type != track_type || ObjectDB::get_instance(track->object_id) == nullptr)) {
+ if (track && (track->type != track_cache_type || ObjectDB::get_instance(track->object_id) == nullptr)) {
playing_caches.erase(track);
memdelete(track);
track_cache.erase(path);
@@ -587,7 +592,9 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track = track_value;
} break;
- case Animation::TYPE_TRANSFORM3D: {
+ case Animation::TYPE_POSITION_3D:
+ case Animation::TYPE_ROTATION_3D:
+ case Animation::TYPE_SCALE_3D: {
#ifndef _3D_DISABLED
Node3D *node_3d = Object::cast_to<Node3D>(child);
@@ -597,6 +604,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
TrackCacheTransform *track_xform = memnew(TrackCacheTransform);
+ track_xform->type = Animation::TYPE_POSITION_3D;
track_xform->node_3d = node_3d;
track_xform->skeleton = nullptr;
@@ -615,6 +623,21 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
track_xform->object_id = track_xform->object->get_instance_id();
track = track_xform;
+
+ switch (track_type) {
+ case Animation::TYPE_POSITION_3D: {
+ track_xform->loc_used = true;
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ track_xform->rot_used = true;
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+ track_xform->scale_used = true;
+ } break;
+ default: {
+ }
+ }
+
#endif // _3D_DISABLED
} break;
case Animation::TYPE_METHOD: {
@@ -670,6 +693,26 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
}
track_cache[path] = track;
+ } else if (track_cache_type == Animation::TYPE_POSITION_3D) {
+ TrackCacheTransform *track_xform = static_cast<TrackCacheTransform *>(track);
+ if (track->setup_pass != setup_pass) {
+ track_xform->loc_used = false;
+ track_xform->rot_used = false;
+ track_xform->scale_used = false;
+ }
+ switch (track_type) {
+ case Animation::TYPE_POSITION_3D: {
+ track_xform->loc_used = true;
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+ track_xform->rot_used = true;
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+ track_xform->scale_used = true;
+ } break;
+ default: {
+ }
+ }
}
track->setup_pass = setup_pass;
@@ -831,8 +874,11 @@ void AnimationTree::_process_graph(real_t p_delta) {
ERR_CONTINUE(!track_cache.has(path));
TrackCache *track = track_cache[path];
- if (track->type != a->track_get_type(i)) {
- continue; //may happen should not
+
+ Animation::TrackType ttype = a->track_get_type(i);
+ if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) {
+ //broken animation, but avoid error spamming
+ continue;
}
track->root_motion = root_motion_track == path;
@@ -848,20 +894,81 @@ void AnimationTree::_process_graph(real_t p_delta) {
continue; //nothing to blend
}
- switch (track->type) {
- case Animation::TYPE_TRANSFORM3D: {
+ switch (ttype) {
+ case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = Vector3();
+ t->rot = Quaternion();
+ t->rot_blend_accum = 0;
+ t->scale = Vector3(1, 1, 1);
+ }
+
if (track->root_motion) {
- if (t->process_pass != process_pass) {
- t->process_pass = process_pass;
- t->loc = Vector3();
- t->rot = Quaternion();
- t->rot_blend_accum = 0;
- t->scale = Vector3(1, 1, 1);
+ real_t prev_time = time - delta;
+ if (prev_time < 0) {
+ if (!a->has_loop()) {
+ prev_time = 0;
+ } else {
+ prev_time = a->get_length() + prev_time;
+ }
}
+ Vector3 loc[2];
+
+ if (prev_time > time) {
+ Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+ if (err != OK) {
+ continue;
+ }
+
+ a->position_track_interpolate(i, a->get_length(), &loc[1]);
+
+ t->loc += (loc[1] - loc[0]) * blend;
+ prev_time = 0;
+ }
+
+ Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+ if (err != OK) {
+ continue;
+ }
+
+ a->position_track_interpolate(i, time, &loc[1]);
+
+ t->loc += (loc[1] - loc[0]) * blend;
+
+ prev_time = 0;
+
+ } else {
+ Vector3 loc;
+
+ Error err = a->position_track_interpolate(i, time, &loc);
+ //ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+ if (err != OK) {
+ continue;
+ }
+
+ t->loc = t->loc.lerp(loc, blend);
+ }
+#endif // _3D_DISABLED
+ } break;
+ case Animation::TYPE_ROTATION_3D: {
+#ifndef _3D_DISABLED
+ TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = Vector3();
+ t->rot = Quaternion();
+ t->rot_blend_accum = 0;
+ t->scale = Vector3(1, 1, 1);
+ }
+
+ if (track->root_motion) {
real_t prev_time = time - delta;
if (prev_time < 0) {
if (!a->has_loop()) {
@@ -871,61 +978,44 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
}
- Vector3 loc[2];
Quaternion rot[2];
- Vector3 scale[2];
if (prev_time > time) {
- Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
+ Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
if (err != OK) {
continue;
}
- a->transform_track_interpolate(i, a->get_length(), &loc[1], &rot[1], &scale[1]);
+ a->rotation_track_interpolate(i, a->get_length(), &rot[1]);
- t->loc += (loc[1] - loc[0]) * blend;
- t->scale += (scale[1] - scale[0]) * blend;
Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
t->rot = (t->rot * q).normalized();
prev_time = 0;
}
- Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
+ Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
if (err != OK) {
continue;
}
- a->transform_track_interpolate(i, time, &loc[1], &rot[1], &scale[1]);
+ a->rotation_track_interpolate(i, time, &rot[1]);
- t->loc += (loc[1] - loc[0]) * blend;
- t->scale += (scale[1] - scale[0]) * blend;
Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
t->rot = (t->rot * q).normalized();
prev_time = 0;
} else {
- Vector3 loc;
Quaternion rot;
- Vector3 scale;
- Error err = a->transform_track_interpolate(i, time, &loc, &rot, &scale);
+ Error err = a->rotation_track_interpolate(i, time, &rot);
//ERR_CONTINUE(err!=OK); //used for testing, should be removed
- if (t->process_pass != process_pass) {
- t->process_pass = process_pass;
- t->loc = loc;
- t->rot = rot;
- t->rot_blend_accum = 0;
- t->scale = scale;
- }
-
if (err != OK) {
continue;
}
- t->loc = t->loc.lerp(loc, blend);
if (t->rot_blend_accum == 0) {
t->rot = rot;
t->rot_blend_accum = blend;
@@ -934,6 +1024,67 @@ void AnimationTree::_process_graph(real_t p_delta) {
t->rot = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized();
t->rot_blend_accum = rot_total;
}
+ }
+#endif // _3D_DISABLED
+ } break;
+ case Animation::TYPE_SCALE_3D: {
+#ifndef _3D_DISABLED
+ TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
+
+ if (t->process_pass != process_pass) {
+ t->process_pass = process_pass;
+ t->loc = Vector3();
+ t->rot = Quaternion();
+ t->rot_blend_accum = 0;
+ t->scale = Vector3(1, 1, 1);
+ }
+
+ if (track->root_motion) {
+ real_t prev_time = time - delta;
+ if (prev_time < 0) {
+ if (!a->has_loop()) {
+ prev_time = 0;
+ } else {
+ prev_time = a->get_length() + prev_time;
+ }
+ }
+
+ Vector3 scale[2];
+
+ if (prev_time > time) {
+ Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+
+ a->scale_track_interpolate(i, a->get_length(), &scale[1]);
+
+ t->scale += (scale[1] - scale[0]) * blend;
+
+ prev_time = 0;
+ }
+
+ Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+
+ a->scale_track_interpolate(i, time, &scale[1]);
+
+ t->scale += (scale[1] - scale[0]) * blend;
+
+ prev_time = 0;
+
+ } else {
+ Vector3 scale;
+
+ Error err = a->scale_track_interpolate(i, time, &scale);
+ //ERR_CONTINUE(err!=OK); //used for testing, should be removed
+
+ if (err != OK) {
+ continue;
+ }
+
t->scale = t->scale.lerp(scale, blend);
}
#endif // _3D_DISABLED
@@ -1198,26 +1349,38 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
switch (track->type) {
- case Animation::TYPE_TRANSFORM3D: {
+ case Animation::TYPE_POSITION_3D: {
#ifndef _3D_DISABLED
TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);
- Transform3D xform;
- xform.origin = t->loc;
-
- xform.basis.set_quaternion_scale(t->rot, t->scale);
-
if (t->root_motion) {
+ Transform3D xform;
+ xform.origin = t->loc;
+ xform.basis.set_quaternion_scale(t->rot, t->scale);
+
root_motion_transform = xform;
- if (t->skeleton && t->bone_idx >= 0) {
- root_motion_transform = (t->skeleton->get_bone_rest(t->bone_idx) * root_motion_transform) * t->skeleton->get_bone_rest(t->bone_idx).affine_inverse();
- }
} else if (t->skeleton && t->bone_idx >= 0) {
- t->skeleton->set_bone_pose(t->bone_idx, xform);
+ if (t->loc_used) {
+ t->skeleton->set_bone_pose_position(t->bone_idx, t->loc);
+ }
+ if (t->rot_used) {
+ t->skeleton->set_bone_pose_rotation(t->bone_idx, t->rot);
+ }
+ if (t->scale_used) {
+ t->skeleton->set_bone_pose_scale(t->bone_idx, t->scale);
+ }
} else if (!t->skeleton) {
- t->node_3d->set_transform(xform);
+ if (t->loc_used) {
+ t->node_3d->set_position(t->loc);
+ }
+ if (t->rot_used) {
+ t->node_3d->set_rotation(t->rot.get_euler());
+ }
+ if (t->scale_used) {
+ t->node_3d->set_scale(t->scale);
+ }
}
#endif // _3D_DISABLED
} break;
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 1e0267682e..ed207dfe0c 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -197,13 +197,16 @@ private:
Skeleton3D *skeleton = nullptr;
#endif // _3D_DISABLED
int bone_idx = -1;
+ bool loc_used = false;
+ bool rot_used = false;
+ bool scale_used = false;
Vector3 loc;
Quaternion rot;
real_t rot_blend_accum = 0.0;
Vector3 scale;
TrackCacheTransform() {
- type = Animation::TYPE_TRANSFORM3D;
+ type = Animation::TYPE_POSITION_3D;
}
};
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index 0334ba667b..d2fd65d3d9 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -135,7 +135,7 @@ void AudioStreamPlayer::play(float p_from_pos) {
Ref<AudioStreamPlayback> stream_playback = stream->instance_playback();
ERR_FAIL_COND_MSG(stream_playback.is_null(), "Failed to instantiate playback.");
- AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos);
+ AudioServer::get_singleton()->start_playback_stream(stream_playback, bus, _get_volume_vector(), p_from_pos, pitch_scale);
stream_playbacks.push_back(stream_playback);
active.set();
set_process_internal(true);
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/button.cpp b/scene/gui/button.cpp
index 9cdf3bf210..e253a27e66 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -427,9 +427,9 @@ Ref<Texture2D> Button::get_icon() const {
return icon;
}
-void Button::set_expand_icon(bool p_expand_icon) {
- if (expand_icon != p_expand_icon) {
- expand_icon = p_expand_icon;
+void Button::set_expand_icon(bool p_enabled) {
+ if (expand_icon != p_enabled) {
+ expand_icon = p_enabled;
update();
minimum_size_changed();
}
@@ -439,9 +439,9 @@ bool Button::is_expand_icon() const {
return expand_icon;
}
-void Button::set_flat(bool p_flat) {
- if (flat != p_flat) {
- flat = p_flat;
+void Button::set_flat(bool p_enabled) {
+ if (flat != p_enabled) {
+ flat = p_enabled;
update();
}
}
@@ -450,9 +450,9 @@ bool Button::is_flat() const {
return flat;
}
-void Button::set_clip_text(bool p_clip_text) {
- if (clip_text != p_clip_text) {
- clip_text = p_clip_text;
+void Button::set_clip_text(bool p_enabled) {
+ if (clip_text != p_enabled) {
+ clip_text = p_enabled;
update();
minimum_size_changed();
}
@@ -553,7 +553,7 @@ void Button::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text_align"), &Button::get_text_align);
ClassDB::bind_method(D_METHOD("set_icon_align", "icon_align"), &Button::set_icon_align);
ClassDB::bind_method(D_METHOD("get_icon_align"), &Button::get_icon_align);
- ClassDB::bind_method(D_METHOD("set_expand_icon"), &Button::set_expand_icon);
+ ClassDB::bind_method(D_METHOD("set_expand_icon", "enabled"), &Button::set_expand_icon);
ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon);
BIND_ENUM_CONSTANT(ALIGN_LEFT);
diff --git a/scene/gui/button.h b/scene/gui/button.h
index 253d079680..fd36cb77af 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -91,13 +91,13 @@ public:
void set_icon(const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_icon() const;
- void set_expand_icon(bool p_expand_icon);
+ void set_expand_icon(bool p_enabled);
bool is_expand_icon() const;
- void set_flat(bool p_flat);
+ void set_flat(bool p_enabled);
bool is_flat() const;
- void set_clip_text(bool p_clip_text);
+ void set_clip_text(bool p_enabled);
bool get_clip_text() const;
void set_text_align(TextAlign p_align);
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index c7d1d7ef82..da54903871 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -433,7 +433,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
return;
}
if (k->is_action("ui_end", true)) {
- code_completion_current_selected = MIN(code_completion_options.size() - 1, code_completion_current_selected + code_completion_max_lines);
+ code_completion_current_selected = code_completion_options.size() - 1;
update();
accept_event();
return;
@@ -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. */
@@ -2930,6 +2937,7 @@ void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) {
return;
}
+ lines_edited_changed += p_to_line - p_from_line;
lines_edited_from = (lines_edited_from == -1) ? MIN(p_from_line, p_to_line) : MIN(lines_edited_from, MIN(p_from_line, p_to_line));
lines_edited_to = (lines_edited_to == -1) ? MAX(p_from_line, p_to_line) : MAX(lines_edited_from, MAX(p_from_line, p_to_line));
}
@@ -2956,7 +2964,6 @@ void CodeEdit::_text_changed() {
}
lc = get_line_count();
- int line_change_size = (lines_edited_to - lines_edited_from);
List<int> breakpoints;
breakpointed_lines.get_key_list(&breakpoints);
for (const int &line : breakpoints) {
@@ -2967,8 +2974,8 @@ void CodeEdit::_text_changed() {
breakpointed_lines.erase(line);
emit_signal(SNAME("breakpoint_toggled"), line);
- int next_line = line + line_change_size;
- if (next_line < lc && is_line_breakpointed(next_line)) {
+ int next_line = line + lines_edited_changed;
+ if (next_line > -1 && next_line < lc && is_line_breakpointed(next_line)) {
emit_signal(SNAME("breakpoint_toggled"), next_line);
breakpointed_lines[next_line] = true;
continue;
@@ -2977,6 +2984,7 @@ void CodeEdit::_text_changed() {
lines_edited_from = -1;
lines_edited_to = -1;
+ lines_edited_changed = 0;
}
CodeEdit::CodeEdit() {
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index d8eccb7d32..f0d971dd35 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -240,6 +240,7 @@ private:
int line_spacing = 1;
/* Callbacks */
+ int lines_edited_changed = 0;
int lines_edited_from = -1;
int lines_edited_to = -1;
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 611035fff9..894175d559 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);
@@ -1100,9 +1106,9 @@ bool ColorPicker::are_presets_visible() const {
void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPicker::set_pick_color);
ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPicker::get_pick_color);
- ClassDB::bind_method(D_METHOD("set_hsv_mode"), &ColorPicker::set_hsv_mode);
+ ClassDB::bind_method(D_METHOD("set_hsv_mode", "enabled"), &ColorPicker::set_hsv_mode);
ClassDB::bind_method(D_METHOD("is_hsv_mode"), &ColorPicker::is_hsv_mode);
- ClassDB::bind_method(D_METHOD("set_raw_mode"), &ColorPicker::set_raw_mode);
+ ClassDB::bind_method(D_METHOD("set_raw_mode", "enabled"), &ColorPicker::set_raw_mode);
ClassDB::bind_method(D_METHOD("is_raw_mode"), &ColorPicker::is_raw_mode);
ClassDB::bind_method(D_METHOD("set_deferred_mode", "mode"), &ColorPicker::set_deferred_mode);
ClassDB::bind_method(D_METHOD("is_deferred_mode"), &ColorPicker::is_deferred_mode);
@@ -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/container.cpp b/scene/gui/container.cpp
index 11941529cd..a1bd82f6f7 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -87,6 +87,9 @@ void Container::_sort_children() {
return;
}
+ notification(NOTIFICATION_PRE_SORT_CHILDREN);
+ emit_signal(SceneStringNames::get_singleton()->pre_sort_children);
+
notification(NOTIFICATION_SORT_CHILDREN);
emit_signal(SceneStringNames::get_singleton()->sort_children);
pending_sort = false;
@@ -174,7 +177,10 @@ void Container::_bind_methods() {
ClassDB::bind_method(D_METHOD("queue_sort"), &Container::queue_sort);
ClassDB::bind_method(D_METHOD("fit_child_in_rect", "child", "rect"), &Container::fit_child_in_rect);
+ BIND_CONSTANT(NOTIFICATION_PRE_SORT_CHILDREN);
BIND_CONSTANT(NOTIFICATION_SORT_CHILDREN);
+
+ ADD_SIGNAL(MethodInfo("pre_sort_children"));
ADD_SIGNAL(MethodInfo("sort_children"));
}
diff --git a/scene/gui/container.h b/scene/gui/container.h
index bce3085f0c..f3ae948556 100644
--- a/scene/gui/container.h
+++ b/scene/gui/container.h
@@ -51,7 +51,8 @@ protected:
public:
enum {
- NOTIFICATION_SORT_CHILDREN = 50
+ NOTIFICATION_PRE_SORT_CHILDREN = 50,
+ NOTIFICATION_SORT_CHILDREN = 51,
};
void fit_child_in_rect(Control *p_child, const Rect2 &p_rect);
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..be692b6a0c 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);
@@ -408,7 +408,7 @@ public:
Rect2 get_rect() const;
Rect2 get_global_rect() const;
Rect2 get_screen_rect() const;
- Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the visual server
+ Rect2 get_window_rect() const; ///< use with care, as it blocks waiting for the rendering server
Rect2 get_anchorable_rect() const override;
void set_rect(const Rect2 &p_rect); // Reset anchors to begin and set rect, for faster container children sorting.
@@ -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 bca3b2059d..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() {
@@ -1071,10 +1065,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
if (mm.is_valid() && box_selecting) {
box_selecting_to = mm->get_position();
- box_selecting_rect = Rect2(MIN(box_selecting_from.x, box_selecting_to.x),
- MIN(box_selecting_from.y, box_selecting_to.y),
- ABS(box_selecting_from.x - box_selecting_to.x),
- ABS(box_selecting_from.y - box_selecting_to.y));
+ box_selecting_rect = Rect2(box_selecting_from.min(box_selecting_to), (box_selecting_from - box_selecting_to).abs());
for (int i = get_child_count() - 1; i >= 0; i--) {
GraphNode *gn = Object::cast_to<GraphNode>(get_child(i));
@@ -2275,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 d9acbeb828..54e7d8f960 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;
}
}
@@ -242,7 +241,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
_reset_caret_blink_timer();
if (b->is_pressed()) {
- accept_event(); //don't pass event further when clicked on text field
+ accept_event(); // don't pass event further when clicked on text field
if (!text.is_empty() && is_editable() && _is_over_clear_button(b->get_position())) {
clear_button_status.press_attempt = true;
clear_button_status.pressing_inside = true;
@@ -260,24 +259,29 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
} else {
if (selecting_enabled) {
- if (!b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - selection.last_dblclk) < 600) {
+ const int triple_click_timeout = 600;
+ const int triple_click_tolerance = 5;
+ const bool is_triple_click = !b->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < triple_click_timeout && b->get_position().distance_to(last_dblclk_pos) < triple_click_tolerance;
+
+ if (is_triple_click && text.length()) {
// Triple-click select all.
selection.enabled = true;
selection.begin = 0;
selection.end = text.length();
selection.double_click = true;
- selection.last_dblclk = 0;
+ last_dblclk = 0;
caret_column = selection.begin;
} else if (b->is_double_click()) {
// Double-click select word.
- 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) {
+ last_dblclk = OS::get_singleton()->get_ticks_msec();
+ last_dblclk_pos = b->get_position();
+ 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;
- selection.last_dblclk = OS::get_singleton()->get_ticks_msec();
caret_column = selection.end;
break;
}
@@ -708,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);
@@ -732,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);
@@ -778,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 {
{
@@ -1109,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;
@@ -1497,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;
}
@@ -1555,6 +1551,20 @@ void LineEdit::deselect() {
update();
}
+bool LineEdit::has_selection() const {
+ return selection.enabled;
+}
+
+int LineEdit::get_selection_from_column() const {
+ ERR_FAIL_COND_V(!selection.enabled, -1);
+ return selection.begin;
+}
+
+int LineEdit::get_selection_to_column() const {
+ ERR_FAIL_COND_V(!selection.enabled, -1);
+ return selection.end;
+}
+
void LineEdit::selection_delete() {
if (selection.enabled) {
delete_text(selection.begin, selection.end);
@@ -1921,7 +1931,7 @@ void LineEdit::_shape() {
TS->shaped_text_clear(text_rid);
String t;
- if (text.length() == 0) {
+ if (text.length() == 0 && ime_text.length() == 0) {
t = placeholder_translated;
} else if (pass) {
t = secret_character.repeat(text.length() + ime_text.length());
@@ -2089,6 +2099,9 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("select_all"), &LineEdit::select_all);
ClassDB::bind_method(D_METHOD("deselect"), &LineEdit::deselect);
+ ClassDB::bind_method(D_METHOD("has_selection"), &LineEdit::has_selection);
+ ClassDB::bind_method(D_METHOD("get_selection_from_column"), &LineEdit::get_selection_from_column);
+ ClassDB::bind_method(D_METHOD("get_selection_to_column"), &LineEdit::get_selection_to_column);
ClassDB::bind_method(D_METHOD("set_text", "text"), &LineEdit::set_text);
ClassDB::bind_method(D_METHOD("get_text"), &LineEdit::get_text);
ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &LineEdit::get_draw_control_chars);
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index e364a79c83..923024dd56 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -136,7 +136,6 @@ private:
bool creating = false;
bool double_click = false;
bool drag_attempt = false;
- uint64_t last_dblclk = 0;
} selection;
struct TextOperation {
@@ -153,6 +152,9 @@ private:
bool pressing_inside = false;
} clear_button_status;
+ uint64_t last_dblclk = 0;
+ Vector2 last_dblclk_pos;
+
bool caret_blink_enabled = false;
bool caret_force_displayed = false;
bool draw_caret = true;
@@ -229,6 +231,9 @@ public:
void select_all();
void selection_delete();
void deselect();
+ bool has_selection() const;
+ int get_selection_from_column() const;
+ int get_selection_to_column() const;
void delete_char();
void delete_text(int p_from_column, int p_to_column);
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/spin_box.cpp b/scene/gui/spin_box.cpp
index 0aec017649..0446e1b402 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -253,14 +253,14 @@ String SpinBox::get_prefix() const {
return prefix;
}
-void SpinBox::set_update_on_text_changed(bool p_update) {
- if (update_on_text_changed == p_update) {
+void SpinBox::set_update_on_text_changed(bool p_enabled) {
+ if (update_on_text_changed == p_enabled) {
return;
}
- update_on_text_changed = p_update;
+ update_on_text_changed = p_enabled;
- if (p_update) {
+ if (p_enabled) {
line_edit->connect("text_changed", callable_mp(this, &SpinBox::_text_changed), Vector<Variant>(), CONNECT_DEFERRED);
} else {
line_edit->disconnect("text_changed", callable_mp(this, &SpinBox::_text_changed));
@@ -271,8 +271,8 @@ bool SpinBox::get_update_on_text_changed() const {
return update_on_text_changed;
}
-void SpinBox::set_editable(bool p_editable) {
- line_edit->set_editable(p_editable);
+void SpinBox::set_editable(bool p_enabled) {
+ line_edit->set_editable(p_enabled);
}
bool SpinBox::is_editable() const {
@@ -284,17 +284,15 @@ void SpinBox::apply() {
}
void SpinBox::_bind_methods() {
- //ClassDB::bind_method(D_METHOD("_value_changed"),&SpinBox::_value_changed);
-
ClassDB::bind_method(D_METHOD("set_align", "align"), &SpinBox::set_align);
ClassDB::bind_method(D_METHOD("get_align"), &SpinBox::get_align);
ClassDB::bind_method(D_METHOD("set_suffix", "suffix"), &SpinBox::set_suffix);
ClassDB::bind_method(D_METHOD("get_suffix"), &SpinBox::get_suffix);
ClassDB::bind_method(D_METHOD("set_prefix", "prefix"), &SpinBox::set_prefix);
ClassDB::bind_method(D_METHOD("get_prefix"), &SpinBox::get_prefix);
- ClassDB::bind_method(D_METHOD("set_editable", "editable"), &SpinBox::set_editable);
+ ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &SpinBox::set_editable);
ClassDB::bind_method(D_METHOD("is_editable"), &SpinBox::is_editable);
- ClassDB::bind_method(D_METHOD("set_update_on_text_changed"), &SpinBox::set_update_on_text_changed);
+ ClassDB::bind_method(D_METHOD("set_update_on_text_changed", "enabled"), &SpinBox::set_update_on_text_changed);
ClassDB::bind_method(D_METHOD("get_update_on_text_changed"), &SpinBox::get_update_on_text_changed);
ClassDB::bind_method(D_METHOD("apply"), &SpinBox::apply);
ClassDB::bind_method(D_METHOD("get_line_edit"), &SpinBox::get_line_edit);
diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h
index 9828b894da..f2299ce1c2 100644
--- a/scene/gui/spin_box.h
+++ b/scene/gui/spin_box.h
@@ -82,7 +82,7 @@ public:
void set_align(LineEdit::Align p_align);
LineEdit::Align get_align() const;
- void set_editable(bool p_editable);
+ void set_editable(bool p_enabled);
bool is_editable() const;
void set_suffix(const String &p_suffix);
@@ -91,7 +91,7 @@ public:
void set_prefix(const String &p_prefix);
String get_prefix() const;
- void set_update_on_text_changed(bool p_update);
+ void set_update_on_text_changed(bool p_enabled);
bool get_update_on_text_changed() const;
void apply();
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index 7faa9aca2c..0755a79eee 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -98,29 +98,45 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) {
if (mm.is_valid()) {
Point2 pos = mm->get_position();
- highlight_arrow = -1;
if (buttons_visible) {
Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
if (is_layout_rtl()) {
if (pos.x < decr->get_width()) {
- highlight_arrow = 1;
+ if (highlight_arrow != 1) {
+ highlight_arrow = 1;
+ update();
+ }
} else if (pos.x < incr->get_width() + decr->get_width()) {
- highlight_arrow = 0;
+ if (highlight_arrow != 0) {
+ highlight_arrow = 0;
+ update();
+ }
+ } else if (highlight_arrow != -1) {
+ highlight_arrow = -1;
+ update();
}
} else {
int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
if (pos.x > limit_minus_buttons + decr->get_width()) {
- highlight_arrow = 1;
+ if (highlight_arrow != 1) {
+ highlight_arrow = 1;
+ update();
+ }
} else if (pos.x > limit_minus_buttons) {
- highlight_arrow = 0;
+ if (highlight_arrow != 0) {
+ highlight_arrow = 0;
+ update();
+ }
+ } else if (highlight_arrow != -1) {
+ highlight_arrow = -1;
+ update();
}
}
}
_update_hover();
- update();
return;
}
@@ -140,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();
}
}
@@ -148,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;
@@ -206,7 +223,7 @@ void Tabs::gui_input(const Ref<InputEvent> &p_event) {
}
int found = -1;
- for (int i = offset; i < tabs.size(); i++) {
+ for (int i = offset; i <= max_drawn_tab; i++) {
if (tabs[i].rb_rect.has_point(pos)) {
rb_pressing = true;
update();
@@ -385,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"));
@@ -417,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"));
@@ -433,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);
}
@@ -870,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
@@ -897,7 +914,7 @@ void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) {
int Tabs::get_tab_idx_at_point(const Point2 &p_point) const {
int hover_now = -1;
- for (int i = offset; i < tabs.size(); i++) {
+ for (int i = offset; i <= max_drawn_tab; i++) {
Rect2 rect = get_tab_rect(i);
if (rect.has_point(p_point)) {
hover_now = i;
@@ -1149,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 09899413f2..dc9a3f337e 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);
}
@@ -107,11 +103,11 @@ void TextEdit::Text::set_direction_and_language(TextServer::Direction p_directio
is_dirty = true;
}
-void TextEdit::Text::set_draw_control_chars(bool p_draw_control_chars) {
- if (draw_control_chars == p_draw_control_chars) {
+void TextEdit::Text::set_draw_control_chars(bool p_enabled) {
+ if (draw_control_chars == p_enabled) {
return;
}
- draw_control_chars = p_draw_control_chars;
+ draw_control_chars = p_enabled;
is_dirty = true;
}
@@ -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;
}
}
@@ -2383,6 +2399,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
}
String TextEdit::get_tooltip(const Point2 &p_pos) const {
+ Object *tooltip_obj = ObjectDB::get_instance(tooltip_obj_id);
if (!tooltip_obj) {
return Control::get_tooltip(p_pos);
}
@@ -2405,7 +2422,8 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const {
}
void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata) {
- tooltip_obj = p_obj;
+ ERR_FAIL_NULL(p_obj);
+ tooltip_obj_id = p_obj->get_instance_id();
tooltip_func = p_function;
tooltip_ud = p_udata;
}
@@ -2554,8 +2572,8 @@ bool TextEdit::is_overtype_mode_enabled() const {
return overtype_mode;
}
-void TextEdit::set_context_menu_enabled(bool p_enable) {
- context_menu_enabled = p_enable;
+void TextEdit::set_context_menu_enabled(bool p_enabled) {
+ context_menu_enabled = p_enabled;
}
bool TextEdit::is_context_menu_enabled() const {
@@ -2570,8 +2588,8 @@ bool TextEdit::is_shortcut_keys_enabled() const {
return shortcut_keys_enabled;
}
-void TextEdit::set_virtual_keyboard_enabled(bool p_enable) {
- virtual_keyboard_enabled = p_enable;
+void TextEdit::set_virtual_keyboard_enabled(bool p_enabled) {
+ virtual_keyboard_enabled = p_enabled;
}
bool TextEdit::is_virtual_keyboard_enabled() const {
@@ -2731,7 +2749,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 +2764,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) {
@@ -3481,8 +3504,8 @@ void TextEdit::set_caret_blink_speed(const float p_speed) {
caret_blink_timer->set_wait_time(p_speed);
}
-void TextEdit::set_move_caret_on_right_click_enabled(const bool p_enable) {
- move_caret_on_right_click = p_enable;
+void TextEdit::set_move_caret_on_right_click_enabled(const bool p_enabled) {
+ move_caret_on_right_click = p_enabled;
}
bool TextEdit::is_move_caret_on_right_click_enabled() const {
@@ -3600,10 +3623,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 +3710,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) {
- 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;
}
}
@@ -3890,9 +3913,9 @@ Vector<String> TextEdit::get_line_wrapped_text(int p_line) const {
/* Viewport */
// Scrolling.
-void TextEdit::set_smooth_scroll_enabled(const bool p_enable) {
- v_scroll->set_smooth_scroll_enabled(p_enable);
- smooth_scroll_enabled = p_enable;
+void TextEdit::set_smooth_scroll_enabled(const bool p_enabled) {
+ v_scroll->set_smooth_scroll_enabled(p_enabled);
+ smooth_scroll_enabled = p_enabled;
}
bool TextEdit::is_smooth_scroll_enabled() const {
@@ -4151,9 +4174,9 @@ void TextEdit::center_viewport_to_caret() {
}
/* Minimap */
-void TextEdit::set_draw_minimap(bool p_draw) {
- if (draw_minimap != p_draw) {
- draw_minimap = p_draw;
+void TextEdit::set_draw_minimap(bool p_enabled) {
+ if (draw_minimap != p_enabled) {
+ draw_minimap = p_enabled;
_update_wrap_at_column();
}
update();
@@ -4434,9 +4457,9 @@ bool TextEdit::is_highlight_all_occurrences_enabled() const {
return highlight_all_occurrences;
}
-void TextEdit::set_draw_control_chars(bool p_draw_control_chars) {
- if (draw_control_chars != p_draw_control_chars) {
- draw_control_chars = p_draw_control_chars;
+void TextEdit::set_draw_control_chars(bool p_enabled) {
+ if (draw_control_chars != p_enabled) {
+ draw_control_chars = p_enabled;
if (menu) {
menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars);
}
@@ -4450,8 +4473,8 @@ bool TextEdit::get_draw_control_chars() const {
return draw_control_chars;
}
-void TextEdit::set_draw_tabs(bool p_draw) {
- draw_tabs = p_draw;
+void TextEdit::set_draw_tabs(bool p_enabled) {
+ draw_tabs = p_enabled;
update();
}
@@ -4459,8 +4482,8 @@ bool TextEdit::is_drawing_tabs() const {
return draw_tabs;
}
-void TextEdit::set_draw_spaces(bool p_draw) {
- draw_spaces = p_draw;
+void TextEdit::set_draw_spaces(bool p_enabled) {
+ draw_spaces = p_enabled;
update();
}
@@ -4477,7 +4500,7 @@ void TextEdit::_bind_methods() {
// Text properties
ClassDB::bind_method(D_METHOD("has_ime_text"), &TextEdit::has_ime_text);
- ClassDB::bind_method(D_METHOD("set_editable", "enable"), &TextEdit::set_editable);
+ ClassDB::bind_method(D_METHOD("set_editable", "enabled"), &TextEdit::set_editable);
ClassDB::bind_method(D_METHOD("is_editable"), &TextEdit::is_editable);
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextEdit::set_text_direction);
@@ -4502,13 +4525,13 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_overtype_mode_enabled", "enabled"), &TextEdit::set_overtype_mode_enabled);
ClassDB::bind_method(D_METHOD("is_overtype_mode_enabled"), &TextEdit::is_overtype_mode_enabled);
- ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &TextEdit::set_context_menu_enabled);
+ ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enabled"), &TextEdit::set_context_menu_enabled);
ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &TextEdit::is_context_menu_enabled);
- ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &TextEdit::set_shortcut_keys_enabled);
+ ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enabled"), &TextEdit::set_shortcut_keys_enabled);
ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &TextEdit::is_shortcut_keys_enabled);
- ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enable"), &TextEdit::set_virtual_keyboard_enabled);
+ ClassDB::bind_method(D_METHOD("set_virtual_keyboard_enabled", "enabled"), &TextEdit::set_virtual_keyboard_enabled);
ClassDB::bind_method(D_METHOD("is_virtual_keyboard_enabled"), &TextEdit::is_virtual_keyboard_enabled);
// Text manipulation
@@ -4743,7 +4766,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("center_viewport_to_caret"), &TextEdit::center_viewport_to_caret);
// Minimap
- ClassDB::bind_method(D_METHOD("draw_minimap", "draw"), &TextEdit::set_draw_minimap);
+ ClassDB::bind_method(D_METHOD("set_draw_minimap", "enabled"), &TextEdit::set_draw_minimap);
ClassDB::bind_method(D_METHOD("is_drawing_minimap"), &TextEdit::is_drawing_minimap);
ClassDB::bind_method(D_METHOD("set_minimap_width", "width"), &TextEdit::set_minimap_width);
@@ -4799,16 +4822,16 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_highlight_current_line", "enabled"), &TextEdit::set_highlight_current_line);
ClassDB::bind_method(D_METHOD("is_highlight_current_line_enabled"), &TextEdit::is_highlight_current_line_enabled);
- ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enable"), &TextEdit::set_highlight_all_occurrences);
+ ClassDB::bind_method(D_METHOD("set_highlight_all_occurrences", "enabled"), &TextEdit::set_highlight_all_occurrences);
ClassDB::bind_method(D_METHOD("is_highlight_all_occurrences_enabled"), &TextEdit::is_highlight_all_occurrences_enabled);
ClassDB::bind_method(D_METHOD("get_draw_control_chars"), &TextEdit::get_draw_control_chars);
- ClassDB::bind_method(D_METHOD("set_draw_control_chars", "enable"), &TextEdit::set_draw_control_chars);
+ ClassDB::bind_method(D_METHOD("set_draw_control_chars", "enabled"), &TextEdit::set_draw_control_chars);
- ClassDB::bind_method(D_METHOD("set_draw_tabs"), &TextEdit::set_draw_tabs);
+ ClassDB::bind_method(D_METHOD("set_draw_tabs", "enabled"), &TextEdit::set_draw_tabs);
ClassDB::bind_method(D_METHOD("is_drawing_tabs"), &TextEdit::is_drawing_tabs);
- ClassDB::bind_method(D_METHOD("set_draw_spaces"), &TextEdit::set_draw_spaces);
+ ClassDB::bind_method(D_METHOD("set_draw_spaces", "enabled"), &TextEdit::set_draw_spaces);
ClassDB::bind_method(D_METHOD("is_drawing_spaces"), &TextEdit::is_drawing_spaces);
ClassDB::bind_method(D_METHOD("get_menu"), &TextEdit::get_menu);
@@ -4845,7 +4868,7 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll");
ADD_GROUP("Minimap", "minimap_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "draw_minimap", "is_drawing_minimap");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "set_draw_minimap", "is_drawing_minimap");
ADD_PROPERTY(PropertyInfo(Variant::INT, "minimap_width"), "set_minimap_width", "get_minimap_width");
ADD_GROUP("Caret", "caret_");
@@ -5051,10 +5074,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 +5090,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 +5381,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 +5443,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) {
- 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 +6035,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..16c1ee9ff9 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);
@@ -182,7 +182,7 @@ private:
void set_font_size(int p_font_size);
void set_font_features(const Dictionary &p_features);
void set_direction_and_language(TextServer::Direction p_direction, const String &p_language);
- void set_draw_control_chars(bool p_draw_control_chars);
+ void set_draw_control_chars(bool p_enabled);
int get_line_height() const;
int get_line_width(int p_line, int p_wrap_index = -1) const;
@@ -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();
@@ -330,7 +330,7 @@ private:
int _get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column) const;
/* Tooltip. */
- Object *tooltip_obj = nullptr;
+ ObjectID tooltip_obj_id;
StringName tooltip_func;
Variant tooltip_ud;
@@ -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();
@@ -630,13 +631,13 @@ public:
void set_overtype_mode_enabled(const bool p_enabled);
bool is_overtype_mode_enabled() const;
- void set_context_menu_enabled(bool p_enable);
+ void set_context_menu_enabled(bool p_enabled);
bool is_context_menu_enabled() const;
void set_shortcut_keys_enabled(bool p_enabled);
bool is_shortcut_keys_enabled() const;
- void set_virtual_keyboard_enabled(bool p_enable);
+ void set_virtual_keyboard_enabled(bool p_enabled);
bool is_virtual_keyboard_enabled() const;
// Text manipulation
@@ -722,7 +723,7 @@ public:
void set_caret_blink_speed(const float p_speed);
float get_caret_blink_speed() const;
- void set_move_caret_on_right_click_enabled(const bool p_enable);
+ void set_move_caret_on_right_click_enabled(const bool p_enabled);
bool is_move_caret_on_right_click_enabled() const;
void set_caret_mid_grapheme_enabled(const bool p_enabled);
@@ -782,7 +783,7 @@ public:
/* Viewport. */
// Scrolling.
- void set_smooth_scroll_enabled(const bool p_enable);
+ void set_smooth_scroll_enabled(const bool p_enabled);
bool is_smooth_scroll_enabled() const;
void set_scroll_past_end_of_file_enabled(const bool p_enabled);
@@ -817,7 +818,7 @@ public:
void center_viewport_to_caret();
// Minimap
- void set_draw_minimap(bool p_draw);
+ void set_draw_minimap(bool p_enabled);
bool is_drawing_minimap() const;
void set_minimap_width(int p_minimap_width);
@@ -884,13 +885,13 @@ public:
void set_highlight_all_occurrences(const bool p_enabled);
bool is_highlight_all_occurrences_enabled() const;
- void set_draw_control_chars(bool p_draw_control_chars);
+ void set_draw_control_chars(bool p_enabled);
bool get_draw_control_chars() const;
- void set_draw_tabs(bool p_draw);
+ void set_draw_tabs(bool p_enabled);
bool is_drawing_tabs() const;
- void set_draw_spaces(bool p_draw);
+ void set_draw_spaces(bool p_enabled);
bool is_drawing_spaces() const;
TextEdit();
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index 35a098c6fd..fe11de128a 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -341,7 +341,11 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu
} break;
case FILL_BILINEAR_LEFT_AND_RIGHT: {
double center_mapped_from_real_width = (width_total * 0.5 - topleft.x) / max_middle_real_size * max_middle_texture_size + topleft.x;
- double drift_from_unscaled_center = (src_rect.size.x * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.x - topleft.x);
+ double drift_from_unscaled_center = 0;
+ if (bottomright.y != topleft.y) { // To avoid division by zero.
+ drift_from_unscaled_center = (src_rect.size.x * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.x - topleft.x);
+ }
+
src_rect.position.x += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5;
src_rect.size.x = width_texture;
dst_rect.position.x += (width_total - width_filled) * 0.5;
@@ -351,7 +355,11 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu
} break;
case FILL_BILINEAR_TOP_AND_BOTTOM: {
double center_mapped_from_real_width = (width_total * 0.5 - topleft.y) / max_middle_real_size * max_middle_texture_size + topleft.y;
- double drift_from_unscaled_center = (src_rect.size.y * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.y - topleft.y);
+ double drift_from_unscaled_center = 0;
+ if (bottomright.y != topleft.y) { // To avoid division by zero.
+ drift_from_unscaled_center = (src_rect.size.y * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.y - topleft.y);
+ }
+
src_rect.position.y += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5;
src_rect.size.y = width_texture;
dst_rect.position.y += (width_total - width_filled) * 0.5;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 2be59ca04d..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 {
@@ -144,6 +140,7 @@ void TreeItem::_change_tree(Tree *p_tree) {
/* cell mode */
void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) {
ERR_FAIL_INDEX(p_column, cells.size());
+
Cell &c = cells.write[p_column];
c.mode = p_mode;
c.min = 0;
@@ -155,8 +152,9 @@ void TreeItem::set_cell_mode(int p_column, TreeCellMode p_mode) {
c.text = "";
c.dirty = true;
c.icon_max_w = 0;
+ c.cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const {
@@ -167,22 +165,27 @@ TreeItem::TreeCellMode TreeItem::get_cell_mode(int p_column) const {
/* check mode */
void TreeItem::set_checked(int p_column, bool p_checked) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].checked = p_checked;
cells.write[p_column].indeterminate = false;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
void TreeItem::set_indeterminate(int p_column, bool p_indeterminate) {
ERR_FAIL_INDEX(p_column, cells.size());
+
// Prevent uncheck if indeterminate set to false twice
if (p_indeterminate == cells[p_column].indeterminate) {
return;
}
+
cells.write[p_column].indeterminate = p_indeterminate;
cells.write[p_column].checked = false;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
bool TreeItem::is_checked(int p_column) const {
@@ -214,8 +217,10 @@ void TreeItem::set_text(int p_column, String p_text) {
}
cells.write[p_column].step = 0;
}
+
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
String TreeItem::get_text(int p_column) const {
@@ -231,7 +236,7 @@ void TreeItem::set_text_direction(int p_column, Control::TextDirection p_text_di
cells.write[p_column].dirty = true;
_changed_notify(p_column);
}
- cached_minimum_size_dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
}
Control::TextDirection TreeItem::get_text_direction(int p_column) const {
@@ -241,10 +246,12 @@ Control::TextDirection TreeItem::get_text_direction(int p_column) const {
void TreeItem::clear_opentype_features(int p_column) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].opentype_features.clear();
cells.write[p_column].dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_value) {
@@ -253,8 +260,9 @@ void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_va
if (!cells[p_column].opentype_features.has(tag) || (int)cells[p_column].opentype_features[tag] != p_value) {
cells.write[p_column].opentype_features[tag] = p_value;
cells.write[p_column].dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
}
@@ -269,11 +277,13 @@ int TreeItem::get_opentype_feature(int p_column, const String &p_name) const {
void TreeItem::set_structured_text_bidi_override(int p_column, Control::StructuredTextParser p_parser) {
ERR_FAIL_INDEX(p_column, cells.size());
+
if (cells[p_column].st_parser != p_parser) {
cells.write[p_column].st_parser = p_parser;
cells.write[p_column].dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
}
@@ -284,10 +294,12 @@ Control::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_
void TreeItem::set_structured_text_bidi_override_options(int p_column, Array p_args) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].st_args = p_args;
cells.write[p_column].dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
Array TreeItem::get_structured_text_bidi_override_options(int p_column) const {
@@ -297,11 +309,13 @@ Array TreeItem::get_structured_text_bidi_override_options(int p_column) const {
void TreeItem::set_language(int p_column, const String &p_language) {
ERR_FAIL_INDEX(p_column, cells.size());
+
if (cells[p_column].language != p_language) {
cells.write[p_column].language = p_language;
cells.write[p_column].dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
}
@@ -312,10 +326,11 @@ String TreeItem::get_language(int p_column) const {
void TreeItem::set_suffix(int p_column, String p_suffix) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].suffix = p_suffix;
+ cells.write[p_column].cached_minimum_size_dirty = true;
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
String TreeItem::get_suffix(int p_column) const {
@@ -325,9 +340,11 @@ String TreeItem::get_suffix(int p_column) const {
void TreeItem::set_icon(int p_column, const Ref<Texture2D> &p_icon) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].icon = p_icon;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
Ref<Texture2D> TreeItem::get_icon(int p_column) const {
@@ -337,9 +354,11 @@ Ref<Texture2D> TreeItem::get_icon(int p_column) const {
void TreeItem::set_icon_region(int p_column, const Rect2 &p_icon_region) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].icon_region = p_icon_region;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
Rect2 TreeItem::get_icon_region(int p_column) const {
@@ -360,9 +379,11 @@ Color TreeItem::get_icon_modulate(int p_column) const {
void TreeItem::set_icon_max_width(int p_column, int p_max) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].icon_max_w = p_max;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
int TreeItem::get_icon_max_width(int p_column) const {
@@ -474,8 +495,11 @@ void TreeItem::uncollapse_tree() {
void TreeItem::set_custom_minimum_height(int p_height) {
custom_min_height = p_height;
+
+ for (Cell &c : cells)
+ c.cached_minimum_size_dirty = true;
+
_changed_notify();
- cached_minimum_size_dirty = true;
}
int TreeItem::get_custom_minimum_height() const {
@@ -799,8 +823,9 @@ void TreeItem::add_button(int p_column, const Ref<Texture2D> &p_button, int p_id
button.disabled = p_disabled;
button.tooltip = p_tooltip;
cells.write[p_column].buttons.push_back(button);
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
int TreeItem::get_button_count(int p_column) const {
@@ -843,8 +868,9 @@ void TreeItem::set_button(int p_column, int p_idx, const Ref<Texture2D> &p_butto
ERR_FAIL_INDEX(p_column, cells.size());
ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size());
cells.write[p_column].buttons.write[p_idx].texture = p_button;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
void TreeItem::set_button_color(int p_column, int p_idx, const Color &p_color) {
@@ -859,8 +885,9 @@ void TreeItem::set_button_disabled(int p_column, int p_idx, bool p_disabled) {
ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size());
cells.write[p_column].buttons.write[p_idx].disabled = p_disabled;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
bool TreeItem::is_button_disabled(int p_column, int p_idx) const {
@@ -872,9 +899,11 @@ bool TreeItem::is_button_disabled(int p_column, int p_idx) const {
void TreeItem::set_editable(int p_column, bool p_editable) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].editable = p_editable;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
bool TreeItem::is_editable(int p_column) {
@@ -906,8 +935,9 @@ void TreeItem::clear_custom_color(int p_column) {
void TreeItem::set_custom_font(int p_column, const Ref<Font> &p_font) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].custom_font = p_font;
- cached_minimum_size_dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
}
Ref<Font> TreeItem::get_custom_font(int p_column) const {
@@ -917,8 +947,9 @@ Ref<Font> TreeItem::get_custom_font(int p_column) const {
void TreeItem::set_custom_font_size(int p_column, int p_font_size) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].custom_font_size = p_font_size;
- cached_minimum_size_dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
}
int TreeItem::get_custom_font_size(int p_column) const {
@@ -961,8 +992,9 @@ Color TreeItem::get_custom_bg_color(int p_column) const {
void TreeItem::set_custom_as_button(int p_column, bool p_button) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].custom_button = p_button;
- cached_minimum_size_dirty = true;
+ cells.write[p_column].cached_minimum_size_dirty = true;
}
bool TreeItem::is_custom_set_as_button(int p_column) const {
@@ -972,9 +1004,11 @@ bool TreeItem::is_custom_set_as_button(int p_column) const {
void TreeItem::set_text_align(int p_column, TextAlign p_align) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].text_align = p_align;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
TreeItem::TextAlign TreeItem::get_text_align(int p_column) const {
@@ -984,9 +1018,11 @@ TreeItem::TextAlign TreeItem::get_text_align(int p_column) const {
void TreeItem::set_expand_right(int p_column, bool p_enable) {
ERR_FAIL_INDEX(p_column, cells.size());
+
cells.write[p_column].expand_right = p_enable;
+ cells.write[p_column].cached_minimum_size_dirty = true;
+
_changed_notify(p_column);
- cached_minimum_size_dirty = true;
}
bool TreeItem::get_expand_right(int p_column) const {
@@ -996,8 +1032,11 @@ bool TreeItem::get_expand_right(int p_column) const {
void TreeItem::set_disable_folding(bool p_disable) {
disable_folding = p_disable;
+
+ for (Cell &c : cells)
+ c.cached_minimum_size_dirty = true;
+
_changed_notify(0);
- cached_minimum_size_dirty = true;
}
bool TreeItem::is_folding_disabled() const {
@@ -1009,14 +1048,12 @@ Size2 TreeItem::get_minimum_size(int p_column) {
Tree *tree = get_tree();
ERR_FAIL_COND_V(!tree, Size2());
- if (cached_minimum_size_dirty) {
- Size2 size;
+ const TreeItem::Cell &cell = cells[p_column];
- // Default offset?
- //size.width += (disable_folding || tree->hide_folding) ? tree->cache.hseparation : tree->cache.item_margin;
+ if (cell.cached_minimum_size_dirty) {
+ Size2 size;
// Text.
- const TreeItem::Cell &cell = cells[p_column];
if (!cell.text.is_empty()) {
if (cell.dirty) {
tree->update_item_cell(this, p_column);
@@ -1052,11 +1089,11 @@ Size2 TreeItem::get_minimum_size(int p_column) {
size.width += (cell.buttons.size() - 1) * tree->cache.button_margin;
}
- cached_minimum_size = size;
- cached_minimum_size_dirty = false;
+ cells.write[p_column].cached_minimum_size = size;
+ cells.write[p_column].cached_minimum_size_dirty = false;
}
- return cached_minimum_size;
+ return cell.cached_minimum_size;
}
Variant TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
@@ -1336,11 +1373,9 @@ 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"));
- v_scroll->set_custom_step(cache.font->get_height(cache.font_size));
+ cache.base_scale = get_theme_default_base_scale();
- for (TreeItem *item = get_root(); item; item = item->get_next()) {
- item->cached_minimum_size_dirty = true;
- }
+ v_scroll->set_custom_step(cache.font->get_height(cache.font_size));
}
int Tree::compute_item_height(TreeItem *p_item) const {
@@ -2009,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;
@@ -4001,10 +4030,12 @@ TreeItem *Tree::get_next_selected(TreeItem *p_item) {
int Tree::get_column_minimum_width(int p_column) const {
ERR_FAIL_INDEX_V(p_column, columns.size(), -1);
+ // Use the custom minimum width.
int min_width = columns[p_column].custom_min_width;
+ // Check if the visible title of the column is wider.
if (show_column_titles) {
- min_width = MAX(cache.font->get_string_size(columns[p_column].title).width, min_width);
+ min_width = MAX(cache.font->get_string_size(columns[p_column].title).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width);
}
if (!columns[p_column].clip_content) {
@@ -4029,7 +4060,11 @@ int Tree::get_column_minimum_width(int p_column) const {
Size2 item_size = item->get_minimum_size(p_column);
if (p_column == 0) {
item_size.width += cache.item_margin * depth;
+ } else {
+ item_size.width += cache.hseparation;
}
+
+ // Check if the item is wider.
min_width = MAX(min_width, item_size.width);
}
}
@@ -4069,9 +4104,6 @@ int Tree::get_column_width(int p_column) const {
}
}
- if (p_column < columns.size() - 1) {
- column_width += cache.hseparation;
- }
return column_width;
}
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 85fed941dc..6ca9458e9b 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -95,6 +95,9 @@ private:
bool expand_right = false;
Color icon_color = Color(1, 1, 1);
+ Size2i cached_minimum_size;
+ bool cached_minimum_size_dirty = true;
+
TextAlign text_align = ALIGN_LEFT;
Variant meta;
@@ -130,9 +133,6 @@ private:
bool disable_folding = false;
int custom_min_height = 0;
- Size2i cached_minimum_size;
- bool cached_minimum_size_dirty = true;
-
TreeItem *parent = nullptr; // parent item
TreeItem *prev = nullptr; // previous in list
TreeItem *next = nullptr; // next in list
@@ -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/http_request.cpp b/scene/main/http_request.cpp
index f24d880045..a4fcc04e20 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -104,9 +104,11 @@ Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_h
CharString charstr = p_request_data.utf8();
size_t len = charstr.length();
- raw_data.resize(len);
- uint8_t *w = raw_data.ptrw();
- memcpy(w, charstr.ptr(), len);
+ if (len > 0) {
+ raw_data.resize(len);
+ uint8_t *w = raw_data.ptrw();
+ memcpy(w, charstr.ptr(), len);
+ }
return request_raw(p_url, p_custom_headers, p_ssl_validate_domain, p_method, raw_data);
}
@@ -597,7 +599,7 @@ void HTTPRequest::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_timeout", "timeout"), &HTTPRequest::set_timeout);
ClassDB::bind_method(D_METHOD("get_timeout"), &HTTPRequest::get_timeout);
- ClassDB::bind_method(D_METHOD("set_download_chunk_size"), &HTTPRequest::set_download_chunk_size);
+ ClassDB::bind_method(D_METHOD("set_download_chunk_size", "chunk_size"), &HTTPRequest::set_download_chunk_size);
ClassDB::bind_method(D_METHOD("get_download_chunk_size"), &HTTPRequest::get_download_chunk_size);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "download_file", PROPERTY_HINT_FILE), "set_download_file", "get_download_file");
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index f5d2d2f2a2..189aebb47d 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;
}
}
@@ -540,12 +536,13 @@ bool Node::is_multiplayer_authority() const {
/***** RPC CONFIG ********/
-uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, Multiplayer::TransferMode p_transfer_mode, int p_channel) {
+uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local, Multiplayer::TransferMode p_transfer_mode, int p_channel) {
for (int i = 0; i < data.rpc_methods.size(); i++) {
if (data.rpc_methods[i].name == p_method) {
Multiplayer::RPCConfig &nd = data.rpc_methods.write[i];
nd.rpc_mode = p_rpc_mode;
nd.transfer_mode = p_transfer_mode;
+ nd.call_local = p_call_local;
nd.channel = p_channel;
return i | (1 << 15);
}
@@ -556,6 +553,7 @@ uint16_t Node::rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc
nd.rpc_mode = p_rpc_mode;
nd.transfer_mode = p_transfer_mode;
nd.channel = p_channel;
+ nd.call_local = p_call_local;
data.rpc_methods.push_back(nd);
return ((uint16_t)data.rpc_methods.size() - 1) | (1 << 15);
}
@@ -1678,10 +1676,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 +1687,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 +1844,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 +1946,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 +1970,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 +2002,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 +2311,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 +2534,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 +2685,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);
@@ -2750,7 +2742,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_multiplayer"), &Node::get_multiplayer);
ClassDB::bind_method(D_METHOD("get_custom_multiplayer"), &Node::get_custom_multiplayer);
ClassDB::bind_method(D_METHOD("set_custom_multiplayer", "api"), &Node::set_custom_multiplayer);
- ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("rpc_config", "method", "rpc_mode", "call_local", "transfer_mode", "channel"), &Node::rpc_config, DEFVAL(false), DEFVAL(Multiplayer::TRANSFER_MODE_RELIABLE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_editor_description", "editor_description"), &Node::set_editor_description);
ClassDB::bind_method(D_METHOD("get_editor_description"), &Node::get_editor_description);
@@ -2839,7 +2831,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..e59a7a390a 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;
@@ -466,7 +466,7 @@ public:
int get_multiplayer_authority() const;
bool is_multiplayer_authority() const;
- uint16_t rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, Multiplayer::TransferMode p_transfer_mode, int p_channel = 0); // config a local method for RPC
+ uint16_t rpc_config(const StringName &p_method, Multiplayer::RPCMode p_rpc_mode, bool p_call_local = false, Multiplayer::TransferMode p_transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE, int p_channel = 0); // config a local method for RPC
Vector<Multiplayer::RPCConfig> get_node_rpc_methods() const;
void rpc(const StringName &p_method, VARIANT_ARG_LIST); // RPC, honors RPCMode, TransferMode, channel
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..a122241cd0 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -570,7 +570,11 @@ void SceneTree::finalize() {
root = nullptr;
}
- // cleanup timers
+ // In case deletion of some objects was queued when destructing the `root`.
+ // E.g. if `queue_free()` was called for some node outside the tree when handling NOTIFICATION_PREDELETE for some node in the tree.
+ _flush_delete_queue();
+
+ // Cleanup timers.
for (Ref<SceneTreeTimer> &timer : timers) {
timer->release_connections();
}
@@ -1105,7 +1109,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/timer.cpp b/scene/main/timer.cpp
index 9e462eb1c8..154e4cf683 100644
--- a/scene/main/timer.cpp
+++ b/scene/main/timer.cpp
@@ -82,6 +82,7 @@ void Timer::_notification(int p_what) {
void Timer::set_wait_time(double p_time) {
ERR_FAIL_COND_MSG(p_time <= 0, "Time should be greater than zero.");
wait_time = p_time;
+ update_configuration_warnings();
}
double Timer::get_wait_time() const {
@@ -179,6 +180,16 @@ void Timer::_set_process(bool p_process, bool p_force) {
processing = p_process;
}
+TypedArray<String> Timer::get_configuration_warnings() const {
+ TypedArray<String> warnings = Node::get_configuration_warnings();
+
+ if (wait_time < 0.05 - CMP_EPSILON) {
+ warnings.push_back(TTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times."));
+ }
+
+ return warnings;
+}
+
void Timer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_wait_time", "time_sec"), &Timer::set_wait_time);
ClassDB::bind_method(D_METHOD("get_wait_time"), &Timer::get_wait_time);
diff --git a/scene/main/timer.h b/scene/main/timer.h
index 2b9faddcb9..e2f34042dd 100644
--- a/scene/main/timer.h
+++ b/scene/main/timer.h
@@ -73,6 +73,8 @@ public:
double get_time_left() const;
+ TypedArray<String> get_configuration_warnings() const override;
+
void set_timer_process_callback(TimerProcessCallback p_callback);
TimerProcessCallback get_timer_process_callback() const;
Timer();
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index b3b743370b..0e62e6e30a 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -57,17 +57,17 @@
#include "servers/audio_server.h"
void ViewportTexture::setup_local_to_scene() {
+ Node *local_scene = get_local_scene();
+ if (!local_scene) {
+ return;
+ }
+
if (vp) {
vp->viewport_textures.erase(this);
}
vp = nullptr;
- Node *local_scene = get_local_scene();
- if (!local_scene) {
- return;
- }
-
Node *vpn = local_scene->get_node(path);
ERR_FAIL_COND_MSG(!vpn, "ViewportTexture: Path to node is invalid.");
@@ -1079,8 +1079,11 @@ Transform2D Viewport::_get_input_pre_xform() const {
}
Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) {
- Transform2D ai = get_final_transform().affine_inverse() * _get_input_pre_xform();
+ if (ev.is_null()) {
+ return ev; // No transformation defined for null event
+ }
+ Transform2D ai = get_final_transform().affine_inverse() * _get_input_pre_xform();
return ev->xformed_by(ai);
}
@@ -2878,9 +2881,8 @@ bool Viewport::gui_is_dragging() const {
void Viewport::set_input_as_handled() {
_drop_physics_mouseover();
- if (handle_input_locally) {
- local_input_handled = true;
- } else {
+
+ if (!handle_input_locally) {
ERR_FAIL_COND(!is_inside_tree());
Viewport *vp = this;
while (true) {
@@ -2892,16 +2894,19 @@ void Viewport::set_input_as_handled() {
}
vp = vp->get_parent()->get_viewport();
}
- vp->set_input_as_handled();
+ if (vp != this) {
+ vp->set_input_as_handled();
+ return;
+ }
}
+
+ local_input_handled = true;
}
bool Viewport::is_input_handled() const {
- if (handle_input_locally) {
- return local_input_handled;
- } else {
- const Viewport *vp = this;
+ if (!handle_input_locally) {
ERR_FAIL_COND_V(!is_inside_tree(), false);
+ const Viewport *vp = this;
while (true) {
if (Object::cast_to<Window>(vp)) {
break;
@@ -2911,8 +2916,11 @@ bool Viewport::is_input_handled() const {
}
vp = vp->get_parent()->get_viewport();
}
- return vp->is_input_handled();
+ if (vp != this) {
+ return vp->is_input_handled();
+ }
}
+ return local_input_handled;
}
void Viewport::set_handle_input_locally(bool p_enable) {
@@ -3443,13 +3451,16 @@ bool Viewport::is_using_xr() {
return use_xr;
}
-void Viewport::set_scale_3d(const Scale3D p_scale_3d) {
- scale_3d = p_scale_3d;
+void Viewport::set_scale_3d(float p_scale_3d) {
+ // Clamp to reasonable values that are actually useful.
+ // Values above 2.0 don't serve a practical purpose since the viewport
+ // isn't displayed with mipmaps.
+ scale_3d = CLAMP(p_scale_3d, 0.1, 2.0);
- RS::get_singleton()->viewport_set_scale_3d(viewport, RS::ViewportScale3D(scale_3d));
+ RS::get_singleton()->viewport_set_scale_3d(viewport, scale_3d);
}
-Viewport::Scale3D Viewport::get_scale_3d() const {
+float Viewport::get_scale_3d() const {
return scale_3d;
}
@@ -3582,7 +3593,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_xr"), "set_use_xr", "is_using_xr");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "scale_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled,75%,50%,33%,25%")), "set_scale_3d", "get_scale_3d");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scale_3d", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scale_3d", "get_scale_3d");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_3d"), "set_as_audio_listener_3d", "is_audio_listener_3d");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d");
@@ -3626,12 +3637,6 @@ void Viewport::_bind_methods() {
ADD_SIGNAL(MethodInfo("size_changed"));
ADD_SIGNAL(MethodInfo("gui_focus_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
- BIND_ENUM_CONSTANT(SCALE_3D_DISABLED);
- BIND_ENUM_CONSTANT(SCALE_3D_75_PERCENT);
- BIND_ENUM_CONSTANT(SCALE_3D_50_PERCENT);
- BIND_ENUM_CONSTANT(SCALE_3D_33_PERCENT);
- BIND_ENUM_CONSTANT(SCALE_3D_25_PERCENT);
-
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_1);
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_4);
@@ -3744,8 +3749,7 @@ Viewport::Viewport() {
ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/tooltip_delay_sec", PropertyInfo(Variant::FLOAT, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater")); // No negative numbers
#ifndef _3D_DISABLED
- int scale = GLOBAL_GET("rendering/3d/viewport/scale");
- set_scale_3d((Scale3D)scale);
+ set_scale_3d(GLOBAL_GET("rendering/3d/viewport/scale"));
#endif // _3D_DISABLED
set_sdf_oversize(sdf_oversize); // Set to server.
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 3732f9cfd1..1f19ff04c9 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -89,14 +89,6 @@ class Viewport : public Node {
GDCLASS(Viewport, Node);
public:
- enum Scale3D {
- SCALE_3D_DISABLED,
- SCALE_3D_75_PERCENT,
- SCALE_3D_50_PERCENT,
- SCALE_3D_33_PERCENT,
- SCALE_3D_25_PERCENT
- };
-
enum ShadowAtlasQuadrantSubdiv {
SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED,
SHADOW_ATLAS_QUADRANT_SUBDIV_1,
@@ -592,7 +584,7 @@ public:
#ifndef _3D_DISABLED
bool use_xr = false;
- Scale3D scale_3d = SCALE_3D_DISABLED;
+ float scale_3d = 1.0;
friend class AudioListener3D;
AudioListener3D *audio_listener_3d = nullptr;
Set<AudioListener3D *> audio_listener_3d_set;
@@ -664,8 +656,8 @@ public:
void set_use_xr(bool p_use_xr);
bool is_using_xr();
- void set_scale_3d(const Scale3D p_scale_3d);
- Scale3D get_scale_3d() const;
+ void set_scale_3d(float p_scale_3d);
+ float get_scale_3d() const;
#endif // _3D_DISABLED
Viewport();
@@ -724,7 +716,6 @@ VARIANT_ENUM_CAST(SubViewport::UpdateMode);
VARIANT_ENUM_CAST(Viewport::ShadowAtlasQuadrantSubdiv);
VARIANT_ENUM_CAST(Viewport::MSAA);
VARIANT_ENUM_CAST(Viewport::ScreenSpaceAA);
-VARIANT_ENUM_CAST(Viewport::Scale3D);
VARIANT_ENUM_CAST(Viewport::DebugDraw);
VARIANT_ENUM_CAST(Viewport::SDFScale);
VARIANT_ENUM_CAST(Viewport::SDFOversize);
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/animation.cpp b/scene/resources/animation.cpp
index b4eec2530b..6612c5ff3c 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -43,8 +43,12 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (tracks.size() == track && what == "type") {
String type = p_value;
- if (type == "transform" || type == "transform3d") {
- add_track(TYPE_TRANSFORM3D);
+ if (type == "position_3d") {
+ add_track(TYPE_POSITION_3D);
+ } else if (type == "rotation_3d") {
+ add_track(TYPE_ROTATION_3D);
+ } else if (type == "scale_3d") {
+ add_track(TYPE_SCALE_3D);
} else if (type == "value") {
add_track(TYPE_VALUE);
} else if (type == "method") {
@@ -75,35 +79,72 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
} else if (what == "enabled") {
track_set_enabled(track, p_value);
} else if (what == "keys" || what == "key_values") {
- if (track_get_type(track) == TYPE_TRANSFORM3D) {
- TransformTrack *tt = static_cast<TransformTrack *>(tracks[track]);
+ if (track_get_type(track) == TYPE_POSITION_3D) {
+ PositionTrack *tt = static_cast<PositionTrack *>(tracks[track]);
Vector<real_t> values = p_value;
int vcount = values.size();
- ERR_FAIL_COND_V(vcount % TRANSFORM_TRACK_SIZE, false);
+ ERR_FAIL_COND_V(vcount % POSITION_TRACK_SIZE, false);
const real_t *r = values.ptr();
- int64_t count = vcount / TRANSFORM_TRACK_SIZE;
- tt->transforms.resize(count);
+ int64_t count = vcount / POSITION_TRACK_SIZE;
+ tt->positions.resize(count);
+ TKey<Vector3> *tw = tt->positions.ptrw();
for (int i = 0; i < count; i++) {
- TKey<TransformKey> &tk = tt->transforms.write[i];
- const real_t *ofs = &r[i * TRANSFORM_TRACK_SIZE];
+ TKey<Vector3> &tk = tw[i];
+ const real_t *ofs = &r[i * POSITION_TRACK_SIZE];
tk.time = ofs[0];
tk.transition = ofs[1];
- tk.value.loc.x = ofs[2];
- tk.value.loc.y = ofs[3];
- tk.value.loc.z = ofs[4];
+ tk.value.x = ofs[2];
+ tk.value.y = ofs[3];
+ tk.value.z = ofs[4];
+ }
+ } else if (track_get_type(track) == TYPE_ROTATION_3D) {
+ RotationTrack *rt = static_cast<RotationTrack *>(tracks[track]);
+ Vector<real_t> values = p_value;
+ int vcount = values.size();
+ ERR_FAIL_COND_V(vcount % ROTATION_TRACK_SIZE, false);
+
+ const real_t *r = values.ptr();
+
+ int64_t count = vcount / ROTATION_TRACK_SIZE;
+ rt->rotations.resize(count);
+
+ TKey<Quaternion> *rw = rt->rotations.ptrw();
+ for (int i = 0; i < count; i++) {
+ TKey<Quaternion> &rk = rw[i];
+ const real_t *ofs = &r[i * ROTATION_TRACK_SIZE];
+ rk.time = ofs[0];
+ rk.transition = ofs[1];
+
+ rk.value.x = ofs[2];
+ rk.value.y = ofs[3];
+ rk.value.z = ofs[4];
+ rk.value.w = ofs[5];
+ }
+ } else if (track_get_type(track) == TYPE_SCALE_3D) {
+ ScaleTrack *st = static_cast<ScaleTrack *>(tracks[track]);
+ Vector<real_t> values = p_value;
+ int vcount = values.size();
+ ERR_FAIL_COND_V(vcount % SCALE_TRACK_SIZE, false);
- tk.value.rot.x = ofs[5];
- tk.value.rot.y = ofs[6];
- tk.value.rot.z = ofs[7];
- tk.value.rot.w = ofs[8];
+ const real_t *r = values.ptr();
+
+ int64_t count = vcount / SCALE_TRACK_SIZE;
+ st->scales.resize(count);
- tk.value.scale.x = ofs[9];
- tk.value.scale.y = ofs[10];
- tk.value.scale.z = ofs[11];
+ TKey<Vector3> *sw = st->scales.ptrw();
+ for (int i = 0; i < count; i++) {
+ TKey<Vector3> &sk = sw[i];
+ const real_t *ofs = &r[i * SCALE_TRACK_SIZE];
+ sk.time = ofs[0];
+ sk.transition = ofs[1];
+
+ sk.value.x = ofs[2];
+ sk.value.y = ofs[3];
+ sk.value.z = ofs[4];
}
} else if (track_get_type(track) == TYPE_VALUE) {
@@ -319,8 +360,14 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
ERR_FAIL_INDEX_V(track, tracks.size(), false);
if (what == "type") {
switch (track_get_type(track)) {
- case TYPE_TRANSFORM3D:
- r_ret = "transform";
+ case TYPE_POSITION_3D:
+ r_ret = "position_3d";
+ break;
+ case TYPE_ROTATION_3D:
+ r_ret = "rotation_3d";
+ break;
+ case TYPE_SCALE_3D:
+ r_ret = "scale_3d";
break;
case TYPE_VALUE:
r_ret = "value";
@@ -352,31 +399,64 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
} else if (what == "enabled") {
r_ret = track_is_enabled(track);
} else if (what == "keys") {
- if (track_get_type(track) == TYPE_TRANSFORM3D) {
+ if (track_get_type(track) == TYPE_POSITION_3D) {
Vector<real_t> keys;
int kk = track_get_key_count(track);
- keys.resize(kk * TRANSFORM_TRACK_SIZE);
+ keys.resize(kk * POSITION_TRACK_SIZE);
real_t *w = keys.ptrw();
int idx = 0;
for (int i = 0; i < track_get_key_count(track); i++) {
Vector3 loc;
- Quaternion rot;
- Vector3 scale;
- transform_track_get_key(track, i, &loc, &rot, &scale);
+ position_track_get_key(track, i, &loc);
w[idx++] = track_get_key_time(track, i);
w[idx++] = track_get_key_transition(track, i);
w[idx++] = loc.x;
w[idx++] = loc.y;
w[idx++] = loc.z;
+ }
+ r_ret = keys;
+ return true;
+ } else if (track_get_type(track) == TYPE_ROTATION_3D) {
+ Vector<real_t> keys;
+ int kk = track_get_key_count(track);
+ keys.resize(kk * ROTATION_TRACK_SIZE);
+
+ real_t *w = keys.ptrw();
+
+ int idx = 0;
+ for (int i = 0; i < track_get_key_count(track); i++) {
+ Quaternion rot;
+ rotation_track_get_key(track, i, &rot);
+
+ w[idx++] = track_get_key_time(track, i);
+ w[idx++] = track_get_key_transition(track, i);
w[idx++] = rot.x;
w[idx++] = rot.y;
w[idx++] = rot.z;
w[idx++] = rot.w;
+ }
+ r_ret = keys;
+ return true;
+
+ } else if (track_get_type(track) == TYPE_SCALE_3D) {
+ Vector<real_t> keys;
+ int kk = track_get_key_count(track);
+ keys.resize(kk * SCALE_TRACK_SIZE);
+
+ real_t *w = keys.ptrw();
+
+ int idx = 0;
+ for (int i = 0; i < track_get_key_count(track); i++) {
+ Vector3 scale;
+ scale_track_get_key(track, i, &scale);
+
+ w[idx++] = track_get_key_time(track, i);
+ w[idx++] = track_get_key_transition(track, i);
w[idx++] = scale.x;
w[idx++] = scale.y;
w[idx++] = scale.z;
@@ -384,7 +464,6 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = keys;
return true;
-
} else if (track_get_type(track) == TYPE_VALUE) {
const ValueTrack *vt = static_cast<const ValueTrack *>(tracks[track]);
@@ -591,10 +670,18 @@ int Animation::add_track(TrackType p_type, int p_at_pos) {
}
switch (p_type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = memnew(TransformTrack);
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = memnew(PositionTrack);
tracks.insert(p_at_pos, tt);
} break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = memnew(RotationTrack);
+ tracks.insert(p_at_pos, rt);
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = memnew(ScaleTrack);
+ tracks.insert(p_at_pos, st);
+ } break;
case TYPE_VALUE: {
tracks.insert(p_at_pos, memnew(ValueTrack));
@@ -629,9 +716,19 @@ void Animation::remove_track(int p_track) {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- _clear(tt->transforms);
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ _clear(tt->positions);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ _clear(rt->rotations);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ _clear(st->scales);
} break;
case TYPE_VALUE: {
@@ -672,7 +769,7 @@ int Animation::get_track_count() const {
}
Animation::TrackType Animation::track_get_type(int p_track) const {
- ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_TRANSFORM3D);
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), TYPE_VALUE);
return tracks[p_track]->type;
}
@@ -720,31 +817,6 @@ bool Animation::track_get_interpolation_loop_wrap(int p_track) const {
return tracks[p_track]->loop_wrap;
}
-// transform
-/*
-template<class T>
-int Animation::_insert_pos(double p_time, T& p_keys) {
- // simple, linear time inset that should be fast enough in reality.
-
- int idx=p_keys.size();
-
- while(true) {
-
-
- if (idx==0 || p_keys[idx-1].time < p_time) {
- //condition for insertion.
- p_keys.insert(idx,T());
- return idx;
- } else if (p_keys[idx-1].time == p_time) {
- // condition for replacing.
- return idx-1;
- }
-
- idx--;
- }
-}
-
-*/
template <class T, class V>
int Animation::_insert(double p_time, T &p_keys, const V &p_value) {
int idx = p_keys.size();
@@ -774,45 +846,153 @@ void Animation::_clear(T &p_keys) {
p_keys.clear();
}
-Error Animation::transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const {
+////
+
+int Animation::position_track_insert_key(int p_track, double p_time, const Vector3 &p_position) {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, -1);
+
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+
+ TKey<Vector3> tkey;
+ tkey.time = p_time;
+ tkey.value = p_position;
+
+ int ret = _insert(p_time, tt->positions, tkey);
+ emit_changed();
+ return ret;
+}
+
+Error Animation::position_track_get_key(int p_track, int p_key, Vector3 *r_position) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER);
- ERR_FAIL_INDEX_V(p_key, tt->transforms.size(), ERR_INVALID_PARAMETER);
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_key, tt->positions.size(), ERR_INVALID_PARAMETER);
- if (r_loc) {
- *r_loc = tt->transforms[p_key].value.loc;
- }
- if (r_rot) {
- *r_rot = tt->transforms[p_key].value.rot;
- }
- if (r_scale) {
- *r_scale = tt->transforms[p_key].value.scale;
+ *r_position = tt->positions[p_key].value;
+
+ return OK;
+}
+
+Error Animation::position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER);
+
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+
+ bool ok = false;
+
+ Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok);
+
+ if (!ok) {
+ return ERR_UNAVAILABLE;
}
+ *r_interpolation = tk;
+ return OK;
+}
+
+////
+
+int Animation::rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation) {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, -1);
+
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+
+ TKey<Quaternion> tkey;
+ tkey.time = p_time;
+ tkey.value = p_rotation;
+
+ int ret = _insert(p_time, rt->rotations, tkey);
+ emit_changed();
+ return ret;
+}
+
+Error Animation::rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_key, rt->rotations.size(), ERR_INVALID_PARAMETER);
+
+ *r_rotation = rt->rotations[p_key].value;
return OK;
}
-int Animation::transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot, const Vector3 &p_scale) {
+Error Animation::rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);
+
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+
+ bool ok = false;
+
+ Quaternion tk = _interpolate(rt->rotations, p_time, rt->interpolation, rt->loop_wrap, &ok);
+
+ if (!ok) {
+ return ERR_UNAVAILABLE;
+ }
+ *r_interpolation = tk;
+ return OK;
+}
+
+////
+
+int Animation::scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale) {
ERR_FAIL_INDEX_V(p_track, tracks.size(), -1);
Track *t = tracks[p_track];
- ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, -1);
+ ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, -1);
- TransformTrack *tt = static_cast<TransformTrack *>(t);
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
- TKey<TransformKey> tkey;
+ TKey<Vector3> tkey;
tkey.time = p_time;
- tkey.value.loc = p_loc;
- tkey.value.rot = p_rot;
- tkey.value.scale = p_scale;
+ tkey.value = p_scale;
- int ret = _insert(p_time, tt->transforms, tkey);
+ int ret = _insert(p_time, st->scales, tkey);
emit_changed();
return ret;
}
+Error Animation::scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_key, st->scales.size(), ERR_INVALID_PARAMETER);
+
+ *r_scale = st->scales[p_key].value;
+
+ return OK;
+}
+
+Error Animation::scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
+ Track *t = tracks[p_track];
+ ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER);
+
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+
+ bool ok = false;
+
+ Vector3 tk = _interpolate(st->scales, p_time, st->interpolation, st->loop_wrap, &ok);
+
+ if (!ok) {
+ return ERR_UNAVAILABLE;
+ }
+ *r_interpolation = tk;
+ return OK;
+}
+
void Animation::track_remove_key_at_time(int p_track, double p_time) {
int idx = track_find_key(p_track, p_time, true);
ERR_FAIL_COND(idx < 0);
@@ -824,10 +1004,22 @@ void Animation::track_remove_key(int p_track, int p_idx) {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX(p_idx, tt->transforms.size());
- tt->transforms.remove(p_idx);
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, tt->positions.size());
+ tt->positions.remove(p_idx);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, rt->rotations.size());
+ rt->rotations.remove(p_idx);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX(p_idx, st->scales.size());
+ st->scales.remove(p_idx);
} break;
case TYPE_VALUE: {
@@ -870,13 +1062,37 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- int k = _find(tt->transforms, p_time);
- if (k < 0 || k >= tt->transforms.size()) {
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ int k = _find(tt->positions, p_time);
+ if (k < 0 || k >= tt->positions.size()) {
return -1;
}
- if (tt->transforms[k].time != p_time && p_exact) {
+ if (tt->positions[k].time != p_time && p_exact) {
+ return -1;
+ }
+ return k;
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ int k = _find(rt->rotations, p_time);
+ if (k < 0 || k >= rt->rotations.size()) {
+ return -1;
+ }
+ if (rt->rotations[k].time != p_time && p_exact) {
+ return -1;
+ }
+ return k;
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *rt = static_cast<ScaleTrack *>(t);
+ int k = _find(rt->scales, p_time);
+ if (k < 0 || k >= rt->scales.size()) {
+ return -1;
+ }
+ if (rt->scales[k].time != p_time && p_exact) {
return -1;
}
return k;
@@ -952,24 +1168,21 @@ void Animation::track_insert_key(int p_track, double p_time, const Variant &p_ke
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- Dictionary d = p_key;
- Vector3 loc;
- if (d.has("location")) {
- loc = d["location"];
- }
-
- Quaternion rot;
- if (d.has("rotation")) {
- rot = d["rotation"];
- }
+ case TYPE_POSITION_3D: {
+ ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I));
+ int idx = position_track_insert_key(p_track, p_time, p_key);
+ track_set_key_transition(p_track, idx, p_transition);
- Vector3 scale;
- if (d.has("scale")) {
- scale = d["scale"];
- }
+ } break;
+ case TYPE_ROTATION_3D: {
+ ERR_FAIL_COND((p_key.get_type() != Variant::QUATERNION) && (p_key.get_type() != Variant::BASIS));
+ int idx = rotation_track_insert_key(p_track, p_time, p_key);
+ track_set_key_transition(p_track, idx, p_transition);
- int idx = transform_track_insert_key(p_track, p_time, loc, rot, scale);
+ } break;
+ case TYPE_SCALE_3D: {
+ ERR_FAIL_COND((p_key.get_type() != Variant::VECTOR3) && (p_key.get_type() != Variant::VECTOR3I));
+ int idx = scale_track_insert_key(p_track, p_time, p_key);
track_set_key_transition(p_track, idx, p_transition);
} break;
@@ -1054,9 +1267,17 @@ int Animation::track_get_key_count(int p_track) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- return tt->transforms.size();
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ return tt->positions.size();
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ return rt->rotations.size();
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ return st->scales.size();
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1089,16 +1310,23 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), Variant());
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), Variant());
- Dictionary d;
- d["location"] = tt->transforms[p_key_idx].value.loc;
- d["rotation"] = tt->transforms[p_key_idx].value.rot;
- d["scale"] = tt->transforms[p_key_idx].value.scale;
+ return tt->positions[p_key_idx].value;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), Variant());
- return d;
+ return rt->rotations[p_key_idx].value;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), Variant());
+
+ return st->scales[p_key_idx].value;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1157,10 +1385,20 @@ double Animation::track_get_key_time(int p_track, int p_key_idx) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1);
- return tt->transforms[p_key_idx].time;
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1);
+ return tt->positions[p_key_idx].time;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1);
+ return rt->rotations[p_key_idx].time;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1);
+ return st->scales[p_key_idx].time;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1202,13 +1440,31 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
- TKey<TransformKey> key = tt->transforms[p_key_idx];
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
+ TKey<Vector3> key = tt->positions[p_key_idx];
key.time = p_time;
- tt->transforms.remove(p_key_idx);
- _insert(p_time, tt->transforms, key);
+ tt->positions.remove(p_key_idx);
+ _insert(p_time, tt->positions, key);
+ return;
+ }
+ case TYPE_ROTATION_3D: {
+ RotationTrack *tt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->rotations.size());
+ TKey<Quaternion> key = tt->rotations[p_key_idx];
+ key.time = p_time;
+ tt->rotations.remove(p_key_idx);
+ _insert(p_time, tt->rotations, key);
+ return;
+ }
+ case TYPE_SCALE_3D: {
+ ScaleTrack *tt = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->scales.size());
+ TKey<Vector3> key = tt->scales[p_key_idx];
+ key.time = p_time;
+ tt->scales.remove(p_key_idx);
+ _insert(p_time, tt->scales, key);
return;
}
case TYPE_VALUE: {
@@ -1266,10 +1522,20 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const {
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, tt->transforms.size(), -1);
- return tt->transforms[p_key_idx].transition;
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1);
+ return tt->positions[p_key_idx].transition;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1);
+ return rt->rotations[p_key_idx].transition;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1);
+ return st->scales[p_key_idx].transition;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1302,21 +1568,28 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
+ case TYPE_POSITION_3D: {
+ ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I));
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
- Dictionary d = p_value;
+ tt->positions.write[p_key_idx].value = p_value;
- if (d.has("location")) {
- tt->transforms.write[p_key_idx].value.loc = d["location"];
- }
- if (d.has("rotation")) {
- tt->transforms.write[p_key_idx].value.rot = d["rotation"];
- }
- if (d.has("scale")) {
- tt->transforms.write[p_key_idx].value.scale = d["scale"];
- }
+ } break;
+ case TYPE_ROTATION_3D: {
+ ERR_FAIL_COND((p_value.get_type() != Variant::QUATERNION) && (p_value.get_type() != Variant::BASIS));
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, rt->rotations.size());
+
+ rt->rotations.write[p_key_idx].value = p_value;
+
+ } break;
+ case TYPE_SCALE_3D: {
+ ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I));
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, st->scales.size());
+
+ st->scales.write[p_key_idx].value = p_value;
} break;
case TYPE_VALUE: {
@@ -1385,10 +1658,20 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr
Track *t = tracks[p_track];
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- TransformTrack *tt = static_cast<TransformTrack *>(t);
- ERR_FAIL_INDEX(p_key_idx, tt->transforms.size());
- tt->transforms.write[p_key_idx].transition = p_transition;
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
+ tt->positions.write[p_key_idx].transition = p_transition;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, rt->rotations.size());
+ rt->rotations.write[p_key_idx].transition = p_transition;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_INDEX(p_key_idx, st->scales.size());
+ st->scales.write[p_key_idx].transition = p_transition;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1450,15 +1733,6 @@ int Animation::_find(const Vector<K> &p_keys, double p_time) const {
return middle;
}
-Animation::TransformKey Animation::_interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const {
- TransformKey ret;
- ret.loc = _interpolate(p_a.loc, p_b.loc, p_c);
- ret.rot = _interpolate(p_a.rot, p_b.rot, p_c);
- ret.scale = _interpolate(p_a.scale, p_b.scale, p_c);
-
- return ret;
-}
-
Vector3 Animation::_interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const {
return p_a.lerp(p_b, p_c);
}
@@ -1477,16 +1751,6 @@ real_t Animation::_interpolate(const real_t &p_a, const real_t &p_b, real_t p_c)
return p_a * (1.0 - p_c) + p_b * p_c;
}
-Animation::TransformKey Animation::_cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const {
- Animation::TransformKey tk;
-
- tk.loc = p_a.loc.cubic_interpolate(p_b.loc, p_pre_a.loc, p_post_b.loc, p_c);
- tk.scale = p_a.scale.cubic_interpolate(p_b.scale, p_pre_a.scale, p_post_b.scale, p_c);
- tk.rot = p_a.rot.cubic_slerp(p_b.rot, p_pre_a.rot, p_post_b.rot, p_c);
-
- return tk;
-}
-
Vector3 Animation::_cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const {
return p_a.cubic_interpolate(p_b, p_pre_a, p_post_b, p_c);
}
@@ -1729,36 +1993,6 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
// do a barrel roll
}
-Error Animation::transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const {
- ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
- Track *t = tracks[p_track];
- ERR_FAIL_COND_V(t->type != TYPE_TRANSFORM3D, ERR_INVALID_PARAMETER);
-
- TransformTrack *tt = static_cast<TransformTrack *>(t);
-
- bool ok = false;
-
- TransformKey tk = _interpolate(tt->transforms, p_time, tt->interpolation, tt->loop_wrap, &ok);
-
- if (!ok) {
- return ERR_UNAVAILABLE;
- }
-
- if (r_loc) {
- *r_loc = tk.loc;
- }
-
- if (r_rot) {
- *r_rot = tk.rot;
- }
-
- if (r_scale) {
- *r_scale = tk.scale;
- }
-
- return OK;
-}
-
Variant Animation::value_track_interpolate(int p_track, double p_time) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
Track *t = tracks[p_track];
@@ -1933,10 +2167,22 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
// handle loop by splitting
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- const TransformTrack *tt = static_cast<const TransformTrack *>(t);
- _track_get_key_indices_in_range(tt->transforms, from_time, length, p_indices);
- _track_get_key_indices_in_range(tt->transforms, 0, to_time, p_indices);
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
+ _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
+ _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ _track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
+ _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
} break;
case TYPE_VALUE: {
@@ -1989,9 +2235,19 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
}
switch (t->type) {
- case TYPE_TRANSFORM3D: {
- const TransformTrack *tt = static_cast<const TransformTrack *>(t);
- _track_get_key_indices_in_range(tt->transforms, from_time, to_time, p_indices);
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices);
+
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices);
+
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices);
} break;
case TYPE_VALUE: {
@@ -2608,7 +2864,10 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_set_enabled", "track_idx", "enabled"), &Animation::track_set_enabled);
ClassDB::bind_method(D_METHOD("track_is_enabled", "track_idx"), &Animation::track_is_enabled);
- ClassDB::bind_method(D_METHOD("transform_track_insert_key", "track_idx", "time", "location", "rotation", "scale"), &Animation::transform_track_insert_key);
+ ClassDB::bind_method(D_METHOD("position_track_insert_key", "track_idx", "time", "position"), &Animation::position_track_insert_key);
+ ClassDB::bind_method(D_METHOD("rotation_track_insert_key", "track_idx", "time", "rotation"), &Animation::rotation_track_insert_key);
+ ClassDB::bind_method(D_METHOD("scale_track_insert_key", "track_idx", "time", "scale"), &Animation::scale_track_insert_key);
+
ClassDB::bind_method(D_METHOD("track_insert_key", "track_idx", "time", "key", "transition"), &Animation::track_insert_key, DEFVAL(1));
ClassDB::bind_method(D_METHOD("track_remove_key", "track_idx", "key_idx"), &Animation::track_remove_key);
ClassDB::bind_method(D_METHOD("track_remove_key_at_time", "track_idx", "time"), &Animation::track_remove_key_at_time);
@@ -2628,7 +2887,6 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_set_interpolation_loop_wrap", "track_idx", "interpolation"), &Animation::track_set_interpolation_loop_wrap);
ClassDB::bind_method(D_METHOD("track_get_interpolation_loop_wrap", "track_idx"), &Animation::track_get_interpolation_loop_wrap);
- ClassDB::bind_method(D_METHOD("transform_track_interpolate", "track_idx", "time_sec"), &Animation::_transform_track_interpolate);
ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode);
ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode);
@@ -2682,7 +2940,9 @@ void Animation::_bind_methods() {
ADD_SIGNAL(MethodInfo("tracks_changed"));
BIND_ENUM_CONSTANT(TYPE_VALUE);
- BIND_ENUM_CONSTANT(TYPE_TRANSFORM3D);
+ BIND_ENUM_CONSTANT(TYPE_POSITION_3D);
+ BIND_ENUM_CONSTANT(TYPE_ROTATION_3D);
+ BIND_ENUM_CONSTANT(TYPE_SCALE_3D);
BIND_ENUM_CONSTANT(TYPE_METHOD);
BIND_ENUM_CONSTANT(TYPE_BEZIER);
BIND_ENUM_CONSTANT(TYPE_AUDIO);
@@ -2709,193 +2969,215 @@ void Animation::clear() {
emit_signal(SceneStringNames::get_singleton()->tracks_changed);
}
-bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm) {
- real_t c = (t1.time - t0.time) / (t2.time - t0.time);
- real_t t[3] = { -1, -1, -1 };
+bool Animation::_position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm) {
+ const Vector3 &v0 = t0.value;
+ const Vector3 &v1 = t1.value;
+ const Vector3 &v2 = t2.value;
- { //translation
+ if (v0.is_equal_approx(v2)) {
+ //0 and 2 are close, let's see if 1 is close
+ if (!v0.is_equal_approx(v1)) {
+ //not close, not optimizable
+ return false;
+ }
- const Vector3 &v0 = t0.value.loc;
- const Vector3 &v1 = t1.value.loc;
- const Vector3 &v2 = t2.value.loc;
+ } else {
+ Vector3 pd = (v2 - v0);
+ real_t d0 = pd.dot(v0);
+ real_t d1 = pd.dot(v1);
+ real_t d2 = pd.dot(v2);
+ if (d1 < d0 || d1 > d2) {
+ return false;
+ }
- if (v0.is_equal_approx(v2)) {
- //0 and 2 are close, let's see if 1 is close
- if (!v0.is_equal_approx(v1)) {
- //not close, not optimizable
- return false;
- }
+ Vector3 s[2] = { v0, v2 };
+ real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
- } else {
- Vector3 pd = (v2 - v0);
- real_t d0 = pd.dot(v0);
- real_t d1 = pd.dot(v1);
- real_t d2 = pd.dot(v2);
- if (d1 < d0 || d1 > d2) {
- return false;
- }
+ if (d > pd.length() * p_allowed_linear_err) {
+ return false; //beyond allowed error for collinearity
+ }
- Vector3 s[2] = { v0, v2 };
- real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+ if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_allowed_angular_error) {
+ return false;
+ }
+ }
- if (d > pd.length() * p_alowed_linear_err) {
- return false; //beyond allowed error for collinearity
- }
+ return true;
+}
- if (p_norm != Vector3() && Math::acos(pd.normalized().dot(p_norm)) > p_alowed_angular_err) {
- return false;
- }
+bool Animation::_rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle) {
+ const Quaternion &q0 = t0.value;
+ const Quaternion &q1 = t1.value;
+ const Quaternion &q2 = t2.value;
+
+ //localize both to rotation from q0
- t[0] = (d1 - d0) / (d2 - d0);
+ if (q0.is_equal_approx(q2)) {
+ if (!q0.is_equal_approx(q1)) {
+ return false;
}
- }
- { //rotation
+ } else {
+ Quaternion r02 = (q0.inverse() * q2).normalized();
+ Quaternion r01 = (q0.inverse() * q1).normalized();
- const Quaternion &q0 = t0.value.rot;
- const Quaternion &q1 = t1.value.rot;
- const Quaternion &q2 = t2.value.rot;
+ Vector3 v02, v01;
+ real_t a02, a01;
- //localize both to rotation from q0
+ r02.get_axis_angle(v02, a02);
+ r01.get_axis_angle(v01, a01);
- if (q0.is_equal_approx(q2)) {
- if (!q0.is_equal_approx(q1)) {
- return false;
- }
+ if (Math::abs(a02) > p_max_optimizable_angle) {
+ return false;
+ }
- } else {
- Quaternion r02 = (q0.inverse() * q2).normalized();
- Quaternion r01 = (q0.inverse() * q1).normalized();
+ if (v01.dot(v02) < 0) {
+ //make sure both rotations go the same way to compare
+ v02 = -v02;
+ a02 = -a02;
+ }
- Vector3 v02, v01;
- real_t a02, a01;
+ real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI;
+ if (err_01 > p_allowed_angular_error) {
+ //not rotating in the same axis
+ return false;
+ }
- r02.get_axis_angle(v02, a02);
- r01.get_axis_angle(v01, a01);
+ if (a01 * a02 < 0) {
+ //not rotating in the same direction
+ return false;
+ }
- if (Math::abs(a02) > p_max_optimizable_angle) {
- return false;
- }
+ real_t tr = a01 / a02;
+ if (tr < 0 || tr > 1) {
+ return false; //rotating too much or too less
+ }
+ }
- if (v01.dot(v02) < 0) {
- //make sure both rotations go the same way to compare
- v02 = -v02;
- a02 = -a02;
- }
+ return true;
+}
- real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized())) / Math_PI;
- if (err_01 > p_alowed_angular_err) {
- //not rotating in the same axis
- return false;
- }
+bool Animation::_scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error) {
+ const Vector3 &v0 = t0.value;
+ const Vector3 &v1 = t1.value;
+ const Vector3 &v2 = t2.value;
- if (a01 * a02 < 0) {
- //not rotating in the same direction
- return false;
- }
+ if (v0.is_equal_approx(v2)) {
+ //0 and 2 are close, let's see if 1 is close
+ if (!v0.is_equal_approx(v1)) {
+ //not close, not optimizable
+ return false;
+ }
- real_t tr = a01 / a02;
- if (tr < 0 || tr > 1) {
- return false; //rotating too much or too less
- }
+ } else {
+ Vector3 pd = (v2 - v0);
+ real_t d0 = pd.dot(v0);
+ real_t d1 = pd.dot(v1);
+ real_t d2 = pd.dot(v2);
+ if (d1 < d0 || d1 > d2) {
+ return false; //beyond segment range
+ }
- t[1] = tr;
+ Vector3 s[2] = { v0, v2 };
+ real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+
+ if (d > pd.length() * p_allowed_linear_error) {
+ return false; //beyond allowed error for colinearity
}
}
- { //scale
+ return true;
+}
- const Vector3 &v0 = t0.value.scale;
- const Vector3 &v1 = t1.value.scale;
- const Vector3 &v2 = t2.value.scale;
+void Animation::_position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err) {
+ ERR_FAIL_INDEX(p_idx, tracks.size());
+ ERR_FAIL_COND(tracks[p_idx]->type != TYPE_POSITION_3D);
+ PositionTrack *tt = static_cast<PositionTrack *>(tracks[p_idx]);
+ bool prev_erased = false;
+ TKey<Vector3> first_erased;
- if (v0.is_equal_approx(v2)) {
- //0 and 2 are close, let's see if 1 is close
- if (!v0.is_equal_approx(v1)) {
- //not close, not optimizable
- return false;
- }
+ Vector3 norm;
- } else {
- Vector3 pd = (v2 - v0);
- real_t d0 = pd.dot(v0);
- real_t d1 = pd.dot(v1);
- real_t d2 = pd.dot(v2);
- if (d1 < d0 || d1 > d2) {
- return false; //beyond segment range
- }
+ for (int i = 1; i < tt->positions.size() - 1; i++) {
+ TKey<Vector3> &t0 = tt->positions.write[i - 1];
+ TKey<Vector3> &t1 = tt->positions.write[i];
+ TKey<Vector3> &t2 = tt->positions.write[i + 1];
- Vector3 s[2] = { v0, v2 };
- real_t d = Geometry3D::get_closest_point_to_segment(v1, s).distance_to(v1);
+ bool erase = _position_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, norm);
+ if (erase && !prev_erased) {
+ norm = (t2.value - t1.value).normalized();
+ }
+
+ if (prev_erased && !_position_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, norm)) {
+ //avoid error to go beyond first erased key
+ erase = false;
+ }
- if (d > pd.length() * p_alowed_linear_err) {
- return false; //beyond allowed error for collinearity
+ if (erase) {
+ if (!prev_erased) {
+ first_erased = t1;
+ prev_erased = true;
}
- t[2] = (d1 - d0) / (d2 - d0);
+ tt->positions.remove(i);
+ i--;
+
+ } else {
+ prev_erased = false;
+ norm = Vector3();
}
}
+}
- bool erase = false;
- if (t[0] == -1 && t[1] == -1 && t[2] == -1) {
- erase = true;
- } else {
- erase = true;
- real_t lt = -1.0;
- for (int j = 0; j < 3; j++) {
- //search for t on first, one must be it
- if (t[j] != -1) {
- lt = t[j]; //official t
- //validate rest
- for (int k = j + 1; k < 3; k++) {
- if (t[k] == -1) {
- continue;
- }
+void Animation::_rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
+ ERR_FAIL_INDEX(p_idx, tracks.size());
+ ERR_FAIL_COND(tracks[p_idx]->type != TYPE_ROTATION_3D);
+ RotationTrack *tt = static_cast<RotationTrack *>(tracks[p_idx]);
+ bool prev_erased = false;
+ TKey<Quaternion> first_erased;
- if (Math::abs(lt - t[k]) > p_alowed_linear_err) {
- erase = false;
- break;
- }
- }
- break;
- }
- }
+ for (int i = 1; i < tt->rotations.size() - 1; i++) {
+ TKey<Quaternion> &t0 = tt->rotations.write[i - 1];
+ TKey<Quaternion> &t1 = tt->rotations.write[i];
+ TKey<Quaternion> &t2 = tt->rotations.write[i + 1];
- ERR_FAIL_COND_V(lt == -1, false);
+ bool erase = _rotation_track_optimize_key(t0, t1, t2, p_allowed_angular_err, p_max_optimizable_angle);
+
+ if (prev_erased && !_rotation_track_optimize_key(t0, first_erased, t2, p_allowed_angular_err, p_max_optimizable_angle)) {
+ //avoid error to go beyond first erased key
+ erase = false;
+ }
if (erase) {
- if (Math::abs(lt - c) > p_alowed_linear_err) {
- //todo, evaluate changing the transition if this fails?
- //this could be done as a second pass and would be
- //able to optimize more
- erase = false;
+ if (!prev_erased) {
+ first_erased = t1;
+ prev_erased = true;
}
+
+ tt->rotations.remove(i);
+ i--;
+
+ } else {
+ prev_erased = false;
}
}
-
- return erase;
}
-void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
+void Animation::_scale_track_optimize(int p_idx, real_t p_allowed_linear_err) {
ERR_FAIL_INDEX(p_idx, tracks.size());
- ERR_FAIL_COND(tracks[p_idx]->type != TYPE_TRANSFORM3D);
- TransformTrack *tt = static_cast<TransformTrack *>(tracks[p_idx]);
+ ERR_FAIL_COND(tracks[p_idx]->type != TYPE_SCALE_3D);
+ ScaleTrack *tt = static_cast<ScaleTrack *>(tracks[p_idx]);
bool prev_erased = false;
- TKey<TransformKey> first_erased;
+ TKey<Vector3> first_erased;
- Vector3 norm;
+ for (int i = 1; i < tt->scales.size() - 1; i++) {
+ TKey<Vector3> &t0 = tt->scales.write[i - 1];
+ TKey<Vector3> &t1 = tt->scales.write[i];
+ TKey<Vector3> &t2 = tt->scales.write[i + 1];
- for (int i = 1; i < tt->transforms.size() - 1; i++) {
- TKey<TransformKey> &t0 = tt->transforms.write[i - 1];
- TKey<TransformKey> &t1 = tt->transforms.write[i];
- TKey<TransformKey> &t2 = tt->transforms.write[i + 1];
+ bool erase = _scale_track_optimize_key(t0, t1, t2, p_allowed_linear_err);
- bool erase = _transform_track_optimize_key(t0, t1, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm);
- if (erase && !prev_erased) {
- norm = (t2.value.loc - t1.value.loc).normalized();
- }
-
- if (prev_erased && !_transform_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle, norm)) {
+ if (prev_erased && !_scale_track_optimize_key(t0, first_erased, t2, p_allowed_linear_err)) {
//avoid error to go beyond first erased key
erase = false;
}
@@ -2906,20 +3188,23 @@ void Animation::_transform_track_optimize(int p_idx, real_t p_allowed_linear_err
prev_erased = true;
}
- tt->transforms.remove(i);
+ tt->scales.remove(i);
i--;
} else {
prev_erased = false;
- norm = Vector3();
}
}
}
void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
for (int i = 0; i < tracks.size(); i++) {
- if (tracks[i]->type == TYPE_TRANSFORM3D) {
- _transform_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err, p_max_optimizable_angle);
+ if (tracks[i]->type == TYPE_POSITION_3D) {
+ _position_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err);
+ } else if (tracks[i]->type == TYPE_ROTATION_3D) {
+ _rotation_track_optimize(i, p_allowed_angular_err, p_max_optimizable_angle);
+ } else if (tracks[i]->type == TYPE_SCALE_3D) {
+ _scale_track_optimize(i, p_allowed_linear_err);
}
}
}
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 9a410bd566..614e12a560 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -42,7 +42,9 @@ class Animation : public Resource {
public:
enum TrackType {
TYPE_VALUE, ///< Set a value in a property, can be interpolated.
- TYPE_TRANSFORM3D, ///< Transform a node or a bone.
+ TYPE_POSITION_3D, ///< Position 3D track
+ TYPE_ROTATION_3D, ///< Rotation 3D track
+ TYPE_SCALE_3D, ///< Scale 3D track
TYPE_METHOD, ///< Call any method on a specific node.
TYPE_BEZIER, ///< Bezier curve
TYPE_AUDIO,
@@ -86,21 +88,31 @@ private:
T value;
};
- struct TransformKey {
- Vector3 loc;
- Quaternion rot;
- Vector3 scale;
+ const int32_t POSITION_TRACK_SIZE = 5;
+ const int32_t ROTATION_TRACK_SIZE = 6;
+ const int32_t SCALE_TRACK_SIZE = 5;
+
+ /* POSITION TRACK */
+
+ struct PositionTrack : public Track {
+ Vector<TKey<Vector3>> positions;
+
+ PositionTrack() { type = TYPE_POSITION_3D; }
};
- // Not necessarily the same size as Transform3D. The amount of numbers in Animation::Key and TransformKey.
- const int32_t TRANSFORM_TRACK_SIZE = 12;
+ /* ROTATION TRACK */
- /* TRANSFORM TRACK */
+ struct RotationTrack : public Track {
+ Vector<TKey<Quaternion>> rotations;
- struct TransformTrack : public Track {
- Vector<TKey<TransformKey>> transforms;
+ RotationTrack() { type = TYPE_ROTATION_3D; }
+ };
- TransformTrack() { type = TYPE_TRANSFORM3D; }
+ /* SCALE TRACK */
+
+ struct ScaleTrack : public Track {
+ Vector<TKey<Vector3>> scales;
+ ScaleTrack() { type = TYPE_SCALE_3D; }
};
/* PROPERTY VALUE TRACK */
@@ -186,14 +198,11 @@ private:
template <class K>
inline int _find(const Vector<K> &p_keys, double p_time) const;
- _FORCE_INLINE_ Animation::TransformKey _interpolate(const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, real_t p_c) const;
-
_FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const;
_FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const;
_FORCE_INLINE_ Variant _interpolate(const Variant &p_a, const Variant &p_b, real_t p_c) const;
_FORCE_INLINE_ real_t _interpolate(const real_t &p_a, const real_t &p_b, real_t p_c) const;
- _FORCE_INLINE_ Animation::TransformKey _cubic_interpolate(const Animation::TransformKey &p_pre_a, const Animation::TransformKey &p_a, const Animation::TransformKey &p_b, const Animation::TransformKey &p_post_b, real_t p_c) const;
_FORCE_INLINE_ Vector3 _cubic_interpolate(const Vector3 &p_pre_a, const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_post_b, real_t p_c) const;
_FORCE_INLINE_ Quaternion _cubic_interpolate(const Quaternion &p_pre_a, const Quaternion &p_a, const Quaternion &p_b, const Quaternion &p_post_b, real_t p_c) const;
_FORCE_INLINE_ Variant _cubic_interpolate(const Variant &p_pre_a, const Variant &p_a, const Variant &p_b, const Variant &p_post_b, real_t p_c) const;
@@ -214,18 +223,6 @@ private:
// bind helpers
private:
- Array _transform_track_interpolate(int p_track, double p_time) const {
- Vector3 loc;
- Quaternion rot;
- Vector3 scale;
- transform_track_interpolate(p_track, p_time, &loc, &rot, &scale);
- Array ret;
- ret.push_back(loc);
- ret.push_back(rot);
- ret.push_back(scale);
- return ret;
- }
-
Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const {
List<int> idxs;
value_track_get_key_indices(p_track, p_time, p_delta, &idxs);
@@ -247,8 +244,13 @@ private:
return idxr;
}
- bool _transform_track_optimize_key(const TKey<TransformKey> &t0, const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, real_t p_alowed_linear_err, real_t p_alowed_angular_err, real_t p_max_optimizable_angle, const Vector3 &p_norm);
- void _transform_track_optimize(int p_idx, real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125);
+ bool _position_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_alowed_linear_err, real_t p_allowed_angular_error, const Vector3 &p_norm);
+ bool _rotation_track_optimize_key(const TKey<Quaternion> &t0, const TKey<Quaternion> &t1, const TKey<Quaternion> &t2, real_t p_allowed_angular_error, float p_max_optimizable_angle);
+ bool _scale_track_optimize_key(const TKey<Vector3> &t0, const TKey<Vector3> &t1, const TKey<Vector3> &t2, real_t p_allowed_linear_error);
+
+ void _position_track_optimize(int p_idx, real_t p_allowed_linear_err, real_t p_allowed_angular_err);
+ void _rotation_track_optimize(int p_idx, real_t p_allowed_angular_err, real_t p_max_optimizable_angle);
+ void _scale_track_optimize(int p_idx, real_t p_allowed_linear_err);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -294,8 +296,18 @@ public:
double track_get_key_time(int p_track, int p_key_idx) const;
real_t track_get_key_transition(int p_track, int p_key_idx) const;
- int transform_track_insert_key(int p_track, double p_time, const Vector3 &p_loc, const Quaternion &p_rot = Quaternion(), const Vector3 &p_scale = Vector3());
- Error transform_track_get_key(int p_track, int p_key, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
+ int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position);
+ Error position_track_get_key(int p_track, int p_key, Vector3 *r_position) const;
+ Error position_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
+
+ int rotation_track_insert_key(int p_track, double p_time, const Quaternion &p_rotation);
+ Error rotation_track_get_key(int p_track, int p_key, Quaternion *r_rotation) const;
+ Error rotation_track_interpolate(int p_track, double p_time, Quaternion *r_interpolation) const;
+
+ int scale_track_insert_key(int p_track, double p_time, const Vector3 &p_scale);
+ Error scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) const;
+ Error scale_track_interpolate(int p_track, double p_time, Vector3 *r_interpolation) const;
+
void track_set_interpolation_type(int p_track, InterpolationType p_interp);
InterpolationType track_get_interpolation_type(int p_track) const;
@@ -324,8 +336,6 @@ public:
void track_set_interpolation_loop_wrap(int p_track, bool p_enable);
bool track_get_interpolation_loop_wrap(int p_track) const;
- Error transform_track_interpolate(int p_track, double p_time, Vector3 *r_loc, Quaternion *r_rot, Vector3 *r_scale) const;
-
Variant value_track_interpolate(int p_track, double p_time) const;
void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
void value_track_set_update_mode(int p_track, UpdateMode p_mode);
diff --git a/scene/resources/canvas_item_material.cpp b/scene/resources/canvas_item_material.cpp
index fa95ab0e79..b291724c4c 100644
--- a/scene/resources/canvas_item_material.cpp
+++ b/scene/resources/canvas_item_material.cpp
@@ -135,7 +135,7 @@ void CanvasItemMaterial::_update_shader() {
code += " particle_frame = mod(particle_frame, particle_total_frames);\n";
code += " }";
code += " UV /= vec2(h_frames, v_frames);\n";
- code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n";
+ code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n";
code += "}\n";
}
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/environment.cpp b/scene/resources/environment.cpp
index cab6c0378a..be0d3a140e 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -173,23 +173,13 @@ Environment::ReflectionSource Environment::get_reflection_source() const {
return reflection_source;
}
-void Environment::set_ao_color(const Color &p_color) {
- ao_color = p_color;
- _update_ambient_light();
-}
-
-Color Environment::get_ao_color() const {
- return ao_color;
-}
-
void Environment::_update_ambient_light() {
RS::get_singleton()->environment_set_ambient_light(
environment,
ambient_color,
RS::EnvironmentAmbientSource(ambient_source),
ambient_energy,
- ambient_sky_contribution, RS::EnvironmentReflectionSource(reflection_source),
- ao_color);
+ ambient_sky_contribution, RS::EnvironmentReflectionSource(reflection_source));
}
// Tonemap
@@ -302,7 +292,7 @@ int Environment::get_ssr_max_steps() const {
}
void Environment::set_ssr_fade_in(float p_fade_in) {
- ssr_fade_in = p_fade_in;
+ ssr_fade_in = MAX(p_fade_in, 0.0f);
_update_ssr();
}
@@ -311,7 +301,7 @@ float Environment::get_ssr_fade_in() const {
}
void Environment::set_ssr_fade_out(float p_fade_out) {
- ssr_fade_out = p_fade_out;
+ ssr_fade_out = MAX(p_fade_out, 0.0f);
_update_ssr();
}
@@ -1092,15 +1082,12 @@ void Environment::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_ambient_light_sky_contribution"), &Environment::get_ambient_light_sky_contribution);
ClassDB::bind_method(D_METHOD("set_reflection_source", "source"), &Environment::set_reflection_source);
ClassDB::bind_method(D_METHOD("get_reflection_source"), &Environment::get_reflection_source);
- ClassDB::bind_method(D_METHOD("set_ao_color", "color"), &Environment::set_ao_color);
- ClassDB::bind_method(D_METHOD("get_ao_color"), &Environment::get_ao_color);
ADD_GROUP("Ambient Light", "ambient_light_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_light_source", PROPERTY_HINT_ENUM, "Background,Disabled,Color,Sky"), "set_ambient_source", "get_ambient_source");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_light_color"), "set_ambient_light_color", "get_ambient_light_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ambient_light_sky_contribution", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ambient_light_sky_contribution", "get_ambient_light_sky_contribution");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ambient_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_ambient_light_energy", "get_ambient_light_energy");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_light_occlusion_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ao_color", "get_ao_color");
ADD_GROUP("Reflected Light", "reflected_light_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "reflected_light_source", PROPERTY_HINT_ENUM, "Background,Disabled,Sky"), "set_reflection_source", "get_reflection_source");
@@ -1304,7 +1291,7 @@ void Environment::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_density", PROPERTY_HINT_RANGE, "0,16,0.0001"), "set_fog_density", "get_fog_density");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_aerial_perspective", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_fog_aerial_perspective", "get_fog_aerial_perspective");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_lesser,or_greater"), "set_fog_height", "get_fog_height");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "0,128,0.001,or_greater"), "set_fog_height_density", "get_fog_height_density");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_density", PROPERTY_HINT_RANGE, "-16,16,0.0001,or_lesser,or_greater"), "set_fog_height_density", "get_fog_height_density");
ClassDB::bind_method(D_METHOD("set_volumetric_fog_enabled", "enabled"), &Environment::set_volumetric_fog_enabled);
ClassDB::bind_method(D_METHOD("is_volumetric_fog_enabled"), &Environment::is_volumetric_fog_enabled);
diff --git a/scene/resources/environment.h b/scene/resources/environment.h
index 0df2c3cc27..46842754f4 100644
--- a/scene/resources/environment.h
+++ b/scene/resources/environment.h
@@ -116,7 +116,6 @@ private:
float ambient_energy = 1.0;
float ambient_sky_contribution = 1.0;
ReflectionSource reflection_source = REFLECTION_SOURCE_BG;
- Color ao_color;
void _update_ambient_light();
// Tonemap
@@ -250,8 +249,6 @@ public:
float get_ambient_light_sky_contribution() const;
void set_reflection_source(ReflectionSource p_source);
ReflectionSource get_reflection_source() const;
- void set_ao_color(const Color &p_color);
- Color get_ao_color() const;
// Tonemap
void set_tonemapper(ToneMapper p_tone_mapper);
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 9b403a18f0..04e2b0dc70 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -116,8 +116,8 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scale", "cache_index", "size", "scale"), &FontData::set_scale);
ClassDB::bind_method(D_METHOD("get_scale", "cache_index", "size"), &FontData::get_scale);
- ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing"), &FontData::set_spacing);
- ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size"), &FontData::get_spacing);
+ ClassDB::bind_method(D_METHOD("set_spacing", "cache_index", "size", "spacing_type", "value"), &FontData::set_spacing);
+ ClassDB::bind_method(D_METHOD("get_spacing", "cache_index", "size", "spacing_type"), &FontData::get_spacing);
ClassDB::bind_method(D_METHOD("get_texture_count", "cache_index", "size"), &FontData::get_texture_count);
ClassDB::bind_method(D_METHOD("clear_textures", "cache_index", "size"), &FontData::clear_textures);
@@ -175,7 +175,7 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char);
ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars);
- ClassDB::bind_method(D_METHOD("get_glyph_index", "char", "variation_selector"), &FontData::get_glyph_index);
+ ClassDB::bind_method(D_METHOD("get_glyph_index", "size", "char", "variation_selector"), &FontData::get_glyph_index);
ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &FontData::get_supported_feature_list);
ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list);
@@ -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..2572c5de33
--- /dev/null
+++ b/scene/resources/importer_mesh.cpp
@@ -0,0 +1,1247 @@
+/*************************************************************************/
+/* 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) {
+ _split_normals(arrays, p_indices, p_normals);
+
+ for (BlendShape &blend_shape : blend_shape_data) {
+ _split_normals(blend_shape.arrays, p_indices, p_normals);
+ }
+}
+
+void ImporterMesh::Surface::_split_normals(Array &r_arrays, const LocalVector<int> &p_indices, const LocalVector<Vector3> &p_normals) {
+ ERR_FAIL_COND(r_arrays.size() != RS::ARRAY_MAX);
+
+ const PackedVector3Array &vertices = r_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 < r_arrays.size(); i++) {
+ if (i == RS::ARRAY_INDEX) {
+ continue;
+ }
+
+ if (r_arrays[i].get_type() == Variant::NIL) {
+ continue;
+ }
+
+ switch (r_arrays[i].get_type()) {
+ case Variant::PACKED_VECTOR3_ARRAY: {
+ PackedVector3Array data = r_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]];
+ }
+ }
+ r_arrays[i] = data;
+ } break;
+ case Variant::PACKED_VECTOR2_ARRAY: {
+ PackedVector2Array data = r_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]];
+ }
+ r_arrays[i] = data;
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+ PackedFloat32Array data = r_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);
+ }
+ r_arrays[i] = data;
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ PackedInt32Array data = r_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);
+ }
+ r_arrays[i] = data;
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ PackedByteArray data = r_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);
+ }
+ r_arrays[i] = data;
+ } break;
+ case Variant::PACKED_COLOR_ARRAY: {
+ PackedColorArray data = r_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]];
+ }
+ r_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;
+ }
+
+ 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..8576312a8a
--- /dev/null
+++ b/scene/resources/importer_mesh.h
@@ -0,0 +1,133 @@
+/*************************************************************************/
+/* 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);
+ static void _split_normals(Array &r_arrays, 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..abb3381c4e 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();
@@ -815,7 +811,7 @@ void BaseMaterial3D::_update_shader() {
code += " particle_frame = mod(particle_frame, particle_total_frames);\n";
code += " }";
code += " UV /= vec2(h_frames, v_frames);\n";
- code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor(particle_frame / h_frames) / v_frames);\n";
+ code += " UV += vec2(mod(particle_frame, h_frames) / h_frames, floor((particle_frame + 0.5) / h_frames) / v_frames);\n";
} break;
case BILLBOARD_MAX:
break; // Internal value, skip.
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..7ffe0b03e1 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
@@ -1577,7 +1577,7 @@ void ArrayMesh::regen_normal_maps() {
}
//dirty hack
-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) = NULL;
+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) = nullptr;
struct ArrayMeshLightmapSurface {
Ref<Material> material;
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..60cda637ca 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,11 +384,11 @@ 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;
- } else if (p_node->get_owner() && p_node->get_owner() != p_owner && p_owner->is_editable_instance(p_node->get_owner())) {
+ } else if (p_node->get_owner() && p_owner->is_ancestor_of(p_node->get_owner()) && p_owner->is_editable_instance(p_node->get_owner())) {
// Node is part 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/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index e7da41db9d..f8be00f5fb 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -1390,6 +1390,12 @@ void QuadMesh::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_offset"), "set_center_offset", "get_center_offset");
}
+uint32_t QuadMesh::surface_get_format(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, 1, 0);
+
+ return RS::ARRAY_FORMAT_VERTEX | RS::ARRAY_FORMAT_NORMAL | RS::ARRAY_FORMAT_TANGENT | RS::ARRAY_FORMAT_TEX_UV;
+}
+
QuadMesh::QuadMesh() {
primitive_type = PRIMITIVE_TRIANGLES;
}
@@ -1460,7 +1466,7 @@ void SphereMesh::_create_mesh_array(Array &p_arr) const {
} else {
Vector3 p = Vector3(x * radius * w, y, z * radius * w);
points.push_back(p);
- Vector3 normal = Vector3(x * radius * w * scale, y / scale, z * radius * w * scale);
+ Vector3 normal = Vector3(x * w * scale, radius * (y / scale), z * w * scale);
normals.push_back(normal.normalized());
};
ADD_TANGENT(z, 0.0, -x, 1.0)
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index 7915cb0028..d447dad97a 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -285,6 +285,8 @@ protected:
virtual void _create_mesh_array(Array &p_arr) const override;
public:
+ virtual uint32_t surface_get_format(int p_idx) const override;
+
QuadMesh();
void set_size(const Size2 &p_size);
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/skeleton_modification_2d.cpp b/scene/resources/skeleton_modification_2d.cpp
index e533fb054a..7ac40b497d 100644
--- a/scene/resources/skeleton_modification_2d.cpp
+++ b/scene/resources/skeleton_modification_2d.cpp
@@ -96,37 +96,25 @@ float SkeletonModification2D::clamp_angle(float p_angle, float p_min_bound, floa
p_max_bound = Math_TAU + p_max_bound;
}
if (p_min_bound > p_max_bound) {
- float tmp = p_min_bound;
- p_min_bound = p_max_bound;
- p_max_bound = tmp;
+ SWAP(p_min_bound, p_max_bound);
}
+ bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound);
+ bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound);
+
// Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle.
- if (p_invert == false) {
- if (p_angle < p_min_bound || p_angle > p_max_bound) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
-
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
- }
- } else {
- if (p_angle > p_min_bound && p_angle < p_max_bound) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
+ if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) {
+ Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
+ Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
+ Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
+ if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
+ p_angle = p_min_bound;
+ } else {
+ p_angle = p_max_bound;
}
}
+
return p_angle;
}
@@ -152,9 +140,7 @@ void SkeletonModification2D::editor_draw_angle_constraints(Bone2D *p_operation_b
arc_angle_max = (Math_PI * 2) + arc_angle_max;
}
if (arc_angle_min > arc_angle_max) {
- float tmp = arc_angle_min;
- arc_angle_min = arc_angle_max;
- arc_angle_max = tmp;
+ SWAP(arc_angle_min, arc_angle_max);
}
arc_angle_min += p_operation_bone->get_bone_angle();
arc_angle_max += p_operation_bone->get_bone_angle();
diff --git a/scene/resources/skeleton_modification_2d_fabrik.cpp b/scene/resources/skeleton_modification_2d_fabrik.cpp
index 6e9429034f..3b5c555f89 100644
--- a/scene/resources/skeleton_modification_2d_fabrik.cpp
+++ b/scene/resources/skeleton_modification_2d_fabrik.cpp
@@ -247,7 +247,7 @@ void SkeletonModification2DFABRIK::chain_backwards() {
}
float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y);
- float length = current_bone2d_node_length / (previous_pose.get_origin() - current_pose.get_origin()).length();
+ float length = current_bone2d_node_length / (current_pose.get_origin().distance_to(previous_pose.get_origin()));
Vector2 finish_position = previous_pose.get_origin().lerp(current_pose.get_origin(), length);
current_pose.set_origin(finish_position);
@@ -268,7 +268,7 @@ void SkeletonModification2DFABRIK::chain_forwards() {
Transform2D next_pose = fabrik_transform_chain[i + 1];
float current_bone2d_node_length = current_bone2d_node->get_length() * MIN(current_bone2d_node->get_global_scale().x, current_bone2d_node->get_global_scale().y);
- float length = current_bone2d_node_length / (current_pose.get_origin() - next_pose.get_origin()).length();
+ float length = current_bone2d_node_length / (next_pose.get_origin().distance_to(current_pose.get_origin()));
Vector2 finish_position = current_pose.get_origin().lerp(next_pose.get_origin(), length);
current_pose.set_origin(finish_position);
diff --git a/scene/resources/skeleton_modification_3d.cpp b/scene/resources/skeleton_modification_3d.cpp
index ee02ede2d5..b476952d86 100644
--- a/scene/resources/skeleton_modification_3d.cpp
+++ b/scene/resources/skeleton_modification_3d.cpp
@@ -72,37 +72,25 @@ real_t SkeletonModification3D::clamp_angle(real_t p_angle, real_t p_min_bound, r
p_max_bound = Math_TAU + p_max_bound;
}
if (p_min_bound > p_max_bound) {
- real_t tmp = p_min_bound;
- p_min_bound = p_max_bound;
- p_max_bound = tmp;
+ SWAP(p_min_bound, p_max_bound);
}
+ bool is_beyond_bounds = (p_angle < p_min_bound || p_angle > p_max_bound);
+ bool is_within_bounds = (p_angle > p_min_bound && p_angle < p_max_bound);
+
// Note: May not be the most optimal way to clamp, but it always constraints to the nearest angle.
- if (p_invert == false) {
- if (p_angle < p_min_bound || p_angle > p_max_bound) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
-
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
- }
- } else {
- if (p_angle > p_min_bound && p_angle < p_max_bound) {
- Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
- Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
- Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
-
- if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
- p_angle = p_min_bound;
- } else {
- p_angle = p_max_bound;
- }
+ if ((!p_invert && is_beyond_bounds) || (p_invert && is_within_bounds)) {
+ Vector2 min_bound_vec = Vector2(Math::cos(p_min_bound), Math::sin(p_min_bound));
+ Vector2 max_bound_vec = Vector2(Math::cos(p_max_bound), Math::sin(p_max_bound));
+ Vector2 angle_vec = Vector2(Math::cos(p_angle), Math::sin(p_angle));
+
+ if (angle_vec.distance_squared_to(min_bound_vec) <= angle_vec.distance_squared_to(max_bound_vec)) {
+ p_angle = p_min_bound;
+ } else {
+ p_angle = p_max_bound;
}
}
+
return p_angle;
}
diff --git a/scene/resources/skeleton_modification_3d_fabrik.cpp b/scene/resources/skeleton_modification_3d_fabrik.cpp
index 69f75eb7b5..20ebbda256 100644
--- a/scene/resources/skeleton_modification_3d_fabrik.cpp
+++ b/scene/resources/skeleton_modification_3d_fabrik.cpp
@@ -168,7 +168,7 @@ void SkeletonModification3DFABRIK::_execute(real_t p_delta) {
// Apply magnet positions:
if (stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx) >= 0) {
int parent_bone_idx = stack->skeleton->get_bone_parent(fabrik_data_chain[i].bone_idx);
- Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx) * stack->skeleton->get_bone_rest(parent_bone_idx));
+ Transform3D conversion_transform = (stack->skeleton->get_bone_global_pose(parent_bone_idx));
local_pose_override.origin += conversion_transform.basis.xform_inv(fabrik_data_chain[i].magnet_position);
} else {
local_pose_override.origin += fabrik_data_chain[i].magnet_position;
@@ -232,7 +232,7 @@ void SkeletonModification3DFABRIK::chain_backwards() {
int current_bone_idx = fabrik_data_chain[i].bone_idx;
Transform3D current_trans = stack->skeleton->local_pose_to_global_pose(current_bone_idx, stack->skeleton->get_bone_local_pose_override(current_bone_idx));
- real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin - current_trans.origin).length();
+ real_t length = fabrik_data_chain[i].length / (current_trans.origin.distance_to(next_bone_trans.origin));
current_trans.origin = next_bone_trans.origin.lerp(current_trans.origin, length);
// Apply it back to the skeleton
@@ -253,7 +253,7 @@ void SkeletonModification3DFABRIK::chain_forwards() {
int next_bone_idx = fabrik_data_chain[i + 1].bone_idx;
Transform3D next_bone_trans = stack->skeleton->local_pose_to_global_pose(next_bone_idx, stack->skeleton->get_bone_local_pose_override(next_bone_idx));
- real_t length = fabrik_data_chain[i].length / (current_trans.origin - next_bone_trans.origin).length();
+ real_t length = fabrik_data_chain[i].length / (next_bone_trans.origin.distance_to(current_trans.origin));
next_bone_trans.origin = current_trans.origin.lerp(next_bone_trans.origin, length);
// Apply it back to the skeleton
diff --git a/scene/resources/skeleton_modification_3d_twoboneik.cpp b/scene/resources/skeleton_modification_3d_twoboneik.cpp
index ae7a3bab7e..c1a71148a7 100644
--- a/scene/resources/skeleton_modification_3d_twoboneik.cpp
+++ b/scene/resources/skeleton_modification_3d_twoboneik.cpp
@@ -455,7 +455,7 @@ void SkeletonModification3DTwoBoneIK::calculate_joint_lengths() {
joint_two_length = 0;
for (int i = 0; i < bone_two_children.size(); i++) {
joint_two_length += bone_two_rest_trans.origin.distance_to(
- stack->skeleton->local_pose_to_global_pose(bone_two_children[i], stack->skeleton->get_bone_rest(bone_two_children[i])).origin);
+ stack->skeleton->get_bone_global_pose(bone_two_children[i]).origin);
}
joint_two_length = joint_two_length / bone_two_children.size();
} else {
diff --git a/scene/resources/skeleton_modification_stack_2d.cpp b/scene/resources/skeleton_modification_stack_2d.cpp
index 72c1c330ef..db9fe62b4d 100644
--- a/scene/resources/skeleton_modification_stack_2d.cpp
+++ b/scene/resources/skeleton_modification_stack_2d.cpp
@@ -247,7 +247,7 @@ void SkeletonModificationStack2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack2D::delete_modification);
ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack2D::set_modification);
- ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack2D::set_modification_count);
+ ClassDB::bind_method(D_METHOD("set_modification_count", "count"), &SkeletonModificationStack2D::set_modification_count);
ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack2D::get_modification_count);
ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack2D::get_is_setup);
diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp
index 3fce0e5dbd..a724b732b9 100644
--- a/scene/resources/skeleton_modification_stack_3d.cpp
+++ b/scene/resources/skeleton_modification_stack_3d.cpp
@@ -200,7 +200,7 @@ void SkeletonModificationStack3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack3D::delete_modification);
ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack3D::set_modification);
- ClassDB::bind_method(D_METHOD("set_modification_count"), &SkeletonModificationStack3D::set_modification_count);
+ ClassDB::bind_method(D_METHOD("set_modification_count", "count"), &SkeletonModificationStack3D::set_modification_count);
ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack3D::get_modification_count);
ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack3D::get_is_setup);
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index 39082b6f7a..de94c92cac 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;
}
@@ -179,14 +175,14 @@ void ProceduralSkyMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_sun_curve"), &ProceduralSkyMaterial::get_sun_curve);
ADD_GROUP("Sky", "sky_");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color"), "set_sky_top_color", "get_sky_top_color");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color"), "set_sky_horizon_color", "get_sky_horizon_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_top_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_top_color", "get_sky_top_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "sky_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_sky_horizon_color", "get_sky_horizon_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_curve", PROPERTY_HINT_EXP_EASING), "set_sky_curve", "get_sky_curve");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sky_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_sky_energy", "get_sky_energy");
ADD_GROUP("Ground", "ground_");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color"), "set_ground_bottom_color", "get_ground_bottom_color");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_horizon_color"), "set_ground_horizon_color", "get_ground_horizon_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_bottom_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_bottom_color", "get_ground_bottom_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_horizon_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_horizon_color", "get_ground_horizon_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_curve", PROPERTY_HINT_EXP_EASING), "set_ground_curve", "get_ground_curve");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ground_energy", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_ground_energy", "get_ground_energy");
@@ -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;
}
@@ -543,16 +531,16 @@ void PhysicalSkyMaterial::_bind_methods() {
ADD_GROUP("Rayleigh", "rayleigh_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rayleigh_coefficient", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_rayleigh_coefficient", "get_rayleigh_coefficient");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "rayleigh_color"), "set_rayleigh_color", "get_rayleigh_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "rayleigh_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_rayleigh_color", "get_rayleigh_color");
ADD_GROUP("Mie", "mie_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mie_coefficient", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_mie_coefficient", "get_mie_coefficient");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mie_eccentricity", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_mie_eccentricity", "get_mie_eccentricity");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "mie_color"), "set_mie_color", "get_mie_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "mie_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_mie_color", "get_mie_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "turbidity", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_turbidity", "get_turbidity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_disk_scale", PROPERTY_HINT_RANGE, "0,360,0.01"), "set_sun_disk_scale", "get_sun_disk_scale");
- ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_color"), "set_ground_color", "get_ground_color");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ground_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ground_color", "get_ground_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "exposure", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_exposure", "get_exposure");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "dither_strength", PROPERTY_HINT_RANGE, "0,10,0.01"), "set_dither_strength", "get_dither_strength");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "night_sky", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_night_sky", "get_night_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/surface_tool.cpp b/scene/resources/surface_tool.cpp
index d5e370568d..a8cd872408 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -1176,6 +1176,7 @@ Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_coun
ERR_FAIL_COND_V(simplify_func == nullptr, lod);
ERR_FAIL_COND_V(vertex_array.size() == 0, lod);
ERR_FAIL_COND_V(index_array.size() == 0, lod);
+ ERR_FAIL_COND_V(index_array.size() % 3 != 0, lod);
lod.resize(index_array.size());
LocalVector<float> vertices; //uses floats
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 3dc32632cc..1b3a7a5f2a 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -1248,6 +1248,14 @@ bool AtlasTexture::is_pixel_opaque(int p_x, int p_y) const {
return atlas->is_pixel_opaque(x, y);
}
+Ref<Image> AtlasTexture::get_image() const {
+ if (!atlas.is_valid() || !atlas->get_image().is_valid()) {
+ return Ref<Image>();
+ }
+
+ return atlas->get_image()->get_rect(region);
+}
+
AtlasTexture::AtlasTexture() {}
/////////////////////////////////////////
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 93f4e2de5a..862b9a47a6 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -250,6 +250,8 @@ public:
bool is_pixel_opaque(int p_x, int p_y) const override;
+ virtual Ref<Image> get_image() const override;
+
AtlasTexture();
};
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 67cbd0e094..b988b2ab3e 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -66,12 +66,13 @@ 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;
tile_meshes_dirty = true;
+ notify_property_list_changed();
emit_changed();
}
TileSet::TileShape TileSet::get_tile_shape() const {
@@ -89,8 +90,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;
@@ -170,6 +171,8 @@ void TileSet::set_source_id(int p_source_id, int p_new_source_id) {
source_ids.append(p_new_source_id);
source_ids.sort();
+ _compute_next_source_id();
+
emit_changed();
}
@@ -386,8 +389,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 +722,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 +861,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 +872,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 +883,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 +938,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 +949,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 +962,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);
}
@@ -1019,20 +1022,25 @@ Vector<Vector2> TileSet::get_tile_shape_polygon() {
void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled, Ref<Texture2D> p_texture) {
if (tile_meshes_dirty) {
- Vector<Vector2> uvs = get_tile_shape_polygon();
+ Vector<Vector2> shape = get_tile_shape_polygon();
+ Vector<Vector2> uvs;
+ uvs.resize(shape.size());
+ for (int i = 0; i < shape.size(); i++) {
+ uvs.write[i] = shape[i] + Vector2(0.5, 0.5);
+ }
Vector<Color> colors;
- colors.resize(uvs.size());
+ colors.resize(shape.size());
colors.fill(Color(1.0, 1.0, 1.0, 1.0));
// Filled mesh.
tile_filled_mesh->clear_surfaces();
Array a;
a.resize(Mesh::ARRAY_MAX);
- a[Mesh::ARRAY_VERTEX] = uvs;
+ a[Mesh::ARRAY_VERTEX] = shape;
a[Mesh::ARRAY_TEX_UV] = uvs;
a[Mesh::ARRAY_COLOR] = colors;
- a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(uvs);
+ a[Mesh::ARRAY_INDEX] = Geometry2D::triangulate_polygon(shape);
tile_filled_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES);
// Lines mesh.
@@ -1040,9 +1048,9 @@ void TileSet::draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform
a.clear();
a.resize(Mesh::ARRAY_MAX);
// Add the first point again when drawing lines.
- uvs.push_back(uvs[0]);
+ shape.push_back(shape[0]);
colors.push_back(colors[0]);
- a[Mesh::ARRAY_VERTEX] = uvs;
+ a[Mesh::ARRAY_VERTEX] = shape;
a[Mesh::ARRAY_COLOR] = colors;
tile_lines_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINE_STRIP, a, Array(), Dictionary(), Mesh::ARRAY_FLAG_USE_2D_VERTICES);
@@ -1906,8 +1914,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);
@@ -1945,11 +1953,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));
@@ -2037,11 +2045,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));
@@ -2123,8 +2131,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 *>();
}
@@ -2574,25 +2582,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;
@@ -2668,8 +2676,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.
@@ -2680,6 +2688,14 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
}
+void TileSet::_validate_property(PropertyInfo &property) const {
+ if (property.name == "tile_layout" && tile_shape == TILE_SHAPE_SQUARE) {
+ property.usage ^= PROPERTY_USAGE_READ_ONLY;
+ } else if (property.name == "tile_offset_axis" && tile_shape == TILE_SHAPE_SQUARE) {
+ property.usage ^= PROPERTY_USAGE_READ_ONLY;
+ }
+}
+
void TileSet::_bind_methods() {
// Sources management.
ClassDB::bind_method(D_METHOD("get_next_source_id"), &TileSet::get_next_source_id);
@@ -2845,8 +2861,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()) {
@@ -2878,18 +2894,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();
}
}
}
@@ -3040,9 +3056,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();
}
}
}
@@ -3050,6 +3066,7 @@ void TileSetAtlasSource::reset_state() {
void TileSetAtlasSource::set_texture(Ref<Texture2D> p_texture) {
texture = p_texture;
+ _clear_tiles_outside_texture();
emit_changed();
}
@@ -3065,6 +3082,7 @@ void TileSetAtlasSource::set_margins(Vector2i p_margins) {
margins = p_margins;
}
+ _clear_tiles_outside_texture();
emit_changed();
}
Vector2i TileSetAtlasSource::get_margins() const {
@@ -3079,6 +3097,7 @@ void TileSetAtlasSource::set_separation(Vector2i p_separation) {
separation = p_separation;
}
+ _clear_tiles_outside_texture();
emit_changed();
}
Vector2i TileSetAtlasSource::get_separation() const {
@@ -3093,6 +3112,7 @@ void TileSetAtlasSource::set_texture_region_size(Vector2i p_tile_size) {
texture_region_size = p_tile_size;
}
+ _clear_tiles_outside_texture();
emit_changed();
}
Vector2i TileSetAtlasSource::get_texture_region_size() const {
@@ -3119,7 +3139,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(":");
@@ -3255,40 +3275,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);
@@ -3297,8 +3317,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;
@@ -3306,27 +3326,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);
}
}
@@ -3338,7 +3358,7 @@ void TileSetAtlasSource::create_tile(const Vector2i p_atlas_coords, const Vector
ERR_FAIL_COND(p_size.x <= 0 || p_size.y <= 0);
bool room_for_tile = has_room_for_tile(p_atlas_coords, p_size, 1, Vector2i(), 1);
- ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile, tiles are already present in the space the tile would cover.");
+ ERR_FAIL_COND_MSG(!room_for_tile, "Cannot create tile. The tile is outside the texture or tiles are already present in the space the tile would cover.");
// Initialize the tile data.
TileAlternativesData tad;
@@ -3368,8 +3388,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
@@ -3520,6 +3540,13 @@ Vector2i TileSetAtlasSource::get_tile_id(int p_index) const {
}
bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile) const {
+ if (p_atlas_coords.x < 0 || p_atlas_coords.y < 0) {
+ return false;
+ }
+ if (p_size.x <= 0 || p_size.y <= 0) {
+ return false;
+ }
+ Size2i atlas_grid_size = get_atlas_grid_size();
for (int frame = 0; frame < p_frames_count; frame++) {
Vector2i frame_coords = p_atlas_coords + (p_size + p_animation_separation) * ((p_animation_columns > 0) ? Vector2i(frame % p_animation_columns, frame / p_animation_columns) : Vector2i(frame, 0));
for (int x = 0; x < p_size.x; x++) {
@@ -3528,12 +3555,46 @@ bool TileSetAtlasSource::has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_s
if (_coords_mapping_cache.has(coords) && _coords_mapping_cache[coords] != p_ignored_tile) {
return false;
}
+ if (coords.x >= atlas_grid_size.x || coords.y >= atlas_grid_size.y) {
+ return false;
+ }
}
}
}
return true;
}
+PackedVector2Array TileSetAtlasSource::get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size) {
+ ERR_FAIL_COND_V(p_margins.x < 0 || p_margins.y < 0, PackedVector2Array());
+ ERR_FAIL_COND_V(p_separation.x < 0 || p_separation.y < 0, PackedVector2Array());
+ ERR_FAIL_COND_V(p_texture_region_size.x <= 0 || p_texture_region_size.y <= 0, PackedVector2Array());
+
+ // Compute the new atlas grid size.
+ Size2 new_grid_size;
+ if (p_texture.is_valid()) {
+ Size2i valid_area = p_texture->get_size() - p_margins;
+
+ // Compute the number of valid tiles in the tiles atlas
+ if (valid_area.x >= p_texture_region_size.x && valid_area.y >= p_texture_region_size.y) {
+ valid_area -= p_texture_region_size;
+ new_grid_size = Size2i(1, 1) + valid_area / (p_texture_region_size + p_separation);
+ }
+ }
+
+ Vector<Vector2> output;
+ for (KeyValue<Vector2i, TileAlternativesData> &E : tiles) {
+ for (unsigned int frame = 0; frame < E.value.animation_frames_durations.size(); frame++) {
+ Vector2i frame_coords = E.key + (E.value.size_in_atlas + E.value.animation_separation) * ((E.value.animation_columns > 0) ? Vector2i(frame % E.value.animation_columns, frame / E.value.animation_columns) : Vector2i(frame, 0));
+ frame_coords += E.value.size_in_atlas;
+ if (frame_coords.x > new_grid_size.x || frame_coords.y > new_grid_size.y) {
+ output.push_back(E.key);
+ break;
+ }
+ }
+ }
+ return output;
+}
+
Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int p_frame) const {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), Rect2i(), vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
ERR_FAIL_INDEX_V(p_frame, (int)tiles[p_atlas_coords].animation_frames_durations.size(), Rect2i());
@@ -3558,8 +3619,7 @@ Vector2i TileSetAtlasSource::get_tile_effective_texture_offset(Vector2i p_atlas_
margin = Vector2i(MAX(0, margin.x), MAX(0, margin.y));
Vector2i effective_texture_offset = Object::cast_to<TileData>(get_tile_data(p_atlas_coords, p_alternative_tile))->get_texture_offset();
if (ABS(effective_texture_offset.x) > margin.x || ABS(effective_texture_offset.y) > margin.y) {
- effective_texture_offset.x = CLAMP(effective_texture_offset.x, -margin.x, margin.x);
- effective_texture_offset.y = CLAMP(effective_texture_offset.y, -margin.y, margin.y);
+ effective_texture_offset = effective_texture_offset.clamp(-margin, margin);
}
return effective_texture_offset;
@@ -3599,34 +3659,6 @@ void TileSetAtlasSource::move_tile_in_atlas(Vector2i p_atlas_coords, Vector2i p_
emit_signal(SNAME("changed"));
}
-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) {
- return true;
- }
- }
-
- return false;
-}
-
-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 (int i = 0; i < to_remove.size(); i++) {
- remove_tile(to_remove[i]);
- }
-}
-
int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) {
ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords)));
ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && tiles[p_atlas_coords].alternatives.has(p_alternative_id_override), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override));
@@ -3727,7 +3759,7 @@ void TileSetAtlasSource::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tile_size_in_atlas", "atlas_coords"), &TileSetAtlasSource::get_tile_size_in_atlas);
ClassDB::bind_method(D_METHOD("has_room_for_tile", "atlas_coords", "size", "animation_columns", "animation_separation", "frames_count", "ignored_tile"), &TileSetAtlasSource::has_room_for_tile, DEFVAL(INVALID_ATLAS_COORDS));
-
+ ClassDB::bind_method(D_METHOD("get_tiles_to_be_removed_on_change", "texture", "margins", "separation", "texture_region_size"), &TileSetAtlasSource::get_tiles_to_be_removed_on_change);
ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords);
ClassDB::bind_method(D_METHOD("set_tile_animation_columns", "atlas_coords", "frame_columns"), &TileSetAtlasSource::set_tile_animation_columns);
@@ -3752,16 +3784,14 @@ void TileSetAtlasSource::_bind_methods() {
// Helpers.
ClassDB::bind_method(D_METHOD("get_atlas_grid_size"), &TileSetAtlasSource::get_atlas_grid_size);
- ClassDB::bind_method(D_METHOD("has_tiles_outside_texture"), &TileSetAtlasSource::has_tiles_outside_texture);
- ClassDB::bind_method(D_METHOD("clear_tiles_outside_texture"), &TileSetAtlasSource::clear_tiles_outside_texture);
ClassDB::bind_method(D_METHOD("get_tile_texture_region", "atlas_coords", "frame"), &TileSetAtlasSource::get_tile_texture_region, DEFVAL(0));
}
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);
}
}
}
@@ -3827,6 +3857,20 @@ void TileSetAtlasSource::_create_coords_mapping_cache(Vector2i p_atlas_coords) {
}
}
+void TileSetAtlasSource::_clear_tiles_outside_texture() {
+ LocalVector<Vector2i> to_remove;
+
+ for (const KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> &E : tiles) {
+ if (!has_room_for_tile(E.key, E.value.size_in_atlas, E.value.animation_columns, E.value.animation_separation, E.value.animation_frames_durations.size(), E.key)) {
+ to_remove.push_back(E.key);
+ }
+ }
+
+ for (unsigned int i = 0; i < to_remove.size(); i++) {
+ remove_tile(to_remove[i]);
+ }
+}
+
/////////////////////////////// TileSetScenesCollectionSource //////////////////////////////////////
void TileSetScenesCollectionSource::_compute_next_alternative_id() {
@@ -4300,9 +4344,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) {
@@ -4313,6 +4374,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());
@@ -4514,11 +4580,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;
@@ -4526,8 +4588,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);
@@ -4629,9 +4702,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);
@@ -4702,6 +4784,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++) {
@@ -4791,8 +4875,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..351bdff89d 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -180,6 +180,7 @@ protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
private:
// --- TileSet data ---
@@ -478,8 +479,10 @@ private:
void _compute_next_alternative_id(const Vector2i p_atlas_coords);
- void _create_coords_mapping_cache(Vector2i p_atlas_coords);
void _clear_coords_mapping_cache(Vector2i p_atlas_coords);
+ void _create_coords_mapping_cache(Vector2i p_atlas_coords);
+
+ void _clear_tiles_outside_texture();
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -533,7 +536,7 @@ public:
virtual Vector2i get_tile_id(int p_index) const override;
bool has_room_for_tile(Vector2i p_atlas_coords, Vector2i p_size, int p_animation_columns, Vector2i p_animation_separation, int p_frames_count, Vector2i p_ignored_tile = INVALID_ATLAS_COORDS) const;
-
+ PackedVector2Array get_tiles_to_be_removed_on_change(Ref<Texture2D> p_texture, Vector2i p_margins, Vector2i p_separation, Vector2i p_texture_region_size);
Vector2i get_tile_at_coords(Vector2i p_atlas_coords) const;
// Animation.
@@ -564,8 +567,6 @@ public:
// Helpers.
Vector2i get_atlas_grid_size() const;
- bool has_tiles_outside_texture();
- void clear_tiles_outside_texture();
Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
Vector2i get_tile_effective_texture_offset(Vector2i p_atlas_coords, int p_alternative_tile) const;
@@ -645,6 +646,8 @@ private:
float one_way_margin = 1.0;
};
+ Vector2 linear_velocity;
+ float angular_velocity = 0.0;
Vector<PolygonShapeTileData> polygons;
};
Vector<PhysicsLayerTileData> physics;
@@ -718,8 +721,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..0dc83b4bb8 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -113,6 +113,10 @@ bool VisualShaderNode::is_output_port_expandable(int p_port) const {
return false;
}
+bool VisualShaderNode::has_output_port_preview(int p_port) const {
+ return true;
+}
+
void VisualShaderNode::_set_output_ports_expanded(const Array &p_values) {
for (int i = 0; i < p_values.size(); i++) {
expanded_output_ports[p_values[i]] = true;
@@ -197,9 +201,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 +464,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 +488,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 +565,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 +579,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 +823,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 +1045,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 +1275,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 +1284,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 +1720,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;
}
}
@@ -2307,7 +2311,21 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::ports[] = {
};
const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
+ // Spatial, Vertex
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Spatial, Fragment
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "tangent", "vec3(0.0, 1.0, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "binormal", "vec3(1.0, 0.0, 0.0)" },
@@ -2315,44 +2333,59 @@ const VisualShaderNodeInput::Port VisualShaderNodeInput::preview_ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
-
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "side", "1.0" },
-
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
// Spatial, Light
- { Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
- { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv2", "vec3(UV, 0.0)" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "viewport_size", "vec3(1.0, 1.0, 0.0)" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Canvas Item, Vertex
+
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "vertex", "vec3(VERTEX, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Canvas Item, Fragment
+
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Canvas Item, Light
+
+ { Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "fragcoord", "FRAGCOORD.rgb" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "uv", "vec3(UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "normal", "vec3(0.0, 0.0, 1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "color", "vec3(1.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "1.0" },
-
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
{ Shader::MODE_CANVAS_ITEM, VisualShader::TYPE_LIGHT, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+ // Sky
+
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_VECTOR, "screen_uv", "vec3(SCREEN_UV, 0.0)" },
+ { Shader::MODE_SKY, VisualShader::TYPE_SKY, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
// Particles
+
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_COLLIDE, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_START_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
{ Shader::MODE_PARTICLES, VisualShader::TYPE_PROCESS_CUSTOM, VisualShaderNode::PORT_TYPE_SCALAR, "time", "TIME" },
+
{ Shader::MODE_MAX, VisualShader::TYPE_MAX, VisualShaderNode::PORT_TYPE_TRANSFORM, nullptr, nullptr },
};
@@ -2413,13 +2446,10 @@ String VisualShaderNodeInput::generate_code(Shader::Mode p_mode, VisualShader::T
case PORT_TYPE_VECTOR: {
code = " " + p_output_vars[0] + " = vec3(0.0);\n";
} break;
- case PORT_TYPE_TRANSFORM: {
- code = " " + p_output_vars[0] + " = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
- } break;
case PORT_TYPE_BOOLEAN: {
code = " " + p_output_vars[0] + " = false;\n";
} break;
- default: //default (none found) is scalar
+ default:
break;
}
}
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index b3efac02aa..f910f28536 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -254,6 +254,8 @@ public:
void set_input_port_connected(int p_port, bool p_connected);
virtual bool is_generate_input_var(int p_port) const;
+ virtual bool has_output_port_preview(int p_port) const;
+
virtual bool is_output_port_expandable(int p_port) const;
void _set_output_ports_expanded(const Array &p_data);
Array _get_output_ports_expanded() const;
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index 5fe801e037..18b933e5cf 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -47,6 +47,10 @@ String VisualShaderNodeParticleEmitter::get_output_port_name(int p_port) const {
return String();
}
+bool VisualShaderNodeParticleEmitter::has_output_port_preview(int p_port) const {
+ return false;
+}
+
VisualShaderNodeParticleEmitter::VisualShaderNodeParticleEmitter() {
}
@@ -265,6 +269,10 @@ Vector<StringName> VisualShaderNodeParticleMultiplyByAxisAngle::get_editable_pro
return props;
}
+bool VisualShaderNodeParticleMultiplyByAxisAngle::has_output_port_preview(int p_port) const {
+ return false;
+}
+
VisualShaderNodeParticleMultiplyByAxisAngle::VisualShaderNodeParticleMultiplyByAxisAngle() {
set_input_port_default_value(1, Vector3(1, 0, 0));
set_input_port_default_value(2, 0.0);
@@ -313,6 +321,10 @@ String VisualShaderNodeParticleConeVelocity::get_output_port_name(int p_port) co
return String();
}
+bool VisualShaderNodeParticleConeVelocity::has_output_port_preview(int p_port) const {
+ return false;
+}
+
String VisualShaderNodeParticleConeVelocity::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String code;
code += " __radians = radians(" + (p_input_vars[1].is_empty() ? (String)get_input_port_default_value(1) : p_input_vars[1]) + ");\n";
@@ -421,6 +433,10 @@ VisualShaderNodeParticleRandomness::OpType VisualShaderNodeParticleRandomness::g
return op_type;
}
+bool VisualShaderNodeParticleRandomness::has_output_port_preview(int p_port) const {
+ return false;
+}
+
VisualShaderNodeParticleRandomness::VisualShaderNodeParticleRandomness() {
set_input_port_default_value(0, 0.0);
set_input_port_default_value(1, 1.0);
@@ -521,6 +537,10 @@ VisualShaderNodeParticleAccelerator::Mode VisualShaderNodeParticleAccelerator::g
return mode;
}
+bool VisualShaderNodeParticleAccelerator::has_output_port_preview(int p_port) const {
+ return false;
+}
+
VisualShaderNodeParticleAccelerator::VisualShaderNodeParticleAccelerator() {
set_input_port_default_value(0, Vector3(1, 1, 1));
set_input_port_default_value(1, 0.0);
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index f5435c3511..b8bc7992cc 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -42,6 +42,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
VisualShaderNodeParticleEmitter();
};
@@ -112,6 +113,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -135,6 +137,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -168,6 +171,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -209,6 +213,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool has_output_port_preview(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index b283749ffa..186764e69e 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -64,6 +64,8 @@ SceneStringNames::SceneStringNames() {
pose_updated = StaticCString::create("pose_updated");
bone_pose_changed = StaticCString::create("bone_pose_changed");
+ bone_enabled_changed = StaticCString::create("bone_enabled_changed");
+ show_rest_only_changed = StaticCString::create("show_rest_only_changed");
mouse_entered = StaticCString::create("mouse_entered");
mouse_exited = StaticCString::create("mouse_exited");
@@ -73,6 +75,7 @@ SceneStringNames::SceneStringNames() {
focus_entered = StaticCString::create("focus_entered");
focus_exited = StaticCString::create("focus_exited");
+ pre_sort_children = StaticCString::create("pre_sort_children");
sort_children = StaticCString::create("sort_children");
body_shape_entered = StaticCString::create("body_shape_entered");
@@ -134,6 +137,7 @@ SceneStringNames::SceneStringNames() {
_spatial_editor_group = StaticCString::create("_spatial_editor_group");
_request_gizmo = StaticCString::create("_request_gizmo");
+ _set_subgizmo_selection = StaticCString::create("_set_subgizmo_selection");
_clear_subgizmo_selection = StaticCString::create("_clear_subgizmo_selection");
offset = StaticCString::create("offset");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index 2923351eab..67007c85e0 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -89,6 +89,7 @@ public:
StringName focus_entered;
StringName focus_exited;
+ StringName pre_sort_children;
StringName sort_children;
StringName finished;
@@ -99,6 +100,8 @@ public:
StringName pose_updated;
StringName bone_pose_changed;
+ StringName bone_enabled_changed;
+ StringName show_rest_only_changed;
StringName body_shape_entered;
StringName body_entered;
@@ -154,6 +157,7 @@ public:
StringName _spatial_editor_group;
StringName _request_gizmo;
+ StringName _set_subgizmo_selection;
StringName _clear_subgizmo_selection;
StringName offset;