summaryrefslogtreecommitdiffstats
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/mesh_instance_2d.cpp3
-rw-r--r--scene/3d/camera_3d.cpp7
-rw-r--r--scene/3d/look_at_modifier_3d.cpp76
-rw-r--r--scene/3d/look_at_modifier_3d.h8
-rw-r--r--scene/3d/node_3d.cpp4
-rw-r--r--scene/3d/physics/collision_shape_3d.cpp98
-rw-r--r--scene/3d/physics/collision_shape_3d.h22
-rw-r--r--scene/3d/retarget_modifier_3d.cpp443
-rw-r--r--scene/3d/retarget_modifier_3d.h112
-rw-r--r--scene/3d/skeleton_3d.cpp8
-rw-r--r--scene/3d/skeleton_3d.h1
-rw-r--r--scene/animation/animation_player.cpp28
-rw-r--r--scene/animation/animation_player.h12
-rw-r--r--scene/gui/color_mode.h2
-rw-r--r--scene/gui/color_picker.cpp20
-rw-r--r--scene/gui/color_picker.h40
-rw-r--r--scene/gui/line_edit.cpp4
-rw-r--r--scene/gui/scroll_bar.cpp16
-rw-r--r--scene/gui/text_edit.cpp8
-rw-r--r--scene/main/viewport.cpp5
-rw-r--r--scene/main/viewport.h1
-rw-r--r--scene/register_scene_types.cpp2
-rw-r--r--scene/resources/3d/box_shape_3d.cpp20
-rw-r--r--scene/resources/3d/box_shape_3d.h1
-rw-r--r--scene/resources/3d/capsule_shape_3d.cpp19
-rw-r--r--scene/resources/3d/capsule_shape_3d.h3
-rw-r--r--scene/resources/3d/concave_polygon_shape_3d.cpp18
-rw-r--r--scene/resources/3d/concave_polygon_shape_3d.h3
-rw-r--r--scene/resources/3d/convex_polygon_shape_3d.cpp39
-rw-r--r--scene/resources/3d/convex_polygon_shape_3d.h3
-rw-r--r--scene/resources/3d/cylinder_shape_3d.cpp19
-rw-r--r--scene/resources/3d/cylinder_shape_3d.h3
-rw-r--r--scene/resources/3d/height_map_shape_3d.cpp55
-rw-r--r--scene/resources/3d/height_map_shape_3d.h2
-rw-r--r--scene/resources/3d/mesh_library.cpp34
-rw-r--r--scene/resources/3d/mesh_library.h4
-rw-r--r--scene/resources/3d/separation_ray_shape_3d.cpp5
-rw-r--r--scene/resources/3d/separation_ray_shape_3d.h3
-rw-r--r--scene/resources/3d/shape_3d.cpp80
-rw-r--r--scene/resources/3d/shape_3d.h16
-rw-r--r--scene/resources/3d/sphere_shape_3d.cpp20
-rw-r--r--scene/resources/3d/sphere_shape_3d.h3
-rw-r--r--scene/resources/3d/world_boundary_shape_3d.cpp48
-rw-r--r--scene/resources/3d/world_boundary_shape_3d.h3
-rw-r--r--scene/resources/animation_library.cpp5
-rw-r--r--scene/resources/animation_library.h1
-rw-r--r--scene/resources/material.cpp14
-rw-r--r--scene/resources/skeleton_profile.cpp8
-rw-r--r--scene/resources/skeleton_profile.h1
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);