summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite.cpp46
-rw-r--r--scene/2d/animated_sprite.h5
-rw-r--r--scene/2d/area_2d.cpp6
-rw-r--r--scene/2d/audio_stream_player_2d.cpp4
-rw-r--r--scene/2d/camera_2d.cpp8
-rw-r--r--scene/2d/canvas_item.cpp2
-rw-r--r--scene/2d/canvas_item.h6
-rw-r--r--scene/2d/light_occluder_2d.cpp6
-rw-r--r--scene/2d/light_occluder_2d.h2
-rw-r--r--scene/2d/line_2d.cpp3
-rw-r--r--scene/2d/mesh_instance_2d.cpp30
-rw-r--r--scene/2d/mesh_instance_2d.h30
-rw-r--r--scene/2d/node_2d.cpp2
-rw-r--r--scene/2d/parallax_layer.cpp1
-rw-r--r--scene/2d/particles_2d.cpp4
-rw-r--r--scene/2d/path_2d.cpp2
-rw-r--r--scene/2d/polygon_2d.cpp170
-rw-r--r--scene/2d/polygon_2d.h24
-rw-r--r--scene/2d/skeleton_2d.cpp116
-rw-r--r--scene/2d/skeleton_2d.h41
-rw-r--r--scene/2d/sprite.cpp34
-rw-r--r--scene/2d/tile_map.cpp17
-rw-r--r--scene/3d/area.cpp2
-rw-r--r--scene/3d/audio_stream_player_3d.cpp2
-rw-r--r--scene/3d/camera.cpp4
-rw-r--r--scene/3d/camera.h4
-rw-r--r--scene/3d/light.cpp12
-rw-r--r--scene/3d/particles.cpp53
-rw-r--r--scene/3d/path.cpp2
-rw-r--r--scene/3d/physics_body.cpp1113
-rw-r--r--scene/3d/physics_body.h264
-rw-r--r--scene/3d/physics_joint.cpp21
-rw-r--r--scene/3d/physics_joint.h3
-rw-r--r--scene/3d/reflection_probe.cpp2
-rw-r--r--scene/3d/skeleton.cpp177
-rw-r--r--scene/3d/skeleton.h37
-rw-r--r--scene/3d/spatial.cpp19
-rw-r--r--scene/3d/spatial.h7
-rw-r--r--scene/3d/sprite_3d.cpp29
-rw-r--r--scene/3d/vehicle_body.cpp7
-rw-r--r--scene/3d/voxel_light_baker.cpp9
-rw-r--r--scene/animation/animation_player.cpp22
-rw-r--r--scene/animation/animation_tree_player.cpp6
-rw-r--r--scene/gui/base_button.cpp16
-rw-r--r--scene/gui/base_button.h4
-rw-r--r--scene/gui/color_picker.cpp54
-rw-r--r--scene/gui/color_picker.h6
-rw-r--r--scene/gui/container.cpp6
-rw-r--r--scene/gui/control.cpp58
-rw-r--r--scene/gui/control.h8
-rw-r--r--scene/gui/grid_container.cpp40
-rw-r--r--scene/gui/grid_container.h1
-rw-r--r--scene/gui/item_list.cpp5
-rw-r--r--scene/gui/label.cpp25
-rw-r--r--scene/gui/line_edit.cpp14
-rw-r--r--scene/gui/line_edit.h4
-rw-r--r--scene/gui/option_button.cpp2
-rw-r--r--scene/gui/progress_bar.cpp9
-rw-r--r--scene/gui/range.cpp45
-rw-r--r--scene/gui/range.h8
-rw-r--r--scene/gui/rich_text_label.cpp139
-rw-r--r--scene/gui/rich_text_label.h8
-rw-r--r--scene/gui/scroll_container.cpp16
-rw-r--r--scene/gui/split_container.cpp7
-rw-r--r--scene/gui/text_edit.cpp1586
-rw-r--r--scene/gui/text_edit.h60
-rw-r--r--scene/main/instance_placeholder.cpp24
-rw-r--r--scene/main/instance_placeholder.h1
-rw-r--r--scene/main/node.cpp66
-rw-r--r--scene/main/node.h17
-rw-r--r--scene/main/scene_tree.cpp82
-rw-r--r--scene/main/scene_tree.h8
-rwxr-xr-xscene/main/timer.cpp7
-rwxr-xr-xscene/main/timer.h2
-rw-r--r--scene/main/viewport.cpp89
-rw-r--r--scene/main/viewport.h12
-rw-r--r--scene/register_scene_types.cpp4
-rw-r--r--scene/resources/curve.cpp166
-rw-r--r--scene/resources/curve.h4
-rw-r--r--scene/resources/default_theme/default_theme.cpp12
-rw-r--r--scene/resources/dynamic_font.cpp538
-rw-r--r--scene/resources/dynamic_font.h37
-rw-r--r--scene/resources/dynamic_font_stb.cpp22
-rw-r--r--scene/resources/dynamic_font_stb.h4
-rw-r--r--scene/resources/font.cpp43
-rw-r--r--scene/resources/font.h49
-rw-r--r--scene/resources/material.cpp8
-rw-r--r--scene/resources/material.h3
-rw-r--r--scene/resources/mesh.cpp4
-rw-r--r--scene/resources/primitive_meshes.cpp56
-rw-r--r--scene/resources/primitive_meshes.h8
-rw-r--r--scene/resources/style_box.cpp2
-rw-r--r--scene/resources/texture.cpp9
-rw-r--r--scene/resources/texture.h2
-rw-r--r--scene/resources/tile_set.cpp20
-rw-r--r--scene/resources/tile_set.h7
-rw-r--r--scene/scene_string_names.cpp1
-rw-r--r--scene/scene_string_names.h1
98 files changed, 4511 insertions, 1272 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 447bd9a090..54194ff543 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -351,7 +351,7 @@ void AnimatedSprite::_notification(int p_what) {
if (frame < 0)
return;
- float speed = frames->get_animation_speed(animation);
+ float speed = frames->get_animation_speed(animation) * speed_scale;
if (speed == 0)
return; //do nothing
@@ -361,7 +361,7 @@ void AnimatedSprite::_notification(int p_what) {
if (timeout <= 0) {
- timeout = 1.0 / speed;
+ timeout = _get_frame_duration();
int fc = frames->get_frame_count(animation);
if (frame >= fc - 1) {
@@ -481,6 +481,22 @@ int AnimatedSprite::get_frame() const {
return frame;
}
+void AnimatedSprite::set_speed_scale(float p_speed_scale) {
+
+ float elapsed = _get_frame_duration() - timeout;
+
+ speed_scale = MAX(p_speed_scale, 0.0f);
+
+ // We adapt the timeout so that the animation speed adapts as soon as the speed scale is changed
+ _reset_timeout();
+ timeout -= elapsed;
+}
+
+float AnimatedSprite::get_speed_scale() const {
+
+ return speed_scale;
+}
+
void AnimatedSprite::set_centered(bool p_center) {
centered = p_center;
@@ -564,21 +580,22 @@ bool AnimatedSprite::is_playing() const {
return playing;
}
+float AnimatedSprite::_get_frame_duration() {
+ if (frames.is_valid() && frames->has_animation(animation)) {
+ float speed = frames->get_animation_speed(animation) * speed_scale;
+ if (speed > 0) {
+ return 1.0 / speed;
+ }
+ }
+ return 0.0;
+}
+
void AnimatedSprite::_reset_timeout() {
if (!playing)
return;
- if (frames.is_valid() && frames->has_animation(animation)) {
- float speed = frames->get_animation_speed(animation);
- if (speed > 0) {
- timeout = 1.0 / speed;
- } else {
- timeout = 0;
- }
- } else {
- timeout = 0;
- }
+ timeout = _get_frame_duration();
}
void AnimatedSprite::set_animation(const StringName &p_animation) {
@@ -636,6 +653,9 @@ void AnimatedSprite::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_frame", "frame"), &AnimatedSprite::set_frame);
ClassDB::bind_method(D_METHOD("get_frame"), &AnimatedSprite::get_frame);
+ ClassDB::bind_method(D_METHOD("set_speed_scale", "speed_scale"), &AnimatedSprite::set_speed_scale);
+ ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimatedSprite::get_speed_scale);
+
ClassDB::bind_method(D_METHOD("_res_changed"), &AnimatedSprite::_res_changed);
ADD_SIGNAL(MethodInfo("frame_changed"));
@@ -644,6 +664,7 @@ void AnimatedSprite::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "frame", PROPERTY_HINT_SPRITE_FRAME), "set_frame", "get_frame");
+ ADD_PROPERTYNO(PropertyInfo(Variant::REAL, "speed_scale"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered");
ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
@@ -658,6 +679,7 @@ AnimatedSprite::AnimatedSprite() {
vflip = false;
frame = 0;
+ speed_scale = 1.0f;
playing = false;
animation = "default";
timeout = 0;
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index c7606d88aa..be5b1ef6d6 100644
--- a/scene/2d/animated_sprite.h
+++ b/scene/2d/animated_sprite.h
@@ -129,6 +129,7 @@ class AnimatedSprite : public Node2D {
bool playing;
StringName animation;
int frame;
+ float speed_scale;
bool centered;
Point2 offset;
@@ -140,6 +141,7 @@ class AnimatedSprite : public Node2D {
void _res_changed();
+ float _get_frame_duration();
void _reset_timeout();
void _set_playing(bool p_playing);
bool _is_playing() const;
@@ -172,6 +174,9 @@ public:
void set_frame(int p_frame);
int get_frame() const;
+ void set_speed_scale(float p_speed_scale);
+ float get_speed_scale() const;
+
void set_centered(bool p_center);
bool is_centered() const;
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index bb914b90fc..c375374dce 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -666,11 +666,11 @@ void Area2D::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode");
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001"), "set_gravity_distance_scale", "get_gravity_distance_scale");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "gravity_vec"), "set_gravity_vector", "get_gravity_vector");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.001"), "set_gravity", "get_gravity");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_linear_damp", "get_linear_damp");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_angular_damp", "get_angular_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_linear_damp", "get_linear_damp");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.01,or_greater"), "set_angular_damp", "get_angular_damp");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable");
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index f998f23d3b..54541293fd 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -461,8 +461,8 @@ void AudioStreamPlayer2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_RANGE, "1,65536,1"), "set_max_distance", "get_max_distance");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "attenuation", PROPERTY_HINT_EXP_EASING), "set_attenuation", "get_attenuation");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "1,4096,1,or_greater"), "set_max_distance", "get_max_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_attenuation", "get_attenuation");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index d172da5bd9..3b86ca76ea 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -91,8 +91,8 @@ Transform2D Camera2D::get_camera_transform() {
if (anchor_mode == ANCHOR_MODE_DRAG_CENTER) {
if (h_drag_enabled && !Engine::get_singleton()->is_editor_hint()) {
- camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT]));
- camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT]));
+ camera_pos.x = MIN(camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * zoom.x * drag_margin[MARGIN_LEFT]));
+ camera_pos.x = MAX(camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * zoom.x * drag_margin[MARGIN_RIGHT]));
} else {
if (h_ofs < 0) {
@@ -104,8 +104,8 @@ Transform2D Camera2D::get_camera_transform() {
if (v_drag_enabled && !Engine::get_singleton()->is_editor_hint()) {
- camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP]));
- camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM]));
+ camera_pos.y = MIN(camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * zoom.y * drag_margin[MARGIN_TOP]));
+ camera_pos.y = MAX(camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * zoom.y * drag_margin[MARGIN_BOTTOM]));
} else {
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index a2637f816e..27bdeda4a8 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -94,6 +94,7 @@ void CanvasItemMaterial::_update_shader() {
case BLEND_MODE_SUB: code += "blend_sub"; break;
case BLEND_MODE_MUL: code += "blend_mul"; break;
case BLEND_MODE_PREMULT_ALPHA: code += "blend_premul_alpha"; break;
+ case BLEND_MODE_DISABLED: code += "blend_disabled"; break;
}
switch (light_mode) {
@@ -1105,6 +1106,7 @@ void CanvasItem::_bind_methods() {
BIND_ENUM_CONSTANT(BLEND_MODE_SUB);
BIND_ENUM_CONSTANT(BLEND_MODE_MUL);
BIND_ENUM_CONSTANT(BLEND_MODE_PREMULT_ALPHA);
+ BIND_ENUM_CONSTANT(BLEND_MODE_DISABLED);
BIND_CONSTANT(NOTIFICATION_TRANSFORM_CHANGED);
BIND_CONSTANT(NOTIFICATION_DRAW);
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index 85de0d2796..10d5082dfc 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -54,7 +54,8 @@ public:
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
- BLEND_MODE_PREMULT_ALPHA
+ BLEND_MODE_PREMULT_ALPHA,
+ BLEND_MODE_DISABLED
};
enum LightMode {
@@ -145,7 +146,8 @@ public:
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
- BLEND_MODE_PREMULT_ALPHA
+ BLEND_MODE_PREMULT_ALPHA,
+ BLEND_MODE_DISABLED
};
private:
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index d4481583fb..c9e5d0f1bc 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -107,12 +107,12 @@ OccluderPolygon2D::~OccluderPolygon2D() {
VS::get_singleton()->free(occ_polygon);
}
-#ifdef DEBUG_ENABLED
void LightOccluder2D::_poly_changed() {
+#ifdef DEBUG_ENABLED
update();
-}
#endif
+}
void LightOccluder2D::_notification(int p_what) {
@@ -221,9 +221,7 @@ void LightOccluder2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_occluder_light_mask", "mask"), &LightOccluder2D::set_occluder_light_mask);
ClassDB::bind_method(D_METHOD("get_occluder_light_mask"), &LightOccluder2D::get_occluder_light_mask);
-#ifdef DEBUG_ENABLED
ClassDB::bind_method("_poly_changed", &LightOccluder2D::_poly_changed);
-#endif
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "occluder", PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D"), "set_occluder_polygon", "get_occluder_polygon");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask");
diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h
index 5e16351a6f..d59c9100b0 100644
--- a/scene/2d/light_occluder_2d.h
+++ b/scene/2d/light_occluder_2d.h
@@ -78,9 +78,7 @@ class LightOccluder2D : public Node2D {
int mask;
Ref<OccluderPolygon2D> occluder_polygon;
-#ifdef DEBUG_ENABLED
void _poly_changed();
-#endif
protected:
void _notification(int p_what);
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index 229c2c6fe8..3e61dd05f4 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -270,7 +270,8 @@ void Line2D::_draw() {
lb.indices,
lb.vertices,
lb.colors,
- lb.uvs,
+ lb.uvs, Vector<int>(), Vector<float>(),
+
texture_rid);
// DEBUG
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp
index adbb227d0c..9f21fe1a1f 100644
--- a/scene/2d/mesh_instance_2d.cpp
+++ b/scene/2d/mesh_instance_2d.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* mesh_instance_2d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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 "mesh_instance_2d.h"
void MeshInstance2D::_notification(int p_what) {
diff --git a/scene/2d/mesh_instance_2d.h b/scene/2d/mesh_instance_2d.h
index d1d1ade0ae..c9889c1c03 100644
--- a/scene/2d/mesh_instance_2d.h
+++ b/scene/2d/mesh_instance_2d.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* mesh_instance_2d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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 MESH_INSTANCE_2D_H
#define MESH_INSTANCE_2D_H
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index f8cec72400..3813bd96fe 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -451,7 +451,7 @@ void Node2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_rotation", PROPERTY_HINT_NONE, "", 0), "set_global_rotation", "get_global_rotation");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_rotation_degrees", PROPERTY_HINT_NONE, "", 0), "set_global_rotation_degrees", "get_global_rotation_degrees");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_scale", PROPERTY_HINT_NONE, "", 0), "set_global_scale", "get_global_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_scale", PROPERTY_HINT_NONE, "", 0), "set_global_scale", "get_global_scale");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_transform", PROPERTY_HINT_NONE, "", 0), "set_global_transform", "get_global_transform");
ADD_GROUP("Z Index", "");
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index 584c2f2c85..2ac6c76032 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -120,7 +120,6 @@ void ParallaxLayer::set_base_offset_and_scale(const Point2 &p_offset, float p_sc
if (mirroring.x) {
double den = mirroring.x * p_scale;
- double before = new_ofs.x;
new_ofs.x -= den * ceil(new_ofs.x / den);
}
diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp
index 7eaa5bb88c..1da1d44b17 100644
--- a/scene/2d/particles_2d.cpp
+++ b/scene/2d/particles_2d.cpp
@@ -367,9 +367,9 @@ void Particles2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("restart"), &Particles2D::restart);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,100000,1"), "set_amount", "get_amount");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount");
ADD_GROUP("Time", "");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale");
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index 7377591f7d..658b998d17 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -299,7 +299,7 @@ void PathFollow2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lookahead", "lookahead"), &PathFollow2D::set_lookahead);
ClassDB::bind_method(D_METHOD("get_lookahead"), &PathFollow2D::get_lookahead);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01"), "set_offset", "get_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_EXP_RANGE, "0,10000,0.01,or_greater"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index bf5bf29b2e..4d6ebc81c3 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -30,7 +30,7 @@
#include "polygon_2d.h"
#include "core/math/geometry.h"
-
+#include "skeleton_2d.h"
Dictionary Polygon2D::_edit_get_state() const {
Dictionary state = Node2D::_edit_get_state();
state["offset"] = offset;
@@ -91,8 +91,20 @@ void Polygon2D::_notification(int p_what) {
if (polygon.size() < 3)
return;
+ Skeleton2D *skeleton_node = NULL;
+ if (has_node(skeleton)) {
+ skeleton_node = Object::cast_to<Skeleton2D>(get_node(skeleton));
+ }
+
+ if (skeleton_node)
+ VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), skeleton_node->get_skeleton());
+ else
+ VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID());
+
Vector<Vector2> points;
Vector<Vector2> uvs;
+ Vector<int> bones;
+ Vector<float> weights;
points.resize(polygon.size());
@@ -180,6 +192,70 @@ void Polygon2D::_notification(int p_what) {
}
}
+ if (!invert && bone_weights.size()) {
+ //a skeleton is set! fill indices and weights
+ int vc = points.size();
+ bones.resize(vc * 4);
+ weights.resize(vc * 4);
+
+ int *bonesw = bones.ptrw();
+ float *weightsw = weights.ptrw();
+
+ for (int i = 0; i < vc * 4; i++) {
+ bonesw[i] = 0;
+ weightsw[i] = 0;
+ }
+
+ for (int i = 0; i < bone_weights.size(); i++) {
+ if (bone_weights[i].weights.size() != points.size()) {
+ continue; //different number of vertices, sorry not using.
+ }
+ if (!skeleton_node->has_node(bone_weights[i].path)) {
+ continue; //node does not exist
+ }
+ Bone2D *bone = Object::cast_to<Bone2D>(skeleton_node->get_node(bone_weights[i].path));
+ if (!bone) {
+ continue;
+ }
+
+ int bone_index = bone->get_index_in_skeleton();
+ PoolVector<float>::Read r = bone_weights[i].weights.read();
+ for (int j = 0; j < vc; j++) {
+ if (r[j] == 0.0)
+ continue; //weight is unpainted, skip
+ //find an index with a weight
+ for (int k = 0; k < 4; k++) {
+ if (weightsw[j * 4 + k] < r[j]) {
+ //this is less than this weight, insert weight!
+ for (int l = 3; l > k; l--) {
+ weightsw[j * 4 + l] = weightsw[j * 4 + l - 1];
+ bonesw[j * 4 + l] = bonesw[j * 4 + l - 1];
+ }
+ weightsw[j * 4 + k] = r[j];
+ bonesw[j * 4 + k] = bone_index;
+ break;
+ }
+ }
+ }
+ }
+
+ //normalize the weights
+ for (int i = 0; i < vc; i++) {
+ float tw = 0;
+ for (int j = 0; j < 4; j++) {
+ tw += weightsw[i * 4 + j];
+ }
+ if (tw == 0)
+ continue; //unpainted, do nothing
+
+ //normalize
+ for (int j = 0; j < 4; j++) {
+ weightsw[i * 4 + j] /= tw;
+ // print_line("point " + itos(i) + " idx " + itos(j) + " index: " + itos(bonesw[i * 4 + j]) + " weight: " + rtos(weightsw[i * 4 + j]));
+ }
+ }
+ }
+
Vector<Color> colors;
int color_len = vertex_colors.size();
colors.resize(len);
@@ -197,7 +273,8 @@ void Polygon2D::_notification(int p_what) {
// VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID());
if (invert || splits.size() == 0) {
- VS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID(), RID(), antialiased);
+ Vector<int> indices = Geometry::triangulate_polygon(points);
+ VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID());
} else {
//use splits
Vector<int> loop;
@@ -268,7 +345,7 @@ void Polygon2D::_notification(int p_what) {
//print_line("loops: " + itos(loops.size()) + " indices: " + itos(indices.size()));
- VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID());
+ VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID());
}
} break;
@@ -430,6 +507,74 @@ Vector2 Polygon2D::get_offset() const {
return offset;
}
+void Polygon2D::add_bone(const NodePath &p_path, const PoolVector<float> &p_weights) {
+
+ Bone bone;
+ bone.path = p_path;
+ bone.weights = p_weights;
+ bone_weights.push_back(bone);
+}
+int Polygon2D::get_bone_count() const {
+ return bone_weights.size();
+}
+NodePath Polygon2D::get_bone_path(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, bone_weights.size(), NodePath());
+ return bone_weights[p_index].path;
+}
+PoolVector<float> Polygon2D::get_bone_weights(int p_index) const {
+
+ ERR_FAIL_INDEX_V(p_index, bone_weights.size(), PoolVector<float>());
+ return bone_weights[p_index].weights;
+}
+void Polygon2D::erase_bone(int p_idx) {
+
+ ERR_FAIL_INDEX(p_idx, bone_weights.size());
+ bone_weights.remove(p_idx);
+}
+
+void Polygon2D::clear_bones() {
+ bone_weights.clear();
+}
+
+void Polygon2D::set_bone_weights(int p_index, const PoolVector<float> &p_weights) {
+ ERR_FAIL_INDEX(p_index, bone_weights.size());
+ bone_weights[p_index].weights = p_weights;
+ update();
+}
+void Polygon2D::set_bone_path(int p_index, const NodePath &p_path) {
+ ERR_FAIL_INDEX(p_index, bone_weights.size());
+ bone_weights[p_index].path = p_path;
+ update();
+}
+
+Array Polygon2D::_get_bones() const {
+ Array bones;
+ for (int i = 0; i < get_bone_count(); i++) {
+ bones.push_back(get_bone_path(i));
+ bones.push_back(get_bone_weights(i));
+ }
+ return bones;
+}
+void Polygon2D::_set_bones(const Array &p_bones) {
+
+ ERR_FAIL_COND(p_bones.size() & 1);
+ clear_bones();
+ for (int i = 0; i < p_bones.size(); i += 2) {
+ add_bone(p_bones[i], p_bones[i + 1]);
+ }
+}
+
+void Polygon2D::set_skeleton(const NodePath &p_skeleton) {
+ if (skeleton == p_skeleton)
+ return;
+ skeleton = p_skeleton;
+ update();
+}
+
+NodePath Polygon2D::get_skeleton() const {
+ return skeleton;
+}
+
void Polygon2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &Polygon2D::set_polygon);
@@ -474,6 +619,21 @@ void Polygon2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Polygon2D::set_offset);
ClassDB::bind_method(D_METHOD("get_offset"), &Polygon2D::get_offset);
+ ClassDB::bind_method(D_METHOD("add_bone", "path", "weights"), &Polygon2D::add_bone);
+ ClassDB::bind_method(D_METHOD("get_bone_count"), &Polygon2D::get_bone_count);
+ ClassDB::bind_method(D_METHOD("get_bone_path", "index"), &Polygon2D::get_bone_path);
+ ClassDB::bind_method(D_METHOD("get_bone_weights", "index"), &Polygon2D::get_bone_weights);
+ ClassDB::bind_method(D_METHOD("erase_bone", "index"), &Polygon2D::erase_bone);
+ ClassDB::bind_method(D_METHOD("clear_bones"), &Polygon2D::clear_bones);
+ ClassDB::bind_method(D_METHOD("set_bone_path", "index", "path"), &Polygon2D::set_bone_path);
+ ClassDB::bind_method(D_METHOD("set_bone_weights", "index", "weights"), &Polygon2D::set_bone_weights);
+
+ ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &Polygon2D::set_skeleton);
+ ClassDB::bind_method(D_METHOD("get_skeleton"), &Polygon2D::get_skeleton);
+
+ ClassDB::bind_method(D_METHOD("_set_bones", "bones"), &Polygon2D::_set_bones);
+ ClassDB::bind_method(D_METHOD("_get_bones"), &Polygon2D::_get_bones);
+
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv");
ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "splits"), "set_splits", "get_splits");
@@ -488,10 +648,14 @@ void Polygon2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_scale"), "set_texture_scale", "get_texture_scale");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation_degrees", PROPERTY_HINT_RANGE, "-1440,1440,0.1"), "set_texture_rotation_degrees", "get_texture_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation", PROPERTY_HINT_NONE, "", 0), "set_texture_rotation", "get_texture_rotation");
+ ADD_GROUP("Skeleton", "");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton"), "set_skeleton", "get_skeleton");
ADD_GROUP("Invert", "invert_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_enable"), "set_invert", "get_invert");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "invert_border", PROPERTY_HINT_RANGE, "0.1,16384,0.1"), "set_invert_border", "get_invert_border");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_bones", "_get_bones");
}
Polygon2D::Polygon2D() {
diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h
index 262208f2f1..575f71d74a 100644
--- a/scene/2d/polygon_2d.h
+++ b/scene/2d/polygon_2d.h
@@ -42,6 +42,13 @@ class Polygon2D : public Node2D {
PoolVector<Color> vertex_colors;
PoolVector<int> splits;
+ struct Bone {
+ NodePath path;
+ PoolVector<float> weights;
+ };
+
+ Vector<Bone> bone_weights;
+
Color color;
Ref<Texture> texture;
Size2 tex_scale;
@@ -56,6 +63,11 @@ class Polygon2D : public Node2D {
mutable bool rect_cache_dirty;
mutable Rect2 item_rect;
+ NodePath skeleton;
+
+ Array _get_bones() const;
+ void _set_bones(const Array &p_bones);
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -114,6 +126,18 @@ public:
void set_offset(const Vector2 &p_offset);
Vector2 get_offset() const;
+ void add_bone(const NodePath &p_path = NodePath(), const PoolVector<float> &p_weights = PoolVector<float>());
+ int get_bone_count() const;
+ NodePath get_bone_path(int p_index) const;
+ PoolVector<float> get_bone_weights(int p_index) const;
+ void erase_bone(int p_idx);
+ void clear_bones();
+ void set_bone_weights(int p_index, const PoolVector<float> &p_weights);
+ void set_bone_path(int p_index, const NodePath &p_path);
+
+ void set_skeleton(const NodePath &p_skeleton);
+ NodePath get_skeleton() const;
+
Polygon2D();
};
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 705e82bcbb..0595cc43b8 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* skeleton_2d.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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 "skeleton_2d.h"
void Bone2D::_notification(int p_what) {
@@ -12,6 +42,8 @@ void Bone2D::_notification(int p_what) {
break;
if (!Object::cast_to<Bone2D>(parent))
break; //skeletons must be chained to Bone2Ds.
+
+ parent = parent->get_parent();
}
if (skeleton) {
@@ -26,6 +58,11 @@ void Bone2D::_notification(int p_what) {
skeleton->_make_transform_dirty();
}
}
+ if (p_what == NOTIFICATION_MOVED_IN_PARENT) {
+ if (skeleton) {
+ skeleton->_make_bone_setup_dirty();
+ }
+ }
if (p_what == NOTIFICATION_EXIT_TREE) {
if (skeleton) {
@@ -46,12 +83,22 @@ void Bone2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rest", "rest"), &Bone2D::set_rest);
ClassDB::bind_method(D_METHOD("get_rest"), &Bone2D::get_rest);
ClassDB::bind_method(D_METHOD("apply_rest"), &Bone2D::apply_rest);
+ ClassDB::bind_method(D_METHOD("get_skeleton_rest"), &Bone2D::get_skeleton_rest);
+ ClassDB::bind_method(D_METHOD("get_index_in_skeleton"), &Bone2D::get_index_in_skeleton);
+
+ ClassDB::bind_method(D_METHOD("set_default_length", "default_length"), &Bone2D::set_default_length);
+ ClassDB::bind_method(D_METHOD("get_default_length"), &Bone2D::get_default_length);
+
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D,"rest"),"set_rest","get_rest");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"default_length",PROPERTY_HINT_RANGE,"1,1024,1"),"set_default_length","get_default_length");
}
void Bone2D::set_rest(const Transform2D &p_rest) {
rest = p_rest;
if (skeleton)
skeleton->_make_bone_setup_dirty();
+
+ update_configuration_warning();
}
Transform2D Bone2D::get_rest() const {
@@ -71,22 +118,56 @@ void Bone2D::apply_rest() {
set_transform(rest);
}
+void Bone2D::set_default_length(float p_length) {
+
+ default_length=p_length;
+
+}
+
+float Bone2D::get_default_length() const {
+ return default_length;
+}
+
+int Bone2D::get_index_in_skeleton() const {
+ ERR_FAIL_COND_V(!skeleton,-1);
+ skeleton->_update_bone_setup();
+ return skeleton_index;
+}
String Bone2D::get_configuration_warning() const {
+
+ String warning = Node2D::get_configuration_warning();
if (!skeleton) {
+ if (warning!=String()) {
+ warning+="\n";
+ }
if (parent_bone) {
- return TTR("This Bone2D chain should end at a Skeleton2D node.");
+ warning+=TTR("This Bone2D chain should end at a Skeleton2D node.");
} else {
- return TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node.");
+ warning+=TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node.");
+ }
+ }
+
+ if (rest==Transform2D(0,0,0,0,0,0)) {
+ if (warning!=String()) {
+ warning+="\n";
}
+ warning+=TTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one.");
+
}
- return Node2D::get_configuration_warning();
+ return warning;
}
Bone2D::Bone2D() {
skeleton = NULL;
parent_bone = NULL;
+ skeleton_index=-1;
+ default_length=16;
set_notify_local_transform(true);
+ //this is a clever hack so the bone knows no rest has been set yet, allowing to show an error.
+ for(int i=0;i<3;i++) {
+ rest[i]=Vector2(0,0);
+ }
}
//////////////////////////////////////
@@ -112,7 +193,14 @@ void Skeleton2D::_update_bone_setup() {
bones.sort(); //sorty so they are always in the same order/index
for (int i = 0; i < bones.size(); i++) {
- bones[i].rest_inverse = bones[i].bone->get_skeleton_rest(); //bind pose
+ bones[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose
+ bones[i].bone->skeleton_index=i;
+ Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent());
+ if (parent_bone) {
+ bones[i].parent_index=parent_bone->skeleton_index;
+ } else {
+ bones[i].parent_index=-1;
+ }
}
transform_dirty = true;
@@ -140,13 +228,20 @@ void Skeleton2D::_update_transform() {
transform_dirty = false;
- Transform2D global_xform = get_global_transform();
- Transform2D global_xform_inverse = global_xform.affine_inverse();
+ for (int i = 0; i < bones.size(); i++) {
+
+ ERR_CONTINUE(bones[i].parent_index>=i);
+ if (bones[i].parent_index>=0) {
+ bones[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();
+ } else {
+ bones[i].accum_transform = bones[i].bone->get_transform();
+ }
+ }
for (int i = 0; i < bones.size(); i++) {
- Transform2D final_xform = bones[i].rest_inverse * bones[i].bone->get_relative_transform_to_parent(this);
- VS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, global_xform * (final_xform * global_xform_inverse));
+ Transform2D final_xform = bones[i].accum_transform * bones[i].rest_inverse;
+ VS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, final_xform);
}
}
@@ -177,10 +272,12 @@ void Skeleton2D::_notification(int p_what) {
_update_bone_setup();
if (transform_dirty)
_update_transform();
+
+ request_ready();
}
if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- _make_transform_dirty();
+ VS::get_singleton()->skeleton_set_base_transform_2d(skeleton,get_global_transform());
}
}
@@ -201,6 +298,7 @@ void Skeleton2D::_bind_methods() {
Skeleton2D::Skeleton2D() {
bone_setup_dirty = true;
transform_dirty = true;
+
skeleton = VS::get_singleton()->skeleton_create();
}
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index 49199f684f..b86cf3be81 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -1,3 +1,33 @@
+/*************************************************************************/
+/* skeleton_2d.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 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 SKELETON_2D_H
#define SKELETON_2D_H
@@ -11,6 +41,10 @@ class Bone2D : public Node2D {
Bone2D *parent_bone;
Skeleton2D *skeleton;
Transform2D rest;
+ float default_length;
+
+friend class Skeleton2D;
+ int skeleton_index;
protected:
void _notification(int p_what);
@@ -24,6 +58,11 @@ public:
String get_configuration_warning() const;
+ void set_default_length(float p_length);
+ float get_default_length() const;
+
+ int get_index_in_skeleton() const;
+
Bone2D();
};
@@ -37,6 +76,8 @@ class Skeleton2D : public Node2D {
return p_bone.bone->is_greater_than(bone);
}
Bone2D *bone;
+ int parent_index;
+ Transform2D accum_transform;
Transform2D rest_inverse;
};
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
index bc39368c88..64d0771fab 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -68,31 +68,31 @@ bool Sprite::_edit_use_rect() const {
void Sprite::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const {
- Size2 s;
- r_filter_clip = false;
+ Rect2 base_rect;
if (region) {
-
- s = region_rect.size;
- r_src_rect = region_rect;
r_filter_clip = region_filter_clip;
+ base_rect = region_rect;
} else {
- s = Size2(texture->get_size());
- s = s / Size2(hframes, vframes);
-
- r_src_rect.size = s;
- r_src_rect.position.x = float(frame % hframes) * s.x;
- r_src_rect.position.y = float(frame / hframes) * s.y;
+ r_filter_clip = false;
+ base_rect = Rect2(0, 0, texture->get_width(), texture->get_height());
}
- Point2 ofs = offset;
+ Size2 frame_size = base_rect.size / Size2(hframes, vframes);
+ Point2 frame_offset = Point2(frame % hframes, frame / hframes);
+ frame_offset *= frame_size;
+
+ r_src_rect.size = frame_size;
+ r_src_rect.position = base_rect.position + frame_offset;
+
+ Point2 dest_offset = offset;
if (centered)
- ofs -= s / 2;
+ dest_offset -= frame_size / 2;
if (Engine::get_singleton()->get_use_pixel_snap()) {
- ofs = ofs.floor();
+ dest_offset = dest_offset.floor();
}
- r_dst_rect = Rect2(ofs, s);
+ r_dst_rect = Rect2(dest_offset, frame_size);
if (hflip)
r_dst_rect.size.x = -r_dst_rect.size.x;
@@ -375,13 +375,13 @@ Rect2 Sprite::get_rect() const {
Size2i s;
if (region) {
-
s = region_rect.size;
} else {
s = texture->get_size();
- s = s / Point2(hframes, vframes);
}
+ s = s / Point2(hframes, vframes);
+
Point2 ofs = offset;
if (centered)
ofs -= s / 2;
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 14eb26b1ec..5294189c65 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -61,6 +61,7 @@ void TileMap::_notification(int p_what) {
}
pending_update = true;
+ _recreate_quadrants();
_update_dirty_quadrants();
RID space = get_world_2d()->get_space();
_update_quadrant_transform();
@@ -304,6 +305,7 @@ void TileMap::_update_dirty_quadrants() {
}
q.occluder_instances.clear();
Ref<ShaderMaterial> prev_material;
+ int prev_z_index;
RID prev_canvas_item;
RID prev_debug_canvas_item;
@@ -324,11 +326,12 @@ void TileMap::_update_dirty_quadrants() {
continue;
Ref<ShaderMaterial> mat = tile_set->tile_get_material(c.id);
+ int z_index = tile_set->tile_get_z_index(c.id);
RID canvas_item;
RID debug_canvas_item;
- if (prev_canvas_item == RID() || prev_material != mat) {
+ if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) {
canvas_item = vs->canvas_item_create();
if (mat.is_valid())
@@ -339,6 +342,7 @@ void TileMap::_update_dirty_quadrants() {
xform.set_origin(q.pos);
vs->canvas_item_set_transform(canvas_item, xform);
vs->canvas_item_set_light_mask(canvas_item, get_light_mask());
+ vs->canvas_item_set_z_index(canvas_item, z_index);
q.canvas_items.push_back(canvas_item);
@@ -354,6 +358,7 @@ void TileMap::_update_dirty_quadrants() {
prev_canvas_item = canvas_item;
prev_material = mat;
+ prev_z_index = z_index;
} else {
canvas_item = prev_canvas_item;
@@ -712,7 +717,7 @@ void TileMap::_make_quadrant_dirty(Map<PosKey, Quadrant>::Element *Q) {
pending_update = true;
if (!is_inside_tree())
return;
- call_deferred("_update_dirty_quadrants");
+ _update_dirty_quadrants();
}
void TileMap::set_cellv(const Vector2 &p_pos, int p_tile, bool p_flip_x, bool p_flip_y, bool p_transpose) {
@@ -841,13 +846,13 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) {
mask |= TileSet::BIND_BOTTOMRIGHT;
}
} else if (tile_set->autotile_get_bitmask_mode(id) == TileSet::BITMASK_3X3) {
- if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) {
+ if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y - 1))) {
mask |= TileSet::BIND_TOPLEFT;
}
if (tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1))) {
mask |= TileSet::BIND_TOP;
}
- if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y - 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) {
+ if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y - 1))) {
mask |= TileSet::BIND_TOPRIGHT;
}
if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) {
@@ -857,13 +862,13 @@ void TileMap::update_cell_bitmask(int p_x, int p_y) {
if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) {
mask |= TileSet::BIND_RIGHT;
}
- if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y))) {
+ if (tile_set->is_tile_bound(id, get_cell(p_x - 1, p_y + 1))) {
mask |= TileSet::BIND_BOTTOMLEFT;
}
if (tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1))) {
mask |= TileSet::BIND_BOTTOM;
}
- if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x, p_y + 1)) && tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y))) {
+ if (tile_set->is_tile_bound(id, get_cell(p_x + 1, p_y + 1))) {
mask |= TileSet::BIND_BOTTOMRIGHT;
}
}
diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp
index 21f471039f..6ea980ec97 100644
--- a/scene/3d/area.cpp
+++ b/scene/3d/area.cpp
@@ -711,7 +711,7 @@ void Area::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001"), "set_gravity_distance_scale", "get_gravity_distance_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_vec"), "set_gravity_vector", "get_gravity_vector");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_gravity", "get_gravity");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_damp", PROPERTY_HINT_RANGE, "0,1024,0.001"), "set_linear_damp", "get_linear_damp");
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index c2a50ec7bb..e7b3645001 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -898,7 +898,7 @@ void AudioStreamPlayer3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,32,0.01"), "set_pitch_scale", "get_pitch_scale");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_RANGE, "0,65536,1"), "set_max_distance", "get_max_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,4096,1,or_greater"), "set_max_distance", "get_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
index 9de189c158..e11e8abe5b 100644
--- a/scene/3d/camera.cpp
+++ b/scene/3d/camera.cpp
@@ -480,8 +480,8 @@ void Camera::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "near"), "set_znear", "get_znear");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "far"), "set_zfar", "get_zfar");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_znear", "get_znear");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar");
BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE);
BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL);
diff --git a/scene/3d/camera.h b/scene/3d/camera.h
index 109bf3adc6..1b506e0c4f 100644
--- a/scene/3d/camera.h
+++ b/scene/3d/camera.h
@@ -132,9 +132,9 @@ public:
virtual Transform get_camera_transform() const;
- Vector3 project_ray_normal(const Point2 &p_pos) const;
+ virtual Vector3 project_ray_normal(const Point2 &p_pos) const;
virtual Vector3 project_ray_origin(const Point2 &p_pos) const;
- Vector3 project_local_ray_normal(const Point2 &p_pos) const;
+ virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const;
virtual Point2 unproject_position(const Vector3 &p_pos) const;
bool is_position_behind(const Vector3 &p_pos) const;
virtual Vector3 project_position(const Point2 &p_point) const;
diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp
index 240bd631a1..7c42638107 100644
--- a/scene/3d/light.cpp
+++ b/scene/3d/light.cpp
@@ -375,7 +375,7 @@ void DirectionalLight::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_normal_bias", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_bias_split_scale", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_BIAS_SPLIT_SCALE);
ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_depth_range", PROPERTY_HINT_ENUM, "Stable,Optimized"), "set_shadow_depth_range", "get_shadow_depth_range");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_max_distance", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_max_distance", PROPERTY_HINT_EXP_RANGE, "0,8192,0.1,or_greater"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE);
BIND_ENUM_CONSTANT(SHADOW_ORTHOGONAL);
BIND_ENUM_CONSTANT(SHADOW_PARALLEL_2_SPLITS);
@@ -428,8 +428,8 @@ void OmniLight::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_shadow_detail"), &OmniLight::get_shadow_detail);
ADD_GROUP("Omni", "omni_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_range", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_RANGE);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_attenuation", PROPERTY_HINT_EXP_EASING), "set_param", "get_param", PARAM_ATTENUATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_range", PROPERTY_HINT_EXP_RANGE, "0,4096,0.1,or_greater"), "set_param", "get_param", PARAM_RANGE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION);
ADD_PROPERTY(PropertyInfo(Variant::INT, "omni_shadow_mode", PROPERTY_HINT_ENUM, "Dual Paraboloid,Cube"), "set_shadow_mode", "get_shadow_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "omni_shadow_detail", PROPERTY_HINT_ENUM, "Vertical,Horizontal"), "set_shadow_detail", "get_shadow_detail");
@@ -450,8 +450,8 @@ OmniLight::OmniLight() :
void SpotLight::_bind_methods() {
ADD_GROUP("Spot", "spot_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_range", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_RANGE);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_attenuation", PROPERTY_HINT_EXP_EASING), "set_param", "get_param", PARAM_ATTENUATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_range", PROPERTY_HINT_EXP_RANGE, "0,4096,0.1,or_greater"), "set_param", "get_param", PARAM_RANGE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_angle", PROPERTY_HINT_RANGE, "0,180,0.1"), "set_param", "get_param", PARAM_SPOT_ANGLE);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_angle_attenuation", PROPERTY_HINT_EXP_EASING), "set_param", "get_param", PARAM_SPOT_ATTENUATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_angle_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_SPOT_ATTENUATION);
}
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
index 693b416f6d..7b5eb8ebc3 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -324,11 +324,11 @@ void Particles::_bind_methods() {
ClassDB::bind_method(D_METHOD("capture_aabb"), &Particles::capture_aabb);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,100000,1"), "set_amount", "get_amount");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount");
ADD_GROUP("Time", "");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01"), "set_lifetime", "get_lifetime");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "speed_scale", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_speed_scale", "get_speed_scale");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio");
@@ -1028,8 +1028,6 @@ void ParticlesMaterial::set_param(Parameter p_param, float p_value) {
case PARAM_ANIM_OFFSET: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset, p_value);
} break;
- case PARAM_MAX: {
- };
}
}
float ParticlesMaterial::get_param(Parameter p_param) const {
@@ -1082,8 +1080,6 @@ void ParticlesMaterial::set_param_randomness(Parameter p_param, float p_value) {
case PARAM_ANIM_OFFSET: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_random, p_value);
} break;
- case PARAM_MAX: {
- };
}
}
float ParticlesMaterial::get_param_randomness(Parameter p_param) const {
@@ -1160,8 +1156,6 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture>
case PARAM_ANIM_OFFSET: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, p_texture);
} break;
- case PARAM_MAX: {
- };
}
_queue_shader_change();
@@ -1233,28 +1227,19 @@ void ParticlesMaterial::set_emission_box_extents(Vector3 p_extents) {
void ParticlesMaterial::set_emission_point_texture(const Ref<Texture> &p_points) {
emission_point_texture = p_points;
- RID texture;
- if (p_points.is_valid())
- texture = p_points->get_rid();
- VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, texture);
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, p_points);
}
void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture> &p_normals) {
emission_normal_texture = p_normals;
- RID texture;
- if (p_normals.is_valid())
- texture = p_normals->get_rid();
- VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, texture);
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, p_normals);
}
void ParticlesMaterial::set_emission_color_texture(const Ref<Texture> &p_colors) {
emission_color_texture = p_colors;
- RID texture;
- if (p_colors.is_valid())
- texture = p_colors->get_rid();
- VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, texture);
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, p_colors);
_queue_shader_change();
}
@@ -1316,10 +1301,7 @@ void ParticlesMaterial::set_trail_size_modifier(const Ref<CurveTexture> &p_trail
curve->ensure_default_setup();
}
- RID texture;
- if (p_trail_size_modifier.is_valid())
- texture = p_trail_size_modifier->get_rid();
- VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_size_modifier, texture);
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_size_modifier, curve);
_queue_shader_change();
}
@@ -1331,10 +1313,7 @@ Ref<CurveTexture> ParticlesMaterial::get_trail_size_modifier() const {
void ParticlesMaterial::set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier) {
trail_color_modifier = p_trail_color_modifier;
- RID texture;
- if (p_trail_color_modifier.is_valid())
- texture = p_trail_color_modifier->get_rid();
- VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_color_modifier, texture);
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_color_modifier, p_trail_color_modifier);
_queue_shader_change();
}
@@ -1481,26 +1460,26 @@ void ParticlesMaterial::_bind_methods() {
ADD_GROUP("Gravity", "");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity");
ADD_GROUP("Initial Velocity", "initial_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY);
ADD_GROUP("Angular Velocity", "angular_");
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity", PROPERTY_HINT_RANGE, "-360,360,0.01"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGULAR_VELOCITY);
ADD_GROUP("Orbit Velocity", "orbit_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01"), "set_param", "get_param", PARAM_ORBIT_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ORBIT_VELOCITY);
ADD_GROUP("Linear Accel", "linear_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01"), "set_param", "get_param", PARAM_LINEAR_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_LINEAR_ACCEL);
ADD_GROUP("Radial Accel", "radial_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01"), "set_param", "get_param", PARAM_RADIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_RADIAL_ACCEL);
ADD_GROUP("Tangential Accel", "tangential_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_TANGENTIAL_ACCEL);
ADD_GROUP("Damping", "");
@@ -1508,11 +1487,11 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_DAMPING);
ADD_GROUP("Angle", "");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1"), "set_param", "get_param", PARAM_ANGLE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANGLE);
ADD_GROUP("Scale", "");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01"), "set_param", "get_param", PARAM_SCALE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "scale_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_SCALE);
ADD_GROUP("Color", "");
@@ -1524,7 +1503,7 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_HUE_VARIATION);
ADD_GROUP("Animation", "anim_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_param", "get_param", PARAM_ANIM_SPEED);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_SPEED);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET);
diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp
index 57d79c960f..154dcb4259 100644
--- a/scene/3d/path.cpp
+++ b/scene/3d/path.cpp
@@ -230,7 +230,7 @@ void PathFollow::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_loop", "loop"), &PathFollow::set_loop);
ClassDB::bind_method(D_METHOD("has_loop"), &PathFollow::has_loop);
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_RANGE, "0,10000,0.01"), "set_offset", "get_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "offset", PROPERTY_HINT_EXP_RANGE, "0,10000,0.01,or_greater"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE, "0,1,0.0001", PROPERTY_USAGE_EDITOR), "set_unit_offset", "get_unit_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "h_offset"), "set_h_offset", "get_h_offset");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_offset"), "set_v_offset", "get_v_offset");
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 9aac391d80..5056fb2fe4 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -34,6 +34,10 @@
#include "method_bind_ext.gen.inc"
#include "scene/scene_string_names.h"
+#ifdef TOOLS_ENABLED
+#include "editor/plugins/spatial_editor_plugin.h"
+#endif
+
void PhysicsBody::_notification(int p_what) {
/*
@@ -1266,3 +1270,1112 @@ KinematicCollision::KinematicCollision() {
collision.local_shape = 0;
owner = NULL;
}
+
+///////////////////////////////////////
+
+bool PhysicalBone::JointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+ return false;
+}
+
+bool PhysicalBone::JointData::_get(const StringName &p_name, Variant &r_ret) const {
+ return false;
+}
+
+void PhysicalBone::JointData::_get_property_list(List<PropertyInfo> *p_list) const {
+}
+
+bool PhysicalBone::PinJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+ if (JointData::_set(p_name, p_value, j)) {
+ return true;
+ }
+
+ if ("joint_constraints/bias" == p_name) {
+ bias = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->pin_joint_set_param(j, PhysicsServer::PIN_JOINT_BIAS, bias);
+
+ } else if ("joint_constraints/damping" == p_name) {
+ damping = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->pin_joint_set_param(j, PhysicsServer::PIN_JOINT_DAMPING, damping);
+
+ } else if ("joint_constraints/impulse_clamp" == p_name) {
+ impulse_clamp = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->pin_joint_set_param(j, PhysicsServer::PIN_JOINT_IMPULSE_CLAMP, impulse_clamp);
+
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool PhysicalBone::PinJointData::_get(const StringName &p_name, Variant &r_ret) const {
+ if (JointData::_get(p_name, r_ret)) {
+ return true;
+ }
+
+ if ("joint_constraints/bias" == p_name) {
+ r_ret = bias;
+ } else if ("joint_constraints/damping" == p_name) {
+ r_ret = damping;
+ } else if ("joint_constraints/impulse_clamp" == p_name) {
+ r_ret = impulse_clamp;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void PhysicalBone::PinJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+ JointData::_get_property_list(p_list);
+
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/damping", PROPERTY_HINT_RANGE, "0.01,8.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/impulse_clamp", PROPERTY_HINT_RANGE, "0.0,64.0,0.01"));
+}
+
+bool PhysicalBone::ConeJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+ if (JointData::_set(p_name, p_value, j)) {
+ return true;
+ }
+
+ if ("joint_constraints/swing_span" == p_name) {
+ swing_span = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN, swing_span);
+
+ } else if ("joint_constraints/twist_span" == p_name) {
+ twist_span = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_TWIST_SPAN, twist_span);
+
+ } else if ("joint_constraints/bias" == p_name) {
+ bias = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_BIAS, bias);
+
+ } else if ("joint_constraints/softness" == p_name) {
+ softness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_SOFTNESS, softness);
+
+ } else if ("joint_constraints/relaxation" == p_name) {
+ relaxation = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_RELAXATION, relaxation);
+
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool PhysicalBone::ConeJointData::_get(const StringName &p_name, Variant &r_ret) const {
+ if (JointData::_get(p_name, r_ret)) {
+ return true;
+ }
+
+ if ("joint_constraints/swing_span" == p_name) {
+ r_ret = Math::rad2deg(swing_span);
+ } else if ("joint_constraints/twist_span" == p_name) {
+ r_ret = Math::rad2deg(twist_span);
+ } else if ("joint_constraints/bias" == p_name) {
+ r_ret = bias;
+ } else if ("joint_constraints/softness" == p_name) {
+ r_ret = softness;
+ } else if ("joint_constraints/relaxation" == p_name) {
+ r_ret = relaxation;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void PhysicalBone::ConeJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+ JointData::_get_property_list(p_list);
+
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/swing_span", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/twist_span", PROPERTY_HINT_RANGE, "-40000,40000,0.1,or_lesser,or_greater"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/relaxation", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+}
+
+bool PhysicalBone::HingeJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+ if (JointData::_set(p_name, p_value, j)) {
+ return true;
+ }
+
+ if ("joint_constraints/angular_limit_enabled" == p_name) {
+ angular_limit_enabled = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_flag(j, PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT, angular_limit_enabled);
+
+ } else if ("joint_constraints/angular_limit_upper" == p_name) {
+ angular_limit_upper = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_UPPER, angular_limit_upper);
+
+ } else if ("joint_constraints/angular_limit_lower" == p_name) {
+ angular_limit_lower = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_LOWER, angular_limit_lower);
+
+ } else if ("joint_constraints/angular_limit_bias" == p_name) {
+ angular_limit_bias = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_BIAS, angular_limit_bias);
+
+ } else if ("joint_constraints/angular_limit_softness" == p_name) {
+ angular_limit_softness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS, angular_limit_softness);
+
+ } else if ("joint_constraints/angular_limit_relaxation" == p_name) {
+ angular_limit_relaxation = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION, angular_limit_relaxation);
+
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool PhysicalBone::HingeJointData::_get(const StringName &p_name, Variant &r_ret) const {
+ if (JointData::_get(p_name, r_ret)) {
+ return true;
+ }
+
+ if ("joint_constraints/angular_limit_enabled" == p_name) {
+ r_ret = angular_limit_enabled;
+ } else if ("joint_constraints/angular_limit_upper" == p_name) {
+ r_ret = Math::rad2deg(angular_limit_upper);
+ } else if ("joint_constraints/angular_limit_lower" == p_name) {
+ r_ret = Math::rad2deg(angular_limit_lower);
+ } else if ("joint_constraints/angular_limit_bias" == p_name) {
+ r_ret = angular_limit_bias;
+ } else if ("joint_constraints/angular_limit_softness" == p_name) {
+ r_ret = angular_limit_softness;
+ } else if ("joint_constraints/angular_limit_relaxation" == p_name) {
+ r_ret = angular_limit_relaxation;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void PhysicalBone::HingeJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+ JointData::_get_property_list(p_list);
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/angular_limit_enabled"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_relaxation", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+}
+
+bool PhysicalBone::SliderJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+ if (JointData::_set(p_name, p_value, j)) {
+ return true;
+ }
+
+ if ("joint_constraints/linear_limit_upper" == p_name) {
+ linear_limit_upper = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER, linear_limit_upper);
+
+ } else if ("joint_constraints/linear_limit_lower" == p_name) {
+ linear_limit_lower = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER, linear_limit_lower);
+
+ } else if ("joint_constraints/linear_limit_softness" == p_name) {
+ linear_limit_softness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS, linear_limit_softness);
+
+ } else if ("joint_constraints/linear_limit_restitution" == p_name) {
+ linear_limit_restitution = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION, linear_limit_restitution);
+
+ } else if ("joint_constraints/linear_limit_damping" == p_name) {
+ linear_limit_damping = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING, linear_limit_restitution);
+
+ } else if ("joint_constraints/angular_limit_upper" == p_name) {
+ angular_limit_upper = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_UPPER, angular_limit_upper);
+
+ } else if ("joint_constraints/angular_limit_lower" == p_name) {
+ angular_limit_lower = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER, angular_limit_lower);
+
+ } else if ("joint_constraints/angular_limit_softness" == p_name) {
+ angular_limit_softness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, angular_limit_softness);
+
+ } else if ("joint_constraints/angular_limit_restitution" == p_name) {
+ angular_limit_restitution = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, angular_limit_softness);
+
+ } else if ("joint_constraints/angular_limit_damping" == p_name) {
+ angular_limit_damping = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING, angular_limit_damping);
+
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool PhysicalBone::SliderJointData::_get(const StringName &p_name, Variant &r_ret) const {
+ if (JointData::_get(p_name, r_ret)) {
+ return true;
+ }
+
+ if ("joint_constraints/linear_limit_upper" == p_name) {
+ r_ret = linear_limit_upper;
+ } else if ("joint_constraints/linear_limit_lower" == p_name) {
+ r_ret = linear_limit_lower;
+ } else if ("joint_constraints/linear_limit_softness" == p_name) {
+ r_ret = linear_limit_softness;
+ } else if ("joint_constraints/linear_limit_restitution" == p_name) {
+ r_ret = linear_limit_restitution;
+ } else if ("joint_constraints/linear_limit_damping" == p_name) {
+ r_ret = linear_limit_damping;
+ } else if ("joint_constraints/angular_limit_upper" == p_name) {
+ r_ret = Math::rad2deg(angular_limit_upper);
+ } else if ("joint_constraints/angular_limit_lower" == p_name) {
+ r_ret = Math::rad2deg(angular_limit_lower);
+ } else if ("joint_constraints/angular_limit_softness" == p_name) {
+ r_ret = angular_limit_softness;
+ } else if ("joint_constraints/angular_limit_restitution" == p_name) {
+ r_ret = angular_limit_restitution;
+ } else if ("joint_constraints/angular_limit_damping" == p_name) {
+ r_ret = angular_limit_damping;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void PhysicalBone::SliderJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+ JointData::_get_property_list(p_list);
+
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_upper"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_lower"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_damping", PROPERTY_HINT_RANGE, "0,16.0,0.01"));
+
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_damping", PROPERTY_HINT_RANGE, "0,16.0,0.01"));
+}
+
+bool PhysicalBone::SixDOFJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+ if (JointData::_set(p_name, p_value, j)) {
+ return true;
+ }
+
+ String path = p_name;
+
+ Vector3::Axis axis;
+ {
+ const String axis_s = path.get_slicec('/', 1);
+ if ("x" == axis_s) {
+ axis = Vector3::AXIS_X;
+ } else if ("y" == axis_s) {
+ axis = Vector3::AXIS_Y;
+ } else if ("z" == axis_s) {
+ axis = Vector3::AXIS_Z;
+ } else {
+ return false;
+ }
+ }
+
+ String var_name = path.get_slicec('/', 2);
+
+ if ("linear_limit_enabled" == var_name) {
+ axis_data[axis].linear_limit_enabled = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j, axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, axis_data[axis].linear_limit_enabled);
+
+ } else if ("linear_limit_upper" == var_name) {
+ axis_data[axis].linear_limit_upper = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT, axis_data[axis].linear_limit_upper);
+
+ } else if ("linear_limit_lower" == var_name) {
+ axis_data[axis].linear_limit_lower = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT, axis_data[axis].linear_limit_lower);
+
+ } else if ("linear_limit_softness" == var_name) {
+ axis_data[axis].linear_limit_softness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, axis_data[axis].linear_limit_softness);
+
+ } else if ("linear_restitution" == var_name) {
+ axis_data[axis].linear_restitution = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION, axis_data[axis].linear_restitution);
+
+ } else if ("linear_damping" == var_name) {
+ axis_data[axis].linear_damping = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING, axis_data[axis].linear_damping);
+
+ } else if ("angular_limit_enabled" == var_name) {
+ axis_data[axis].angular_limit_enabled = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j, axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, axis_data[axis].angular_limit_enabled);
+
+ } else if ("angular_limit_upper" == var_name) {
+ axis_data[axis].angular_limit_upper = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT, axis_data[axis].angular_limit_upper);
+
+ } else if ("angular_limit_lower" == var_name) {
+ axis_data[axis].angular_limit_lower = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT, axis_data[axis].angular_limit_lower);
+
+ } else if ("angular_limit_softness" == var_name) {
+ axis_data[axis].angular_limit_softness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS, axis_data[axis].angular_limit_softness);
+
+ } else if ("angular_restitution" == var_name) {
+ axis_data[axis].angular_restitution = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION, axis_data[axis].angular_restitution);
+
+ } else if ("angular_damping" == var_name) {
+ axis_data[axis].angular_damping = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING, axis_data[axis].angular_damping);
+
+ } else if ("erp" == var_name) {
+ axis_data[axis].erp = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_ERP, axis_data[axis].erp);
+
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool PhysicalBone::SixDOFJointData::_get(const StringName &p_name, Variant &r_ret) const {
+ if (JointData::_get(p_name, r_ret)) {
+ return true;
+ }
+
+ String path = p_name;
+
+ int axis;
+ {
+ const String axis_s = path.get_slicec('/', 1);
+ if ("x" == axis_s) {
+ axis = 0;
+ } else if ("y" == axis_s) {
+ axis = 1;
+ } else if ("z" == axis_s) {
+ axis = 2;
+ } else {
+ return false;
+ }
+ }
+
+ String var_name = path.get_slicec('/', 2);
+
+ if ("linear_limit_enabled" == var_name) {
+ r_ret = axis_data[axis].linear_limit_enabled;
+ } else if ("linear_limit_upper" == var_name) {
+ r_ret = axis_data[axis].linear_limit_upper;
+ } else if ("linear_limit_lower" == var_name) {
+ r_ret = axis_data[axis].linear_limit_lower;
+ } else if ("linear_limit_softness" == var_name) {
+ r_ret = axis_data[axis].linear_limit_softness;
+ } else if ("linear_restitution" == var_name) {
+ r_ret = axis_data[axis].linear_restitution;
+ } else if ("linear_damping" == var_name) {
+ r_ret = axis_data[axis].linear_damping;
+ } else if ("angular_limit_enabled" == var_name) {
+ r_ret = axis_data[axis].angular_limit_enabled;
+ } else if ("angular_limit_upper" == var_name) {
+ r_ret = Math::rad2deg(axis_data[axis].angular_limit_upper);
+ } else if ("angular_limit_lower" == var_name) {
+ r_ret = Math::rad2deg(axis_data[axis].angular_limit_lower);
+ } else if ("angular_limit_softness" == var_name) {
+ r_ret = axis_data[axis].angular_limit_softness;
+ } else if ("angular_restitution" == var_name) {
+ r_ret = axis_data[axis].angular_restitution;
+ } else if ("angular_damping" == var_name) {
+ r_ret = axis_data[axis].angular_damping;
+ } else if ("erp" == var_name) {
+ r_ret = axis_data[axis].erp;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void PhysicalBone::SixDOFJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+ const StringName axis_names[] = { "x", "y", "z" };
+ for (int i = 0; i < 3; ++i) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/linear_limit_enabled"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_upper"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_lower"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/angular_limit_enabled"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/erp"));
+ }
+}
+
+bool PhysicalBone::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "bone_name") {
+ set_bone_name(p_value);
+ return true;
+ }
+
+ if (joint_data) {
+ if (joint_data->_set(p_name, p_value)) {
+#ifdef TOOLS_ENABLED
+ if (get_gizmo().is_valid())
+ get_gizmo()->redraw();
+#endif
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool PhysicalBone::_get(const StringName &p_name, Variant &r_ret) const {
+ if (p_name == "bone_name") {
+ r_ret = get_bone_name();
+ return true;
+ }
+
+ if (joint_data) {
+ return joint_data->_get(p_name, r_ret);
+ }
+
+ return false;
+}
+
+void PhysicalBone::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ Skeleton *parent = find_skeleton_parent(get_parent());
+
+ if (parent) {
+
+ String names;
+ for (int i = 0; i < parent->get_bone_count(); i++) {
+ if (i > 0)
+ names += ",";
+ names += parent->get_bone_name(i);
+ }
+
+ p_list->push_back(PropertyInfo(Variant::STRING, "bone_name", PROPERTY_HINT_ENUM, names));
+ } else {
+
+ p_list->push_back(PropertyInfo(Variant::STRING, "bone_name"));
+ }
+
+ if (joint_data) {
+ joint_data->_get_property_list(p_list);
+ }
+}
+
+void PhysicalBone::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ parent_skeleton = find_skeleton_parent(get_parent());
+ update_bone_id();
+ reset_to_rest_position();
+ _reset_physics_simulation_state();
+ break;
+ case NOTIFICATION_EXIT_TREE:
+ if (parent_skeleton) {
+ if (-1 != bone_id) {
+ parent_skeleton->unbind_physical_bone_from_bone(bone_id);
+ }
+ }
+ parent_skeleton = NULL;
+ update_bone_id();
+ break;
+ case NOTIFICATION_TRANSFORM_CHANGED:
+ if (Engine::get_singleton()->is_editor_hint()) {
+
+ update_offset();
+ }
+ break;
+ }
+}
+
+void PhysicalBone::_direct_state_changed(Object *p_state) {
+
+ if (!simulate_physics) {
+ return;
+ }
+
+ /// Update bone transform
+
+ PhysicsDirectBodyState *state;
+
+#ifdef DEBUG_ENABLED
+ state = Object::cast_to<PhysicsDirectBodyState>(p_state);
+#else
+ state = (PhysicsDirectBodyState *)p_state; //trust it
+#endif
+
+ Transform global_transform(state->get_transform());
+
+ set_ignore_transform_notification(true);
+ set_global_transform(global_transform);
+ set_ignore_transform_notification(false);
+
+ // Update skeleton
+ if (parent_skeleton) {
+ if (-1 != bone_id) {
+ parent_skeleton->set_bone_global_pose(bone_id, parent_skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse));
+ }
+ }
+}
+
+void PhysicalBone::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_direct_state_changed"), &PhysicalBone::_direct_state_changed);
+
+ ClassDB::bind_method(D_METHOD("set_joint_type", "joint_type"), &PhysicalBone::set_joint_type);
+ ClassDB::bind_method(D_METHOD("get_joint_type"), &PhysicalBone::get_joint_type);
+
+ ClassDB::bind_method(D_METHOD("set_joint_offset", "offset"), &PhysicalBone::set_joint_offset);
+ ClassDB::bind_method(D_METHOD("get_joint_offset"), &PhysicalBone::get_joint_offset);
+
+ ClassDB::bind_method(D_METHOD("set_body_offset", "offset"), &PhysicalBone::set_body_offset);
+ ClassDB::bind_method(D_METHOD("get_body_offset"), &PhysicalBone::get_body_offset);
+
+ ClassDB::bind_method(D_METHOD("is_static_body"), &PhysicalBone::is_static_body);
+
+ ClassDB::bind_method(D_METHOD("get_simulate_physics"), &PhysicalBone::get_simulate_physics);
+
+ ClassDB::bind_method(D_METHOD("is_simulating_physics"), &PhysicalBone::is_simulating_physics);
+
+ ClassDB::bind_method(D_METHOD("set_mass", "mass"), &PhysicalBone::set_mass);
+ ClassDB::bind_method(D_METHOD("get_mass"), &PhysicalBone::get_mass);
+
+ ClassDB::bind_method(D_METHOD("set_weight", "weight"), &PhysicalBone::set_weight);
+ ClassDB::bind_method(D_METHOD("get_weight"), &PhysicalBone::get_weight);
+
+ ClassDB::bind_method(D_METHOD("set_friction", "friction"), &PhysicalBone::set_friction);
+ ClassDB::bind_method(D_METHOD("get_friction"), &PhysicalBone::get_friction);
+
+ ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &PhysicalBone::set_bounce);
+ ClassDB::bind_method(D_METHOD("get_bounce"), &PhysicalBone::get_bounce);
+
+ ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &PhysicalBone::set_gravity_scale);
+ ClassDB::bind_method(D_METHOD("get_gravity_scale"), &PhysicalBone::get_gravity_scale);
+
+ ADD_GROUP("Joint", "joint_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "joint_type", PROPERTY_HINT_ENUM, "None,PinJoint,ConeJoint,HingeJoint,SliderJoint,6DOFJoint"), "set_joint_type", "get_joint_type");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "joint_offset"), "set_joint_offset", "get_joint_offset");
+
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "body_offset"), "set_body_offset", "get_body_offset");
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_weight", "get_weight");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale");
+
+ BIND_ENUM_CONSTANT(JOINT_TYPE_NONE);
+ BIND_ENUM_CONSTANT(JOINT_TYPE_PIN);
+ BIND_ENUM_CONSTANT(JOINT_TYPE_CONE);
+ BIND_ENUM_CONSTANT(JOINT_TYPE_HINGE);
+ BIND_ENUM_CONSTANT(JOINT_TYPE_SLIDER);
+ BIND_ENUM_CONSTANT(JOINT_TYPE_6DOF);
+}
+
+Skeleton *PhysicalBone::find_skeleton_parent(Node *p_parent) {
+ if (!p_parent) {
+ return NULL;
+ }
+ Skeleton *s = Object::cast_to<Skeleton>(p_parent);
+ return s ? s : find_skeleton_parent(p_parent->get_parent());
+}
+
+void PhysicalBone::_fix_joint_offset() {
+ // Clamp joint origin to bone origin
+ if (parent_skeleton) {
+ joint_offset.origin = body_offset.affine_inverse().origin;
+ }
+}
+
+void PhysicalBone::_reload_joint() {
+
+ if (joint.is_valid()) {
+ PhysicsServer::get_singleton()->free(joint);
+ joint = RID();
+ }
+
+ if (!parent_skeleton) {
+ return;
+ }
+
+ PhysicalBone *body_a = parent_skeleton->get_physical_bone_parent(bone_id);
+ if (!body_a) {
+ return;
+ }
+
+ Transform joint_transf = get_global_transform() * joint_offset;
+ Transform local_a = body_a->get_global_transform().affine_inverse() * joint_transf;
+ local_a.orthonormalize();
+
+ switch (get_joint_type()) {
+ case JOINT_TYPE_PIN: {
+
+ joint = PhysicsServer::get_singleton()->joint_create_pin(body_a->get_rid(), local_a.origin, get_rid(), joint_offset.origin);
+ const PinJointData *pjd(static_cast<const PinJointData *>(joint_data));
+ PhysicsServer::get_singleton()->pin_joint_set_param(joint, PhysicsServer::PIN_JOINT_BIAS, pjd->bias);
+ PhysicsServer::get_singleton()->pin_joint_set_param(joint, PhysicsServer::PIN_JOINT_DAMPING, pjd->damping);
+ PhysicsServer::get_singleton()->pin_joint_set_param(joint, PhysicsServer::PIN_JOINT_IMPULSE_CLAMP, pjd->impulse_clamp);
+
+ } break;
+ case JOINT_TYPE_CONE: {
+
+ joint = PhysicsServer::get_singleton()->joint_create_cone_twist(body_a->get_rid(), local_a, get_rid(), joint_offset);
+ const ConeJointData *cjd(static_cast<const ConeJointData *>(joint_data));
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN, cjd->swing_span);
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_TWIST_SPAN, cjd->twist_span);
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_BIAS, cjd->bias);
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_SOFTNESS, cjd->softness);
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_RELAXATION, cjd->relaxation);
+
+ } break;
+ case JOINT_TYPE_HINGE: {
+
+ joint = PhysicsServer::get_singleton()->joint_create_hinge(body_a->get_rid(), local_a, get_rid(), joint_offset);
+ const HingeJointData *hjd(static_cast<const HingeJointData *>(joint_data));
+ PhysicsServer::get_singleton()->hinge_joint_set_flag(joint, PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT, hjd->angular_limit_enabled);
+ PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_UPPER, hjd->angular_limit_upper);
+ PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_LOWER, hjd->angular_limit_lower);
+ PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_BIAS, hjd->angular_limit_bias);
+ PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS, hjd->angular_limit_softness);
+ PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION, hjd->angular_limit_relaxation);
+
+ } break;
+ case JOINT_TYPE_SLIDER: {
+
+ joint = PhysicsServer::get_singleton()->joint_create_slider(body_a->get_rid(), local_a, get_rid(), joint_offset);
+ const SliderJointData *sjd(static_cast<const SliderJointData *>(joint_data));
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER, sjd->linear_limit_upper);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER, sjd->linear_limit_lower);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS, sjd->linear_limit_softness);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION, sjd->linear_limit_restitution);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING, sjd->linear_limit_restitution);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_UPPER, sjd->angular_limit_upper);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER, sjd->angular_limit_lower);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, sjd->angular_limit_softness);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, sjd->angular_limit_softness);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING, sjd->angular_limit_damping);
+
+ } break;
+ case JOINT_TYPE_6DOF: {
+
+ joint = PhysicsServer::get_singleton()->joint_create_generic_6dof(body_a->get_rid(), local_a, get_rid(), joint_offset);
+ const SixDOFJointData *g6dofjd(static_cast<const SixDOFJointData *>(joint_data));
+ for (int axis = 0; axis < 3; ++axis) {
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, g6dofjd->axis_data[axis].linear_limit_enabled);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT, g6dofjd->axis_data[axis].linear_limit_upper);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT, g6dofjd->axis_data[axis].linear_limit_lower);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, g6dofjd->axis_data[axis].linear_limit_softness);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION, g6dofjd->axis_data[axis].linear_restitution);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING, g6dofjd->axis_data[axis].linear_damping);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, g6dofjd->axis_data[axis].angular_limit_enabled);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT, g6dofjd->axis_data[axis].angular_limit_upper);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT, g6dofjd->axis_data[axis].angular_limit_lower);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS, g6dofjd->axis_data[axis].angular_limit_softness);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION, g6dofjd->axis_data[axis].angular_restitution);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING, g6dofjd->axis_data[axis].angular_damping);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_ERP, g6dofjd->axis_data[axis].erp);
+ }
+
+ } break;
+ }
+}
+
+void PhysicalBone::_on_bone_parent_changed() {
+ _reload_joint();
+}
+
+void PhysicalBone::_set_gizmo_move_joint(bool p_move_joint) {
+#ifdef TOOLS_ENABLED
+ gizmo_move_joint = p_move_joint;
+ SpatialEditor::get_singleton()->update_transform_gizmo();
+#endif
+}
+
+#ifdef TOOLS_ENABLED
+Transform PhysicalBone::get_global_gizmo_transform() const {
+ return gizmo_move_joint ? get_global_transform() * joint_offset : get_global_transform();
+}
+
+Transform PhysicalBone::get_local_gizmo_transform() const {
+ return gizmo_move_joint ? get_transform() * joint_offset : get_transform();
+}
+#endif
+
+const PhysicalBone::JointData *PhysicalBone::get_joint_data() const {
+ return joint_data;
+}
+
+Skeleton *PhysicalBone::find_skeleton_parent() {
+ return find_skeleton_parent(this);
+}
+
+void PhysicalBone::set_joint_type(JointType p_joint_type) {
+
+ if (p_joint_type == get_joint_type())
+ return;
+
+ memdelete(joint_data);
+ joint_data = NULL;
+ switch (p_joint_type) {
+ case JOINT_TYPE_PIN:
+ joint_data = memnew(PinJointData);
+ break;
+ case JOINT_TYPE_CONE:
+ joint_data = memnew(ConeJointData);
+ break;
+ case JOINT_TYPE_HINGE:
+ joint_data = memnew(HingeJointData);
+ break;
+ case JOINT_TYPE_SLIDER:
+ joint_data = memnew(SliderJointData);
+ break;
+ case JOINT_TYPE_6DOF:
+ joint_data = memnew(SixDOFJointData);
+ break;
+ }
+
+ _reload_joint();
+
+#ifdef TOOLS_ENABLED
+ _change_notify();
+ if (get_gizmo().is_valid())
+ get_gizmo()->redraw();
+#endif
+}
+
+PhysicalBone::JointType PhysicalBone::get_joint_type() const {
+ return joint_data ? joint_data->get_joint_type() : JOINT_TYPE_NONE;
+}
+
+void PhysicalBone::set_joint_offset(const Transform &p_offset) {
+ joint_offset = p_offset;
+
+ _fix_joint_offset();
+
+ set_ignore_transform_notification(true);
+ reset_to_rest_position();
+ set_ignore_transform_notification(false);
+
+#ifdef TOOLS_ENABLED
+ if (get_gizmo().is_valid())
+ get_gizmo()->redraw();
+#endif
+}
+
+const Transform &PhysicalBone::get_body_offset() const {
+ return body_offset;
+}
+
+void PhysicalBone::set_body_offset(const Transform &p_offset) {
+ body_offset = p_offset;
+ body_offset_inverse = body_offset.affine_inverse();
+
+ _fix_joint_offset();
+
+ set_ignore_transform_notification(true);
+ reset_to_rest_position();
+ set_ignore_transform_notification(false);
+
+#ifdef TOOLS_ENABLED
+ if (get_gizmo().is_valid())
+ get_gizmo()->redraw();
+#endif
+}
+
+const Transform &PhysicalBone::get_joint_offset() const {
+ return joint_offset;
+}
+
+void PhysicalBone::set_static_body(bool p_static) {
+
+ static_body = p_static;
+
+ set_as_toplevel(!static_body);
+
+ _reset_physics_simulation_state();
+}
+
+bool PhysicalBone::is_static_body() {
+ return static_body;
+}
+
+void PhysicalBone::set_simulate_physics(bool p_simulate) {
+ if (simulate_physics == p_simulate) {
+ return;
+ }
+
+ simulate_physics = p_simulate;
+ _reset_physics_simulation_state();
+}
+
+bool PhysicalBone::get_simulate_physics() {
+ return simulate_physics;
+}
+
+bool PhysicalBone::is_simulating_physics() {
+ return _internal_simulate_physics && !_internal_static_body;
+}
+
+void PhysicalBone::set_bone_name(const String &p_name) {
+
+ bone_name = p_name;
+ bone_id = -1;
+
+ update_bone_id();
+ reset_to_rest_position();
+}
+
+const String &PhysicalBone::get_bone_name() const {
+
+ return bone_name;
+}
+
+void PhysicalBone::set_mass(real_t p_mass) {
+
+ ERR_FAIL_COND(p_mass <= 0);
+ mass = p_mass;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_MASS, mass);
+}
+
+real_t PhysicalBone::get_mass() const {
+
+ return mass;
+}
+
+void PhysicalBone::set_weight(real_t p_weight) {
+
+ set_mass(p_weight / real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8)));
+}
+
+real_t PhysicalBone::get_weight() const {
+
+ return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8));
+}
+
+void PhysicalBone::set_friction(real_t p_friction) {
+
+ ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
+
+ friction = p_friction;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction);
+}
+
+real_t PhysicalBone::get_friction() const {
+
+ return friction;
+}
+
+void PhysicalBone::set_bounce(real_t p_bounce) {
+
+ ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
+
+ bounce = p_bounce;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce);
+}
+real_t PhysicalBone::get_bounce() const {
+
+ return bounce;
+}
+
+void PhysicalBone::set_gravity_scale(real_t p_gravity_scale) {
+
+ gravity_scale = p_gravity_scale;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_GRAVITY_SCALE, gravity_scale);
+}
+
+real_t PhysicalBone::get_gravity_scale() const {
+
+ return gravity_scale;
+}
+
+PhysicalBone::PhysicalBone() :
+ PhysicsBody(PhysicsServer::BODY_MODE_STATIC),
+#ifdef TOOLS_ENABLED
+ gizmo_move_joint(false),
+#endif
+ joint_data(NULL),
+ static_body(false),
+ simulate_physics(false),
+ _internal_static_body(false),
+ _internal_simulate_physics(false),
+ bone_id(-1),
+ parent_skeleton(NULL),
+ bone_name(""),
+ bounce(0),
+ mass(1),
+ friction(1),
+ gravity_scale(1) {
+
+ set_static_body(static_body);
+ _reset_physics_simulation_state();
+}
+
+PhysicalBone::~PhysicalBone() {
+ memdelete(joint_data);
+}
+
+void PhysicalBone::update_bone_id() {
+ if (!parent_skeleton) {
+ return;
+ }
+
+ const int new_bone_id = parent_skeleton->find_bone(bone_name);
+
+ if (new_bone_id != bone_id) {
+ if (-1 != bone_id) {
+ // Assert the unbind from old node
+ parent_skeleton->unbind_physical_bone_from_bone(bone_id);
+ parent_skeleton->unbind_child_node_from_bone(bone_id, this);
+ }
+
+ bone_id = new_bone_id;
+
+ parent_skeleton->bind_physical_bone_to_bone(bone_id, this);
+
+ _fix_joint_offset();
+ _internal_static_body = !static_body; // Force staticness reset
+ _reset_staticness_state();
+ }
+}
+
+void PhysicalBone::update_offset() {
+#ifdef TOOLS_ENABLED
+ if (parent_skeleton) {
+
+ Transform bone_transform(parent_skeleton->get_global_transform());
+ if (-1 != bone_id)
+ bone_transform *= parent_skeleton->get_bone_global_pose(bone_id);
+
+ if (gizmo_move_joint) {
+ bone_transform *= body_offset;
+ set_joint_offset(bone_transform.affine_inverse() * get_global_transform());
+ } else {
+ set_body_offset(bone_transform.affine_inverse() * get_global_transform());
+ }
+ }
+#endif
+}
+
+void PhysicalBone::reset_to_rest_position() {
+ if (parent_skeleton) {
+ if (-1 == bone_id) {
+ set_global_transform(parent_skeleton->get_global_transform() * body_offset);
+ } else {
+ set_global_transform(parent_skeleton->get_global_transform() * parent_skeleton->get_bone_global_pose(bone_id) * body_offset);
+ }
+ }
+}
+
+void PhysicalBone::_reset_physics_simulation_state() {
+ if (simulate_physics && !static_body) {
+ _start_physics_simulation();
+ } else {
+ _stop_physics_simulation();
+ }
+
+ _reset_staticness_state();
+}
+
+void PhysicalBone::_reset_staticness_state() {
+
+ if (parent_skeleton && -1 != bone_id) {
+ if (static_body && simulate_physics) { // With this check I'm sure the position of this body is updated only when it's necessary
+
+ if (_internal_static_body) {
+ return;
+ }
+
+ parent_skeleton->bind_child_node_to_bone(bone_id, this);
+ _internal_static_body = true;
+ } else {
+
+ if (!_internal_static_body) {
+ return;
+ }
+
+ parent_skeleton->unbind_child_node_from_bone(bone_id, this);
+ _internal_static_body = false;
+ }
+ }
+}
+
+void PhysicalBone::_start_physics_simulation() {
+ if (_internal_simulate_physics || !parent_skeleton) {
+ return;
+ }
+ reset_to_rest_position();
+ PhysicsServer::get_singleton()->body_set_mode(get_rid(), PhysicsServer::BODY_MODE_RIGID);
+ PhysicsServer::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer());
+ PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask());
+ PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed");
+ parent_skeleton->set_bone_ignore_animation(bone_id, true);
+ _internal_simulate_physics = true;
+}
+
+void PhysicalBone::_stop_physics_simulation() {
+ if (!_internal_simulate_physics || !parent_skeleton) {
+ return;
+ }
+ PhysicsServer::get_singleton()->body_set_mode(get_rid(), PhysicsServer::BODY_MODE_STATIC);
+ PhysicsServer::get_singleton()->body_set_collision_layer(get_rid(), 0);
+ PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(), 0);
+ PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), NULL, "");
+ parent_skeleton->set_bone_ignore_animation(bone_id, false);
+ _internal_simulate_physics = false;
+}
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
index ffdc9ab309..17d2769c79 100644
--- a/scene/3d/physics_body.h
+++ b/scene/3d/physics_body.h
@@ -33,6 +33,7 @@
#include "scene/3d/collision_object.h"
#include "servers/physics_server.h"
+#include "skeleton.h"
#include "vset.h"
class PhysicsBody : public CollisionObject {
@@ -342,4 +343,267 @@ public:
KinematicCollision();
};
+class PhysicalBone : public PhysicsBody {
+
+ GDCLASS(PhysicalBone, PhysicsBody);
+
+public:
+ enum JointType {
+ JOINT_TYPE_NONE,
+ JOINT_TYPE_PIN,
+ JOINT_TYPE_CONE,
+ JOINT_TYPE_HINGE,
+ JOINT_TYPE_SLIDER,
+ JOINT_TYPE_6DOF
+ };
+
+ struct JointData {
+ virtual JointType get_joint_type() { return JOINT_TYPE_NONE; }
+
+ /// "j" is used to set the parameter inside the PhysicsServer
+ virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+ };
+
+ struct PinJointData : public JointData {
+ virtual JointType get_joint_type() { return JOINT_TYPE_PIN; }
+
+ virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ real_t bias;
+ real_t damping;
+ real_t impulse_clamp;
+
+ PinJointData() :
+ bias(0.3),
+ damping(1.),
+ impulse_clamp(0) {}
+ };
+
+ struct ConeJointData : public JointData {
+ virtual JointType get_joint_type() { return JOINT_TYPE_CONE; }
+
+ virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ real_t swing_span;
+ real_t twist_span;
+ real_t bias;
+ real_t softness;
+ real_t relaxation;
+
+ ConeJointData() :
+ swing_span(Math_PI * 0.25),
+ twist_span(Math_PI),
+ bias(0.3),
+ softness(0.8),
+ relaxation(1.) {}
+ };
+
+ struct HingeJointData : public JointData {
+ virtual JointType get_joint_type() { return JOINT_TYPE_HINGE; }
+
+ virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ bool angular_limit_enabled;
+ real_t angular_limit_upper;
+ real_t angular_limit_lower;
+ real_t angular_limit_bias;
+ real_t angular_limit_softness;
+ real_t angular_limit_relaxation;
+
+ HingeJointData() :
+ angular_limit_enabled(false),
+ angular_limit_upper(Math_PI * 0.5),
+ angular_limit_lower(-Math_PI * 0.5),
+ angular_limit_bias(0.3),
+ angular_limit_softness(0.9),
+ angular_limit_relaxation(1.) {}
+ };
+
+ struct SliderJointData : public JointData {
+ virtual JointType get_joint_type() { return JOINT_TYPE_SLIDER; }
+
+ virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ real_t linear_limit_upper;
+ real_t linear_limit_lower;
+ real_t linear_limit_softness;
+ real_t linear_limit_restitution;
+ real_t linear_limit_damping;
+ real_t angular_limit_upper;
+ real_t angular_limit_lower;
+ real_t angular_limit_softness;
+ real_t angular_limit_restitution;
+ real_t angular_limit_damping;
+
+ SliderJointData() :
+ linear_limit_upper(1.),
+ linear_limit_lower(-1.),
+ linear_limit_softness(1.),
+ linear_limit_restitution(0.7),
+ linear_limit_damping(1.),
+ angular_limit_upper(0),
+ angular_limit_lower(0),
+ angular_limit_softness(1.),
+ angular_limit_restitution(0.7),
+ angular_limit_damping(1.) {}
+ };
+
+ struct SixDOFJointData : public JointData {
+ struct SixDOFAxisData {
+ bool linear_limit_enabled;
+ real_t linear_limit_upper;
+ real_t linear_limit_lower;
+ real_t linear_limit_softness;
+ real_t linear_restitution;
+ real_t linear_damping;
+ bool angular_limit_enabled;
+ real_t angular_limit_upper;
+ real_t angular_limit_lower;
+ real_t angular_limit_softness;
+ real_t angular_restitution;
+ real_t angular_damping;
+ real_t erp;
+
+ SixDOFAxisData() :
+ linear_limit_enabled(true),
+ linear_limit_upper(0),
+ linear_limit_lower(0),
+ linear_limit_softness(0.7),
+ linear_restitution(0.5),
+ linear_damping(1.),
+ angular_limit_enabled(true),
+ angular_limit_upper(0),
+ angular_limit_lower(0),
+ angular_limit_softness(0.5),
+ angular_restitution(0),
+ angular_damping(1.),
+ erp(0.5) {}
+ };
+
+ virtual JointType get_joint_type() { return JOINT_TYPE_6DOF; }
+
+ virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ SixDOFAxisData axis_data[3];
+
+ SixDOFJointData() {}
+ };
+
+private:
+#ifdef TOOLS_ENABLED
+ // if false gizmo move body
+ bool gizmo_move_joint;
+#endif
+
+ JointData *joint_data;
+ Transform joint_offset;
+ RID joint;
+
+ Skeleton *parent_skeleton;
+ Transform body_offset;
+ Transform body_offset_inverse;
+ bool static_body;
+ bool _internal_static_body;
+ bool simulate_physics;
+ bool _internal_simulate_physics;
+ int bone_id;
+
+ String bone_name;
+ real_t bounce;
+ real_t mass;
+ real_t friction;
+ real_t gravity_scale;
+
+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;
+ void _notification(int p_what);
+ void _direct_state_changed(Object *p_state);
+
+ static void _bind_methods();
+
+private:
+ static Skeleton *find_skeleton_parent(Node *p_parent);
+ void _fix_joint_offset();
+ void _reload_joint();
+
+public:
+ void _on_bone_parent_changed();
+ void _set_gizmo_move_joint(bool p_move_joint);
+
+public:
+#ifdef TOOLS_ENABLED
+ virtual Transform get_global_gizmo_transform() const;
+ virtual Transform get_local_gizmo_transform() const;
+#endif
+
+ const JointData *get_joint_data() const;
+ Skeleton *find_skeleton_parent();
+
+ int get_bone_id() const { return bone_id; }
+
+ void set_joint_type(JointType p_joint_type);
+ JointType get_joint_type() const;
+
+ void set_joint_offset(const Transform &p_offset);
+ const Transform &get_joint_offset() const;
+
+ void set_body_offset(const Transform &p_offset);
+ const Transform &get_body_offset() const;
+
+ void set_static_body(bool p_static);
+ bool is_static_body();
+
+ void set_simulate_physics(bool p_simulate);
+ bool get_simulate_physics();
+ bool is_simulating_physics();
+
+ void set_bone_name(const String &p_name);
+ const String &get_bone_name() const;
+
+ void set_mass(real_t p_mass);
+ real_t get_mass() const;
+
+ void set_weight(real_t p_weight);
+ real_t get_weight() const;
+
+ void set_friction(real_t p_friction);
+ real_t get_friction() const;
+
+ void set_bounce(real_t p_bounce);
+ real_t get_bounce() const;
+
+ void set_gravity_scale(real_t p_gravity_scale);
+ real_t get_gravity_scale() const;
+
+ PhysicalBone();
+ ~PhysicalBone();
+
+private:
+ void update_bone_id();
+ void update_offset();
+ void reset_to_rest_position();
+
+ void _reset_physics_simulation_state();
+ void _reset_staticness_state();
+
+ void _start_physics_simulation();
+ void _stop_physics_simulation();
+};
+
+VARIANT_ENUM_CAST(PhysicalBone::JointType);
+
#endif // PHYSICS_BODY__H
diff --git a/scene/3d/physics_joint.cpp b/scene/3d/physics_joint.cpp
index 2e9f1a241a..b2d10006f7 100644
--- a/scene/3d/physics_joint.cpp
+++ b/scene/3d/physics_joint.cpp
@@ -716,6 +716,9 @@ void Generic6DOFJoint::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_x/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_LIMIT_SOFTNESS);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_x/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_RESTITUTION);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_x/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_x", "get_param_x", PARAM_LINEAR_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_x/target_velocity"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_x/force_limit"), "set_param_x", "get_param_x", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_ANGULAR_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_x/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_x", "_get_angular_hi_limit_x");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_x/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_x", "_get_angular_lo_limit_x");
@@ -734,6 +737,9 @@ void Generic6DOFJoint::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_y/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_LIMIT_SOFTNESS);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_y/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_RESTITUTION);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_y/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_y", "get_param_y", PARAM_LINEAR_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_LINEAR_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_y/target_velocity"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_y/force_limit"), "set_param_y", "get_param_y", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_y/enabled"), "set_flag_y", "get_flag_y", FLAG_ENABLE_ANGULAR_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_y/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_y", "_get_angular_hi_limit_y");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_y/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_y", "_get_angular_lo_limit_y");
@@ -752,6 +758,9 @@ void Generic6DOFJoint::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_z/softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_LIMIT_SOFTNESS);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_z/restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_RESTITUTION);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_limit_z/damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param_z", "get_param_z", PARAM_LINEAR_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_motor_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_LINEAR_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_z/target_velocity"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "linear_motor_z/force_limit"), "set_param_z", "get_param_z", PARAM_LINEAR_MOTOR_FORCE_LIMIT);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "angular_limit_z/enabled"), "set_flag_z", "get_flag_z", FLAG_ENABLE_ANGULAR_LIMIT);
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_z/upper_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_hi_limit_z", "_get_angular_hi_limit_z");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_limit_z/lower_angle", PROPERTY_HINT_RANGE, "-180,180,0.01"), "_set_angular_lo_limit_z", "_get_angular_lo_limit_z");
@@ -769,6 +778,8 @@ void Generic6DOFJoint::_bind_methods() {
BIND_ENUM_CONSTANT(PARAM_LINEAR_LIMIT_SOFTNESS);
BIND_ENUM_CONSTANT(PARAM_LINEAR_RESTITUTION);
BIND_ENUM_CONSTANT(PARAM_LINEAR_DAMPING);
+ BIND_ENUM_CONSTANT(PARAM_LINEAR_MOTOR_TARGET_VELOCITY);
+ BIND_ENUM_CONSTANT(PARAM_LINEAR_MOTOR_FORCE_LIMIT);
BIND_ENUM_CONSTANT(PARAM_ANGULAR_LOWER_LIMIT);
BIND_ENUM_CONSTANT(PARAM_ANGULAR_UPPER_LIMIT);
BIND_ENUM_CONSTANT(PARAM_ANGULAR_LIMIT_SOFTNESS);
@@ -783,6 +794,7 @@ void Generic6DOFJoint::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_ENABLE_LINEAR_LIMIT);
BIND_ENUM_CONSTANT(FLAG_ENABLE_ANGULAR_LIMIT);
BIND_ENUM_CONSTANT(FLAG_ENABLE_MOTOR);
+ BIND_ENUM_CONSTANT(FLAG_ENABLE_LINEAR_MOTOR);
BIND_ENUM_CONSTANT(FLAG_MAX);
}
@@ -912,6 +924,8 @@ Generic6DOFJoint::Generic6DOFJoint() {
set_param_x(PARAM_LINEAR_LIMIT_SOFTNESS, 0.7);
set_param_x(PARAM_LINEAR_RESTITUTION, 0.5);
set_param_x(PARAM_LINEAR_DAMPING, 1.0);
+ set_param_x(PARAM_LINEAR_MOTOR_TARGET_VELOCITY, 0);
+ set_param_x(PARAM_LINEAR_MOTOR_FORCE_LIMIT, 0);
set_param_x(PARAM_ANGULAR_LOWER_LIMIT, 0);
set_param_x(PARAM_ANGULAR_UPPER_LIMIT, 0);
set_param_x(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f);
@@ -925,12 +939,15 @@ Generic6DOFJoint::Generic6DOFJoint() {
set_flag_x(FLAG_ENABLE_ANGULAR_LIMIT, true);
set_flag_x(FLAG_ENABLE_LINEAR_LIMIT, true);
set_flag_x(FLAG_ENABLE_MOTOR, false);
+ set_flag_x(FLAG_ENABLE_LINEAR_MOTOR, false);
set_param_y(PARAM_LINEAR_LOWER_LIMIT, 0);
set_param_y(PARAM_LINEAR_UPPER_LIMIT, 0);
set_param_y(PARAM_LINEAR_LIMIT_SOFTNESS, 0.7);
set_param_y(PARAM_LINEAR_RESTITUTION, 0.5);
set_param_y(PARAM_LINEAR_DAMPING, 1.0);
+ set_param_y(PARAM_LINEAR_MOTOR_TARGET_VELOCITY, 0);
+ set_param_y(PARAM_LINEAR_MOTOR_FORCE_LIMIT, 0);
set_param_y(PARAM_ANGULAR_LOWER_LIMIT, 0);
set_param_y(PARAM_ANGULAR_UPPER_LIMIT, 0);
set_param_y(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f);
@@ -944,12 +961,15 @@ Generic6DOFJoint::Generic6DOFJoint() {
set_flag_y(FLAG_ENABLE_ANGULAR_LIMIT, true);
set_flag_y(FLAG_ENABLE_LINEAR_LIMIT, true);
set_flag_y(FLAG_ENABLE_MOTOR, false);
+ set_flag_y(FLAG_ENABLE_LINEAR_MOTOR, false);
set_param_z(PARAM_LINEAR_LOWER_LIMIT, 0);
set_param_z(PARAM_LINEAR_UPPER_LIMIT, 0);
set_param_z(PARAM_LINEAR_LIMIT_SOFTNESS, 0.7);
set_param_z(PARAM_LINEAR_RESTITUTION, 0.5);
set_param_z(PARAM_LINEAR_DAMPING, 1.0);
+ set_param_z(PARAM_LINEAR_MOTOR_TARGET_VELOCITY, 0);
+ set_param_z(PARAM_LINEAR_MOTOR_FORCE_LIMIT, 0);
set_param_z(PARAM_ANGULAR_LOWER_LIMIT, 0);
set_param_z(PARAM_ANGULAR_UPPER_LIMIT, 0);
set_param_z(PARAM_ANGULAR_LIMIT_SOFTNESS, 0.5f);
@@ -963,4 +983,5 @@ Generic6DOFJoint::Generic6DOFJoint() {
set_flag_z(FLAG_ENABLE_ANGULAR_LIMIT, true);
set_flag_z(FLAG_ENABLE_LINEAR_LIMIT, true);
set_flag_z(FLAG_ENABLE_MOTOR, false);
+ set_flag_z(FLAG_ENABLE_LINEAR_MOTOR, false);
}
diff --git a/scene/3d/physics_joint.h b/scene/3d/physics_joint.h
index 000109ac55..37870d6f30 100644
--- a/scene/3d/physics_joint.h
+++ b/scene/3d/physics_joint.h
@@ -249,6 +249,8 @@ public:
PARAM_LINEAR_LIMIT_SOFTNESS = PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS,
PARAM_LINEAR_RESTITUTION = PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION,
PARAM_LINEAR_DAMPING = PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING,
+ PARAM_LINEAR_MOTOR_TARGET_VELOCITY = PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_TARGET_VELOCITY,
+ PARAM_LINEAR_MOTOR_FORCE_LIMIT = PhysicsServer::G6DOF_JOINT_LINEAR_MOTOR_FORCE_LIMIT,
PARAM_ANGULAR_LOWER_LIMIT = PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT,
PARAM_ANGULAR_UPPER_LIMIT = PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT,
PARAM_ANGULAR_LIMIT_SOFTNESS = PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS,
@@ -265,6 +267,7 @@ public:
FLAG_ENABLE_LINEAR_LIMIT = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT,
FLAG_ENABLE_ANGULAR_LIMIT = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT,
FLAG_ENABLE_MOTOR = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_MOTOR,
+ FLAG_ENABLE_LINEAR_MOTOR = PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_MOTOR,
FLAG_MAX = PhysicsServer::G6DOF_JOINT_FLAG_MAX
};
diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp
index 2178da02b5..7e3a87cbd4 100644
--- a/scene/3d/reflection_probe.cpp
+++ b/scene/3d/reflection_probe.cpp
@@ -240,7 +240,7 @@ void ReflectionProbe::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Once,Always"), "set_update_mode", "get_update_mode");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "intensity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_intensity", "get_intensity");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_RANGE, "0,16384,0.1"), "set_max_distance", "get_max_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,16384,0.1,or_greater"), "set_max_distance", "get_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "extents"), "set_extents", "get_extents");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "origin_offset"), "set_origin_offset", "get_origin_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "box_projection"), "set_enable_box_projection", "is_box_projection_enabled");
diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp
index d3a13c741e..76d90dc6ff 100644
--- a/scene/3d/skeleton.cpp
+++ b/scene/3d/skeleton.cpp
@@ -33,6 +33,7 @@
#include "message_queue.h"
#include "core/project_settings.h"
+#include "scene/3d/physics_body.h"
#include "scene/resources/surface_tool.h"
bool Skeleton::_set(const StringName &p_path, const Variant &p_value) {
@@ -329,7 +330,7 @@ void Skeleton::add_bone(const String &p_name) {
_make_dirty();
update_gizmo();
}
-int Skeleton::find_bone(String p_name) const {
+int Skeleton::find_bone(const String &p_name) const {
for (int i = 0; i < bones.size(); i++) {
@@ -346,6 +347,19 @@ String Skeleton::get_bone_name(int p_bone) const {
return bones[p_bone].name;
}
+bool Skeleton::is_bone_parent_of(int p_bone, int p_parent_bone_id) const {
+
+ int parent_of_bone = get_bone_parent(p_bone);
+
+ if (-1 == parent_of_bone)
+ return false;
+
+ if (parent_of_bone == p_parent_bone_id)
+ return true;
+
+ return is_bone_parent_of(parent_of_bone, p_parent_bone_id);
+}
+
int Skeleton::get_bone_count() const {
return bones.size();
@@ -377,6 +391,17 @@ void Skeleton::unparent_bone_and_rest(int p_bone) {
_make_dirty();
}
+void Skeleton::set_bone_ignore_animation(int p_bone, bool p_ignore) {
+ ERR_FAIL_INDEX(p_bone, bones.size());
+ bones[p_bone].ignore_animation = p_ignore;
+}
+
+bool Skeleton::is_bone_ignore_animation(int p_bone) const {
+
+ ERR_FAIL_INDEX_V(p_bone, bones.size(), false);
+ return bones[p_bone].ignore_animation;
+}
+
void Skeleton::set_bone_disable_rest(int p_bone, bool p_disable) {
ERR_FAIL_INDEX(p_bone, bones.size());
@@ -522,6 +547,150 @@ void Skeleton::localize_rests() {
}
}
+void Skeleton::bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone) {
+ ERR_FAIL_INDEX(p_bone, bones.size());
+ ERR_FAIL_COND(bones[p_bone].physical_bone);
+ ERR_FAIL_COND(!p_physical_bone);
+ bones[p_bone].physical_bone = p_physical_bone;
+
+ _rebuild_physical_bones_cache();
+}
+
+void Skeleton::unbind_physical_bone_from_bone(int p_bone) {
+ ERR_FAIL_INDEX(p_bone, bones.size());
+ bones[p_bone].physical_bone = NULL;
+
+ _rebuild_physical_bones_cache();
+}
+
+PhysicalBone *Skeleton::get_physical_bone(int p_bone) {
+ ERR_FAIL_INDEX_V(p_bone, bones.size(), NULL);
+
+ return bones[p_bone].physical_bone;
+}
+
+PhysicalBone *Skeleton::get_physical_bone_parent(int p_bone) {
+ ERR_FAIL_INDEX_V(p_bone, bones.size(), NULL);
+
+ if (bones[p_bone].cache_parent_physical_bone) {
+ return bones[p_bone].cache_parent_physical_bone;
+ }
+
+ return _get_physical_bone_parent(p_bone);
+}
+
+PhysicalBone *Skeleton::_get_physical_bone_parent(int p_bone) {
+ ERR_FAIL_INDEX_V(p_bone, bones.size(), NULL);
+
+ const int parent_bone = bones[p_bone].parent;
+ if (0 > parent_bone) {
+ return NULL;
+ }
+
+ PhysicalBone *pb = bones[parent_bone].physical_bone;
+ if (pb) {
+ return pb;
+ } else {
+ return get_physical_bone_parent(parent_bone);
+ }
+}
+
+void Skeleton::_rebuild_physical_bones_cache() {
+ const int b_size = bones.size();
+ for (int i = 0; i < b_size; ++i) {
+ bones[i].cache_parent_physical_bone = _get_physical_bone_parent(i);
+ if (bones[i].physical_bone)
+ bones[i].physical_bone->_on_bone_parent_changed();
+ }
+}
+
+void _pb_stop_simulation(Node *p_node) {
+
+ for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
+ _pb_stop_simulation(p_node->get_child(i));
+ }
+
+ PhysicalBone *pb = Object::cast_to<PhysicalBone>(p_node);
+ if (pb) {
+ pb->set_simulate_physics(false);
+ pb->set_static_body(false);
+ }
+}
+
+void Skeleton::physical_bones_stop_simulation() {
+ _pb_stop_simulation(this);
+}
+
+void _pb_start_simulation(const Skeleton *p_skeleton, Node *p_node, const Vector<int> &p_sim_bones) {
+
+ for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
+ _pb_start_simulation(p_skeleton, p_node->get_child(i), p_sim_bones);
+ }
+
+ PhysicalBone *pb = Object::cast_to<PhysicalBone>(p_node);
+ if (pb) {
+ bool sim = false;
+ for (int i = p_sim_bones.size() - 1; 0 <= i; --i) {
+ if (p_sim_bones[i] == pb->get_bone_id() || p_skeleton->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
+ sim = true;
+ break;
+ }
+ }
+
+ pb->set_simulate_physics(true);
+ if (sim) {
+ pb->set_static_body(false);
+ } else {
+ pb->set_static_body(true);
+ }
+ }
+}
+
+void Skeleton::physical_bones_start_simulation_on(const Array &p_bones) {
+
+ Vector<int> sim_bones;
+ if (p_bones.size() <= 0) {
+ 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;
+ for (int i = sim_bones.size() - 1; 0 <= i; --i) {
+ if (Variant::STRING == p_bones.get(i).get_type()) {
+ int bone_id = find_bone(p_bones.get(i));
+ if (bone_id != -1)
+ sim_bones[c++] = bone_id;
+ }
+ }
+ sim_bones.resize(c);
+ }
+
+ _pb_start_simulation(this, this, sim_bones);
+}
+
+void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) {
+
+ for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
+ _physical_bones_add_remove_collision_exception(p_add, p_node->get_child(i), p_exception);
+ }
+
+ CollisionObject *co = Object::cast_to<CollisionObject>(p_node);
+ if (co) {
+ if (p_add) {
+ PhysicsServer::get_singleton()->body_add_collision_exception(co->get_rid(), p_exception);
+ } else {
+ PhysicsServer::get_singleton()->body_remove_collision_exception(co->get_rid(), p_exception);
+ }
+ }
+}
+
+void Skeleton::physical_bones_add_collision_exception(RID p_exception) {
+ _physical_bones_add_remove_collision_exception(true, this, p_exception);
+}
+
+void Skeleton::physical_bones_remove_collision_exception(RID p_exception) {
+ _physical_bones_add_remove_collision_exception(false, this, p_exception);
+}
+
void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton::add_bone);
@@ -558,6 +727,11 @@ void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_transform", "bone_idx"), &Skeleton::get_bone_transform);
+ ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton::physical_bones_stop_simulation);
+ ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton::physical_bones_start_simulation_on, DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton::physical_bones_add_collision_exception);
+ ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton::physical_bones_remove_collision_exception);
+
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
}
@@ -570,6 +744,5 @@ Skeleton::Skeleton() {
}
Skeleton::~Skeleton() {
-
VisualServer::get_singleton()->free(skeleton);
}
diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h
index d693670055..dad11960a5 100644
--- a/scene/3d/skeleton.h
+++ b/scene/3d/skeleton.h
@@ -37,6 +37,8 @@
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
+
+class PhysicalBone;
class Skeleton : public Spatial {
GDCLASS(Skeleton, Spatial);
@@ -48,6 +50,8 @@ class Skeleton : public Spatial {
bool enabled;
int parent;
+ bool ignore_animation;
+
bool disable_rest;
Transform rest;
Transform rest_global_inverse;
@@ -60,13 +64,19 @@ class Skeleton : public Spatial {
Transform transform_final;
+ PhysicalBone *physical_bone;
+ PhysicalBone *cache_parent_physical_bone;
+
List<uint32_t> nodes_bound;
Bone() {
parent = -1;
enabled = true;
+ ignore_animation = false;
custom_pose_enable = false;
disable_rest = false;
+ physical_bone = NULL;
+ cache_parent_physical_bone = NULL;
}
};
@@ -110,14 +120,19 @@ public:
// skeleton creation api
void add_bone(const String &p_name);
- int find_bone(String p_name) const;
+ int find_bone(const String &p_name) const;
String get_bone_name(int p_bone) const;
+ bool is_bone_parent_of(int p_bone_id, int p_parent_bone_id) const;
+
void set_bone_parent(int p_bone, int p_parent);
int get_bone_parent(int p_bone) const;
void unparent_bone_and_rest(int p_bone);
+ void set_bone_ignore_animation(int p_bone, bool p_ignore);
+ bool is_bone_ignore_animation(int p_bone) const;
+
void set_bone_disable_rest(int p_bone, bool p_disable);
bool is_bone_rest_disabled(int p_bone) const;
@@ -149,6 +164,26 @@ public:
void localize_rests(); // used for loaders and tools
+ // Physical bone API
+
+ void bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone);
+ void unbind_physical_bone_from_bone(int p_bone);
+
+ PhysicalBone *get_physical_bone(int p_bone);
+ PhysicalBone *get_physical_bone_parent(int p_bone);
+
+private:
+ /// This is a slow API os it's cached
+ PhysicalBone *_get_physical_bone_parent(int p_bone);
+ void _rebuild_physical_bones_cache();
+
+public:
+ void physical_bones_stop_simulation();
+ void physical_bones_start_simulation_on(const Array &p_bones);
+ void physical_bones_add_collision_exception(RID p_exception);
+ void physical_bones_remove_collision_exception(RID p_exception);
+
+public:
Skeleton();
~Skeleton();
};
diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp
index dab0e573d7..748aa8aad4 100644
--- a/scene/3d/spatial.cpp
+++ b/scene/3d/spatial.cpp
@@ -286,6 +286,16 @@ Transform Spatial::get_global_transform() const {
return data.global_transform;
}
+#ifdef TOOLS_ENABLED
+Transform Spatial::get_global_gizmo_transform() const {
+ return get_global_transform();
+}
+
+Transform Spatial::get_local_gizmo_transform() const {
+ return get_transform();
+}
+#endif
+
Spatial *Spatial::get_parent_spatial() const {
return data.parent;
@@ -430,10 +440,9 @@ Ref<SpatialGizmo> Spatial::get_gizmo() const {
#endif
}
-#ifdef TOOLS_ENABLED
-
void Spatial::_update_gizmo() {
+#ifdef TOOLS_ENABLED
if (!is_inside_world())
return;
data.gizmo_dirty = false;
@@ -445,8 +454,10 @@ void Spatial::_update_gizmo() {
data.gizmo->clear();
}
}
+#endif
}
+#ifdef TOOLS_ENABLED
void Spatial::set_disable_gizmo(bool p_enabled) {
data.gizmo_disabled = p_enabled;
@@ -726,9 +737,7 @@ void Spatial::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_set_as_toplevel"), &Spatial::is_set_as_toplevel);
ClassDB::bind_method(D_METHOD("get_world"), &Spatial::get_world);
-#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("_update_gizmo"), &Spatial::_update_gizmo);
-#endif
ClassDB::bind_method(D_METHOD("update_gizmo"), &Spatial::update_gizmo);
ClassDB::bind_method(D_METHOD("set_gizmo", "gizmo"), &Spatial::set_gizmo);
@@ -790,9 +799,7 @@ void Spatial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale");
ADD_GROUP("Visibility", "");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
-#ifdef TOOLS_ENABLED
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "SpatialGizmo", 0), "set_gizmo", "get_gizmo");
-#endif
ADD_SIGNAL(MethodInfo("visibility_changed"));
}
diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h
index 518bba9a51..a43bed3e4a 100644
--- a/scene/3d/spatial.h
+++ b/scene/3d/spatial.h
@@ -100,10 +100,8 @@ class Spatial : public Node {
#endif
} data;
-#ifdef TOOLS_ENABLED
void _update_gizmo();
-#endif
void _notify_dirty();
void _propagate_transform_changed(Spatial *p_origin);
@@ -147,6 +145,11 @@ public:
Transform get_transform() const;
Transform get_global_transform() const;
+#ifdef TOOLS_ENABLED
+ virtual Transform get_global_gizmo_transform() const;
+ virtual Transform get_local_gizmo_transform() const;
+#endif
+
void set_as_toplevel(bool p_enabled);
bool is_set_as_toplevel() const;
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index bc44c91f64..232855c978 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -366,17 +366,11 @@ void Sprite3D::_draw() {
final_rect.position * pixel_size,
};
-
- // Properly setup UVs for impostor textures (AtlasTexture).
- RID texture_rid = texture->get_rid();
- Vector2 src_tsize = Vector2(
- VS::get_singleton()->texture_get_width(texture_rid),
- VS::get_singleton()->texture_get_height(texture_rid));
Vector2 uvs[4] = {
- final_src_rect.position / src_tsize,
- (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize,
- (final_src_rect.position + final_src_rect.size) / src_tsize,
- (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize,
+ final_src_rect.position / tsize,
+ (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / tsize,
+ (final_src_rect.position + final_src_rect.size) / tsize,
+ (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / tsize,
};
if (is_flipped_h()) {
@@ -655,23 +649,18 @@ void AnimatedSprite3D::_draw() {
float pixel_size = get_pixel_size();
Vector2 vertices[4] = {
+
(final_rect.position + Vector2(0, final_rect.size.y)) * pixel_size,
(final_rect.position + final_rect.size) * pixel_size,
(final_rect.position + Vector2(final_rect.size.x, 0)) * pixel_size,
final_rect.position * pixel_size,
};
-
- // Properly setup UVs for impostor textures (AtlasTexture).
- RID texture_rid = texture->get_rid();
- Vector2 src_tsize = Vector2(
- VS::get_singleton()->texture_get_width(texture_rid),
- VS::get_singleton()->texture_get_height(texture_rid));
Vector2 uvs[4] = {
- final_src_rect.position / src_tsize,
- (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / src_tsize,
- (final_src_rect.position + final_src_rect.size) / src_tsize,
- (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / src_tsize,
+ final_src_rect.position / tsize,
+ (final_src_rect.position + Vector2(final_src_rect.size.x, 0)) / tsize,
+ (final_src_rect.position + final_src_rect.size) / tsize,
+ (final_src_rect.position + Vector2(0, final_src_rect.size.y)) / tsize,
};
if (is_flipped_h()) {
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
index b72665aa2b..ee8d249981 100644
--- a/scene/3d/vehicle_body.cpp
+++ b/scene/3d/vehicle_body.cpp
@@ -672,13 +672,8 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) {
m_forwardImpulse.resize(numWheel);
m_sideImpulse.resize(numWheel);
- int numWheelsOnGround = 0;
-
//collapse all those loops into one!
for (int i = 0; i < wheels.size(); i++) {
- VehicleWheel &wheelInfo = *wheels[i];
- if (wheelInfo.m_raycastInfo.m_isInContact)
- numWheelsOnGround++;
m_sideImpulse[i] = real_t(0.);
m_forwardImpulse[i] = real_t(0.);
}
@@ -929,7 +924,7 @@ void VehicleBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_steering"), &VehicleBody::get_steering);
ADD_GROUP("Motion", "");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01"), "set_engine_force", "get_engine_force");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "engine_force", PROPERTY_HINT_RANGE, "0.00,1024.0,0.01,or_greater"), "set_engine_force", "get_engine_force");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "brake", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_brake", "get_brake");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "steering", PROPERTY_HINT_RANGE, "-180,180.0,0.01"), "set_steering", "get_steering");
}
diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp
index d389b69ef3..670df5cc7f 100644
--- a/scene/3d/voxel_light_baker.cpp
+++ b/scene/3d/voxel_light_baker.cpp
@@ -2316,13 +2316,10 @@ Ref<MultiMesh> VoxelLightBaker::create_debug_multimesh(DebugMode p_mode) {
PoolVector<Vector3> vertices;
PoolVector<Color> colors;
-
- int vtx_idx = 0;
#define ADD_VTX(m_idx) \
; \
vertices.push_back(face_points[m_idx]); \
- colors.push_back(Color(1, 1, 1, 1)); \
- vtx_idx++;
+ colors.push_back(Color(1, 1, 1, 1));
for (int i = 0; i < 6; i++) {
@@ -2338,9 +2335,9 @@ Ref<MultiMesh> VoxelLightBaker::create_debug_multimesh(DebugMode p_mode) {
for (int k = 0; k < 3; k++) {
if (i < 3)
- face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ face_points[j][(i + k) % 3] = v[k];
else
- face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ face_points[3 - j][(i + k) % 3] = -v[k];
}
}
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index eca7caeaf0..a0e0137863 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -246,8 +246,9 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) {
if (a->track_get_path(i).get_subname_count() == 1 && Object::cast_to<Skeleton>(child)) {
- bone_idx = Object::cast_to<Skeleton>(child)->find_bone(a->track_get_path(i).get_subname(0));
- if (bone_idx == -1) {
+ Skeleton *sk = Object::cast_to<Skeleton>(child);
+ bone_idx = sk->find_bone(a->track_get_path(i).get_subname(0));
+ if (bone_idx == -1 || sk->is_bone_ignore_animation(bone_idx)) {
continue;
}
@@ -506,7 +507,6 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float
void AnimationPlayer::_animation_process_data(PlaybackData &cd, float p_delta, float p_blend) {
float delta = p_delta * speed_scale * cd.speed_scale;
- bool backwards = delta < 0;
float next_pos = cd.pos + delta;
float len = cd.from->animation->get_length();
@@ -524,6 +524,8 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, float p_delta, f
if (&cd == &playback.current) {
+ bool backwards = delta < 0;
+
if (!backwards && cd.pos <= len && next_pos == len /*&& playback.blend.empty()*/) {
//playback finished
end_reached = true;
@@ -579,16 +581,14 @@ void AnimationPlayer::_animation_process2(float p_delta) {
}
void AnimationPlayer::_animation_update_transforms() {
+ {
+ Transform t;
+ for (int i = 0; i < cache_update_size; i++) {
- for (int i = 0; i < cache_update_size; i++) {
-
- TrackNodeCache *nc = cache_update[i];
-
- ERR_CONTINUE(nc->accum_pass != accum_pass);
+ TrackNodeCache *nc = cache_update[i];
- if (nc->spatial) {
+ ERR_CONTINUE(nc->accum_pass != accum_pass);
- Transform t;
t.origin = nc->loc_accum;
t.basis.set_quat_scale(nc->rot_accum, nc->scale_accum);
if (nc->skeleton && nc->bone_idx >= 0) {
@@ -1208,7 +1208,7 @@ NodePath AnimationPlayer::get_root() const {
void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
String pf = p_function;
- if (p_function == "play" || p_function == "remove_animation" || p_function == "has_animation" || p_function == "queue") {
+ if (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 (List<StringName>::Element *E = al.front(); E; E = E->next()) {
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index 42fa20068b..ce5b372d72 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -813,7 +813,11 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
t.value = t.object->get_indexed(t.subpath);
t.value.zero();
- t.skip = false;
+ if (t.skeleton) {
+ t.skip = t.skeleton->is_bone_ignore_animation(t.bone_idx);
+ } else {
+ t.skip = false;
+ }
}
/* STEP 2 PROCESS ANIMATIONS */
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index dbfb96697d..acdbd9de08 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -60,7 +60,7 @@ void BaseButton::_gui_input(Ref<InputEvent> p_event) {
Ref<InputEventMouseButton> b = p_event;
if (b.is_valid()) {
- if (status.disabled || b->get_button_index() != 1)
+ if (status.disabled || ((1 << (b->get_button_index() - 1)) & button_mask) == 0)
return;
if (status.pressing_button)
@@ -408,6 +408,16 @@ BaseButton::ActionMode BaseButton::get_action_mode() const {
return action_mode;
}
+void BaseButton::set_button_mask(int p_mask) {
+
+ button_mask = p_mask;
+}
+
+int BaseButton::get_button_mask() const {
+
+ return button_mask;
+}
+
void BaseButton::set_enabled_focus_mode(FocusMode p_mode) {
enabled_focus_mode = p_mode;
@@ -496,6 +506,8 @@ void BaseButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_disabled"), &BaseButton::is_disabled);
ClassDB::bind_method(D_METHOD("set_action_mode", "mode"), &BaseButton::set_action_mode);
ClassDB::bind_method(D_METHOD("get_action_mode"), &BaseButton::get_action_mode);
+ ClassDB::bind_method(D_METHOD("set_button_mask", "mask"), &BaseButton::set_button_mask);
+ ClassDB::bind_method(D_METHOD("get_button_mask"), &BaseButton::get_button_mask);
ClassDB::bind_method(D_METHOD("get_draw_mode"), &BaseButton::get_draw_mode);
ClassDB::bind_method(D_METHOD("set_enabled_focus_mode", "mode"), &BaseButton::set_enabled_focus_mode);
ClassDB::bind_method(D_METHOD("get_enabled_focus_mode"), &BaseButton::get_enabled_focus_mode);
@@ -517,6 +529,7 @@ void BaseButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode");
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
ADD_PROPERTYNO(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode");
+ ADD_PROPERTYNO(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask");
ADD_PROPERTY(PropertyInfo(Variant::INT, "enabled_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_enabled_focus_mode", "get_enabled_focus_mode");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "ShortCut"), "set_shortcut", "get_shortcut");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group");
@@ -542,6 +555,7 @@ BaseButton::BaseButton() {
set_focus_mode(FOCUS_ALL);
enabled_focus_mode = FOCUS_ALL;
action_mode = ACTION_MODE_BUTTON_RELEASE;
+ button_mask = BUTTON_MASK_LEFT;
}
BaseButton::~BaseButton() {
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index 6917e112ab..79638bbcce 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -49,6 +49,7 @@ public:
};
private:
+ int button_mask;
bool toggle_mode;
FocusMode enabled_focus_mode;
Ref<ShortCut> shortcut;
@@ -104,6 +105,9 @@ public:
void set_action_mode(ActionMode p_mode);
ActionMode get_action_mode() const;
+ void set_button_mask(int p_mask);
+ int get_button_mask() const;
+
void set_enabled_focus_mode(FocusMode p_mode);
FocusMode get_enabled_focus_mode() const;
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 6f34f3e49f..34891832e2 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -656,8 +656,9 @@ ColorPicker::ColorPicker() :
void ColorPickerButton::_color_changed(const Color &p_color) {
+ color = p_color;
update();
- emit_signal("color_changed", p_color);
+ emit_signal("color_changed", color);
}
void ColorPickerButton::_modal_closed() {
@@ -667,6 +668,7 @@ void ColorPickerButton::_modal_closed() {
void ColorPickerButton::pressed() {
+ _update_picker();
popup->set_position(get_global_position() - picker->get_combined_minimum_size());
popup->popup();
picker->set_focus_on_line_edit();
@@ -679,37 +681,44 @@ void ColorPickerButton::_notification(int p_what) {
Ref<StyleBox> normal = get_stylebox("normal");
Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size());
draw_texture_rect(Control::get_icon("bg", "ColorPickerButton"), r, true);
- draw_rect(r, picker->get_pick_color());
+ draw_rect(r, color);
}
- if (p_what == MainLoop::NOTIFICATION_WM_QUIT_REQUEST) {
+ if (p_what == MainLoop::NOTIFICATION_WM_QUIT_REQUEST && popup) {
popup->hide();
}
}
void ColorPickerButton::set_pick_color(const Color &p_color) {
- picker->set_pick_color(p_color);
+ color = p_color;
+ if (picker) {
+ picker->set_pick_color(p_color);
+ }
+
update();
- emit_signal("color_changed", p_color);
}
Color ColorPickerButton::get_pick_color() const {
- return picker->get_pick_color();
+ return color;
}
void ColorPickerButton::set_edit_alpha(bool p_show) {
- picker->set_edit_alpha(p_show);
+ edit_alpha = p_show;
+ if (picker) {
+ picker->set_edit_alpha(p_show);
+ }
}
bool ColorPickerButton::is_editing_alpha() const {
- return picker->is_editing_alpha();
+ return edit_alpha;
}
-ColorPicker *ColorPickerButton::get_picker() const {
+ColorPicker *ColorPickerButton::get_picker() {
+ _update_picker();
return picker;
}
@@ -718,6 +727,19 @@ PopupPanel *ColorPickerButton::get_popup() const {
return popup;
}
+void ColorPickerButton::_update_picker() {
+ if (!picker) {
+ popup = memnew(PopupPanel);
+ picker = memnew(ColorPicker);
+ popup->add_child(picker);
+ add_child(popup);
+ picker->connect("color_changed", this, "_color_changed");
+ popup->connect("modal_closed", this, "_modal_closed");
+ picker->set_pick_color(color);
+ picker->set_edit_alpha(edit_alpha);
+ }
+}
+
void ColorPickerButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPickerButton::set_pick_color);
@@ -737,12 +759,10 @@ void ColorPickerButton::_bind_methods() {
ColorPickerButton::ColorPickerButton() {
- popup = memnew(PopupPanel);
- picker = memnew(ColorPicker);
- popup->add_child(picker);
-
- picker->connect("color_changed", this, "_color_changed");
- popup->connect("modal_closed", this, "_modal_closed");
-
- add_child(popup);
+ //Initialization is now done deferred
+ //this improves performance in the inspector as the color picker
+ //can be expensive to initialize
+ picker = NULL;
+ popup = NULL;
+ edit_alpha = true;
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 7d1a554ada..6b63e5fe60 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -118,12 +118,16 @@ class ColorPickerButton : public Button {
PopupPanel *popup;
ColorPicker *picker;
+ Color color;
+ bool edit_alpha;
void _color_changed(const Color &p_color);
void _modal_closed();
virtual void pressed();
+ void _update_picker();
+
protected:
void _notification(int);
static void _bind_methods();
@@ -135,7 +139,7 @@ public:
void set_edit_alpha(bool p_show);
bool is_editing_alpha() const;
- ColorPicker *get_picker() const;
+ ColorPicker *get_picker();
PopupPanel *get_popup() const;
ColorPickerButton();
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index 7cb0ad5707..177582c87c 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -34,9 +34,9 @@
void Container::_child_minsize_changed() {
- Size2 ms = get_combined_minimum_size();
- if (ms.width > get_size().width || ms.height > get_size().height)
- minimum_size_changed();
+ //Size2 ms = get_combined_minimum_size();
+ //if (ms.width > get_size().width || ms.height > get_size().height) {
+ minimum_size_changed();
queue_sort();
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index b7c1d35fd7..3097ecaf16 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -155,12 +155,21 @@ Size2 Control::get_custom_minimum_size() const {
return data.custom_minimum_size;
}
-Size2 Control::get_combined_minimum_size() const {
+void Control::_update_minimum_size_cache() {
Size2 minsize = get_minimum_size();
minsize.x = MAX(minsize.x, data.custom_minimum_size.x);
minsize.y = MAX(minsize.y, data.custom_minimum_size.y);
- return minsize;
+ data.minimum_size_cache = minsize;
+ data.minimum_size_valid = true;
+}
+
+Size2 Control::get_combined_minimum_size() const {
+
+ if (!data.minimum_size_valid) {
+ const_cast<Control *>(this)->_update_minimum_size_cache();
+ }
+ return data.minimum_size_cache;
}
Size2 Control::_edit_get_minimum_size() const {
@@ -259,14 +268,17 @@ void Control::_update_minimum_size() {
if (!is_inside_tree())
return;
- data.pending_min_size_update = false;
Size2 minsize = get_combined_minimum_size();
if (minsize.x > data.size_cache.x ||
minsize.y > data.size_cache.y) {
_size_changed();
}
- emit_signal(SceneStringNames::get_singleton()->minimum_size_changed);
+ data.updating_last_minimum_size = false;
+
+ if (minsize != data.last_minimum_size) {
+ emit_signal(SceneStringNames::get_singleton()->minimum_size_changed);
+ }
}
bool Control::_get(const StringName &p_name, Variant &r_ret) const {
@@ -437,8 +449,12 @@ void Control::_notification(int p_notification) {
case NOTIFICATION_ENTER_TREE: {
- _size_changed();
-
+ } break;
+ case NOTIFICATION_POST_ENTER_TREE: {
+ if (is_visible_in_tree()) {
+ data.minimum_size_valid = false;
+ _size_changed();
+ }
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -620,13 +636,12 @@ void Control::_notification(int p_notification) {
if (is_inside_tree()) {
_modal_stack_remove();
- minimum_size_changed();
}
//remove key focus
//remove modalness
} else {
-
+ data.minimum_size_valid = false;
_size_changed();
}
@@ -2464,17 +2479,25 @@ void Control::minimum_size_changed() {
if (!is_inside_tree() || data.block_minimum_size_adjust)
return;
- if (data.pending_min_size_update)
+ Control *invalidate = this;
+
+ //invalidate cache upwards
+ while (invalidate && invalidate->data.minimum_size_valid) {
+ invalidate->data.minimum_size_valid = false;
+ if (invalidate->is_set_as_toplevel())
+ break; // do not go further up
+ invalidate = invalidate->data.parent;
+ }
+
+ if (!is_visible_in_tree())
+ return;
+
+ if (data.updating_last_minimum_size)
return;
- data.pending_min_size_update = true;
- MessageQueue::get_singleton()->push_call(this, "_update_minimum_size");
+ data.updating_last_minimum_size = true;
- if (!is_toplevel_control()) {
- Control *pc = get_parent_control();
- if (pc)
- pc->minimum_size_changed();
- }
+ MessageQueue::get_singleton()->push_call(this, "_update_minimum_size");
}
int Control::get_v_size_flags() const {
@@ -2985,7 +3008,6 @@ Control::Control() {
data.h_size_flags = SIZE_FILL;
data.v_size_flags = SIZE_FILL;
data.expand = 1;
- data.pending_min_size_update = false;
data.rotation = 0;
data.parent_canvas_item = NULL;
data.scale = Vector2(1, 1);
@@ -2995,6 +3017,8 @@ Control::Control() {
data.disable_visibility_clip = false;
data.h_grow = GROW_DIRECTION_END;
data.v_grow = GROW_DIRECTION_END;
+ data.minimum_size_valid = false;
+ data.updating_last_minimum_size = false;
data.clip_contents = false;
for (int i = 0; i < 4; i++) {
diff --git a/scene/gui/control.h b/scene/gui/control.h
index b5453e60f5..9124256624 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -148,6 +148,11 @@ private:
Point2 pos_cache;
Size2 size_cache;
+ Size2 minimum_size_cache;
+ bool minimum_size_valid;
+
+ Size2 last_minimum_size;
+ bool updating_last_minimum_size;
float margin[4];
float anchor[4];
@@ -164,7 +169,6 @@ private:
int h_size_flags;
int v_size_flags;
float expand;
- bool pending_min_size_update;
Point2 custom_minimum_size;
bool pass_on_modal_close_click;
@@ -244,6 +248,8 @@ private:
void _modal_stack_remove();
void _modal_set_prev_focus_owner(ObjectID p_prev);
+ void _update_minimum_size_cache();
+
protected:
virtual void add_child_notify(Node *p_child);
virtual void remove_child_notify(Node *p_child);
diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp
index b401abd436..278e4123d7 100644
--- a/scene/gui/grid_container.cpp
+++ b/scene/gui/grid_container.cpp
@@ -36,6 +36,8 @@ void GridContainer::_notification(int p_what) {
case NOTIFICATION_SORT_CHILDREN: {
+ int valid_controls_index;
+
Map<int, int> col_minw; // max of min_width of all controls in each col (indexed by col)
Map<int, int> row_minh; // max of min_height of all controls in each row (indexed by row)
Set<int> col_expanded; // columns which have the SIZE_EXPAND flag set
@@ -47,13 +49,15 @@ void GridContainer::_notification(int p_what) {
int max_row = get_child_count() / columns;
// Compute the per-column/per-row data
+ valid_controls_index = 0;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
if (!c || !c->is_visible_in_tree())
continue;
- int row = i / columns;
- int col = i % columns;
+ int row = valid_controls_index / columns;
+ int col = valid_controls_index % columns;
+ valid_controls_index++;
Size2i ms = c->get_combined_minimum_size();
if (col_minw.has(col))
@@ -136,12 +140,14 @@ void GridContainer::_notification(int p_what) {
int col_ofs = 0;
int row_ofs = 0;
+ valid_controls_index = 0;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
if (!c || !c->is_visible_in_tree())
continue;
- int row = i / columns;
- int col = i % columns;
+ int row = valid_controls_index / columns;
+ int col = valid_controls_index % columns;
+ valid_controls_index++;
if (col == 0) {
col_ofs = 0;
@@ -178,6 +184,8 @@ void GridContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_columns", "columns"), &GridContainer::set_columns);
ClassDB::bind_method(D_METHOD("get_columns"), &GridContainer::get_columns);
+ ClassDB::bind_method(D_METHOD("get_child_control_at_cell", "row", "column"),
+ &GridContainer::get_child_control_at_cell);
ADD_PROPERTY(PropertyInfo(Variant::INT, "columns", PROPERTY_HINT_RANGE, "1,1024,1"), "set_columns", "get_columns");
}
@@ -190,17 +198,19 @@ Size2 GridContainer::get_minimum_size() const {
int hsep = get_constant("hseparation");
int vsep = get_constant("vseparation");
- int idx = 0;
int max_row = 0;
int max_col = 0;
+ int valid_controls_index = 0;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
if (!c || !c->is_visible_in_tree())
continue;
- int row = idx / columns;
- int col = idx % columns;
+ int row = valid_controls_index / columns;
+ int col = valid_controls_index % columns;
+ valid_controls_index++;
+
Size2i ms = c->get_combined_minimum_size();
if (col_minw.has(col))
col_minw[col] = MAX(col_minw[col], ms.width);
@@ -213,7 +223,6 @@ Size2 GridContainer::get_minimum_size() const {
row_minh[row] = ms.height;
max_col = MAX(col, max_col);
max_row = MAX(row, max_row);
- idx++;
}
Size2 ms;
@@ -232,6 +241,21 @@ Size2 GridContainer::get_minimum_size() const {
return ms;
}
+Control *GridContainer::get_child_control_at_cell(int row, int column) {
+ Control *c;
+ int grid_index = row * columns + column;
+ for (int i = 0; i < get_child_count(); i++) {
+ c = Object::cast_to<Control>(get_child(i));
+ if (!c || !c->is_visible_in_tree())
+ continue;
+
+ if (grid_index == i) {
+ break;
+ }
+ }
+ return c;
+}
+
GridContainer::GridContainer() {
set_mouse_filter(MOUSE_FILTER_PASS);
diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h
index 243d06f034..7e3470dc89 100644
--- a/scene/gui/grid_container.h
+++ b/scene/gui/grid_container.h
@@ -47,6 +47,7 @@ public:
void set_columns(int p_columns);
int get_columns() const;
virtual Size2 get_minimum_size() const;
+ Control *get_child_control_at_cell(int row, int column);
GridContainer();
};
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 511dc248a0..57b9a9a11b 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -1121,6 +1121,7 @@ void ItemList::_notification(int p_what) {
text_ofs += base_ofs;
text_ofs += items[i].rect_cache.position;
+ FontDrawer drawer(font, Color(1, 1, 1));
for (int j = 0; j < ss; j++) {
if (j == line_limit_cache[line]) {
@@ -1129,7 +1130,7 @@ void ItemList::_notification(int p_what) {
if (line >= max_text_lines)
break;
}
- ofs += font->draw_char(get_canvas_item(), text_ofs + Vector2(ofs + (max_len - line_size_cache[line]) / 2, line * (font_height + line_separation)).floor(), items[i].text[j], items[i].text[j + 1], modulate);
+ ofs += drawer.draw_char(get_canvas_item(), text_ofs + Vector2(ofs + (max_len - line_size_cache[line]) / 2, line * (font_height + line_separation)).floor(), items[i].text[j], items[i].text[j + 1], modulate);
}
//special multiline mode
@@ -1243,7 +1244,7 @@ bool ItemList::is_pos_at_end_of_items(const Point2 &p_pos) const {
String ItemList::get_tooltip(const Point2 &p_pos) const {
- int closest = get_item_at_position(p_pos);
+ int closest = get_item_at_position(p_pos, true);
if (closest != -1) {
if (!items[closest].tooltip_enabled) {
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 830f724b3c..9af479c1cc 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -93,6 +93,7 @@ void Label::_notification(int p_what) {
bool use_outline = get_constant("shadow_as_outline");
Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
int line_spacing = get_constant("line_spacing");
+ Color font_outline_modulate = get_color("font_outline_modulate");
style->draw(ci, Rect2(Point2(0, 0), get_size()));
@@ -102,7 +103,8 @@ void Label::_notification(int p_what) {
int lines_visible = (size.y + line_spacing) / font_h;
- int space_w = font->get_char_size(' ').width;
+ // ceiling to ensure autowrapping does not cut text
+ int space_w = Math::ceil(font->get_char_size(' ').width);
int chars_total = 0;
int vbegin = 0, vsep = 0;
@@ -150,6 +152,7 @@ void Label::_notification(int p_what) {
int line = 0;
int line_to = lines_skipped + (lines_visible > 0 ? lines_visible : 1);
+ FontDrawer drawer(font, font_outline_modulate);
while (wc) {
/* handle lines not meant to be drawn quickly */
if (line >= line_to)
@@ -244,11 +247,11 @@ void Label::_notification(int p_what) {
n = String::char_uppercase(c);
}
- float move = font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + shadow_ofs, c, n, font_color_shadow);
+ float move = font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + shadow_ofs, c, n, font_color_shadow, false);
if (use_outline) {
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, shadow_ofs.y), c, n, font_color_shadow);
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow);
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, shadow_ofs.y), c, n, font_color_shadow, false);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow, false);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c, n, font_color_shadow, false);
}
x_ofs_shadow += move;
chars_total_shadow++;
@@ -265,7 +268,7 @@ void Label::_notification(int p_what) {
n = String::char_uppercase(c);
}
- x_ofs += font->draw_char(ci, Point2(x_ofs, y_ofs), c, n, font_color);
+ x_ofs += drawer.draw_char(ci, Point2(x_ofs, y_ofs), c, n, font_color);
chars_total++;
}
}
@@ -329,7 +332,8 @@ int Label::get_longest_line_width() const {
}
} else {
- int char_width = font->get_char_size(current, xl_text[i + 1]).width;
+ // ceiling to ensure autowrapping does not cut text
+ int char_width = Math::ceil(font->get_char_size(current, xl_text[i + 1]).width);
line_width += char_width;
}
}
@@ -382,7 +386,8 @@ void Label::regenerate_word_cache() {
int word_pos = 0;
int line_width = 0;
int space_count = 0;
- int space_width = font->get_char_size(' ').width;
+ // ceiling to ensure autowrapping does not cut text
+ int space_width = Math::ceil(font->get_char_size(' ').width);
int line_spacing = get_constant("line_spacing");
line_count = 1;
total_char_cache = 0;
@@ -444,8 +449,8 @@ void Label::regenerate_word_cache() {
if (current_word_size == 0) {
word_pos = i;
}
-
- char_width = font->get_char_size(current, xl_text[i + 1]).width;
+ // ceiling to ensure autowrapping does not cut text
+ char_width = Math::ceil(font->get_char_size(current, xl_text[i + 1]).width);
current_word_size += char_width;
line_width += char_width;
total_char_cache++;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index ef0d5e4c6e..e57af0a4c0 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -656,6 +656,7 @@ void LineEdit::_notification(int p_what) {
}
int caret_height = font->get_height() > y_area ? y_area : font->get_height();
+ FontDrawer drawer(font, Color(1, 1, 1));
while (true) {
//end of string, break!
@@ -683,7 +684,7 @@ void LineEdit::_notification(int p_what) {
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 1)), font_color);
}
- font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color);
+ drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color);
x_ofs += im_char_width;
ofs++;
@@ -704,7 +705,7 @@ void LineEdit::_notification(int p_what) {
if (selected)
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(char_width, caret_height)), selection_color);
- font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
+ drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color);
if (char_ofs == cursor_pos && draw_caret) {
if (ime_text.length() == 0) {
@@ -737,7 +738,7 @@ void LineEdit::_notification(int p_what) {
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs + caret_height), Size2(im_char_width, 1)), font_color);
}
- font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color);
+ drawer.draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, font_color);
x_ofs += im_char_width;
ofs++;
@@ -1143,7 +1144,6 @@ void LineEdit::clear_internal() {
_clear_undo_stack();
cached_width = 0;
- cached_placeholder_width = 0;
cursor_pos = 0;
window_pos = 0;
undo_text = "";
@@ -1350,12 +1350,12 @@ PopupMenu *LineEdit::get_menu() const {
return menu;
}
-#ifdef TOOLS_ENABLED
void LineEdit::_editor_settings_changed() {
+#ifdef TOOLS_ENABLED
cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
-}
#endif
+}
void LineEdit::set_expand_to_text_length(bool p_enabled) {
@@ -1424,9 +1424,7 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("_text_changed"), &LineEdit::_text_changed);
ClassDB::bind_method(D_METHOD("_toggle_draw_caret"), &LineEdit::_toggle_draw_caret);
-#ifdef TOOLS_ENABLED
ClassDB::bind_method("_editor_settings_changed", &LineEdit::_editor_settings_changed);
-#endif
ClassDB::bind_method(D_METHOD("set_align", "align"), &LineEdit::set_align);
ClassDB::bind_method(D_METHOD("get_align"), &LineEdit::get_align);
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 48dde2461e..e9314ba8dd 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -124,7 +124,6 @@ private:
void shift_selection_check_post(bool);
void selection_fill_at_cursor();
- void selection_delete();
void set_window_pos(int p_pos);
void set_cursor_at_pixel_pos(int p_x);
@@ -135,9 +134,7 @@ private:
void clear_internal();
void changed_internal();
-#ifdef TOOLS_ENABLED
void _editor_settings_changed();
-#endif
void _gui_input(Ref<InputEvent> p_event);
void _notification(int p_what);
@@ -160,6 +157,7 @@ public:
void select(int p_from = 0, int p_to = -1);
void select_all();
+ void selection_delete();
void deselect();
void delete_char();
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index a9402d6404..c5e4149782 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -297,7 +297,7 @@ void OptionButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("_focused"), &OptionButton::_focused);
ClassDB::bind_method(D_METHOD("add_item", "label", "id"), &OptionButton::add_item, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id"), &OptionButton::add_icon_item);
+ ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id"), &OptionButton::add_icon_item, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &OptionButton::set_item_text);
ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "texture"), &OptionButton::set_item_icon);
ClassDB::bind_method(D_METHOD("set_item_disabled", "idx", "disabled"), &OptionButton::set_item_disabled);
diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp
index c85bed0451..37e519e375 100644
--- a/scene/gui/progress_bar.cpp
+++ b/scene/gui/progress_bar.cpp
@@ -33,13 +33,16 @@
Size2 ProgressBar::get_minimum_size() const {
Ref<StyleBox> bg = get_stylebox("bg");
+ Ref<StyleBox> fg = get_stylebox("fg");
Ref<Font> font = get_font("font");
- Size2 ms = bg->get_minimum_size() + bg->get_center_size();
+ Size2 minimum_size = bg->get_minimum_size();
+ minimum_size.height = MAX(minimum_size.height, fg->get_minimum_size().height);
+ minimum_size.width = MAX(minimum_size.width, fg->get_minimum_size().width);
if (percent_visible) {
- ms.height = MAX(ms.height, bg->get_minimum_size().height + font->get_height());
+ minimum_size.height = MAX(minimum_size.height, bg->get_minimum_size().height + font->get_height());
}
- return ms;
+ return minimum_size;
}
void ProgressBar::_notification(int p_what) {
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index cd6c6bb65c..4062e48640 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -71,10 +71,10 @@ void Range::set_value(double p_val) {
p_val = Math::round(p_val);
}
- if (p_val > shared->max - shared->page)
+ if (!shared->allow_greater && p_val > shared->max - shared->page)
p_val = shared->max - shared->page;
- if (p_val < shared->min)
+ if (!shared->allow_lesser && p_val < shared->min)
p_val = shared->min;
if (shared->val == p_val)
@@ -136,9 +136,9 @@ void Range::set_as_ratio(double p_value) {
double v;
- if (shared->exp_ratio && get_min() > 0) {
+ if (shared->exp_ratio && get_min() >= 0) {
- double exp_min = Math::log(get_min()) / Math::log((double)2);
+ double exp_min = get_min() == 0 ? 0.0 : Math::log(get_min()) / Math::log((double)2);
double exp_max = Math::log(get_max()) / Math::log((double)2);
v = Math::pow(2, exp_min + (exp_max - exp_min) * p_value);
} else {
@@ -151,21 +151,24 @@ void Range::set_as_ratio(double p_value) {
v = percent + get_min();
}
}
+ v = CLAMP(v, get_min(), get_max());
set_value(v);
}
double Range::get_as_ratio() const {
- if (shared->exp_ratio && get_min() > 0) {
+ if (shared->exp_ratio && get_min() >= 0) {
- double exp_min = Math::log(get_min()) / Math::log((double)2);
+ double exp_min = get_min() == 0 ? 0.0 : Math::log(get_min()) / Math::log((double)2);
double exp_max = Math::log(get_max()) / Math::log((double)2);
- double v = Math::log(get_value()) / Math::log((double)2);
+ float value = CLAMP(get_value(), shared->min, shared->max);
+ double v = Math::log(value) / Math::log((double)2);
return (v - exp_min) / (exp_max - exp_min);
} else {
- return (get_value() - get_min()) / (get_max() - get_min());
+ float value = CLAMP(get_value(), shared->min, shared->max);
+ return (value - get_min()) / (get_max() - get_min());
}
}
@@ -193,6 +196,8 @@ void Range::unshare() {
nshared->val = shared->val;
nshared->step = shared->step;
nshared->page = shared->page;
+ nshared->allow_greater = shared->allow_greater;
+ nshared->allow_lesser = shared->allow_lesser;
_unref_shared();
_ref_shared(nshared);
}
@@ -236,6 +241,10 @@ void Range::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_using_rounded_values"), &Range::is_using_rounded_values);
ClassDB::bind_method(D_METHOD("set_exp_ratio", "enabled"), &Range::set_exp_ratio);
ClassDB::bind_method(D_METHOD("is_ratio_exp"), &Range::is_ratio_exp);
+ ClassDB::bind_method(D_METHOD("set_allow_greater", "allow"), &Range::set_allow_greater);
+ ClassDB::bind_method(D_METHOD("is_greater_allowed"), &Range::is_greater_allowed);
+ ClassDB::bind_method(D_METHOD("set_allow_lesser", "allow"), &Range::set_allow_lesser);
+ ClassDB::bind_method(D_METHOD("is_lesser_allowed"), &Range::is_lesser_allowed);
ClassDB::bind_method(D_METHOD("share", "with"), &Range::_share);
ClassDB::bind_method(D_METHOD("unshare"), &Range::unshare);
@@ -251,6 +260,8 @@ void Range::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::REAL, "ratio", PROPERTY_HINT_RANGE, "0,1,0.01", 0), "set_as_ratio", "get_as_ratio");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exp_edit"), "set_exp_ratio", "is_ratio_exp");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rounded"), "set_use_rounded_values", "is_using_rounded_values");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "allow_greater"), "set_allow_greater", "is_greater_allowed");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "allow_lesser"), "set_allow_lesser", "is_lesser_allowed");
}
void Range::set_use_rounded_values(bool p_enable) {
@@ -273,6 +284,22 @@ bool Range::is_ratio_exp() const {
return shared->exp_ratio;
}
+void Range::set_allow_greater(bool p_allow) {
+ shared->allow_greater = p_allow;
+}
+
+bool Range::is_greater_allowed() const {
+ return shared->allow_greater;
+}
+
+void Range::set_allow_lesser(bool p_allow) {
+ shared->allow_lesser = p_allow;
+}
+
+bool Range::is_lesser_allowed() const {
+ return shared->allow_lesser;
+}
+
Range::Range() {
shared = memnew(Shared);
shared->min = 0;
@@ -282,6 +309,8 @@ Range::Range() {
shared->page = 0;
shared->owners.insert(this);
shared->exp_ratio = false;
+ shared->allow_greater = false;
+ shared->allow_lesser = false;
_rounded_values = false;
}
diff --git a/scene/gui/range.h b/scene/gui/range.h
index de9383afd8..125f559248 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -43,6 +43,8 @@ class Range : public Control {
double val, min, max;
double step, page;
bool exp_ratio;
+ bool allow_greater;
+ bool allow_lesser;
Set<Range *> owners;
void emit_value_changed();
void emit_changed(const char *p_what = "");
@@ -86,6 +88,12 @@ public:
void set_exp_ratio(bool p_enable);
bool is_ratio_exp() const;
+ void set_allow_greater(bool p_allow);
+ bool is_greater_allowed() const;
+
+ void set_allow_lesser(bool p_allow);
+ bool is_lesser_allowed() const;
+
void share(Range *p_range);
void unshare();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 6bfc4d4dee..f34559fc8d 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -86,11 +86,59 @@ RichTextLabel::Item *RichTextLabel::_get_next_item(Item *p_item, bool p_free) {
return NULL;
}
+RichTextLabel::Item *RichTextLabel::_get_prev_item(Item *p_item, bool p_free) {
+ if (p_free) {
+
+ if (p_item->subitems.size()) {
+
+ return p_item->subitems.back()->get();
+ } else if (!p_item->parent) {
+ return NULL;
+ } else if (p_item->E->prev()) {
+
+ return p_item->E->prev()->get();
+ } else {
+ //go back until something with a prev is found
+ while (p_item->parent && !p_item->E->prev()) {
+ p_item = p_item->parent;
+ }
+
+ if (p_item->parent)
+ return p_item->E->prev()->get();
+ else
+ return NULL;
+ }
+
+ } else {
+ if (p_item->subitems.size() && p_item->type != ITEM_TABLE) {
+
+ return p_item->subitems.back()->get();
+ } else if (p_item->type == ITEM_FRAME) {
+ return NULL;
+ } else if (p_item->E->prev()) {
+
+ return p_item->E->prev()->get();
+ } else {
+ //go back until something with a prev is found
+ while (p_item->type != ITEM_FRAME && !p_item->E->prev()) {
+ p_item = p_item->parent;
+ }
+
+ if (p_item->type != ITEM_FRAME)
+ return p_item->E->prev()->get();
+ else
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
Rect2 RichTextLabel::_get_text_rect() {
Ref<StyleBox> style = get_stylebox("normal");
return Rect2(style->get_offset(), get_size() - style->get_minimum_size());
}
-int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) {
+int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) {
RID ci;
if (r_outside)
@@ -269,10 +317,12 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
int descent = font->get_descent();
Color color;
+ Color font_color_shadow;
bool underline = false;
if (p_mode == PROCESS_DRAW) {
color = _find_color(text, p_base_color);
+ font_color_shadow = _find_color(text, p_font_color_shadow);
underline = _find_underline(text);
if (_find_meta(text, &meta)) {
@@ -284,7 +334,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
}
rchar = 0;
-
+ FontDrawer drawer(font, Color(1, 1, 1));
while (*c) {
int end = 0;
@@ -297,7 +347,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1;
line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1;
}
-
while (c[end] != 0 && !(end && c[end - 1] == ' ' && c[end] != ' ')) {
int cw = font->get_char_size(c[end], c[end + 1]).width;
@@ -314,7 +363,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
end++;
}
-
CHECK_HEIGHT(fh);
ENSURE_WIDTH(w);
@@ -376,16 +424,30 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
if (c[i] == '\t')
visible = false;
- if (selected) {
+ if (visible) {
+ if (selected) {
+ cw = font->get_char_size(c[i], c[i + 1]).x;
+ draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
+ }
- cw = font->get_char_size(c[i], c[i + 1]).x;
- draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
- if (visible)
- font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
+ if (p_font_color_shadow.a > 0) {
+ float x_ofs_shadow = align_ofs + pofs;
+ float y_ofs_shadow = y + lh - line_descent;
+ float move = font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, c[i], c[i + 1], p_font_color_shadow);
+
+ if (p_shadow_as_outline) {
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
+ }
+ x_ofs_shadow += move;
+ }
- } else {
- if (visible)
- cw = font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], color);
+ if (selected) {
+ drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
+ } else {
+ cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], color);
+ }
}
p_char_count++;
@@ -464,6 +526,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
int vseparation = get_constant("table_vseparation");
Color ccolor = _find_color(table, p_base_color);
Vector2 draw_ofs = Point2(wofs, y);
+ Color font_color_shadow = get_color("font_color_shadow");
+ bool use_outline = get_constant("shadow_as_outline");
+ Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
if (p_mode == PROCESS_CACHE) {
@@ -487,7 +552,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
for (int i = 0; i < frame->lines.size(); i++) {
- _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color());
+ _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs);
table->columns[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width);
table->columns[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width);
}
@@ -560,7 +625,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
for (int i = 0; i < frame->lines.size(); i++) {
int ly = 0;
- _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color());
+ _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs);
frame->lines[i].height_cache = ly; //actual height
frame->lines[i].height_accum_cache = ly; //actual height
}
@@ -593,9 +658,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
if (visible) {
if (p_mode == PROCESS_DRAW) {
- nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor);
+ nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs);
} else if (p_mode == PROCESS_POINTER) {
- _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, p_click_pos, r_click_item, r_click_char, r_outside);
+ _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs, p_click_pos, r_click_item, r_click_char, r_outside);
if (r_click_item && *r_click_item) {
RETURN; // exit early
}
@@ -676,9 +741,7 @@ void RichTextLabel::_scroll_changed(double) {
void RichTextLabel::_update_scroll() {
- int total_height = 0;
- if (main->lines.size())
- total_height = main->lines[main->lines.size() - 1].height_accum_cache + get_stylebox("normal")->get_minimum_size().height;
+ int total_height = get_content_height();
bool exceeds = total_height > get_size().height && scroll_active;
@@ -767,12 +830,18 @@ void RichTextLabel::_notification(int p_what) {
int y = (main->lines[from_line].height_accum_cache - main->lines[from_line].height_cache) - ofs;
Ref<Font> base_font = get_font("normal_font");
Color base_color = get_color("default_color");
+ Color font_color_shadow = get_color("font_color_shadow");
+ bool use_outline = get_constant("shadow_as_outline");
+ Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
+
+ float x_ofs = 0;
visible_line_count = 0;
while (y < size.height && from_line < main->lines.size()) {
- visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, Point2i(), NULL, NULL, NULL, total_chars);
+ visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), NULL, NULL, NULL, total_chars);
total_chars += main->lines[from_line].char_count;
+
from_line++;
}
}
@@ -787,6 +856,9 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
Size2 size = get_size();
Rect2 text_rect = _get_text_rect();
int ofs = vscroll->get_value();
+ Color font_color_shadow = get_color("font_color_shadow");
+ bool use_outline = get_constant("shadow_as_outline");
+ Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
//todo, change to binary search
int from_line = 0;
@@ -807,7 +879,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
while (y < text_rect.get_size().height && from_line < p_frame->lines.size()) {
- _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, p_click, r_click_item, r_click_char, r_outside);
+ _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, p_click, r_click_item, r_click_char, r_outside);
if (r_click_item && *r_click_item)
return;
from_line++;
@@ -867,9 +939,9 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
// Erase previous selection.
if (selection.active) {
selection.from = NULL;
- selection.from_char = NULL;
+ selection.from_char = '\0';
selection.to = NULL;
- selection.to_char = NULL;
+ selection.to_char = '\0';
selection.active = false;
update();
@@ -1182,13 +1254,16 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
//validate invalid lines
Size2 size = get_size();
Rect2 text_rect = _get_text_rect();
+ Color font_color_shadow = get_color("font_color_shadow");
+ bool use_outline = get_constant("shadow_as_outline");
+ Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
Ref<Font> base_font = get_font("normal_font");
for (int i = p_frame->first_invalid_line; i < p_frame->lines.size(); i++) {
int y = 0;
- _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color());
+ _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color(), font_color_shadow, use_outline, shadow_ofs);
p_frame->lines[i].height_cache = y;
p_frame->lines[i].height_accum_cache = y;
@@ -1862,7 +1937,7 @@ void RichTextLabel::set_selection_enabled(bool p_enabled) {
}
}
-bool RichTextLabel::search(const String &p_string, bool p_from_selection) {
+bool RichTextLabel::search(const String &p_string, bool p_from_selection, bool p_search_previous) {
ERR_FAIL_COND_V(!selection.enabled, false);
Item *it = main;
@@ -1911,7 +1986,10 @@ bool RichTextLabel::search(const String &p_string, bool p_from_selection) {
}
}
- it = _get_next_item(it, true);
+ if (p_search_previous)
+ it = _get_prev_item(it, true);
+ else
+ it = _get_next_item(it, true);
charidx = 0;
}
@@ -2030,6 +2108,13 @@ float RichTextLabel::get_percent_visible() const {
return percent_visible;
}
+int RichTextLabel::get_content_height() {
+ int total_height = 0;
+ if (main->lines.size())
+ total_height = main->lines[main->lines.size() - 1].height_accum_cache + get_stylebox("normal")->get_minimum_size().height;
+ return total_height;
+}
+
void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &RichTextLabel::_gui_input);
@@ -2096,6 +2181,8 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_line_count"), &RichTextLabel::get_line_count);
ClassDB::bind_method(D_METHOD("get_visible_line_count"), &RichTextLabel::get_visible_line_count);
+ ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height);
+
ADD_GROUP("BBCode", "bbcode_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bbcode_text", PROPERTY_HINT_MULTILINE_TEXT), "set_bbcode", "get_bbcode");
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 83938cff61..af368af46a 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -190,7 +190,6 @@ private:
struct ItemNewline : public Item {
- int line; // FIXME: Overriding base's line ?
ItemNewline() { type = ITEM_NEWLINE; }
};
@@ -270,7 +269,7 @@ private:
int visible_characters;
float percent_visible;
- int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Point2i &p_click_pos = Point2i(), Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL, int p_char_count = 0);
+ int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL, int p_char_count = 0);
void _find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL);
Ref<Font> _find_font(Item *p_item);
@@ -285,6 +284,7 @@ private:
void _gui_input(Ref<InputEvent> p_event);
Item *_get_next_item(Item *p_item, bool p_free = false);
+ Item *_get_prev_item(Item *p_item, bool p_free = false);
Rect2 _get_text_rect();
@@ -334,12 +334,14 @@ public:
void set_tab_size(int p_spaces);
int get_tab_size() const;
- bool search(const String &p_string, bool p_from_selection = false);
+ bool search(const String &p_string, bool p_from_selection = false, bool p_search_previous = false);
void scroll_to_line(int p_line);
int get_line_count() const;
int get_visible_line_count() const;
+ int get_content_height();
+
VScrollBar *get_v_scroll() { return vscroll; }
virtual CursorShape get_cursor_shape(const Point2 &p_pos) const;
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index a1dcf3b002..2dd5c64378 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -37,6 +37,7 @@ bool ScrollContainer::clips_input() const {
Size2 ScrollContainer::get_minimum_size() const {
+ Ref<StyleBox> sb = get_stylebox("bg");
Size2 min_size;
for (int i = 0; i < get_child_count(); i++) {
@@ -64,8 +65,9 @@ Size2 ScrollContainer::get_minimum_size() const {
if (v_scroll->is_visible_in_tree()) {
min_size.x += v_scroll->get_minimum_size().x;
}
+ min_size += sb->get_minimum_size();
return min_size;
-};
+}
void ScrollContainer::_cancel_drag() {
set_physics_process_internal(false);
@@ -233,6 +235,12 @@ void ScrollContainer::_notification(int p_what) {
child_max_size = Size2(0, 0);
Size2 size = get_size();
+ Point2 ofs;
+
+ Ref<StyleBox> sb = get_stylebox("bg");
+ size -= sb->get_minimum_size();
+ ofs += sb->get_offset();
+
if (h_scroll->is_visible_in_tree())
size.y -= h_scroll->get_minimum_size().y;
@@ -268,6 +276,7 @@ void ScrollContainer::_notification(int p_what) {
else
r.size.height = minsize.height;
}
+ r.position += ofs;
fit_child_in_rect(c, r);
}
update();
@@ -275,6 +284,9 @@ void ScrollContainer::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
+ Ref<StyleBox> sb = get_stylebox("bg");
+ draw_style_box(sb, Rect2(Vector2(), get_size()));
+
update_scrollbars();
}
@@ -353,6 +365,8 @@ void ScrollContainer::_notification(int p_what) {
void ScrollContainer::update_scrollbars() {
Size2 size = get_size();
+ Ref<StyleBox> sb = get_stylebox("bg");
+ size -= sb->get_minimum_size();
Size2 hmin = h_scroll->get_combined_minimum_size();
Size2 vmin = v_scroll->get_combined_minimum_size();
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index bf7033e8ba..c38c411333 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -33,13 +33,6 @@
#include "label.h"
#include "margin_container.h"
-struct _MinSizeCache {
-
- int min_size;
- bool will_stretch;
- int final_size;
-};
-
Control *SplitContainer::_getch(int p_idx) const {
int idx = 0;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 24a13db3c3..c6ff8489c0 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -117,7 +117,6 @@ void TextEdit::Text::set_indent_size(int p_indent_size) {
void TextEdit::Text::_update_line_cache(int p_line) const {
int w = 0;
- int tab_w = font->get_char_size(' ').width * indent_size;
int len = text[p_line].data.length();
const CharType *str = text[p_line].data.c_str();
@@ -125,22 +124,13 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
//update width
for (int i = 0; i < len; i++) {
- if (str[i] == '\t') {
-
- int left = w % tab_w;
- if (left == 0)
- w += tab_w;
- else
- w += tab_w - w % tab_w; // is right...
-
- } else {
-
- w += font->get_char_size(str[i], str[i + 1]).width;
- }
+ w += get_char_width(str[i], str[i + 1], w);
}
text[p_line].width_cache = w;
+ text[p_line].wrap_amount_cache = -1;
+
//update regions
text[p_line].region_info.clear();
@@ -242,10 +232,32 @@ int TextEdit::Text::get_line_width(int p_line) const {
return text[p_line].width_cache;
}
-void TextEdit::Text::clear_caches() {
+void TextEdit::Text::set_line_wrap_amount(int p_line, int p_wrap_amount) const {
+
+ ERR_FAIL_INDEX(p_line, text.size());
+
+ text[p_line].wrap_amount_cache = p_wrap_amount;
+}
+
+int TextEdit::Text::get_line_wrap_amount(int p_line) const {
+
+ ERR_FAIL_INDEX_V(p_line, text.size(), -1);
+
+ return text[p_line].wrap_amount_cache;
+}
+
+void TextEdit::Text::clear_width_cache() {
- for (int i = 0; i < text.size(); i++)
+ for (int i = 0; i < text.size(); i++) {
text[i].width_cache = -1;
+ }
+}
+
+void TextEdit::Text::clear_wrap_cache() {
+
+ for (int i = 0; i < text.size(); i++) {
+ text[i].wrap_amount_cache = -1;
+ }
}
void TextEdit::Text::clear() {
@@ -270,6 +282,7 @@ void TextEdit::Text::set(int p_line, const String &p_text) {
ERR_FAIL_INDEX(p_line, text.size());
text[p_line].width_cache = -1;
+ text[p_line].wrap_amount_cache = -1;
text[p_line].data = p_text;
}
@@ -280,6 +293,7 @@ void TextEdit::Text::insert(int p_at, const String &p_text) {
line.breakpoint = false;
line.hidden = false;
line.width_cache = -1;
+ line.wrap_amount_cache = -1;
line.data = p_text;
text.insert(p_at, line);
}
@@ -288,6 +302,25 @@ void TextEdit::Text::remove(int p_at) {
text.remove(p_at);
}
+int TextEdit::Text::get_char_width(CharType c, CharType next_c, int px) const {
+
+ int tab_w = font->get_char_size(' ').width * indent_size;
+ int w = 0;
+
+ if (c == '\t') {
+
+ int left = px % tab_w;
+ if (left == 0)
+ w = tab_w;
+ else
+ w = tab_w - px % tab_w; // is right...
+ } else {
+
+ w = font->get_char_size(c, next_c).width;
+ }
+ return w;
+}
+
void TextEdit::_update_scrollbars() {
Size2 size = get_size();
@@ -302,9 +335,12 @@ void TextEdit::_update_scrollbars() {
int hscroll_rows = ((hmin.height - 1) / get_row_height()) + 1;
int visible_rows = get_visible_rows();
- int num_rows = MAX(visible_rows, num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), MIN(visible_rows, text.size() - 1 - cursor.line_ofs)));
- int total_rows = (is_hiding_enabled() ? get_total_unhidden_rows() : text.size());
+ int first_vis_line = get_first_visible_line();
+ int wi;
+ int num_rows = MAX(visible_rows, num_lines_from_rows(first_vis_line, cursor.wrap_ofs, visible_rows, wi));
+
+ int total_rows = get_total_visible_rows();
if (scroll_past_end_of_file_enabled) {
total_rows += visible_rows - 1;
}
@@ -350,28 +386,24 @@ void TextEdit::_update_scrollbars() {
if (use_vscroll) {
v_scroll->show();
- v_scroll->set_max(total_rows);
- v_scroll->set_page(visible_rows);
+ v_scroll->set_max(total_rows + get_visible_rows_offset());
+ v_scroll->set_page(visible_rows + get_visible_rows_offset());
if (smooth_scroll_enabled) {
v_scroll->set_step(0.25);
} else {
v_scroll->set_step(1);
}
-
- update_line_scroll_pos();
- if (fabs(v_scroll->get_value() - get_line_scroll_pos()) >= 1) {
- cursor.line_ofs += v_scroll->get_value() - get_line_scroll_pos();
- }
+ set_v_scroll(get_v_scroll());
} else {
cursor.line_ofs = 0;
- line_scroll_pos = 0;
+ cursor.wrap_ofs = 0;
v_scroll->set_value(0);
v_scroll->hide();
}
- if (use_hscroll) {
+ if (use_hscroll && !is_wrap_enabled()) {
h_scroll->show();
h_scroll->set_max(total_width);
@@ -422,8 +454,8 @@ void TextEdit::_update_selection_mode_pointer() {
select(selection.selecting_line, selection.selecting_column, row, col);
- cursor_set_line(row);
- cursor_set_column(col);
+ cursor_set_line(row, false);
+ cursor_set_column(col, false);
update();
click_select_held->start();
@@ -476,7 +508,7 @@ void TextEdit::_update_selection_mode_word() {
cursor_set_column(selection.to_column);
}
}
- cursor_set_line(row);
+ cursor_set_line(row, false);
update();
click_select_held->start();
@@ -491,15 +523,15 @@ void TextEdit::_update_selection_mode_line() {
col = 0;
if (row < selection.selecting_line) {
// cursor is above us
- cursor_set_line(row - 1);
+ cursor_set_line(row - 1, false);
selection.selecting_column = text[selection.selecting_line].length();
} else {
// cursor is below us
- cursor_set_line(row + 1);
+ cursor_set_line(row + 1, false);
selection.selecting_column = 0;
col = text[row].length();
}
- cursor_set_column(0);
+ cursor_set_column(0, false);
select(selection.selecting_line, selection.selecting_column, row, col);
update();
@@ -517,13 +549,13 @@ void TextEdit::_notification(int p_what) {
MessageQueue::get_singleton()->push_call(this, "_cursor_changed_emit");
if (text_changed_dirty)
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
-
+ update_wrap_at();
} break;
case NOTIFICATION_RESIZED: {
cache.size = get_size();
- adjust_viewport_to_cursor();
-
+ _update_scrollbars();
+ update_wrap_at();
} break;
case NOTIFICATION_THEME_CHANGED: {
@@ -540,17 +572,17 @@ void TextEdit::_notification(int p_what) {
update();
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- if (scrolling && v_scroll->get_value() != target_v_scroll) {
- double target_y = target_v_scroll - v_scroll->get_value();
+ if (scrolling && get_v_scroll() != target_v_scroll) {
+ double target_y = target_v_scroll - get_v_scroll();
double dist = sqrt(target_y * target_y);
double vel = ((target_y / dist) * v_scroll_speed) * get_physics_process_delta_time();
if (Math::abs(vel) >= dist) {
- v_scroll->set_value(target_v_scroll);
+ set_v_scroll(target_v_scroll);
scrolling = false;
set_physics_process_internal(false);
} else {
- v_scroll->set_value(v_scroll->get_value() + vel);
+ set_v_scroll(get_v_scroll() + vel);
}
} else {
scrolling = false;
@@ -777,11 +809,14 @@ void TextEdit::_notification(int p_what) {
String highlighted_text = get_selection_text();
String line_num_padding = line_numbers_zero_padded ? "0" : " ";
- update_line_scroll_pos();
- int line = cursor.line_ofs - 1;
- // another row may be visible during smooth scrolling
+ int cursor_wrap_index = get_cursor_wrap_index();
+
+ FontDrawer drawer(cache.font, Color(1, 1, 1));
+
+ int line = get_first_visible_line() - 1;
int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0);
+ draw_amount += times_line_wraps(line + 1);
for (int i = 0; i < draw_amount; i++) {
line++;
@@ -799,269 +834,360 @@ void TextEdit::_notification(int p_what) {
if (line < 0 || line >= (int)text.size())
continue;
- const String &str = text[line];
+ const String &fullstr = text[line];
- int char_margin = xmargin_beg - cursor.x_ofs;
- int char_ofs = 0;
-
- int ofs_readonly = 0;
- int ofs_x = 0;
+ Map<int, HighlighterInfo> color_map;
+ if (syntax_coloring) {
+ color_map = _get_line_syntax_highlighting(line);
+ }
+ // ensure we at least use the font color
+ Color current_color = cache.font_color;
if (readonly) {
- ofs_readonly = cache.style_readonly->get_offset().y / 2;
- ofs_x = cache.style_readonly->get_offset().x / 2;
+ current_color.a *= readonly_alpha;
}
- int ofs_y = (i * get_row_height() + cache.line_spacing / 2) + ofs_readonly;
- if (smooth_scroll_enabled)
- ofs_y -= ((v_scroll->get_value() - get_line_scroll_pos()) * get_row_height());
bool underlined = false;
- // check if line contains highlighted word
- int highlighted_text_col = -1;
- int search_text_col = -1;
- int highlighted_word_col = -1;
+ int line_wrap_amount = times_line_wraps(line);
+ int last_wrap_column = 0;
+ Vector<String> wrap_rows = get_wrap_rows_text(line);
+
+ for (int line_wrap_index = 0; line_wrap_index < line_wrap_amount + 1; line_wrap_index++) {
+ if (line_wrap_index != 0) {
+ i++;
+ if (i >= draw_amount)
+ break;
+ }
+
+ const String &str = wrap_rows[line_wrap_index];
+ int indent_px = line_wrap_index != 0 ? get_indent_level(line) * cache.font->get_char_size(' ').width : 0;
- if (!search_text.empty())
- search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0);
+ if (line_wrap_index > 0)
+ last_wrap_column += wrap_rows[line_wrap_index - 1].length();
- if (highlighted_text.length() != 0 && highlighted_text != search_text)
- highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
+ int char_margin = xmargin_beg - cursor.x_ofs;
+ char_margin += indent_px;
+ int char_ofs = 0;
- if (select_identifiers_enabled && highlighted_word.length() != 0) {
- if (_is_char(highlighted_word[0])) {
- highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
+ int ofs_readonly = 0;
+ int ofs_x = 0;
+ if (readonly) {
+ ofs_readonly = cache.style_readonly->get_offset().y / 2;
+ ofs_x = cache.style_readonly->get_offset().x / 2;
}
- }
- if (text.is_marked(line)) {
+ int ofs_y = (i * get_row_height() + cache.line_spacing / 2) + ofs_readonly;
+ ofs_y -= cursor.wrap_ofs * get_row_height();
+ if (smooth_scroll_enabled)
+ ofs_y += (-get_v_scroll_offset()) * get_row_height();
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.mark_color);
- }
+ // check if line contains highlighted word
+ int highlighted_text_col = -1;
+ int search_text_col = -1;
+ int highlighted_word_col = -1;
- if (str.length() == 0) {
- // draw line background if empty as we won't loop at at all
- if (line == cursor.line && highlight_current_line) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, get_row_height()), cache.current_line_color);
+ if (!search_text.empty())
+ search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0);
+
+ if (highlighted_text.length() != 0 && highlighted_text != search_text)
+ highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
+
+ if (select_identifiers_enabled && highlighted_word.length() != 0) {
+ if (_is_char(highlighted_word[0])) {
+ highlighted_word_col = _get_column_pos_of_word(highlighted_word, fullstr, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
+ }
}
- // give visual indication of empty selected line
- if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) {
- int char_w = cache.font->get_char_size(' ').width;
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, get_row_height()), cache.selection_color);
+ if (text.is_marked(line)) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.mark_color);
}
- } else {
- // if it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later.
- if (line == cursor.line && highlight_current_line) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg + ofs_x, get_row_height()), cache.current_line_color);
+
+ if (str.length() == 0) {
+ // draw line background if empty as we won't loop at at all
+ if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(ofs_x, ofs_y, xmargin_end, get_row_height()), cache.current_line_color);
+ }
+
+ // give visual indication of empty selected line
+ if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) {
+ int char_w = cache.font->get_char_size(' ').width;
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, char_w, get_row_height()), cache.selection_color);
+ }
+ } else {
+ // if it has text, then draw current line marker in the margin, as line number etc will draw over it, draw the rest of line marker later.
+ if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(0, ofs_y, xmargin_beg, get_row_height()), cache.current_line_color);
+ }
}
- }
- if (text.is_breakpoint(line) && !draw_breakpoint_gutter) {
+ if (line_wrap_index == 0) {
+ // only do these if we are on the first wrapped part of a line
+
+ if (text.is_breakpoint(line) && !draw_breakpoint_gutter) {
#ifdef TOOLS_ENABLED
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.breakpoint_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y + get_row_height() - EDSCALE, xmargin_end - xmargin_beg, EDSCALE), cache.breakpoint_color);
#else
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.breakpoint_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - xmargin_beg, get_row_height()), cache.breakpoint_color);
#endif
- }
+ }
- // draw breakpoint marker
- if (text.is_breakpoint(line)) {
- if (draw_breakpoint_gutter) {
- int vertical_gap = (get_row_height() * 40) / 100;
- int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
- int marker_height = get_row_height() - (vertical_gap * 2);
- int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2);
- // no transparency on marker
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2, ofs_y + vertical_gap, marker_width, marker_height), Color(cache.breakpoint_color.r, cache.breakpoint_color.g, cache.breakpoint_color.b));
- }
- }
+ // draw breakpoint marker
+ if (text.is_breakpoint(line)) {
+ if (draw_breakpoint_gutter) {
+ int vertical_gap = (get_row_height() * 40) / 100;
+ int horizontal_gap = (cache.breakpoint_gutter_width * 30) / 100;
+ int marker_height = get_row_height() - (vertical_gap * 2);
+ int marker_width = cache.breakpoint_gutter_width - (horizontal_gap * 2);
+ // no transparency on marker
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cache.style_normal->get_margin(MARGIN_LEFT) + horizontal_gap - 2, ofs_y + vertical_gap, marker_width, marker_height), Color(cache.breakpoint_color.r, cache.breakpoint_color.g, cache.breakpoint_color.b));
+ }
+ }
- // draw fold markers
- if (draw_fold_gutter) {
- int horizontal_gap = (cache.fold_gutter_width * 30) / 100;
- int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w;
- if (is_folded(line)) {
- int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2;
- int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2;
- cache.folded_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
- } else if (can_fold(line)) {
- int xofs = -cache.can_fold_icon->get_width() / 2 - horizontal_gap + 3;
- int yofs = (get_row_height() - cache.can_fold_icon->get_height()) / 2;
- cache.can_fold_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
- }
- }
+ // draw fold markers
+ if (draw_fold_gutter) {
+ int horizontal_gap = (cache.fold_gutter_width * 30) / 100;
+ int gutter_left = cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + cache.line_number_w;
+ if (is_folded(line)) {
+ int xofs = horizontal_gap - (cache.can_fold_icon->get_width()) / 2;
+ int yofs = (get_row_height() - cache.folded_icon->get_height()) / 2;
+ cache.folded_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
+ } else if (can_fold(line)) {
+ int xofs = -cache.can_fold_icon->get_width() / 2 - horizontal_gap + 3;
+ int yofs = (get_row_height() - cache.can_fold_icon->get_height()) / 2;
+ cache.can_fold_icon->draw(ci, Point2(gutter_left + xofs + ofs_x, ofs_y + yofs), cache.code_folding_color);
+ }
+ }
+
+ // draw line numbers
+ if (cache.line_number_w) {
+ String fc = String::num(line + 1);
+ while (fc.length() < line_number_char_count) {
+ fc = line_num_padding + fc;
+ }
- if (cache.line_number_w) {
- String fc = String::num(line + 1);
- while (fc.length() < line_number_char_count) {
- fc = line_num_padding + fc;
+ cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color);
+ }
}
- cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color);
- }
+ //loop through characters in one line
+ for (int j = 0; j < str.length(); j++) {
- //loop through characters in one line
- Map<int, HighlighterInfo> color_map;
- if (syntax_coloring) {
- color_map = _get_line_syntax_highlighting(line);
- }
+ if (syntax_coloring) {
+ if (color_map.has(last_wrap_column + j)) {
+ current_color = color_map[last_wrap_column + j].color;
+ if (readonly) {
+ current_color.a *= readonly_alpha;
+ }
+ }
+ color = current_color;
+ }
- // ensure we at least use the font color
- Color current_color = cache.font_color;
- if (readonly) {
- current_color.a *= readonly_alpha;
- }
- for (int j = 0; j < str.length(); j++) {
+ int char_w;
+
+ //handle tabulator
+ char_w = text.get_char_width(str[j], str[j + 1], char_ofs);
+
+ if ((char_ofs + char_margin) < xmargin_beg) {
+ char_ofs += char_w;
- if (syntax_coloring) {
- if (color_map.has(j)) {
- current_color = color_map[j].color;
- if (readonly) {
- current_color.a *= readonly_alpha;
+ // line highlighting handle horizontal clipping
+ if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
+
+ if (j == str.length() - 1) {
+ // end of line when last char is skipped
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color);
+ } else if ((char_ofs + char_margin) > xmargin_beg) {
+ // char next to margin is skipped
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, (char_ofs + char_margin) - (xmargin_beg + ofs_x), get_row_height()), cache.current_line_color);
+ }
}
+ continue;
}
- color = current_color;
- }
- int char_w;
- //handle tabulator
+ if ((char_ofs + char_margin + char_w) >= xmargin_end) {
+ if (syntax_coloring)
+ continue;
+ else
+ break;
+ }
- if (str[j] == '\t') {
- int left = char_ofs % tab_w;
- if (left == 0)
- char_w = tab_w;
- else
- char_w = tab_w - char_ofs % tab_w; // is right...
+ bool in_search_result = false;
- } else {
- char_w = cache.font->get_char_size(str[j], str[j + 1]).width;
- }
+ if (search_text_col != -1) {
+ // if we are at the end check for new search result on same line
+ if (j >= search_text_col + search_text.length())
+ search_text_col = _get_column_pos_of_word(search_text, str, search_flags, j);
- if ((char_ofs + char_margin) < xmargin_beg) {
- char_ofs += char_w;
+ in_search_result = j >= search_text_col && j < search_text_col + search_text.length();
- // line highlighting handle horizontal clipping
- if (line == cursor.line && highlight_current_line) {
+ if (in_search_result) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.search_result_color);
+ }
+ }
- if (j == str.length() - 1) {
- // end of line when last char is skipped
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, xmargin_end - (xmargin_beg + ofs_x), get_row_height()), cache.current_line_color);
+ //current line highlighting
+ bool in_selection = (selection.active && line >= selection.from_line && line <= selection.to_line && (line > selection.from_line || last_wrap_column + j >= selection.from_column) && (line < selection.to_line || last_wrap_column + j < selection.to_column));
- } else if ((char_ofs + char_margin) > xmargin_beg) {
- // char next to margin is skipped
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(xmargin_beg + ofs_x, ofs_y, (char_ofs + char_margin) - xmargin_beg, get_row_height()), cache.current_line_color);
+ if (line == cursor.line && cursor_wrap_index == line_wrap_index && highlight_current_line) {
+ // draw the wrap indent offset highlight
+ if (line_wrap_index != 0 && j == 0) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin - indent_px, ofs_y, (char_ofs + char_margin), get_row_height()), cache.current_line_color);
+ }
+ // if its the last char draw to end of the line
+ if (j == str.length() - 1) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + char_w, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color);
+ }
+ // actual text
+ if (!in_selection) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.current_line_color);
}
}
- continue;
- }
- if ((char_ofs + char_margin + char_w) >= xmargin_end) {
- if (syntax_coloring)
- continue;
- else
- break;
- }
-
- bool in_search_result = false;
+ if (in_selection) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.selection_color);
+ }
- if (search_text_col != -1) {
- // if we are at the end check for new search result on same line
- if (j >= search_text_col + search_text.length())
- search_text_col = _get_column_pos_of_word(search_text, str, search_flags, j);
+ if (in_search_result) {
+ Color border_color = (line == search_result_line && j >= search_result_col && j < search_result_col + search_text.length()) ? cache.font_color : cache.search_result_border_color;
- in_search_result = j >= search_text_col && j < search_text_col + search_text.length();
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, 1)), border_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y + get_row_height() - 1), Size2i(char_w, 1)), border_color);
- if (in_search_result) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin, ofs_y), Size2i(char_w, get_row_height())), cache.search_result_color);
+ if (j == search_text_col)
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(1, get_row_height())), border_color);
+ if (j == search_text_col + search_text.length() - 1)
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + char_w + ofs_x - 1, ofs_y), Size2i(1, get_row_height())), border_color);
}
- }
- //current line highlighting
- bool in_selection = (selection.active && line >= selection.from_line && line <= selection.to_line && (line > selection.from_line || j >= selection.from_column) && (line < selection.to_line || j < selection.to_column));
+ if (highlight_all_occurrences) {
+ if (highlighted_text_col != -1) {
+
+ // if we are at the end check for new word on same line
+ if (j > highlighted_text_col + highlighted_text.length()) {
+ highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j);
+ }
+
+ bool in_highlighted_word = (j >= highlighted_text_col && j < highlighted_text_col + highlighted_text.length());
- if (line == cursor.line && highlight_current_line) {
- // if its the last char draw to end of the line
- if (j == str.length() - 1) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(char_ofs + char_margin + char_w, ofs_y, xmargin_end - (char_ofs + char_margin + char_w), get_row_height()), cache.current_line_color);
+ // if this is the original highlighted text we don't want to highlight it again
+ if (cursor.line == line && cursor_wrap_index == line_wrap_index && (cursor.column >= highlighted_text_col && cursor.column <= highlighted_text_col + highlighted_text.length())) {
+ in_highlighted_word = false;
+ }
+
+ if (in_highlighted_word) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.word_highlighted_color);
+ }
+ }
}
- // actual text
- if (!in_selection) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.current_line_color);
+
+ if (highlighted_word_col != -1) {
+ if (j + last_wrap_column > highlighted_word_col + highlighted_word.length()) {
+ highlighted_word_col = _get_column_pos_of_word(highlighted_word, fullstr, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j + last_wrap_column);
+ }
+ underlined = (j + last_wrap_column >= highlighted_word_col && j + last_wrap_column < highlighted_word_col + highlighted_word.length());
}
- }
- if (in_selection) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.selection_color);
- }
+ if (brace_matching_enabled) {
+ if ((brace_open_match_line == line && brace_open_match_column == last_wrap_column + j) ||
+ (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_open_matching || brace_open_mismatch))) {
- if (in_search_result) {
- Color border_color = (line == search_result_line && j >= search_result_col && j < search_result_col + search_text.length()) ? cache.font_color : cache.search_result_border_color;
+ if (brace_open_mismatch)
+ color = cache.brace_mismatch_color;
+ drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ }
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, 1)), border_color);
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y + get_row_height() - 1), Size2i(char_w, 1)), border_color);
+ if ((brace_close_match_line == line && brace_close_match_column == last_wrap_column + j) ||
+ (cursor.column == last_wrap_column + j + 1 && cursor.line == line && cursor_wrap_index == line_wrap_index && (brace_close_matching || brace_close_mismatch))) {
- if (j == search_text_col)
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(1, get_row_height())), border_color);
- if (j == search_text_col + search_text.length() - 1)
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + char_w + ofs_x - 1, ofs_y), Size2i(1, get_row_height())), border_color);
- }
+ if (brace_close_mismatch)
+ color = cache.brace_mismatch_color;
+ drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ }
+ }
+
+ if (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index) {
- if (highlight_all_occurrences) {
- if (highlighted_text_col != -1) {
+ cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y);
- // if we are at the end check for new word on same line
- if (j > highlighted_text_col + highlighted_text.length()) {
- highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j);
+ if (insert_mode) {
+ cursor_pos.y += (get_row_height() - 3);
}
- bool in_highlighted_word = (j >= highlighted_text_col && j < highlighted_text_col + highlighted_text.length());
+ int caret_w = (str[j] == '\t') ? cache.font->get_char_size(' ').width : char_w;
+ if (ime_text.length() > 0) {
+ int ofs = 0;
+ while (true) {
+ if (ofs >= ime_text.length())
+ break;
+
+ CharType cchar = ime_text[ofs];
+ CharType next = ime_text[ofs + 1];
+ int im_char_width = cache.font->get_char_size(cchar, next).width;
- /* if this is the original highlighted text we don't want to highlight it again */
- if (cursor.line == line && (cursor.column >= highlighted_text_col && cursor.column <= highlighted_text_col + highlighted_text.length())) {
- in_highlighted_word = false;
- }
+ if ((char_ofs + char_margin + im_char_width) >= xmargin_end)
+ break;
+
+ bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y;
+ if (selected) {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 3)), color);
+ } else {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
+ }
+
+ drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
- if (in_highlighted_word) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(char_ofs + char_margin + ofs_x, ofs_y), Size2i(char_w, get_row_height())), cache.word_highlighted_color);
+ char_ofs += im_char_width;
+ ofs++;
+ }
+ }
+ if (ime_text.length() == 0) {
+ if (draw_caret) {
+ if (insert_mode) {
+ int caret_h = (block_caret) ? 4 : 1;
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, caret_h)), cache.caret_color);
+ } else {
+ caret_w = (block_caret) ? caret_w : 1;
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color);
+ }
+ }
}
}
- }
- if (highlighted_word_col != -1) {
- if (j > highlighted_word_col + highlighted_word.length()) {
- highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j);
+ if (cursor.column == last_wrap_column + j && cursor.line == line && cursor_wrap_index == line_wrap_index && block_caret && draw_caret && !insert_mode) {
+ color = cache.caret_background_color;
+ } else if (!syntax_coloring && block_caret) {
+ color = cache.font_color;
+ color.a *= readonly_alpha;
}
- underlined = (j >= highlighted_word_col && j < highlighted_word_col + highlighted_word.length());
- }
- if (brace_matching_enabled) {
- if ((brace_open_match_line == line && brace_open_match_column == j) ||
- (cursor.column == j && cursor.line == line && (brace_open_matching || brace_open_mismatch))) {
-
- if (brace_open_mismatch)
- color = cache.brace_mismatch_color;
- cache.font->draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ if (str[j] >= 32) {
+ int w = drawer.draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ if (underlined) {
+ draw_rect(Rect2(char_ofs + char_margin + ofs_x, ofs_y + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ }
+ } else if (draw_tabs && str[j] == '\t') {
+ int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2;
+ cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color);
}
- if (
- (brace_close_match_line == line && brace_close_match_column == j) ||
- (cursor.column == j + 1 && cursor.line == line && (brace_close_matching || brace_close_mismatch))) {
+ char_ofs += char_w;
- if (brace_close_mismatch)
- color = cache.brace_mismatch_color;
- cache.font->draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), '_', str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
+ if (line_wrap_index == line_wrap_amount && j == str.length() - 1 && is_folded(line)) {
+ int yofs = (get_row_height() - cache.folded_eol_icon->get_height()) / 2;
+ int xofs = cache.folded_eol_icon->get_width() / 2;
+ Color eol_color = cache.code_folding_color;
+ eol_color.a = 1;
+ cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs + ofs_x, ofs_y + yofs), eol_color);
}
}
- if (cursor.column == j && cursor.line == line) {
+ if (cursor.column == last_wrap_column + str.length() && cursor.line == line && cursor_wrap_index == line_wrap_index && (char_ofs + char_margin) >= xmargin_beg) {
cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y);
if (insert_mode) {
cursor_pos.y += (get_row_height() - 3);
}
-
- int caret_w = (str[j] == '\t') ? cache.font->get_char_size(' ').width : char_w;
if (ime_text.length() > 0) {
int ofs = 0;
while (true) {
@@ -1082,7 +1208,7 @@ void TextEdit::_notification(int p_what) {
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
}
- cache.font->draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
+ drawer.draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
char_ofs += im_char_width;
ofs++;
@@ -1091,92 +1217,17 @@ void TextEdit::_notification(int p_what) {
if (ime_text.length() == 0) {
if (draw_caret) {
if (insert_mode) {
+ int char_w = cache.font->get_char_size(' ').width;
int caret_h = (block_caret) ? 4 : 1;
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, caret_h)), cache.caret_color);
+ VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(char_w, caret_h)), cache.caret_color);
} else {
- caret_w = (block_caret) ? caret_w : 1;
+ int char_w = cache.font->get_char_size(' ').width;
+ int caret_w = (block_caret) ? char_w : 1;
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color);
}
}
}
}
-
- if (cursor.column == j && cursor.line == line && block_caret && draw_caret && !insert_mode) {
- color = cache.caret_background_color;
- } else if (!syntax_coloring && block_caret) {
- color = cache.font_color;
- color.a *= readonly_alpha;
- }
-
- if (str[j] >= 32) {
- int w = cache.font->draw_char(ci, Point2i(char_ofs + char_margin + ofs_x, ofs_y + ascent), str[j], str[j + 1], in_selection && override_selected_font_color ? cache.font_selected_color : color);
- if (underlined) {
- draw_rect(Rect2(char_ofs + char_margin + ofs_x, ofs_y + ascent + 2, w, 1), in_selection && override_selected_font_color ? cache.font_selected_color : color);
- }
- }
-
- else if (draw_tabs && str[j] == '\t') {
- int yofs = (get_row_height() - cache.tab_icon->get_height()) / 2;
- cache.tab_icon->draw(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + yofs), in_selection && override_selected_font_color ? cache.font_selected_color : color);
- }
-
- char_ofs += char_w;
-
- if (j == str.length() - 1 && is_folded(line)) {
- int yofs = (get_row_height() - cache.folded_eol_icon->get_height()) / 2;
- int xofs = cache.folded_eol_icon->get_width() / 2;
- Color eol_color = cache.code_folding_color;
- eol_color.a = 1;
- cache.folded_eol_icon->draw(ci, Point2(char_ofs + char_margin + xofs + ofs_x, ofs_y + yofs), eol_color);
- }
- }
-
- if (cursor.column == str.length() && cursor.line == line && (char_ofs + char_margin) >= xmargin_beg) {
-
- cursor_pos = Point2i(char_ofs + char_margin + ofs_x, ofs_y);
-
- if (insert_mode) {
- cursor_pos.y += (get_row_height() - 3);
- }
- if (ime_text.length() > 0) {
- int ofs = 0;
- while (true) {
- if (ofs >= ime_text.length())
- break;
-
- CharType cchar = ime_text[ofs];
- CharType next = ime_text[ofs + 1];
- int im_char_width = cache.font->get_char_size(cchar, next).width;
-
- if ((char_ofs + char_margin + im_char_width) >= xmargin_end)
- break;
-
- bool selected = ofs >= ime_selection.x && ofs < ime_selection.x + ime_selection.y;
- if (selected) {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 3)), color);
- } else {
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(char_ofs + char_margin, ofs_y + get_row_height()), Size2(im_char_width, 1)), color);
- }
-
- cache.font->draw_char(ci, Point2(char_ofs + char_margin + ofs_x, ofs_y + ascent), cchar, next, color);
-
- char_ofs += im_char_width;
- ofs++;
- }
- }
- if (ime_text.length() == 0) {
- if (draw_caret) {
- if (insert_mode) {
- int char_w = cache.font->get_char_size(' ').width;
- int caret_h = (block_caret) ? 4 : 1;
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(char_w, caret_h)), cache.caret_color);
- } else {
- int char_w = cache.font->get_char_size(' ').width;
- int caret_w = (block_caret) ? char_w : 1;
- VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor_pos, Size2i(caret_w, get_row_height())), cache.caret_color);
- }
- }
- }
}
}
@@ -1603,16 +1654,17 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
float rows = p_mouse.y;
rows -= cache.style_normal->get_margin(MARGIN_TOP);
- rows += (CLAMP(v_scroll->get_value() - get_line_scroll_pos(true), 0, 1) * get_row_height());
rows /= get_row_height();
- int first_vis_line = CLAMP(cursor.line_ofs, 0, text.size() - 1);
+ rows += get_v_scroll_offset();
+ int first_vis_line = get_first_visible_line();
int row = first_vis_line + Math::floor(rows);
+ int wrap_index = 0;
- if (is_hiding_enabled()) {
- // row will be offset by the hidden rows
- int f_ofs = num_lines_from(first_vis_line, rows + 1) - 1;
+ if (is_wrap_enabled() || is_hiding_enabled()) {
+
+ int f_ofs = num_lines_from_rows(first_vis_line, cursor.wrap_ofs, rows + 1, wrap_index) - 1;
row = first_vis_line + f_ofs;
- row = CLAMP(row, 0, text.size() - num_lines_from(text.size() - 1, -1));
+ row = CLAMP(row, 0, get_last_visible_line() + 1);
}
if (row < 0)
@@ -1626,9 +1678,19 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
col = text[row].size();
} else {
- col = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width);
- col += cursor.x_ofs;
- col = get_char_pos_for(col, get_line(row));
+ int colx = p_mouse.x - (cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width);
+ colx += cursor.x_ofs;
+ col = get_char_pos_for_line(colx, row, wrap_index);
+ if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) {
+ // move back one if we are at the end of the row
+ Vector<String> rows = get_wrap_rows_text(row);
+ int row_end_col = 0;
+ for (int i = 0; i < wrap_index + 1; i++) {
+ row_end_col += rows[i].length();
+ }
+ if (col >= row_end_col)
+ col -= 1;
+ }
}
r_row = row;
@@ -1703,7 +1765,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_reset_caret_blink_timer();
int row, col;
- update_line_scroll_pos();
_get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col);
if (mb->get_command() && highlighted_word != String()) {
@@ -1835,7 +1896,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_reset_caret_blink_timer();
int row, col;
- update_line_scroll_pos();
_get_mouse_pos(Point2i(mb->get_position().x, mb->get_position().y), row, col);
if (is_right_click_moving_caret()) {
@@ -2410,6 +2470,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
cursor_set_line(line);
cursor_set_column(column);
+#ifdef APPLE_STYLE_KEYS
+ } else if (k->get_command()) {
+ int cursor_current_column = cursor.column;
+ cursor.column = 0;
+ _remove_text(cursor.line, 0, cursor.line, cursor_current_column);
+#endif
} else {
if (cursor.line > 0 && is_line_hidden(cursor.line - 1))
unfold_line(cursor.line - 1);
@@ -2446,13 +2512,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
break;
} else if (k->get_command()) {
#endif
- bool prev_char = false;
int cc = cursor.column;
if (cc == 0 && cursor.line > 0) {
cursor_set_line(cursor.line - 1);
cursor_set_column(text[cursor.line].length());
} else {
+ bool prev_char = false;
+
while (cc > 0) {
bool ischar = _is_text_char(text[cursor.line][cc - 1]);
@@ -2507,13 +2574,14 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
break;
} else if (k->get_command()) {
#endif
- bool prev_char = false;
int cc = cursor.column;
if (cc == text[cursor.line].length() && cursor.line < text.size() - 1) {
cursor_set_line(cursor.line + 1);
cursor_set_column(0);
} else {
+ bool prev_char = false;
+
while (cc < text[cursor.line].length()) {
bool ischar = _is_text_char(text[cursor.line][cc]);
@@ -2565,11 +2633,25 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
break;
}
- if (k->get_command())
+ if (k->get_command()) {
cursor_set_line(0);
- else
+ } else
#endif
- cursor_set_line(cursor_get_line() - num_lines_from(CLAMP(cursor.line - 1, 0, text.size() - 1), -1));
+ {
+ int cur_wrap_index = get_cursor_wrap_index();
+ if (cur_wrap_index > 0) {
+ cursor_set_line(cursor.line, true, false, cur_wrap_index - 1);
+ } else if (cursor.line == 0) {
+ cursor_set_column(0);
+ } else {
+ int new_line = cursor.line - num_lines_from(cursor.line - 1, -1);
+ if (line_wraps(new_line)) {
+ cursor_set_line(new_line, true, false, times_line_wraps(new_line));
+ } else {
+ cursor_set_line(new_line, true, false);
+ }
+ }
+ }
if (k->get_shift())
_post_shift_selection();
@@ -2597,22 +2679,25 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
break;
}
- {
#else
if (k->get_command() && k->get_alt()) {
_scroll_lines_down();
break;
}
- if (k->get_command())
- cursor_set_line(text.size() - 1, true, false);
- else {
+ if (k->get_command()) {
+ cursor_set_line(get_last_unhidden_line(), true, false, 9999);
+ } else
#endif
- if (!is_last_visible_line(cursor.line)) {
- cursor_set_line(cursor_get_line() + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1), true, false);
+ {
+ int cur_wrap_index = get_cursor_wrap_index();
+ if (cur_wrap_index < times_line_wraps(cursor.line)) {
+ cursor_set_line(cursor.line, true, false, cur_wrap_index + 1);
+ } else if (cursor.line == get_last_unhidden_line()) {
+ cursor_set_column(text[cursor.line].length());
} else {
- cursor_set_line(text.size() - 1);
- cursor_set_column(get_line(cursor.line).length(), true);
+ int new_line = cursor.line + num_lines_from(CLAMP(cursor.line + 1, 0, text.size() - 1), 1);
+ cursor_set_line(new_line, true, false, 0);
}
}
@@ -2621,7 +2706,6 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_cancel_code_hint();
} break;
-
case KEY_DELETE: {
if (readonly)
@@ -2684,7 +2768,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
next_line = line;
next_column = column;
-
+#ifdef APPLE_STYLE_KEYS
+ } else if (k->get_command()) {
+ next_column = curline_len;
+ next_line = cursor.line;
+#endif
} else {
next_column = cursor.column < curline_len ? (cursor.column + 1) : 0;
}
@@ -2724,19 +2812,31 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
cursor_set_line(0);
cursor_set_column(0);
} else {
- // compute whitespace symbols seq length
- int current_line_whitespace_len = 0;
- while (current_line_whitespace_len < text[cursor.line].length()) {
- CharType c = text[cursor.line][current_line_whitespace_len];
- if (c != '\t' && c != ' ')
- break;
- current_line_whitespace_len++;
+
+ // move cursor column to start of wrapped row and then to start of text
+ Vector<String> rows = get_wrap_rows_text(cursor.line);
+ int wi = get_cursor_wrap_index();
+ int row_start_col = 0;
+ for (int i = 0; i < wi; i++) {
+ row_start_col += rows[i].length();
}
+ if (cursor.column == row_start_col || wi == 0) {
+ // compute whitespace symbols seq length
+ int current_line_whitespace_len = 0;
+ while (current_line_whitespace_len < text[cursor.line].length()) {
+ CharType c = text[cursor.line][current_line_whitespace_len];
+ if (c != '\t' && c != ' ')
+ break;
+ current_line_whitespace_len++;
+ }
- if (cursor_get_column() == current_line_whitespace_len)
- cursor_set_column(0);
- else
- cursor_set_column(current_line_whitespace_len);
+ if (cursor_get_column() == current_line_whitespace_len)
+ cursor_set_column(0);
+ else
+ cursor_set_column(current_line_whitespace_len);
+ } else {
+ cursor_set_column(row_start_col);
+ }
}
if (k->get_shift())
@@ -2761,7 +2861,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (k->get_shift())
_pre_shift_selection();
- cursor_set_line(text.size() - 1, true, false);
+ cursor_set_line(get_last_unhidden_line(), true, false, 9999);
if (k->get_shift())
_post_shift_selection();
@@ -2776,8 +2876,20 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
_pre_shift_selection();
if (k->get_command())
- cursor_set_line(text.size() - 1, true, false);
- cursor_set_column(text[cursor.line].length());
+ cursor_set_line(get_last_unhidden_line(), true, false, 9999);
+
+ // move cursor column to end of wrapped row and then to end of text
+ Vector<String> rows = get_wrap_rows_text(cursor.line);
+ int wi = get_cursor_wrap_index();
+ int row_end_col = -1;
+ for (int i = 0; i < wi + 1; i++) {
+ row_end_col += rows[i].length();
+ }
+ if (wi == rows.size() - 1 || cursor.column == row_end_col) {
+ cursor_set_column(text[cursor.line].length());
+ } else {
+ cursor_set_column(row_end_col);
+ }
if (k->get_shift())
_post_shift_selection();
@@ -2801,7 +2913,9 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (k->get_shift())
_pre_shift_selection();
- cursor_set_line(cursor_get_line() - num_lines_from(cursor.line, -get_visible_rows()), true, false);
+ int wi;
+ int n_line = cursor.line - num_lines_from_rows(cursor.line, get_cursor_wrap_index(), -get_visible_rows(), wi) + 1;
+ cursor_set_line(n_line, true, false, wi);
if (k->get_shift())
_post_shift_selection();
@@ -2815,14 +2929,16 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
scancode_handled = false;
break;
}
- // numlock disabled. fallthrough to key_pageup
+ // numlock disabled. fallthrough to key_pagedown
}
case KEY_PAGEDOWN: {
if (k->get_shift())
_pre_shift_selection();
- cursor_set_line(cursor_get_line() + num_lines_from(cursor.line, get_visible_rows()), true, false);
+ int wi;
+ int n_line = cursor.line + num_lines_from_rows(cursor.line, get_cursor_wrap_index(), get_visible_rows(), wi) - 1;
+ cursor_set_line(n_line, true, false, wi);
if (k->get_shift())
_post_shift_selection();
@@ -2833,13 +2949,64 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
} break;
case KEY_A: {
+#ifndef APPLE_STYLE_KEYS
if (!k->get_command() || k->get_shift() || k->get_alt()) {
scancode_handled = false;
break;
}
-
select_all();
+#else
+ if (k->get_alt() || (!k->get_shift() && !k->get_command() && !k->get_control())) {
+ scancode_handled = false;
+ break;
+ }
+ if (!k->get_shift() && k->get_command())
+ select_all();
+ else if (k->get_control()) {
+ if (k->get_shift())
+ _pre_shift_selection();
+
+ int current_line_whitespace_len = 0;
+ while (current_line_whitespace_len < text[cursor.line].length()) {
+ CharType c = text[cursor.line][current_line_whitespace_len];
+ if (c != '\t' && c != ' ')
+ break;
+ current_line_whitespace_len++;
+ }
+
+ if (cursor_get_column() == current_line_whitespace_len)
+ cursor_set_column(0);
+ else
+ cursor_set_column(current_line_whitespace_len);
+
+ if (k->get_shift())
+ _post_shift_selection();
+ else if (k->get_command() || k->get_control())
+ deselect();
+ }
+ } break;
+ case KEY_E: {
+
+ if (!k->get_control() || k->get_command() || k->get_alt()) {
+ scancode_handled = false;
+ break;
+ }
+
+ if (k->get_shift())
+ _pre_shift_selection();
+
+ if (k->get_command())
+ cursor_set_line(text.size() - 1, true, false);
+ cursor_set_column(text[cursor.line].length());
+
+ if (k->get_shift())
+ _post_shift_selection();
+ else if (k->get_command() || k->get_control())
+ deselect();
+ _cancel_completion();
+ completion_hint = "";
+#endif
} break;
case KEY_X: {
if (readonly) {
@@ -3016,7 +3183,7 @@ void TextEdit::_scroll_up(real_t p_delta) {
if (scrolling) {
target_v_scroll = (target_v_scroll - p_delta);
} else {
- target_v_scroll = (v_scroll->get_value() - p_delta);
+ target_v_scroll = (get_v_scroll() - p_delta);
}
if (smooth_scroll_enabled) {
@@ -3030,7 +3197,7 @@ void TextEdit::_scroll_up(real_t p_delta) {
set_physics_process_internal(true);
}
} else {
- v_scroll->set_value(target_v_scroll);
+ set_v_scroll(target_v_scroll);
}
}
@@ -3042,20 +3209,15 @@ void TextEdit::_scroll_down(real_t p_delta) {
if (scrolling) {
target_v_scroll = (target_v_scroll + p_delta);
} else {
- target_v_scroll = (v_scroll->get_value() + p_delta);
+ target_v_scroll = (get_v_scroll() + p_delta);
}
if (smooth_scroll_enabled) {
- int max_v_scroll = get_total_unhidden_rows();
- if (!scroll_past_end_of_file_enabled) {
- max_v_scroll -= get_visible_rows();
- max_v_scroll = CLAMP(max_v_scroll, 0, get_total_unhidden_rows());
- }
-
+ int max_v_scroll = v_scroll->get_max() - v_scroll->get_page();
if (target_v_scroll > max_v_scroll) {
target_v_scroll = max_v_scroll;
+ v_scroll->set_value(target_v_scroll);
}
-
if (Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) {
v_scroll->set_value(target_v_scroll);
} else {
@@ -3063,7 +3225,7 @@ void TextEdit::_scroll_down(real_t p_delta) {
set_physics_process_internal(true);
}
} else {
- v_scroll->set_value(target_v_scroll);
+ set_v_scroll(target_v_scroll);
}
}
@@ -3094,35 +3256,37 @@ void TextEdit::_scroll_lines_up() {
scrolling = false;
// adjust the vertical scroll
- if (get_v_scroll() >= 0) {
- set_v_scroll(get_v_scroll() - 1);
- }
+ set_v_scroll(get_v_scroll() - 1);
- // adjust the cursor
- int num_lines = num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), get_visible_rows());
- if (cursor.line >= cursor.line_ofs + num_lines && !selection.active) {
- cursor_set_line(cursor.line_ofs + num_lines, false, false);
+ // adjust the cursor to viewport
+ if (!selection.active) {
+ int cur_line = cursor.line;
+ int cur_wrap = get_cursor_wrap_index();
+ int last_vis_line = get_last_visible_line();
+ int last_vis_wrap = get_last_visible_line_wrap_index();
+
+ if (cur_line > last_vis_line || (cur_line == last_vis_line && cur_wrap > last_vis_wrap)) {
+ cursor_set_line(last_vis_line, false, false, last_vis_wrap);
+ }
}
}
void TextEdit::_scroll_lines_down() {
scrolling = false;
- // calculate the maximum vertical scroll position
- int max_v_scroll = get_total_unhidden_rows();
- if (!scroll_past_end_of_file_enabled) {
- max_v_scroll -= get_visible_rows();
- max_v_scroll = CLAMP(max_v_scroll, 0, get_total_unhidden_rows());
- }
-
// adjust the vertical scroll
- if (get_v_scroll() < max_v_scroll) {
- set_v_scroll(get_v_scroll() + 1);
- }
+ set_v_scroll(get_v_scroll() + 1);
+
+ // adjust the cursor to viewport
+ if (!selection.active) {
+ int cur_line = cursor.line;
+ int cur_wrap = get_cursor_wrap_index();
+ int first_vis_line = get_first_visible_line();
+ int first_vis_wrap = cursor.wrap_ofs;
- // adjust the cursor
- if (cursor.line <= cursor.line_ofs - 1 && !selection.active) {
- cursor_set_line(cursor.line_ofs, false, false);
+ if (cur_line < first_vis_line || (cur_line == first_vis_line && cur_wrap < first_vis_wrap)) {
+ cursor_set_line(first_vis_line, false, false, first_vis_wrap);
+ }
}
}
@@ -3176,6 +3340,8 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
text.set_hidden(p_line, false);
}
+ text.set_line_wrap_amount(p_line, -1);
+
r_end_line = p_line + substrings.size() - 1;
r_end_column = text[r_end_line].length() - postinsert_text.length();
@@ -3230,6 +3396,8 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
text.set(p_from_line, pre_text + post_text);
+ text.set_line_wrap_amount(p_from_line, -1);
+
if (!text_changed_dirty && !setting_text) {
if (is_inside_tree())
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
@@ -3388,61 +3556,63 @@ int TextEdit::get_visible_rows() const {
int total = cache.size.height;
total -= cache.style_normal->get_minimum_size().height;
+ if (h_scroll->is_visible_in_tree())
+ total -= h_scroll->get_size().height;
total /= get_row_height();
return total;
}
-int TextEdit::get_total_unhidden_rows() const {
- if (!is_hiding_enabled())
+int TextEdit::get_total_visible_rows() const {
+
+ // returns the total amount of rows we need in the editor.
+ // This skips hidden lines and counts each wrapping of a line.
+ if (!is_hiding_enabled() && !is_wrap_enabled())
return text.size();
- int total_unhidden = 0;
+ int total_rows = 0;
for (int i = 0; i < text.size(); i++) {
- if (!text.is_hidden(i))
- total_unhidden++;
+ if (!text.is_hidden(i)) {
+ total_rows++;
+ total_rows += times_line_wraps(i);
+ }
}
- return total_unhidden;
+ return total_rows;
}
-double TextEdit::get_line_scroll_pos(bool p_recalculate) const {
+void TextEdit::update_wrap_at() {
- if (!is_hiding_enabled())
- return cursor.line_ofs;
- if (!p_recalculate)
- return line_scroll_pos;
+ wrap_at = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width - wrap_right_offset;
+ update_cursor_wrap_offset();
+ text.clear_wrap_cache();
- // count num unhidden lines to the cursor line ofs
- double new_line_scroll_pos = 0;
- int to = CLAMP(cursor.line_ofs, 0, text.size() - 1);
- for (int i = 0; i < to; i++) {
- if (!text.is_hidden(i))
- new_line_scroll_pos++;
+ for (int i = 0; i < text.size(); i++) {
+ // update all values that wrap
+ if (!line_wraps(i))
+ continue;
+ Vector<String> rows = get_wrap_rows_text(i);
+ text.set_line_wrap_amount(i, rows.size() - 1);
}
- return new_line_scroll_pos;
}
-void TextEdit::update_line_scroll_pos() {
+void TextEdit::adjust_viewport_to_cursor() {
- if (!is_hiding_enabled()) {
- line_scroll_pos = cursor.line_ofs;
- return;
- }
+ // make sure cursor is visible on the screen
+ scrolling = false;
- // count num unhidden lines to the cursor line ofs
- double new_line_scroll_pos = 0;
- int to = CLAMP(cursor.line_ofs, 0, text.size() - 1);
- for (int i = 0; i < to; i++) {
- if (!text.is_hidden(i))
- new_line_scroll_pos++;
- }
- line_scroll_pos = new_line_scroll_pos;
-}
+ int cur_line = cursor.line;
+ int cur_wrap = get_cursor_wrap_index();
-void TextEdit::adjust_viewport_to_cursor() {
- scrolling = false;
+ int first_vis_line = get_first_visible_line();
+ int first_vis_wrap = cursor.wrap_ofs;
+ int last_vis_line = get_last_visible_line();
+ int last_vis_wrap = get_last_visible_line_wrap_index();
- if (cursor.line_ofs > cursor.line) {
- cursor.line_ofs = cursor.line;
+ if (cur_line < first_vis_line || (cur_line == first_vis_line && cur_wrap < first_vis_wrap)) {
+ // cursor is above screen
+ set_line_as_first_visible(cur_line, cur_wrap);
+ } else if (cur_line > last_vis_line || (cur_line == last_vis_line && cur_wrap > last_vis_wrap)) {
+ // cursor is below screen
+ set_line_as_last_visible(cur_line, cur_wrap);
}
int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width;
@@ -3450,91 +3620,174 @@ void TextEdit::adjust_viewport_to_cursor() {
visible_width -= v_scroll->get_combined_minimum_size().width;
visible_width -= 20; // give it a little more space
- int visible_rows = get_visible_rows();
- if (h_scroll->is_visible_in_tree() && !scroll_past_end_of_file_enabled)
- visible_rows -= ((h_scroll->get_combined_minimum_size().height - 1) / get_row_height());
- int num_rows = num_lines_from(CLAMP(cursor.line_ofs, 0, text.size() - 1), MIN(visible_rows, text.size() - 1 - cursor.line_ofs));
-
- // make sure the cursor is on the screen
- // above the caret
- if (cursor.line > (cursor.line_ofs + MAX(num_rows, visible_rows))) {
- cursor.line_ofs = cursor.line - num_lines_from(cursor.line, -visible_rows) + 1;
- }
- // below the caret
- if (cursor.line_ofs == cursor.line) {
- cursor.line_ofs = cursor.line - 2;
- }
- int line_ofs_max = text.size() - 1;
- if (!scroll_past_end_of_file_enabled) {
- line_ofs_max -= num_lines_from(text.size() - 1, -visible_rows) - 1;
- line_ofs_max += (h_scroll->is_visible_in_tree() ? 1 : 0);
- line_ofs_max += (cursor.line == text.size() - 1 ? 1 : 0);
- }
- line_ofs_max = MAX(line_ofs_max, 0);
- cursor.line_ofs = CLAMP(cursor.line_ofs, 0, line_ofs_max);
-
- // adjust x offset
- int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]);
+ if (!is_wrap_enabled()) {
+ // adjust x offset
+ int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]);
- if (cursor_x > (cursor.x_ofs + visible_width))
- cursor.x_ofs = cursor_x - visible_width + 1;
+ if (cursor_x > (cursor.x_ofs + visible_width))
+ cursor.x_ofs = cursor_x - visible_width + 1;
- if (cursor_x < cursor.x_ofs)
- cursor.x_ofs = cursor_x;
-
- updating_scrolls = true;
- h_scroll->set_value(cursor.x_ofs);
- update_line_scroll_pos();
- double new_v_scroll = get_line_scroll_pos();
- // keep offset if smooth scroll is enabled
- if (smooth_scroll_enabled) {
- new_v_scroll += fmod(v_scroll->get_value(), 1.0);
+ if (cursor_x < cursor.x_ofs)
+ cursor.x_ofs = cursor_x;
+ } else {
+ cursor.x_ofs = 0;
}
- v_scroll->set_value(new_v_scroll);
- updating_scrolls = false;
+ h_scroll->set_value(cursor.x_ofs);
+
update();
}
void TextEdit::center_viewport_to_cursor() {
- scrolling = false;
- if (cursor.line_ofs > cursor.line)
- cursor.line_ofs = cursor.line;
+ // move viewport so the cursor is in the center of the screen
+ scrolling = false;
if (is_line_hidden(cursor.line))
unfold_line(cursor.line);
+ set_line_as_center_visible(cursor.line, get_cursor_wrap_index());
int visible_width = cache.size.width - cache.style_normal->get_minimum_size().width - cache.line_number_w - cache.breakpoint_gutter_width - cache.fold_gutter_width;
if (v_scroll->is_visible_in_tree())
visible_width -= v_scroll->get_combined_minimum_size().width;
visible_width -= 20; // give it a little more space
- int visible_rows = get_visible_rows();
- if (h_scroll->is_visible_in_tree())
- visible_rows -= ((h_scroll->get_combined_minimum_size().height - 1) / get_row_height());
- if (text.size() >= visible_rows) {
- int max_ofs = text.size() - (scroll_past_end_of_file_enabled ? 1 : MAX(num_lines_from(text.size() - 1, -visible_rows), 0));
- cursor.line_ofs = CLAMP(cursor.line - num_lines_from(MAX(cursor.line - visible_rows / 2, 0), -visible_rows / 2), 0, max_ofs);
+ if (is_wrap_enabled()) {
+ // center x offset
+ int cursor_x = get_column_x_offset_for_line(cursor.column, cursor.line);
+
+ if (cursor_x > (cursor.x_ofs + visible_width))
+ cursor.x_ofs = cursor_x - visible_width + 1;
+
+ if (cursor_x < cursor.x_ofs)
+ cursor.x_ofs = cursor_x;
+ } else {
+ cursor.x_ofs = 0;
}
- int cursor_x = get_column_x_offset(cursor.column, text[cursor.line]);
+ h_scroll->set_value(cursor.x_ofs);
- if (cursor_x > (cursor.x_ofs + visible_width))
- cursor.x_ofs = cursor_x - visible_width + 1;
+ update();
+}
+
+void TextEdit::update_cursor_wrap_offset() {
+ int first_vis_line = get_first_visible_line();
+ if (line_wraps(first_vis_line)) {
+ cursor.wrap_ofs = MIN(cursor.wrap_ofs, times_line_wraps(first_vis_line));
+ } else {
+ cursor.wrap_ofs = 0;
+ }
+ set_line_as_first_visible(cursor.line_ofs, cursor.wrap_ofs);
+}
- if (cursor_x < cursor.x_ofs)
- cursor.x_ofs = cursor_x;
+bool TextEdit::line_wraps(int line) const {
- updating_scrolls = true;
- h_scroll->set_value(cursor.x_ofs);
- update_line_scroll_pos();
- double new_v_scroll = get_line_scroll_pos();
- // keep offset if smooth scroll is enabled
- if (smooth_scroll_enabled) {
- new_v_scroll += fmod(v_scroll->get_value(), 1.0);
+ ERR_FAIL_INDEX_V(line, text.size(), 0);
+ if (!is_wrap_enabled())
+ return false;
+ return text.get_line_width(line) > wrap_at;
+}
+
+int TextEdit::times_line_wraps(int line) const {
+
+ ERR_FAIL_INDEX_V(line, text.size(), 0);
+ if (!line_wraps(line))
+ return 0;
+
+ int wrap_amount = text.get_line_wrap_amount(line);
+ if (wrap_amount == -1) {
+ // update the value
+ Vector<String> rows = get_wrap_rows_text(line);
+ wrap_amount = rows.size() - 1;
+ text.set_line_wrap_amount(line, wrap_amount);
}
- v_scroll->set_value(new_v_scroll);
- updating_scrolls = false;
- update();
+
+ return wrap_amount;
+}
+
+Vector<String> TextEdit::get_wrap_rows_text(int p_line) const {
+
+ ERR_FAIL_INDEX_V(p_line, text.size(), Vector<String>());
+
+ Vector<String> lines;
+ if (!line_wraps(p_line)) {
+ lines.push_back(text[p_line]);
+ return lines;
+ }
+
+ int px = 0;
+ int col = 0;
+ String line_text = text[p_line];
+ String wrap_substring = "";
+
+ int word_px = 0;
+ String word_str = "";
+ int cur_wrap_index = 0;
+
+ int tab_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width;
+
+ while (col < line_text.length()) {
+ char c = line_text[col];
+ int w = text.get_char_width(c, line_text[col + 1], px + word_px);
+
+ int indent_ofs = (cur_wrap_index != 0 ? tab_offset_px : 0);
+
+ word_str += c;
+ word_px += w;
+ if (c == ' ') {
+ // end of a word; add this word to the substring
+ wrap_substring += word_str;
+ px += word_px;
+ word_str = "";
+ word_px = 0;
+ }
+
+ if ((indent_ofs + px + word_px) > wrap_at) {
+ // do not want to add this word
+ if (indent_ofs + word_px > wrap_at) {
+ // not enough space; add it anyway
+ wrap_substring += word_str;
+ px += word_px;
+ word_str = "";
+ word_px = 0;
+ }
+ lines.push_back(wrap_substring);
+ // reset for next wrap
+ cur_wrap_index++;
+ wrap_substring = "";
+ px = 0;
+ }
+ col++;
+ }
+ // line ends before hit wrap_at; add this word to the substring
+ wrap_substring += word_str;
+ px += word_px;
+ lines.push_back(wrap_substring);
+ return lines;
+}
+
+int TextEdit::get_cursor_wrap_index() const {
+
+ return get_line_wrap_index_at_col(cursor.line, cursor.column);
+}
+
+int TextEdit::get_line_wrap_index_at_col(int p_line, int p_column) const {
+
+ ERR_FAIL_INDEX_V(p_line, text.size(), 0);
+
+ if (!line_wraps(p_line))
+ return 0;
+
+ // loop through wraps in the line text until we get to the column
+ int wrap_index = 0;
+ int col = 0;
+ Vector<String> rows = get_wrap_rows_text(p_line);
+ for (int i = 0; i < rows.size(); i++) {
+ wrap_index = i;
+ String s = rows[wrap_index];
+ col += s.length();
+ if (col > p_column)
+ break;
+ }
+ return wrap_index;
}
void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) {
@@ -3546,7 +3799,7 @@ void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) {
if (cursor.column > get_line(cursor.line).length())
cursor.column = get_line(cursor.line).length();
- cursor.last_fit_x = get_column_x_offset(cursor.column, get_line(cursor.line));
+ cursor.last_fit_x = get_column_x_offset_for_line(cursor.column, cursor.line);
if (p_adjust_viewport)
adjust_viewport_to_cursor();
@@ -3558,7 +3811,7 @@ void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) {
}
}
-void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_hidden) {
+void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_hidden, int p_wrap_index) {
if (setting_row)
return;
@@ -3567,8 +3820,8 @@ void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_
if (p_row < 0)
p_row = 0;
- if (p_row >= (int)text.size())
- p_row = (int)text.size() - 1;
+ if (p_row >= text.size())
+ p_row = text.size() - 1;
if (!p_can_be_hidden) {
if (is_line_hidden(CLAMP(p_row, 0, text.size() - 1))) {
@@ -3586,7 +3839,18 @@ void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport, bool p_can_be_
}
}
cursor.line = p_row;
- cursor.column = get_char_pos_for(cursor.last_fit_x, get_line(cursor.line));
+
+ int n_col = get_char_pos_for_line(cursor.last_fit_x, p_row, p_wrap_index);
+ if (is_wrap_enabled() && p_wrap_index < times_line_wraps(p_row)) {
+ Vector<String> rows = get_wrap_rows_text(p_row);
+ int row_end_col = 0;
+ for (int i = 0; i < p_wrap_index + 1; i++) {
+ row_end_col += rows[i].length();
+ }
+ if (n_col >= row_end_col)
+ n_col -= 1;
+ }
+ cursor.column = n_col;
if (p_adjust_viewport)
adjust_viewport_to_cursor();
@@ -3663,9 +3927,25 @@ void TextEdit::_scroll_moved(double p_to_val) {
if (h_scroll->is_visible_in_tree())
cursor.x_ofs = h_scroll->get_value();
if (v_scroll->is_visible_in_tree()) {
- double val = v_scroll->get_value();
- cursor.line_ofs = num_lines_from(0, (int)floor(val));
- line_scroll_pos = (int)floor(val);
+
+ // set line ofs and wrap ofs
+ int v_scroll_i = floor(get_v_scroll());
+ int sc = 0;
+ int n_line;
+ for (n_line = 0; n_line < text.size(); n_line++) {
+ if (!is_line_hidden(n_line)) {
+ sc++;
+ sc += times_line_wraps(n_line);
+ if (sc > v_scroll_i)
+ break;
+ }
+ }
+ int line_wrap_amount = times_line_wraps(n_line);
+ int wi = line_wrap_amount - (sc - v_scroll_i - 1);
+ wi = CLAMP(wi, 0, line_wrap_amount);
+
+ cursor.line_ofs = n_line;
+ cursor.wrap_ofs = wi;
}
update();
}
@@ -3675,29 +3955,73 @@ int TextEdit::get_row_height() const {
return cache.font->get_height() + cache.line_spacing;
}
-int TextEdit::get_char_pos_for(int p_px, String p_str) const {
+int TextEdit::get_char_pos_for_line(int p_px, int p_line, int p_wrap_index) const {
- int px = 0;
- int c = 0;
+ ERR_FAIL_INDEX_V(p_line, text.size(), 0);
- int tab_w = cache.font->get_char_size(' ').width * indent_size;
+ if (line_wraps(p_line)) {
- while (c < p_str.length()) {
+ int line_wrap_amount = times_line_wraps(p_line);
+ int wrap_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width;
+ if (p_wrap_index > line_wrap_amount)
+ p_wrap_index = line_wrap_amount;
+ if (p_wrap_index > 0)
+ p_px -= wrap_offset_px;
+ else
+ p_wrap_index = 0;
+ Vector<String> rows = get_wrap_rows_text(p_line);
+ int c_pos = get_char_pos_for(p_px, rows[p_wrap_index]);
+ for (int i = 0; i < p_wrap_index; i++) {
+ String s = rows[i];
+ c_pos += s.length();
+ }
- int w = 0;
+ return c_pos;
+ } else {
- if (p_str[c] == '\t') {
+ return get_char_pos_for(p_px, text[p_line]);
+ }
+}
- int left = px % tab_w;
- if (left == 0)
- w = tab_w;
- else
- w = tab_w - px % tab_w; // is right...
+int TextEdit::get_column_x_offset_for_line(int p_char, int p_line) const {
- } else {
+ ERR_FAIL_INDEX_V(p_line, text.size(), 0);
- w = cache.font->get_char_size(p_str[c], p_str[c + 1]).width;
+ if (line_wraps(p_line)) {
+
+ int n_char = p_char;
+ int col = 0;
+ Vector<String> rows = get_wrap_rows_text(p_line);
+ int wrap_index = 0;
+ for (int i = 0; i < rows.size(); i++) {
+ wrap_index = i;
+ String s = rows[wrap_index];
+ col += s.length();
+ if (col > p_char)
+ break;
+ n_char -= s.length();
}
+ int px = get_column_x_offset(n_char, rows[wrap_index]);
+
+ int wrap_offset_px = get_indent_level(p_line) * cache.font->get_char_size(' ').width;
+ if (wrap_index != 0)
+ px += wrap_offset_px;
+
+ return px;
+ } else {
+
+ return get_column_x_offset(p_char, text[p_line]);
+ }
+}
+
+int TextEdit::get_char_pos_for(int p_px, String p_str) const {
+
+ int px = 0;
+ int c = 0;
+
+ while (c < p_str.length()) {
+
+ int w = text.get_char_width(p_str[c], p_str[c + 1], px);
if (p_px < (px + w / 2))
break;
@@ -3708,28 +4032,16 @@ int TextEdit::get_char_pos_for(int p_px, String p_str) const {
return c;
}
-int TextEdit::get_column_x_offset(int p_char, String p_str) {
+int TextEdit::get_column_x_offset(int p_char, String p_str) const {
int px = 0;
- int tab_w = cache.font->get_char_size(' ').width * indent_size;
-
for (int i = 0; i < p_char; i++) {
if (i >= p_str.length())
break;
- if (p_str[i] == '\t') {
-
- int left = px % tab_w;
- if (left == 0)
- px += tab_w;
- else
- px += tab_w - px % tab_w; // is right...
-
- } else {
- px += cache.font->get_char_size(p_str[i], p_str[i + 1]).width;
- }
+ px += text.get_char_width(p_str[i], p_str[i + 1], px);
}
return px;
@@ -3805,7 +4117,7 @@ void TextEdit::set_text(String p_text) {
cursor.line = 0;
cursor.x_ofs = 0;
cursor.line_ofs = 0;
- line_scroll_pos = 0;
+ cursor.wrap_ofs = 0;
cursor.last_fit_x = 0;
cursor_set_line(0);
cursor_set_column(0);
@@ -3891,7 +4203,7 @@ void TextEdit::_clear() {
cursor.line = 0;
cursor.x_ofs = 0;
cursor.line_ofs = 0;
- line_scroll_pos = 0;
+ cursor.wrap_ofs = 0;
cursor.last_fit_x = 0;
}
@@ -3913,14 +4225,14 @@ bool TextEdit::is_readonly() const {
return readonly;
}
-void TextEdit::set_wrap(bool p_wrap) {
+void TextEdit::set_wrap_enabled(bool p_wrap_enabled) {
- wrap = p_wrap;
+ wrap_enabled = p_wrap_enabled;
}
-bool TextEdit::is_wrapping() const {
+bool TextEdit::is_wrap_enabled() const {
- return wrap;
+ return wrap_enabled;
}
void TextEdit::set_max_chars(int p_max_chars) {
@@ -4069,7 +4381,7 @@ void TextEdit::clear_colors() {
keywords.clear();
color_regions.clear();
color_region_cache.clear();
- text.clear_caches();
+ text.clear_width_cache();
}
void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color) {
@@ -4089,7 +4401,7 @@ Color TextEdit::get_keyword_color(String p_keyword) const {
void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
color_regions.push_back(ColorRegion(p_begin_key, p_end_key, p_color, p_line_only));
- text.clear_caches();
+ text.clear_width_cache();
update();
}
@@ -4174,6 +4486,7 @@ void TextEdit::paste() {
String clipboard = OS::get_singleton()->get_clipboard();
+ begin_complex_operation();
if (selection.active) {
selection.active = false;
@@ -4190,6 +4503,8 @@ void TextEdit::paste() {
}
_insert_text_at_cursor(clipboard);
+ end_complex_operation();
+
update();
}
@@ -4583,52 +4898,99 @@ void TextEdit::unhide_all_lines() {
update();
}
-int TextEdit::num_lines_from(int p_line_from, int unhidden_amount) const {
+int TextEdit::num_lines_from(int p_line_from, int visible_amount) const {
- // returns the number of hidden and unhidden lines from p_line_from to p_line_from + amount of visible lines
- ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(unhidden_amount));
+ // returns the number of lines (hidden and unhidden) from p_line_from to (p_line_from + visible_amount of unhidden lines)
+ ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(visible_amount));
if (!is_hiding_enabled())
- return ABS(unhidden_amount);
+ return ABS(visible_amount);
+
int num_visible = 0;
int num_total = 0;
- if (unhidden_amount >= 0) {
+ if (visible_amount >= 0) {
for (int i = p_line_from; i < text.size(); i++) {
num_total++;
- if (!is_line_hidden(i))
+ if (!is_line_hidden(i)) {
num_visible++;
- if (num_visible >= unhidden_amount)
+ }
+ if (num_visible >= visible_amount)
break;
}
} else {
- unhidden_amount = ABS(unhidden_amount);
+ visible_amount = ABS(visible_amount);
for (int i = p_line_from; i >= 0; i--) {
num_total++;
- if (!is_line_hidden(i))
+ if (!is_line_hidden(i)) {
num_visible++;
- if (num_visible >= unhidden_amount)
+ }
+ if (num_visible >= visible_amount)
break;
}
}
return num_total;
}
-bool TextEdit::is_last_visible_line(int p_line) const {
+int TextEdit::num_lines_from_rows(int p_line_from, int p_wrap_index_from, int visible_amount, int &wrap_index) const {
- ERR_FAIL_INDEX_V(p_line, text.size(), false);
+ // returns the number of lines (hidden and unhidden) from (p_line_from + p_wrap_index_from) row to (p_line_from + visible_amount of unhidden and wrapped rows)
+ // wrap index is set to the wrap index of the last line
+ wrap_index = 0;
+ ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(visible_amount));
- if (p_line == text.size() - 1)
- return true;
+ if (!is_hiding_enabled() && !is_wrap_enabled())
+ return ABS(visible_amount);
+ int num_visible = 0;
+ int num_total = 0;
+ if (visible_amount == 0) {
+ num_total = 0;
+ wrap_index = 0;
+ } else if (visible_amount > 0) {
+ int i;
+ num_visible -= p_wrap_index_from;
+ for (i = p_line_from; i < text.size(); i++) {
+ num_total++;
+ if (!is_line_hidden(i)) {
+ num_visible++;
+ num_visible += times_line_wraps(i);
+ }
+ if (num_visible >= visible_amount)
+ break;
+ }
+ wrap_index = times_line_wraps(MIN(i, text.size() - 1)) - (num_visible - visible_amount);
+ } else {
+ visible_amount = ABS(visible_amount);
+ int i;
+ num_visible -= times_line_wraps(p_line_from) - p_wrap_index_from;
+ for (i = p_line_from; i >= 0; i--) {
+ num_total++;
+ if (!is_line_hidden(i)) {
+ num_visible++;
+ num_visible += times_line_wraps(i);
+ }
+ if (num_visible >= visible_amount)
+ break;
+ }
+ wrap_index = (num_visible - visible_amount);
+ }
+ wrap_index = MAX(wrap_index, 0);
+ return num_total;
+}
+
+int TextEdit::get_last_unhidden_line() const {
+
+ // returns the last line in the text that is not hidden
if (!is_hiding_enabled())
- return false;
+ return text.size() - 1;
- for (int i = p_line + 1; i < text.size(); i++) {
- if (!is_line_hidden(i))
- return false;
+ int last_line;
+ for (last_line = text.size() - 1; last_line > 0; last_line--) {
+ if (!is_line_hidden(last_line)) {
+ break;
+ }
}
-
- return true;
+ return last_line;
}
int TextEdit::get_indent_level(int p_line) const {
@@ -4648,7 +5010,7 @@ int TextEdit::get_indent_level(int p_line) const {
break;
}
}
- return tab_count + whitespace_count / indent_size;
+ return tab_count * indent_size + whitespace_count;
}
bool TextEdit::is_line_comment(int p_line) const {
@@ -4978,6 +5340,11 @@ void TextEdit::set_indent_size(const int p_size) {
update();
}
+int TextEdit::get_indent_size() {
+
+ return indent_size;
+}
+
void TextEdit::set_draw_tabs(bool p_draw) {
draw_tabs = p_draw;
@@ -4991,6 +5358,7 @@ bool TextEdit::is_drawing_tabs() const {
void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) {
override_selected_font_color = p_override_selected_font_color;
}
+
bool TextEdit::is_overriding_selected_font_color() const {
return override_selected_font_color;
}
@@ -5011,58 +5379,143 @@ bool TextEdit::is_insert_text_operation() {
uint32_t TextEdit::get_version() const {
return current_op.version;
}
+
uint32_t TextEdit::get_saved_version() const {
return saved_version;
}
+
void TextEdit::tag_saved_version() {
saved_version = get_version();
}
-int TextEdit::get_v_scroll() const {
+double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const {
- return v_scroll->get_value();
-}
-void TextEdit::set_v_scroll(int p_scroll) {
+ if (!is_wrap_enabled() && !is_hiding_enabled())
+ return p_line;
- if (p_scroll < 0) {
- p_scroll = 0;
- }
- if (!scroll_past_end_of_file_enabled) {
- if (p_scroll + get_visible_rows() > get_total_unhidden_rows()) {
- int num_rows = num_lines_from(CLAMP(p_scroll, 0, text.size() - 1), MIN(get_visible_rows(), text.size() - 1 - p_scroll));
- p_scroll = text.size() - num_rows;
+ // count the number of visible lines up to this line
+ double new_line_scroll_pos = 0;
+ int to = CLAMP(p_line, 0, text.size() - 1);
+ for (int i = 0; i < to; i++) {
+ if (!text.is_hidden(i)) {
+ new_line_scroll_pos++;
+ new_line_scroll_pos += times_line_wraps(i);
}
}
+ new_line_scroll_pos += p_wrap_index;
+ return new_line_scroll_pos;
+}
+
+void TextEdit::set_line_as_first_visible(int p_line, int p_wrap_index) {
+
+ set_v_scroll(get_scroll_pos_for_line(p_line, p_wrap_index));
+}
+
+void TextEdit::set_line_as_center_visible(int p_line, int p_wrap_index) {
+
+ int visible_rows = get_visible_rows();
+ int wi;
+ int first_line = p_line - num_lines_from_rows(p_line, p_wrap_index, -visible_rows / 2, wi) + 1;
+
+ set_v_scroll(get_scroll_pos_for_line(first_line, wi));
+}
+
+void TextEdit::set_line_as_last_visible(int p_line, int p_wrap_index) {
+
+ int wi;
+ int first_line = p_line - num_lines_from_rows(p_line, p_wrap_index, -get_visible_rows() - 1, wi) + 1;
+
+ set_v_scroll(get_scroll_pos_for_line(first_line, wi) + get_visible_rows_offset());
+}
+
+int TextEdit::get_first_visible_line() const {
+
+ return CLAMP(cursor.line_ofs, 0, text.size() - 1);
+}
+
+int TextEdit::get_last_visible_line() const {
+
+ int first_vis_line = get_first_visible_line();
+ int last_vis_line = 0;
+ int wi;
+ last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi) - 1;
+ last_vis_line = CLAMP(last_vis_line, 0, text.size() - 1);
+ return last_vis_line;
+}
+
+int TextEdit::get_last_visible_line_wrap_index() const {
+
+ int first_vis_line = get_first_visible_line();
+ int last_vis_line = 0;
+ int wi;
+ last_vis_line = first_vis_line + num_lines_from_rows(first_vis_line, cursor.wrap_ofs, get_visible_rows() + 1, wi) - 1;
+ return wi;
+}
+
+double TextEdit::get_visible_rows_offset() const {
+
+ double total = cache.size.height;
+ total -= cache.style_normal->get_minimum_size().height;
+ if (h_scroll->is_visible_in_tree())
+ total -= h_scroll->get_size().height;
+ total /= (double)get_row_height();
+ total = total - floor(total);
+ total = -CLAMP(total, 0.001, 1) + 1;
+ return total;
+}
+
+double TextEdit::get_v_scroll_offset() const {
+
+ double val = get_v_scroll() - floor(get_v_scroll());
+ return CLAMP(val, 0, 1);
+}
+
+double TextEdit::get_v_scroll() const {
+
+ return v_scroll->get_value();
+}
+
+void TextEdit::set_v_scroll(double p_scroll) {
+
v_scroll->set_value(p_scroll);
- cursor.line_ofs = num_lines_from(0, p_scroll);
- line_scroll_pos = p_scroll;
+ int max_v_scroll = v_scroll->get_max() - v_scroll->get_page();
+ if (p_scroll >= max_v_scroll - 1.0)
+ _scroll_moved(v_scroll->get_value());
}
int TextEdit::get_h_scroll() const {
return h_scroll->get_value();
}
+
void TextEdit::set_h_scroll(int p_scroll) {
+ if (p_scroll < 0) {
+ p_scroll = 0;
+ }
h_scroll->set_value(p_scroll);
}
void TextEdit::set_smooth_scroll_enabled(bool p_enable) {
+
v_scroll->set_smooth_scroll_enabled(p_enable);
smooth_scroll_enabled = p_enable;
}
bool TextEdit::is_smooth_scroll_enabled() const {
+
return smooth_scroll_enabled;
}
void TextEdit::set_v_scroll_speed(float p_speed) {
+
v_scroll_speed = p_speed;
}
float TextEdit::get_v_scroll_speed() const {
+
return v_scroll_speed;
}
@@ -5542,7 +5995,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_line", "line"), &TextEdit::get_line);
ClassDB::bind_method(D_METHOD("cursor_set_column", "column", "adjust_viewport"), &TextEdit::cursor_set_column, DEFVAL(true));
- ClassDB::bind_method(D_METHOD("cursor_set_line", "line", "adjust_viewport", "can_be_hidden"), &TextEdit::cursor_set_line, DEFVAL(true), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("cursor_set_line", "line", "adjust_viewport", "can_be_hidden", "wrap_index"), &TextEdit::cursor_set_line, DEFVAL(true), DEFVAL(true), DEFVAL(0));
ClassDB::bind_method(D_METHOD("cursor_get_column"), &TextEdit::cursor_get_column);
ClassDB::bind_method(D_METHOD("cursor_get_line"), &TextEdit::cursor_get_line);
@@ -5559,8 +6012,8 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_readonly", "enable"), &TextEdit::set_readonly);
ClassDB::bind_method(D_METHOD("is_readonly"), &TextEdit::is_readonly);
- ClassDB::bind_method(D_METHOD("set_wrap", "enable"), &TextEdit::set_wrap);
- ClassDB::bind_method(D_METHOD("is_wrapping"), &TextEdit::is_wrapping);
+ ClassDB::bind_method(D_METHOD("set_wrap_enabled", "enable"), &TextEdit::set_wrap_enabled);
+ ClassDB::bind_method(D_METHOD("is_wrap_enabled"), &TextEdit::is_wrap_enabled);
// ClassDB::bind_method(D_METHOD("set_max_chars", "amount"), &TextEdit::set_max_chars);
// ClassDB::bind_method(D_METHOD("get_max_char"), &TextEdit::get_max_chars);
ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &TextEdit::set_context_menu_enabled);
@@ -5638,7 +6091,7 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scrolling"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hiding_enabled"), "set_hiding_enabled", "is_hiding_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_lines"), "set_wrap", "is_wrapping");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_enabled"), "set_wrap_enabled", "is_wrap_enabled");
// ADD_PROPERTY(PropertyInfo(Variant::BOOL, "max_chars"), "set_max_chars", "get_max_chars");
ADD_GROUP("Caret", "caret_");
@@ -5673,7 +6126,8 @@ TextEdit::TextEdit() {
draw_caret = true;
max_chars = 0;
clear();
- wrap = false;
+ wrap_enabled = false;
+ wrap_right_offset = 10;
set_focus_mode(FOCUS_ALL);
syntax_highlighter = NULL;
_update_caches();
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 30e70bfd0b..5c82d1ac20 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -76,6 +76,7 @@ public:
bool marked : 1;
bool breakpoint : 1;
bool hidden : 1;
+ int wrap_amount_cache : 24;
Map<int, ColorRegionInfo> region_info;
String data;
};
@@ -94,6 +95,9 @@ public:
void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; }
int get_line_width(int p_line) const;
int get_max_width(bool p_exclude_hidden = false) const;
+ int get_char_width(CharType c, CharType next_c, int px) const;
+ void set_line_wrap_amount(int p_line, int p_wrap_amount) const;
+ int get_line_wrap_amount(int p_line) const;
const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const;
void set(int p_line, const String &p_text);
void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; }
@@ -106,7 +110,8 @@ public:
void remove(int p_at);
int size() const { return text.size(); }
void clear();
- void clear_caches();
+ void clear_width_cache();
+ void clear_wrap_cache();
_FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
Text() { indent_size = 4; }
};
@@ -115,7 +120,7 @@ private:
struct Cursor {
int last_fit_x;
int line, column; ///< cursor
- int x_ofs, line_ofs;
+ int x_ofs, line_ofs, wrap_ofs;
} cursor;
struct Selection {
@@ -263,8 +268,11 @@ private:
bool block_caret;
bool right_click_moves_caret;
+ bool wrap_enabled;
+ int wrap_at;
+ int wrap_right_offset;
+
bool setting_row;
- bool wrap;
bool draw_tabs;
bool override_selected_font_color;
bool cursor_changed_dirty;
@@ -321,19 +329,34 @@ private:
int search_result_line;
int search_result_col;
- double line_scroll_pos;
-
bool context_menu_enabled;
int get_visible_rows() const;
- int get_total_unhidden_rows() const;
- double get_line_scroll_pos(bool p_recalculate = false) const;
- void update_line_scroll_pos();
-
+ int get_total_visible_rows() const;
+
+ void update_cursor_wrap_offset();
+ void update_wrap_at();
+ bool line_wraps(int line) const;
+ int times_line_wraps(int line) const;
+ Vector<String> get_wrap_rows_text(int p_line) const;
+ int get_cursor_wrap_index() const;
+ int get_line_wrap_index_at_col(int p_line, int p_column) const;
int get_char_count();
+ double get_scroll_pos_for_line(int p_line, int p_wrap_index = 0) const;
+ void set_line_as_first_visible(int p_line, int p_wrap_index = 0);
+ void set_line_as_center_visible(int p_line, int p_wrap_index = 0);
+ void set_line_as_last_visible(int p_line, int p_wrap_index = 0);
+ int get_first_visible_line() const;
+ int get_last_visible_line() const;
+ int get_last_visible_line_wrap_index() const;
+ double get_visible_rows_offset() const;
+ double get_v_scroll_offset() const;
+
+ int get_char_pos_for_line(int p_px, int p_line, int p_wrap_index = 0) const;
+ int get_column_x_offset_for_line(int p_char, int p_line) const;
int get_char_pos_for(int p_px, String p_str) const;
- int get_column_x_offset(int p_char, String p_str);
+ int get_column_x_offset(int p_char, String p_str) const;
void adjust_viewport_to_cursor();
double get_scroll_line_diff() const;
@@ -455,8 +478,10 @@ public:
bool is_line_hidden(int p_line) const;
void fold_all_lines();
void unhide_all_lines();
- int num_lines_from(int p_line_from, int unhidden_amount) const;
- bool is_last_visible_line(int p_line) const;
+ int num_lines_from(int p_line_from, int visible_amount) const;
+ int num_lines_from_rows(int p_line_from, int p_wrap_index_from, int visible_amount, int &wrap_index) const;
+ int get_last_unhidden_line() const;
+
bool can_fold(int p_line) const;
bool is_folded(int p_line) const;
void fold_line(int p_line);
@@ -493,7 +518,7 @@ public:
void center_viewport_to_cursor();
void cursor_set_column(int p_col, bool p_adjust_viewport = true);
- void cursor_set_line(int p_row, bool p_adjust_viewport = true, bool p_can_be_hidden = true);
+ void cursor_set_line(int p_row, bool p_adjust_viewport = true, bool p_can_be_hidden = true, int p_wrap_index = 0);
int cursor_get_column() const;
int cursor_get_line() const;
@@ -516,8 +541,8 @@ public:
void set_max_chars(int p_max_chars);
int get_max_chars() const;
- void set_wrap(bool p_wrap);
- bool is_wrapping() const;
+ void set_wrap_enabled(bool p_wrap_enabled);
+ bool is_wrap_enabled() const;
void clear();
@@ -557,6 +582,7 @@ public:
void set_indent_using_spaces(const bool p_use_spaces);
bool is_indent_using_spaces() const;
void set_indent_size(const int p_size);
+ int get_indent_size();
void set_draw_tabs(bool p_draw);
bool is_drawing_tabs() const;
void set_override_selected_font_color(bool p_override_selected_font_color);
@@ -577,8 +603,8 @@ public:
Color get_member_color(String p_member) const;
void clear_member_keywords();
- int get_v_scroll() const;
- void set_v_scroll(int p_scroll);
+ double get_v_scroll() const;
+ void set_v_scroll(double p_scroll);
int get_h_scroll() const;
void set_h_scroll(int p_scroll);
diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp
index 24b04c0c4a..1443d5efbf 100644
--- a/scene/main/instance_placeholder.cpp
+++ b/scene/main/instance_placeholder.cpp
@@ -52,6 +52,7 @@ bool InstancePlaceholder::_get(const StringName &p_name, Variant &r_ret) const {
}
return false;
}
+
void InstancePlaceholder::_get_property_list(List<PropertyInfo> *p_list) const {
for (const List<PropSet>::Element *E = stored_values.front(); E; E = E->next()) {
@@ -73,13 +74,14 @@ String InstancePlaceholder::get_instance_path() const {
return path;
}
-void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_scene) {
- ERR_FAIL_COND(!is_inside_tree());
+Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene> &p_custom_scene) {
+
+ ERR_FAIL_COND_V(!is_inside_tree(), NULL);
Node *base = get_parent();
if (!base)
- return;
+ return NULL;
Ref<PackedScene> ps;
if (p_custom_scene.is_valid())
@@ -88,7 +90,7 @@ void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_s
ps = ResourceLoader::load(path, "PackedScene");
if (!ps.is_valid())
- return;
+ return NULL;
Node *scene = ps->instance();
scene->set_name(get_name());
int pos = get_position_in_parent();
@@ -97,11 +99,20 @@ void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_s
scene->set(E->get().name, E->get().value);
}
- queue_delete();
+ if (p_replace) {
+ queue_delete();
+ base->remove_child(this);
+ }
- base->remove_child(this);
base->add_child(scene);
base->move_child(scene, pos);
+
+ return scene;
+}
+
+void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_scene) {
+ //Deprecated by
+ create_instance(true, p_custom_scene);
}
Dictionary InstancePlaceholder::get_stored_values(bool p_with_order) {
@@ -124,6 +135,7 @@ Dictionary InstancePlaceholder::get_stored_values(bool p_with_order) {
void InstancePlaceholder::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_stored_values", "with_order"), &InstancePlaceholder::get_stored_values, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("create_instance", "replace", "custom_scene"), &InstancePlaceholder::create_instance, DEFVAL(false), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("replace_by_instance", "custom_scene"), &InstancePlaceholder::replace_by_instance, DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("get_instance_path"), &InstancePlaceholder::get_instance_path);
}
diff --git a/scene/main/instance_placeholder.h b/scene/main/instance_placeholder.h
index d70f1318ea..2158257c93 100644
--- a/scene/main/instance_placeholder.h
+++ b/scene/main/instance_placeholder.h
@@ -60,6 +60,7 @@ public:
Dictionary get_stored_values(bool p_with_order = false);
+ Node *create_instance(bool p_replace = false, const Ref<PackedScene> &p_custom_scene = Ref<PackedScene>());
void replace_by_instance(const Ref<PackedScene> &p_custom_scene = Ref<PackedScene>());
InstancePlaceholder();
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index fcf8768094..3643aedb85 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -136,7 +136,6 @@ void Node::_notification(int p_notification) {
get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_ready, NULL, 0);
}
- //emit_signal(SceneStringNames::get_singleton()->enter_tree);
} break;
case NOTIFICATION_POSTINITIALIZE: {
@@ -177,9 +176,13 @@ void Node::_propagate_ready() {
data.children[i]->_propagate_ready();
}
data.blocked--;
+
+ notification(NOTIFICATION_POST_ENTER_TREE);
+
if (data.ready_first) {
data.ready_first = false;
notification(NOTIFICATION_READY);
+ emit_signal(SceneStringNames::get_singleton()->ready);
}
}
@@ -477,7 +480,7 @@ bool Node::is_network_master() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
- return get_multiplayer_api()->get_network_unique_id() == data.network_master;
+ return get_multiplayer()->get_network_unique_id() == data.network_master;
}
/***** RPC CONFIG ********/
@@ -668,12 +671,12 @@ Variant Node::_rpc_unreliable_id_bind(const Variant **p_args, int p_argcount, Va
void Node::rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) {
ERR_FAIL_COND(!is_inside_tree());
- get_multiplayer_api()->rpcp(this, p_peer_id, p_unreliable, p_method, p_arg, p_argcount);
+ get_multiplayer()->rpcp(this, p_peer_id, p_unreliable, p_method, p_arg, p_argcount);
}
void Node::rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) {
ERR_FAIL_COND(!is_inside_tree());
- get_multiplayer_api()->rsetp(this, p_peer_id, p_unreliable, p_property, p_value);
+ get_multiplayer()->rsetp(this, p_peer_id, p_unreliable, p_property, p_value);
}
/******** RSET *********/
@@ -698,21 +701,21 @@ void Node::rset_unreliable_id(int p_peer_id, const StringName &p_property, const
}
//////////// end of rpc
-Ref<MultiplayerAPI> Node::get_multiplayer_api() const {
- if (multiplayer_api.is_valid())
- return multiplayer_api;
+Ref<MultiplayerAPI> Node::get_multiplayer() const {
+ if (multiplayer.is_valid())
+ return multiplayer;
if (!is_inside_tree())
return Ref<MultiplayerAPI>();
- return get_tree()->get_multiplayer_api();
+ return get_tree()->get_multiplayer();
}
-Ref<MultiplayerAPI> Node::get_custom_multiplayer_api() const {
- return multiplayer_api;
+Ref<MultiplayerAPI> Node::get_custom_multiplayer() const {
+ return multiplayer;
}
-void Node::set_custom_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api) {
+void Node::set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
- multiplayer_api = p_multiplayer_api;
+ multiplayer = p_multiplayer;
}
const Map<StringName, Node::RPCMode>::Element *Node::get_node_rpc_mode(const StringName &p_method) {
@@ -979,9 +982,23 @@ void Node::_set_name_nocheck(const StringName &p_name) {
data.name = p_name;
}
+String Node::invalid_character = ". : @ / \"";
+
+bool Node::_validate_node_name(String &p_name) {
+ String name = p_name;
+ Vector<String> chars = Node::invalid_character.split(" ");
+ for (int i = 0; i < chars.size(); i++) {
+ name = name.replace(chars[i], "");
+ }
+ bool is_valid = name == p_name;
+ p_name = name;
+ return is_valid;
+}
+
void Node::set_name(const String &p_name) {
- String name = p_name.replace(":", "").replace("/", "").replace("@", "");
+ String name = p_name;
+ _validate_node_name(name);
ERR_FAIL_COND(name == "");
data.name = name;
@@ -2567,18 +2584,21 @@ Array Node::_get_children() const {
return arr;
}
-#ifdef TOOLS_ENABLED
void Node::set_import_path(const NodePath &p_import_path) {
+#ifdef TOOLS_ENABLED
data.import_path = p_import_path;
+#endif
}
NodePath Node::get_import_path() const {
+#ifdef TOOLS_ENABLED
return data.import_path;
-}
-
+#else
+ return NodePath();
#endif
+}
static void _add_nodes_to_options(const Node *p_base, const Node *p_node, List<String> *r_options) {
@@ -2731,19 +2751,16 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_network_master"), &Node::is_network_master);
- ClassDB::bind_method(D_METHOD("get_multiplayer_api"), &Node::get_multiplayer_api);
- ClassDB::bind_method(D_METHOD("get_custom_multiplayer_api"), &Node::get_custom_multiplayer_api);
- ClassDB::bind_method(D_METHOD("set_custom_multiplayer_api", "api"), &Node::set_custom_multiplayer_api);
+ 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", "mode"), &Node::rpc_config);
ClassDB::bind_method(D_METHOD("rset_config", "property", "mode"), &Node::rset_config);
-#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path);
ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path);
ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path");
-#endif
-
{
MethodInfo mi;
@@ -2800,6 +2817,7 @@ void Node::_bind_methods() {
BIND_ENUM_CONSTANT(DUPLICATE_SCRIPTS);
BIND_ENUM_CONSTANT(DUPLICATE_USE_INSTANCING);
+ ADD_SIGNAL(MethodInfo("ready"));
ADD_SIGNAL(MethodInfo("renamed"));
ADD_SIGNAL(MethodInfo("tree_entered"));
ADD_SIGNAL(MethodInfo("tree_exiting"));
@@ -2815,8 +2833,8 @@ void Node::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name");
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename");
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer_api");
- ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "custom_multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer_api", "get_custom_multiplayer_api");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer", "get_custom_multiplayer");
BIND_VMETHOD(MethodInfo("_process", PropertyInfo(Variant::REAL, "delta")));
BIND_VMETHOD(MethodInfo("_physics_process", PropertyInfo(Variant::REAL, "delta")));
diff --git a/scene/main/node.h b/scene/main/node.h
index b9bafb1ed1..540f34cba7 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -151,7 +151,7 @@ private:
NAME_CASING_SNAKE_CASE
};
- Ref<MultiplayerAPI> multiplayer_api;
+ Ref<MultiplayerAPI> multiplayer;
void _print_tree_pretty(const String prefix, const bool last);
void _print_tree(const Node *p_node);
@@ -190,6 +190,12 @@ private:
void _set_tree(SceneTree *p_tree);
+#ifdef TOOLS_ENABLED
+ friend class SceneTreeEditor;
+#endif
+ static String invalid_character;
+ static bool _validate_node_name(String &p_name);
+
protected:
void _block() { data.blocked++; }
void _unblock() { data.blocked--; }
@@ -231,6 +237,7 @@ public:
NOTIFICATION_TRANSLATION_CHANGED = 24,
NOTIFICATION_INTERNAL_PROCESS = 25,
NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26,
+ NOTIFICATION_POST_ENTER_TREE = 27,
};
@@ -374,10 +381,8 @@ public:
void force_parent_owned() { data.parent_owned = true; } //hack to avoid duplicate nodes
-#ifdef TOOLS_ENABLED
void set_import_path(const NodePath &p_import_path); //path used when imported, used by scene editors to keep tracking
NodePath get_import_path() const;
-#endif
bool is_owned_by_parent() const;
@@ -415,9 +420,9 @@ public:
void rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
void rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value);
- Ref<MultiplayerAPI> get_multiplayer_api() const;
- Ref<MultiplayerAPI> get_custom_multiplayer_api() const;
- void set_custom_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api);
+ Ref<MultiplayerAPI> get_multiplayer() const;
+ Ref<MultiplayerAPI> get_custom_multiplayer() const;
+ void set_custom_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
const Map<StringName, RPCMode>::Element *get_node_rpc_mode(const StringName &p_method);
const Map<StringName, RPCMode>::Element *get_node_rset_mode(const StringName &p_property);
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 4419dfe70f..607dbebf6c 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -33,6 +33,7 @@
#include "editor/editor_node.h"
#include "io/marshalls.h"
#include "io/resource_loader.h"
+#include "main/input_default.h"
#include "message_queue.h"
#include "node.h"
#include "os/keyboard.h"
@@ -484,7 +485,7 @@ bool SceneTree::idle(float p_time) {
idle_process_time = p_time;
- multiplayer_api->poll();
+ multiplayer->poll();
emit_signal("idle_frame");
@@ -620,6 +621,13 @@ void SceneTree::_notification(int p_notification) {
case NOTIFICATION_WM_FOCUS_IN:
case NOTIFICATION_WM_FOCUS_OUT: {
+ if (p_notification == NOTIFICATION_WM_FOCUS_IN) {
+ InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton());
+ if (id) {
+ id->ensure_touch_mouse_raised();
+ }
+ }
+
get_root()->propagate_notification(p_notification);
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {
@@ -1197,16 +1205,20 @@ void SceneTree::set_screen_stretch(StretchMode p_mode, StretchAspect p_aspect, c
_update_root_rect();
}
-#ifdef TOOLS_ENABLED
void SceneTree::set_edited_scene_root(Node *p_node) {
+#ifdef TOOLS_ENABLED
edited_scene_root = p_node;
+#endif
}
Node *SceneTree::get_edited_scene_root() const {
+#ifdef TOOLS_ENABLED
return edited_scene_root;
-}
+#else
+ return NULL;
#endif
+}
void SceneTree::set_current_scene(Node *p_scene) {
@@ -1656,70 +1668,70 @@ void SceneTree::_server_disconnected() {
emit_signal("server_disconnected");
}
-Ref<MultiplayerAPI> SceneTree::get_multiplayer_api() const {
- return multiplayer_api;
+Ref<MultiplayerAPI> SceneTree::get_multiplayer() const {
+ return multiplayer;
}
-void SceneTree::set_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api) {
- ERR_FAIL_COND(!p_multiplayer_api.is_valid());
+void SceneTree::set_multiplayer(Ref<MultiplayerAPI> p_multiplayer) {
+ ERR_FAIL_COND(!p_multiplayer.is_valid());
- if (multiplayer_api.is_valid()) {
- multiplayer_api->disconnect("network_peer_connected", this, "_network_peer_connected");
- multiplayer_api->disconnect("network_peer_disconnected", this, "_network_peer_disconnected");
- multiplayer_api->disconnect("connected_to_server", this, "_connected_to_server");
- multiplayer_api->disconnect("connection_failed", this, "_connection_failed");
- multiplayer_api->disconnect("server_disconnected", this, "_server_disconnected");
+ if (multiplayer.is_valid()) {
+ multiplayer->disconnect("network_peer_connected", this, "_network_peer_connected");
+ multiplayer->disconnect("network_peer_disconnected", this, "_network_peer_disconnected");
+ multiplayer->disconnect("connected_to_server", this, "_connected_to_server");
+ multiplayer->disconnect("connection_failed", this, "_connection_failed");
+ multiplayer->disconnect("server_disconnected", this, "_server_disconnected");
}
- multiplayer_api = p_multiplayer_api;
- multiplayer_api->set_root_node(root);
+ multiplayer = p_multiplayer;
+ multiplayer->set_root_node(root);
- multiplayer_api->connect("network_peer_connected", this, "_network_peer_connected");
- multiplayer_api->connect("network_peer_disconnected", this, "_network_peer_disconnected");
- multiplayer_api->connect("connected_to_server", this, "_connected_to_server");
- multiplayer_api->connect("connection_failed", this, "_connection_failed");
- multiplayer_api->connect("server_disconnected", this, "_server_disconnected");
+ multiplayer->connect("network_peer_connected", this, "_network_peer_connected");
+ multiplayer->connect("network_peer_disconnected", this, "_network_peer_disconnected");
+ multiplayer->connect("connected_to_server", this, "_connected_to_server");
+ multiplayer->connect("connection_failed", this, "_connection_failed");
+ multiplayer->connect("server_disconnected", this, "_server_disconnected");
}
void SceneTree::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer) {
- multiplayer_api->set_network_peer(p_network_peer);
+ multiplayer->set_network_peer(p_network_peer);
}
Ref<NetworkedMultiplayerPeer> SceneTree::get_network_peer() const {
- return multiplayer_api->get_network_peer();
+ return multiplayer->get_network_peer();
}
bool SceneTree::is_network_server() const {
- return multiplayer_api->is_network_server();
+ return multiplayer->is_network_server();
}
bool SceneTree::has_network_peer() const {
- return multiplayer_api->has_network_peer();
+ return multiplayer->has_network_peer();
}
int SceneTree::get_network_unique_id() const {
- return multiplayer_api->get_network_unique_id();
+ return multiplayer->get_network_unique_id();
}
Vector<int> SceneTree::get_network_connected_peers() const {
- return multiplayer_api->get_network_connected_peers();
+ return multiplayer->get_network_connected_peers();
}
int SceneTree::get_rpc_sender_id() const {
- return multiplayer_api->get_rpc_sender_id();
+ return multiplayer->get_rpc_sender_id();
}
void SceneTree::set_refuse_new_network_connections(bool p_refuse) {
- multiplayer_api->set_refuse_new_network_connections(p_refuse);
+ multiplayer->set_refuse_new_network_connections(p_refuse);
}
bool SceneTree::is_refusing_new_network_connections() const {
- return multiplayer_api->is_refusing_new_network_connections();
+ return multiplayer->is_refusing_new_network_connections();
}
void SceneTree::_bind_methods() {
@@ -1737,10 +1749,8 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_debug_navigation_hint", "enable"), &SceneTree::set_debug_navigation_hint);
ClassDB::bind_method(D_METHOD("is_debugging_navigation_hint"), &SceneTree::is_debugging_navigation_hint);
-#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("set_edited_scene_root", "scene"), &SceneTree::set_edited_scene_root);
ClassDB::bind_method(D_METHOD("get_edited_scene_root"), &SceneTree::get_edited_scene_root);
-#endif
ClassDB::bind_method(D_METHOD("set_pause", "enable"), &SceneTree::set_pause);
ClassDB::bind_method(D_METHOD("is_paused"), &SceneTree::is_paused);
@@ -1790,8 +1800,8 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene);
- ClassDB::bind_method(D_METHOD("set_multiplayer_api", "multiplayer_api"), &SceneTree::set_multiplayer_api);
- ClassDB::bind_method(D_METHOD("get_multiplayer_api"), &SceneTree::get_multiplayer_api);
+ ClassDB::bind_method(D_METHOD("set_multiplayer", "multiplayer"), &SceneTree::set_multiplayer);
+ ClassDB::bind_method(D_METHOD("get_multiplayer"), &SceneTree::get_multiplayer);
ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &SceneTree::set_network_peer);
ClassDB::bind_method(D_METHOD("get_network_peer"), &SceneTree::get_network_peer);
ClassDB::bind_method(D_METHOD("is_network_server"), &SceneTree::is_network_server);
@@ -1815,13 +1825,11 @@ void SceneTree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused"), "set_pause", "is_paused");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_font_oversampling"), "set_use_font_oversampling", "is_using_font_oversampling");
-#ifdef TOOLS_ENABLED
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "edited_scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_edited_scene_root", "get_edited_scene_root");
-#endif
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "current_scene", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_current_scene", "get_current_scene");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "", "get_root");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_multiplayer_api", "get_multiplayer_api");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_multiplayer", "get_multiplayer");
ADD_SIGNAL(MethodInfo("tree_changed"));
ADD_SIGNAL(MethodInfo("node_added", PropertyInfo(Variant::OBJECT, "node")));
@@ -1926,7 +1934,7 @@ SceneTree::SceneTree() {
root->set_world(Ref<World>(memnew(World)));
// Initialize network state
- set_multiplayer_api(Ref<MultiplayerAPI>(memnew(MultiplayerAPI)));
+ set_multiplayer(Ref<MultiplayerAPI>(memnew(MultiplayerAPI)));
//root->set_world_2d( Ref<World2D>( memnew( World2D )));
root->set_as_audio_listener(true);
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 203c1d9c9c..6e0156546e 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -185,7 +185,7 @@ private:
///network///
- Ref<MultiplayerAPI> multiplayer_api;
+ Ref<MultiplayerAPI> multiplayer;
void _network_peer_connected(int p_id);
void _network_peer_disconnected(int p_id);
@@ -390,10 +390,8 @@ public:
//void change_scene(const String& p_path);
//Node *get_loaded_scene();
-#ifdef TOOLS_ENABLED
void set_edited_scene_root(Node *p_node);
Node *get_edited_scene_root() const;
-#endif
void set_current_scene(Node *p_scene);
Node *get_current_scene() const;
@@ -412,8 +410,8 @@ public:
//network API
- Ref<MultiplayerAPI> get_multiplayer_api() const;
- void set_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api);
+ Ref<MultiplayerAPI> get_multiplayer() const;
+ void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer);
void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer);
Ref<NetworkedMultiplayerPeer> get_network_peer() const;
bool is_network_server() const;
diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp
index ad2cdbfd0f..c285694dfa 100755
--- a/scene/main/timer.cpp
+++ b/scene/main/timer.cpp
@@ -107,7 +107,10 @@ bool Timer::has_autostart() const {
return autostart;
}
-void Timer::start() {
+void Timer::start(float p_time) {
+ if (p_time > 0) {
+ set_wait_time(p_time);
+ }
time_left = wait_time;
_set_process(true);
}
@@ -185,7 +188,7 @@ void Timer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_autostart", "enable"), &Timer::set_autostart);
ClassDB::bind_method(D_METHOD("has_autostart"), &Timer::has_autostart);
- ClassDB::bind_method(D_METHOD("start"), &Timer::start);
+ ClassDB::bind_method(D_METHOD("start", "time_sec"), &Timer::start, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("stop"), &Timer::stop);
ClassDB::bind_method(D_METHOD("set_paused", "paused"), &Timer::set_paused);
diff --git a/scene/main/timer.h b/scene/main/timer.h
index 410d985407..2f42252a7e 100755
--- a/scene/main/timer.h
+++ b/scene/main/timer.h
@@ -64,7 +64,7 @@ public:
void set_autostart(bool p_start);
bool has_autostart() const;
- void start();
+ void start(float p_time = -1);
void stop();
void set_paused(bool p_paused);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 568a765420..f631fd6f3a 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -72,6 +72,9 @@ void ViewportTexture::setup_local_to_scene() {
vp->viewport_textures.insert(this);
VS::get_singleton()->texture_set_proxy(proxy, vp->texture_rid);
+
+ vp->texture_flags = flags;
+ VS::get_singleton()->texture_set_flags(vp->texture_rid, flags);
}
void ViewportTexture::set_viewport_path_in_scene(const NodePath &p_path) {
@@ -122,20 +125,18 @@ Ref<Image> ViewportTexture::get_data() const {
return VS::get_singleton()->texture_get_data(vp->texture_rid);
}
void ViewportTexture::set_flags(uint32_t p_flags) {
+ flags = p_flags;
if (!vp)
return;
- vp->texture_flags = p_flags;
- VS::get_singleton()->texture_set_flags(vp->texture_rid, p_flags);
+ vp->texture_flags = flags;
+ VS::get_singleton()->texture_set_flags(vp->texture_rid, flags);
}
uint32_t ViewportTexture::get_flags() const {
- if (!vp)
- return 0;
-
- return vp->texture_flags;
+ return flags;
}
void ViewportTexture::_bind_methods() {
@@ -149,6 +150,7 @@ void ViewportTexture::_bind_methods() {
ViewportTexture::ViewportTexture() {
vp = NULL;
+ flags = 0;
set_local_to_scene(true);
proxy = VS::get_singleton()->texture_create();
}
@@ -189,6 +191,7 @@ Viewport::GUI::GUI() {
tooltip = NULL;
tooltip_popup = NULL;
tooltip_label = NULL;
+ subwindow_visibility_dirty = false;
subwindow_order_dirty = false;
}
@@ -1251,6 +1254,24 @@ void Viewport::warp_mouse(const Vector2 &p_pos) {
Input::get_singleton()->warp_mouse_position(gpos);
}
+void Viewport::_gui_prepare_subwindows() {
+
+ if (gui.subwindow_visibility_dirty) {
+
+ gui.subwindows.clear();
+ for (List<Control *>::Element *E = gui.all_known_subwindows.front(); E; E = E->next()) {
+ if (E->get()->is_visible_in_tree()) {
+ gui.subwindows.push_back(E->get());
+ }
+ }
+
+ gui.subwindow_visibility_dirty = false;
+ gui.subwindow_order_dirty = true;
+ }
+
+ _gui_sort_subwindows();
+}
+
void Viewport::_gui_sort_subwindows() {
if (!gui.subwindow_order_dirty)
@@ -1326,7 +1347,7 @@ void Viewport::_gui_show_tooltip() {
gui.tooltip_label->set_anchor_and_margin(MARGIN_RIGHT, Control::ANCHOR_END, -ttp->get_margin(MARGIN_RIGHT));
gui.tooltip_label->set_anchor_and_margin(MARGIN_BOTTOM, Control::ANCHOR_END, -ttp->get_margin(MARGIN_BOTTOM));
gui.tooltip_label->set_text(tooltip.strip_edges());
- Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_label->get_combined_minimum_size() + ttp->get_minimum_size());
+ Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_label->get_minimum_size() + ttp->get_minimum_size());
Rect2 vr = gui.tooltip_label->get_viewport_rect();
if (r.size.x + r.position.x > vr.size.x)
r.position.x = vr.size.x - r.size.x;
@@ -1393,7 +1414,7 @@ void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_inpu
Control *Viewport::_gui_find_control(const Point2 &p_global) {
- _gui_sort_subwindows();
+ _gui_prepare_subwindows();
for (List<Control *>::Element *E = gui.subwindows.back(); E; E = E->prev()) {
@@ -2093,8 +2114,14 @@ List<Control *>::Element *Viewport::_gui_add_root_control(Control *p_control) {
List<Control *>::Element *Viewport::_gui_add_subwindow_control(Control *p_control) {
- gui.subwindow_order_dirty = true;
- return gui.subwindows.push_back(p_control);
+ p_control->connect("visibility_changed", this, "_subwindow_visibility_changed");
+
+ if (p_control->is_visible_in_tree()) {
+ gui.subwindow_order_dirty = true;
+ gui.subwindows.push_back(p_control);
+ }
+
+ return gui.all_known_subwindows.push_back(p_control);
}
void Viewport::_gui_set_subwindow_order_dirty() {
@@ -2168,9 +2195,7 @@ void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) {
p_control->set_position(gui.last_mouse_pos);
p_base->get_root_parent_control()->add_child(p_control); //add as child of viewport
p_control->raise();
- if (gui.drag_preview) {
- memdelete(gui.drag_preview);
- }
+
gui.drag_preview = p_control;
}
@@ -2181,7 +2206,17 @@ void Viewport::_gui_remove_root_control(List<Control *>::Element *RI) {
void Viewport::_gui_remove_subwindow_control(List<Control *>::Element *SI) {
- gui.subwindows.erase(SI);
+ ERR_FAIL_COND(!SI);
+
+ Control *control = SI->get();
+
+ control->disconnect("visibility_changed", this, "_subwindow_visibility_changed");
+
+ List<Control *>::Element *E = gui.subwindows.find(control);
+ if (E)
+ gui.subwindows.erase(E);
+
+ gui.all_known_subwindows.erase(SI);
}
void Viewport::_gui_unfocus_control(Control *p_control) {
@@ -2208,7 +2243,7 @@ void Viewport::_gui_hid_control(Control *p_control) {
*/
if (gui.key_focus == p_control)
- gui.key_focus = NULL;
+ _gui_remove_focus();
if (gui.mouse_over == p_control)
gui.mouse_over = NULL;
if (gui.tooltip == p_control)
@@ -2477,6 +2512,16 @@ bool Viewport::is_3d_disabled() const {
return disable_3d;
}
+void Viewport::set_keep_3d_linear(bool p_keep_3d_linear) {
+ keep_3d_linear = p_keep_3d_linear;
+ VS::get_singleton()->viewport_set_keep_3d_linear(viewport, keep_3d_linear);
+}
+
+bool Viewport::get_keep_3d_linear() const {
+
+ return keep_3d_linear;
+}
+
Variant Viewport::gui_get_drag_data() const {
return gui.drag_data;
}
@@ -2660,6 +2705,9 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_disable_3d", "disable"), &Viewport::set_disable_3d);
ClassDB::bind_method(D_METHOD("is_3d_disabled"), &Viewport::is_3d_disabled);
+ ClassDB::bind_method(D_METHOD("set_keep_3d_linear", "keep_3d_linear"), &Viewport::set_keep_3d_linear);
+ ClassDB::bind_method(D_METHOD("get_keep_3d_linear"), &Viewport::get_keep_3d_linear);
+
ClassDB::bind_method(D_METHOD("_gui_show_tooltip"), &Viewport::_gui_show_tooltip);
ClassDB::bind_method(D_METHOD("_gui_remove_focus"), &Viewport::_gui_remove_focus);
ClassDB::bind_method(D_METHOD("_post_gui_grab_click_focus"), &Viewport::_post_gui_grab_click_focus);
@@ -2673,6 +2721,8 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shadow_atlas_quadrant_subdiv", "quadrant", "subdiv"), &Viewport::set_shadow_atlas_quadrant_subdiv);
ClassDB::bind_method(D_METHOD("get_shadow_atlas_quadrant_subdiv", "quadrant"), &Viewport::get_shadow_atlas_quadrant_subdiv);
+ ClassDB::bind_method(D_METHOD("_subwindow_visibility_changed"), &Viewport::_subwindow_visibility_changed);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "arvr"), "set_use_arvr", "use_arvr");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
@@ -2684,6 +2734,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x"), "set_msaa", "get_msaa");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr"), "set_hdr", "get_hdr");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_3d_linear"), "set_keep_3d_linear", "get_keep_3d_linear");
ADD_PROPERTY(PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_ENUM, "2D,2D No-Sampling,3D,3D No-Effects"), "set_usage", "get_usage");
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw");
ADD_GROUP("Render Target", "render_target_");
@@ -2752,6 +2803,13 @@ void Viewport::_bind_methods() {
BIND_ENUM_CONSTANT(CLEAR_MODE_ONLY_NEXT_FRAME);
}
+void Viewport::_subwindow_visibility_changed() {
+
+ // unfortunately, we don't know the sender, i.e. which subwindow changed;
+ // so we have to check them all.
+ gui.subwindow_visibility_dirty = true;
+}
+
Viewport::Viewport() {
world_2d = Ref<World2D>(memnew(World2D));
@@ -2806,6 +2864,7 @@ Viewport::Viewport() {
disable_input = false;
disable_3d = false;
+ keep_3d_linear = false;
//window tooltip
gui.tooltip_timer = -1;
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 94e49033e0..c1ef58de69 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -58,6 +58,7 @@ class ViewportTexture : public Texture {
friend class Viewport;
Viewport *vp;
+ uint32_t flags;
RID proxy;
@@ -226,6 +227,7 @@ private:
void _update_global_transform();
bool disable_3d;
+ bool keep_3d_linear;
UpdateMode update_mode;
RID texture_rid;
uint32_t texture_flags;
@@ -266,7 +268,9 @@ private:
List<Control *> modal_stack;
Transform2D focus_inv_xform;
bool subwindow_order_dirty;
- List<Control *> subwindows;
+ bool subwindow_visibility_dirty;
+ List<Control *> subwindows; // visible subwindows
+ List<Control *> all_known_subwindows;
bool roots_order_dirty;
List<Control *> roots;
int canvas_sort_index; //for sorting items with canvas as root
@@ -277,6 +281,7 @@ private:
bool disable_input;
void _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input);
+ void _gui_prepare_subwindows();
void _gui_sort_subwindows();
void _gui_sort_roots();
void _gui_sort_modal_stack();
@@ -433,6 +438,9 @@ public:
void set_disable_3d(bool p_disable);
bool is_3d_disabled() const;
+ void set_keep_3d_linear(bool p_keep_3d_linear);
+ bool get_keep_3d_linear() const;
+
void set_attach_to_screen_rect(const Rect2 &p_rect);
Rect2 get_attach_to_screen_rect() const;
@@ -463,6 +471,8 @@ public:
void set_snap_controls_to_pixels(bool p_enable);
bool is_snap_controls_to_pixels_enabled() const;
+ void _subwindow_visibility_changed();
+
Viewport();
~Viewport();
};
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 19d8f09ebe..7533fa5f6c 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -58,6 +58,7 @@
#include "scene/2d/ray_cast_2d.h"
#include "scene/2d/remote_transform_2d.h"
#include "scene/2d/screen_button.h"
+#include "scene/2d/skeleton_2d.h"
#include "scene/2d/sprite.h"
#include "scene/2d/tile_map.h"
#include "scene/2d/visibility_notifier_2d.h"
@@ -389,6 +390,7 @@ void register_scene_types() {
ClassDB::register_class<RigidBody>();
ClassDB::register_class<KinematicCollision>();
ClassDB::register_class<KinematicBody>();
+ ClassDB::register_class<PhysicalBone>();
ClassDB::register_class<VehicleBody>();
ClassDB::register_class<VehicleWheel>();
@@ -449,6 +451,8 @@ void register_scene_types() {
ClassDB::register_class<VisibilityNotifier2D>();
ClassDB::register_class<VisibilityEnabler2D>();
ClassDB::register_class<Polygon2D>();
+ ClassDB::register_class<Skeleton2D>();
+ ClassDB::register_class<Bone2D>();
ClassDB::register_class<Light2D>();
ClassDB::register_class<LightOccluder2D>();
ClassDB::register_class<OccluderPolygon2D>();
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 5fd6f6c74d..4ec1e8973d 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -806,6 +806,87 @@ float Curve2D::get_bake_interval() const {
return bake_interval;
}
+Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const {
+ // Brute force method
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ int pc = baked_point_cache.size();
+ if (pc == 0) {
+ ERR_EXPLAIN("No points in Curve2D");
+ ERR_FAIL_COND_V(pc == 0, Vector2());
+ }
+
+ if (pc == 1)
+ return baked_point_cache.get(0);
+
+ PoolVector2Array::Read r = baked_point_cache.read();
+
+ Vector2 nearest;
+ float nearest_dist = -1.0f;
+
+ for (int i = 0; i < pc - 1; i++) {
+ Vector2 origin = r[i];
+ Vector2 direction = (r[i + 1] - origin) / bake_interval;
+
+ float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ Vector2 proj = origin + direction * d;
+
+ float dist = proj.distance_squared_to(p_to_point);
+
+ if (nearest_dist < 0.0f || dist < nearest_dist) {
+ nearest = proj;
+ nearest_dist = dist;
+ }
+ }
+
+ return nearest;
+}
+
+float Curve2D::get_closest_offset(const Vector2 &p_to_point) const {
+ // Brute force method
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ int pc = baked_point_cache.size();
+ if (pc == 0) {
+ ERR_EXPLAIN("No points in Curve2D");
+ ERR_FAIL_COND_V(pc == 0, 0.0f);
+ }
+
+ if (pc == 1)
+ return 0.0f;
+
+ PoolVector2Array::Read r = baked_point_cache.read();
+
+ float nearest = 0.0f;
+ float nearest_dist = -1.0f;
+ float offset = 0.0f;
+
+ for (int i = 0; i < pc - 1; i++) {
+ Vector2 origin = r[i];
+ Vector2 direction = (r[i + 1] - origin) / bake_interval;
+
+ float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ Vector2 proj = origin + direction * d;
+
+ float dist = proj.distance_squared_to(p_to_point);
+
+ if (nearest_dist < 0.0f || dist < nearest_dist) {
+ nearest = offset + d;
+ nearest_dist = dist;
+ }
+
+ offset += bake_interval;
+ }
+
+ return nearest;
+}
+
Dictionary Curve2D::_get_data() const {
Dictionary dc;
@@ -909,6 +990,8 @@ void Curve2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve2D::get_baked_length);
ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve2D::interpolate_baked, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve2D::get_baked_points);
+ ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve2D::get_closest_point);
+ ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve2D::get_closest_offset);
ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve2D::tessellate, DEFVAL(5), DEFVAL(4));
ClassDB::bind_method(D_METHOD("_get_data"), &Curve2D::_get_data);
@@ -1276,6 +1359,87 @@ PoolRealArray Curve3D::get_baked_tilts() const {
return baked_tilt_cache;
}
+Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const {
+ // Brute force method
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ int pc = baked_point_cache.size();
+ if (pc == 0) {
+ ERR_EXPLAIN("No points in Curve3D");
+ ERR_FAIL_COND_V(pc == 0, Vector3());
+ }
+
+ if (pc == 1)
+ return baked_point_cache.get(0);
+
+ PoolVector3Array::Read r = baked_point_cache.read();
+
+ Vector3 nearest;
+ float nearest_dist = -1.0f;
+
+ for (int i = 0; i < pc - 1; i++) {
+ Vector3 origin = r[i];
+ Vector3 direction = (r[i + 1] - origin) / bake_interval;
+
+ float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ Vector3 proj = origin + direction * d;
+
+ float dist = proj.distance_squared_to(p_to_point);
+
+ if (nearest_dist < 0.0f || dist < nearest_dist) {
+ nearest = proj;
+ nearest_dist = dist;
+ }
+ }
+
+ return nearest;
+}
+
+float Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
+ // Brute force method
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ int pc = baked_point_cache.size();
+ if (pc == 0) {
+ ERR_EXPLAIN("No points in Curve3D");
+ ERR_FAIL_COND_V(pc == 0, 0.0f);
+ }
+
+ if (pc == 1)
+ return 0.0f;
+
+ PoolVector3Array::Read r = baked_point_cache.read();
+
+ float nearest = 0.0f;
+ float nearest_dist = -1.0f;
+ float offset = 0.0f;
+
+ for (int i = 0; i < pc - 1; i++) {
+ Vector3 origin = r[i];
+ Vector3 direction = (r[i + 1] - origin) / bake_interval;
+
+ float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ Vector3 proj = origin + direction * d;
+
+ float dist = proj.distance_squared_to(p_to_point);
+
+ if (nearest_dist < 0.0f || dist < nearest_dist) {
+ nearest = offset + d;
+ nearest_dist = dist;
+ }
+
+ offset += bake_interval;
+ }
+
+ return nearest;
+}
+
void Curve3D::set_bake_interval(float p_tolerance) {
bake_interval = p_tolerance;
@@ -1404,6 +1568,8 @@ void Curve3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve3D::interpolate_baked, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve3D::get_baked_points);
ClassDB::bind_method(D_METHOD("get_baked_tilts"), &Curve3D::get_baked_tilts);
+ ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve3D::get_closest_point);
+ ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve3D::get_closest_offset);
ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve3D::tessellate, DEFVAL(5), DEFVAL(4));
ClassDB::bind_method(D_METHOD("_get_data"), &Curve3D::_get_data);
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 4f55e4055c..492eb05d1e 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -199,6 +199,8 @@ public:
float get_baked_length() const;
Vector2 interpolate_baked(float p_offset, bool p_cubic = false) const;
PoolVector2Array get_baked_points() const; //useful for going through
+ Vector2 get_closest_point(const Vector2 &p_to_point) const;
+ float get_closest_offset(const Vector2 &p_to_point) const;
PoolVector2Array tessellate(int p_max_stages = 5, float p_tolerance = 4) const; //useful for display
@@ -268,6 +270,8 @@ public:
float interpolate_baked_tilt(float p_offset) const;
PoolVector3Array get_baked_points() const; //useful for going through
PoolRealArray get_baked_tilts() const; //useful for going through
+ Vector3 get_closest_point(const Vector3 &p_to_point) const;
+ float get_closest_offset(const Vector3 &p_to_point) const;
PoolVector3Array tessellate(int p_max_stages = 5, float p_tolerance = 4) const; //useful for display
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index ea70797530..3ea856541e 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -415,6 +415,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "Label", Color(1, 1, 1));
theme->set_color("font_color_shadow", "Label", Color(0, 0, 0, 0));
+ theme->set_color("font_outline_modulate", "Label", Color(1, 1, 1));
theme->set_constant("shadow_offset_x", "Label", 1 * scale);
theme->set_constant("shadow_offset_y", "Label", 1 * scale);
@@ -543,6 +544,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("updown", "SpinBox", make_icon(spinbox_updown_png));
+ //scroll container
+ Ref<StyleBoxEmpty> empty;
+ empty.instance();
+ theme->set_stylebox("bg", "ScrollContainer", empty);
+
// WindowDialog
theme->set_stylebox("panel", "WindowDialog", sb_expand(make_stylebox(popup_window_png, 10, 26, 10, 8), 8, 24, 8, 6));
@@ -818,6 +824,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color_selected", "RichTextLabel", font_color_selection);
theme->set_color("selection_color", "RichTextLabel", Color(0.1, 0.1, 1, 0.8));
+ theme->set_color("font_color_shadow", "RichTextLabel", Color(0, 0, 0, 0));
+
+ theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * scale);
+ theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * scale);
+ theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * scale);
+
theme->set_constant("line_separation", "RichTextLabel", 1 * scale);
theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale);
theme->set_constant("table_vseparation", "RichTextLabel", 3 * scale);
diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp
index 26e29b3ccb..05493d5777 100644
--- a/scene/resources/dynamic_font.cpp
+++ b/scene/resources/dynamic_font.cpp
@@ -33,8 +33,12 @@
#include "os/file_access.h"
#include "os/os.h"
-bool DynamicFontData::CacheID::operator<(CacheID right) const {
+#include FT_STROKER_H
+
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+bool DynamicFontData::CacheID::operator<(CacheID right) const {
return key < right.key;
}
@@ -212,8 +216,8 @@ Error DynamicFontAtSize::_load() {
error = FT_Set_Pixel_Sizes(face, 0, id.size * oversampling);
}
- ascent = (face->size->metrics.ascender >> 6) / oversampling * scale_color_font;
- descent = (-face->size->metrics.descender >> 6) / oversampling * scale_color_font;
+ ascent = (face->size->metrics.ascender / 64.0) / oversampling * scale_color_font;
+ descent = (-face->size->metrics.descender / 64.0) / oversampling * scale_color_font;
linegap = 0;
texture_flags = 0;
if (id.mipmaps)
@@ -241,18 +245,11 @@ float DynamicFontAtSize::get_descent() const {
return descent;
}
-Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const {
+const Pair<const DynamicFontAtSize::Character *, DynamicFontAtSize *> DynamicFontAtSize::_find_char_with_font(CharType p_char, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const {
+ const Character *chr = char_map.getptr(p_char);
+ ERR_FAIL_COND_V(!chr, (Pair<const Character *, DynamicFontAtSize *>(NULL, NULL)));
- if (!valid)
- return Size2(1, 1);
- const_cast<DynamicFontAtSize *>(this)->_update_char(p_char);
-
- const Character *c = char_map.getptr(p_char);
- ERR_FAIL_COND_V(!c, Size2());
-
- Size2 ret(0, get_height());
-
- if (!c->found) {
+ if (!chr->found) {
//not found, try in fallbacks
for (int i = 0; i < p_fallbacks.size(); i++) {
@@ -262,52 +259,54 @@ Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next, const V
continue;
fb->_update_char(p_char);
- const Character *ch = fb->char_map.getptr(p_char);
- ERR_CONTINUE(!ch);
+ const Character *fallback_chr = fb->char_map.getptr(p_char);
+ ERR_CONTINUE(!fallback_chr);
- if (!ch->found)
+ if (!fallback_chr->found)
continue;
- c = ch;
- break;
+ return Pair<const Character *, DynamicFontAtSize *>(fallback_chr, fb);
}
- //not found, try 0xFFFD to display 'not found'.
- if (!c->found) {
-
- const_cast<DynamicFontAtSize *>(this)->_update_char(0xFFFD);
- c = char_map.getptr(0xFFFD);
- ERR_FAIL_COND_V(!c, Size2());
- }
+ //not found, try 0xFFFD to display 'not found'.
+ const_cast<DynamicFontAtSize *>(this)->_update_char(0xFFFD);
+ chr = char_map.getptr(0xFFFD);
+ ERR_FAIL_COND_V(!chr, (Pair<const Character *, DynamicFontAtSize *>(NULL, NULL)));
}
- if (c->found) {
- ret.x = c->advance;
- }
+ return Pair<const Character *, DynamicFontAtSize *>(chr, const_cast<DynamicFontAtSize *>(this));
+}
+
+float DynamicFontAtSize::_get_kerning_advance(const DynamicFontAtSize *font, CharType p_char, CharType p_next) const {
+ float advance = 0.0;
if (p_next) {
FT_Vector delta;
- FT_Get_Kerning(face, p_char, p_next, FT_KERNING_DEFAULT, &delta);
+ FT_Get_Kerning(font->face, p_char, p_next, FT_KERNING_DEFAULT, &delta);
+ advance = (delta.x / 64.0) / oversampling;
+ }
+
+ return advance;
+}
- if (delta.x == 0) {
- for (int i = 0; i < p_fallbacks.size(); i++) {
+Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const {
- DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr());
- if (!fb->valid)
- continue;
+ if (!valid)
+ return Size2(1, 1);
+ const_cast<DynamicFontAtSize *>(this)->_update_char(p_char);
- FT_Get_Kerning(fb->face, p_char, p_next, FT_KERNING_DEFAULT, &delta);
+ Pair<const Character *, DynamicFontAtSize *> char_pair_with_font = _find_char_with_font(p_char, p_fallbacks);
+ const Character *ch = char_pair_with_font.first;
+ DynamicFontAtSize *font = char_pair_with_font.second;
+ ERR_FAIL_COND_V(!ch, Size2());
- if (delta.x == 0)
- continue;
+ Size2 ret(0, get_height());
- ret.x += (delta.x >> 6) / oversampling;
- break;
- }
- } else {
- ret.x += (delta.x >> 6) / oversampling;
- }
+ if (ch->found) {
+ ret.x = ch->advance;
}
+ ret.x += _get_kerning_advance(font, p_char, p_next);
+
return ret;
}
@@ -321,102 +320,42 @@ void DynamicFontAtSize::set_texture_flags(uint32_t p_flags) {
}
}
-float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const {
+float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks, bool p_advance_only) const {
if (!valid)
return 0;
const_cast<DynamicFontAtSize *>(this)->_update_char(p_char);
- const Character *c = char_map.getptr(p_char);
+ Pair<const Character *, DynamicFontAtSize *> char_pair_with_font = _find_char_with_font(p_char, p_fallbacks);
+ const Character *ch = char_pair_with_font.first;
+ DynamicFontAtSize *font = char_pair_with_font.second;
- float advance = 0;
+ ERR_FAIL_COND_V(!ch, 0.0);
- if (!c->found) {
+ float advance = 0.0;
- //not found, try in fallbacks
- bool used_fallback = false;
-
- for (int i = 0; i < p_fallbacks.size(); i++) {
+ if (ch->found) {
+ ERR_FAIL_COND_V(ch->texture_idx < -1 || ch->texture_idx >= font->textures.size(), 0);
- DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr());
- if (!fb->valid)
- continue;
-
- fb->_update_char(p_char);
- const Character *ch = fb->char_map.getptr(p_char);
- ERR_CONTINUE(!ch);
-
- if (!ch->found)
- continue;
+ if (!p_advance_only && ch->texture_idx != -1) {
Point2 cpos = p_pos;
cpos.x += ch->h_align;
- cpos.y -= fb->get_ascent();
+ cpos.y -= font->get_ascent();
cpos.y += ch->v_align;
- ERR_FAIL_COND_V(ch->texture_idx < -1 || ch->texture_idx >= fb->textures.size(), 0);
- if (ch->texture_idx != -1) {
- Color modulate = p_modulate;
- if (FT_HAS_COLOR(fb->face)) {
- modulate.r = modulate.g = modulate.b = 1;
- }
- VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, ch->rect.size * Vector2(fb->scale_color_font, fb->scale_color_font)), fb->textures[ch->texture_idx].texture->get_rid(), ch->rect_uv, modulate, false, RID(), false);
- }
- advance = ch->advance;
- used_fallback = true;
- break;
- }
- //not found, try 0xFFFD to display 'not found'.
-
- if (!used_fallback) {
-
- const_cast<DynamicFontAtSize *>(this)->_update_char(0xFFFD);
- c = char_map.getptr(0xFFFD);
- }
- }
-
- if (c->found) {
-
- Point2 cpos = p_pos;
- cpos.x += c->h_align;
- cpos.y -= get_ascent();
- cpos.y += c->v_align;
- ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0);
- if (c->texture_idx != -1) {
Color modulate = p_modulate;
if (FT_HAS_COLOR(face)) {
- modulate.r = modulate.g = modulate.b = 1;
+ modulate.r = modulate.g = modulate.b = 1.0;
}
- VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size * Vector2(scale_color_font, scale_color_font)), textures[c->texture_idx].texture->get_rid(), c->rect_uv, modulate, false, RID(), false);
+ RID texture = font->textures[ch->texture_idx].texture->get_rid();
+ VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, ch->rect.size * Vector2(font->scale_color_font, font->scale_color_font)), texture, ch->rect_uv, modulate, false, RID(), false);
}
- advance = c->advance;
- //textures[c->texture_idx].texture->draw(p_canvas_item,Vector2());
- }
- if (p_next) {
-
- FT_Vector delta;
- FT_Get_Kerning(face, p_char, p_next, FT_KERNING_DEFAULT, &delta);
-
- if (delta.x == 0) {
- for (int i = 0; i < p_fallbacks.size(); i++) {
-
- DynamicFontAtSize *fb = const_cast<DynamicFontAtSize *>(p_fallbacks[i].ptr());
- if (!fb->valid)
- continue;
-
- FT_Get_Kerning(fb->face, p_char, p_next, FT_KERNING_DEFAULT, &delta);
-
- if (delta.x == 0)
- continue;
-
- advance += (delta.x >> 6) / oversampling;
- break;
- }
- } else {
- advance += (delta.x >> 6) / oversampling;
- }
+ advance = ch->advance;
}
+ advance += _get_kerning_advance(font, p_char, p_next);
+
return advance;
}
@@ -440,96 +379,37 @@ void DynamicFontAtSize::_ft_stream_close(FT_Stream stream) {
memdelete(f);
}
-void DynamicFontAtSize::_update_char(CharType p_char) {
-
- if (char_map.has(p_char))
- return;
-
- _THREAD_SAFE_METHOD_
-
- FT_GlyphSlot slot = face->glyph;
-
- if (FT_Get_Char_Index(face, p_char) == 0) {
- //not found
- Character ch;
- ch.texture_idx = -1;
- ch.advance = 0;
- ch.h_align = 0;
- ch.v_align = 0;
- ch.found = false;
-
- char_map[p_char] = ch;
- return;
- }
-
- int ft_hinting;
-
- switch (font->hinting) {
- case DynamicFontData::HINTING_NONE:
- ft_hinting = FT_LOAD_NO_HINTING;
- break;
- case DynamicFontData::HINTING_LIGHT:
- ft_hinting = FT_LOAD_TARGET_LIGHT;
- break;
- default:
- ft_hinting = FT_LOAD_TARGET_NORMAL;
- break;
- }
-
- int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0) | ft_hinting);
- if (!error) {
- error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
- }
- if (error) {
-
- int advance = 0;
- Character ch;
- ch.texture_idx = -1;
- ch.advance = advance;
- ch.h_align = 0;
- ch.v_align = 0;
- ch.found = false;
-
- char_map[p_char] = ch;
-
- return;
- }
-
- int w = slot->bitmap.width;
- int h = slot->bitmap.rows;
- int yofs = slot->bitmap_top;
- int xofs = slot->bitmap_left;
- int advance = slot->advance.x >> 6;
-
- int mw = w + rect_margin * 2;
- int mh = h + rect_margin * 2;
-
- if (mw > 4096 || mh > 4096) {
-
- ERR_FAIL_COND(mw > 4096);
- ERR_FAIL_COND(mh > 4096);
- }
+DynamicFontAtSize::Character DynamicFontAtSize::Character::not_found() {
+ Character ch;
+ ch.texture_idx = -1;
+ ch.advance = 0;
+ ch.h_align = 0;
+ ch.v_align = 0;
+ ch.found = false;
+ return ch;
+}
- //find a texture to fit this...
+DynamicFontAtSize::TexturePosition DynamicFontAtSize::_find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height) {
+ TexturePosition ret;
+ ret.index = -1;
+ ret.x = 0;
+ ret.y = 0;
- int color_size = slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
- Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
- int tex_index = -1;
- int tex_x = 0;
- int tex_y = 0;
+ int mw = p_width;
+ int mh = p_height;
for (int i = 0; i < textures.size(); i++) {
CharTexture &ct = textures[i];
- if (ct.texture->get_format() != require_format)
+ if (ct.texture->get_format() != p_image_format)
continue;
if (mw > ct.texture_size || mh > ct.texture_size) //too big for this texture
continue;
- tex_y = 0x7FFFFFFF;
- tex_x = 0;
+ ret.y = 0x7FFFFFFF;
+ ret.x = 0;
for (int j = 0; j < ct.texture_size - mw; j++) {
@@ -542,25 +422,27 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
max_y = y;
}
- if (max_y < tex_y) {
- tex_y = max_y;
- tex_x = j;
+ if (max_y < ret.y) {
+ ret.y = max_y;
+ ret.x = j;
}
}
- if (tex_y == 0x7FFFFFFF || tex_y + mh > ct.texture_size)
+ if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_size)
continue; //fail, could not fit it here
- tex_index = i;
+ ret.index = i;
break;
}
- if (tex_index == -1) {
+ //print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" X: "+itos(tex_x)+" Y: "+itos(tex_y));
+
+ if (ret.index == -1) {
//could not find texture to fit, create one
- tex_x = 0;
- tex_y = 0;
+ ret.x = 0;
+ ret.y = 0;
- int texsize = MAX(id.size * 8, 256);
+ int texsize = MAX(id.size * oversampling * 8, 256);
if (mw > texsize)
texsize = mw; //special case, adapt to it?
if (mh > texsize)
@@ -572,13 +454,13 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
CharTexture tex;
tex.texture_size = texsize;
- tex.imgdata.resize(texsize * texsize * color_size);
+ tex.imgdata.resize(texsize * texsize * p_color_size); //grayscale alpha
{
//zero texture
PoolVector<uint8_t>::Write w = tex.imgdata.write();
- ERR_FAIL_COND(texsize * texsize * color_size > tex.imgdata.size());
- for (int i = 0; i < texsize * texsize * color_size; i++) {
+ ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
+ for (int i = 0; i < texsize * texsize * p_color_size; i++) {
w[i] = 0;
}
}
@@ -587,12 +469,31 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
tex.offsets[i] = 0;
textures.push_back(tex);
- tex_index = textures.size() - 1;
+ ret.index = textures.size() - 1;
}
+ return ret;
+}
+
+DynamicFontAtSize::Character DynamicFontAtSize::_bitmap_to_character(FT_Bitmap bitmap, int yofs, int xofs, float advance) {
+ int w = bitmap.width;
+ int h = bitmap.rows;
+
+ int mw = w + rect_margin * 2;
+ int mh = h + rect_margin * 2;
+
+ ERR_FAIL_COND_V(mw > 4096, Character::not_found());
+ ERR_FAIL_COND_V(mh > 4096, Character::not_found());
+
+ int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
+ Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
+
+ TexturePosition tex_pos = _find_texture_pos_for_glyph(color_size, require_format, mw, mh);
+ ERR_FAIL_COND_V(tex_pos.index < 0, Character::not_found());
+
//fit character in char texture
- CharTexture &tex = textures[tex_index];
+ CharTexture &tex = textures[tex_pos.index];
{
PoolVector<uint8_t>::Write wr = tex.imgdata.write();
@@ -600,30 +501,30 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
- int ofs = ((i + tex_y + rect_margin) * tex.texture_size + j + tex_x + rect_margin) * color_size;
- ERR_FAIL_COND(ofs >= tex.imgdata.size());
- switch (slot->bitmap.pixel_mode) {
+ int ofs = ((i + tex_pos.y + rect_margin) * tex.texture_size + j + tex_pos.x + rect_margin) * color_size;
+ ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), Character::not_found());
+ switch (bitmap.pixel_mode) {
case FT_PIXEL_MODE_MONO: {
- int byte = i * slot->bitmap.pitch + (j >> 3);
+ int byte = i * bitmap.pitch + (j >> 3);
int bit = 1 << (7 - (j % 8));
wr[ofs + 0] = 255; //grayscale as 1
- wr[ofs + 1] = slot->bitmap.buffer[byte] & bit ? 255 : 0;
+ wr[ofs + 1] = bitmap.buffer[byte] & bit ? 255 : 0;
} break;
case FT_PIXEL_MODE_GRAY:
wr[ofs + 0] = 255; //grayscale as 1
- wr[ofs + 1] = slot->bitmap.buffer[i * slot->bitmap.pitch + j];
+ wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
break;
case FT_PIXEL_MODE_BGRA: {
- int ofs_color = i * slot->bitmap.pitch + (j << 2);
- wr[ofs + 2] = slot->bitmap.buffer[ofs_color + 0];
- wr[ofs + 1] = slot->bitmap.buffer[ofs_color + 1];
- wr[ofs + 0] = slot->bitmap.buffer[ofs_color + 2];
- wr[ofs + 3] = slot->bitmap.buffer[ofs_color + 3];
+ int ofs_color = i * bitmap.pitch + (j << 2);
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
+ wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
} break;
// TODO: FT_PIXEL_MODE_LCD
default:
- ERR_EXPLAIN("Font uses unsupported pixel format: " + itos(slot->bitmap.pixel_mode));
- ERR_FAIL();
+ ERR_EXPLAIN("Font uses unsupported pixel format: " + itos(bitmap.pixel_mode));
+ ERR_FAIL_V(Character::not_found());
break;
}
}
@@ -645,31 +546,105 @@ void DynamicFontAtSize::_update_char(CharType p_char) {
// update height array
- for (int k = tex_x; k < tex_x + mw; k++) {
-
- tex.offsets[k] = tex_y + mh;
+ for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
+ tex.offsets[k] = tex_pos.y + mh;
}
Character chr;
chr.h_align = xofs * scale_color_font / oversampling;
chr.v_align = ascent - (yofs * scale_color_font / oversampling); // + ascent - descent;
chr.advance = advance * scale_color_font / oversampling;
- chr.texture_idx = tex_index;
+ chr.texture_idx = tex_pos.index;
chr.found = true;
- chr.rect_uv = Rect2(tex_x + rect_margin, tex_y + rect_margin, w, h);
+ chr.rect_uv = Rect2(tex_pos.x + rect_margin, tex_pos.y + rect_margin, w, h);
chr.rect = chr.rect_uv;
chr.rect.position /= oversampling;
- chr.rect.size /= oversampling;
+ chr.rect.size = chr.rect.size * scale_color_font / oversampling;
+ return chr;
+}
+
+DynamicFontAtSize::Character DynamicFontAtSize::_make_outline_char(CharType p_char) {
+ Character ret = Character::not_found();
- char_map[p_char] = chr;
+ if (FT_Load_Char(face, p_char, FT_LOAD_NO_BITMAP | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0)) != 0)
+ return ret;
+
+ FT_Stroker stroker;
+ if (FT_Stroker_New(library, &stroker) != 0)
+ return ret;
+
+ FT_Stroker_Set(stroker, (int)(id.outline_size * oversampling * 64.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
+ FT_Glyph glyph;
+ FT_BitmapGlyph glyph_bitmap;
+
+ if (FT_Get_Glyph(face->glyph, &glyph) != 0)
+ goto cleanup_stroker;
+ if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0)
+ goto cleanup_glyph;
+ if (FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1) != 0)
+ goto cleanup_glyph;
+
+ glyph_bitmap = (FT_BitmapGlyph)glyph;
+ ret = _bitmap_to_character(glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, glyph->advance.x / 65536.0);
+
+cleanup_glyph:
+ FT_Done_Glyph(glyph);
+cleanup_stroker:
+ FT_Stroker_Done(stroker);
+ return ret;
}
-bool DynamicFontAtSize::update_oversampling() {
- if (oversampling == font_oversampling)
- return false;
- if (!valid)
- return false;
+void DynamicFontAtSize::_update_char(CharType p_char) {
+
+ if (char_map.has(p_char))
+ return;
+
+ _THREAD_SAFE_METHOD_
+
+ Character character = Character::not_found();
+
+ FT_GlyphSlot slot = face->glyph;
+
+ if (FT_Get_Char_Index(face, p_char) == 0) {
+ char_map[p_char] = character;
+ return;
+ }
+
+ int ft_hinting;
+
+ switch (font->hinting) {
+ case DynamicFontData::HINTING_NONE:
+ ft_hinting = FT_LOAD_NO_HINTING;
+ break;
+ case DynamicFontData::HINTING_LIGHT:
+ ft_hinting = FT_LOAD_TARGET_LIGHT;
+ break;
+ default:
+ ft_hinting = FT_LOAD_TARGET_NORMAL;
+ break;
+ }
+
+ int error = FT_Load_Char(face, p_char, FT_HAS_COLOR(face) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | (font->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
+ if (error) {
+ char_map[p_char] = character;
+ return;
+ }
+
+ if (id.outline_size > 0) {
+ character = _make_outline_char(p_char);
+ } else {
+ error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
+ if (!error)
+ character = _bitmap_to_character(slot->bitmap, slot->bitmap_top, slot->bitmap_left, slot->advance.x / 64.0);
+ }
+
+ char_map[p_char] = character;
+}
+
+void DynamicFontAtSize::update_oversampling() {
+ if (oversampling == font_oversampling || !valid)
+ return;
FT_Done_FreeType(library);
textures.clear();
@@ -677,8 +652,6 @@ bool DynamicFontAtSize::update_oversampling() {
oversampling = font_oversampling;
valid = false;
_load();
-
- return true;
}
DynamicFontAtSize::DynamicFontAtSize() {
@@ -707,11 +680,27 @@ DynamicFontAtSize::~DynamicFontAtSize() {
void DynamicFont::_reload_cache() {
ERR_FAIL_COND(cache_id.size < 1);
- if (!data.is_valid())
+ if (!data.is_valid()) {
+ data_at_size.unref();
+ outline_data_at_size.unref();
+ fallback_data_at_size.resize(0);
+ fallback_outline_data_at_size.resize(0);
return;
+ }
+
data_at_size = data->_get_dynamic_font_at_size(cache_id);
+ if (outline_cache_id.outline_size > 0) {
+ outline_data_at_size = data->_get_dynamic_font_at_size(outline_cache_id);
+ fallback_outline_data_at_size.resize(fallback_data_at_size.size());
+ } else {
+ outline_data_at_size.unref();
+ fallback_outline_data_at_size.resize(0);
+ }
+
for (int i = 0; i < fallbacks.size(); i++) {
fallback_data_at_size[i] = fallbacks[i]->_get_dynamic_font_at_size(cache_id);
+ if (outline_cache_id.outline_size > 0)
+ fallback_outline_data_at_size[i] = fallbacks[i]->_get_dynamic_font_at_size(outline_cache_id);
}
emit_changed();
@@ -721,12 +710,10 @@ void DynamicFont::_reload_cache() {
void DynamicFont::set_font_data(const Ref<DynamicFontData> &p_data) {
data = p_data;
- if (data.is_valid())
- data_at_size = data->_get_dynamic_font_at_size(cache_id);
- else
- data_at_size = Ref<DynamicFontAtSize>();
+ _reload_cache();
emit_changed();
+ _change_notify();
}
Ref<DynamicFontData> DynamicFont::get_font_data() const {
@@ -739,6 +726,7 @@ void DynamicFont::set_size(int p_size) {
if (cache_id.size == p_size)
return;
cache_id.size = p_size;
+ outline_cache_id.size = p_size;
_reload_cache();
}
@@ -747,6 +735,30 @@ int DynamicFont::get_size() const {
return cache_id.size;
}
+void DynamicFont::set_outline_size(int p_size) {
+ if (outline_cache_id.outline_size == p_size)
+ return;
+ ERR_FAIL_COND(p_size < 0 || p_size > UINT8_MAX);
+ outline_cache_id.outline_size = p_size;
+ _reload_cache();
+}
+
+int DynamicFont::get_outline_size() const {
+ return outline_cache_id.outline_size;
+}
+
+void DynamicFont::set_outline_color(Color p_color) {
+ if (p_color != outline_color) {
+ outline_color = p_color;
+ emit_changed();
+ _change_notify();
+ }
+}
+
+Color DynamicFont::get_outline_color() const {
+ return outline_color;
+}
+
bool DynamicFont::get_use_mipmaps() const {
return cache_id.mipmaps;
@@ -757,6 +769,7 @@ void DynamicFont::set_use_mipmaps(bool p_enable) {
if (cache_id.mipmaps == p_enable)
return;
cache_id.mipmaps = p_enable;
+ outline_cache_id.mipmaps = p_enable;
_reload_cache();
}
@@ -770,6 +783,7 @@ void DynamicFont::set_use_filter(bool p_enable) {
if (cache_id.filter == p_enable)
return;
cache_id.filter = p_enable;
+ outline_cache_id.filter = p_enable;
_reload_cache();
}
@@ -859,13 +873,24 @@ bool DynamicFont::is_distance_field_hint() const {
return false;
}
-float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate) const {
+bool DynamicFont::has_outline() const {
+ return outline_cache_id.outline_size > 0;
+}
+
+float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const {
+ const Ref<DynamicFontAtSize> &font_at_size = p_outline && outline_cache_id.outline_size > 0 ? outline_data_at_size : data_at_size;
- if (!data_at_size.is_valid())
+ if (!font_at_size.is_valid())
return 0;
- return data_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, fallback_data_at_size) + spacing_char;
+ const Vector<Ref<DynamicFontAtSize> > &fallbacks = p_outline && outline_cache_id.outline_size > 0 ? fallback_outline_data_at_size : fallback_data_at_size;
+ Color color = p_outline && outline_cache_id.outline_size > 0 ? p_modulate * outline_color : p_modulate;
+
+ // If requested outline draw, but no outline is present, simply return advance without drawing anything
+ bool advance_only = p_outline && outline_cache_id.outline_size == 0;
+ return font_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, color, fallbacks, advance_only) + spacing_char;
}
+
void DynamicFont::set_fallback(int p_idx, const Ref<DynamicFontData> &p_data) {
ERR_FAIL_COND(p_data.is_null());
@@ -879,6 +904,8 @@ void DynamicFont::add_fallback(const Ref<DynamicFontData> &p_data) {
ERR_FAIL_COND(p_data.is_null());
fallbacks.push_back(p_data);
fallback_data_at_size.push_back(fallbacks[fallbacks.size() - 1]->_get_dynamic_font_at_size(cache_id)); //const..
+ if (outline_cache_id.outline_size > 0)
+ fallback_outline_data_at_size.push_back(fallbacks[fallbacks.size() - 1]->_get_dynamic_font_at_size(outline_cache_id));
_change_notify();
emit_changed();
@@ -963,6 +990,12 @@ void DynamicFont::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_size", "data"), &DynamicFont::set_size);
ClassDB::bind_method(D_METHOD("get_size"), &DynamicFont::get_size);
+ ClassDB::bind_method(D_METHOD("set_outline_size", "size"), &DynamicFont::set_outline_size);
+ ClassDB::bind_method(D_METHOD("get_outline_size"), &DynamicFont::get_outline_size);
+
+ ClassDB::bind_method(D_METHOD("set_outline_color", "color"), &DynamicFont::set_outline_color);
+ ClassDB::bind_method(D_METHOD("get_outline_color"), &DynamicFont::get_outline_color);
+
ClassDB::bind_method(D_METHOD("set_use_mipmaps", "enable"), &DynamicFont::set_use_mipmaps);
ClassDB::bind_method(D_METHOD("get_use_mipmaps"), &DynamicFont::get_use_mipmaps);
ClassDB::bind_method(D_METHOD("set_use_filter", "enable"), &DynamicFont::set_use_filter);
@@ -978,6 +1011,8 @@ void DynamicFont::_bind_methods() {
ADD_GROUP("Settings", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "size"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "outline_size"), "set_outline_size", "get_outline_size");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "outline_color"), "set_outline_color", "get_outline_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_mipmaps"), "set_use_mipmaps", "get_use_mipmaps");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_filter"), "set_use_filter", "get_use_filter");
ADD_GROUP("Extra Spacing", "extra_spacing");
@@ -1005,6 +1040,7 @@ DynamicFont::DynamicFont() :
spacing_bottom = 0;
spacing_char = 0;
spacing_space = 0;
+ outline_color = Color(1, 1, 1);
if (dynamic_font_mutex)
dynamic_font_mutex->lock();
dynamic_fonts.add(&font_list);
@@ -1040,7 +1076,13 @@ void DynamicFont::update_oversampling() {
SelfList<DynamicFont> *E = dynamic_fonts.first();
while (E) {
- if (E->self()->data_at_size.is_valid() && E->self()->data_at_size->update_oversampling()) {
+ if (E->self()->data_at_size.is_valid()) {
+ E->self()->data_at_size->update_oversampling();
+
+ if (E->self()->outline_data_at_size.is_valid()) {
+ E->self()->outline_data_at_size->update_oversampling();
+ }
+
changed.push_back(Ref<DynamicFont>(E->self()));
}
E = E->next();
diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h
index db8bd87587..f460bca2d4 100644
--- a/scene/resources/dynamic_font.h
+++ b/scene/resources/dynamic_font.h
@@ -35,6 +35,7 @@
#include "io/resource_loader.h"
#include "os/mutex.h"
#include "os/thread_safe.h"
+#include "pair.h"
#include "scene/resources/font.h"
#include <ft2build.h>
@@ -49,10 +50,10 @@ class DynamicFontData : public Resource {
public:
struct CacheID {
-
union {
struct {
uint32_t size : 16;
+ uint32_t outline_size : 8;
bool mipmaps : 1;
bool filter : 1;
};
@@ -148,8 +149,22 @@ class DynamicFontAtSize : public Reference {
texture_idx = 0;
v_align = 0;
}
+
+ static Character not_found();
};
+ struct TexturePosition {
+ int index;
+ int x;
+ int y;
+ };
+
+ const Pair<const Character *, DynamicFontAtSize *> _find_char_with_font(CharType p_char, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const;
+ Character _make_outline_char(CharType p_char);
+ float _get_kerning_advance(const DynamicFontAtSize *font, CharType p_char, CharType p_next) const;
+ TexturePosition _find_texture_pos_for_glyph(int p_color_size, Image::Format p_image_format, int p_width, int p_height);
+ Character _bitmap_to_character(FT_Bitmap bitmap, int yofs, int xofs, float advance);
+
static unsigned long _ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count);
static void _ft_stream_close(FT_Stream stream);
@@ -174,10 +189,10 @@ public:
Size2 get_char_size(CharType p_char, CharType p_next, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const;
- float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks) const;
+ float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, const Vector<Ref<DynamicFontAtSize> > &p_fallbacks, bool p_advance_only = false) const;
void set_texture_flags(uint32_t p_flags);
- bool update_oversampling();
+ void update_oversampling();
DynamicFontAtSize();
~DynamicFontAtSize();
@@ -200,17 +215,23 @@ public:
private:
Ref<DynamicFontData> data;
Ref<DynamicFontAtSize> data_at_size;
+ Ref<DynamicFontAtSize> outline_data_at_size;
Vector<Ref<DynamicFontData> > fallbacks;
Vector<Ref<DynamicFontAtSize> > fallback_data_at_size;
+ Vector<Ref<DynamicFontAtSize> > fallback_outline_data_at_size;
DynamicFontData::CacheID cache_id;
+ DynamicFontData::CacheID outline_cache_id;
+
bool valid;
int spacing_top;
int spacing_bottom;
int spacing_char;
int spacing_space;
+ Color outline_color;
+
protected:
void _reload_cache();
@@ -227,6 +248,12 @@ public:
void set_size(int p_size);
int get_size() const;
+ void set_outline_size(int p_size);
+ int get_outline_size() const;
+
+ void set_outline_color(Color p_color);
+ Color get_outline_color() const;
+
bool get_use_mipmaps() const;
void set_use_mipmaps(bool p_enable);
@@ -251,7 +278,9 @@ public:
virtual bool is_distance_field_hint() const;
- virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const;
+ virtual bool has_outline() const;
+
+ virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const;
SelfList<DynamicFont> font_list;
diff --git a/scene/resources/dynamic_font_stb.cpp b/scene/resources/dynamic_font_stb.cpp
index 098b794a95..29f1106d16 100644
--- a/scene/resources/dynamic_font_stb.cpp
+++ b/scene/resources/dynamic_font_stb.cpp
@@ -162,7 +162,7 @@ Size2 DynamicFontAtSize::get_char_size(CharType p_char, CharType p_next) const {
return ret;
}
-float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate) const {
+float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const {
const_cast<DynamicFontAtSize *>(this)->_update_char(p_char);
@@ -172,13 +172,15 @@ float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2 &p_pos, CharT
return 0;
}
- Point2 cpos = p_pos;
- cpos.x += c->h_align;
- cpos.y -= get_ascent();
- cpos.y += c->v_align;
- ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0);
- if (c->texture_idx != -1)
- VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx].texture->get_rid(), c->rect, p_modulate);
+ if (!p_outline) {
+ Point2 cpos = p_pos;
+ cpos.x += c->h_align;
+ cpos.y -= get_ascent();
+ cpos.y += c->v_align;
+ ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0);
+ if (c->texture_idx != -1)
+ VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx].texture->get_rid(), c->rect, p_modulate);
+ }
//textures[c->texture_idx].texture->draw(p_canvas_item,Vector2());
@@ -459,12 +461,12 @@ bool DynamicFont::is_distance_field_hint() const {
return false;
}
-float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate) const {
+float DynamicFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const {
if (!data_at_size.is_valid())
return 0;
- return data_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate);
+ return data_at_size->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, p_outline);
}
DynamicFont::DynamicFont() {
diff --git a/scene/resources/dynamic_font_stb.h b/scene/resources/dynamic_font_stb.h
index 4c1097d28b..feae29c0c2 100644
--- a/scene/resources/dynamic_font_stb.h
+++ b/scene/resources/dynamic_font_stb.h
@@ -136,7 +136,7 @@ public:
Size2 get_char_size(CharType p_char, CharType p_next = 0) const;
- float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const;
+ float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const;
DynamicFontAtSize();
~DynamicFontAtSize();
@@ -171,7 +171,7 @@ public:
virtual bool is_distance_field_hint() const;
- virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const;
+ virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const;
DynamicFont();
~DynamicFont();
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 6fc5778dd8..0bae9d9b2d 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -32,12 +32,12 @@
#include "core/io/resource_loader.h"
#include "core/os/file_access.h"
+#include "method_bind_ext.gen.inc"
-void Font::draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate) const {
-
+void Font::draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate, const Color &p_outline_modulate) const {
float length = get_string_size(p_text).width;
if (length >= p_width) {
- draw(p_canvas_item, p_pos, p_text, p_modulate, p_width);
+ draw(p_canvas_item, p_pos, p_text, p_modulate, p_width, p_outline_modulate);
return;
}
@@ -56,13 +56,14 @@ void Font::draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, f
ERR_PRINT("Unknown halignment type");
} break;
}
- draw(p_canvas_item, p_pos + Point2(ofs, 0), p_text, p_modulate, p_width);
+ draw(p_canvas_item, p_pos + Point2(ofs, 0), p_text, p_modulate, p_width, p_outline_modulate);
}
-void Font::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w) const {
-
+void Font::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate, int p_clip_w, const Color &p_outline_modulate) const {
Vector2 ofs;
+ int chars_drawn = 0;
+ bool with_outline = has_outline();
for (int i = 0; i < p_text.length(); i++) {
int width = get_char_size(p_text[i]).width;
@@ -70,7 +71,15 @@ void Font::draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, co
if (p_clip_w >= 0 && (ofs.x + width) > p_clip_w)
break; //clip
- ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], p_modulate);
+ ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], with_outline ? p_outline_modulate : p_modulate, with_outline);
+ ++chars_drawn;
+ }
+
+ if (has_outline()) {
+ ofs = Vector2(0, 0);
+ for (int i = 0; i < chars_drawn; i++) {
+ ofs.x += draw_char(p_canvas_item, p_pos + ofs, p_text[i], p_text[i + 1], p_modulate, false);
+ }
}
}
@@ -81,13 +90,14 @@ void Font::update_changes() {
void Font::_bind_methods() {
- ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "string", "modulate", "clip_w"), &Font::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("draw", "canvas_item", "position", "string", "modulate", "clip_w", "outline_modulate"), &Font::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(-1), DEFVAL(Color(1, 1, 1)));
ClassDB::bind_method(D_METHOD("get_ascent"), &Font::get_ascent);
ClassDB::bind_method(D_METHOD("get_descent"), &Font::get_descent);
ClassDB::bind_method(D_METHOD("get_height"), &Font::get_height);
ClassDB::bind_method(D_METHOD("is_distance_field_hint"), &Font::is_distance_field_hint);
ClassDB::bind_method(D_METHOD("get_string_size", "string"), &Font::get_string_size);
- ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "position", "char", "next", "modulate"), &Font::draw_char, DEFVAL(-1), DEFVAL(Color(1, 1, 1)));
+ ClassDB::bind_method(D_METHOD("has_outline"), &Font::has_outline);
+ ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "position", "char", "next", "modulate", "outline"), &Font::draw_char, DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(false));
ClassDB::bind_method(D_METHOD("update_changes"), &Font::update_changes);
}
@@ -494,23 +504,24 @@ Ref<BitmapFont> BitmapFont::get_fallback() const {
return fallback;
}
-float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate) const {
+float BitmapFont::draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next, const Color &p_modulate, bool p_outline) const {
const Character *c = char_map.getptr(p_char);
if (!c) {
if (fallback.is_valid())
- return fallback->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate);
+ return fallback->draw_char(p_canvas_item, p_pos, p_char, p_next, p_modulate, p_outline);
return 0;
}
- Point2 cpos = p_pos;
- cpos.x += c->h_align;
- cpos.y -= ascent;
- cpos.y += c->v_align;
ERR_FAIL_COND_V(c->texture_idx < -1 || c->texture_idx >= textures.size(), 0);
- if (c->texture_idx != -1)
+ if (!p_outline && c->texture_idx != -1) {
+ Point2 cpos = p_pos;
+ cpos.x += c->h_align;
+ cpos.y -= ascent;
+ cpos.y += c->v_align;
VisualServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas_item, Rect2(cpos, c->rect.size), textures[c->texture_idx]->get_rid(), c->rect, p_modulate, false, RID(), false);
+ }
return get_char_size(p_char, p_next).width;
}
diff --git a/scene/resources/font.h b/scene/resources/font.h
index ae08890be3..4e295b6035 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -56,14 +56,55 @@ public:
virtual bool is_distance_field_hint() const = 0;
- void draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1) const;
- void draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate = Color(1, 1, 1)) const;
- virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const = 0;
+ void draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1, const Color &p_outline_modulate = Color(1, 1, 1)) const;
+ void draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate = Color(1, 1, 1), const Color &p_outline_modulate = Color(1, 1, 1)) const;
+
+ virtual bool has_outline() const { return false; }
+ virtual float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const = 0;
void update_changes();
Font();
};
+// Helper class to that draws outlines immediately and draws characters in its destructor.
+class FontDrawer {
+ const Ref<Font> &font;
+ Color outline_color;
+ bool has_outline;
+
+ struct PendingDraw {
+ RID canvas_item;
+ Point2 pos;
+ CharType chr;
+ CharType next;
+ Color modulate;
+ };
+
+ Vector<PendingDraw> pending_draws;
+
+public:
+ FontDrawer(const Ref<Font> &p_font, const Color &p_outline_color) :
+ font(p_font),
+ outline_color(p_outline_color) {
+ has_outline = p_font->has_outline();
+ }
+
+ float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) {
+ if (has_outline) {
+ PendingDraw draw = { p_canvas_item, p_pos, p_char, p_next, p_modulate };
+ pending_draws.push_back(draw);
+ }
+ return font->draw_char(p_canvas_item, p_pos, p_char, p_next, has_outline ? outline_color : p_modulate, has_outline);
+ }
+
+ ~FontDrawer() {
+ for (int i = 0; i < pending_draws.size(); ++i) {
+ const PendingDraw &draw = pending_draws[i];
+ font->draw_char(draw.canvas_item, draw.pos, draw.chr, draw.next, draw.modulate, false);
+ }
+ }
+};
+
class BitmapFont : public Font {
GDCLASS(BitmapFont, Font);
@@ -153,7 +194,7 @@ public:
void set_distance_field_hint(bool p_distance_field);
bool is_distance_field_hint() const;
- float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1)) const;
+ float draw_char(RID p_canvas_item, const Point2 &p_pos, CharType p_char, CharType p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const;
BitmapFont();
~BitmapFont();
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index a83ef198fb..5e7569586a 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -387,10 +387,12 @@ void SpatialMaterial::_update_shader() {
if (flags[FLAG_USE_VERTEX_LIGHTING]) {
code += ",vertex_lighting";
}
-
if (flags[FLAG_TRIPLANAR_USE_WORLD] && (flags[FLAG_UV1_USE_TRIPLANAR] || flags[FLAG_UV2_USE_TRIPLANAR])) {
code += ",world_vertex_coords";
}
+ if (flags[FLAG_DONT_RECEIVE_SHADOWS]) {
+ code += ",shadows_disabled";
+ }
code += ";\n";
code += "uniform vec4 albedo : hint_color;\n";
@@ -550,7 +552,7 @@ void SpatialMaterial::_update_shader() {
//handle animation
code += "\tint particle_total_frames = particles_anim_h_frames * particles_anim_v_frames;\n";
- code += "\tint particle_frame = int(INSTANCE_CUSTOM.y * float(particle_total_frames));\n";
+ code += "\tint particle_frame = int(INSTANCE_CUSTOM.z * float(particle_total_frames));\n";
code += "\tif (particles_anim_loop) particle_frame=clamp(particle_frame,0,particle_total_frames-1); else particle_frame=abs(particle_frame)%particle_total_frames;\n";
code += "\tUV /= vec2(float(particles_anim_h_frames),float(particles_anim_v_frames));\n";
code += "\tUV += vec2(float(particle_frame % particles_anim_h_frames) / float(particles_anim_h_frames),float(particle_frame / particles_anim_h_frames) / float(particles_anim_v_frames));\n";
@@ -1849,6 +1851,7 @@ void SpatialMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_world_triplanar"), "set_flag", "get_flag", FLAG_TRIPLANAR_USE_WORLD);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_fixed_size"), "set_flag", "get_flag", FLAG_FIXED_SIZE);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_albedo_tex_force_srgb"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_do_not_receive_shadows"), "set_flag", "get_flag", FLAG_DONT_RECEIVE_SHADOWS);
ADD_GROUP("Vertex Color", "vertex_color");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_use_as_albedo"), "set_flag", "get_flag", FLAG_ALBEDO_FROM_VERTEX_COLOR);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_is_srgb"), "set_flag", "get_flag", FLAG_SRGB_VERTEX_COLOR);
@@ -2038,6 +2041,7 @@ void SpatialMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_USE_ALPHA_SCISSOR);
BIND_ENUM_CONSTANT(FLAG_TRIPLANAR_USE_WORLD);
BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
+ BIND_ENUM_CONSTANT(FLAG_DONT_RECEIVE_SHADOWS);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(DIFFUSE_BURLEY);
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 2c297cda41..ce733bfb8d 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -188,6 +188,7 @@ public:
FLAG_EMISSION_ON_UV2,
FLAG_USE_ALPHA_SCISSOR,
FLAG_ALBEDO_TEXTURE_FORCE_SRGB,
+ FLAG_DONT_RECEIVE_SHADOWS,
FLAG_MAX
};
@@ -236,7 +237,7 @@ private:
uint64_t blend_mode : 2;
uint64_t depth_draw_mode : 2;
uint64_t cull_mode : 2;
- uint64_t flags : 14;
+ uint64_t flags : 15;
uint64_t detail_blend_mode : 2;
uint64_t diffuse_mode : 3;
uint64_t specular_mode : 2;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index d87644381c..b0620d3363 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -222,7 +222,6 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
continue;
Array a = surface_get_arrays(i);
- int vcount = 0;
if (i == 0) {
arrays = a;
@@ -230,6 +229,7 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
index_accum += v.size();
} else {
+ int vcount = 0;
for (int j = 0; j < arrays.size(); j++) {
if (arrays[j].get_type() == Variant::NIL || a[j].get_type() == Variant::NIL) {
@@ -1194,8 +1194,6 @@ Error ArrayMesh::lightmap_unwrap(const Transform &p_base_transform, float p_texe
for (int j = 0; j < 3; j++) {
- int vertex_idx = gen_vertices[gen_indices[i + j]];
-
SurfaceTool::Vertex v = surfaces[surface].vertices[uv_index[gen_vertices[gen_indices[i + j]]].second];
if (surfaces[surface].format & ARRAY_FORMAT_COLOR) {
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 056ac2772b..e0562d9e4a 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -57,6 +57,31 @@ void PrimitiveMesh::_update() const {
}
}
+ if (flip_faces) {
+ PoolVector<Vector3> normals = arr[VS::ARRAY_NORMAL];
+ PoolVector<int> indices = arr[VS::ARRAY_INDEX];
+ if (normals.size() && indices.size()) {
+
+ {
+ int nc = normals.size();
+ PoolVector<Vector3>::Write w = normals.write();
+ for (int i = 0; i < nc; i++) {
+ w[i] = -w[i];
+ }
+ }
+
+ {
+ int ic = indices.size();
+ PoolVector<int>::Write w = indices.write();
+ for (int i = 0; i < ic; i += 3) {
+ SWAP(w[i + 0], w[i + 1]);
+ }
+ }
+ arr[VS::ARRAY_NORMAL] = normals;
+ arr[VS::ARRAY_INDEX] = indices;
+ }
+ }
+
// in with the new
VisualServer::get_singleton()->mesh_clear(mesh);
VisualServer::get_singleton()->mesh_add_surface_from_arrays(mesh, (VisualServer::PrimitiveType)primitive_type, arr);
@@ -166,7 +191,15 @@ void PrimitiveMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mesh_arrays"), &PrimitiveMesh::get_mesh_arrays);
+ ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &PrimitiveMesh::set_custom_aabb);
+ ClassDB::bind_method(D_METHOD("get_custom_aabb"), &PrimitiveMesh::get_custom_aabb);
+
+ ClassDB::bind_method(D_METHOD("set_flip_faces", "flip_faces"), &PrimitiveMesh::set_flip_faces);
+ ClassDB::bind_method(D_METHOD("get_flip_faces"), &PrimitiveMesh::get_flip_faces);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+ ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_faces"), "set_flip_faces", "get_flip_faces");
}
void PrimitiveMesh::set_material(const Ref<Material> &p_material) {
@@ -187,7 +220,30 @@ Array PrimitiveMesh::get_mesh_arrays() const {
return surface_get_arrays(0);
}
+void PrimitiveMesh::set_custom_aabb(const AABB &p_custom) {
+
+ custom_aabb = p_custom;
+ VS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb);
+ emit_changed();
+}
+
+AABB PrimitiveMesh::get_custom_aabb() const {
+
+ return custom_aabb;
+}
+
+void PrimitiveMesh::set_flip_faces(bool p_enable) {
+ flip_faces = p_enable;
+ _request_update();
+}
+
+bool PrimitiveMesh::get_flip_faces() const {
+ return flip_faces;
+}
+
PrimitiveMesh::PrimitiveMesh() {
+
+ flip_faces = false;
// defaults
mesh = VisualServer::get_singleton()->mesh_create();
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index 94a7a055a3..a91aa09ffe 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -48,8 +48,10 @@ class PrimitiveMesh : public Mesh {
private:
RID mesh;
mutable AABB aabb;
+ AABB custom_aabb;
Ref<Material> material;
+ bool flip_faces;
mutable bool pending_request;
void _update() const;
@@ -81,6 +83,12 @@ public:
Array get_mesh_arrays() const;
+ void set_custom_aabb(const AABB &p_custom);
+ AABB get_custom_aabb() const;
+
+ void set_flip_faces(bool p_enable);
+ bool get_flip_faces() const;
+
PrimitiveMesh();
~PrimitiveMesh();
};
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index ad5a0fd9ab..7da65ac984 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -186,7 +186,7 @@ Size2 StyleBoxTexture::get_center_size() const {
if (texture.is_null())
return Size2();
- return texture->get_size() - get_minimum_size();
+ return region_rect.size - get_minimum_size();
}
void StyleBoxTexture::set_expand_margin_size(Margin p_expand_margin, float p_size) {
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index c0f6756fd1..56a2e7afba 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -220,12 +220,15 @@ Image::Format ImageTexture::get_format() const {
return format;
}
-void ImageTexture::load(const String &p_path) {
+Error ImageTexture::load(const String &p_path) {
Ref<Image> img;
img.instance();
- img->load(p_path);
- create_from_image(img);
+ Error err = img->load(p_path);
+ if (err == OK) {
+ create_from_image(img);
+ }
+ return err;
}
void ImageTexture::set_data(const Ref<Image> &p_image) {
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 93d7ec4ef9..d81fd3b19b 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -124,7 +124,7 @@ public:
void set_flags(uint32_t p_flags);
uint32_t get_flags() const;
Image::Format get_format() const;
- void load(const String &p_path);
+ Error load(const String &p_path);
void set_data(const Ref<Image> &p_image);
Ref<Image> get_data() const;
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index bebbf6e238..42d64376f5 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -142,6 +142,8 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
tile_set_navigation_polygon(id, p_value);
else if (what == "navigation_offset")
tile_set_navigation_polygon_offset(id, p_value);
+ else if (what == "z_index")
+ tile_set_z_index(id, p_value);
else
return false;
@@ -239,6 +241,8 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = tile_get_navigation_polygon(id);
else if (what == "navigation_offset")
r_ret = tile_get_navigation_polygon_offset(id);
+ else if (what == "z_index")
+ r_ret = tile_get_z_index(id);
else
return false;
@@ -278,6 +282,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_EDITOR));
p_list->push_back(PropertyInfo(Variant::BOOL, pre + "shape_one_way", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "shapes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::INT, pre + "z_index", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"));
}
}
@@ -748,6 +753,19 @@ Vector<TileSet::ShapeData> TileSet::tile_get_shapes(int p_id) const {
return tile_map[p_id].shapes_data;
}
+int TileSet::tile_get_z_index(int p_id) const {
+
+ ERR_FAIL_COND_V(!tile_map.has(p_id), 0);
+ return tile_map[p_id].z_index;
+}
+
+void TileSet::tile_set_z_index(int p_id, int p_z_index) {
+
+ ERR_FAIL_COND(!tile_map.has(p_id));
+ tile_map[p_id].z_index = p_z_index;
+ emit_changed();
+}
+
void TileSet::_tile_set_shapes(int p_id, const Array &p_shapes) {
ERR_FAIL_COND(!tile_map.has(p_id));
@@ -929,6 +947,8 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("tile_get_light_occluder", "id"), &TileSet::tile_get_light_occluder);
ClassDB::bind_method(D_METHOD("tile_set_occluder_offset", "id", "occluder_offset"), &TileSet::tile_set_occluder_offset);
ClassDB::bind_method(D_METHOD("tile_get_occluder_offset", "id"), &TileSet::tile_get_occluder_offset);
+ ClassDB::bind_method(D_METHOD("tile_set_z_index", "id", "z_index"), &TileSet::tile_set_z_index);
+ ClassDB::bind_method(D_METHOD("tile_get_z_index", "id"), &TileSet::tile_get_z_index);
ClassDB::bind_method(D_METHOD("remove_tile", "id"), &TileSet::remove_tile);
ClassDB::bind_method(D_METHOD("clear"), &TileSet::clear);
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 706d04998f..d5704ac9a0 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -113,11 +113,13 @@ private:
Color modulate;
TileMode tile_mode;
AutotileData autotile_data;
+ int z_index;
// Default modulate for back-compat
explicit TileData() :
tile_mode(SINGLE_TILE),
- modulate(1, 1, 1) {}
+ modulate(1, 1, 1),
+ z_index(0) {}
};
Map<int, TileData> tile_map;
@@ -220,6 +222,9 @@ public:
Ref<NavigationPolygon> autotile_get_navigation_polygon(int p_id, const Vector2 &p_coord) const;
const Map<Vector2, Ref<NavigationPolygon> > &autotile_get_navigation_map(int p_id) const;
+ void tile_set_z_index(int p_id, int p_z_index);
+ int tile_get_z_index(int p_id) const;
+
void remove_tile(int p_id);
bool has_tile(int p_id) const;
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index fdce0eb2b6..2dc32b893d 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -51,6 +51,7 @@ SceneStringNames::SceneStringNames() {
tree_entered = StaticCString::create("tree_entered");
tree_exiting = StaticCString::create("tree_exiting");
tree_exited = StaticCString::create("tree_exited");
+ ready = StaticCString::create("ready");
item_rect_changed = StaticCString::create("item_rect_changed");
size_flags_changed = StaticCString::create("size_flags_changed");
minimum_size_changed = StaticCString::create("minimum_size_changed");
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index 63a9a5db4d..2e6da26d68 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -71,6 +71,7 @@ public:
StringName tree_entered;
StringName tree_exiting;
StringName tree_exited;
+ StringName ready;
StringName size_flags_changed;
StringName minimum_size_changed;
StringName sleeping_state_changed;