|
@@ -32,10 +32,11 @@
|
|
|
#include "skeleton_3d.compat.inc"
|
|
|
|
|
|
#include "core/variant/type_info.h"
|
|
|
-#include "scene/3d/physics/physical_bone_3d.h"
|
|
|
-#include "scene/3d/physics/physics_body_3d.h"
|
|
|
#include "scene/resources/surface_tool.h"
|
|
|
#include "scene/scene_string_names.h"
|
|
|
+#ifndef DISABLE_DEPRECATED
|
|
|
+#include "scene/3d/physical_bone_simulator_3d.h"
|
|
|
+#endif // _DISABLE_DEPRECATED
|
|
|
|
|
|
void SkinReference::_skin_changed() {
|
|
|
if (skeleton_node) {
|
|
@@ -70,6 +71,12 @@ SkinReference::~SkinReference() {
|
|
|
bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
|
|
|
String path = p_path;
|
|
|
|
|
|
+#ifndef DISABLE_DEPRECATED
|
|
|
+ if (path.begins_with("animate_physical_bones")) {
|
|
|
+ set_animate_physical_bones(p_value);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
if (!path.begins_with("bones/")) {
|
|
|
return false;
|
|
|
}
|
|
@@ -134,6 +141,12 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
|
|
|
bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
|
|
|
String path = p_path;
|
|
|
|
|
|
+#ifndef DISABLE_DEPRECATED
|
|
|
+ if (path.begins_with("animate_physical_bones")) {
|
|
|
+ r_ret = get_animate_physical_bones();
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
if (!path.begins_with("bones/")) {
|
|
|
return false;
|
|
|
}
|
|
@@ -251,26 +264,70 @@ void Skeleton3D::_update_process_order() {
|
|
|
}
|
|
|
|
|
|
process_order_dirty = false;
|
|
|
+
|
|
|
+ emit_signal("bone_list_changed");
|
|
|
}
|
|
|
|
|
|
+#ifndef DISABLE_DEPRECATED
|
|
|
+void Skeleton3D::setup_simulator() {
|
|
|
+ PhysicalBoneSimulator3D *sim = memnew(PhysicalBoneSimulator3D);
|
|
|
+ simulator = sim;
|
|
|
+ sim->is_compat = true;
|
|
|
+ sim->set_active(false); // Don't run unneeded process.
|
|
|
+ add_child(sim);
|
|
|
+}
|
|
|
+
|
|
|
+void Skeleton3D::remove_simulator() {
|
|
|
+ remove_child(simulator);
|
|
|
+ memdelete(simulator);
|
|
|
+}
|
|
|
+#endif // _DISABLE_DEPRECATED
|
|
|
+
|
|
|
void Skeleton3D::_notification(int p_what) {
|
|
|
switch (p_what) {
|
|
|
case NOTIFICATION_ENTER_TREE: {
|
|
|
- if (dirty) {
|
|
|
- notification(NOTIFICATION_UPDATE_SKELETON);
|
|
|
- }
|
|
|
+ _process_changed();
|
|
|
+ _make_modifiers_dirty();
|
|
|
+ force_update_all_dirty_bones();
|
|
|
+#ifndef DISABLE_DEPRECATED
|
|
|
+ setup_simulator();
|
|
|
+#endif // _DISABLE_DEPRECATED
|
|
|
+ } break;
|
|
|
+#ifndef DISABLE_DEPRECATED
|
|
|
+ case NOTIFICATION_EXIT_TREE: {
|
|
|
+ remove_simulator();
|
|
|
} break;
|
|
|
+#endif // _DISABLE_DEPRECATED
|
|
|
case NOTIFICATION_UPDATE_SKELETON: {
|
|
|
- RenderingServer *rs = RenderingServer::get_singleton();
|
|
|
- Bone *bonesptr = bones.ptrw();
|
|
|
+ // Update bone transforms to apply unprocessed poses.
|
|
|
+ force_update_all_dirty_bones();
|
|
|
+
|
|
|
+ updating = true;
|
|
|
|
|
|
+ Bone *bonesptr = bones.ptrw();
|
|
|
int len = bones.size();
|
|
|
- dirty = false;
|
|
|
|
|
|
- // Update bone transforms.
|
|
|
- force_update_all_bone_transforms();
|
|
|
+ // Process modifiers.
|
|
|
+ _find_modifiers();
|
|
|
+ LocalVector<Transform3D> current_bone_poses;
|
|
|
+ LocalVector<Vector3> current_pose_positions;
|
|
|
+ LocalVector<Quaternion> current_pose_rotations;
|
|
|
+ LocalVector<Vector3> current_pose_scales;
|
|
|
+ LocalVector<Transform3D> current_bone_global_poses;
|
|
|
+ if (!modifiers.is_empty()) {
|
|
|
+ // Store unmodified bone poses.
|
|
|
+ for (int i = 0; i < len; i++) {
|
|
|
+ current_bone_poses.push_back(bones[i].pose_cache);
|
|
|
+ current_pose_positions.push_back(bones[i].pose_position);
|
|
|
+ current_pose_rotations.push_back(bones[i].pose_rotation);
|
|
|
+ current_pose_scales.push_back(bones[i].pose_scale);
|
|
|
+ current_bone_global_poses.push_back(bones[i].global_pose);
|
|
|
+ }
|
|
|
+ _process_modifiers();
|
|
|
+ }
|
|
|
|
|
|
// Update skins.
|
|
|
+ RenderingServer *rs = RenderingServer::get_singleton();
|
|
|
for (SkinReference *E : skin_bindings) {
|
|
|
const Skin *skin = E->skin.operator->();
|
|
|
RID skeleton = E->skeleton;
|
|
@@ -322,74 +379,78 @@ void Skeleton3D::_notification(int p_what) {
|
|
|
for (uint32_t i = 0; i < bind_count; i++) {
|
|
|
uint32_t bone_index = E->skin_bone_indices_ptrs[i];
|
|
|
ERR_CONTINUE(bone_index >= (uint32_t)len);
|
|
|
- rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].pose_global * skin->get_bind_pose(i));
|
|
|
+ rs->skeleton_bone_set_transform(skeleton, i, bonesptr[bone_index].global_pose * skin->get_bind_pose(i));
|
|
|
}
|
|
|
}
|
|
|
- emit_signal(SceneStringNames::get_singleton()->pose_updated);
|
|
|
- } break;
|
|
|
|
|
|
-#ifndef _3D_DISABLED
|
|
|
- case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
|
|
- // This is active only if the skeleton animates the physical bones
|
|
|
- // and the state of the bone is not active.
|
|
|
- if (animate_physical_bones) {
|
|
|
- for (int i = 0; i < bones.size(); i += 1) {
|
|
|
- if (bones[i].physical_bone) {
|
|
|
- if (bones[i].physical_bone->is_simulating_physics() == false) {
|
|
|
- bones[i].physical_bone->reset_to_rest_position();
|
|
|
- }
|
|
|
- }
|
|
|
+ if (!modifiers.is_empty()) {
|
|
|
+ // Restore unmodified bone poses.
|
|
|
+ for (int i = 0; i < len; i++) {
|
|
|
+ bonesptr[i].pose_cache = current_bone_poses[i];
|
|
|
+ bonesptr[i].pose_position = current_pose_positions[i];
|
|
|
+ bonesptr[i].pose_rotation = current_pose_rotations[i];
|
|
|
+ bonesptr[i].pose_scale = current_pose_scales[i];
|
|
|
+ bonesptr[i].global_pose = current_bone_global_poses[i];
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ updating = false;
|
|
|
+ is_update_needed = false;
|
|
|
} break;
|
|
|
- case NOTIFICATION_READY: {
|
|
|
- if (Engine::get_singleton()->is_editor_hint()) {
|
|
|
- set_physics_process_internal(true);
|
|
|
+ case NOTIFICATION_INTERNAL_PROCESS:
|
|
|
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
|
|
+ _find_modifiers();
|
|
|
+ if (!modifiers.is_empty()) {
|
|
|
+ _update_deferred();
|
|
|
}
|
|
|
} break;
|
|
|
-#endif // _3D_DISABLED
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void Skeleton3D::clear_bones_global_pose_override() {
|
|
|
- for (int i = 0; i < bones.size(); i += 1) {
|
|
|
- bones.write[i].global_pose_override_amount = 0;
|
|
|
- bones.write[i].global_pose_override_reset = true;
|
|
|
+void Skeleton3D::set_modifier_callback_mode_process(Skeleton3D::ModifierCallbackModeProcess p_mode) {
|
|
|
+ if (modifier_callback_mode_process == p_mode) {
|
|
|
+ return;
|
|
|
}
|
|
|
- _make_dirty();
|
|
|
+ modifier_callback_mode_process = p_mode;
|
|
|
+ _process_changed();
|
|
|
}
|
|
|
|
|
|
-void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) {
|
|
|
- const int bone_size = bones.size();
|
|
|
- ERR_FAIL_INDEX(p_bone, bone_size);
|
|
|
- bones.write[p_bone].global_pose_override_amount = p_amount;
|
|
|
- bones.write[p_bone].global_pose_override = p_pose;
|
|
|
- bones.write[p_bone].global_pose_override_reset = !p_persistent;
|
|
|
- _make_dirty();
|
|
|
+Skeleton3D::ModifierCallbackModeProcess Skeleton3D::get_modifier_callback_mode_process() const {
|
|
|
+ return modifier_callback_mode_process;
|
|
|
}
|
|
|
|
|
|
-Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const {
|
|
|
- const int bone_size = bones.size();
|
|
|
- ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
|
|
|
- return bones[p_bone].global_pose_override;
|
|
|
+void Skeleton3D::_process_changed() {
|
|
|
+ if (modifier_callback_mode_process == MODIFIER_CALLBACK_MODE_PROCESS_IDLE) {
|
|
|
+ set_process_internal(true);
|
|
|
+ set_physics_process_internal(false);
|
|
|
+ } else if (modifier_callback_mode_process == MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS) {
|
|
|
+ set_process_internal(false);
|
|
|
+ set_physics_process_internal(true);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Skeleton3D::_make_modifiers_dirty() {
|
|
|
+ modifiers_dirty = true;
|
|
|
+ _update_deferred();
|
|
|
}
|
|
|
|
|
|
Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const {
|
|
|
const int bone_size = bones.size();
|
|
|
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
|
|
|
- if (dirty) {
|
|
|
- const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
|
|
|
- }
|
|
|
- return bones[p_bone].pose_global;
|
|
|
+ const_cast<Skeleton3D *>(this)->force_update_all_dirty_bones();
|
|
|
+ return bones[p_bone].global_pose;
|
|
|
}
|
|
|
|
|
|
-Transform3D Skeleton3D::get_bone_global_pose_no_override(int p_bone) const {
|
|
|
+void Skeleton3D::set_bone_global_pose(int p_bone, const Transform3D &p_pose) {
|
|
|
const int bone_size = bones.size();
|
|
|
- ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
|
|
|
- if (dirty) {
|
|
|
- const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
|
|
|
+ ERR_FAIL_INDEX(p_bone, bone_size);
|
|
|
+
|
|
|
+ Transform3D pt;
|
|
|
+ if (bones[p_bone].parent >= 0) {
|
|
|
+ pt = get_bone_global_pose(bones[p_bone].parent);
|
|
|
}
|
|
|
- return bones[p_bone].pose_global_no_override;
|
|
|
+ Transform3D t = pt.affine_inverse() * p_pose;
|
|
|
+ set_bone_pose(p_bone, t);
|
|
|
}
|
|
|
|
|
|
void Skeleton3D::set_motion_scale(float p_motion_scale) {
|
|
@@ -548,7 +609,7 @@ Transform3D Skeleton3D::get_bone_global_rest(int p_bone) const {
|
|
|
const int bone_size = bones.size();
|
|
|
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
|
|
|
if (rest_dirty) {
|
|
|
- const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
|
|
|
+ const_cast<Skeleton3D *>(this)->force_update_all_bone_transforms();
|
|
|
}
|
|
|
return bones[p_bone].global_rest;
|
|
|
}
|
|
@@ -588,6 +649,19 @@ void Skeleton3D::clear_bones() {
|
|
|
|
|
|
// Posing api
|
|
|
|
|
|
+void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) {
|
|
|
+ const int bone_size = bones.size();
|
|
|
+ ERR_FAIL_INDEX(p_bone, bone_size);
|
|
|
+
|
|
|
+ bones.write[p_bone].pose_position = p_pose.origin;
|
|
|
+ bones.write[p_bone].pose_rotation = p_pose.basis.get_rotation_quaternion();
|
|
|
+ bones.write[p_bone].pose_scale = p_pose.basis.get_scale();
|
|
|
+ bones.write[p_bone].pose_cache_dirty = true;
|
|
|
+ if (is_inside_tree()) {
|
|
|
+ _make_dirty();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void Skeleton3D::set_bone_pose_position(int p_bone, const Vector3 &p_position) {
|
|
|
const int bone_size = bones.size();
|
|
|
ERR_FAIL_INDEX(p_bone, bone_size);
|
|
@@ -654,7 +728,7 @@ void Skeleton3D::reset_bone_poses() {
|
|
|
Transform3D Skeleton3D::get_bone_pose(int p_bone) const {
|
|
|
const int bone_size = bones.size();
|
|
|
ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
|
|
|
- ((Skeleton3D *)this)->bones.write[p_bone].update_pose_cache();
|
|
|
+ const_cast<Skeleton3D *>(this)->bones.write[p_bone].update_pose_cache();
|
|
|
return bones[p_bone].pose_cache;
|
|
|
}
|
|
|
|
|
@@ -662,11 +736,15 @@ void Skeleton3D::_make_dirty() {
|
|
|
if (dirty) {
|
|
|
return;
|
|
|
}
|
|
|
+ dirty = true;
|
|
|
+ _update_deferred();
|
|
|
+}
|
|
|
|
|
|
- if (is_inside_tree()) {
|
|
|
- notify_deferred_thread_group(NOTIFICATION_UPDATE_SKELETON);
|
|
|
+void Skeleton3D::_update_deferred() {
|
|
|
+ if (!is_update_needed && !updating && is_inside_tree()) {
|
|
|
+ is_update_needed = true;
|
|
|
+ notify_deferred_thread_group(NOTIFICATION_UPDATE_SKELETON); // It must never be called more than once in a single frame.
|
|
|
}
|
|
|
- dirty = true;
|
|
|
}
|
|
|
|
|
|
void Skeleton3D::localize_rests() {
|
|
@@ -687,173 +765,6 @@ void Skeleton3D::localize_rests() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void Skeleton3D::set_animate_physical_bones(bool p_enabled) {
|
|
|
- animate_physical_bones = p_enabled;
|
|
|
-
|
|
|
- if (Engine::get_singleton()->is_editor_hint() == false) {
|
|
|
- bool sim = false;
|
|
|
- for (int i = 0; i < bones.size(); i += 1) {
|
|
|
- if (bones[i].physical_bone) {
|
|
|
- bones[i].physical_bone->reset_physics_simulation_state();
|
|
|
- if (bones[i].physical_bone->is_simulating_physics()) {
|
|
|
- sim = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- set_physics_process_internal(sim == false && p_enabled);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-bool Skeleton3D::get_animate_physical_bones() const {
|
|
|
- return animate_physical_bones;
|
|
|
-}
|
|
|
-
|
|
|
-void Skeleton3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone) {
|
|
|
- const int bone_size = bones.size();
|
|
|
- ERR_FAIL_INDEX(p_bone, bone_size);
|
|
|
- ERR_FAIL_COND(bones[p_bone].physical_bone);
|
|
|
- ERR_FAIL_NULL(p_physical_bone);
|
|
|
- bones.write[p_bone].physical_bone = p_physical_bone;
|
|
|
-
|
|
|
- _rebuild_physical_bones_cache();
|
|
|
-}
|
|
|
-
|
|
|
-void Skeleton3D::unbind_physical_bone_from_bone(int p_bone) {
|
|
|
- const int bone_size = bones.size();
|
|
|
- ERR_FAIL_INDEX(p_bone, bone_size);
|
|
|
- bones.write[p_bone].physical_bone = nullptr;
|
|
|
-
|
|
|
- _rebuild_physical_bones_cache();
|
|
|
-}
|
|
|
-
|
|
|
-PhysicalBone3D *Skeleton3D::get_physical_bone(int p_bone) {
|
|
|
- const int bone_size = bones.size();
|
|
|
- ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
|
|
|
-
|
|
|
- return bones[p_bone].physical_bone;
|
|
|
-}
|
|
|
-
|
|
|
-PhysicalBone3D *Skeleton3D::get_physical_bone_parent(int p_bone) {
|
|
|
- const int bone_size = bones.size();
|
|
|
- ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
|
|
|
-
|
|
|
- if (bones[p_bone].cache_parent_physical_bone) {
|
|
|
- return bones[p_bone].cache_parent_physical_bone;
|
|
|
- }
|
|
|
-
|
|
|
- return _get_physical_bone_parent(p_bone);
|
|
|
-}
|
|
|
-
|
|
|
-PhysicalBone3D *Skeleton3D::_get_physical_bone_parent(int p_bone) {
|
|
|
- const int bone_size = bones.size();
|
|
|
- ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr);
|
|
|
-
|
|
|
- const int parent_bone = bones[p_bone].parent;
|
|
|
- if (0 > parent_bone) {
|
|
|
- return nullptr;
|
|
|
- }
|
|
|
-
|
|
|
- PhysicalBone3D *pb = bones[parent_bone].physical_bone;
|
|
|
- if (pb) {
|
|
|
- return pb;
|
|
|
- } else {
|
|
|
- return get_physical_bone_parent(parent_bone);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void Skeleton3D::_rebuild_physical_bones_cache() {
|
|
|
- const int b_size = bones.size();
|
|
|
- for (int i = 0; i < b_size; ++i) {
|
|
|
- PhysicalBone3D *parent_pb = _get_physical_bone_parent(i);
|
|
|
- if (parent_pb != bones[i].cache_parent_physical_bone) {
|
|
|
- bones.write[i].cache_parent_physical_bone = parent_pb;
|
|
|
- if (bones[i].physical_bone) {
|
|
|
- bones[i].physical_bone->_on_bone_parent_changed();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void _pb_stop_simulation(Node *p_node) {
|
|
|
- for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
|
|
|
- _pb_stop_simulation(p_node->get_child(i));
|
|
|
- }
|
|
|
-
|
|
|
- PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
|
|
|
- if (pb) {
|
|
|
- pb->set_simulate_physics(false);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void Skeleton3D::physical_bones_stop_simulation() {
|
|
|
- _pb_stop_simulation(this);
|
|
|
- if (Engine::get_singleton()->is_editor_hint() == false && animate_physical_bones) {
|
|
|
- set_physics_process_internal(true);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void _pb_start_simulation(const Skeleton3D *p_skeleton, Node *p_node, const Vector<int> &p_sim_bones) {
|
|
|
- for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
|
|
|
- _pb_start_simulation(p_skeleton, p_node->get_child(i), p_sim_bones);
|
|
|
- }
|
|
|
-
|
|
|
- PhysicalBone3D *pb = Object::cast_to<PhysicalBone3D>(p_node);
|
|
|
- if (pb) {
|
|
|
- if (p_sim_bones.is_empty()) { // If no bones is specified, activate ragdoll on full body.
|
|
|
- pb->set_simulate_physics(true);
|
|
|
- } else {
|
|
|
- for (int i = p_sim_bones.size() - 1; 0 <= i; --i) {
|
|
|
- if (p_sim_bones[i] == pb->get_bone_id() || p_skeleton->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
|
|
|
- pb->set_simulate_physics(true);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void Skeleton3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) {
|
|
|
- set_physics_process_internal(false);
|
|
|
-
|
|
|
- Vector<int> sim_bones;
|
|
|
- if (p_bones.size() > 0) {
|
|
|
- sim_bones.resize(p_bones.size());
|
|
|
- int c = 0;
|
|
|
- for (int i = sim_bones.size() - 1; 0 <= i; --i) {
|
|
|
- int bone_id = find_bone(p_bones[i]);
|
|
|
- if (bone_id != -1) {
|
|
|
- sim_bones.write[c++] = bone_id;
|
|
|
- }
|
|
|
- }
|
|
|
- sim_bones.resize(c);
|
|
|
- }
|
|
|
-
|
|
|
- _pb_start_simulation(this, this, sim_bones);
|
|
|
-}
|
|
|
-
|
|
|
-void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) {
|
|
|
- for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
|
|
|
- _physical_bones_add_remove_collision_exception(p_add, p_node->get_child(i), p_exception);
|
|
|
- }
|
|
|
-
|
|
|
- CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_node);
|
|
|
- if (co) {
|
|
|
- if (p_add) {
|
|
|
- PhysicsServer3D::get_singleton()->body_add_collision_exception(co->get_rid(), p_exception);
|
|
|
- } else {
|
|
|
- PhysicsServer3D::get_singleton()->body_remove_collision_exception(co->get_rid(), p_exception);
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void Skeleton3D::physical_bones_add_collision_exception(RID p_exception) {
|
|
|
- _physical_bones_add_remove_collision_exception(true, this, p_exception);
|
|
|
-}
|
|
|
-
|
|
|
-void Skeleton3D::physical_bones_remove_collision_exception(RID p_exception) {
|
|
|
- _physical_bones_add_remove_collision_exception(false, this, p_exception);
|
|
|
-}
|
|
|
-
|
|
|
void Skeleton3D::_skin_changed() {
|
|
|
_make_dirty();
|
|
|
}
|
|
@@ -927,18 +838,23 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {
|
|
|
}
|
|
|
|
|
|
void Skeleton3D::force_update_all_dirty_bones() {
|
|
|
- if (dirty) {
|
|
|
- const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
|
|
|
+ if (!dirty) {
|
|
|
+ return;
|
|
|
}
|
|
|
+ force_update_all_bone_transforms();
|
|
|
}
|
|
|
|
|
|
void Skeleton3D::force_update_all_bone_transforms() {
|
|
|
_update_process_order();
|
|
|
-
|
|
|
for (int i = 0; i < parentless_bones.size(); i++) {
|
|
|
force_update_bone_children_transforms(parentless_bones[i]);
|
|
|
}
|
|
|
rest_dirty = false;
|
|
|
+ dirty = false;
|
|
|
+ if (updating) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ emit_signal(SceneStringNames::get_singleton()->pose_updated);
|
|
|
}
|
|
|
|
|
|
void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
|
|
@@ -961,32 +877,43 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
|
|
|
Transform3D pose = b.pose_cache;
|
|
|
|
|
|
if (b.parent >= 0) {
|
|
|
- b.pose_global = bonesptr[b.parent].pose_global * pose;
|
|
|
- b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * pose;
|
|
|
+ b.global_pose = bonesptr[b.parent].global_pose * pose;
|
|
|
} else {
|
|
|
- b.pose_global = pose;
|
|
|
- b.pose_global_no_override = pose;
|
|
|
+ b.global_pose = pose;
|
|
|
}
|
|
|
} else {
|
|
|
if (b.parent >= 0) {
|
|
|
- b.pose_global = bonesptr[b.parent].pose_global * b.rest;
|
|
|
- b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * b.rest;
|
|
|
+ b.global_pose = bonesptr[b.parent].global_pose * b.rest;
|
|
|
} else {
|
|
|
- b.pose_global = b.rest;
|
|
|
- b.pose_global_no_override = b.rest;
|
|
|
+ b.global_pose = b.rest;
|
|
|
}
|
|
|
}
|
|
|
if (rest_dirty) {
|
|
|
b.global_rest = b.parent >= 0 ? bonesptr[b.parent].global_rest * b.rest : b.rest;
|
|
|
}
|
|
|
|
|
|
+#ifndef DISABLE_DEPRECATED
|
|
|
+ if (bone_enabled) {
|
|
|
+ Transform3D pose = b.pose_cache;
|
|
|
+ if (b.parent >= 0) {
|
|
|
+ b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * pose;
|
|
|
+ } else {
|
|
|
+ b.pose_global_no_override = pose;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (b.parent >= 0) {
|
|
|
+ b.pose_global_no_override = bonesptr[b.parent].pose_global_no_override * b.rest;
|
|
|
+ } else {
|
|
|
+ b.pose_global_no_override = b.rest;
|
|
|
+ }
|
|
|
+ }
|
|
|
if (b.global_pose_override_amount >= CMP_EPSILON) {
|
|
|
- b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
|
|
|
+ b.global_pose = b.global_pose.interpolate_with(b.global_pose_override, b.global_pose_override_amount);
|
|
|
}
|
|
|
-
|
|
|
if (b.global_pose_override_reset) {
|
|
|
b.global_pose_override_amount = 0.0;
|
|
|
}
|
|
|
+#endif // _DISABLE_DEPRECATED
|
|
|
|
|
|
// Add the bone's children to the list of bones to be processed.
|
|
|
int child_bone_size = b.child_bones.size();
|
|
@@ -998,6 +925,72 @@ void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void Skeleton3D::_find_modifiers() {
|
|
|
+ if (!modifiers_dirty) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ modifiers.clear();
|
|
|
+ for (int i = 0; i < get_child_count(); i++) {
|
|
|
+ SkeletonModifier3D *c = Object::cast_to<SkeletonModifier3D>(get_child(i));
|
|
|
+ if (c) {
|
|
|
+ modifiers.push_back(c->get_instance_id());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ modifiers_dirty = false;
|
|
|
+}
|
|
|
+
|
|
|
+void Skeleton3D::_process_modifiers() {
|
|
|
+ for (const ObjectID &oid : modifiers) {
|
|
|
+ Object *t_obj = ObjectDB::get_instance(oid);
|
|
|
+ if (!t_obj) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ SkeletonModifier3D *mod = cast_to<SkeletonModifier3D>(t_obj);
|
|
|
+ if (!mod) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ real_t influence = mod->get_influence();
|
|
|
+ if (influence < 1.0) {
|
|
|
+ LocalVector<Transform3D> old_poses;
|
|
|
+ for (int i = 0; i < get_bone_count(); i++) {
|
|
|
+ old_poses.push_back(get_bone_pose(i));
|
|
|
+ }
|
|
|
+ mod->process_modification();
|
|
|
+ LocalVector<Transform3D> new_poses;
|
|
|
+ for (int i = 0; i < get_bone_count(); i++) {
|
|
|
+ new_poses.push_back(get_bone_pose(i));
|
|
|
+ }
|
|
|
+ for (int i = 0; i < get_bone_count(); i++) {
|
|
|
+ if (old_poses[i] == new_poses[i]) {
|
|
|
+ continue; // Avoid unneeded calculation.
|
|
|
+ }
|
|
|
+ set_bone_pose(i, old_poses[i].interpolate_with(new_poses[i], influence));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ mod->process_modification();
|
|
|
+ }
|
|
|
+ force_update_all_dirty_bones();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Skeleton3D::add_child_notify(Node *p_child) {
|
|
|
+ if (Object::cast_to<SkeletonModifier3D>(p_child)) {
|
|
|
+ _make_modifiers_dirty();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Skeleton3D::move_child_notify(Node *p_child) {
|
|
|
+ if (Object::cast_to<SkeletonModifier3D>(p_child)) {
|
|
|
+ _make_modifiers_dirty();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void Skeleton3D::remove_child_notify(Node *p_child) {
|
|
|
+ if (Object::cast_to<SkeletonModifier3D>(p_child)) {
|
|
|
+ _make_modifiers_dirty();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void Skeleton3D::_bind_methods() {
|
|
|
ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone);
|
|
|
ClassDB::bind_method(D_METHOD("find_bone", "name"), &Skeleton3D::find_bone);
|
|
@@ -1028,6 +1021,7 @@ void Skeleton3D::_bind_methods() {
|
|
|
ClassDB::bind_method(D_METHOD("clear_bones"), &Skeleton3D::clear_bones);
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose);
|
|
|
+ ClassDB::bind_method(D_METHOD("set_bone_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_pose);
|
|
|
ClassDB::bind_method(D_METHOD("set_bone_pose_position", "bone_idx", "position"), &Skeleton3D::set_bone_pose_position);
|
|
|
ClassDB::bind_method(D_METHOD("set_bone_pose_rotation", "bone_idx", "rotation"), &Skeleton3D::set_bone_pose_rotation);
|
|
|
ClassDB::bind_method(D_METHOD("set_bone_pose_scale", "bone_idx", "scale"), &Skeleton3D::set_bone_pose_scale);
|
|
@@ -1042,11 +1036,8 @@ void Skeleton3D::_bind_methods() {
|
|
|
ClassDB::bind_method(D_METHOD("is_bone_enabled", "bone_idx"), &Skeleton3D::is_bone_enabled);
|
|
|
ClassDB::bind_method(D_METHOD("set_bone_enabled", "bone_idx", "enabled"), &Skeleton3D::set_bone_enabled, DEFVAL(true));
|
|
|
|
|
|
- ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override);
|
|
|
- ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false));
|
|
|
- ClassDB::bind_method(D_METHOD("get_bone_global_pose_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_override);
|
|
|
ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton3D::get_bone_global_pose);
|
|
|
- ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override);
|
|
|
+ ClassDB::bind_method(D_METHOD("set_bone_global_pose", "bone_idx", "pose"), &Skeleton3D::set_bone_global_pose);
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms);
|
|
|
ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms);
|
|
@@ -1057,27 +1048,126 @@ void Skeleton3D::_bind_methods() {
|
|
|
ClassDB::bind_method(D_METHOD("set_show_rest_only", "enabled"), &Skeleton3D::set_show_rest_only);
|
|
|
ClassDB::bind_method(D_METHOD("is_show_rest_only"), &Skeleton3D::is_show_rest_only);
|
|
|
|
|
|
- ClassDB::bind_method(D_METHOD("set_animate_physical_bones", "enabled"), &Skeleton3D::set_animate_physical_bones);
|
|
|
- ClassDB::bind_method(D_METHOD("get_animate_physical_bones"), &Skeleton3D::get_animate_physical_bones);
|
|
|
-
|
|
|
- ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton3D::physical_bones_stop_simulation);
|
|
|
- ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton3D::physical_bones_start_simulation_on, DEFVAL(Array()));
|
|
|
- ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton3D::physical_bones_add_collision_exception);
|
|
|
- ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton3D::physical_bones_remove_collision_exception);
|
|
|
+ ClassDB::bind_method(D_METHOD("set_modifier_callback_mode_process", "mode"), &Skeleton3D::set_modifier_callback_mode_process);
|
|
|
+ ClassDB::bind_method(D_METHOD("get_modifier_callback_mode_process"), &Skeleton3D::get_modifier_callback_mode_process);
|
|
|
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "motion_scale", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater"), "set_motion_scale", "get_motion_scale");
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_rest_only"), "set_show_rest_only", "is_show_rest_only");
|
|
|
-#ifndef _3D_DISABLED
|
|
|
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones");
|
|
|
-#endif // _3D_DISABLED
|
|
|
+
|
|
|
+ ADD_GROUP("Modifier", "modifier_");
|
|
|
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "modifier_reset_on_save", PROPERTY_HINT_NONE, ""), "set_modifier_reset_on_save_enabled", "is_modifier_reset_on_save_enabled");
|
|
|
+ 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("pose_updated"));
|
|
|
ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx")));
|
|
|
ADD_SIGNAL(MethodInfo("bone_enabled_changed", PropertyInfo(Variant::INT, "bone_idx")));
|
|
|
+ ADD_SIGNAL(MethodInfo("bone_list_changed"));
|
|
|
ADD_SIGNAL(MethodInfo("show_rest_only_changed"));
|
|
|
|
|
|
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
|
|
|
+ BIND_ENUM_CONSTANT(MODIFIER_CALLBACK_MODE_PROCESS_PHYSICS);
|
|
|
+ BIND_ENUM_CONSTANT(MODIFIER_CALLBACK_MODE_PROCESS_IDLE);
|
|
|
+
|
|
|
+#ifndef DISABLE_DEPRECATED
|
|
|
+ ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override);
|
|
|
+ ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false));
|
|
|
+ ClassDB::bind_method(D_METHOD("get_bone_global_pose_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_override);
|
|
|
+ ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override);
|
|
|
+
|
|
|
+ ClassDB::bind_method(D_METHOD("set_animate_physical_bones", "enabled"), &Skeleton3D::set_animate_physical_bones);
|
|
|
+ ClassDB::bind_method(D_METHOD("get_animate_physical_bones"), &Skeleton3D::get_animate_physical_bones);
|
|
|
+ ClassDB::bind_method(D_METHOD("physical_bones_stop_simulation"), &Skeleton3D::physical_bones_stop_simulation);
|
|
|
+ ClassDB::bind_method(D_METHOD("physical_bones_start_simulation", "bones"), &Skeleton3D::physical_bones_start_simulation_on, DEFVAL(Array()));
|
|
|
+ ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton3D::physical_bones_add_collision_exception);
|
|
|
+ ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton3D::physical_bones_remove_collision_exception);
|
|
|
+#endif // _DISABLE_DEPRECATED
|
|
|
+}
|
|
|
+
|
|
|
+#ifndef DISABLE_DEPRECATED
|
|
|
+void Skeleton3D::clear_bones_global_pose_override() {
|
|
|
+ for (int i = 0; i < bones.size(); i += 1) {
|
|
|
+ bones.write[i].global_pose_override_amount = 0;
|
|
|
+ bones.write[i].global_pose_override_reset = true;
|
|
|
+ }
|
|
|
+ _make_dirty();
|
|
|
+}
|
|
|
+
|
|
|
+void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) {
|
|
|
+ const int bone_size = bones.size();
|
|
|
+ ERR_FAIL_INDEX(p_bone, bone_size);
|
|
|
+ bones.write[p_bone].global_pose_override_amount = p_amount;
|
|
|
+ bones.write[p_bone].global_pose_override = p_pose;
|
|
|
+ bones.write[p_bone].global_pose_override_reset = !p_persistent;
|
|
|
+ _make_dirty();
|
|
|
+}
|
|
|
+
|
|
|
+Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const {
|
|
|
+ const int bone_size = bones.size();
|
|
|
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
|
|
|
+ return bones[p_bone].global_pose_override;
|
|
|
+}
|
|
|
+
|
|
|
+Transform3D Skeleton3D::get_bone_global_pose_no_override(int p_bone) const {
|
|
|
+ const int bone_size = bones.size();
|
|
|
+ ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D());
|
|
|
+ const_cast<Skeleton3D *>(this)->force_update_all_dirty_bones();
|
|
|
+ return bones[p_bone].pose_global_no_override;
|
|
|
+}
|
|
|
+
|
|
|
+Node *Skeleton3D::get_simulator() {
|
|
|
+ return simulator;
|
|
|
+}
|
|
|
+
|
|
|
+void Skeleton3D::set_animate_physical_bones(bool p_enabled) {
|
|
|
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
|
|
|
+ if (!sim) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sim->set_active(p_enabled);
|
|
|
+}
|
|
|
+
|
|
|
+bool Skeleton3D::get_animate_physical_bones() const {
|
|
|
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
|
|
|
+ if (!sim) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return sim->is_active();
|
|
|
+}
|
|
|
+
|
|
|
+void Skeleton3D::physical_bones_stop_simulation() {
|
|
|
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
|
|
|
+ if (!sim) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sim->physical_bones_stop_simulation();
|
|
|
+ sim->set_active(false);
|
|
|
+}
|
|
|
+
|
|
|
+void Skeleton3D::physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones) {
|
|
|
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
|
|
|
+ if (!sim) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sim->set_active(true);
|
|
|
+ sim->physical_bones_start_simulation_on(p_bones);
|
|
|
+}
|
|
|
+
|
|
|
+void Skeleton3D::physical_bones_add_collision_exception(RID p_exception) {
|
|
|
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
|
|
|
+ if (!sim) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sim->physical_bones_add_collision_exception(p_exception);
|
|
|
+}
|
|
|
+
|
|
|
+void Skeleton3D::physical_bones_remove_collision_exception(RID p_exception) {
|
|
|
+ PhysicalBoneSimulator3D *sim = cast_to<PhysicalBoneSimulator3D>(simulator);
|
|
|
+ if (!sim) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sim->physical_bones_remove_collision_exception(p_exception);
|
|
|
}
|
|
|
+#endif // _DISABLE_DEPRECATED
|
|
|
|
|
|
Skeleton3D::Skeleton3D() {
|
|
|
}
|