summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/animated_sprite.cpp24
-rw-r--r--scene/2d/animated_sprite.h6
-rw-r--r--scene/2d/area_2d.cpp4
-rw-r--r--scene/2d/canvas_item.cpp4
-rw-r--r--scene/2d/cpu_particles_2d.cpp11
-rw-r--r--scene/2d/navigation_polygon.cpp2
-rw-r--r--scene/2d/particles_2d.cpp4
-rw-r--r--scene/2d/physics_body_2d.cpp6
-rw-r--r--scene/2d/position_2d.cpp5
-rw-r--r--scene/2d/sprite.cpp4
-rw-r--r--scene/2d/tile_map.cpp16
-rw-r--r--scene/3d/audio_stream_player_3d.cpp1
-rw-r--r--scene/3d/baked_lightmap.cpp5
-rw-r--r--scene/3d/cpu_particles.cpp10
-rw-r--r--scene/3d/gi_probe.cpp1
-rw-r--r--scene/3d/mesh_instance.cpp37
-rw-r--r--scene/3d/mesh_instance.h7
-rw-r--r--scene/3d/navigation.cpp2
-rw-r--r--scene/3d/particles.cpp4
-rw-r--r--scene/3d/physics_body.cpp11
-rw-r--r--scene/3d/skeleton.cpp291
-rw-r--r--scene/3d/skeleton.h57
-rw-r--r--scene/animation/animation_cache.cpp10
-rw-r--r--scene/animation/animation_player.cpp4
-rw-r--r--scene/animation/animation_tree.cpp2
-rw-r--r--scene/animation/animation_tree_player.cpp6
-rw-r--r--scene/animation/skeleton_ik.cpp2
-rw-r--r--scene/gui/color_picker.cpp18
-rw-r--r--scene/gui/control.cpp1
-rw-r--r--scene/gui/grid_container.cpp35
-rw-r--r--scene/gui/line_edit.cpp85
-rw-r--r--scene/gui/line_edit.h3
-rw-r--r--scene/gui/option_button.cpp6
-rw-r--r--scene/gui/rich_text_effect.cpp5
-rw-r--r--scene/gui/rich_text_effect.h2
-rw-r--r--scene/gui/rich_text_label.cpp85
-rw-r--r--scene/gui/rich_text_label.h59
-rw-r--r--scene/gui/slider.cpp1
-rw-r--r--scene/gui/tab_container.cpp112
-rw-r--r--scene/gui/tab_container.h4
-rw-r--r--scene/gui/tabs.cpp18
-rw-r--r--scene/gui/tabs.h4
-rw-r--r--scene/gui/text_edit.cpp10
-rw-r--r--scene/gui/tree.cpp10
-rw-r--r--scene/gui/tree.h1
-rw-r--r--scene/main/node.cpp2
-rw-r--r--scene/main/viewport.cpp16
-rw-r--r--scene/register_scene_types.cpp3
-rw-r--r--scene/resources/default_theme/default_theme.cpp3
-rw-r--r--scene/resources/default_theme/overbright_indicator.pngbin0 -> 593 bytes
-rw-r--r--scene/resources/default_theme/theme_data.h4
-rw-r--r--scene/resources/dynamic_font.cpp13
-rw-r--r--scene/resources/dynamic_font_stb.cpp2
-rw-r--r--scene/resources/font.cpp4
-rw-r--r--scene/resources/mesh_library.cpp32
-rw-r--r--scene/resources/particles_material.cpp4
-rw-r--r--scene/resources/polygon_path_finder.cpp6
-rw-r--r--scene/resources/resource_format_text.cpp7
-rw-r--r--scene/resources/shader.cpp5
-rw-r--r--scene/resources/skin.cpp132
-rw-r--r--scene/resources/skin.h84
-rw-r--r--scene/resources/text_file.cpp5
-rw-r--r--scene/resources/texture.cpp6
-rw-r--r--scene/resources/visual_shader.cpp8
64 files changed, 896 insertions, 435 deletions
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 0b20b781f0..20ec06f033 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -102,7 +102,7 @@ Rect2 AnimatedSprite::_get_rect() const {
void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture> &p_frame, int p_at_pos) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND(!E);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
if (p_at_pos >= 0 && p_at_pos < E->get().frames.size())
E->get().frames.insert(p_at_pos, p_frame);
@@ -114,7 +114,7 @@ void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture> &p_fra
int SpriteFrames::get_frame_count(const StringName &p_anim) const {
const Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND_V(!E, 0);
+ ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist.");
return E->get().frames.size();
}
@@ -122,7 +122,7 @@ int SpriteFrames::get_frame_count(const StringName &p_anim) const {
void SpriteFrames::remove_frame(const StringName &p_anim, int p_idx) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND(!E);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
E->get().frames.remove(p_idx);
emit_changed();
@@ -130,7 +130,7 @@ void SpriteFrames::remove_frame(const StringName &p_anim, int p_idx) {
void SpriteFrames::clear(const StringName &p_anim) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND(!E);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
E->get().frames.clear();
emit_changed();
@@ -144,7 +144,7 @@ void SpriteFrames::clear_all() {
void SpriteFrames::add_animation(const StringName &p_anim) {
- ERR_FAIL_COND(animations.has(p_anim));
+ ERR_FAIL_COND_MSG(animations.has(p_anim), "SpriteFrames already has animation '" + p_anim + "'.");
animations[p_anim] = Anim();
animations[p_anim].normal_name = String(p_anim) + NORMAL_SUFFIX;
@@ -161,8 +161,8 @@ void SpriteFrames::remove_animation(const StringName &p_anim) {
void SpriteFrames::rename_animation(const StringName &p_prev, const StringName &p_next) {
- ERR_FAIL_COND(!animations.has(p_prev));
- ERR_FAIL_COND(animations.has(p_next));
+ ERR_FAIL_COND_MSG(!animations.has(p_prev), "SpriteFrames doesn't have animation '" + String(p_prev) + "'.");
+ ERR_FAIL_COND_MSG(animations.has(p_next), "Animation '" + String(p_next) + "' already exists.");
Anim anim = animations[p_prev];
animations.erase(p_prev);
@@ -202,26 +202,26 @@ Vector<String> SpriteFrames::get_animation_names() const {
void SpriteFrames::set_animation_speed(const StringName &p_anim, float p_fps) {
- ERR_FAIL_COND(p_fps < 0);
+ ERR_FAIL_COND_MSG(p_fps < 0, "Animation speed cannot be negative (" + itos(p_fps) + ").");
Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND(!E);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
E->get().speed = p_fps;
}
float SpriteFrames::get_animation_speed(const StringName &p_anim) const {
const Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND_V(!E, 0);
+ ERR_FAIL_COND_V_MSG(!E, 0, "Animation '" + String(p_anim) + "' doesn't exist.");
return E->get().speed;
}
void SpriteFrames::set_animation_loop(const StringName &p_anim, bool p_loop) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND(!E);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
E->get().loop = p_loop;
}
bool SpriteFrames::get_animation_loop(const StringName &p_anim) const {
const Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND_V(!E, false);
+ ERR_FAIL_COND_V_MSG(!E, false, "Animation '" + String(p_anim) + "' doesn't exist.");
return E->get().loop;
}
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index 2cc372bd93..3192d44678 100644
--- a/scene/2d/animated_sprite.h
+++ b/scene/2d/animated_sprite.h
@@ -85,7 +85,7 @@ public:
_FORCE_INLINE_ Ref<Texture> get_frame(const StringName &p_anim, int p_idx) const {
const Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND_V(!E, Ref<Texture>());
+ ERR_FAIL_COND_V_MSG(!E, Ref<Texture>(), "Animation '" + String(p_anim) + "' doesn't exist.");
ERR_FAIL_COND_V(p_idx < 0, Ref<Texture>());
if (p_idx >= E->get().frames.size())
return Ref<Texture>();
@@ -96,7 +96,7 @@ public:
_FORCE_INLINE_ Ref<Texture> get_normal_frame(const StringName &p_anim, int p_idx) const {
const Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND_V(!E, Ref<Texture>());
+ ERR_FAIL_COND_V_MSG(!E, Ref<Texture>(), "Animation '" + String(p_anim) + "' doesn't exist.");
ERR_FAIL_COND_V(p_idx < 0, Ref<Texture>());
const Map<StringName, Anim>::Element *EN = animations.find(E->get().normal_name);
@@ -109,7 +109,7 @@ public:
void set_frame(const StringName &p_anim, int p_idx, const Ref<Texture> &p_frame) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
- ERR_FAIL_COND(!E);
+ ERR_FAIL_COND_MSG(!E, "Animation '" + String(p_anim) + "' doesn't exist.");
ERR_FAIL_COND(p_idx < 0);
if (p_idx >= E->get().frames.size())
return;
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
index a636eea285..66a1318cb7 100644
--- a/scene/2d/area_2d.cpp
+++ b/scene/2d/area_2d.cpp
@@ -438,7 +438,7 @@ bool Area2D::is_monitorable() const {
Array Area2D::get_overlapping_bodies() const {
- ERR_FAIL_COND_V(!monitoring, Array());
+ ERR_FAIL_COND_V_MSG(!monitoring, Array(), "Can't find overlapping bodies when monitoring is off.");
Array ret;
ret.resize(body_map.size());
int idx = 0;
@@ -456,7 +456,7 @@ Array Area2D::get_overlapping_bodies() const {
Array Area2D::get_overlapping_areas() const {
- ERR_FAIL_COND_V(!monitoring, Array());
+ ERR_FAIL_COND_V_MSG(!monitoring, Array(), "Can't find overlapping bodies when monitoring is off.");
Array ret;
ret.resize(area_map.size());
int idx = 0;
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index fc5e5cbba2..b38fbfe981 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -602,9 +602,7 @@ void CanvasItem::_notification(int p_what) {
}
global_invalid = true;
} break;
- case NOTIFICATION_DRAW: {
-
- } break;
+ case NOTIFICATION_DRAW:
case NOTIFICATION_TRANSFORM_CHANGED: {
} break;
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index f9f273d494..85c423964b 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -44,7 +44,7 @@ void CPUParticles2D::set_emitting(bool p_emitting) {
void CPUParticles2D::set_amount(int p_amount) {
- ERR_FAIL_COND(p_amount < 1);
+ ERR_FAIL_COND_MSG(p_amount < 1, "Amount of particles must be greater than 0.");
particles.resize(p_amount);
{
@@ -62,7 +62,7 @@ void CPUParticles2D::set_amount(int p_amount) {
}
void CPUParticles2D::set_lifetime(float p_lifetime) {
- ERR_FAIL_COND(p_lifetime <= 0);
+ ERR_FAIL_COND_MSG(p_lifetime <= 0, "Particles lifetime must be greater than 0.");
lifetime = p_lifetime;
}
@@ -866,8 +866,8 @@ void CPUParticles2D::_particles_process(float p_delta) {
}
//scale by scale
- float base_scale = Math::lerp(parameters[PARAM_SCALE] * tex_scale, 1.0f, p.scale_rand * randomness[PARAM_SCALE]);
- if (base_scale == 0.0) base_scale = 0.000001;
+ float base_scale = tex_scale * Math::lerp(parameters[PARAM_SCALE], 1.0f, p.scale_rand * randomness[PARAM_SCALE]);
+ if (base_scale < 0.000001) base_scale = 0.000001;
p.transform.elements[0] *= base_scale;
p.transform.elements[1] *= base_scale;
@@ -1122,8 +1122,9 @@ void CPUParticles2D::_notification(int p_what) {
}
void CPUParticles2D::convert_from_particles(Node *p_particles) {
+
Particles2D *particles = Object::cast_to<Particles2D>(p_particles);
- ERR_FAIL_COND(!particles);
+ ERR_FAIL_COND_MSG(!particles, "Only Particles2D nodes can be converted to CPUParticles2D.");
set_emitting(particles->is_emitting());
set_amount(particles->get_amount());
diff --git a/scene/2d/navigation_polygon.cpp b/scene/2d/navigation_polygon.cpp
index e389d5f98f..678db85ff0 100644
--- a/scene/2d/navigation_polygon.cpp
+++ b/scene/2d/navigation_polygon.cpp
@@ -271,7 +271,7 @@ void NavigationPolygon::make_polygons_from_outlines() {
struct Polygon p;
- for (int i = 0; i < tp.GetNumPoints(); i++) {
+ for (int64_t i = 0; i < tp.GetNumPoints(); i++) {
Map<Vector2, int>::Element *E = points.find(tp[i]);
if (!E) {
diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp
index 93c12f0103..0bf8237d37 100644
--- a/scene/2d/particles_2d.cpp
+++ b/scene/2d/particles_2d.cpp
@@ -51,13 +51,13 @@ void Particles2D::set_emitting(bool p_emitting) {
void Particles2D::set_amount(int p_amount) {
- ERR_FAIL_COND(p_amount < 1);
+ ERR_FAIL_COND_MSG(p_amount < 1, "Amount of particles cannot be smaller than 1.");
amount = p_amount;
VS::get_singleton()->particles_set_amount(particles, amount);
}
void Particles2D::set_lifetime(float p_lifetime) {
- ERR_FAIL_COND(p_lifetime <= 0);
+ ERR_FAIL_COND_MSG(p_lifetime <= 0, "Particles lifetime must be greater than 0.");
lifetime = p_lifetime;
VS::get_singleton()->particles_set_lifetime(particles, lifetime);
}
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 28f7243c44..3a4f397fe0 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -199,7 +199,7 @@ void StaticBody2D::set_friction(real_t p_friction) {
WARN_DEPRECATED_MSG("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
- ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
+ ERR_FAIL_COND_MSG(p_friction < 0 || p_friction > 1, "Friction must be between 0 and 1.");
if (physics_material_override.is_null()) {
physics_material_override.instance();
@@ -227,7 +227,7 @@ void StaticBody2D::set_bounce(real_t p_bounce) {
WARN_DEPRECATED_MSG("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.");
- ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
+ ERR_FAIL_COND_MSG(p_bounce < 0 || p_bounce > 1, "Bounce must be between 0 and 1.");
if (physics_material_override.is_null()) {
physics_material_override.instance();
@@ -622,7 +622,7 @@ void RigidBody2D::set_friction(real_t p_friction) {
WARN_DEPRECATED_MSG("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
- ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
+ ERR_FAIL_COND_MSG(p_friction < 0 || p_friction > 1, "Friction must be between 0 and 1.");
if (physics_material_override.is_null()) {
physics_material_override.instance();
diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp
index f0c46a5fb7..e37407ceb3 100644
--- a/scene/2d/position_2d.cpp
+++ b/scene/2d/position_2d.cpp
@@ -38,8 +38,9 @@ const float DEFAULT_GIZMO_EXTENTS = 10.0;
void Position2D::_draw_cross() {
float extents = get_gizmo_extents();
- draw_line(Point2(-extents, 0), Point2(+extents, 0), Color(1, 0.5, 0.5));
- draw_line(Point2(0, -extents), Point2(0, +extents), Color(0.5, 1, 0.5));
+ // Colors taken from `axis_x_color` and `axis_y_color` (defined in `editor/editor_themes.cpp`)
+ draw_line(Point2(-extents, 0), Point2(+extents, 0), Color(0.96, 0.20, 0.32));
+ draw_line(Point2(0, -extents), Point2(0, +extents), Color(0.53, 0.84, 0.01));
}
Rect2 Position2D::_edit_get_rect() const {
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
index d7a8005187..af9ce2a4bf 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -281,7 +281,7 @@ Vector2 Sprite::get_frame_coords() const {
void Sprite::set_vframes(int p_amount) {
- ERR_FAIL_COND(p_amount < 1);
+ ERR_FAIL_COND_MSG(p_amount < 1, "Amount of vframes cannot be smaller than 1.");
vframes = p_amount;
update();
item_rect_changed();
@@ -294,7 +294,7 @@ int Sprite::get_vframes() const {
void Sprite::set_hframes(int p_amount) {
- ERR_FAIL_COND(p_amount < 1);
+ ERR_FAIL_COND_MSG(p_amount < 1, "Amount of hframes cannot be smaller than 1.");
hframes = p_amount;
update();
item_rect_changed();
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 15423f8c5e..2bfdfd7d02 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -216,7 +216,7 @@ Size2 TileMap::get_cell_size() const {
void TileMap::set_quadrant_size(int p_size) {
- ERR_FAIL_COND(p_size < 1);
+ ERR_FAIL_COND_MSG(p_size < 1, "Quadrant size cannot be smaller than 1.");
_clear_quadrants();
quadrant_size = p_size;
@@ -1549,7 +1549,8 @@ Vector2 TileMap::_map_to_world(int p_x, int p_y, bool p_ignore_ofs) const {
ret += get_cell_transform()[1] * (half_offset == HALF_OFFSET_Y ? 0.5 : -0.5);
}
} break;
- default: {
+ case HALF_OFFSET_DISABLED: {
+ // Nothing to do.
}
}
}
@@ -1612,26 +1613,27 @@ Vector2 TileMap::world_to_map(const Vector2 &p_pos) const {
switch (half_offset) {
case HALF_OFFSET_X: {
- if (ret.y > 0 ? int(ret.y) & 1 : (int(ret.y) - 1) & 1) {
+ if (int(floor(ret.y)) & 1) {
ret.x -= 0.5;
}
} break;
case HALF_OFFSET_NEGATIVE_X: {
- if (ret.y > 0 ? int(ret.y) & 1 : (int(ret.y) - 1) & 1) {
+ if (int(floor(ret.y)) & 1) {
ret.x += 0.5;
}
} break;
case HALF_OFFSET_Y: {
- if (ret.x > 0 ? int(ret.x) & 1 : (int(ret.x) - 1) & 1) {
+ if (int(floor(ret.x)) & 1) {
ret.y -= 0.5;
}
} break;
case HALF_OFFSET_NEGATIVE_Y: {
- if (ret.x > 0 ? int(ret.x) & 1 : (int(ret.x) - 1) & 1) {
+ if (int(floor(ret.x)) & 1) {
ret.y += 0.5;
}
} break;
- default: {
+ case HALF_OFFSET_DISABLED: {
+ // Nothing to do.
}
}
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 27f16f7601..05ae281cc1 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -836,6 +836,7 @@ void AudioStreamPlayer3D::set_emission_angle(float p_angle) {
ERR_FAIL_COND(p_angle < 0 || p_angle > 90);
emission_angle = p_angle;
update_gizmo();
+ _change_notify("emission_angle");
}
float AudioStreamPlayer3D::get_emission_angle() const {
diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/baked_lightmap.cpp
index c5ff4dadbc..4b1eccb40d 100644
--- a/scene/3d/baked_lightmap.cpp
+++ b/scene/3d/baked_lightmap.cpp
@@ -88,7 +88,7 @@ float BakedLightmapData::get_energy() const {
void BakedLightmapData::add_user(const NodePath &p_path, const Ref<Texture> &p_lightmap, int p_instance) {
- ERR_FAIL_COND(p_lightmap.is_null());
+ ERR_FAIL_COND_MSG(p_lightmap.is_null(), "It's not a reference to a valid Texture object.");
User user;
user.path = p_path;
user.lightmap = p_lightmap;
@@ -215,6 +215,7 @@ float BakedLightmap::get_capture_cell_size() const {
void BakedLightmap::set_extents(const Vector3 &p_extents) {
extents = p_extents;
update_gizmo();
+ _change_notify("bake_extents");
}
Vector3 BakedLightmap::get_extents() const {
@@ -359,7 +360,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, bool p_create_vi
//check for valid save path
DirAccessRef d = DirAccess::open(save_path);
if (!d) {
- ERR_PRINTS("Invalid Save Path: " + save_path);
+ ERR_PRINTS("Invalid Save Path '" + save_path + "'.");
return BAKE_ERROR_NO_SAVE_PATH;
}
}
diff --git a/scene/3d/cpu_particles.cpp b/scene/3d/cpu_particles.cpp
index fc16bc36cb..93ff60bc4e 100644
--- a/scene/3d/cpu_particles.cpp
+++ b/scene/3d/cpu_particles.cpp
@@ -53,7 +53,7 @@ void CPUParticles::set_emitting(bool p_emitting) {
void CPUParticles::set_amount(int p_amount) {
- ERR_FAIL_COND(p_amount < 1);
+ ERR_FAIL_COND_MSG(p_amount < 1, "Amount of particles must be greater than 0.");
particles.resize(p_amount);
{
@@ -71,7 +71,7 @@ void CPUParticles::set_amount(int p_amount) {
}
void CPUParticles::set_lifetime(float p_lifetime) {
- ERR_FAIL_COND(p_lifetime <= 0);
+ ERR_FAIL_COND_MSG(p_lifetime <= 0, "Particles lifetime must be greater than 0.");
lifetime = p_lifetime;
}
@@ -915,8 +915,8 @@ void CPUParticles::_particles_process(float p_delta) {
}
//scale by scale
- float base_scale = Math::lerp(parameters[PARAM_SCALE] * tex_scale, 1.0f, p.scale_rand * randomness[PARAM_SCALE]);
- if (base_scale == 0.0) base_scale = 0.000001;
+ float base_scale = tex_scale * Math::lerp(parameters[PARAM_SCALE], 1.0f, p.scale_rand * randomness[PARAM_SCALE]);
+ if (base_scale < 0.000001) base_scale = 0.000001;
p.transform.basis.scale(Vector3(1, 1, 1) * base_scale);
@@ -1193,7 +1193,7 @@ void CPUParticles::_notification(int p_what) {
void CPUParticles::convert_from_particles(Node *p_particles) {
Particles *particles = Object::cast_to<Particles>(p_particles);
- ERR_FAIL_COND(!particles);
+ ERR_FAIL_COND_MSG(!particles, "Only Particles nodes can be converted to CPUParticles.");
set_emitting(particles->is_emitting());
set_amount(particles->get_amount());
diff --git a/scene/3d/gi_probe.cpp b/scene/3d/gi_probe.cpp
index a04f156d80..ccc87b924c 100644
--- a/scene/3d/gi_probe.cpp
+++ b/scene/3d/gi_probe.cpp
@@ -243,6 +243,7 @@ void GIProbe::set_extents(const Vector3 &p_extents) {
extents = p_extents;
update_gizmo();
+ _change_notify("extents");
}
Vector3 GIProbe::get_extents() const {
diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp
index 89072519d5..50ca466df3 100644
--- a/scene/3d/mesh_instance.cpp
+++ b/scene/3d/mesh_instance.cpp
@@ -149,12 +149,38 @@ Ref<Mesh> MeshInstance::get_mesh() const {
void MeshInstance::_resolve_skeleton_path() {
- if (skeleton_path.is_empty())
+ Ref<SkinReference> new_skin_reference;
+
+ if (!skeleton_path.is_empty()) {
+ Skeleton *skeleton = Object::cast_to<Skeleton>(get_node(skeleton_path));
+ if (skeleton) {
+ new_skin_reference = skeleton->register_skin(skin);
+ if (skin.is_null()) {
+ //a skin was created for us
+ skin = new_skin_reference->get_skin();
+ _change_notify();
+ }
+ }
+ }
+
+ skin_ref = new_skin_reference;
+
+ if (skin_ref.is_valid()) {
+ VisualServer::get_singleton()->instance_attach_skeleton(get_instance(), skin_ref->get_skeleton());
+ } else {
+ VisualServer::get_singleton()->instance_attach_skeleton(get_instance(), RID());
+ }
+}
+
+void MeshInstance::set_skin(const Ref<Skin> &p_skin) {
+ skin = p_skin;
+ if (!is_inside_tree())
return;
+ _resolve_skeleton_path();
+}
- Skeleton *skeleton = Object::cast_to<Skeleton>(get_node(skeleton_path));
- if (skeleton)
- VisualServer::get_singleton()->instance_attach_skeleton(get_instance(), skeleton->get_skeleton());
+Ref<Skin> MeshInstance::get_skin() const {
+ return skin;
}
void MeshInstance::set_skeleton_path(const NodePath &p_skeleton) {
@@ -365,6 +391,8 @@ void MeshInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mesh"), &MeshInstance::get_mesh);
ClassDB::bind_method(D_METHOD("set_skeleton_path", "skeleton_path"), &MeshInstance::set_skeleton_path);
ClassDB::bind_method(D_METHOD("get_skeleton_path"), &MeshInstance::get_skeleton_path);
+ ClassDB::bind_method(D_METHOD("set_skin", "skin"), &MeshInstance::set_skin);
+ ClassDB::bind_method(D_METHOD("get_skin"), &MeshInstance::get_skin);
ClassDB::bind_method(D_METHOD("get_surface_material_count"), &MeshInstance::get_surface_material_count);
ClassDB::bind_method(D_METHOD("set_surface_material", "surface", "material"), &MeshInstance::set_surface_material);
@@ -380,6 +408,7 @@ void MeshInstance::_bind_methods() {
ClassDB::set_method_flags("MeshInstance", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton"), "set_skeleton_path", "get_skeleton_path");
}
diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h
index 8b690b0c21..77ead75dd3 100644
--- a/scene/3d/mesh_instance.h
+++ b/scene/3d/mesh_instance.h
@@ -31,8 +31,10 @@
#ifndef MESH_INSTANCE_H
#define MESH_INSTANCE_H
+#include "scene/3d/skeleton.h"
#include "scene/3d/visual_instance.h"
#include "scene/resources/mesh.h"
+#include "scene/resources/skin.h"
class MeshInstance : public GeometryInstance {
@@ -40,6 +42,8 @@ class MeshInstance : public GeometryInstance {
protected:
Ref<Mesh> mesh;
+ Ref<Skin> skin;
+ Ref<SkinReference> skin_ref;
NodePath skeleton_path;
struct BlendShapeTrack {
@@ -70,6 +74,9 @@ public:
void set_mesh(const Ref<Mesh> &p_mesh);
Ref<Mesh> get_mesh() const;
+ void set_skin(const Ref<Skin> &p_skin);
+ Ref<Skin> get_skin() const;
+
void set_skeleton_path(const NodePath &p_skeleton);
NodePath get_skeleton_path();
diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp
index 12d562c0c6..ba0460d47c 100644
--- a/scene/3d/navigation.cpp
+++ b/scene/3d/navigation.cpp
@@ -230,7 +230,7 @@ void Navigation::navmesh_set_transform(int p_id, const Transform &p_xform) {
}
void Navigation::navmesh_remove(int p_id) {
- ERR_FAIL_COND(!navmesh_map.has(p_id));
+ ERR_FAIL_COND_MSG(!navmesh_map.has(p_id), "Trying to remove nonexisting navmesh with id: " + itos(p_id));
_navmesh_unlink(p_id);
navmesh_map.erase(p_id);
}
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
index a6ccdb5791..241eb7d1ca 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -57,13 +57,13 @@ void Particles::set_emitting(bool p_emitting) {
void Particles::set_amount(int p_amount) {
- ERR_FAIL_COND(p_amount < 1);
+ ERR_FAIL_COND_MSG(p_amount < 1, "Amount of particles cannot be smaller than 1.");
amount = p_amount;
VS::get_singleton()->particles_set_amount(particles, amount);
}
void Particles::set_lifetime(float p_lifetime) {
- ERR_FAIL_COND(p_lifetime <= 0);
+ ERR_FAIL_COND_MSG(p_lifetime <= 0, "Particles lifetime must be greater than 0.");
lifetime = p_lifetime;
VS::get_singleton()->particles_set_lifetime(particles, lifetime);
}
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 8db1e883e6..a02cc4bee6 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -188,7 +188,7 @@ void StaticBody::set_friction(real_t p_friction) {
WARN_DEPRECATED_MSG("The method set_friction has been deprecated and will be removed in the future, use physics material instead.");
- ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
+ ERR_FAIL_COND_MSG(p_friction < 0 || p_friction > 1, "Friction must be between 0 and 1.");
if (physics_material_override.is_null()) {
physics_material_override.instance();
@@ -216,7 +216,7 @@ void StaticBody::set_bounce(real_t p_bounce) {
WARN_DEPRECATED_MSG("The method set_bounce has been deprecated and will be removed in the future, use physics material instead.");
- ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
+ ERR_FAIL_COND_MSG(p_bounce < 0 || p_bounce > 1, "Bounce must be between 0 and 1.");
if (physics_material_override.is_null()) {
physics_material_override.instance();
@@ -2182,7 +2182,7 @@ void PhysicalBone::_notification(int p_what) {
void PhysicalBone::_direct_state_changed(Object *p_state) {
- if (!simulate_physics) {
+ if (!simulate_physics || !_internal_simulate_physics) {
return;
}
@@ -2205,7 +2205,7 @@ void PhysicalBone::_direct_state_changed(Object *p_state) {
// 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));
+ parent_skeleton->set_bone_global_pose_override(bone_id, parent_skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse), 1.0, true);
}
}
}
@@ -2716,7 +2716,6 @@ void PhysicalBone::_start_physics_simulation() {
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;
}
@@ -2728,6 +2727,6 @@ void PhysicalBone::_stop_physics_simulation() {
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);
+ parent_skeleton->set_bone_global_pose_override(bone_id, Transform(), 0.0, false);
_internal_simulate_physics = false;
}
diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp
index e192e040f2..ae79b4eebf 100644
--- a/scene/3d/skeleton.cpp
+++ b/scene/3d/skeleton.cpp
@@ -36,6 +36,34 @@
#include "scene/3d/physics_body.h"
#include "scene/resources/surface_tool.h"
+void SkinReference::_skin_changed() {
+ if (skeleton_node) {
+ skeleton_node->_make_dirty();
+ }
+}
+
+void SkinReference::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_skin_changed"), &SkinReference::_skin_changed);
+ ClassDB::bind_method(D_METHOD("get_skeleton"), &SkinReference::get_skeleton);
+ ClassDB::bind_method(D_METHOD("get_skin"), &SkinReference::get_skin);
+}
+
+RID SkinReference::get_skeleton() const {
+ return skeleton;
+}
+
+Ref<Skin> SkinReference::get_skin() const {
+ return skin;
+}
+
+SkinReference::~SkinReference() {
+ if (skeleton_node) {
+ skeleton_node->skin_bindings.erase(this);
+ }
+
+ VS::get_singleton()->free(skeleton);
+}
+
bool Skeleton::_set(const StringName &p_path, const Variant &p_value) {
String path = p_path;
@@ -196,110 +224,82 @@ void Skeleton::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_WORLD: {
-
- VS::get_singleton()->skeleton_set_world_transform(skeleton, use_bones_in_world_transform, get_global_transform());
-
- } break;
- case NOTIFICATION_EXIT_WORLD: {
-
- } break;
- case NOTIFICATION_TRANSFORM_CHANGED: {
-
- VS::get_singleton()->skeleton_set_world_transform(skeleton, use_bones_in_world_transform, get_global_transform());
- } break;
case NOTIFICATION_UPDATE_SKELETON: {
VisualServer *vs = VisualServer::get_singleton();
Bone *bonesptr = bones.ptrw();
int len = bones.size();
- vs->skeleton_allocate(skeleton, len); // if same size, nothing really happens
-
_update_process_order();
const int *order = process_order.ptr();
- // pose changed, rebuild cache of inverses
- if (rest_global_inverse_dirty) {
-
- // calculate global rests and invert them
- for (int i = 0; i < len; i++) {
- Bone &b = bonesptr[order[i]];
- if (b.parent >= 0)
- b.rest_global_inverse = bonesptr[b.parent].rest_global_inverse * b.rest;
- else
- b.rest_global_inverse = b.rest;
- }
- for (int i = 0; i < len; i++) {
- Bone &b = bonesptr[order[i]];
- b.rest_global_inverse.affine_invert();
- }
-
- rest_global_inverse_dirty = false;
- }
-
for (int i = 0; i < len; i++) {
Bone &b = bonesptr[order[i]];
- if (b.disable_rest) {
- if (b.enabled) {
-
- Transform pose = b.pose;
- if (b.custom_pose_enable) {
+ if (b.global_pose_override_amount >= 0.999) {
+ b.pose_global = b.global_pose_override;
+ } else {
+ if (b.disable_rest) {
+ if (b.enabled) {
- pose = b.custom_pose * pose;
- }
+ Transform pose = b.pose;
+ if (b.custom_pose_enable) {
+ pose = b.custom_pose * pose;
+ }
+ if (b.parent >= 0) {
- if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global * pose;
+ } else {
- b.pose_global = bonesptr[b.parent].pose_global * pose;
+ b.pose_global = pose;
+ }
} else {
- b.pose_global = pose;
- }
- } else {
-
- if (b.parent >= 0) {
+ if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global;
- } else {
+ b.pose_global = bonesptr[b.parent].pose_global;
+ } else {
- b.pose_global = Transform();
+ b.pose_global = Transform();
+ }
}
- }
-
- } else {
- if (b.enabled) {
- Transform pose = b.pose;
- if (b.custom_pose_enable) {
+ } else {
+ if (b.enabled) {
- pose = b.custom_pose * pose;
- }
+ Transform pose = b.pose;
+ if (b.custom_pose_enable) {
+ pose = b.custom_pose * pose;
+ }
+ if (b.parent >= 0) {
- if (b.parent >= 0) {
+ b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose);
+ } else {
- b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose);
+ b.pose_global = b.rest * pose;
+ }
} else {
- b.pose_global = b.rest * pose;
- }
- } else {
-
- if (b.parent >= 0) {
+ if (b.parent >= 0) {
- b.pose_global = bonesptr[b.parent].pose_global * b.rest;
- } else {
+ b.pose_global = bonesptr[b.parent].pose_global * b.rest;
+ } else {
- b.pose_global = b.rest;
+ b.pose_global = b.rest;
+ }
}
}
+
+ if (b.global_pose_override_amount >= CMP_EPSILON) {
+ b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
+ }
}
- b.transform_final = b.pose_global * b.rest_global_inverse;
- vs->skeleton_bone_set_transform(skeleton, order[i], b.transform_final);
+ if (b.global_pose_override_reset) {
+ b.global_pose_override_amount = 0.0;
+ }
for (List<uint32_t>::Element *E = b.nodes_bound.front(); E; E = E->next()) {
@@ -311,28 +311,37 @@ void Skeleton::_notification(int p_what) {
}
}
+ //update skins
+ for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
+
+ const Skin *skin = E->get()->skin.operator->();
+ RID skeleton = E->get()->skeleton;
+ uint32_t bind_count = skin->get_bind_count();
+
+ if (E->get()->bind_count != bind_count) {
+ VS::get_singleton()->skeleton_allocate(skeleton, bind_count);
+ E->get()->bind_count = bind_count;
+ }
+
+ for (uint32_t i = 0; i < bind_count; i++) {
+ uint32_t bone_index = skin->get_bind_bone(i);
+ ERR_CONTINUE(bone_index >= (uint32_t)len);
+ vs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i));
+ }
+ }
+
dirty = false;
} break;
}
}
-Transform Skeleton::get_bone_transform(int p_bone) const {
- ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform());
- if (dirty)
- const_cast<Skeleton *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
- return bones[p_bone].pose_global * bones[p_bone].rest_global_inverse;
-}
-
-void Skeleton::set_bone_global_pose(int p_bone, const Transform &p_pose) {
+void Skeleton::set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent) {
ERR_FAIL_INDEX(p_bone, bones.size());
- if (bones[p_bone].parent == -1) {
-
- set_bone_pose(p_bone, bones[p_bone].rest_global_inverse * p_pose); //fast
- } else {
-
- set_bone_pose(p_bone, bones[p_bone].rest.affine_inverse() * (get_bone_global_pose(bones[p_bone].parent).affine_inverse() * p_pose)); //slow
- }
+ bones.write[p_bone].global_pose_override_amount = p_amount;
+ bones.write[p_bone].global_pose_override = p_pose;
+ bones.write[p_bone].global_pose_override_reset = !p_persistent;
+ _make_dirty();
}
Transform Skeleton::get_bone_global_pose(int p_bone) const {
@@ -343,11 +352,6 @@ Transform Skeleton::get_bone_global_pose(int p_bone) const {
return bones[p_bone].pose_global;
}
-RID Skeleton::get_skeleton() const {
-
- return skeleton;
-}
-
// skeleton creation api
void Skeleton::add_bone(const String &p_name) {
@@ -362,8 +366,6 @@ void Skeleton::add_bone(const String &p_name) {
b.name = p_name;
bones.push_back(b);
process_order_dirty = true;
-
- rest_global_inverse_dirty = true;
_make_dirty();
update_gizmo();
}
@@ -408,7 +410,6 @@ void Skeleton::set_bone_parent(int p_bone, int p_parent) {
ERR_FAIL_COND(p_parent != -1 && (p_parent < 0));
bones.write[p_bone].parent = p_parent;
- rest_global_inverse_dirty = true;
process_order_dirty = true;
_make_dirty();
}
@@ -426,23 +427,11 @@ void Skeleton::unparent_bone_and_rest(int p_bone) {
}
bones.write[p_bone].parent = -1;
- bones.write[p_bone].rest_global_inverse = bones[p_bone].rest.affine_inverse(); //same thing
process_order_dirty = true;
_make_dirty();
}
-void Skeleton::set_bone_ignore_animation(int p_bone, bool p_ignore) {
- ERR_FAIL_INDEX(p_bone, bones.size());
- bones.write[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());
@@ -467,7 +456,6 @@ void Skeleton::set_bone_rest(int p_bone, const Transform &p_rest) {
ERR_FAIL_INDEX(p_bone, bones.size());
bones.write[p_bone].rest = p_rest;
- rest_global_inverse_dirty = true;
_make_dirty();
}
Transform Skeleton::get_bone_rest(int p_bone) const {
@@ -482,7 +470,6 @@ void Skeleton::set_bone_enabled(int p_bone, bool p_enabled) {
ERR_FAIL_INDEX(p_bone, bones.size());
bones.write[p_bone].enabled = p_enabled;
- rest_global_inverse_dirty = true;
_make_dirty();
}
bool Skeleton::is_bone_enabled(int p_bone) const {
@@ -529,7 +516,6 @@ void Skeleton::get_bound_child_nodes_to_bone(int p_bone, List<Node *> *p_bound)
void Skeleton::clear_bones() {
bones.clear();
- rest_global_inverse_dirty = true;
process_order_dirty = true;
_make_dirty();
@@ -747,14 +733,67 @@ void Skeleton::physical_bones_remove_collision_exception(RID p_exception) {
#endif // _3D_DISABLED
-void Skeleton::set_use_bones_in_world_transform(bool p_enable) {
- use_bones_in_world_transform = p_enable;
- if (is_inside_tree()) {
- VS::get_singleton()->skeleton_set_world_transform(skeleton, use_bones_in_world_transform, get_global_transform());
- }
+void Skeleton::_skin_changed() {
+ _make_dirty();
}
-bool Skeleton::is_using_bones_in_world_transform() const {
- return use_bones_in_world_transform;
+
+Ref<SkinReference> Skeleton::register_skin(const Ref<Skin> &p_skin) {
+
+ for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
+ if (E->get()->skin == p_skin) {
+ return Ref<SkinReference>(E->get());
+ }
+ }
+
+ Ref<Skin> skin = p_skin;
+
+ if (skin.is_null()) {
+ //need to create one from existing code, this is for compatibility only
+ //when skeletons did not support skins. It is also used by gizmo
+ //to display the skeleton.
+
+ skin.instance();
+ skin->set_bind_count(bones.size());
+ _update_process_order(); //just in case
+
+ // pose changed, rebuild cache of inverses
+ const Bone *bonesptr = bones.ptr();
+ int len = bones.size();
+ const int *order = process_order.ptr();
+
+ // calculate global rests and invert them
+ for (int i = 0; i < len; i++) {
+ const Bone &b = bonesptr[order[i]];
+ if (b.parent >= 0) {
+ skin->set_bind_pose(order[i], skin->get_bind_pose(b.parent) * b.rest);
+ } else {
+ skin->set_bind_pose(order[i], b.rest);
+ }
+ }
+
+ for (int i = 0; i < len; i++) {
+ //the inverse is what is actually required
+ skin->set_bind_bone(i, i);
+ skin->set_bind_pose(i, skin->get_bind_pose(i).affine_inverse());
+ }
+ }
+
+ ERR_FAIL_COND_V(skin.is_null(), Ref<SkinReference>());
+
+ Ref<SkinReference> skin_ref;
+ skin_ref.instance();
+
+ skin_ref->skeleton_node = this;
+ skin_ref->bind_count = 0;
+ skin_ref->skeleton = VisualServer::get_singleton()->skeleton_create();
+ skin_ref->skeleton_node = this;
+ skin_ref->skin = skin;
+
+ skin_bindings.insert(skin_ref.operator->());
+
+ skin->connect("changed", skin_ref.operator->(), "_skin_changed");
+ _make_dirty();
+ return skin_ref;
}
void Skeleton::_bind_methods() {
@@ -773,6 +812,8 @@ void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton::get_bone_rest);
ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton::set_bone_rest);
+ ClassDB::bind_method(D_METHOD("register_skin", "skin"), &Skeleton::register_skin);
+
ClassDB::bind_method(D_METHOD("localize_rests"), &Skeleton::localize_rests);
ClassDB::bind_method(D_METHOD("set_bone_disable_rest", "bone_idx", "disable"), &Skeleton::set_bone_disable_rest);
@@ -787,17 +828,12 @@ void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton::get_bone_pose);
ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton::set_bone_pose);
- ClassDB::bind_method(D_METHOD("set_bone_global_pose", "bone_idx", "pose"), &Skeleton::set_bone_global_pose);
+ ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton::set_bone_global_pose_override, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton::get_bone_global_pose);
ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton::get_bone_custom_pose);
ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton::set_bone_custom_pose);
- ClassDB::bind_method(D_METHOD("get_bone_transform", "bone_idx"), &Skeleton::get_bone_transform);
-
- ClassDB::bind_method(D_METHOD("set_use_bones_in_world_transform", "enable"), &Skeleton::set_use_bones_in_world_transform);
- ClassDB::bind_method(D_METHOD("is_using_bones_in_world_transform"), &Skeleton::is_using_bones_in_world_transform);
-
#ifndef _3D_DISABLED
ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton::physical_bones_stop_simulation);
@@ -807,22 +843,19 @@ void Skeleton::_bind_methods() {
#endif // _3D_DISABLED
- ClassDB::bind_method(D_METHOD("set_bone_ignore_animation", "bone", "ignore"), &Skeleton::set_bone_ignore_animation);
-
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones_in_world_transform"), "set_use_bones_in_world_transform", "is_using_bones_in_world_transform");
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
}
Skeleton::Skeleton() {
- rest_global_inverse_dirty = true;
dirty = false;
process_order_dirty = true;
- skeleton = VisualServer::get_singleton()->skeleton_create();
- set_notify_transform(true);
- use_bones_in_world_transform = false;
}
Skeleton::~Skeleton() {
- VisualServer::get_singleton()->free(skeleton);
+
+ //some skins may remain bound
+ for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) {
+ E->get()->skeleton_node = nullptr;
+ }
}
diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h
index 5b55dffbc8..824d9567fa 100644
--- a/scene/3d/skeleton.h
+++ b/scene/3d/skeleton.h
@@ -33,6 +33,7 @@
#include "core/rid.h"
#include "scene/3d/spatial.h"
+#include "scene/resources/skin.h"
#ifndef _3D_DISABLED
typedef int BoneId;
@@ -40,10 +41,38 @@ typedef int BoneId;
class PhysicalBone;
#endif // _3D_DISABLED
+class Skeleton;
+
+class SkinReference : public Reference {
+ GDCLASS(SkinReference, Reference)
+ friend class Skeleton;
+
+ Skeleton *skeleton_node;
+ RID skeleton;
+ Ref<Skin> skin;
+ uint32_t bind_count = 0;
+ void _skin_changed();
+
+protected:
+ static void _bind_methods();
+
+public:
+ RID get_skeleton() const;
+ Ref<Skin> get_skin() const;
+ ~SkinReference();
+};
+
class Skeleton : public Spatial {
GDCLASS(Skeleton, Spatial);
+private:
+ friend class SkinReference;
+
+ Set<SkinReference *> skin_bindings;
+
+ void _skin_changed();
+
struct Bone {
String name;
@@ -52,11 +81,8 @@ class Skeleton : public Spatial {
int parent;
int sort_index; //used for re-sorting process order
- bool ignore_animation;
-
bool disable_rest;
Transform rest;
- Transform rest_global_inverse;
Transform pose;
Transform pose_global;
@@ -64,7 +90,9 @@ class Skeleton : public Spatial {
bool custom_pose_enable;
Transform custom_pose;
- Transform transform_final;
+ float global_pose_override_amount;
+ bool global_pose_override_reset;
+ Transform global_pose_override;
#ifndef _3D_DISABLED
PhysicalBone *physical_bone;
@@ -76,9 +104,10 @@ class Skeleton : public Spatial {
Bone() {
parent = -1;
enabled = true;
- ignore_animation = false;
- custom_pose_enable = false;
disable_rest = false;
+ custom_pose_enable = false;
+ global_pose_override_amount = 0;
+ global_pose_override_reset = false;
#ifndef _3D_DISABLED
physical_bone = NULL;
cache_parent_physical_bone = NULL;
@@ -86,17 +115,12 @@ class Skeleton : public Spatial {
}
};
- bool rest_global_inverse_dirty;
-
Vector<Bone> bones;
Vector<int> process_order;
bool process_order_dirty;
- RID skeleton;
-
void _make_dirty();
bool dirty;
- bool use_bones_in_world_transform;
// bind helpers
Array _get_bound_child_nodes_to_bone(int p_bone) const {
@@ -127,8 +151,6 @@ public:
NOTIFICATION_UPDATE_SKELETON = 50
};
- RID get_skeleton() const;
-
// skeleton creation api
void add_bone(const String &p_name);
int find_bone(const String &p_name) const;
@@ -141,9 +163,6 @@ public:
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;
@@ -151,10 +170,9 @@ public:
void set_bone_rest(int p_bone, const Transform &p_rest);
Transform get_bone_rest(int p_bone) const;
- Transform get_bone_transform(int p_bone) const;
Transform get_bone_global_pose(int p_bone) const;
- void set_bone_global_pose(int p_bone, const Transform &p_pose);
+ void set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent = false);
void set_bone_enabled(int p_bone, bool p_enabled);
bool is_bone_enabled(int p_bone) const;
@@ -176,8 +194,7 @@ public:
void localize_rests(); // used for loaders and tools
int get_process_order(int p_idx);
- void set_use_bones_in_world_transform(bool p_enable);
- bool is_using_bones_in_world_transform() const;
+ Ref<SkinReference> register_skin(const Ref<Skin> &p_skin);
#ifndef _3D_DISABLED
// Physical bone API
diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp
index e26bd5b5a1..5956609244 100644
--- a/scene/animation/animation_cache.cpp
+++ b/scene/animation/animation_cache.cpp
@@ -80,7 +80,7 @@ void AnimationCache::_update_cache() {
if (!node) {
path_cache.push_back(Path());
- ERR_CONTINUE_MSG(!node, "Invalid track path in animation: " + np + ".");
+ ERR_CONTINUE_MSG(!node, "Invalid track path in animation '" + np + "'.");
}
Path path;
@@ -91,7 +91,7 @@ void AnimationCache::_update_cache() {
if (np.get_subname_count() > 1) {
path_cache.push_back(Path());
- ERR_CONTINUE_MSG(animation->track_get_type(i) == Animation::TYPE_TRANSFORM, "Transform tracks can't have a subpath: " + np + ".");
+ ERR_CONTINUE_MSG(animation->track_get_type(i) == Animation::TYPE_TRANSFORM, "Transform tracks can't have a subpath '" + np + "'.");
}
Spatial *sp = Object::cast_to<Spatial>(node);
@@ -99,7 +99,7 @@ void AnimationCache::_update_cache() {
if (!sp) {
path_cache.push_back(Path());
- ERR_CONTINUE_MSG(!sp, "Transform track not of type Spatial: " + np + ".");
+ ERR_CONTINUE_MSG(!sp, "Transform track not of type Spatial '" + np + "'.");
}
if (np.get_subname_count() == 1) {
@@ -110,13 +110,13 @@ void AnimationCache::_update_cache() {
if (!sk) {
path_cache.push_back(Path());
- ERR_CONTINUE_MSG(!sk, "Property defined in Transform track, but not a Skeleton!: " + np + ".");
+ ERR_CONTINUE_MSG(!sk, "Property defined in Transform track, but not a Skeleton! '" + np + "'.");
}
int idx = sk->find_bone(ps);
if (idx == -1) {
path_cache.push_back(Path());
- ERR_CONTINUE_MSG(idx == -1, "Property defined in Transform track, but not a Skeleton Bone!: " + np + ".");
+ ERR_CONTINUE_MSG(idx == -1, "Property defined in Transform track, but not a Skeleton Bone! '" + np + "'.");
}
path.bone_idx = idx;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 051f832882..f9bf129b59 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -256,7 +256,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) {
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)) {
+ if (bone_idx == -1) {
continue;
}
@@ -1098,7 +1098,7 @@ void AnimationPlayer::get_animation_list(List<StringName> *p_animations) const {
void AnimationPlayer::set_blend_time(const StringName &p_animation1, const StringName &p_animation2, float p_time) {
- ERR_FAIL_COND(p_time < 0);
+ ERR_FAIL_COND_MSG(p_time < 0, "Blend time cannot be smaller than 0.");
BlendKey bk;
bk.from = p_animation1;
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index bb7c400cfe..eb152bc41e 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -622,7 +622,7 @@ bool AnimationTree::_update_caches(AnimationPlayer *player) {
Skeleton *sk = Object::cast_to<Skeleton>(spatial);
int bone_idx = sk->find_bone(path.get_subname(0));
- if (bone_idx != -1 && !sk->is_bone_ignore_animation(bone_idx)) {
+ if (bone_idx != -1) {
track_xform->skeleton = sk;
track_xform->bone_idx = bone_idx;
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index 8f6d53c21c..ba5936562a 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -820,11 +820,7 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
t.value = t.object->get_indexed(t.subpath);
t.value.zero();
- if (t.skeleton) {
- t.skip = t.skeleton->is_bone_ignore_animation(t.bone_idx);
- } else {
- t.skip = false;
- }
+ t.skip = false;
}
/* STEP 2 PROCESS ANIMATIONS */
diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp
index 7a1b10792b..4ec22cf3df 100644
--- a/scene/animation/skeleton_ik.cpp
+++ b/scene/animation/skeleton_ik.cpp
@@ -320,7 +320,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
new_bone_pose.basis = new_bone_pose.basis * p_task->chain.tips[0].end_effector->goal_transform.basis;
}
- p_task->skeleton->set_bone_global_pose(ci->bone, new_bone_pose);
+ p_task->skeleton->set_bone_global_pose_override(ci->bone, new_bone_pose, 1.0);
if (!ci->children.empty())
ci = &ci->children.write[0];
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 6dd9e401f6..ffe011e5f7 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -396,11 +396,18 @@ void ColorPicker::_update_text_value() {
}
void ColorPicker::_sample_draw() {
- Rect2 r = Rect2(Point2(), Size2(uv_edit->get_size().width, sample->get_size().height * 0.95));
+ const Rect2 r = Rect2(Point2(), Size2(uv_edit->get_size().width, sample->get_size().height * 0.95));
+
if (color.a < 1.0) {
sample->draw_texture_rect(get_icon("preset_bg", "ColorPicker"), r, true);
}
+
sample->draw_rect(r, color);
+
+ if (color.r > 1 || color.g > 1 || color.b > 1) {
+ // Draw an indicator to denote that the color is "overbright" and can't be displayed accurately in the preview
+ sample->draw_texture(get_icon("overbright_indicator", "ColorPicker"), Point2());
+ }
}
void ColorPicker::_hsv_draw(int p_which, Control *c) {
@@ -894,10 +901,15 @@ void ColorPickerButton::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_DRAW: {
- Ref<StyleBox> normal = get_stylebox("normal");
- Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size());
+ const Ref<StyleBox> normal = get_stylebox("normal");
+ const 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, color);
+
+ if (color.r > 1 || color.g > 1 || color.b > 1) {
+ // Draw an indicator to denote that the color is "overbright" and can't be displayed accurately in the preview
+ draw_texture(Control::get_icon("overbright_indicator", "ColorPicker"), normal->get_offset());
+ }
} break;
case MainLoop::NOTIFICATION_WM_QUIT_REQUEST: {
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index d39f017cad..f8f29632b3 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -645,6 +645,7 @@ void Control::_notification(int p_notification) {
} break;
case NOTIFICATION_THEME_CHANGED: {
+ minimum_size_changed();
update();
} break;
case NOTIFICATION_MODAL_CLOSE: {
diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp
index d0e2edc7b5..a6842603fd 100644
--- a/scene/gui/grid_container.cpp
+++ b/scene/gui/grid_container.cpp
@@ -36,20 +36,18 @@ 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
- Set<int> row_expanded; // rows which have the SIZE_EXPAND flag set
+ 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.
+ Set<int> row_expanded; // Rows which have the SIZE_EXPAND flag set.
int hsep = get_constant("hseparation");
int vsep = get_constant("vseparation");
int max_col = MIN(get_child_count(), columns);
int max_row = get_child_count() / columns;
- // Compute the per-column/per-row data
- valid_controls_index = 0;
+ // Compute the per-column/per-row data.
+ 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())
@@ -77,7 +75,12 @@ void GridContainer::_notification(int p_what) {
}
}
- // Evaluate the remaining space for expanded columns/rows
+ // Consider all empty columns expanded.
+ for (int i = valid_controls_index; i < columns; i++) {
+ col_expanded.insert(i);
+ }
+
+ // Evaluate the remaining space for expanded columns/rows.
Size2 remaining_space = get_size();
for (Map<int, int>::Element *E = col_minw.front(); E; E = E->next()) {
if (!col_expanded.has(E->key()))
@@ -93,7 +96,7 @@ void GridContainer::_notification(int p_what) {
bool can_fit = false;
while (!can_fit && col_expanded.size() > 0) {
- // Check if all minwidth constraints are ok if we use the remaining space
+ // Check if all minwidth constraints are OK if we use the remaining space.
can_fit = true;
int max_index = col_expanded.front()->get();
for (Set<int>::Element *E = col_expanded.front(); E; E = E->next()) {
@@ -105,7 +108,7 @@ void GridContainer::_notification(int p_what) {
}
}
- // If not, the column with maximum minwidth is not expanded
+ // If not, the column with maximum minwidth is not expanded.
if (!can_fit) {
col_expanded.erase(max_index);
remaining_space.width -= col_minw[max_index];
@@ -114,7 +117,7 @@ void GridContainer::_notification(int p_what) {
can_fit = false;
while (!can_fit && row_expanded.size() > 0) {
- // Check if all minwidth constraints are ok if we use the remaining space
+ // Check if all minheight constraints are OK if we use the remaining space.
can_fit = true;
int max_index = row_expanded.front()->get();
for (Set<int>::Element *E = row_expanded.front(); E; E = E->next()) {
@@ -126,14 +129,14 @@ void GridContainer::_notification(int p_what) {
}
}
- // If not, the row with maximum minwidth is not expanded
+ // If not, the row with maximum minheight is not expanded.
if (!can_fit) {
row_expanded.erase(max_index);
remaining_space.height -= row_minh[max_index];
}
}
- // Finally, fit the nodes
+ // Finally, fit the nodes.
int col_expand = col_expanded.size() > 0 ? remaining_space.width / col_expanded.size() : 0;
int row_expand = row_expanded.size() > 0 ? remaining_space.height / row_expanded.size() : 0;
@@ -152,11 +155,11 @@ void GridContainer::_notification(int p_what) {
if (col == 0) {
col_ofs = 0;
if (row > 0)
- row_ofs += ((row_expanded.has(row - 1)) ? row_expand : row_minh[row - 1]) + vsep;
+ row_ofs += (row_expanded.has(row - 1) ? row_expand : row_minh[row - 1]) + vsep;
}
Point2 p(col_ofs, row_ofs);
- Size2 s((col_expanded.has(col)) ? col_expand : col_minw[col], (row_expanded.has(row)) ? row_expand : row_minh[row]);
+ Size2 s(col_expanded.has(col) ? col_expand : col_minw[col], row_expanded.has(row) ? row_expand : row_minh[row]);
fit_child_in_rect(c, Rect2(p, s));
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index c2959b3a72..ab6f80bfa9 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -201,7 +201,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
switch (code) {
- case (KEY_X): { // CUT
+ case (KEY_X): { // CUT.
if (editable) {
cut_text();
@@ -209,12 +209,13 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
} break;
- case (KEY_C): { // COPY
+ case (KEY_C): { // COPY.
+
copy_text();
} break;
- case (KEY_V): { // PASTE
+ case (KEY_V): { // PASTE.
if (editable) {
@@ -223,7 +224,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
} break;
- case (KEY_Z): { // undo / redo
+ case (KEY_Z): { // Undo/redo.
if (editable) {
if (k->get_shift()) {
redo();
@@ -233,7 +234,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
}
} break;
- case (KEY_U): { // Delete from start to cursor
+ case (KEY_U): { // Delete from start to cursor.
if (editable) {
@@ -254,7 +255,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
} break;
- case (KEY_Y): { // PASTE (Yank for unix users)
+ case (KEY_Y): { // PASTE (Yank for unix users).
if (editable) {
@@ -262,7 +263,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
}
} break;
- case (KEY_K): { // Delete from cursor_pos to end
+ case (KEY_K): { // Delete from cursor_pos to end.
if (editable) {
@@ -272,15 +273,15 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
}
} break;
- case (KEY_A): { //Select All
+ case (KEY_A): { // Select all.
select();
} break;
#ifdef APPLE_STYLE_KEYS
- case (KEY_LEFT): { // Go to start of text - like HOME key
+ case (KEY_LEFT): { // Go to start of text - like HOME key.
set_cursor_position(0);
} break;
- case (KEY_RIGHT): { // Go to end of text - like END key
+ case (KEY_RIGHT): { // Go to end of text - like END key.
set_cursor_position(text.length());
} break;
#endif
@@ -473,7 +474,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
int text_len = text.length();
if (cursor_pos == text_len)
- break; // nothing to do
+ break; // Nothing to do.
#ifdef APPLE_STYLE_KEYS
if (k->get_alt()) {
@@ -740,7 +741,7 @@ void LineEdit::_notification(int p_what) {
Color cursor_color = get_color("cursor_color");
const String &t = using_placeholder ? placeholder_translated : text;
- // draw placeholder color
+ // Draw placeholder color.
if (using_placeholder)
font_color.a *= placeholder_alpha;
@@ -755,6 +756,7 @@ void LineEdit::_notification(int p_what) {
color_icon = get_color("clear_button_color");
}
}
+
r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon);
if (align == ALIGN_CENTER) {
@@ -772,7 +774,7 @@ void LineEdit::_notification(int p_what) {
FontDrawer drawer(font, Color(1, 1, 1));
while (true) {
- //end of string, break!
+ // End of string, break.
if (char_ofs >= t.length())
break;
@@ -809,7 +811,7 @@ void LineEdit::_notification(int p_what) {
CharType next = (pass && !text.empty()) ? secret_character[0] : t[char_ofs + 1];
int char_width = font->get_char_size(cchar, next).width;
- // end of widget, break!
+ // End of widget, break.
if ((x_ofs + char_width) > ofs_max)
break;
@@ -864,7 +866,7 @@ void LineEdit::_notification(int p_what) {
}
}
- if (char_ofs == cursor_pos && draw_caret) { //may be at the end
+ if (char_ofs == cursor_pos && draw_caret) { // May be at the end.
if (ime_text.length() == 0) {
#ifdef TOOLS_ENABLED
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(Math::round(EDSCALE), caret_height)), cursor_color);
@@ -1053,7 +1055,7 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) {
}
pixel_ofs += char_w;
- if (pixel_ofs > p_x) { //found what we look for
+ if (pixel_ofs > p_x) { // Found what we look for.
break;
}
@@ -1266,10 +1268,10 @@ void LineEdit::set_cursor_position(int p_pos) {
Ref<Font> font = get_font("font");
if (cursor_pos <= window_pos) {
- /* Adjust window if cursor goes too much to the left */
+ // Adjust window if cursor goes too much to the left.
set_window_pos(MAX(0, cursor_pos - 1));
} else {
- /* Adjust window if cursor goes too much to the right */
+ // Adjust window if cursor goes too much to the right.
int window_width = get_size().width - style->get_minimum_size().width;
bool display_clear_icon = !text.empty() && is_editable() && clear_button_enabled;
if (right_icon.is_valid() || display_clear_icon) {
@@ -1288,10 +1290,10 @@ void LineEdit::set_cursor_position(int p_pos) {
for (int i = cursor_pos; i >= window_pos; i--) {
if (i >= text.length()) {
- //do not do this, because if the cursor is at the end, its just fine that it takes no space
- //accum_width = font->get_char_size(' ').width; //anything should do
+ // Do not do this, because if the cursor is at the end, its just fine that it takes no space.
+ // accum_width = font->get_char_size(' ').width;
} else {
- accum_width += font->get_char_size(text[i], i + 1 < text.length() ? text[i + 1] : 0).width; //anything should do
+ accum_width += font->get_char_size(text[i], i + 1 < text.length() ? text[i + 1] : 0).width; // Anything should do.
}
if (accum_width > window_width)
break;
@@ -1353,23 +1355,31 @@ Size2 LineEdit::get_minimum_size() const {
Ref<StyleBox> style = get_stylebox("normal");
Ref<Font> font = get_font("font");
- Size2 min = style->get_minimum_size();
- min.height += font->get_height();
+ Size2 min_size;
- //minimum size of text
+ // Minimum size of text.
int space_size = font->get_char_size(' ').x;
- int mstext = get_constant("minimum_spaces") * space_size;
+ min_size.width = get_constant("minimum_spaces") * space_size;
if (expand_to_text_length) {
- mstext = MAX(mstext, font->get_string_size(text).x + space_size); //add a spce because some fonts are too exact, and because cursor needs a bit more when at the end
+ // Add a space because some fonts are too exact, and because cursor needs a bit more when at the end.
+ min_size.width = MAX(min_size.width, font->get_string_size(text).x + space_size);
}
- min.width += mstext;
+ min_size.height = font->get_height();
- return min;
-}
+ // Take icons into account.
+ if (!text.empty() && is_editable() && clear_button_enabled) {
+ min_size.width = MAX(min_size.width, Control::get_icon("clear")->get_width());
+ min_size.height = MAX(min_size.height, Control::get_icon("clear")->get_height());
+ }
+ if (right_icon.is_valid()) {
+ min_size.width = MAX(min_size.width, right_icon->get_width());
+ min_size.height = MAX(min_size.height, right_icon->get_height());
+ }
-/* selection */
+ return style->get_minimum_size() + min_size;
+}
void LineEdit::deselect() {
@@ -1460,8 +1470,8 @@ bool LineEdit::is_secret() const {
void LineEdit::set_secret_character(const String &p_string) {
- // An empty string as the secret character would crash the engine
- // It also wouldn't make sense to use multiple characters as the secret character
+ // An empty string as the secret character would crash the engine.
+ // It also wouldn't make sense to use multiple characters as the secret character.
ERR_FAIL_COND_MSG(p_string.length() != 1, "Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given).");
secret_character = p_string;
@@ -1613,8 +1623,11 @@ void LineEdit::set_right_icon(const Ref<Texture> &p_icon) {
update();
}
-void LineEdit::_text_changed() {
+Ref<Texture> LineEdit::get_right_icon() {
+ return right_icon;
+}
+void LineEdit::_text_changed() {
if (expand_to_text_length)
minimum_size_changed();
@@ -1735,6 +1748,8 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &LineEdit::is_shortcut_keys_enabled);
ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &LineEdit::set_selecting_enabled);
ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &LineEdit::is_selecting_enabled);
+ ClassDB::bind_method(D_METHOD("set_right_icon", "icon"), &LineEdit::set_right_icon);
+ ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon);
ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text")));
ADD_SIGNAL(MethodInfo("text_entered", PropertyInfo(Variant::STRING, "new_text")));
@@ -1760,11 +1775,11 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
ADD_GROUP("Placeholder", "placeholder_");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha");
@@ -1811,7 +1826,7 @@ LineEdit::LineEdit() {
context_menu_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
- editable = false; // initialise to opposite first, so we get past the early-out in set_editable
+ editable = false; // Initialise to opposite first, so we get past the early-out in set_editable.
set_editable(true);
menu->connect("id_pressed", this, "menu_option");
expand_to_text_length = false;
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 9356840163..3424131dad 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -82,7 +82,7 @@ private:
int cursor_pos;
int window_pos;
- int max_length; // 0 for no maximum
+ int max_length; // 0 for no maximum.
int cached_width;
int cached_placeholder_width;
@@ -230,6 +230,7 @@ public:
bool is_selecting_enabled() const;
void set_right_icon(const Ref<Texture> &p_icon);
+ Ref<Texture> get_right_icon();
virtual bool is_text_field() const;
LineEdit();
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index d1840e43a3..de8df4215d 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -116,10 +116,16 @@ void OptionButton::add_item(const String &p_label, int p_id) {
void OptionButton::set_item_text(int p_idx, const String &p_text) {
popup->set_item_text(p_idx, p_text);
+
+ if (current == p_idx)
+ set_text(p_text);
}
void OptionButton::set_item_icon(int p_idx, const Ref<Texture> &p_icon) {
popup->set_item_icon(p_idx, p_icon);
+
+ if (current == p_idx)
+ set_icon(p_icon);
}
void OptionButton::set_item_id(int p_idx, int p_id) {
diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp
index 67fa85b832..f9e0be5b31 100644
--- a/scene/gui/rich_text_effect.cpp
+++ b/scene/gui/rich_text_effect.cpp
@@ -119,4 +119,9 @@ CharFXTransform::CharFXTransform() {
offset = Point2();
color = Color();
character = 0;
+ elapsed_time = 0.0f;
+}
+
+CharFXTransform::~CharFXTransform() {
+ environment.clear();
}
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
index f9c3e15399..4330cebfe6 100644
--- a/scene/gui/rich_text_effect.h
+++ b/scene/gui/rich_text_effect.h
@@ -64,6 +64,8 @@ public:
Dictionary environment;
CharFXTransform();
+ ~CharFXTransform();
+
uint64_t get_relative_index() { return relative_index; }
void set_relative_index(uint64_t p_index) { relative_index = p_index; }
uint64_t get_absolute_index() { return absolute_index; }
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index d9ae42d6e6..c5330c78e1 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -348,6 +348,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
int it_char_start = p_char_count;
Vector<ItemFX *> fx_stack = Vector<ItemFX *>();
+ _fetch_item_fx_stack(text, fx_stack);
bool custom_fx_ok = true;
if (p_mode == PROCESS_DRAW) {
@@ -359,8 +360,14 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
strikethrough = true;
}
- fade = _fetch_by_type<ItemFade>(text, ITEM_FADE);
- _fetch_item_stack<ItemFX>(text, fx_stack);
+ Item *fade_item = it;
+ while (fade_item) {
+ if (fade_item->type == ITEM_FADE) {
+ fade = static_cast<ItemFade *>(fade_item);
+ break;
+ }
+ fade_item = fade_item->parent;
+ }
} else if (p_mode == PROCESS_CACHE) {
l.char_count += text->text.length();
@@ -467,18 +474,16 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
faded_visibility > 0.0f);
for (int j = 0; j < fx_stack.size(); j++) {
- ItemCustomFX *item_custom = Object::cast_to<ItemCustomFX>(fx_stack[j]);
- ItemShake *item_shake = Object::cast_to<ItemShake>(fx_stack[j]);
- ItemWave *item_wave = Object::cast_to<ItemWave>(fx_stack[j]);
- ItemTornado *item_tornado = Object::cast_to<ItemTornado>(fx_stack[j]);
- ItemRainbow *item_rainbow = Object::cast_to<ItemRainbow>(fx_stack[j]);
-
- if (item_custom && custom_fx_ok) {
- Ref<CharFXTransform> charfx = Ref<CharFXTransform>(memnew(CharFXTransform));
- Ref<RichTextEffect> custom_effect = _get_custom_effect_by_code(item_custom->identifier);
+ ItemFX *item_fx = fx_stack[j];
+
+ if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
+ ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
+
+ Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
+ Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
+
if (!custom_effect.is_null()) {
charfx->elapsed_time = item_custom->elapsed_time;
- charfx->environment = item_custom->environment;
charfx->relative_index = c_item_offset;
charfx->absolute_index = p_char_count;
charfx->visibility = visible;
@@ -494,7 +499,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
visible &= charfx->visibility;
fx_char = charfx->character;
}
- } else if (item_shake) {
+ } else if (item_fx->type == ITEM_SHAKE) {
+ ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
+
uint64_t char_current_rand = item_shake->offset_random(c_item_offset);
uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset);
uint64_t max_rand = 2147483647;
@@ -509,14 +516,20 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
Math::cos(current_offset),
n_time)) *
(float)item_shake->strength / 10.0f;
- } else if (item_wave) {
+ } else if (item_fx->type == ITEM_WAVE) {
+ ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
+
double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f);
fx_offset += Point2(0, 1) * value;
- } else if (item_tornado) {
+ } else if (item_fx->type == ITEM_TORNADO) {
+ ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
+
double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius);
double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius);
fx_offset += Point2(torn_x, torn_y);
- } else if (item_rainbow) {
+ } else if (item_fx->type == ITEM_RAINBOW) {
+ ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
+
fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)),
item_rainbow->saturation,
item_rainbow->value,
@@ -884,7 +897,11 @@ void RichTextLabel::_update_scroll() {
void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, float p_delta_time) {
Item *it = p_frame;
while (it) {
- ItemFX *ifx = Object::cast_to<ItemFX>(it);
+ ItemFX *ifx = NULL;
+
+ if (it->type == ITEM_CUSTOMFX || it->type == ITEM_SHAKE || it->type == ITEM_WAVE || it->type == ITEM_TORNADO || it->type == ITEM_RAINBOW) {
+ ifx = static_cast<ItemFX *>(it);
+ }
if (!ifx) {
it = _get_next_item(it, true);
@@ -893,7 +910,12 @@ void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, float p_delta_
ifx->elapsed_time += p_delta_time;
- ItemShake *shake = Object::cast_to<ItemShake>(it);
+ ItemShake *shake = NULL;
+
+ if (it->type == ITEM_SHAKE) {
+ shake = static_cast<ItemShake *>(it);
+ }
+
if (shake) {
bool cycle = (shake->elapsed_time > (1.0f / shake->rate));
if (cycle) {
@@ -983,9 +1005,6 @@ void RichTextLabel::_notification(int p_what) {
case NOTIFICATION_INTERNAL_PROCESS: {
float dt = get_process_delta_time();
- for (int i = 0; i < custom_effects.size(); i++) {
- }
-
_update_fx(main, dt);
update();
}
@@ -1408,6 +1427,17 @@ bool RichTextLabel::_find_by_type(Item *p_item, ItemType p_type) {
return false;
}
+void RichTextLabel::_fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack) {
+ Item *item = p_item;
+ while (item) {
+ if (item->type == ITEM_CUSTOMFX || item->type == ITEM_SHAKE || item->type == ITEM_WAVE || item->type == ITEM_TORNADO || item->type == ITEM_RAINBOW) {
+ r_stack.push_back(static_cast<ItemFX *>(item));
+ }
+
+ item = item->parent;
+ }
+}
+
bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) {
Item *item = p_item;
@@ -1776,10 +1806,10 @@ void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_freq
_add_item(item, true);
}
-void RichTextLabel::push_customfx(String p_identifier, Dictionary p_environment) {
+void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment) {
ItemCustomFX *item = memnew(ItemCustomFX);
- item->identifier = p_identifier;
- item->environment = p_environment;
+ item->custom_effect = p_custom_effect;
+ item->char_fx_transform->environment = p_environment;
_add_item(item, true);
}
@@ -2287,7 +2317,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
Ref<RichTextEffect> effect = _get_custom_effect_by_code(identifier);
if (!effect.is_null()) {
- push_customfx(identifier, properties);
+ push_customfx(effect, properties);
pos = brk_end + 1;
tag_stack.push_front(identifier);
set_process_internal(true);
@@ -2700,17 +2730,16 @@ Size2 RichTextLabel::get_minimum_size() const {
}
Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) {
- Ref<RichTextEffect> r;
for (int i = 0; i < custom_effects.size(); i++) {
if (!custom_effects[i].is_valid())
continue;
if (custom_effects[i]->get_bbcode() == p_bbcode_identifier) {
- r = custom_effects[i];
+ return custom_effects[i];
}
}
- return r;
+ return Ref<RichTextEffect>();
}
Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressions) {
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 6755f9ef2a..1c90d974e4 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -103,8 +103,7 @@ private:
}
};
- struct Item : public Object {
-
+ struct Item {
int index;
Item *parent;
ItemType type;
@@ -128,7 +127,6 @@ private:
};
struct ItemFrame : public Item {
-
int parent_line;
bool cell;
Vector<Line> lines;
@@ -144,70 +142,58 @@ private:
};
struct ItemText : public Item {
-
String text;
ItemText() { type = ITEM_TEXT; }
};
struct ItemImage : public Item {
-
Ref<Texture> image;
ItemImage() { type = ITEM_IMAGE; }
};
struct ItemFont : public Item {
-
Ref<Font> font;
ItemFont() { type = ITEM_FONT; }
};
struct ItemColor : public Item {
-
Color color;
ItemColor() { type = ITEM_COLOR; }
};
struct ItemUnderline : public Item {
-
ItemUnderline() { type = ITEM_UNDERLINE; }
};
struct ItemStrikethrough : public Item {
-
ItemStrikethrough() { type = ITEM_STRIKETHROUGH; }
};
struct ItemMeta : public Item {
-
Variant meta;
ItemMeta() { type = ITEM_META; }
};
struct ItemAlign : public Item {
-
Align align;
ItemAlign() { type = ITEM_ALIGN; }
};
struct ItemIndent : public Item {
-
int level;
ItemIndent() { type = ITEM_INDENT; }
};
struct ItemList : public Item {
-
ListType list_type;
ItemList() { type = ITEM_LIST; }
};
struct ItemNewline : public Item {
-
ItemNewline() { type = ITEM_NEWLINE; }
};
struct ItemTable : public Item {
-
struct Column {
bool expand;
int expand_ratio;
@@ -301,18 +287,20 @@ private:
};
struct ItemCustomFX : public ItemFX {
- String identifier;
- Dictionary environment;
+ Ref<CharFXTransform> char_fx_transform;
+ Ref<RichTextEffect> custom_effect;
ItemCustomFX() {
- identifier = "";
- environment = Dictionary();
type = ITEM_CUSTOMFX;
+
+ char_fx_transform.instance();
}
virtual ~ItemCustomFX() {
_clear_children();
- environment.clear();
+
+ char_fx_transform.unref();
+ custom_effect.unref();
}
};
@@ -391,32 +379,7 @@ private:
bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = NULL);
bool _find_layout_subitem(Item *from, Item *to);
bool _find_by_type(Item *p_item, ItemType p_type);
- template <typename T>
- T *_fetch_by_type(Item *p_item, ItemType p_type) {
- Item *item = p_item;
- T *result = NULL;
- while (item) {
- if (item->type == p_type) {
- result = Object::cast_to<T>(item);
- if (result)
- return result;
- }
- item = item->parent;
- }
-
- return result;
- };
- template <typename T>
- void _fetch_item_stack(Item *p_item, Vector<T *> &r_stack) {
- Item *item = p_item;
- while (item) {
- T *found = Object::cast_to<T>(item);
- if (found) {
- r_stack.push_back(found);
- }
- item = item->parent;
- }
- }
+ void _fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack);
void _update_scroll();
void _update_fx(ItemFrame *p_frame, float p_delta_time);
@@ -456,11 +419,11 @@ public:
void push_meta(const Variant &p_meta);
void push_table(int p_columns);
void push_fade(int p_start_index, int p_length);
- void push_shake(int p_level, float p_rate);
+ void push_shake(int p_strength, float p_rate);
void push_wave(float p_frequency, float p_amplitude);
void push_tornado(float p_frequency, float p_radius);
void push_rainbow(float p_saturation, float p_value, float p_frequency);
- void push_customfx(String p_identifier, Dictionary p_environment);
+ void push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment);
void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1);
int get_current_table_column() const;
void push_cell();
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index b777e77bc3..9f853cf0c8 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -287,7 +287,6 @@ void Slider::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrollable"), "set_scrollable", "is_scrollable");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tick_count", PROPERTY_HINT_RANGE, "0,4096,1"), "set_ticks", "get_ticks");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ticks_on_borders"), "set_ticks_on_borders", "get_ticks_on_borders");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
}
Slider::Slider(Orientation p_orientation) {
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 292d80be9d..a29ba36bad 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -94,7 +94,7 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) {
return;
}
- // Do not activate tabs when tabs is empty
+ // Do not activate tabs when tabs is empty.
if (get_tab_count() == 0)
return;
@@ -140,6 +140,76 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) {
pos.x -= tab_width;
}
}
+
+ Ref<InputEventMouseMotion> mm = p_event;
+
+ if (mm.is_valid()) {
+
+ Point2 pos(mm->get_position().x, mm->get_position().y);
+ Size2 size = get_size();
+
+ // Mouse must be on tabs in the tab header area.
+ if (pos.x < tabs_ofs_cache || pos.y > _get_top_margin()) {
+
+ if (menu_hovered || highlight_arrow > -1) {
+ menu_hovered = false;
+ highlight_arrow = -1;
+ update();
+ }
+ return;
+ }
+
+ Ref<Texture> menu = get_icon("menu");
+ if (popup) {
+
+ if (pos.x >= size.width - menu->get_width()) {
+ if (!menu_hovered) {
+ menu_hovered = true;
+ highlight_arrow = -1;
+ update();
+ return;
+ }
+ } else if (menu_hovered) {
+ menu_hovered = false;
+ update();
+ }
+
+ if (menu_hovered) {
+ return;
+ }
+ }
+
+ // Do not activate tabs when tabs is empty.
+ if ((get_tab_count() == 0 || !buttons_visible_cache) && menu_hovered) {
+ highlight_arrow = -1;
+ update();
+ return;
+ }
+
+ int popup_ofs = 0;
+ if (popup) {
+ popup_ofs = menu->get_width();
+ }
+
+ Ref<Texture> increment = get_icon("increment");
+ Ref<Texture> decrement = get_icon("decrement");
+ if (pos.x >= size.width - increment->get_width() - popup_ofs) {
+
+ if (highlight_arrow != 1) {
+ highlight_arrow = 1;
+ update();
+ }
+ } else if (pos.x >= size.width - increment->get_width() - decrement->get_width() - popup_ofs) {
+
+ if (highlight_arrow != 0) {
+ highlight_arrow = 0;
+ update();
+ }
+ } else if (highlight_arrow > -1) {
+ highlight_arrow = -1;
+ update();
+ }
+ }
}
void TabContainer::_notification(int p_what) {
@@ -203,9 +273,11 @@ void TabContainer::_notification(int p_what) {
Ref<StyleBox> tab_fg = get_stylebox("tab_fg");
Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled");
Ref<Texture> increment = get_icon("increment");
+ Ref<Texture> increment_hl = get_icon("increment_highlight");
Ref<Texture> decrement = get_icon("decrement");
+ Ref<Texture> decrement_hl = get_icon("decrement_highlight");
Ref<Texture> menu = get_icon("menu");
- Ref<Texture> menu_hl = get_icon("menu_hl");
+ Ref<Texture> menu_hl = get_icon("menu_highlight");
Ref<Font> font = get_font("font");
Color font_color_fg = get_color("font_color_fg");
Color font_color_bg = get_color("font_color_bg");
@@ -332,7 +404,7 @@ void TabContainer::_notification(int p_what) {
x = get_size().width;
if (popup) {
x -= menu->get_width();
- if (mouse_x_cache > x)
+ if (menu_hovered)
menu_hl->draw(get_canvas_item(), Size2(x, (header_height - menu_hl->get_height()) / 2));
else
menu->draw(get_canvas_item(), Size2(x, (header_height - menu->get_height()) / 2));
@@ -340,23 +412,26 @@ void TabContainer::_notification(int p_what) {
// Draw the navigation buttons.
if (buttons_visible_cache) {
- int y_center = header_height / 2;
x -= increment->get_width();
- increment->draw(canvas,
- Point2(x, y_center - (increment->get_height() / 2)),
- Color(1, 1, 1, last_tab_cache < tabs.size() - 1 ? 1.0 : 0.5));
+ if (last_tab_cache < tabs.size() - 1) {
+ draw_texture(highlight_arrow == 1 ? increment_hl : increment, Point2(x, (header_height - increment->get_height()) / 2));
+ } else {
+ draw_texture(increment, Point2(x, (header_height - increment->get_height()) / 2), Color(1, 1, 1, 0.5));
+ }
x -= decrement->get_width();
- decrement->draw(canvas,
- Point2(x, y_center - (decrement->get_height() / 2)),
- Color(1, 1, 1, first_tab_cache > 0 ? 1.0 : 0.5));
+ if (first_tab_cache > 0) {
+ draw_texture(highlight_arrow == 0 ? decrement_hl : decrement, Point2(x, (header_height - decrement->get_height()) / 2));
+ } else {
+ draw_texture(decrement, Point2(x, (header_height - decrement->get_height()) / 2), Color(1, 1, 1, 0.5));
+ }
}
} break;
case NOTIFICATION_THEME_CHANGED: {
minimum_size_changed();
- call_deferred("_on_theme_changed"); //wait until all changed theme
+ call_deferred("_on_theme_changed"); // Wait until all changed theme.
} break;
}
}
@@ -367,6 +442,14 @@ void TabContainer::_on_theme_changed() {
}
}
+void TabContainer::_on_mouse_exited() {
+ if (menu_hovered || highlight_arrow > -1) {
+ menu_hovered = false;
+ highlight_arrow = -1;
+ update();
+ }
+}
+
int TabContainer::_get_tab_width(int p_index) const {
ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0);
@@ -894,6 +977,7 @@ void TabContainer::set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs) {
bool TabContainer::get_use_hidden_tabs_for_min_size() const {
return use_hidden_tabs_for_min_size;
}
+
void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &TabContainer::_gui_input);
@@ -925,6 +1009,7 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("_child_renamed_callback"), &TabContainer::_child_renamed_callback);
ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed);
+ ClassDB::bind_method(D_METHOD("_on_mouse_exited"), &TabContainer::_on_mouse_exited);
ClassDB::bind_method(D_METHOD("_update_current_tab"), &TabContainer::_update_current_tab);
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
@@ -947,14 +1032,17 @@ TabContainer::TabContainer() {
first_tab_cache = 0;
last_tab_cache = 0;
buttons_visible_cache = false;
+ menu_hovered = false;
+ highlight_arrow = -1;
tabs_ofs_cache = 0;
current = 0;
previous = 0;
- mouse_x_cache = 0;
align = ALIGN_CENTER;
tabs_visible = true;
popup = NULL;
drag_to_rearrange_enabled = false;
tabs_rearrange_group = -1;
use_hidden_tabs_for_min_size = false;
+
+ connect("mouse_exited", this, "_on_mouse_exited");
}
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 0314f86837..0c17ebc3ae 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -46,7 +46,6 @@ public:
};
private:
- int mouse_x_cache;
int first_tab_cache;
int tabs_ofs_cache;
int last_tab_cache;
@@ -54,6 +53,8 @@ private:
int previous;
bool tabs_visible;
bool buttons_visible_cache;
+ bool menu_hovered;
+ int highlight_arrow;
TabAlign align;
Control *_get_tab(int p_idx) const;
int _get_top_margin() const;
@@ -65,6 +66,7 @@ private:
Vector<Control *> _get_tabs() const;
int _get_tab_width(int p_index) const;
void _on_theme_changed();
+ void _on_mouse_exited();
void _update_current_tab();
protected:
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index 7b0836cd28..93b091e8d0 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -226,12 +226,6 @@ void Tabs::_notification(int p_what) {
minimum_size_changed();
update();
} break;
- case NOTIFICATION_MOUSE_EXIT: {
- rb_hover = -1;
- cb_hover = -1;
- hover = -1;
- update();
- } break;
case NOTIFICATION_RESIZED: {
_update_cache();
_ensure_no_over_offset();
@@ -597,6 +591,15 @@ void Tabs::_update_cache() {
}
}
+void Tabs::_on_mouse_exited() {
+
+ rb_hover = -1;
+ cb_hover = -1;
+ hover = -1;
+ highlight_arrow = -1;
+ update();
+}
+
void Tabs::add_tab(const String &p_str, const Ref<Texture> &p_icon) {
Tab t;
@@ -948,6 +951,7 @@ void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input);
ClassDB::bind_method(D_METHOD("_update_hover"), &Tabs::_update_hover);
+ ClassDB::bind_method(D_METHOD("_on_mouse_exited"), &Tabs::_on_mouse_exited);
ClassDB::bind_method(D_METHOD("get_tab_count"), &Tabs::get_tab_count);
ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &Tabs::set_current_tab);
ClassDB::bind_method(D_METHOD("get_current_tab"), &Tabs::get_current_tab);
@@ -1024,4 +1028,6 @@ Tabs::Tabs() {
hover = -1;
drag_to_rearrange_enabled = false;
tabs_rearrange_group = -1;
+
+ connect("mouse_exited", this, "_on_mouse_exited");
}
diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h
index 7c54f1acf2..a762b5b9cb 100644
--- a/scene/gui/tabs.h
+++ b/scene/gui/tabs.h
@@ -89,7 +89,7 @@ private:
bool cb_pressing;
CloseButtonDisplayPolicy cb_displaypolicy;
- int hover; // hovered tab
+ int hover; // Hovered tab.
int min_width;
bool scrolling_enabled;
bool drag_to_rearrange_enabled;
@@ -101,6 +101,8 @@ private:
void _update_hover();
void _update_cache();
+ void _on_mouse_exited();
+
protected:
void _gui_input(const Ref<InputEvent> &p_event);
void _notification(int p_what);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 7227942ccb..e2bb4e3e91 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -2455,7 +2455,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mm.is_valid()) {
if (select_identifiers_enabled) {
- if (mm->get_command() && mm->get_button_mask() == 0) {
+ if (!dragging_minimap && !dragging_selection && mm->get_command() && mm->get_button_mask() == 0) {
String new_word = get_word_at_pos(mm->get_position());
if (new_word != highlighted_word) {
@@ -2513,7 +2513,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
#endif
if (select_identifiers_enabled) {
- if (k->is_pressed()) {
+ if (k->is_pressed() && !dragging_minimap && !dragging_selection) {
highlighted_word = get_word_at_pos(get_local_mouse_position());
update();
@@ -3965,7 +3965,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r_end_line, int *r_end_char) {
- if (!setting_text)
+ if (!setting_text && idle_detect->is_inside_tree())
idle_detect->start();
if (undo_enabled) {
@@ -4019,7 +4019,7 @@ void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r
void TextEdit::_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) {
- if (!setting_text)
+ if (!setting_text && idle_detect->is_inside_tree())
idle_detect->start();
String text;
@@ -6110,7 +6110,7 @@ bool TextEdit::is_indent_using_spaces() const {
}
void TextEdit::set_indent_size(const int p_size) {
- ERR_FAIL_COND(p_size <= 0);
+ ERR_FAIL_COND_MSG(p_size <= 0, "Indend size must be greater than 0.");
indent_size = p_size;
text.set_indent_size(p_size);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 57663bbe82..2fc4be6900 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -547,6 +547,11 @@ Ref<Texture> TreeItem::get_button(int p_column, int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), Ref<Texture>());
return cells[p_column].buttons[p_idx].texture;
}
+String TreeItem::get_button_tooltip(int p_column, int p_idx) const {
+ ERR_FAIL_INDEX_V(p_column, cells.size(), String());
+ ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), String());
+ return cells[p_column].buttons[p_idx].tooltip;
+}
int TreeItem::get_button_id(int p_column, int p_idx) const {
ERR_FAIL_INDEX_V(p_column, cells.size(), -1);
ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), -1);
@@ -795,6 +800,7 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_button", "column", "button", "button_idx", "disabled", "tooltip"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_button_count", "column"), &TreeItem::get_button_count);
+ ClassDB::bind_method(D_METHOD("get_button_tooltip", "column", "button_idx"), &TreeItem::get_button_tooltip);
ClassDB::bind_method(D_METHOD("get_button", "column", "button_idx"), &TreeItem::get_button);
ClassDB::bind_method(D_METHOD("set_button", "column", "button_idx", "button"), &TreeItem::set_button);
ClassDB::bind_method(D_METHOD("erase_button", "column", "button_idx"), &TreeItem::erase_button);
@@ -2546,7 +2552,9 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
} else {
Rect2 rect = get_selected()->get_meta("__focus_rect");
if (rect.has_point(Point2(b->get_position().x, b->get_position().y))) {
- edit_selected();
+ if (!edit_selected()) {
+ emit_signal("item_double_clicked");
+ }
} else {
emit_signal("item_double_clicked");
}
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index f12d8fc4d2..47befb0c15 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -201,6 +201,7 @@ public:
void add_button(int p_column, const Ref<Texture> &p_button, int p_id = -1, bool p_disabled = false, const String &p_tooltip = "");
int get_button_count(int p_column) const;
+ String get_button_tooltip(int p_column, int p_idx) const;
Ref<Texture> get_button(int p_column, int p_idx) const;
int get_button_id(int p_column, int p_idx) const;
void erase_button(int p_column, int p_idx);
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index ba04cb69f2..7b6c90766f 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -2171,7 +2171,7 @@ void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p
if (get_filename() != "") {
Ref<PackedScene> res = ResourceLoader::load(get_filename());
- ERR_FAIL_COND(res.is_null());
+ ERR_FAIL_COND_MSG(res.is_null(), "Cannot load scene: " + get_filename());
node = res->instance();
ERR_FAIL_COND(!node);
} else {
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index b5c82ce4e3..39c5759871 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -98,22 +98,22 @@ NodePath ViewportTexture::get_viewport_path_in_scene() const {
int ViewportTexture::get_width() const {
- ERR_FAIL_COND_V(!vp, 0);
+ ERR_FAIL_COND_V_MSG(!vp, 0, "Viewport Texture must be set to use it.");
return vp->size.width;
}
int ViewportTexture::get_height() const {
- ERR_FAIL_COND_V(!vp, 0);
+ ERR_FAIL_COND_V_MSG(!vp, 0, "Viewport Texture must be set to use it.");
return vp->size.height;
}
Size2 ViewportTexture::get_size() const {
- ERR_FAIL_COND_V(!vp, Size2());
+ ERR_FAIL_COND_V_MSG(!vp, Size2(), "Viewport Texture must be set to use it.");
return vp->size;
}
RID ViewportTexture::get_rid() const {
- //ERR_FAIL_COND_V(!vp, RID());
+ //ERR_FAIL_COND_V_MSG(!vp, RID(), "Viewport Texture must be set to use it.");
return proxy;
}
@@ -123,7 +123,7 @@ bool ViewportTexture::has_alpha() const {
}
Ref<Image> ViewportTexture::get_data() const {
- ERR_FAIL_COND_V(!vp, Ref<Image>());
+ ERR_FAIL_COND_V_MSG(!vp, Ref<Image>(), "Viewport Texture must be set to use it.");
return VS::get_singleton()->texture_get_data(vp->texture_rid);
}
void ViewportTexture::set_flags(uint32_t p_flags) {
@@ -1742,6 +1742,12 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
return; // no one gets the event if exclusive NO ONE
}
+ if (mb->get_button_index() == BUTTON_WHEEL_UP || mb->get_button_index() == BUTTON_WHEEL_DOWN || mb->get_button_index() == BUTTON_WHEEL_LEFT || mb->get_button_index() == BUTTON_WHEEL_RIGHT) {
+ //cancel scroll wheel events, only clicks should trigger focus changes.
+ set_input_as_handled();
+ return;
+ }
+
top->notification(Control::NOTIFICATION_MODAL_CLOSE);
top->_modal_stack_remove();
top->hide();
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index e3af521e8c..418ee6af0e 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -364,6 +364,9 @@ void register_scene_types() {
/* REGISTER 3D */
+ ClassDB::register_class<Skin>();
+ ClassDB::register_virtual_class<SkinReference>();
+
ClassDB::register_class<Spatial>();
ClassDB::register_virtual_class<SpatialGizmo>();
ClassDB::register_class<Skeleton>();
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index f68dc9af38..0dcc184a1d 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -763,7 +763,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("folder_icon_modulate", "FileDialog", Color(1, 1, 1));
theme->set_color("files_disabled", "FileDialog", Color(0, 0, 0, 0.7));
- // colorPicker
+ // ColorPicker
theme->set_constant("margin", "ColorPicker", 4 * scale);
theme->set_constant("sv_width", "ColorPicker", 256 * scale);
@@ -776,6 +776,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("color_hue", "ColorPicker", make_icon(color_picker_hue_png));
theme->set_icon("color_sample", "ColorPicker", make_icon(color_picker_sample_png));
theme->set_icon("preset_bg", "ColorPicker", make_icon(mini_checkerboard_png));
+ theme->set_icon("overbright_indicator", "ColorPicker", make_icon(overbright_indicator_png));
theme->set_icon("bg", "ColorPickerButton", make_icon(mini_checkerboard_png));
diff --git a/scene/resources/default_theme/overbright_indicator.png b/scene/resources/default_theme/overbright_indicator.png
new file mode 100644
index 0000000000..89f800c230
--- /dev/null
+++ b/scene/resources/default_theme/overbright_indicator.png
Binary files differ
diff --git a/scene/resources/default_theme/theme_data.h b/scene/resources/default_theme/theme_data.h
index 11904b7aff..0d57fc6b14 100644
--- a/scene/resources/default_theme/theme_data.h
+++ b/scene/resources/default_theme/theme_data.h
@@ -218,6 +218,10 @@ static const unsigned char option_button_pressed_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x10, 0x8, 0x3, 0x0, 0x0, 0x0, 0x40, 0xde, 0x8d, 0x6b, 0x0, 0x0, 0x1, 0x4a, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31, 0x2f, 0x37, 0x46, 0x43, 0x4f, 0x2b, 0x2b, 0x31, 0x2e, 0x2e, 0x34, 0x47, 0x44, 0x50, 0x2a, 0x2a, 0x30, 0x55, 0x52, 0x5f, 0x22, 0x22, 0x27, 0x3d, 0x3a, 0x45, 0x56, 0x52, 0x60, 0x24, 0x24, 0x28, 0x24, 0x24, 0x29, 0x43, 0x40, 0x4c, 0x42, 0x40, 0x4b, 0x4c, 0x49, 0x56, 0x2a, 0x2a, 0x31, 0x2a, 0x2a, 0x30, 0x2d, 0x2d, 0x34, 0x2f, 0x2f, 0x36, 0x2e, 0x2e, 0x35, 0x2c, 0x2c, 0x32, 0x3a, 0x38, 0x41, 0x36, 0x34, 0x3d, 0x44, 0x41, 0x4c, 0x26, 0x26, 0x2b, 0x24, 0x24, 0x28, 0x27, 0x27, 0x2d, 0x29, 0x29, 0x2f, 0x28, 0x28, 0x2e, 0x25, 0x25, 0x2b, 0x23, 0x23, 0x28, 0x44, 0x42, 0x4e, 0x36, 0x34, 0x3e, 0x44, 0x41, 0x4e, 0x26, 0x26, 0x2c, 0x25, 0x25, 0x2a, 0x2a, 0x2a, 0x2f, 0x2b, 0x2b, 0x31, 0x22, 0x22, 0x26, 0x46, 0x42, 0x4f, 0x38, 0x35, 0x3f, 0x2d, 0x2d, 0x33, 0x22, 0x22, 0x27, 0x47, 0x45, 0x50, 0x39, 0x37, 0x40, 0x27, 0x27, 0x2b, 0x2e, 0x2e, 0x34, 0x2c, 0x2c, 0x31, 0x29, 0x29, 0x2e, 0x49, 0x46, 0x53, 0x3a, 0x38, 0x42, 0x4a, 0x47, 0x54, 0x3b, 0x39, 0x43, 0x24, 0x24, 0x2a, 0x24, 0x24, 0x29, 0x20, 0x20, 0x25, 0x4b, 0x49, 0x55, 0x3c, 0x3a, 0x44, 0x28, 0x28, 0x2d, 0x2b, 0x2b, 0x30, 0x29, 0x29, 0x2d, 0x20, 0x20, 0x23, 0x4e, 0x4a, 0x58, 0x3e, 0x3b, 0x46, 0x22, 0x22, 0x28, 0x27, 0x27, 0x2c, 0x1e, 0x1e, 0x22, 0x50, 0x4d, 0x5a, 0x3f, 0x3d, 0x48, 0x3f, 0x3d, 0x47, 0x4f, 0x4c, 0x59, 0x21, 0x21, 0x26, 0x21, 0x21, 0x25, 0x23, 0x23, 0x27, 0x20, 0x20, 0x24, 0x1d, 0x1d, 0x21, 0x45, 0x42, 0x4d, 0x41, 0x3e, 0x49, 0x40, 0x3e, 0x48, 0x50, 0x4e, 0x5a, 0x1f, 0x1f, 0x24, 0x1f, 0x1f, 0x23, 0x1e, 0x1e, 0x21, 0x52, 0x4e, 0x5c, 0x51, 0x4e, 0x5b, 0x5d, 0x59, 0x69, 0x10, 0x9d, 0xe0, 0x3c, 0x0, 0x0, 0x0, 0x24, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x4, 0xa, 0x11, 0x19, 0x1f, 0x22, 0x24, 0x1d, 0x16, 0xd, 0x7, 0x2, 0x15, 0x25, 0x34, 0x3f, 0x46, 0x47, 0x48, 0x43, 0x3a, 0x2d, 0x1b, 0x77, 0xef, 0xe6, 0x49, 0xef, 0xe6, 0xef, 0xe7, 0x77, 0xef, 0xe4, 0x4a, 0xba, 0xea, 0xc1, 0xeb, 0x0, 0x0, 0x0, 0xe6, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x6c, 0xcf, 0x3, 0x62, 0x4, 0x51, 0x10, 0x4, 0xd0, 0xaa, 0x31, 0x62, 0xdb, 0xb8, 0x49, 0x2e, 0x9e, 0x3b, 0xc4, 0xb6, 0x9d, 0xc5, 0x58, 0x1f, 0xc1, 0xd6, 0xe8, 0x77, 0xf7, 0x1b, 0x59, 0x6c, 0x2, 0x20, 0x37, 0xaa, 0xc5, 0x17, 0x7e, 0xc7, 0x62, 0x28, 0x45, 0x75, 0xfe, 0x6c, 0xe1, 0x4f, 0x68, 0x86, 0x41, 0x69, 0x44, 0x51, 0x4b, 0xb1, 0xce, 0xcc, 0xe4, 0x83, 0xd7, 0xb0, 0x48, 0x6b, 0x98, 0xe8, 0x9, 0x38, 0x70, 0x8b, 0xa, 0xcc, 0x12, 0x1a, 0xf0, 0x4d, 0xac, 0x87, 0xf3, 0x96, 0x6f, 0x8e, 0x5f, 0x56, 0xc0, 0x53, 0x20, 0x8f, 0xbf, 0xdb, 0x86, 0x58, 0x5b, 0x9, 0xbf, 0x47, 0x80, 0xa, 0x58, 0x1a, 0x38, 0xad, 0x9, 0x5f, 0xac, 0xe3, 0x20, 0xbc, 0x4b, 0x46, 0x4b, 0x0, 0x3a, 0x1a, 0x24, 0xd0, 0x69, 0x85, 0xc0, 0x63, 0x5, 0x60, 0x68, 0xf0, 0x36, 0x7f, 0xf3, 0xaa, 0xbe, 0xe1, 0x61, 0x81, 0x69, 0x5, 0x72, 0x5b, 0x83, 0xe4, 0x6a, 0x59, 0x16, 0xf7, 0x53, 0x47, 0x77, 0x8b, 0xad, 0x12, 0xe4, 0xb9, 0xa3, 0xc1, 0xe6, 0x83, 0x7b, 0x20, 0xd6, 0xb4, 0xe7, 0xbf, 0xed, 0xe1, 0x1a, 0xd8, 0xfa, 0xdf, 0xb9, 0x70, 0xb8, 0x21, 0xd6, 0xbb, 0x17, 0x1b, 0xe3, 0x4c, 0x6a, 0xb0, 0xbd, 0x25, 0x5, 0x3b, 0x5e, 0x7c, 0x21, 0xc0, 0xc2, 0x68, 0xee, 0xf1, 0xbc, 0x6, 0x46, 0xb1, 0xbd, 0x5e, 0x30, 0x5, 0x27, 0x19, 0x24, 0xb8, 0x61, 0x6e, 0xf8, 0xf5, 0xf7, 0xcd, 0x47, 0x16, 0xa0, 0x18, 0x13, 0x6a, 0x64, 0x7d, 0xff, 0x8f, 0x1e, 0x59, 0x84, 0xa2, 0x1b, 0x0, 0xe5, 0xe0, 0x4e, 0x46, 0x1d, 0x98, 0x92, 0x5c, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
+static const unsigned char overbright_indicator_png[] = {
+ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x10, 0x4, 0x3, 0x0, 0x0, 0x0, 0xed, 0xdd, 0xe2, 0x52, 0x0, 0x0, 0x1, 0x85, 0x69, 0x43, 0x43, 0x50, 0x49, 0x43, 0x43, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x0, 0x0, 0x78, 0x9c, 0x7d, 0x91, 0x3d, 0x48, 0xc3, 0x40, 0x1c, 0xc5, 0x5f, 0x53, 0xa5, 0x2a, 0x2d, 0xe, 0x16, 0x11, 0x75, 0xc8, 0x50, 0x9d, 0x2c, 0x8a, 0x8a, 0x38, 0x6a, 0x15, 0x8a, 0x50, 0x21, 0xd4, 0xa, 0xad, 0x3a, 0x98, 0x5c, 0xfa, 0x21, 0x34, 0x69, 0x48, 0x52, 0x5c, 0x1c, 0x5, 0xd7, 0x82, 0x83, 0x1f, 0x8b, 0x55, 0x7, 0x17, 0x67, 0x5d, 0x1d, 0x5c, 0x5, 0x41, 0xf0, 0x3, 0xc4, 0xc5, 0xd5, 0x49, 0xd1, 0x45, 0x4a, 0xfc, 0x5f, 0x5a, 0x68, 0x11, 0xe3, 0xc1, 0x71, 0x3f, 0xde, 0xdd, 0x7b, 0xdc, 0xbd, 0x3, 0x84, 0x6a, 0x91, 0x69, 0x56, 0xdb, 0x18, 0xa0, 0xe9, 0xb6, 0x99, 0x8c, 0xc7, 0xc4, 0x74, 0x66, 0x45, 0xc, 0xbc, 0xa2, 0x13, 0x3, 0x8, 0xa1, 0x17, 0xa3, 0x32, 0xb3, 0x8c, 0x59, 0x49, 0x4a, 0xc0, 0x73, 0x7c, 0xdd, 0xc3, 0xc7, 0xd7, 0xbb, 0x28, 0xcf, 0xf2, 0x3e, 0xf7, 0xe7, 0x8, 0xa9, 0x59, 0x8b, 0x1, 0x3e, 0x91, 0x78, 0x86, 0x19, 0xa6, 0x4d, 0xbc, 0x4e, 0x3c, 0xb5, 0x69, 0x1b, 0x9c, 0xf7, 0x89, 0xc3, 0xac, 0x20, 0xab, 0xc4, 0xe7, 0xc4, 0x23, 0x26, 0x5d, 0x90, 0xf8, 0x91, 0xeb, 0x4a, 0x9d, 0xdf, 0x38, 0xe7, 0x5d, 0x16, 0x78, 0x66, 0xd8, 0x4c, 0x25, 0xe7, 0x88, 0xc3, 0xc4, 0x62, 0xbe, 0x85, 0x95, 0x16, 0x66, 0x5, 0x53, 0x23, 0x9e, 0x24, 0x8e, 0xa8, 0x9a, 0x4e, 0xf9, 0x42, 0xba, 0xce, 0x2a, 0xe7, 0x2d, 0xce, 0x5a, 0xb1, 0xcc, 0x1a, 0xf7, 0xe4, 0x2f, 0xc, 0x66, 0xf5, 0xe5, 0x25, 0xae, 0xd3, 0x1c, 0x44, 0x1c, 0xb, 0x58, 0x84, 0x4, 0x11, 0xa, 0xca, 0xd8, 0x40, 0x11, 0x36, 0xa2, 0xb4, 0xea, 0xa4, 0x58, 0x48, 0xd2, 0x7e, 0xcc, 0xc3, 0xdf, 0xef, 0xfa, 0x25, 0x72, 0x29, 0xe4, 0xda, 0x0, 0x23, 0xc7, 0x3c, 0x4a, 0xd0, 0x20, 0xbb, 0x7e, 0xf0, 0x3f, 0xf8, 0xdd, 0xad, 0x95, 0x9b, 0x18, 0xaf, 0x27, 0x5, 0x63, 0x40, 0xfb, 0x8b, 0xe3, 0x7c, 0xc, 0x1, 0x81, 0x5d, 0xa0, 0x56, 0x71, 0x9c, 0xef, 0x63, 0xc7, 0xa9, 0x9d, 0x0, 0xfe, 0x67, 0xe0, 0x4a, 0x6f, 0xfa, 0x4b, 0x55, 0x60, 0xfa, 0x93, 0xf4, 0x4a, 0x53, 0x8b, 0x1c, 0x1, 0xdd, 0xdb, 0xc0, 0xc5, 0x75, 0x53, 0x53, 0xf6, 0x80, 0xcb, 0x1d, 0xa0, 0xef, 0xc9, 0x90, 0x4d, 0xd9, 0x95, 0xfc, 0x34, 0x85, 0x5c, 0xe, 0x78, 0x3f, 0xa3, 0x6f, 0xca, 0x0, 0x3d, 0xb7, 0x40, 0xd7, 0x6a, 0xbd, 0xb7, 0xc6, 0x3e, 0x4e, 0x1f, 0x80, 0x14, 0x75, 0x95, 0xb8, 0x1, 0xe, 0xe, 0x81, 0xe1, 0x3c, 0x65, 0xaf, 0x79, 0xbc, 0xbb, 0xa3, 0xb5, 0xb7, 0x7f, 0xcf, 0x34, 0xfa, 0xfb, 0x1, 0x8e, 0x80, 0x72, 0xb2, 0xed, 0x78, 0xfa, 0x7b, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xe, 0xc4, 0x0, 0x0, 0xe, 0xc4, 0x1, 0x95, 0x2b, 0xe, 0x1b, 0x0, 0x0, 0x0, 0x15, 0x50, 0x4c, 0x54, 0x45, 0xff, 0xff, 0xff, 0x63, 0x63, 0x66, 0x0, 0x0, 0x3, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4c, 0x39, 0x3a, 0xe, 0x0, 0x0, 0x0, 0x6, 0x74, 0x52, 0x4e, 0x53, 0xff, 0xff, 0xff, 0x7f, 0x0, 0x80, 0x2c, 0x16, 0xc1, 0x6d, 0x0, 0x0, 0x0, 0x1, 0x62, 0x4b, 0x47, 0x44, 0x6, 0x61, 0x66, 0xb8, 0x7d, 0x0, 0x0, 0x0, 0x32, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x0, 0x1, 0x46, 0x65, 0x17, 0x17, 0x30, 0x43, 0xc8, 0x4, 0x50, 0x88, 0x1c, 0x52, 0x1, 0x0, 0x2, 0x40, 0x14, 0xbb, 0x70, 0x8b, 0x40, 0xff, 0x2c, 0x18, 0xbe, 0xc6, 0xed, 0x8d, 0x42, 0xa1, 0x50, 0x28, 0x14, 0xa, 0x85, 0xbd, 0xb0, 0x13, 0xfc, 0x71, 0x1, 0xca, 0xf, 0x19, 0x62, 0x24, 0xd6, 0x8, 0xaa, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
+};
+
static const unsigned char panel_bg_png[] = {
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x8, 0x1, 0x3, 0x0, 0x0, 0x0, 0xfe, 0xc1, 0x2c, 0xc8, 0x0, 0x0, 0x0, 0x6, 0x50, 0x4c, 0x54, 0x45, 0x25, 0x25, 0x2a, 0x35, 0x32, 0x3b, 0x4a, 0x73, 0x58, 0x4a, 0x0, 0x0, 0x0, 0xa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x40, 0x3, 0x0, 0x0, 0x10, 0x0, 0x1, 0xb3, 0xac, 0xe2, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp
index 2364a4a8a3..99a2881d58 100644
--- a/scene/resources/dynamic_font.cpp
+++ b/scene/resources/dynamic_font.cpp
@@ -130,7 +130,7 @@ Error DynamicFontAtSize::_load() {
} else {
FileAccess *f = FileAccess::open(font->font_path, FileAccess::READ);
- ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
+ ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open font file '" + font->font_path + "'.");
size_t len = f->get_len();
_fontdata[font->font_path] = Vector<uint8_t>();
@@ -145,7 +145,7 @@ Error DynamicFontAtSize::_load() {
if (font->font_mem == NULL && font->font_path != String()) {
FileAccess *f = FileAccess::open(font->font_path, FileAccess::READ);
- ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
+ ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open font file '" + font->font_path + "'.");
memset(&stream, 0, sizeof(FT_StreamRec));
stream.base = NULL;
@@ -182,18 +182,17 @@ Error DynamicFontAtSize::_load() {
//error = FT_New_Face( library, src_path.utf8().get_data(),0,&face );
if (error == FT_Err_Unknown_File_Format) {
- ERR_EXPLAIN("Unknown font format.");
+
FT_Done_FreeType(library);
+ ERR_FAIL_V_MSG(ERR_FILE_CANT_OPEN, "Unknown font format.");
} else if (error) {
- ERR_EXPLAIN("Error loading font.");
FT_Done_FreeType(library);
+ ERR_FAIL_V_MSG(ERR_FILE_CANT_OPEN, "Error loading font.");
}
- ERR_FAIL_COND_V(error, ERR_FILE_CANT_OPEN);
-
- if (FT_HAS_COLOR(face)) {
+ if (FT_HAS_COLOR(face) && face->num_fixed_sizes > 0) {
int best_match = 0;
int diff = ABS(id.size - ((int64_t)face->available_sizes[0].width));
scale_color_font = float(id.size) / face->available_sizes[0].width;
diff --git a/scene/resources/dynamic_font_stb.cpp b/scene/resources/dynamic_font_stb.cpp
index ccff617a16..412bffa5dc 100644
--- a/scene/resources/dynamic_font_stb.cpp
+++ b/scene/resources/dynamic_font_stb.cpp
@@ -480,7 +480,7 @@ RES ResourceFormatLoaderDynamicFont::load(const String &p_path, const String &p_
*r_error = ERR_FILE_CANT_OPEN;
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V(!f, RES());
+ ERR_FAIL_COND_V_MSG(!f, RES(), "Cannot load font from file '" + p_path + "'.");
PoolVector<uint8_t> data;
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index cff77acdd7..124e4d37e6 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -96,7 +96,7 @@ void Font::_bind_methods() {
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("get_wordwrap_string_size", "string", "p_width"), &Font::get_wordwrap_string_size);
+ ClassDB::bind_method(D_METHOD("get_wordwrap_string_size", "string", "width"), &Font::get_wordwrap_string_size);
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);
@@ -358,7 +358,7 @@ float BitmapFont::get_descent() const {
void BitmapFont::add_texture(const Ref<Texture> &p_texture) {
- ERR_FAIL_COND(p_texture.is_null());
+ ERR_FAIL_COND_MSG(p_texture.is_null(), "It's not a reference to a valid Texture object.");
textures.push_back(p_texture);
}
diff --git a/scene/resources/mesh_library.cpp b/scene/resources/mesh_library.cpp
index d40a5dee2e..f04af29761 100644
--- a/scene/resources/mesh_library.cpp
+++ b/scene/resources/mesh_library.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "mesh_library.h"
+#include "core/engine.h"
bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) {
@@ -117,7 +118,7 @@ void MeshLibrary::create_item(int p_item) {
void MeshLibrary::set_item_name(int p_item, const String &p_name) {
- ERR_FAIL_COND(!item_map.has(p_item));
+ ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].name = p_name;
emit_changed();
_change_notify();
@@ -125,7 +126,7 @@ void MeshLibrary::set_item_name(int p_item, const String &p_name) {
void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) {
- ERR_FAIL_COND(!item_map.has(p_item));
+ ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].mesh = p_mesh;
notify_change_to_owners();
emit_changed();
@@ -134,7 +135,7 @@ void MeshLibrary::set_item_mesh(int p_item, const Ref<Mesh> &p_mesh) {
void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) {
- ERR_FAIL_COND(!item_map.has(p_item));
+ ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].shapes = p_shapes;
_change_notify();
notify_change_to_owners();
@@ -144,7 +145,7 @@ void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes)
void MeshLibrary::set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navmesh) {
- ERR_FAIL_COND(!item_map.has(p_item));
+ ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].navmesh = p_navmesh;
_change_notify();
notify_change_to_owners();
@@ -154,7 +155,7 @@ void MeshLibrary::set_item_navmesh(int p_item, const Ref<NavigationMesh> &p_navm
void MeshLibrary::set_item_navmesh_transform(int p_item, const Transform &p_transform) {
- ERR_FAIL_COND(!item_map.has(p_item));
+ ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].navmesh_transform = p_transform;
notify_change_to_owners();
emit_changed();
@@ -163,7 +164,7 @@ void MeshLibrary::set_item_navmesh_transform(int p_item, const Transform &p_tran
void MeshLibrary::set_item_preview(int p_item, const Ref<Texture> &p_preview) {
- ERR_FAIL_COND(!item_map.has(p_item));
+ ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map[p_item].preview = p_preview;
emit_changed();
_change_notify();
@@ -171,37 +172,42 @@ void MeshLibrary::set_item_preview(int p_item, const Ref<Texture> &p_preview) {
String MeshLibrary::get_item_name(int p_item) const {
- ERR_FAIL_COND_V(!item_map.has(p_item), "");
+ ERR_FAIL_COND_V_MSG(!item_map.has(p_item), "", "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].name;
}
Ref<Mesh> MeshLibrary::get_item_mesh(int p_item) const {
- ERR_FAIL_COND_V(!item_map.has(p_item), Ref<Mesh>());
+ ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Ref<Mesh>(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].mesh;
}
Vector<MeshLibrary::ShapeData> MeshLibrary::get_item_shapes(int p_item) const {
- ERR_FAIL_COND_V(!item_map.has(p_item), Vector<ShapeData>());
+ ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Vector<ShapeData>(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].shapes;
}
Ref<NavigationMesh> MeshLibrary::get_item_navmesh(int p_item) const {
- ERR_FAIL_COND_V(!item_map.has(p_item), Ref<NavigationMesh>());
+ ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Ref<NavigationMesh>(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].navmesh;
}
Transform MeshLibrary::get_item_navmesh_transform(int p_item) const {
- ERR_FAIL_COND_V(!item_map.has(p_item), Transform());
+ ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Transform(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].navmesh_transform;
}
Ref<Texture> MeshLibrary::get_item_preview(int p_item) const {
- ERR_FAIL_COND_V(!item_map.has(p_item), Ref<Texture>());
+ if (!Engine::get_singleton()->is_editor_hint()) {
+ ERR_PRINT("MeshLibrary item previews are only generated in an editor context, which means they aren't available in a running project.");
+ return Ref<Texture>();
+ }
+
+ ERR_FAIL_COND_V_MSG(!item_map.has(p_item), Ref<Texture>(), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
return item_map[p_item].preview;
}
@@ -211,7 +217,7 @@ bool MeshLibrary::has_item(int p_item) const {
}
void MeshLibrary::remove_item(int p_item) {
- ERR_FAIL_COND(!item_map.has(p_item));
+ ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'.");
item_map.erase(p_item);
notify_change_to_owners();
_change_notify();
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index dc6ef2b49c..969743f78c 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -567,8 +567,8 @@ void ParticlesMaterial::_update_shader() {
}
}
//scale by scale
- code += " float base_scale = mix(scale * tex_scale, 1.0, scale_random * scale_rand);\n";
- code += " if (base_scale == 0.0) {\n";
+ code += " float base_scale = tex_scale * mix(scale, 1.0, scale_random * scale_rand);\n";
+ code += " if (base_scale < 0.000001) {\n";
code += " base_scale = 0.000001;\n";
code += " }\n";
if (trail_size_modifier.is_valid()) {
diff --git a/scene/resources/polygon_path_finder.cpp b/scene/resources/polygon_path_finder.cpp
index 52fc21ac11..bd3236cb5b 100644
--- a/scene/resources/polygon_path_finder.cpp
+++ b/scene/resources/polygon_path_finder.cpp
@@ -466,11 +466,11 @@ Dictionary PolygonPathFinder::_get_data() const {
PoolVector<Vector2> p;
PoolVector<int> ind;
Array connections;
- p.resize(points.size() - 2);
- connections.resize(points.size() - 2);
+ p.resize(MAX(0, points.size() - 2));
+ connections.resize(MAX(0, points.size() - 2));
ind.resize(edges.size() * 2);
PoolVector<float> penalties;
- penalties.resize(points.size() - 2);
+ penalties.resize(MAX(0, points.size() - 2));
{
PoolVector<Vector2>::Write wp = p.write();
PoolVector<float>::Write pw = penalties.write();
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index cd229732ba..baffc1396d 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -1225,7 +1225,7 @@ Ref<ResourceInteractiveLoader> ResourceFormatLoaderText::load_interactive(const
Error err;
FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
- ERR_FAIL_COND_V(err != OK, Ref<ResourceInteractiveLoader>());
+ ERR_FAIL_COND_V_MSG(err != OK, Ref<ResourceInteractiveLoader>(), "Cannot open file '" + p_path + "'.");
Ref<ResourceInteractiveLoaderText> ria = memnew(ResourceInteractiveLoaderText);
String path = p_original_path != "" ? p_original_path : p_path;
@@ -1321,7 +1321,7 @@ Error ResourceFormatLoaderText::convert_file_to_binary(const String &p_src_path,
Error err;
FileAccess *f = FileAccess::open(p_src_path, FileAccess::READ, &err);
- ERR_FAIL_COND_V(err != OK, ERR_CANT_OPEN);
+ ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_OPEN, "Cannot open file '" + p_src_path + "'.");
Ref<ResourceInteractiveLoaderText> ria = memnew(ResourceInteractiveLoaderText);
const String &path = p_src_path;
@@ -1481,7 +1481,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
Error err;
f = FileAccess::open(p_path, FileAccess::WRITE, &err);
- ERR_FAIL_COND_V(err, ERR_CANT_OPEN);
+ ERR_FAIL_COND_V_MSG(err, ERR_CANT_OPEN, "Cannot save file '" + p_path + "'.");
FileAccessRef _fref(f);
local_path = ProjectSettings::get_singleton()->localize_path(p_path);
@@ -1713,6 +1713,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
}
if (groups.size()) {
+ groups.sort_custom<StringName::AlphCompare>();
String sgroups = " groups=[\n";
for (int j = 0; j < groups.size(); j++) {
sgroups += "\"" + String(groups[j]).c_escape() + "\",\n";
diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp
index 89570beb5f..57e2470164 100644
--- a/scene/resources/shader.cpp
+++ b/scene/resources/shader.cpp
@@ -222,10 +222,7 @@ Error ResourceFormatSaverShader::save(const String &p_path, const RES &p_resourc
Error err;
FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err);
- if (err) {
-
- ERR_FAIL_COND_V(err, err);
- }
+ ERR_FAIL_COND_V_MSG(err, err, "Cannot save shader '" + p_path + "'.");
file->store_string(source);
if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
diff --git a/scene/resources/skin.cpp b/scene/resources/skin.cpp
new file mode 100644
index 0000000000..98c114e0e6
--- /dev/null
+++ b/scene/resources/skin.cpp
@@ -0,0 +1,132 @@
+/*************************************************************************/
+/* skin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 "skin.h"
+
+void Skin::set_bind_count(int p_size) {
+ ERR_FAIL_COND(p_size < 0);
+ binds.resize(p_size);
+ binds_ptr = binds.ptrw();
+ bind_count = p_size;
+ emit_changed();
+}
+
+void Skin::add_bind(int p_bone, const Transform &p_pose) {
+ uint32_t index = bind_count;
+ set_bind_count(bind_count + 1);
+ set_bind_bone(index, p_bone);
+ set_bind_pose(index, p_pose);
+}
+
+void Skin::set_bind_bone(int p_index, int p_bone) {
+ ERR_FAIL_INDEX(p_index, bind_count);
+ binds_ptr[p_index].bone = p_bone;
+ emit_changed();
+}
+
+void Skin::set_bind_pose(int p_index, const Transform &p_pose) {
+ ERR_FAIL_INDEX(p_index, bind_count);
+ binds_ptr[p_index].pose = p_pose;
+ emit_changed();
+}
+
+void Skin::clear_binds() {
+ binds.clear();
+ binds_ptr = nullptr;
+ bind_count = 0;
+ emit_changed();
+}
+
+bool Skin::_set(const StringName &p_name, const Variant &p_value) {
+ String name = p_name;
+ if (name == "bind_count") {
+ set_bind_count(p_value);
+ return true;
+ } else if (name.begins_with("bind/")) {
+ int index = name.get_slicec('/', 1).to_int();
+ String what = name.get_slicec('/', 2);
+ if (what == "bone") {
+ set_bind_bone(index, p_value);
+ return true;
+ } else if (what == "pose") {
+ set_bind_pose(index, p_value);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Skin::_get(const StringName &p_name, Variant &r_ret) const {
+
+ String name = p_name;
+ if (name == "bind_count") {
+ r_ret = get_bind_count();
+ return true;
+ } else if (name.begins_with("bind/")) {
+ int index = name.get_slicec('/', 1).to_int();
+ String what = name.get_slicec('/', 2);
+ if (what == "bone") {
+ r_ret = get_bind_bone(index);
+ return true;
+ } else if (what == "pose") {
+ r_ret = get_bind_pose(index);
+ return true;
+ }
+ }
+ return false;
+}
+void Skin::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::INT, "bind_count", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"));
+ for (int i = 0; i < get_bind_count(); i++) {
+ p_list->push_back(PropertyInfo(Variant::INT, "bind/" + itos(i) + "/bone", PROPERTY_HINT_RANGE, "0,16384,1,or_greater"));
+ p_list->push_back(PropertyInfo(Variant::TRANSFORM, "bind/" + itos(i) + "/pose"));
+ }
+}
+
+void Skin::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_bind_count", "bind_count"), &Skin::set_bind_count);
+ ClassDB::bind_method(D_METHOD("get_bind_count"), &Skin::get_bind_count);
+
+ ClassDB::bind_method(D_METHOD("add_bind", "bone", "pose"), &Skin::add_bind);
+
+ ClassDB::bind_method(D_METHOD("set_bind_pose", "bind_index", "pose"), &Skin::set_bind_pose);
+ ClassDB::bind_method(D_METHOD("get_bind_pose", "bind_index"), &Skin::get_bind_pose);
+
+ ClassDB::bind_method(D_METHOD("set_bind_bone", "bind_index", "bone"), &Skin::set_bind_bone);
+ ClassDB::bind_method(D_METHOD("get_bind_bone", "bind_index"), &Skin::get_bind_bone);
+
+ ClassDB::bind_method(D_METHOD("clear_binds"), &Skin::clear_binds);
+}
+
+Skin::Skin() {
+ bind_count = 0;
+ binds_ptr = nullptr;
+}
diff --git a/scene/resources/skin.h b/scene/resources/skin.h
new file mode 100644
index 0000000000..7dd02eca5d
--- /dev/null
+++ b/scene/resources/skin.h
@@ -0,0 +1,84 @@
+/*************************************************************************/
+/* skin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 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 SKIN_H
+#define SKIN_H
+
+#include "core/resource.h"
+
+class Skin : public Resource {
+ GDCLASS(Skin, Resource)
+
+ struct Bind {
+ int bone;
+ Transform pose;
+ };
+
+ Vector<Bind> binds;
+
+ Bind *binds_ptr;
+ int bind_count;
+
+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;
+
+ static void _bind_methods();
+
+public:
+ void set_bind_count(int p_size);
+ inline int get_bind_count() const { return bind_count; }
+
+ void add_bind(int p_bone, const Transform &p_pose);
+
+ void set_bind_bone(int p_index, int p_bone);
+ void set_bind_pose(int p_index, const Transform &p_pose);
+
+ inline int get_bind_bone(int p_index) const {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(p_index, bind_count, -1);
+#endif
+ return binds_ptr[p_index].bone;
+ }
+
+ inline Transform get_bind_pose(int p_index) const {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(p_index, bind_count, Transform());
+#endif
+ return binds_ptr[p_index].pose;
+ }
+
+ void clear_binds();
+
+ Skin();
+};
+
+#endif // SKIN_H
diff --git a/scene/resources/text_file.cpp b/scene/resources/text_file.cpp
index b84f3f1f9e..3faedc883d 100644
--- a/scene/resources/text_file.cpp
+++ b/scene/resources/text_file.cpp
@@ -53,9 +53,8 @@ Error TextFile::load_text(const String &p_path) {
PoolVector<uint8_t> sourcef;
Error err;
FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
- if (err) {
- ERR_FAIL_COND_V(err, err);
- }
+
+ ERR_FAIL_COND_V_MSG(err, err, "Cannot open TextFile '" + p_path + "'.");
int len = f->get_len();
sourcef.resize(len + 1);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index e44b17584b..d15a972358 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -175,7 +175,7 @@ void ImageTexture::_reload_hook(const RID &p_hook) {
img.instance();
Error err = ImageLoader::load_image(path, img);
- ERR_FAIL_COND(err != OK);
+ ERR_FAIL_COND_MSG(err != OK, "Cannot load image from path '" + path + "'.");
VisualServer::get_singleton()->texture_set_data(texture, img);
@@ -2355,7 +2355,7 @@ RES ResourceFormatLoaderTextureLayered::load(const String &p_path, const String
}
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- ERR_FAIL_COND_V(!f, RES());
+ ERR_FAIL_COND_V_MSG(!f, RES(), "Cannot open file '" + p_path + "'.");
uint8_t header[5] = { 0, 0, 0, 0, 0 };
f->get_buffer(header, 4);
@@ -2372,7 +2372,7 @@ RES ResourceFormatLoaderTextureLayered::load(const String &p_path, const String
}
} else {
- ERR_FAIL_V_MSG(RES(), "Unrecognized layered texture file format: " + String((const char *)header) + ".");
+ ERR_FAIL_V_MSG(RES(), "Unrecognized layered texture file format '" + String((const char *)header) + "'.");
}
int tw = f->get_32();
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index f5ea6adc85..3f2261b043 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -2410,10 +2410,10 @@ void VisualShaderNodeGroupBase::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_output_port", "id"), &VisualShaderNodeGroupBase::has_output_port);
ClassDB::bind_method(D_METHOD("clear_output_ports"), &VisualShaderNodeGroupBase::clear_output_ports);
- ClassDB::bind_method(D_METHOD("set_input_port_name"), &VisualShaderNodeGroupBase::set_input_port_name);
- ClassDB::bind_method(D_METHOD("set_input_port_type"), &VisualShaderNodeGroupBase::set_input_port_type);
- ClassDB::bind_method(D_METHOD("set_output_port_name"), &VisualShaderNodeGroupBase::set_output_port_name);
- ClassDB::bind_method(D_METHOD("set_output_port_type"), &VisualShaderNodeGroupBase::set_output_port_type);
+ ClassDB::bind_method(D_METHOD("set_input_port_name", "id", "name"), &VisualShaderNodeGroupBase::set_input_port_name);
+ ClassDB::bind_method(D_METHOD("set_input_port_type", "id", "type"), &VisualShaderNodeGroupBase::set_input_port_type);
+ ClassDB::bind_method(D_METHOD("set_output_port_name", "id", "name"), &VisualShaderNodeGroupBase::set_output_port_name);
+ ClassDB::bind_method(D_METHOD("set_output_port_type", "id", "type"), &VisualShaderNodeGroupBase::set_output_port_type);
ClassDB::bind_method(D_METHOD("get_free_input_port_id"), &VisualShaderNodeGroupBase::get_free_input_port_id);
ClassDB::bind_method(D_METHOD("get_free_output_port_id"), &VisualShaderNodeGroupBase::get_free_output_port_id);