diff options
Diffstat (limited to 'scene')
55 files changed, 923 insertions, 376 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..c0c1d8f691 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; 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..ead1e69f90 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,77 @@ 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) { + if (b.global_pose_override_amount >= 0.999) { + b.pose_global = b.global_pose_override; + } else { + if (b.disable_rest) { + if (b.enabled) { - Transform pose = b.pose; - if (b.custom_pose_enable) { + Transform pose = b.pose; + if (b.parent >= 0) { - pose = b.custom_pose * pose; - } + b.pose_global = bonesptr[b.parent].pose_global * pose; + } else { - if (b.parent >= 0) { - - 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) { + } else { + if (b.enabled) { - Transform pose = b.pose; - if (b.custom_pose_enable) { + Transform pose = b.pose; - 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 +306,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 +347,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 +361,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 +405,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 +422,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 +451,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 +465,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 +511,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(); @@ -552,23 +533,6 @@ Transform Skeleton::get_bone_pose(int p_bone) const { return bones[p_bone].pose; } -void Skeleton::set_bone_custom_pose(int p_bone, const Transform &p_custom_pose) { - - ERR_FAIL_INDEX(p_bone, bones.size()); - //ERR_FAIL_COND( !is_inside_scene() ); - - bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform()); - bones.write[p_bone].custom_pose = p_custom_pose; - - _make_dirty(); -} - -Transform Skeleton::get_bone_custom_pose(int p_bone) const { - - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); - return bones[p_bone].custom_pose; -} - void Skeleton::_make_dirty() { if (dirty) @@ -747,14 +711,66 @@ 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"); + return skin_ref; } void Skeleton::_bind_methods() { @@ -773,6 +789,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 +805,9 @@ 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 +817,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..f20c550055 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,19 +81,15 @@ 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; - 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 +101,9 @@ class Skeleton : public Spatial { Bone() { parent = -1; enabled = true; - ignore_animation = false; - custom_pose_enable = false; disable_rest = false; + global_pose_override_amount = 0; + global_pose_override_reset = false; #ifndef _3D_DISABLED physical_bone = NULL; cache_parent_physical_bone = NULL; @@ -86,17 +111,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 +147,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 +159,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 +166,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; @@ -170,14 +184,10 @@ public: void set_bone_pose(int p_bone, const Transform &p_pose); Transform get_bone_pose(int p_bone) const; - void set_bone_custom_pose(int p_bone, const Transform &p_custom_pose); - Transform get_bone_custom_pose(int p_bone) const; - 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/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/line_edit.cpp b/scene/gui/line_edit.cpp index 2445456a1f..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()) { @@ -531,6 +532,16 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { set_cursor_position(text.length()); shift_selection_check_post(k->get_shift()); } break; + case KEY_MENU: { + if (context_menu_enabled) { + Point2 pos = Point2(get_cursor_pixel_pos(), (get_size().y + get_font("font")->get_height()) / 2); + menu->set_position(get_global_transform().xform(pos)); + menu->set_size(Vector2(1, 1)); + menu->set_scale(get_global_transform().get_scale()); + menu->popup(); + menu->grab_focus(); + } + } break; default: { @@ -730,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; @@ -745,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) { @@ -762,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; @@ -799,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; @@ -854,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); @@ -1043,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; } @@ -1053,6 +1065,52 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) { set_cursor_position(ofs); } +int LineEdit::get_cursor_pixel_pos() { + + Ref<Font> font = get_font("font"); + int ofs = window_pos; + Ref<StyleBox> style = get_stylebox("normal"); + int pixel_ofs = 0; + Size2 size = get_size(); + bool display_clear_icon = !text.empty() && is_editable() && clear_button_enabled; + int r_icon_width = Control::get_icon("clear")->get_width(); + + switch (align) { + + case ALIGN_FILL: + case ALIGN_LEFT: { + + pixel_ofs = int(style->get_offset().x); + } break; + case ALIGN_CENTER: { + + if (window_pos != 0) + pixel_ofs = int(style->get_offset().x); + else + pixel_ofs = int(size.width - (cached_width)) / 2; + + if (display_clear_icon) + pixel_ofs -= int(r_icon_width / 2 + style->get_margin(MARGIN_RIGHT)); + } break; + case ALIGN_RIGHT: { + + pixel_ofs = int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_width)); + + if (display_clear_icon) + pixel_ofs -= int(r_icon_width + style->get_margin(MARGIN_RIGHT)); + } break; + } + + while (ofs < cursor_pos) { + if (font != NULL) { + pixel_ofs += font->get_char_size(text[ofs]).width; + } + ofs++; + } + + return pixel_ofs; +} + bool LineEdit::cursor_get_blink_enabled() const { return caret_blink_enabled; } @@ -1210,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) { @@ -1232,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; @@ -1297,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() { @@ -1404,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; @@ -1557,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(); @@ -1679,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"))); @@ -1704,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"); @@ -1755,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 c8fe845e3e..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; @@ -143,6 +143,7 @@ private: void set_window_pos(int p_pos); void set_cursor_at_pixel_pos(int p_x); + int get_cursor_pixel_pos(); void _reset_caret_blink_timer(); void _toggle_draw_caret(); @@ -229,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_label.h b/scene/gui/rich_text_label.h index 6755f9ef2a..481f8d9746 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -81,7 +81,7 @@ protected: static void _bind_methods(); private: - struct Item; + class Item; struct Line { @@ -103,8 +103,10 @@ private: } }; - struct Item : public Object { + class Item : public Object { + GDCLASS(Item, Object); + public: int index; Item *parent; ItemType type; @@ -127,8 +129,10 @@ private: virtual ~Item() { _clear_children(); } }; - struct ItemFrame : public Item { + class ItemFrame : public Item { + GDCLASS(ItemFrame, Item); + public: int parent_line; bool cell; Vector<Line> lines; @@ -143,71 +147,95 @@ private: } }; - struct ItemText : public Item { + class ItemText : public Item { + GDCLASS(ItemText, Item); + public: String text; ItemText() { type = ITEM_TEXT; } }; - struct ItemImage : public Item { + class ItemImage : public Item { + GDCLASS(ItemImage, Item); + public: Ref<Texture> image; ItemImage() { type = ITEM_IMAGE; } }; - struct ItemFont : public Item { + class ItemFont : public Item { + GDCLASS(ItemFont, Item); + public: Ref<Font> font; ItemFont() { type = ITEM_FONT; } }; - struct ItemColor : public Item { + class ItemColor : public Item { + GDCLASS(ItemColor, Item); + public: Color color; ItemColor() { type = ITEM_COLOR; } }; - struct ItemUnderline : public Item { + class ItemUnderline : public Item { + GDCLASS(ItemUnderline, Item); + public: ItemUnderline() { type = ITEM_UNDERLINE; } }; - struct ItemStrikethrough : public Item { + class ItemStrikethrough : public Item { + GDCLASS(ItemStrikethrough, Item); + public: ItemStrikethrough() { type = ITEM_STRIKETHROUGH; } }; - struct ItemMeta : public Item { + class ItemMeta : public Item { + GDCLASS(ItemMeta, Item); + public: Variant meta; ItemMeta() { type = ITEM_META; } }; - struct ItemAlign : public Item { + class ItemAlign : public Item { + GDCLASS(ItemAlign, Item); + public: Align align; ItemAlign() { type = ITEM_ALIGN; } }; - struct ItemIndent : public Item { + class ItemIndent : public Item { + GDCLASS(ItemIndent, Item); + public: int level; ItemIndent() { type = ITEM_INDENT; } }; - struct ItemList : public Item { + class ItemList : public Item { + GDCLASS(ItemList, Item); + public: ListType list_type; ItemList() { type = ITEM_LIST; } }; - struct ItemNewline : public Item { + class ItemNewline : public Item { + GDCLASS(ItemNewline, Item); + public: ItemNewline() { type = ITEM_NEWLINE; } }; - struct ItemTable : public Item { + class ItemTable : public Item { + GDCLASS(ItemTable, Item); + public: struct Column { bool expand; int expand_ratio; @@ -221,14 +249,20 @@ private: ItemTable() { type = ITEM_TABLE; } }; - struct ItemFade : public Item { + class ItemFade : public Item { + GDCLASS(ItemFade, Item); + + public: int starting_index; int length; ItemFade() { type = ITEM_FADE; } }; - struct ItemFX : public Item { + class ItemFX : public Item { + GDCLASS(ItemFX, Item); + + public: float elapsed_time; ItemFX() { @@ -236,7 +270,10 @@ private: } }; - struct ItemShake : public ItemFX { + class ItemShake : public ItemFX { + GDCLASS(ItemShake, ItemFX); + + public: int strength; float rate; uint64_t _current_rng; @@ -265,7 +302,10 @@ private: } }; - struct ItemWave : public ItemFX { + class ItemWave : public ItemFX { + GDCLASS(ItemWave, ItemFX); + + public: float frequency; float amplitude; @@ -276,7 +316,10 @@ private: } }; - struct ItemTornado : public ItemFX { + class ItemTornado : public ItemFX { + GDCLASS(ItemTornado, ItemFX); + + public: float radius; float frequency; @@ -287,7 +330,10 @@ private: } }; - struct ItemRainbow : public ItemFX { + class ItemRainbow : public ItemFX { + GDCLASS(ItemRainbow, ItemFX); + + public: float saturation; float value; float frequency; @@ -300,7 +346,10 @@ private: } }; - struct ItemCustomFX : public ItemFX { + class ItemCustomFX : public ItemFX { + GDCLASS(ItemCustomFX, ItemFX); + + public: String identifier; Dictionary environment; @@ -456,7 +505,7 @@ 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); 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 9678ebe6ea..75c845b8db 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -2081,6 +2081,44 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co r_col = col; } +Vector2i TextEdit::_get_cursor_pixel_pos() { + adjust_viewport_to_cursor(); + int row = (cursor.line - get_first_visible_line() - cursor.wrap_ofs); + // Correct for hidden and wrapped lines + for (int i = get_first_visible_line(); i < cursor.line; i++) { + if (is_line_hidden(i)) { + row -= 1; + continue; + } + row += times_line_wraps(i); + } + // Row might be wrapped. Adjust row and r_column + Vector<String> rows2 = get_wrap_rows_text(cursor.line); + while (rows2.size() > 1) { + if (cursor.column >= rows2[0].length()) { + cursor.column -= rows2[0].length(); + rows2.remove(0); + row++; + } else { + break; + } + } + + // Calculate final pixel position + int y = (row - get_v_scroll_offset() + 1 /*Bottom of line*/) * get_row_height(); + int x = cache.style_normal->get_margin(MARGIN_LEFT) + cache.line_number_w + cache.breakpoint_gutter_width + cache.fold_gutter_width + cache.info_gutter_width - cursor.x_ofs; + int ix = 0; + while (ix < rows2[0].size() && ix < cursor.column) { + if (cache.font != NULL) { + x += cache.font->get_char_size(rows2[0].get(ix)).width; + } + ix++; + } + x += get_indent_level(cursor.line) * cache.font->get_char_size(' ').width; + + return Vector2i(x, y); +} + void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const { float rows = p_mouse.y; @@ -2417,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) { @@ -2475,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(); @@ -3596,6 +3634,16 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { } break; + case KEY_MENU: { + if (context_menu_enabled) { + menu->set_position(get_global_transform().xform(_get_cursor_pixel_pos())); + menu->set_size(Vector2(1, 1)); + menu->set_scale(get_global_transform().get_scale()); + menu->popup(); + menu->grab_focus(); + } + } break; + default: { scancode_handled = false; @@ -6062,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/text_edit.h b/scene/gui/text_edit.h index e98201c1eb..e5d9b006fe 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -587,6 +587,7 @@ public: int cursor_get_column() const; int cursor_get_line() const; + Vector2i _get_cursor_pixel_pos(); bool cursor_get_blink_enabled() const; void cursor_set_blink_enabled(const bool p_enabled); 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/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index 2364a4a8a3..5704212831 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,17 +182,16 @@ 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)) { int best_match = 0; int diff = ABS(id.size - ((int64_t)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..c94e143580 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -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..ad8da63abf 100644 --- a/scene/resources/mesh_library.cpp +++ b/scene/resources/mesh_library.cpp @@ -117,7 +117,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 +125,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 +134,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 +144,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 +154,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 +163,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 +171,37 @@ 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>()); + 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 +211,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); |