diff options
Diffstat (limited to 'scene')
49 files changed, 1253 insertions, 97 deletions
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp index a30e1576bf..86b807cbcf 100644 --- a/scene/2d/mesh_instance_2d.cpp +++ b/scene/2d/mesh_instance_2d.cpp @@ -67,6 +67,9 @@ void MeshInstance2D::set_mesh(const Ref<Mesh> &p_mesh) { mesh = p_mesh; if (mesh.is_valid()) { + // If mesh is a PrimitiveMesh, calling get_rid on it can trigger a changed callback + // so do this before connecting to the change signal. + mesh->get_rid(); mesh->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw)); } diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 77665317a8..fb64bead13 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -531,9 +531,12 @@ Vector3 Camera3D::project_position(const Point2 &p_point, real_t p_z_depth) cons } Size2 viewport_size = get_viewport()->get_visible_rect().size; - Projection cm = _get_camera_projection(p_z_depth); + Projection cm = _get_camera_projection(_near); - Vector2 vp_he = cm.get_viewport_half_extents(); + Plane z_slice(Vector3(0, 0, 1), -p_z_depth); + Vector3 res; + z_slice.intersect_3(cm.get_projection_plane(Projection::Planes::PLANE_RIGHT), cm.get_projection_plane(Projection::Planes::PLANE_TOP), &res); + Vector2 vp_he(res.x, res.y); Vector2 point; point.x = (p_point.x / viewport_size.x) * 2.0 - 1.0; diff --git a/scene/3d/look_at_modifier_3d.cpp b/scene/3d/look_at_modifier_3d.cpp index a251f5a78c..e1ed24273b 100644 --- a/scene/3d/look_at_modifier_3d.cpp +++ b/scene/3d/look_at_modifier_3d.cpp @@ -35,7 +35,7 @@ void LookAtModifier3D::_validate_property(PropertyInfo &p_property) const { SkeletonModifier3D::_validate_property(p_property); - if (p_property.name == "bone" || p_property.name == "origin_bone") { + if (p_property.name == "bone_name" || p_property.name == "origin_bone_name") { Skeleton3D *skeleton = get_skeleton(); if (skeleton) { p_property.hint = PROPERTY_HINT_ENUM; @@ -51,11 +51,11 @@ void LookAtModifier3D::_validate_property(PropertyInfo &p_property) const { p_property.usage = PROPERTY_USAGE_NONE; } } else if (origin_from == ORIGIN_FROM_EXTERNAL_NODE) { - if (p_property.name == "origin_bone") { + if (p_property.name == "origin_bone" || p_property.name == "origin_bone_name") { p_property.usage = PROPERTY_USAGE_NONE; } } else { - if (p_property.name == "origin_external_node" || p_property.name == "origin_bone") { + if (p_property.name == "origin_external_node" || p_property.name == "origin_bone" || p_property.name == "origin_bone_name") { p_property.usage = PROPERTY_USAGE_NONE; } } @@ -77,8 +77,29 @@ PackedStringArray LookAtModifier3D::get_configuration_warnings() const { return warnings; } +void LookAtModifier3D::set_bone_name(const String &p_bone_name) { + bone_name = p_bone_name; + Skeleton3D *sk = get_skeleton(); + if (sk) { + set_bone(sk->find_bone(bone_name)); + } +} + +String LookAtModifier3D::get_bone_name() const { + return bone_name; +} + void LookAtModifier3D::set_bone(int p_bone) { bone = p_bone; + Skeleton3D *sk = get_skeleton(); + if (sk) { + if (bone <= -1 || bone >= sk->get_bone_count()) { + WARN_PRINT("Bone index out of range!"); + bone = -1; + } else { + bone_name = sk->get_bone_name(bone); + } + } } int LookAtModifier3D::get_bone() const { @@ -134,8 +155,29 @@ LookAtModifier3D::OriginFrom LookAtModifier3D::get_origin_from() const { return origin_from; } +void LookAtModifier3D::set_origin_bone_name(const String &p_bone_name) { + origin_bone_name = p_bone_name; + Skeleton3D *sk = get_skeleton(); + if (sk) { + set_origin_bone(sk->find_bone(origin_bone_name)); + } +} + +String LookAtModifier3D::get_origin_bone_name() const { + return origin_bone_name; +} + void LookAtModifier3D::set_origin_bone(int p_bone) { origin_bone = p_bone; + Skeleton3D *sk = get_skeleton(); + if (sk) { + if (origin_bone <= -1 || origin_bone >= sk->get_bone_count()) { + WARN_PRINT("Bone index out of range!"); + origin_bone = -1; + } else { + origin_bone_name = sk->get_bone_name(origin_bone); + } + } } int LookAtModifier3D::get_origin_bone() const { @@ -332,6 +374,8 @@ void LookAtModifier3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_target_node", "target_node"), &LookAtModifier3D::set_target_node); ClassDB::bind_method(D_METHOD("get_target_node"), &LookAtModifier3D::get_target_node); + ClassDB::bind_method(D_METHOD("set_bone_name", "bone_name"), &LookAtModifier3D::set_bone_name); + ClassDB::bind_method(D_METHOD("get_bone_name"), &LookAtModifier3D::get_bone_name); ClassDB::bind_method(D_METHOD("set_bone", "bone"), &LookAtModifier3D::set_bone); ClassDB::bind_method(D_METHOD("get_bone"), &LookAtModifier3D::get_bone); ClassDB::bind_method(D_METHOD("set_forward_axis", "forward_axis"), &LookAtModifier3D::set_forward_axis); @@ -345,6 +389,8 @@ void LookAtModifier3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_origin_from", "origin_from"), &LookAtModifier3D::set_origin_from); ClassDB::bind_method(D_METHOD("get_origin_from"), &LookAtModifier3D::get_origin_from); + ClassDB::bind_method(D_METHOD("set_origin_bone_name", "bone_name"), &LookAtModifier3D::set_origin_bone_name); + ClassDB::bind_method(D_METHOD("get_origin_bone_name"), &LookAtModifier3D::get_origin_bone_name); ClassDB::bind_method(D_METHOD("set_origin_bone", "bone"), &LookAtModifier3D::set_origin_bone); ClassDB::bind_method(D_METHOD("get_origin_bone"), &LookAtModifier3D::get_origin_bone); ClassDB::bind_method(D_METHOD("set_origin_external_node", "external_node"), &LookAtModifier3D::set_origin_external_node); @@ -399,14 +445,16 @@ void LookAtModifier3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node", PROPERTY_HINT_NODE_TYPE, "Node3D"), "set_target_node", "get_target_node"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "bone", PROPERTY_HINT_ENUM, ""), "set_bone", "get_bone"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "bone_name", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_bone_name", "get_bone_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_bone", "get_bone"); ADD_PROPERTY(PropertyInfo(Variant::INT, "forward_axis", PROPERTY_HINT_ENUM, "+X,-X,+Y,-Y,+Z,-Z"), "set_forward_axis", "get_forward_axis"); ADD_PROPERTY(PropertyInfo(Variant::INT, "primary_rotation_axis", PROPERTY_HINT_ENUM, "X,Y,Z"), "set_primary_rotation_axis", "get_primary_rotation_axis"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_secondary_rotation"), "set_use_secondary_rotation", "is_using_secondary_rotation"); ADD_GROUP("Origin Settings", "origin_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "origin_from", PROPERTY_HINT_ENUM, "Self,SpecificBone,ExternalNode"), "set_origin_from", "get_origin_from"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "origin_bone", PROPERTY_HINT_ENUM, ""), "set_origin_bone", "get_origin_bone"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "origin_bone_name", PROPERTY_HINT_ENUM_SUGGESTION, ""), "set_origin_bone_name", "get_origin_bone_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "origin_bone", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_origin_bone", "get_origin_bone"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "origin_external_node", PROPERTY_HINT_NODE_TYPE, "Node3D"), "set_origin_external_node", "get_origin_external_node"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "origin_offset"), "set_origin_offset", "get_origin_offset"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "origin_safe_margin", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater,suffix:m"), "set_origin_safe_margin", "get_origin_safe_margin"); @@ -476,7 +524,7 @@ void LookAtModifier3D::_process_modification() { destination = skeleton->get_bone_pose_rotation(bone); } else { Transform3D origin_tr; - if (origin_from == ORIGIN_FROM_SPECIFIC_BONE && origin_bone < skeleton->get_bone_count()) { + if (origin_from == ORIGIN_FROM_SPECIFIC_BONE && origin_bone >= 0 && origin_bone < skeleton->get_bone_count()) { origin_tr = skeleton->get_global_transform() * skeleton->get_bone_global_pose(origin_bone); } else if (origin_from == ORIGIN_FROM_EXTERNAL_NODE) { Node3D *origin_src = Object::cast_to<Node3D>(get_node_or_null(origin_external_node)); @@ -488,7 +536,7 @@ void LookAtModifier3D::_process_modification() { } else { origin_tr = bone_rest_space; } - forward_vector = bone_rest_space.basis.xform_inv((target->get_global_position() - origin_tr.translated_local(origin_offset).origin)); + forward_vector = bone_rest_space.orthonormalized().basis.xform_inv((target->get_global_position() - origin_tr.translated_local(origin_offset).origin)); forward_vector_nrm = forward_vector.normalized(); if (forward_vector_nrm.abs().is_equal_approx(get_vector_from_axis(primary_rotation_axis))) { destination = skeleton->get_bone_pose_rotation(bone); @@ -499,6 +547,8 @@ void LookAtModifier3D::_process_modification() { } // Detect flipping. + bool is_not_max_influence = influence < 1.0; + bool is_flippable = use_angle_limitation || is_not_max_influence; Vector3::Axis current_forward_axis = get_axis_from_bone_axis(forward_axis); if (is_intersecting_axis(prev_forward_vector, forward_vector, current_forward_axis, secondary_rotation_axis) || is_intersecting_axis(prev_forward_vector, forward_vector, primary_rotation_axis, primary_rotation_axis, true) || @@ -506,16 +556,20 @@ void LookAtModifier3D::_process_modification() { (prev_forward_vector != Vector3(0, 0, 0) && forward_vector == Vector3(0, 0, 0)) || (prev_forward_vector == Vector3(0, 0, 0) && forward_vector != Vector3(0, 0, 0))) { init_transition(); - } else if (use_angle_limitation && signbit(prev_forward_vector[secondary_rotation_axis]) != signbit(forward_vector[secondary_rotation_axis])) { + } else if (is_flippable && signbit(prev_forward_vector[secondary_rotation_axis]) != signbit(forward_vector[secondary_rotation_axis])) { // Flipping by angle_limitation can be detected by sign of secondary rotation axes during forward_vector is rotated more than 90 degree from forward_axis (means dot production is negative). Vector3 prev_forward_vector_nrm = forward_vector.normalized(); Vector3 rest_forward_vector = get_vector_from_bone_axis(forward_axis); if (symmetry_limitation) { - if (!Math::is_equal_approx(primary_limit_angle, (float)Math_TAU) && prev_forward_vector_nrm.dot(rest_forward_vector) < 0 && forward_vector_nrm.dot(rest_forward_vector) < 0) { + if ((is_not_max_influence || !Math::is_equal_approx(primary_limit_angle, (float)Math_TAU)) && + prev_forward_vector_nrm.dot(rest_forward_vector) < 0 && + forward_vector_nrm.dot(rest_forward_vector) < 0) { init_transition(); } } else { - if (!Math::is_equal_approx(primary_positive_limit_angle + primary_negative_limit_angle, (float)Math_TAU) && prev_forward_vector_nrm.dot(rest_forward_vector) < 0 && forward_vector_nrm.dot(rest_forward_vector) < 0) { + if ((is_not_max_influence || !Math::is_equal_approx(primary_positive_limit_angle + primary_negative_limit_angle, (float)Math_TAU)) && + prev_forward_vector_nrm.dot(rest_forward_vector) < 0 && + forward_vector_nrm.dot(rest_forward_vector) < 0) { init_transition(); } } @@ -530,7 +584,7 @@ void LookAtModifier3D::_process_modification() { delta = get_physics_process_delta_time(); } remaining = MAX(0, remaining - time_step * delta); - if (use_angle_limitation) { + if (is_flippable) { // Interpolate through the rest same as AnimationTree blending for preventing to penetrate the bone into the body. Quaternion rest = skeleton->get_bone_rest(bone).basis.get_rotation_quaternion(); float weight = Tween::run_equation(transition_type, ease_type, 1 - remaining, 0.0, 1.0, 1.0); diff --git a/scene/3d/look_at_modifier_3d.h b/scene/3d/look_at_modifier_3d.h index f4bdc381ad..b793152ec9 100644 --- a/scene/3d/look_at_modifier_3d.h +++ b/scene/3d/look_at_modifier_3d.h @@ -56,7 +56,8 @@ public: }; private: - int bone = 0; + String bone_name; + int bone = -1; Vector3 forward_vector; Vector3 forward_vector_nrm; @@ -66,6 +67,7 @@ private: bool use_secondary_rotation = true; OriginFrom origin_from = ORIGIN_FROM_SELF; + String origin_bone_name; int origin_bone = -1; NodePath origin_external_node; @@ -125,6 +127,8 @@ protected: virtual void _process_modification() override; public: + void set_bone_name(const String &p_bone_name); + String get_bone_name() const; void set_bone(int p_bone); int get_bone() const; @@ -137,6 +141,8 @@ public: void set_origin_from(OriginFrom p_origin_from); OriginFrom get_origin_from() const; + void set_origin_bone_name(const String &p_bone_name); + String get_origin_bone_name() const; void set_origin_bone(int p_bone); int get_origin_bone() const; void set_origin_external_node(const NodePath &p_external_node); diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 91edd49177..9e27b49c72 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -843,10 +843,10 @@ void Node3D::reparent(Node *p_parent, bool p_keep_global_transform) { ERR_THREAD_GUARD; if (p_keep_global_transform) { Transform3D temp = get_global_transform(); - Node::reparent(p_parent); + Node::reparent(p_parent, p_keep_global_transform); set_global_transform(temp); } else { - Node::reparent(p_parent); + Node::reparent(p_parent, p_keep_global_transform); } } diff --git a/scene/3d/physics/collision_shape_3d.cpp b/scene/3d/physics/collision_shape_3d.cpp index c169045701..84dd03d353 100644 --- a/scene/3d/physics/collision_shape_3d.cpp +++ b/scene/3d/physics/collision_shape_3d.cpp @@ -83,12 +83,19 @@ void CollisionShape3D::_update_in_shape_owner(bool p_xform_only) { void CollisionShape3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_PARENTED: { +#ifdef DEBUG_ENABLED + if (debug_color == get_placeholder_default_color()) { + debug_color = SceneTree::get_singleton()->get_debug_collisions_color(); + } +#endif // DEBUG_ENABLED + collision_object = Object::cast_to<CollisionObject3D>(get_parent()); if (collision_object) { owner_id = collision_object->create_shape_owner(this); if (shape.is_valid()) { collision_object->shape_owner_add_shape(owner_id, shape); } + _update_in_shape_owner(); } } break; @@ -168,11 +175,26 @@ void CollisionShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_shape"), &CollisionShape3D::get_shape); ClassDB::bind_method(D_METHOD("set_disabled", "enable"), &CollisionShape3D::set_disabled); ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionShape3D::is_disabled); + ClassDB::bind_method(D_METHOD("make_convex_from_siblings"), &CollisionShape3D::make_convex_from_siblings); ClassDB::set_method_flags("CollisionShape3D", "make_convex_from_siblings", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); + +#ifdef DEBUG_ENABLED + ClassDB::bind_method(D_METHOD("set_debug_color", "color"), &CollisionShape3D::set_debug_color); + ClassDB::bind_method(D_METHOD("get_debug_color"), &CollisionShape3D::get_debug_color); + + ClassDB::bind_method(D_METHOD("set_enable_debug_fill", "enable"), &CollisionShape3D::set_debug_fill_enabled); + ClassDB::bind_method(D_METHOD("get_enable_debug_fill"), &CollisionShape3D::get_debug_fill_enabled); + + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_color"), "set_debug_color", "get_debug_color"); + // Default value depends on a project setting, override for doc generation purposes. + ADD_PROPERTY_DEFAULT("debug_color", get_placeholder_default_color()); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_fill"), "set_enable_debug_fill", "get_enable_debug_fill"); +#endif // DEBUG_ENABLED } void CollisionShape3D::set_shape(const Ref<Shape3D> &p_shape) { @@ -180,11 +202,27 @@ void CollisionShape3D::set_shape(const Ref<Shape3D> &p_shape) { return; } if (shape.is_valid()) { + shape->disconnect_changed(callable_mp(this, &CollisionShape3D::shape_changed)); shape->disconnect_changed(callable_mp((Node3D *)this, &Node3D::update_gizmos)); } shape = p_shape; if (shape.is_valid()) { +#ifdef DEBUG_ENABLED + if (shape->get_debug_color() != get_placeholder_default_color()) { + set_debug_color(shape->get_debug_color()); + set_debug_fill_enabled(shape->get_debug_fill()); + } else if (get_debug_color() != get_placeholder_default_color()) { + shape->set_debug_color(debug_color); + shape->set_debug_fill(debug_fill); + } else { + set_debug_color(SceneTree::get_singleton()->get_debug_collisions_color()); + shape->set_debug_color(SceneTree::get_singleton()->get_debug_collisions_color()); + shape->set_debug_fill(debug_fill); + } +#endif // DEBUG_ENABLED + shape->connect_changed(callable_mp((Node3D *)this, &Node3D::update_gizmos)); + shape->connect_changed(callable_mp(this, &CollisionShape3D::shape_changed)); } update_gizmos(); if (collision_object) { @@ -217,6 +255,66 @@ bool CollisionShape3D::is_disabled() const { return disabled; } +#ifdef DEBUG_ENABLED +void CollisionShape3D::set_debug_color(const Color &p_color) { + if (p_color == get_placeholder_default_color()) { + debug_color = SceneTree::get_singleton()->get_debug_collisions_color(); + } else if (debug_color != p_color) { + debug_color = p_color; + + if (shape.is_valid()) { + shape->set_debug_color(p_color); + } + } +} + +Color CollisionShape3D::get_debug_color() const { + return debug_color; +} + +void CollisionShape3D::set_debug_fill_enabled(bool p_enable) { + if (debug_fill == p_enable) { + return; + } + + debug_fill = p_enable; + + if (shape.is_valid()) { + shape->set_debug_fill(p_enable); + } +} + +bool CollisionShape3D::get_debug_fill_enabled() const { + return debug_fill; +} + +bool CollisionShape3D::_property_can_revert(const StringName &p_name) const { + if (p_name == "debug_color") { + return true; + } + return false; +} + +bool CollisionShape3D::_property_get_revert(const StringName &p_name, Variant &r_property) const { + if (p_name == "debug_color") { + r_property = SceneTree::get_singleton()->get_debug_collisions_color(); + return true; + } + return false; +} +#endif // DEBUG_ENABLED + +void CollisionShape3D::shape_changed() { +#ifdef DEBUG_ENABLED + if (shape->get_debug_color() != debug_color) { + set_debug_color(shape->get_debug_color()); + } + if (shape->get_debug_fill() != debug_fill) { + set_debug_fill_enabled(shape->get_debug_fill()); + } +#endif // DEBUG_ENABLED +} + CollisionShape3D::CollisionShape3D() { //indicator = RenderingServer::get_singleton()->mesh_create(); set_notify_local_transform(true); diff --git a/scene/3d/physics/collision_shape_3d.h b/scene/3d/physics/collision_shape_3d.h index 74f7c16d33..56544806da 100644 --- a/scene/3d/physics/collision_shape_3d.h +++ b/scene/3d/physics/collision_shape_3d.h @@ -45,6 +45,13 @@ class CollisionShape3D : public Node3D { uint32_t owner_id = 0; CollisionObject3D *collision_object = nullptr; +#ifdef DEBUG_ENABLED + Color debug_color = get_placeholder_default_color(); + bool debug_fill = true; + + static const Color get_placeholder_default_color() { return Color(0.0, 0.0, 0.0, 0.0); } +#endif // DEBUG_ENABLED + #ifndef DISABLE_DEPRECATED void resource_changed(Ref<Resource> res); #endif @@ -57,6 +64,13 @@ protected: void _notification(int p_what); static void _bind_methods(); +#ifdef DEBUG_ENABLED + bool _property_can_revert(const StringName &p_name) const; + bool _property_get_revert(const StringName &p_name, Variant &r_property) const; +#endif // DEBUG_ENABLED + + void shape_changed(); + public: void make_convex_from_siblings(); @@ -66,6 +80,14 @@ public: void set_disabled(bool p_disabled); bool is_disabled() const; +#ifdef DEBUG_ENABLED + void set_debug_color(const Color &p_color); + Color get_debug_color() const; + + void set_debug_fill_enabled(bool p_enable); + bool get_debug_fill_enabled() const; +#endif // DEBUG_ENABLED + PackedStringArray get_configuration_warnings() const override; CollisionShape3D(); diff --git a/scene/3d/retarget_modifier_3d.cpp b/scene/3d/retarget_modifier_3d.cpp new file mode 100644 index 0000000000..7e2462faf8 --- /dev/null +++ b/scene/3d/retarget_modifier_3d.cpp @@ -0,0 +1,443 @@ +/**************************************************************************/ +/* retarget_modifier_3d.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* REDOT ENGINE */ +/* https://redotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2024-present Redot Engine contributors */ +/* (see REDOT_AUTHORS.md) */ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 "retarget_modifier_3d.h" + +PackedStringArray RetargetModifier3D::get_configuration_warnings() const { + PackedStringArray warnings = SkeletonModifier3D::get_configuration_warnings(); + if (child_skeletons.is_empty()) { + warnings.push_back(RTR("There is no child Skeleton3D!")); + } + return warnings; +} + +/// Caching + +void RetargetModifier3D::_profile_changed(Ref<SkeletonProfile> p_old, Ref<SkeletonProfile> p_new) { + if (p_old.is_valid() && p_old->is_connected(SNAME("profile_updated"), callable_mp(this, &RetargetModifier3D::cache_rests_with_reset))) { + p_old->disconnect(SNAME("profile_updated"), callable_mp(this, &RetargetModifier3D::cache_rests_with_reset)); + } + profile = p_new; + if (p_new.is_valid() && !p_new->is_connected(SNAME("profile_updated"), callable_mp(this, &RetargetModifier3D::cache_rests_with_reset))) { + p_new->connect(SNAME("profile_updated"), callable_mp(this, &RetargetModifier3D::cache_rests_with_reset)); + } + cache_rests_with_reset(); +} + +void RetargetModifier3D::_skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) { + if (p_old && p_old->is_connected(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::cache_rests))) { + p_old->disconnect(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::cache_rests)); + } + if (p_new && !p_new->is_connected(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::cache_rests))) { + p_new->connect(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::cache_rests)); + } + cache_rests(); +} + +void RetargetModifier3D::cache_rests_with_reset() { + _reset_child_skeleton_poses(); + cache_rests(); +} + +void RetargetModifier3D::cache_rests() { + source_bone_ids.clear(); + + Skeleton3D *source_skeleton = get_skeleton(); + if (profile.is_null() || !source_skeleton) { + return; + } + + PackedStringArray bone_names = profile->get_bone_names(); + for (const String &E : bone_names) { + source_bone_ids.push_back(source_skeleton->find_bone(E)); + } + + for (int i = 0; i < child_skeletons.size(); i++) { + _update_child_skeleton_rests(i); + } +} + +Vector<RetargetModifier3D::RetargetBoneInfo> RetargetModifier3D::cache_bone_global_rests(Skeleton3D *p_skeleton) { + // Retarget global pose in model space: + // tgt_global_pose.basis = src_global_pose.basis * src_rest.basis.inv * src_parent_global_rest.basis.inv * tgt_parent_global_rest.basis * tgt_rest.basis + // tgt_global_pose.origin = src_global_pose.origin + Skeleton3D *source_skeleton = get_skeleton(); + Vector<RetargetBoneInfo> bone_rests; + if (profile.is_null() || !source_skeleton) { + return bone_rests; + } + PackedStringArray bone_names = profile->get_bone_names(); + for (const String &E : bone_names) { + RetargetBoneInfo rbi; + int source_bone_id = source_skeleton->find_bone(E); + if (source_bone_id >= 0) { + Transform3D parent_global_rest; + int bone_parent = source_skeleton->get_bone_parent(source_bone_id); + if (bone_parent >= 0) { + parent_global_rest = source_skeleton->get_bone_global_rest(bone_parent); + } + rbi.post_basis = source_skeleton->get_bone_rest(source_bone_id).basis.inverse() * parent_global_rest.basis.inverse(); + } + int target_bone_id = p_skeleton->find_bone(E); + rbi.bone_id = target_bone_id; + if (target_bone_id >= 0) { + Transform3D parent_global_rest; + int bone_parent = p_skeleton->get_bone_parent(target_bone_id); + if (bone_parent >= 0) { + parent_global_rest = p_skeleton->get_bone_global_rest(bone_parent); + } + rbi.post_basis = rbi.post_basis * parent_global_rest.basis * p_skeleton->get_bone_rest(target_bone_id).basis; + } + bone_rests.push_back(rbi); + } + return bone_rests; +} + +Vector<RetargetModifier3D::RetargetBoneInfo> RetargetModifier3D::cache_bone_rests(Skeleton3D *p_skeleton) { + // Retarget pose in model space: + // tgt_pose.basis = tgt_parent_global_rest.basis.inv * src_parent_global_rest.basis * src_pose.basis * src_rest.basis.inv * src_parent_global_rest.basis.inv * tgt_parent_global_rest.basis * tgt_rest.basis + // tgt_pose.origin = tgt_parent_global_rest.basis.inv.xform(src_parent_global_rest.basis.xform(src_pose.origin - src_rest.origin)) + tgt_rest.origin + Skeleton3D *source_skeleton = get_skeleton(); + Vector<RetargetBoneInfo> bone_rests; + if (profile.is_null() || !source_skeleton) { + return bone_rests; + } + PackedStringArray bone_names = profile->get_bone_names(); + for (const String &E : bone_names) { + RetargetBoneInfo rbi; + int source_bone_id = source_skeleton->find_bone(E); + if (source_bone_id >= 0) { + Transform3D parent_global_rest; + int bone_parent = source_skeleton->get_bone_parent(source_bone_id); + if (bone_parent >= 0) { + parent_global_rest = source_skeleton->get_bone_global_rest(bone_parent); + } + rbi.pre_basis = parent_global_rest.basis; + rbi.post_basis = source_skeleton->get_bone_rest(source_bone_id).basis.inverse() * parent_global_rest.basis.inverse(); + } + + int target_bone_id = p_skeleton->find_bone(E); + rbi.bone_id = target_bone_id; + if (target_bone_id >= 0) { + Transform3D parent_global_rest; + int bone_parent = p_skeleton->get_bone_parent(target_bone_id); + if (bone_parent >= 0) { + parent_global_rest = p_skeleton->get_bone_global_rest(bone_parent); + } + rbi.pre_basis = parent_global_rest.basis.inverse() * rbi.pre_basis; + rbi.post_basis = rbi.post_basis * parent_global_rest.basis * p_skeleton->get_bone_rest(target_bone_id).basis; + } + bone_rests.push_back(rbi); + } + return bone_rests; +} + +void RetargetModifier3D::_update_child_skeleton_rests(int p_child_skeleton_idx) { + ERR_FAIL_INDEX(p_child_skeleton_idx, child_skeletons.size()); + Skeleton3D *c = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(child_skeletons[p_child_skeleton_idx].skeleton_id)); + if (!c) { + return; + } + if (use_global_pose) { + child_skeletons.write[p_child_skeleton_idx].humanoid_bone_rests = cache_bone_global_rests(c); + } else { + child_skeletons.write[p_child_skeleton_idx].humanoid_bone_rests = cache_bone_rests(c); + } +} + +void RetargetModifier3D::_update_child_skeletons() { + _reset_child_skeletons(); + + for (int i = 0; i < get_child_count(); i++) { + RetargetInfo ri; + Skeleton3D *c = Object::cast_to<Skeleton3D>(get_child(i)); + if (c) { + int id = child_skeletons.size(); + ri.skeleton_id = c->get_instance_id(); + child_skeletons.push_back(ri); + c->connect(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::_update_child_skeleton_rests).bind(id)); + } + } + + cache_rests(); + update_configuration_warnings(); +} + +void RetargetModifier3D::_reset_child_skeleton_poses() { + for (const RetargetInfo &E : child_skeletons) { + Skeleton3D *c = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(E.skeleton_id)); + if (!c) { + continue; + } + if (c->is_connected(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::_update_child_skeleton_rests))) { + c->disconnect(SNAME("rest_updated"), callable_mp(this, &RetargetModifier3D::_update_child_skeleton_rests)); + } + for (const RetargetBoneInfo &F : E.humanoid_bone_rests) { + if (F.bone_id < 0) { + continue; + } + c->reset_bone_pose(F.bone_id); + } + } +} + +void RetargetModifier3D::_reset_child_skeletons() { + _reset_child_skeleton_poses(); + child_skeletons.clear(); +} + +/// General functions + +void RetargetModifier3D::add_child_notify(Node *p_child) { + if (Object::cast_to<Skeleton3D>(p_child)) { + _update_child_skeletons(); + } +} + +void RetargetModifier3D::move_child_notify(Node *p_child) { + if (Object::cast_to<Skeleton3D>(p_child)) { + _update_child_skeletons(); + } +} + +void RetargetModifier3D::remove_child_notify(Node *p_child) { + if (Object::cast_to<Skeleton3D>(p_child)) { + // Reset after process. + callable_mp(this, &RetargetModifier3D::_update_child_skeletons).call_deferred(); + } +} + +void RetargetModifier3D::_validate_property(PropertyInfo &p_property) const { + if (use_global_pose) { + if (p_property.name == "position_enabled" || p_property.name == "rotation_enabled" || p_property.name == "scale_enabled") { + p_property.usage = PROPERTY_USAGE_NONE; + } + } +} + +void RetargetModifier3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_profile", "profile"), &RetargetModifier3D::set_profile); + ClassDB::bind_method(D_METHOD("get_profile"), &RetargetModifier3D::get_profile); + ClassDB::bind_method(D_METHOD("set_use_global_pose", "use_global_pose"), &RetargetModifier3D::set_use_global_pose); + ClassDB::bind_method(D_METHOD("is_using_global_pose"), &RetargetModifier3D::is_using_global_pose); + ClassDB::bind_method(D_METHOD("set_position_enabled", "enabled"), &RetargetModifier3D::set_position_enabled); + ClassDB::bind_method(D_METHOD("is_position_enabled"), &RetargetModifier3D::is_position_enabled); + ClassDB::bind_method(D_METHOD("set_rotation_enabled", "enabled"), &RetargetModifier3D::set_rotation_enabled); + ClassDB::bind_method(D_METHOD("is_rotation_enabled"), &RetargetModifier3D::is_rotation_enabled); + ClassDB::bind_method(D_METHOD("set_scale_enabled", "enabled"), &RetargetModifier3D::set_scale_enabled); + ClassDB::bind_method(D_METHOD("is_scale_enabled"), &RetargetModifier3D::is_scale_enabled); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "profile", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonProfile"), "set_profile", "get_profile"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_global_pose"), "set_use_global_pose", "is_using_global_pose"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "position_enabled"), "set_position_enabled", "is_position_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rotation_enabled"), "set_rotation_enabled", "is_rotation_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scale_enabled"), "set_scale_enabled", "is_scale_enabled"); +} + +void RetargetModifier3D::_set_active(bool p_active) { + if (!p_active) { + _reset_child_skeleton_poses(); + } +} + +void RetargetModifier3D::_retarget_global_pose() { + Skeleton3D *source_skeleton = get_skeleton(); + if (profile.is_null() || !source_skeleton) { + return; + } + + LocalVector<Transform3D> source_poses; + if (influence < 1.0) { + for (int source_bone_id : source_bone_ids) { + source_poses.push_back(source_bone_id < 0 ? Transform3D() : source_skeleton->get_bone_global_rest(source_bone_id).interpolate_with(source_skeleton->get_bone_global_pose(source_bone_id), influence)); + } + } else { + for (int source_bone_id : source_bone_ids) { + source_poses.push_back(source_bone_id < 0 ? Transform3D() : source_skeleton->get_bone_global_pose(source_bone_id)); + } + } + + for (const RetargetInfo &E : child_skeletons) { + Skeleton3D *target_skeleton = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(E.skeleton_id)); + if (!target_skeleton) { + continue; + } + for (int i = 0; i < source_bone_ids.size(); i++) { + int target_bone_id = E.humanoid_bone_rests[i].bone_id; + if (target_bone_id < 0) { + continue; + } + Transform3D retarget_pose = source_poses[i]; + retarget_pose.basis = retarget_pose.basis * E.humanoid_bone_rests[i].post_basis; + target_skeleton->set_bone_global_pose(target_bone_id, retarget_pose); + } + } +} + +void RetargetModifier3D::_retarget_pose() { + Skeleton3D *source_skeleton = get_skeleton(); + if (profile.is_null() || !source_skeleton) { + return; + } + + LocalVector<Transform3D> source_poses; + if (influence < 1.0) { + for (int source_bone_id : source_bone_ids) { + source_poses.push_back(source_bone_id < 0 ? Transform3D() : source_skeleton->get_bone_rest(source_bone_id).interpolate_with(source_skeleton->get_bone_pose(source_bone_id), influence)); + } + } else { + for (int source_bone_id : source_bone_ids) { + source_poses.push_back(source_bone_id < 0 ? Transform3D() : source_skeleton->get_bone_pose(source_bone_id)); + } + } + + for (const RetargetInfo &E : child_skeletons) { + Skeleton3D *target_skeleton = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(E.skeleton_id)); + if (!target_skeleton) { + continue; + } + float motion_scale_ratio = target_skeleton->get_motion_scale() / source_skeleton->get_motion_scale(); + for (int i = 0; i < source_bone_ids.size(); i++) { + int target_bone_id = E.humanoid_bone_rests[i].bone_id; + if (target_bone_id < 0) { + continue; + } + int source_bone_id = source_bone_ids[i]; + if (source_bone_id < 0) { + continue; + } + + Transform3D extracted_transform = source_poses[i]; + extracted_transform.basis = E.humanoid_bone_rests[i].pre_basis * extracted_transform.basis * E.humanoid_bone_rests[i].post_basis; + extracted_transform.origin = E.humanoid_bone_rests[i].pre_basis.xform((extracted_transform.origin - source_skeleton->get_bone_rest(source_bone_id).origin) * motion_scale_ratio) + target_skeleton->get_bone_rest(target_bone_id).origin; + + Transform3D retarget_pose = target_skeleton->get_bone_pose(target_bone_id); + if (enable_position) { + retarget_pose.origin = extracted_transform.origin; + } + if (enable_rotation) { + retarget_pose.basis = extracted_transform.basis.get_rotation_quaternion(); + } + if (enable_scale) { + retarget_pose.basis.scale_local(extracted_transform.basis.get_scale()); + } + target_skeleton->set_bone_pose(target_bone_id, retarget_pose); + } + } +} + +void RetargetModifier3D::_process_modification() { + if (use_global_pose) { + _retarget_global_pose(); + } else { + _retarget_pose(); + } +} + +void RetargetModifier3D::set_profile(Ref<SkeletonProfile> p_profile) { + if (profile == p_profile) { + return; + } + _profile_changed(profile, p_profile); +} + +Ref<SkeletonProfile> RetargetModifier3D::get_profile() const { + return profile; +} + +void RetargetModifier3D::set_use_global_pose(bool p_use_global_pose) { + if (use_global_pose == p_use_global_pose) { + return; + } + + use_global_pose = p_use_global_pose; + cache_rests_with_reset(); + + notify_property_list_changed(); +} + +bool RetargetModifier3D::is_using_global_pose() const { + return use_global_pose; +} + +void RetargetModifier3D::set_position_enabled(bool p_enabled) { + if (enable_position != p_enabled) { + _reset_child_skeleton_poses(); + } + enable_position = p_enabled; +} + +bool RetargetModifier3D::is_position_enabled() const { + return enable_position; +} + +void RetargetModifier3D::set_rotation_enabled(bool p_enabled) { + if (enable_rotation != p_enabled) { + _reset_child_skeleton_poses(); + } + enable_rotation = p_enabled; +} + +bool RetargetModifier3D::is_rotation_enabled() const { + return enable_rotation; +} + +void RetargetModifier3D::set_scale_enabled(bool p_enabled) { + if (enable_scale != p_enabled) { + _reset_child_skeleton_poses(); + } + enable_scale = p_enabled; +} + +bool RetargetModifier3D::is_scale_enabled() const { + return enable_scale; +} + +void RetargetModifier3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + _update_child_skeletons(); + } break; + case NOTIFICATION_EDITOR_PRE_SAVE: { + _reset_child_skeleton_poses(); + } break; + case NOTIFICATION_EXIT_TREE: { + _reset_child_skeletons(); + } break; + } +} + +RetargetModifier3D::RetargetModifier3D() { +} + +RetargetModifier3D::~RetargetModifier3D() { +} diff --git a/scene/3d/retarget_modifier_3d.h b/scene/3d/retarget_modifier_3d.h new file mode 100644 index 0000000000..b6b5735bc2 --- /dev/null +++ b/scene/3d/retarget_modifier_3d.h @@ -0,0 +1,112 @@ +/**************************************************************************/ +/* retarget_modifier_3d.h */ +/**************************************************************************/ +/* This file is part of: */ +/* REDOT ENGINE */ +/* https://redotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2024-present Redot Engine contributors */ +/* (see REDOT_AUTHORS.md) */ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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 RETARGET_MODIFIER_3D_H +#define RETARGET_MODIFIER_3D_H + +#include "scene/3d/skeleton_modifier_3d.h" +#include "scene/resources/skeleton_profile.h" + +class RetargetModifier3D : public SkeletonModifier3D { + GDCLASS(RetargetModifier3D, SkeletonModifier3D); + + Ref<SkeletonProfile> profile; + + bool use_global_pose = false; + bool enable_position = true; + bool enable_rotation = true; + bool enable_scale = true; + + struct RetargetBoneInfo { + int bone_id = -1; + Basis pre_basis; + Basis post_basis; + }; + + struct RetargetInfo { + ObjectID skeleton_id; + Vector<RetargetBoneInfo> humanoid_bone_rests; + }; + + Vector<RetargetInfo> child_skeletons; + Vector<int> source_bone_ids; + + void _update_child_skeleton_rests(int p_child_skeleton_idx); + void _update_child_skeletons(); + void _reset_child_skeleton_poses(); + void _reset_child_skeletons(); + + void cache_rests_with_reset(); + void cache_rests(); + Vector<RetargetBoneInfo> cache_bone_global_rests(Skeleton3D *p_skeleton); + Vector<RetargetBoneInfo> cache_bone_rests(Skeleton3D *p_skeleton); + Vector<RetargetBoneInfo> get_humanoid_bone_rests(Skeleton3D *p_skeleton); + + void _retarget_global_pose(); + void _retarget_pose(); + +protected: + virtual void _skeleton_changed(Skeleton3D *p_old, Skeleton3D *p_new) override; + void _profile_changed(Ref<SkeletonProfile> p_old, Ref<SkeletonProfile> p_new); + + void _validate_property(PropertyInfo &p_property) const; + + static void _bind_methods(); + virtual void _notification(int p_what); + + virtual void add_child_notify(Node *p_child) override; + virtual void move_child_notify(Node *p_child) override; + virtual void remove_child_notify(Node *p_child) override; + + virtual void _set_active(bool p_active) override; + virtual void _process_modification() override; + +public: + virtual PackedStringArray get_configuration_warnings() const override; + + void set_use_global_pose(bool p_use_global_pose); + bool is_using_global_pose() const; + void set_position_enabled(bool p_enabled); + bool is_position_enabled() const; + void set_rotation_enabled(bool p_enabled); + bool is_rotation_enabled() const; + void set_scale_enabled(bool p_enabled); + bool is_scale_enabled() const; + + void set_profile(Ref<SkeletonProfile> p_profile); + Ref<SkeletonProfile> get_profile() const; + + RetargetModifier3D(); + virtual ~RetargetModifier3D(); +}; + +#endif // RETARGET_MODIFIER_3D_H diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 89212503ca..273b68d277 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -1050,7 +1050,12 @@ void Skeleton3D::force_update_all_bone_transforms() { for (int i = 0; i < parentless_bones.size(); i++) { force_update_bone_children_transforms(parentless_bones[i]); } - rest_dirty = false; + if (rest_dirty) { + rest_dirty = false; + emit_signal(SNAME("rest_updated")); + } else { + rest_dirty = false; + } dirty = false; if (updating) { return; @@ -1260,6 +1265,7 @@ void Skeleton3D::_bind_methods() { ADD_GROUP("Modifier", "modifier_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "modifier_callback_mode_process", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_modifier_callback_mode_process", "get_modifier_callback_mode_process"); + ADD_SIGNAL(MethodInfo("rest_updated")); ADD_SIGNAL(MethodInfo("pose_updated")); ADD_SIGNAL(MethodInfo("skeleton_updated")); ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx"))); diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 2abf989560..7df102a301 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -224,6 +224,7 @@ public: // Skeleton creation API uint64_t get_version() const; int add_bone(const String &p_name); + void remove_bone(int p_bone); int find_bone(const String &p_name) const; String get_bone_name(int p_bone) const; void set_bone_name(int p_bone, const String &p_name); diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index 0b720d6710..81fea2d0ec 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -166,8 +166,8 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f double delta = p_started ? 0 : p_delta * speed; double next_pos = cd.pos + delta; - double start = get_section_start_time(); - double end = get_section_end_time(); + double start = cd.get_start_time(); + double end = cd.get_end_time(); Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE; @@ -391,7 +391,7 @@ void AnimationPlayer::play_section_with_markers_backwards(const StringName &p_na } void AnimationPlayer::play_section_backwards(const StringName &p_name, double p_start_time, double p_end_time, double p_custom_blend) { - play_section(p_name, p_start_time, p_end_time, -1, true); + play_section(p_name, p_start_time, p_end_time, p_custom_blend, -1, true); } void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) { @@ -497,8 +497,8 @@ void AnimationPlayer::play_section(const StringName &p_name, double p_start_time c.current.start_time = p_start_time; c.current.end_time = p_end_time; - double start = get_section_start_time(); - double end = get_section_end_time(); + double start = playback.current.get_start_time(); + double end = playback.current.get_end_time(); if (!end_reached) { playback_queue.clear(); @@ -662,8 +662,8 @@ void AnimationPlayer::seek_internal(double p_time, bool p_update, bool p_update_ } } - double start = get_section_start_time(); - double end = get_section_end_time(); + double start = playback.current.get_start_time(); + double end = playback.current.get_end_time(); // Clamp the seek position. p_time = CLAMP(p_time, start, end); @@ -727,7 +727,7 @@ void AnimationPlayer::set_section(double p_start_time, double p_end_time) { ERR_FAIL_COND_MSG(Animation::is_greater_or_equal_approx(p_start_time, 0) && Animation::is_greater_or_equal_approx(p_end_time, 0) && Animation::is_greater_or_equal_approx(p_start_time, p_end_time), vformat("Start time %f is greater than end time %f.", p_start_time, p_end_time)); playback.current.start_time = p_start_time; playback.current.end_time = p_end_time; - playback.current.pos = CLAMP(playback.current.pos, get_section_start_time(), get_section_end_time()); + playback.current.pos = CLAMP(playback.current.pos, playback.current.get_start_time(), playback.current.get_end_time()); } void AnimationPlayer::reset_section() { @@ -737,18 +737,12 @@ void AnimationPlayer::reset_section() { double AnimationPlayer::get_section_start_time() const { ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.start_time, "AnimationPlayer has no current animation."); - if (Animation::is_less_approx(playback.current.start_time, 0) || playback.current.start_time > playback.current.from->animation->get_length()) { - return 0; - } - return playback.current.start_time; + return playback.current.get_start_time(); } double AnimationPlayer::get_section_end_time() const { ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.end_time, "AnimationPlayer has no current animation."); - if (Animation::is_less_approx(playback.current.end_time, 0) || playback.current.end_time > playback.current.from->animation->get_length()) { - return playback.current.from->animation->get_length(); - } - return playback.current.end_time; + return playback.current.get_end_time(); } bool AnimationPlayer::has_section() const { @@ -779,7 +773,7 @@ void AnimationPlayer::_stop_internal(bool p_reset, bool p_keep_state) { _clear_caches(); Playback &c = playback; // c.blend.clear(); - double start = c.current.from ? get_section_start_time() : 0; + double start = c.current.from ? playback.current.get_start_time() : 0; if (p_reset) { c.blend.clear(); if (p_keep_state) { diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h index dd8ffd3286..c20978f13b 100644 --- a/scene/animation/animation_player.h +++ b/scene/animation/animation_player.h @@ -72,6 +72,18 @@ private: float speed_scale = 1.0; double start_time = 0.0; double end_time = 0.0; + double get_start_time() const { + if (from && (Animation::is_less_approx(start_time, 0) || Animation::is_greater_approx(start_time, from->animation->get_length()))) { + return 0; + } + return start_time; + } + double get_end_time() const { + if (from && (Animation::is_less_approx(end_time, 0) || Animation::is_greater_approx(end_time, from->animation->get_length()))) { + return from->animation->get_length(); + } + return end_time; + } }; struct Blend { diff --git a/scene/gui/color_mode.h b/scene/gui/color_mode.h index 10c1d7ab48..684e3ee9d5 100644 --- a/scene/gui/color_mode.h +++ b/scene/gui/color_mode.h @@ -35,8 +35,6 @@ #include "scene/gui/color_picker.h" -struct Color; - class ColorMode { public: ColorPicker *color_picker = nullptr; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index e6edb5e0bf..96234e5db8 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -32,21 +32,25 @@ #include "color_picker.h" -#include "core/input/input.h" #include "core/io/image.h" -#include "core/math/color.h" +#include "scene/gui/aspect_ratio_container.h" #include "scene/gui/color_mode.h" +#include "scene/gui/grid_container.h" +#include "scene/gui/label.h" +#include "scene/gui/line_edit.h" #include "scene/gui/margin_container.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/option_button.h" +#include "scene/gui/popup_menu.h" +#include "scene/gui/slider.h" +#include "scene/gui/spin_box.h" +#include "scene/gui/texture_rect.h" #include "scene/resources/image_texture.h" #include "scene/resources/style_box_flat.h" #include "scene/resources/style_box_texture.h" #include "scene/theme/theme_db.h" -#include "servers/display_server.h" #include "thirdparty/misc/ok_color_shader.h" -List<Color> ColorPicker::preset_cache; -List<Color> ColorPicker::recent_preset_cache; - void ColorPicker::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { @@ -161,10 +165,6 @@ void ColorPicker::_update_theme_item_cache() { theme_cache.base_scale = get_theme_default_base_scale(); } -Ref<Shader> ColorPicker::wheel_shader; -Ref<Shader> ColorPicker::circle_shader; -Ref<Shader> ColorPicker::circle_ok_color_shader; - void ColorPicker::init_shaders() { wheel_shader.instantiate(); wheel_shader->set_code(R"( diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 95aa94603b..ec7b22a64f 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -33,28 +33,24 @@ #ifndef COLOR_PICKER_H #define COLOR_PICKER_H -#include "scene/gui/aspect_ratio_container.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" -#include "scene/gui/control.h" -#include "scene/gui/grid_container.h" -#include "scene/gui/label.h" -#include "scene/gui/line_edit.h" -#include "scene/gui/menu_button.h" -#include "scene/gui/option_button.h" -#include "scene/gui/panel.h" #include "scene/gui/popup.h" -#include "scene/gui/separator.h" -#include "scene/gui/slider.h" -#include "scene/gui/spin_box.h" -#include "scene/gui/texture_rect.h" -#include "scene/resources/style_box_flat.h" +class AspectRatioContainer; class ColorMode; -class ColorModeRGB; -class ColorModeHSV; -class ColorModeRAW; -class ColorModeOKHSL; +class ColorPickerShape; +class GridContainer; +class HSlider; +class Label; +class LineEdit; +class MarginContainer; +class MenuButton; +class OptionButton; +class PopupMenu; +class SpinBox; +class StyleBoxFlat; +class TextureRect; class ColorPresetButton : public BaseButton { GDCLASS(ColorPresetButton, BaseButton); @@ -112,11 +108,11 @@ public: static const int SLIDER_COUNT = 4; private: - static Ref<Shader> wheel_shader; - static Ref<Shader> circle_shader; - static Ref<Shader> circle_ok_color_shader; - static List<Color> preset_cache; - static List<Color> recent_preset_cache; + static inline Ref<Shader> wheel_shader; + static inline Ref<Shader> circle_shader; + static inline Ref<Shader> circle_ok_color_shader; + static inline List<Color> preset_cache; + static inline List<Color> recent_preset_cache; #ifdef TOOLS_ENABLED Object *editor_settings = nullptr; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 819d467da7..8d4e06c7c1 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -552,7 +552,9 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { pending_select_all_on_focus = false; } - show_virtual_keyboard(); + if (editable) { + show_virtual_keyboard(); + } } queue_redraw(); diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 2ceba9276f..35c3376025 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -95,7 +95,7 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { return; } - ofs -= decr_size; + ofs -= decr_size + theme_cache.scroll_style->get_margin(orientation == VERTICAL ? SIDE_TOP : SIDE_LEFT); if (ofs < grabber_ofs) { if (scrolling) { @@ -153,7 +153,7 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { Ref<Texture2D> decr = theme_cache.decrement_icon; double decr_size = orientation == VERTICAL ? decr->get_height() : decr->get_width(); - ofs -= decr_size; + ofs -= decr_size + theme_cache.scroll_style->get_margin(orientation == VERTICAL ? SIDE_TOP : SIDE_LEFT); double diff = (ofs - drag.pos_at_click) / get_area_size(); @@ -250,8 +250,6 @@ void ScrollBar::_notification(int p_what) { incr = theme_cache.increment_icon; } - Ref<StyleBox> bg = has_focus() ? theme_cache.scroll_focus_style : theme_cache.scroll_style; - Ref<StyleBox> grabber; if (drag.active) { grabber = theme_cache.grabber_pressed_style; @@ -279,7 +277,11 @@ void ScrollBar::_notification(int p_what) { area.height -= incr->get_height() + decr->get_height(); } - bg->draw(ci, Rect2(ofs, area)); + if (has_focus()) { + theme_cache.scroll_focus_style->draw(ci, Rect2(ofs, area)); + } else { + theme_cache.scroll_style->draw(ci, Rect2(ofs, area)); + } if (orientation == HORIZONTAL) { ofs.width += area.width; @@ -294,11 +296,11 @@ void ScrollBar::_notification(int p_what) { grabber_rect.size.width = get_grabber_size(); grabber_rect.size.height = get_size().height; grabber_rect.position.y = 0; - grabber_rect.position.x = get_grabber_offset() + decr->get_width() + bg->get_margin(SIDE_LEFT); + grabber_rect.position.x = get_grabber_offset() + decr->get_width() + theme_cache.scroll_style->get_margin(SIDE_LEFT); } else { grabber_rect.size.width = get_size().width; grabber_rect.size.height = get_grabber_size(); - grabber_rect.position.y = get_grabber_offset() + decr->get_height() + bg->get_margin(SIDE_TOP); + grabber_rect.position.y = get_grabber_offset() + decr->get_height() + theme_cache.scroll_style->get_margin(SIDE_TOP); grabber_rect.position.x = 0; } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index d885b07d14..d0b749302f 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1607,7 +1607,9 @@ void TextEdit::_notification(int p_what) { draw_caret = true; } - _show_virtual_keyboard(); + if (editable) { + _show_virtual_keyboard(); + } } break; case NOTIFICATION_FOCUS_EXIT: { @@ -2010,7 +2012,9 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } } - _show_virtual_keyboard(); + if (editable) { + _show_virtual_keyboard(); + } } } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index f66d39dbad..1d5afab526 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -1261,6 +1261,10 @@ Ref<World2D> Viewport::get_world_2d() const { return world_2d; } +Transform2D Viewport::get_stretch_transform() const { + return stretch_transform; +} + Transform2D Viewport::get_final_transform() const { ERR_READ_THREAD_GUARD_V(Transform2D()); return stretch_transform * global_canvas_transform; @@ -4643,6 +4647,7 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_global_canvas_transform", "xform"), &Viewport::set_global_canvas_transform); ClassDB::bind_method(D_METHOD("get_global_canvas_transform"), &Viewport::get_global_canvas_transform); + ClassDB::bind_method(D_METHOD("get_stretch_transform"), &Viewport::get_stretch_transform); ClassDB::bind_method(D_METHOD("get_final_transform"), &Viewport::get_final_transform); ClassDB::bind_method(D_METHOD("get_screen_transform"), &Viewport::get_screen_transform); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 31fbefe841..644e55e966 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -518,6 +518,7 @@ public: void set_global_canvas_transform(const Transform2D &p_transform); Transform2D get_global_canvas_transform() const; + Transform2D get_stretch_transform() const; virtual Transform2D get_final_transform() const; void gui_set_root_order_dirty(); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 8767a59356..ae272d9a9f 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -279,6 +279,7 @@ #include "scene/3d/physics/vehicle_body_3d.h" #include "scene/3d/reflection_probe.h" #include "scene/3d/remote_transform_3d.h" +#include "scene/3d/retarget_modifier_3d.h" #include "scene/3d/skeleton_3d.h" #include "scene/3d/skeleton_ik_3d.h" #include "scene/3d/skeleton_modifier_3d.h" @@ -596,6 +597,7 @@ void register_scene_types() { GDREGISTER_CLASS(Marker3D); GDREGISTER_CLASS(RootMotionView); GDREGISTER_VIRTUAL_CLASS(SkeletonModifier3D); + GDREGISTER_CLASS(RetargetModifier3D); OS::get_singleton()->yield(); // may take time to init diff --git a/scene/resources/3d/box_shape_3d.cpp b/scene/resources/3d/box_shape_3d.cpp index ef604f67b9..e4c02894b2 100644 --- a/scene/resources/3d/box_shape_3d.cpp +++ b/scene/resources/3d/box_shape_3d.cpp @@ -31,6 +31,8 @@ /**************************************************************************/ #include "box_shape_3d.h" + +#include "scene/resources/3d/primitive_meshes.h" #include "servers/physics_server_3d.h" Vector<Vector3> BoxShape3D::get_debug_mesh_lines() const { @@ -49,6 +51,24 @@ Vector<Vector3> BoxShape3D::get_debug_mesh_lines() const { return lines; } +Ref<ArrayMesh> BoxShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Array box_array; + box_array.resize(RS::ARRAY_MAX); + BoxMesh::create_mesh_array(box_array, size); + + Vector<Color> colors; + const PackedVector3Array &verts = box_array[RS::ARRAY_VERTEX]; + const int32_t verts_size = verts.size(); + for (int i = 0; i < verts_size; i++) { + colors.append(p_modulate); + } + + Ref<ArrayMesh> box_mesh = memnew(ArrayMesh); + box_array[RS::ARRAY_COLOR] = colors; + box_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, box_array); + return box_mesh; +} + real_t BoxShape3D::get_enclosing_radius() const { return size.length() / 2; } diff --git a/scene/resources/3d/box_shape_3d.h b/scene/resources/3d/box_shape_3d.h index 96064b61a0..1649a1c38d 100644 --- a/scene/resources/3d/box_shape_3d.h +++ b/scene/resources/3d/box_shape_3d.h @@ -53,6 +53,7 @@ public: Vector3 get_size() const; virtual Vector<Vector3> get_debug_mesh_lines() const override; + virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; BoxShape3D(); diff --git a/scene/resources/3d/capsule_shape_3d.cpp b/scene/resources/3d/capsule_shape_3d.cpp index 6ad16121e7..4a37e88bd0 100644 --- a/scene/resources/3d/capsule_shape_3d.cpp +++ b/scene/resources/3d/capsule_shape_3d.cpp @@ -32,6 +32,7 @@ #include "capsule_shape_3d.h" +#include "scene/resources/3d/primitive_meshes.h" #include "servers/physics_server_3d.h" Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() const { @@ -69,6 +70,24 @@ Vector<Vector3> CapsuleShape3D::get_debug_mesh_lines() const { return points; } +Ref<ArrayMesh> CapsuleShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Array capsule_array; + capsule_array.resize(RS::ARRAY_MAX); + CapsuleMesh::create_mesh_array(capsule_array, radius, height, 32, 8); + + Vector<Color> colors; + const PackedVector3Array &verts = capsule_array[RS::ARRAY_VERTEX]; + const int32_t verts_size = verts.size(); + for (int i = 0; i < verts_size; i++) { + colors.append(p_modulate); + } + + Ref<ArrayMesh> capsule_mesh = memnew(ArrayMesh); + capsule_array[RS::ARRAY_COLOR] = colors; + capsule_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, capsule_array); + return capsule_mesh; +} + real_t CapsuleShape3D::get_enclosing_radius() const { return height * 0.5; } diff --git a/scene/resources/3d/capsule_shape_3d.h b/scene/resources/3d/capsule_shape_3d.h index 3ed834b01b..0b29292a1b 100644 --- a/scene/resources/3d/capsule_shape_3d.h +++ b/scene/resources/3d/capsule_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class CapsuleShape3D : public Shape3D { GDCLASS(CapsuleShape3D, Shape3D); float radius = 0.5; @@ -52,6 +54,7 @@ public: float get_height() const; virtual Vector<Vector3> get_debug_mesh_lines() const override; + virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; CapsuleShape3D(); diff --git a/scene/resources/3d/concave_polygon_shape_3d.cpp b/scene/resources/3d/concave_polygon_shape_3d.cpp index 2a553390e1..45cb99f698 100644 --- a/scene/resources/3d/concave_polygon_shape_3d.cpp +++ b/scene/resources/3d/concave_polygon_shape_3d.cpp @@ -32,6 +32,7 @@ #include "concave_polygon_shape_3d.h" +#include "scene/resources/mesh.h" #include "servers/physics_server_3d.h" Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const { @@ -61,6 +62,23 @@ Vector<Vector3> ConcavePolygonShape3D::get_debug_mesh_lines() const { return points; } +Ref<ArrayMesh> ConcavePolygonShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Vector<Color> colors; + + for (int i = 0; i < faces.size(); i++) { + colors.push_back(p_modulate); + } + + Ref<ArrayMesh> mesh = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[RS::ARRAY_VERTEX] = faces; + a[RS::ARRAY_COLOR] = colors; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + + return mesh; +} + real_t ConcavePolygonShape3D::get_enclosing_radius() const { Vector<Vector3> data = get_faces(); const Vector3 *read = data.ptr(); diff --git a/scene/resources/3d/concave_polygon_shape_3d.h b/scene/resources/3d/concave_polygon_shape_3d.h index 2bb69be43c..56654dc4c0 100644 --- a/scene/resources/3d/concave_polygon_shape_3d.h +++ b/scene/resources/3d/concave_polygon_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class ConcavePolygonShape3D : public Shape3D { GDCLASS(ConcavePolygonShape3D, Shape3D); @@ -74,6 +76,7 @@ public: bool is_backface_collision_enabled() const; virtual Vector<Vector3> get_debug_mesh_lines() const override; + virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; ConcavePolygonShape3D(); diff --git a/scene/resources/3d/convex_polygon_shape_3d.cpp b/scene/resources/3d/convex_polygon_shape_3d.cpp index 3e6db206ff..a9fb8dd015 100644 --- a/scene/resources/3d/convex_polygon_shape_3d.cpp +++ b/scene/resources/3d/convex_polygon_shape_3d.cpp @@ -32,6 +32,7 @@ #include "convex_polygon_shape_3d.h" #include "core/math/convex_hull.h" +#include "scene/resources/mesh.h" #include "servers/physics_server_3d.h" Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() const { @@ -55,6 +56,44 @@ Vector<Vector3> ConvexPolygonShape3D::get_debug_mesh_lines() const { return Vector<Vector3>(); } +Ref<ArrayMesh> ConvexPolygonShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + const Vector<Vector3> hull_points = get_points(); + + Vector<Vector3> verts; + Vector<Color> colors; + Vector<int> indices; + + if (hull_points.size() >= 3) { + Geometry3D::MeshData md; + Error err = ConvexHullComputer::convex_hull(hull_points, md); + if (err == OK) { + verts = md.vertices; + for (int i = 0; i < verts.size(); i++) { + colors.push_back(p_modulate); + } + for (const Geometry3D::MeshData::Face &face : md.faces) { + const int first_point = face.indices[0]; + const int indices_count = face.indices.size(); + for (int i = 1; i < indices_count - 1; i++) { + indices.push_back(first_point); + indices.push_back(face.indices[i]); + indices.push_back(face.indices[i + 1]); + } + } + } + } + + Ref<ArrayMesh> mesh = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[RS::ARRAY_VERTEX] = verts; + a[RS::ARRAY_COLOR] = colors; + a[RS::ARRAY_INDEX] = indices; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + + return mesh; +} + real_t ConvexPolygonShape3D::get_enclosing_radius() const { Vector<Vector3> data = get_points(); const Vector3 *read = data.ptr(); diff --git a/scene/resources/3d/convex_polygon_shape_3d.h b/scene/resources/3d/convex_polygon_shape_3d.h index 708f8652a5..15dd3b860b 100644 --- a/scene/resources/3d/convex_polygon_shape_3d.h +++ b/scene/resources/3d/convex_polygon_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class ConvexPolygonShape3D : public Shape3D { GDCLASS(ConvexPolygonShape3D, Shape3D); Vector<Vector3> points; @@ -49,6 +51,7 @@ public: Vector<Vector3> get_points() const; virtual Vector<Vector3> get_debug_mesh_lines() const override; + virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; ConvexPolygonShape3D(); diff --git a/scene/resources/3d/cylinder_shape_3d.cpp b/scene/resources/3d/cylinder_shape_3d.cpp index 164ab49c77..0f8747e326 100644 --- a/scene/resources/3d/cylinder_shape_3d.cpp +++ b/scene/resources/3d/cylinder_shape_3d.cpp @@ -32,6 +32,7 @@ #include "cylinder_shape_3d.h" +#include "scene/resources/3d/primitive_meshes.h" #include "servers/physics_server_3d.h" Vector<Vector3> CylinderShape3D::get_debug_mesh_lines() const { @@ -62,6 +63,24 @@ Vector<Vector3> CylinderShape3D::get_debug_mesh_lines() const { return points; } +Ref<ArrayMesh> CylinderShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Array cylinder_array; + cylinder_array.resize(RS::ARRAY_MAX); + CylinderMesh::create_mesh_array(cylinder_array, radius, radius, height, 32); + + Vector<Color> colors; + const PackedVector3Array &verts = cylinder_array[RS::ARRAY_VERTEX]; + const int32_t verts_size = verts.size(); + for (int i = 0; i < verts_size; i++) { + colors.append(p_modulate); + } + + Ref<ArrayMesh> cylinder_mesh = memnew(ArrayMesh); + cylinder_array[RS::ARRAY_COLOR] = colors; + cylinder_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, cylinder_array); + return cylinder_mesh; +} + real_t CylinderShape3D::get_enclosing_radius() const { return Vector2(radius, height * 0.5).length(); } diff --git a/scene/resources/3d/cylinder_shape_3d.h b/scene/resources/3d/cylinder_shape_3d.h index d4cf04a61b..08f31db496 100644 --- a/scene/resources/3d/cylinder_shape_3d.h +++ b/scene/resources/3d/cylinder_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class CylinderShape3D : public Shape3D { GDCLASS(CylinderShape3D, Shape3D); float radius = 0.5; @@ -51,6 +53,7 @@ public: float get_height() const; virtual Vector<Vector3> get_debug_mesh_lines() const override; + virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; CylinderShape3D(); diff --git a/scene/resources/3d/height_map_shape_3d.cpp b/scene/resources/3d/height_map_shape_3d.cpp index 7854f41690..205f54bbaf 100644 --- a/scene/resources/3d/height_map_shape_3d.cpp +++ b/scene/resources/3d/height_map_shape_3d.cpp @@ -33,6 +33,7 @@ #include "height_map_shape_3d.h" #include "core/io/image.h" +#include "scene/resources/mesh.h" #include "servers/physics_server_3d.h" Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const { @@ -84,6 +85,60 @@ Vector<Vector3> HeightMapShape3D::get_debug_mesh_lines() const { return points; } +Ref<ArrayMesh> HeightMapShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Vector<Vector3> verts; + Vector<Color> colors; + Vector<int> indices; + + // This will be slow for large maps... + + if ((map_width != 0) && (map_depth != 0)) { + Vector2 size = Vector2(map_width - 1, map_depth - 1) * -0.5; + const real_t *r = map_data.ptr(); + + for (int d = 0; d <= map_depth - 2; d++) { + const int this_row_offset = map_width * d; + const int next_row_offset = this_row_offset + map_width; + + for (int w = 0; w <= map_width - 2; w++) { + const float height_tl = r[next_row_offset + w]; + const float height_bl = r[this_row_offset + w]; + const float height_br = r[this_row_offset + w + 1]; + const float height_tr = r[next_row_offset + w + 1]; + + const int index_offset = verts.size(); + + verts.push_back(Vector3(size.x + w, height_tl, size.y + d + 1)); + verts.push_back(Vector3(size.x + w, height_bl, size.y + d)); + verts.push_back(Vector3(size.x + w + 1, height_br, size.y + d)); + verts.push_back(Vector3(size.x + w + 1, height_tr, size.y + d + 1)); + + colors.push_back(p_modulate); + colors.push_back(p_modulate); + colors.push_back(p_modulate); + colors.push_back(p_modulate); + + indices.push_back(index_offset); + indices.push_back(index_offset + 1); + indices.push_back(index_offset + 2); + indices.push_back(index_offset); + indices.push_back(index_offset + 2); + indices.push_back(index_offset + 3); + } + } + } + + Ref<ArrayMesh> mesh = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[RS::ARRAY_VERTEX] = verts; + a[RS::ARRAY_COLOR] = colors; + a[RS::ARRAY_INDEX] = indices; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + + return mesh; +} + real_t HeightMapShape3D::get_enclosing_radius() const { return Vector3(real_t(map_width), max_height - min_height, real_t(map_depth)).length(); } diff --git a/scene/resources/3d/height_map_shape_3d.h b/scene/resources/3d/height_map_shape_3d.h index 8ac9cd29bc..71933eac9e 100644 --- a/scene/resources/3d/height_map_shape_3d.h +++ b/scene/resources/3d/height_map_shape_3d.h @@ -35,6 +35,7 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; class Image; class HeightMapShape3D : public Shape3D { @@ -64,6 +65,7 @@ public: void update_map_data_from_image(const Ref<Image> &p_image, real_t p_height_min, real_t p_height_max); virtual Vector<Vector3> get_debug_mesh_lines() const override; + virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; HeightMapShape3D(); diff --git a/scene/resources/3d/mesh_library.cpp b/scene/resources/3d/mesh_library.cpp index f0823bedc6..0b490e6d55 100644 --- a/scene/resources/3d/mesh_library.cpp +++ b/scene/resources/3d/mesh_library.cpp @@ -49,6 +49,24 @@ bool MeshLibrary::_set(const StringName &p_name, const Variant &p_value) { set_item_mesh(idx, p_value); } else if (what == "mesh_transform") { set_item_mesh_transform(idx, p_value); + } else if (what == "mesh_cast_shadow") { + switch ((int)p_value) { + case 0: { + set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_OFF); + } break; + case 1: { + set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON); + } break; + case 2: { + set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_DOUBLE_SIDED); + } break; + case 3: { + set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_SHADOWS_ONLY); + } break; + default: { + set_item_mesh_cast_shadow(idx, RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON); + } break; + } } else if (what == "shape") { Vector<ShapeData> shapes; ShapeData sd; @@ -93,6 +111,8 @@ bool MeshLibrary::_get(const StringName &p_name, Variant &r_ret) const { r_ret = get_item_mesh(idx); } else if (what == "mesh_transform") { r_ret = get_item_mesh_transform(idx); + } else if (what == "mesh_cast_shadow") { + r_ret = (int)get_item_mesh_cast_shadow(idx); } else if (what == "shapes") { r_ret = _get_item_shapes(idx); } else if (what == "navigation_mesh") { @@ -122,6 +142,7 @@ void MeshLibrary::_get_property_list(List<PropertyInfo> *p_list) const { p_list->push_back(PropertyInfo(Variant::STRING, prop_name + PNAME("name"))); p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("mesh"), PROPERTY_HINT_RESOURCE_TYPE, "Mesh")); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("mesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); + p_list->push_back(PropertyInfo(Variant::INT, prop_name + PNAME("mesh_cast_shadow"), PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only")); p_list->push_back(PropertyInfo(Variant::ARRAY, prop_name + PNAME("shapes"))); p_list->push_back(PropertyInfo(Variant::OBJECT, prop_name + PNAME("navigation_mesh"), PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh")); p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prop_name + PNAME("navigation_mesh_transform"), PROPERTY_HINT_NONE, "suffix:m")); @@ -156,6 +177,12 @@ void MeshLibrary::set_item_mesh_transform(int p_item, const Transform3D &p_trans emit_changed(); } +void MeshLibrary::set_item_mesh_cast_shadow(int p_item, RS::ShadowCastingSetting p_shadow_casting_setting) { + ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); + item_map[p_item].mesh_cast_shadow = p_shadow_casting_setting; + emit_changed(); +} + void MeshLibrary::set_item_shapes(int p_item, const Vector<ShapeData> &p_shapes) { ERR_FAIL_COND_MSG(!item_map.has(p_item), "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); item_map[p_item].shapes = p_shapes; @@ -202,6 +229,11 @@ Transform3D MeshLibrary::get_item_mesh_transform(int p_item) const { return item_map[p_item].mesh_transform; } +RS::ShadowCastingSetting MeshLibrary::get_item_mesh_cast_shadow(int p_item) const { + ERR_FAIL_COND_V_MSG(!item_map.has(p_item), RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON, "Requested for nonexistent MeshLibrary item '" + itos(p_item) + "'."); + return item_map[p_item].mesh_cast_shadow; +} + Vector<MeshLibrary::ShapeData> MeshLibrary::get_item_shapes(int p_item) const { 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; @@ -330,6 +362,7 @@ void MeshLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_name", "id", "name"), &MeshLibrary::set_item_name); ClassDB::bind_method(D_METHOD("set_item_mesh", "id", "mesh"), &MeshLibrary::set_item_mesh); ClassDB::bind_method(D_METHOD("set_item_mesh_transform", "id", "mesh_transform"), &MeshLibrary::set_item_mesh_transform); + ClassDB::bind_method(D_METHOD("set_item_mesh_cast_shadow", "id", "shadow_casting_setting"), &MeshLibrary::set_item_mesh_cast_shadow); ClassDB::bind_method(D_METHOD("set_item_navigation_mesh", "id", "navigation_mesh"), &MeshLibrary::set_item_navigation_mesh); ClassDB::bind_method(D_METHOD("set_item_navigation_mesh_transform", "id", "navigation_mesh"), &MeshLibrary::set_item_navigation_mesh_transform); ClassDB::bind_method(D_METHOD("set_item_navigation_layers", "id", "navigation_layers"), &MeshLibrary::set_item_navigation_layers); @@ -338,6 +371,7 @@ void MeshLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_name", "id"), &MeshLibrary::get_item_name); ClassDB::bind_method(D_METHOD("get_item_mesh", "id"), &MeshLibrary::get_item_mesh); ClassDB::bind_method(D_METHOD("get_item_mesh_transform", "id"), &MeshLibrary::get_item_mesh_transform); + ClassDB::bind_method(D_METHOD("get_item_mesh_cast_shadow", "id"), &MeshLibrary::get_item_mesh_cast_shadow); ClassDB::bind_method(D_METHOD("get_item_navigation_mesh", "id"), &MeshLibrary::get_item_navigation_mesh); ClassDB::bind_method(D_METHOD("get_item_navigation_mesh_transform", "id"), &MeshLibrary::get_item_navigation_mesh_transform); ClassDB::bind_method(D_METHOD("get_item_navigation_layers", "id"), &MeshLibrary::get_item_navigation_layers); diff --git a/scene/resources/3d/mesh_library.h b/scene/resources/3d/mesh_library.h index e52fbb281f..726ed98a7a 100644 --- a/scene/resources/3d/mesh_library.h +++ b/scene/resources/3d/mesh_library.h @@ -37,6 +37,7 @@ #include "core/templates/rb_map.h" #include "scene/3d/navigation_region_3d.h" #include "scene/resources/mesh.h" +#include "servers/rendering_server.h" #include "shape_3d.h" class MeshLibrary : public Resource { @@ -52,6 +53,7 @@ public: String name; Ref<Mesh> mesh; Transform3D mesh_transform; + RS::ShadowCastingSetting mesh_cast_shadow = RS::ShadowCastingSetting::SHADOW_CASTING_SETTING_ON; Vector<ShapeData> shapes; Ref<Texture2D> preview; Ref<NavigationMesh> navigation_mesh; @@ -77,6 +79,7 @@ public: void set_item_name(int p_item, const String &p_name); void set_item_mesh(int p_item, const Ref<Mesh> &p_mesh); void set_item_mesh_transform(int p_item, const Transform3D &p_transform); + void set_item_mesh_cast_shadow(int p_item, RS::ShadowCastingSetting p_shadow_casting_setting); void set_item_navigation_mesh(int p_item, const Ref<NavigationMesh> &p_navigation_mesh); void set_item_navigation_mesh_transform(int p_item, const Transform3D &p_transform); void set_item_navigation_layers(int p_item, uint32_t p_navigation_layers); @@ -85,6 +88,7 @@ public: String get_item_name(int p_item) const; Ref<Mesh> get_item_mesh(int p_item) const; Transform3D get_item_mesh_transform(int p_item) const; + RS::ShadowCastingSetting get_item_mesh_cast_shadow(int p_item) const; Ref<NavigationMesh> get_item_navigation_mesh(int p_item) const; Transform3D get_item_navigation_mesh_transform(int p_item) const; uint32_t get_item_navigation_layers(int p_item) const; diff --git a/scene/resources/3d/separation_ray_shape_3d.cpp b/scene/resources/3d/separation_ray_shape_3d.cpp index d9aa636289..1f97d06b67 100644 --- a/scene/resources/3d/separation_ray_shape_3d.cpp +++ b/scene/resources/3d/separation_ray_shape_3d.cpp @@ -32,6 +32,7 @@ #include "separation_ray_shape_3d.h" +#include "scene/resources/mesh.h" #include "servers/physics_server_3d.h" Vector<Vector3> SeparationRayShape3D::get_debug_mesh_lines() const { @@ -43,6 +44,10 @@ Vector<Vector3> SeparationRayShape3D::get_debug_mesh_lines() const { return points; } +Ref<ArrayMesh> SeparationRayShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + return memnew(ArrayMesh); +} + real_t SeparationRayShape3D::get_enclosing_radius() const { return length; } diff --git a/scene/resources/3d/separation_ray_shape_3d.h b/scene/resources/3d/separation_ray_shape_3d.h index 5c7681fcbe..1aad61bdc4 100644 --- a/scene/resources/3d/separation_ray_shape_3d.h +++ b/scene/resources/3d/separation_ray_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class SeparationRayShape3D : public Shape3D { GDCLASS(SeparationRayShape3D, Shape3D); float length = 1.0; @@ -52,6 +54,7 @@ public: bool get_slide_on_slope() const; virtual Vector<Vector3> get_debug_mesh_lines() const override; + virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; SeparationRayShape3D(); diff --git a/scene/resources/3d/shape_3d.cpp b/scene/resources/3d/shape_3d.cpp index 04f1653d2e..78a9d66827 100644 --- a/scene/resources/3d/shape_3d.cpp +++ b/scene/resources/3d/shape_3d.cpp @@ -68,6 +68,34 @@ void Shape3D::set_margin(real_t p_margin) { PhysicsServer3D::get_singleton()->shape_set_margin(shape, margin); } +#ifdef DEBUG_ENABLED +void Shape3D::set_debug_color(const Color &p_color) { + if (p_color == debug_color) { + return; + } + + debug_color = p_color; + _update_shape(); +} + +Color Shape3D::get_debug_color() const { + return debug_color; +} + +void Shape3D::set_debug_fill(bool p_fill) { + if (p_fill == debug_fill) { + return; + } + + debug_fill = p_fill; + _update_shape(); +} + +bool Shape3D::get_debug_fill() const { + return debug_fill; +} +#endif // DEBUG_ENABLED + Ref<ArrayMesh> Shape3D::get_debug_mesh() { if (debug_mesh_cache.is_valid()) { return debug_mesh_cache; @@ -81,29 +109,57 @@ Ref<ArrayMesh> Shape3D::get_debug_mesh() { //make mesh Vector<Vector3> array; array.resize(lines.size()); - { - Vector3 *w = array.ptrw(); - for (int i = 0; i < lines.size(); i++) { - w[i] = lines[i]; - } + Vector3 *v = array.ptrw(); + + Vector<Color> arraycol; + arraycol.resize(lines.size()); + Color *c = arraycol.ptrw(); + + for (int i = 0; i < lines.size(); i++) { + v[i] = lines[i]; + c[i] = debug_color; } - Array arr; - arr.resize(Mesh::ARRAY_MAX); - arr[Mesh::ARRAY_VERTEX] = array; + Array lines_array; + lines_array.resize(Mesh::ARRAY_MAX); + lines_array[Mesh::ARRAY_VERTEX] = array; + lines_array[Mesh::ARRAY_COLOR] = arraycol; - SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop()); + Ref<StandardMaterial3D> material = get_debug_collision_material(); - debug_mesh_cache->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, arr); + debug_mesh_cache->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, lines_array); + debug_mesh_cache->surface_set_material(0, material); - if (st) { - debug_mesh_cache->surface_set_material(0, st->get_debug_collision_material()); + if (debug_fill) { + Array solid_array = get_debug_arraymesh_faces(debug_color * Color(1.0, 1.0, 1.0, 0.0625))->surface_get_arrays(0); + debug_mesh_cache->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, solid_array); + debug_mesh_cache->surface_set_material(1, material); } } return debug_mesh_cache; } +Ref<Material> Shape3D::get_debug_collision_material() { + if (collision_material.is_valid()) { + return collision_material; + } + + Ref<StandardMaterial3D> material = memnew(StandardMaterial3D); + material->set_albedo(Color(1.0, 1.0, 1.0)); + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1); + material->set_cull_mode(StandardMaterial3D::CULL_BACK); + material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); + material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + + collision_material = material; + + return collision_material; +} + void Shape3D::_update_shape() { emit_changed(); debug_mesh_cache.unref(); diff --git a/scene/resources/3d/shape_3d.h b/scene/resources/3d/shape_3d.h index 2adfd152f9..7b005c1fb9 100644 --- a/scene/resources/3d/shape_3d.h +++ b/scene/resources/3d/shape_3d.h @@ -36,6 +36,7 @@ #include "core/io/resource.h" class ArrayMesh; +class Material; class Shape3D : public Resource { GDCLASS(Shape3D, Resource); @@ -46,6 +47,10 @@ class Shape3D : public Resource { real_t margin = 0.04; Ref<ArrayMesh> debug_mesh_cache; + Ref<Material> collision_material; + + Color debug_color = Color(0.0, 0.0, 0.0, 0.0); + bool debug_fill = true; protected: static void _bind_methods(); @@ -53,6 +58,8 @@ protected: _FORCE_INLINE_ RID get_shape() const { return shape; } Shape3D(RID p_shape); + Ref<Material> get_debug_collision_material(); + virtual void _update_shape(); public: @@ -60,6 +67,7 @@ public: Ref<ArrayMesh> get_debug_mesh(); virtual Vector<Vector3> get_debug_mesh_lines() const = 0; // { return Vector<Vector3>(); } + virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const = 0; /// Returns the radius of a sphere that fully enclose this shape virtual real_t get_enclosing_radius() const = 0; @@ -71,6 +79,14 @@ public: real_t get_margin() const; void set_margin(real_t p_margin); +#ifdef DEBUG_ENABLED + void set_debug_color(const Color &p_color); + Color get_debug_color() const; + + void set_debug_fill(bool p_fill); + bool get_debug_fill() const; +#endif // DEBUG_ENABLED + Shape3D(); ~Shape3D(); }; diff --git a/scene/resources/3d/sphere_shape_3d.cpp b/scene/resources/3d/sphere_shape_3d.cpp index 59ed80fd3b..15806dd66b 100644 --- a/scene/resources/3d/sphere_shape_3d.cpp +++ b/scene/resources/3d/sphere_shape_3d.cpp @@ -32,6 +32,8 @@ #include "sphere_shape_3d.h" +#include "scene/resources/3d/primitive_meshes.h" +#include "scene/resources/material.h" #include "servers/physics_server_3d.h" Vector<Vector3> SphereShape3D::get_debug_mesh_lines() const { @@ -56,6 +58,24 @@ Vector<Vector3> SphereShape3D::get_debug_mesh_lines() const { return points; } +Ref<ArrayMesh> SphereShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Array sphere_array; + sphere_array.resize(RS::ARRAY_MAX); + SphereMesh::create_mesh_array(sphere_array, radius, radius * 2, 32); + + Vector<Color> colors; + const PackedVector3Array &verts = sphere_array[RS::ARRAY_VERTEX]; + const int32_t verts_size = verts.size(); + for (int i = 0; i < verts_size; i++) { + colors.append(p_modulate); + } + + Ref<ArrayMesh> sphere_mesh = memnew(ArrayMesh); + sphere_array[RS::ARRAY_COLOR] = colors; + sphere_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, sphere_array); + return sphere_mesh; +} + real_t SphereShape3D::get_enclosing_radius() const { return radius; } diff --git a/scene/resources/3d/sphere_shape_3d.h b/scene/resources/3d/sphere_shape_3d.h index 068b7f9ace..14b29f74cf 100644 --- a/scene/resources/3d/sphere_shape_3d.h +++ b/scene/resources/3d/sphere_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class SphereShape3D : public Shape3D { GDCLASS(SphereShape3D, Shape3D); float radius = 0.5f; @@ -49,6 +51,7 @@ public: float get_radius() const; virtual Vector<Vector3> get_debug_mesh_lines() const override; + virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override; SphereShape3D(); diff --git a/scene/resources/3d/world_boundary_shape_3d.cpp b/scene/resources/3d/world_boundary_shape_3d.cpp index 7cff587a20..1957b2c99e 100644 --- a/scene/resources/3d/world_boundary_shape_3d.cpp +++ b/scene/resources/3d/world_boundary_shape_3d.cpp @@ -32,6 +32,7 @@ #include "world_boundary_shape_3d.h" +#include "scene/resources/mesh.h" #include "servers/physics_server_3d.h" Vector<Vector3> WorldBoundaryShape3D::get_debug_mesh_lines() const { @@ -63,6 +64,53 @@ Vector<Vector3> WorldBoundaryShape3D::get_debug_mesh_lines() const { return points; } +Ref<ArrayMesh> WorldBoundaryShape3D::get_debug_arraymesh_faces(const Color &p_modulate) const { + Plane p = get_plane(); + + Vector3 n1 = p.get_any_perpendicular_normal(); + Vector3 n2 = p.normal.cross(n1).normalized(); + + Vector3 pface[4] = { + p.normal * p.d + n1 * 10.0 + n2 * 10.0, + p.normal * p.d + n1 * 10.0 + n2 * -10.0, + p.normal * p.d + n1 * -10.0 + n2 * -10.0, + p.normal * p.d + n1 * -10.0 + n2 * 10.0, + }; + + Vector<Vector3> points = { + pface[0], + pface[1], + pface[2], + pface[3], + }; + + Vector<Color> colors = { + p_modulate, + p_modulate, + p_modulate, + p_modulate, + }; + + Vector<int> indices = { + 0, + 1, + 2, + 0, + 2, + 3, + }; + + Ref<ArrayMesh> mesh = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[RS::ARRAY_VERTEX] = points; + a[RS::ARRAY_COLOR] = colors; + a[RS::ARRAY_INDEX] = indices; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + + return mesh; +} + void WorldBoundaryShape3D::_update_shape() { PhysicsServer3D::get_singleton()->shape_set_data(get_shape(), plane); Shape3D::_update_shape(); diff --git a/scene/resources/3d/world_boundary_shape_3d.h b/scene/resources/3d/world_boundary_shape_3d.h index cd7bc72a5e..ea70ba4dc4 100644 --- a/scene/resources/3d/world_boundary_shape_3d.h +++ b/scene/resources/3d/world_boundary_shape_3d.h @@ -35,6 +35,8 @@ #include "scene/resources/3d/shape_3d.h" +class ArrayMesh; + class WorldBoundaryShape3D : public Shape3D { GDCLASS(WorldBoundaryShape3D, Shape3D); Plane plane; @@ -48,6 +50,7 @@ public: const Plane &get_plane() const; virtual Vector<Vector3> get_debug_mesh_lines() const override; + virtual Ref<ArrayMesh> get_debug_arraymesh_faces(const Color &p_modulate) const override; virtual real_t get_enclosing_radius() const override { // Should be infinite? return 0; diff --git a/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp index fd9df49b4f..0fba9db429 100644 --- a/scene/resources/animation_library.cpp +++ b/scene/resources/animation_library.cpp @@ -127,6 +127,10 @@ void AnimationLibrary::get_animation_list(List<StringName> *p_animations) const } } +int AnimationLibrary::get_animation_list_size() const { + return animations.size(); +} + void AnimationLibrary::_set_data(const Dictionary &p_data) { for (KeyValue<StringName, Ref<Animation>> &K : animations) { K.value->disconnect_changed(callable_mp(this, &AnimationLibrary::_animation_changed)); @@ -168,6 +172,7 @@ void AnimationLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("has_animation", "name"), &AnimationLibrary::has_animation); ClassDB::bind_method(D_METHOD("get_animation", "name"), &AnimationLibrary::get_animation); ClassDB::bind_method(D_METHOD("get_animation_list"), &AnimationLibrary::_get_animation_list); + ClassDB::bind_method(D_METHOD("get_animation_list_size"), &AnimationLibrary::get_animation_list_size); ClassDB::bind_method(D_METHOD("_set_data", "data"), &AnimationLibrary::_set_data); ClassDB::bind_method(D_METHOD("_get_data"), &AnimationLibrary::_get_data); diff --git a/scene/resources/animation_library.h b/scene/resources/animation_library.h index bafc355ce4..032f25167d 100644 --- a/scene/resources/animation_library.h +++ b/scene/resources/animation_library.h @@ -63,6 +63,7 @@ public: bool has_animation(const StringName &p_name) const; Ref<Animation> get_animation(const StringName &p_name) const; void get_animation_list(List<StringName> *p_animations) const; + int get_animation_list_size() const; #ifdef TOOLS_ENABLED virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index b7f5c4c338..2b15a123b9 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -2739,13 +2739,13 @@ float BaseMaterial3D::get_grow() const { return grow; } -static Plane _get_texture_mask(BaseMaterial3D::TextureChannel p_channel) { - static const Plane masks[5] = { - Plane(1, 0, 0, 0), - Plane(0, 1, 0, 0), - Plane(0, 0, 1, 0), - Plane(0, 0, 0, 1), - Plane(0.3333333, 0.3333333, 0.3333333, 0), +static Vector4 _get_texture_mask(BaseMaterial3D::TextureChannel p_channel) { + static const Vector4 masks[5] = { + Vector4(1, 0, 0, 0), + Vector4(0, 1, 0, 0), + Vector4(0, 0, 1, 0), + Vector4(0, 0, 0, 1), + Vector4(0.3333333, 0.3333333, 0.3333333, 0), }; return masks[p_channel]; diff --git a/scene/resources/skeleton_profile.cpp b/scene/resources/skeleton_profile.cpp index 369d72f801..bc1c55c614 100644 --- a/scene/resources/skeleton_profile.cpp +++ b/scene/resources/skeleton_profile.cpp @@ -271,6 +271,14 @@ int SkeletonProfile::find_bone(const StringName &p_bone_name) const { return -1; } +PackedStringArray SkeletonProfile::get_bone_names() { + PackedStringArray s; + for (const SkeletonProfileBone &bone : bones) { + s.push_back(bone.bone_name); + } + return s; +} + StringName SkeletonProfile::get_bone_name(int p_bone_idx) const { ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName()); return bones[p_bone_idx].bone_name; diff --git a/scene/resources/skeleton_profile.h b/scene/resources/skeleton_profile.h index 6db0afaac5..8de514c7c1 100644 --- a/scene/resources/skeleton_profile.h +++ b/scene/resources/skeleton_profile.h @@ -99,6 +99,7 @@ public: int find_bone(const StringName &p_bone_name) const; + PackedStringArray get_bone_names(); StringName get_bone_name(int p_bone_idx) const; void set_bone_name(int p_bone_idx, const StringName &p_bone_name); |