diff options
Diffstat (limited to 'scene/animation')
-rw-r--r-- | scene/animation/animation_blend_space_1d.cpp | 3 | ||||
-rw-r--r-- | scene/animation/animation_blend_space_1d.h | 1 | ||||
-rw-r--r-- | scene/animation/animation_blend_space_2d.cpp | 15 | ||||
-rw-r--r-- | scene/animation/animation_blend_space_2d.h | 1 | ||||
-rw-r--r-- | scene/animation/animation_blend_tree.cpp | 4 | ||||
-rw-r--r-- | scene/animation/animation_cache.cpp | 12 | ||||
-rw-r--r-- | scene/animation/animation_node_state_machine.cpp | 10 | ||||
-rw-r--r-- | scene/animation/animation_player.cpp | 18 | ||||
-rw-r--r-- | scene/animation/animation_tree.cpp | 21 | ||||
-rw-r--r-- | scene/animation/animation_tree_player.cpp | 12 | ||||
-rw-r--r-- | scene/animation/skeleton_ik.cpp | 551 | ||||
-rw-r--r-- | scene/animation/skeleton_ik.h | 212 | ||||
-rw-r--r-- | scene/animation/tween.cpp | 67 | ||||
-rw-r--r-- | scene/animation/tween.h | 4 |
14 files changed, 833 insertions, 98 deletions
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index d3d2870c3f..1bc9fa4b12 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -4,10 +4,9 @@ void AnimationNodeBlendSpace1D::set_tree(AnimationTree *p_player) { AnimationRootNode::set_tree(p_player); - for(int i=0;i<blend_points_used;i++) { + for (int i = 0; i < blend_points_used; i++) { blend_points[i].node->set_tree(p_player); } - } void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const { diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index 774894ef4b..d1ed4c6a1f 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -34,7 +34,6 @@ protected: static void _bind_methods(); public: - virtual void set_tree(AnimationTree *p_player); void add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index = -1); diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 82db647124..3c93a0c8ec 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -4,12 +4,11 @@ void AnimationNodeBlendSpace2D::set_tree(AnimationTree *p_player) { AnimationRootNode::set_tree(p_player); - for(int i=0;i<blend_points_used;i++) { + for (int i = 0; i < blend_points_used; i++) { blend_points[i].node->set_tree(p_player); } } - void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index) { ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); ERR_FAIL_COND(p_node.is_null()); @@ -25,7 +24,7 @@ void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_ for (int i = 0; i < triangles.size(); i++) { for (int j = 0; j < 3; j++) { if (triangles[i].points[j] >= p_at_index) { - triangles[i].points[j]++; + triangles.write[i].points[j]++; } } } @@ -82,7 +81,7 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) { erase = true; break; } else if (triangles[i].points[j] > p_point) { - triangles[i].points[j]--; + triangles.write[i].points[j]--; } } if (erase) { @@ -265,9 +264,9 @@ Vector<int> AnimationNodeBlendSpace2D::_get_triangles() const { t.resize(triangles.size() * 3); for (int i = 0; i < triangles.size(); i++) { - t[i * 3 + 0] = triangles[i].points[0]; - t[i * 3 + 1] = triangles[i].points[1]; - t[i * 3 + 2] = triangles[i].points[2]; + t.write[i * 3 + 0] = triangles[i].points[0]; + t.write[i * 3 + 1] = triangles[i].points[1]; + t.write[i * 3 + 2] = triangles[i].points[2]; } return t; } @@ -285,7 +284,7 @@ void AnimationNodeBlendSpace2D::_update_triangles() { Vector<Vector2> points; points.resize(blend_points_used); for (int i = 0; i < blend_points_used; i++) { - points[i] = blend_points[i].position; + points.write[i] = blend_points[i].position; } Vector<Delaunay2D::Triangle> triangles = Delaunay2D::triangulate(points); diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h index 4778299df1..74d20b6013 100644 --- a/scene/animation/animation_blend_space_2d.h +++ b/scene/animation/animation_blend_space_2d.h @@ -47,7 +47,6 @@ protected: static void _bind_methods(); public: - virtual void set_tree(AnimationTree *p_player); void add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index = -1); diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 6dcd5ca8ea..65904410d3 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -288,8 +288,8 @@ void AnimationNodeOneShot::_bind_methods() { ADD_GROUP("", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); - BIND_CONSTANT(MIX_MODE_BLEND) - BIND_CONSTANT(MIX_MODE_ADD) + BIND_ENUM_CONSTANT(MIX_MODE_BLEND) + BIND_ENUM_CONSTANT(MIX_MODE_ADD) } AnimationNodeOneShot::AnimationNodeOneShot() { diff --git a/scene/animation/animation_cache.cpp b/scene/animation/animation_cache.cpp index 949a0be3bc..756907c41c 100644 --- a/scene/animation/animation_cache.cpp +++ b/scene/animation/animation_cache.cpp @@ -43,7 +43,7 @@ void AnimationCache::_node_exit_tree(Node *p_node) { if (path_cache[i].node != p_node) continue; - path_cache[i].valid = false; //invalidate path cache + path_cache.write[i].valid = false; //invalidate path cache } } @@ -196,7 +196,7 @@ void AnimationCache::set_track_transform(int p_idx, const Transform &p_transform ERR_FAIL_COND(!cache_valid); ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache[p_idx]; + Path &p = path_cache.write[p_idx]; if (!p.valid) return; @@ -217,7 +217,7 @@ void AnimationCache::set_track_value(int p_idx, const Variant &p_value) { ERR_FAIL_COND(!cache_valid); ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache[p_idx]; + Path &p = path_cache.write[p_idx]; if (!p.valid) return; @@ -232,7 +232,7 @@ void AnimationCache::call_track(int p_idx, const StringName &p_method, const Var ERR_FAIL_COND(!cache_valid); ERR_FAIL_INDEX(p_idx, path_cache.size()); - Path &p = path_cache[p_idx]; + Path &p = path_cache.write[p_idx]; if (!p.valid) return; @@ -297,11 +297,11 @@ void AnimationCache::set_all(float p_time, float p_delta) { call_track(i, name, NULL, 0, err); } else { - Vector<Variant *> argptrs; + Vector<const Variant *> argptrs; argptrs.resize(args.size()); for (int j = 0; j < args.size(); j++) { - argptrs[j] = &args[j]; + argptrs.write[j] = &args.write[j]; } call_track(i, name, (const Variant **)&argptrs[0], args.size(), err); diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index c5ad980806..f478112a36 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -71,9 +71,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); - BIND_CONSTANT(SWITCH_MODE_IMMEDIATE); - BIND_CONSTANT(SWITCH_MODE_SYNC); - BIND_CONSTANT(SWITCH_MODE_AT_END); + BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE); + BIND_ENUM_CONSTANT(SWITCH_MODE_SYNC); + BIND_ENUM_CONSTANT(SWITCH_MODE_AT_END); } AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { @@ -169,11 +169,11 @@ void AnimationNodeStateMachine::rename_node(const StringName &p_name, const Stri for (int i = 0; i < transitions.size(); i++) { if (transitions[i].from == p_name) { - transitions[i].from = p_new_name; + transitions.write[i].from = p_new_name; } if (transitions[i].to == p_name) { - transitions[i].to = p_new_name; + transitions.write[i].to = p_new_name; } } diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp index eac2c8d0c1..2782354432 100644 --- a/scene/animation/animation_player.cpp +++ b/scene/animation/animation_player.cpp @@ -233,7 +233,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) { for (int i = 0; i < a->get_track_count(); i++) { - p_anim->node_cache[i] = NULL; + p_anim->node_cache.write[i] = NULL; RES resource; Vector<StringName> leftover_path; Node *child = parent->get_node_and_resource(a->track_get_path(i), resource, leftover_path); @@ -265,12 +265,12 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) { if (node_cache_map.has(key)) { - p_anim->node_cache[i] = &node_cache_map[key]; + p_anim->node_cache.write[i] = &node_cache_map[key]; } else { node_cache_map[key] = TrackNodeCache(); - p_anim->node_cache[i] = &node_cache_map[key]; + p_anim->node_cache.write[i] = &node_cache_map[key]; p_anim->node_cache[i]->path = a->track_get_path(i); p_anim->node_cache[i]->node = child; p_anim->node_cache[i]->resource = resource; @@ -331,11 +331,7 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) { if (!p_anim->node_cache[i]->bezier_anim.has(a->track_get_path(i).get_concatenated_subnames())) { TrackNodeCache::BezierAnim ba; - String path = leftover_path[leftover_path.size() - 1]; - Vector<String> index = path.split("."); - for (int j = 0; j < index.size(); j++) { - ba.bezier_property.push_back(index[j]); - } + ba.bezier_property = leftover_path; ba.object = resource.is_valid() ? (Object *)resource.ptr() : (Object *)child; ba.owner = p_anim->node_cache[i]; @@ -549,6 +545,12 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, float int s = params.size(); ERR_CONTINUE(s > VARIANT_ARG_MAX); +#ifdef DEBUG_ENABLED + if (!nc->node->has_method(method)) { + ERR_PRINTS("Invalid method call '" + method + "'. '" + a->get_name() + "' at node '" + get_path() + "'."); + } +#endif + if (can_call) { MessageQueue::get_singleton()->push_call( nc->node, diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 83ec9f819b..de9f82dadc 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -78,9 +78,9 @@ float AnimationNode::blend_input(int p_input, float p_time, bool p_seek, float p return 0; } - inputs[p_input].last_pass = state->last_pass; + inputs.write[p_input].last_pass = state->last_pass; - return _blend_node(node, p_time, p_seek, p_blend, p_filter, p_optimize, &inputs[p_input].activity); + return _blend_node(node, p_time, p_seek, p_blend, p_filter, p_optimize, &inputs.write[p_input].activity); } float AnimationNode::blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { @@ -221,7 +221,7 @@ StringName AnimationNode::get_input_connection(int p_input) { void AnimationNode::set_input_connection(int p_input, const StringName &p_connection) { ERR_FAIL_INDEX(p_input, inputs.size()); - inputs[p_input].connected_to = p_connection; + inputs.write[p_input].connected_to = p_connection; } String AnimationNode::get_caption() const { @@ -248,7 +248,7 @@ void AnimationNode::add_input(const String &p_name) { void AnimationNode::set_input_name(int p_input, const String &p_name) { ERR_FAIL_INDEX(p_input, inputs.size()); ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1); - inputs[p_input].name = p_name; + inputs.write[p_input].name = p_name; emit_changed(); } @@ -877,7 +877,7 @@ void AnimationTree::_process_graph(float p_delta) { continue; t->loc = t->loc.linear_interpolate(loc, blend); - if (t->rot_blend_accum==0) { + if (t->rot_blend_accum == 0) { t->rot = rot; t->rot_blend_accum = blend; } else { @@ -1052,7 +1052,7 @@ void AnimationTree::_process_graph(float p_delta) { float len = t->start > time ? (a->get_length() - t->start) + time : time - t->start; if (len > t->len) { - stop=true; + stop = true; } } @@ -1065,7 +1065,7 @@ void AnimationTree::_process_graph(float p_delta) { } } - float db = Math::linear2db(MAX(blend,0.00001)); + float db = Math::linear2db(MAX(blend, 0.00001)); if (t->object->has_method("set_unit_db")) { t->object->call("set_unit_db", db); } else { @@ -1310,16 +1310,17 @@ void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_root_motion_transform"), &AnimationTree::get_root_motion_transform); - - ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationTree::_node_removed); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_tree_root", "get_tree_root"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player",PROPERTY_HINT_NODE_PATH_VALID_TYPES,"AnimationPlayer"), "set_animation_player", "get_animation_player"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_mode", "get_process_mode"); ADD_GROUP("Root Motion", "root_motion_"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "root_motion_track"), "set_root_motion_track", "get_root_motion_track"); + + BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS); + BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE); } AnimationTree::AnimationTree() { diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp index 026215508b..179f5d9698 100644 --- a/scene/animation/animation_tree_player.cpp +++ b/scene/animation/animation_tree_player.cpp @@ -1152,7 +1152,7 @@ void AnimationTreePlayer::transition_node_set_input_auto_advance(const StringNam GET_NODE(NODE_TRANSITION, TransitionNode); ERR_FAIL_INDEX(p_input, n->input_data.size()); - n->input_data[p_input].auto_advance = p_auto_advance; + n->input_data.write[p_input].auto_advance = p_auto_advance; } void AnimationTreePlayer::transition_node_set_xfade_time(const StringName &p_node, float p_time) { @@ -1365,7 +1365,7 @@ void AnimationTreePlayer::remove_node(const StringName &p_node) { for (int i = 0; i < nb->inputs.size(); i++) { if (nb->inputs[i].node == p_node) - nb->inputs[i].node = StringName(); + nb->inputs.write[i].node = StringName(); } } @@ -1426,11 +1426,11 @@ Error AnimationTreePlayer::connect_nodes(const StringName &p_src_node, const Str for (int i = 0; i < nb->inputs.size(); i++) { if (nb->inputs[i].node == p_src_node) - nb->inputs[i].node = StringName(); + nb->inputs.write[i].node = StringName(); } } - dst->inputs[p_dst_input].node = p_src_node; + dst->inputs.write[p_dst_input].node = p_src_node; _clear_cycle_test(); @@ -1463,7 +1463,7 @@ void AnimationTreePlayer::disconnect_nodes(const StringName &p_node, int p_input NodeBase *dst = node_map[p_node]; ERR_FAIL_INDEX(p_input, dst->inputs.size()); - dst->inputs[p_input].node = StringName(); + dst->inputs.write[p_input].node = StringName(); last_error = CONNECT_INCOMPLETE; dirty_caches = true; } @@ -1703,7 +1703,7 @@ Error AnimationTreePlayer::node_rename(const StringName &p_node, const StringNam for (int i = 0; i < nb->inputs.size(); i++) { if (nb->inputs[i].node == p_node) { - nb->inputs[i].node = p_new_name; + nb->inputs.write[i].node = p_new_name; } } } diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp new file mode 100644 index 0000000000..4991cedfab --- /dev/null +++ b/scene/animation/skeleton_ik.cpp @@ -0,0 +1,551 @@ +/*************************************************************************/ +/* skeleton_ik.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +/** + * @author AndreaCatania + */ + +#include "skeleton_ik.h" + +FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) { + for (int i = childs.size() - 1; 0 <= i; --i) { + if (p_bone_id == childs[i].bone) { + return &childs.write[i]; + } + } + return NULL; +} + +FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::add_child(const BoneId p_bone_id) { + const int infant_child_id = childs.size(); + childs.resize(infant_child_id + 1); + childs.write[infant_child_id].bone = p_bone_id; + childs.write[infant_child_id].parent_item = this; + return &childs.write[infant_child_id]; +} + +/// Build a chain that starts from the root to tip +void FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain) { + + ERR_FAIL_COND(-1 == p_task->root_bone); + + Chain &chain(p_task->chain); + + chain.tips.resize(p_task->end_effectors.size()); + chain.chain_root.bone = p_task->root_bone; + chain.chain_root.initial_transform = p_task->skeleton->get_bone_global_pose(chain.chain_root.bone); + chain.chain_root.current_pos = chain.chain_root.initial_transform.origin; + chain.chain_root.pb = p_task->skeleton->get_physical_bone(chain.chain_root.bone); + chain.middle_chain_item = NULL; + + // Holds all IDs that are composing a single chain in reverse order + Vector<BoneId> chain_ids; + // This is used to know the chain size + int sub_chain_size; + // Resize only one time in order to fit all joints for performance reason + chain_ids.resize(p_task->skeleton->get_bone_count()); + + for (int x = p_task->end_effectors.size() - 1; 0 <= x; --x) { + + const EndEffector *ee(&p_task->end_effectors[x]); + ERR_FAIL_COND(p_task->root_bone >= ee->tip_bone); + ERR_FAIL_INDEX(ee->tip_bone, p_task->skeleton->get_bone_count()); + + sub_chain_size = 0; + // Picks all IDs that composing a single chain in reverse order (except the root) + BoneId chain_sub_tip(ee->tip_bone); + while (chain_sub_tip > p_task->root_bone) { + + chain_ids.write[sub_chain_size++] = chain_sub_tip; + chain_sub_tip = p_task->skeleton->get_bone_parent(chain_sub_tip); + } + + BoneId middle_chain_item_id = (((float)sub_chain_size) * 0.5); + + // Build chain by reading chain ids in reverse order + // For each chain item id will be created a ChainItem if doesn't exists + ChainItem *sub_chain(&chain.chain_root); + for (int i = sub_chain_size - 1; 0 <= i; --i) { + + ChainItem *child_ci(sub_chain->find_child(chain_ids[i])); + if (!child_ci) { + + child_ci = sub_chain->add_child(chain_ids[i]); + + child_ci->pb = p_task->skeleton->get_physical_bone(child_ci->bone); + + child_ci->initial_transform = p_task->skeleton->get_bone_global_pose(child_ci->bone); + child_ci->current_pos = child_ci->initial_transform.origin; + + if (child_ci->parent_item) { + child_ci->length = (child_ci->current_pos - child_ci->parent_item->current_pos).length(); + } + } + + sub_chain = child_ci; + + if (middle_chain_item_id == i) { + chain.middle_chain_item = child_ci; + } + } + + if (!middle_chain_item_id) + chain.middle_chain_item = NULL; + + // Initialize current tip + chain.tips.write[x].chain_item = sub_chain; + chain.tips.write[x].end_effector = ee; + + if (p_force_simple_chain) { + // NOTE: + // This is an "hack" that force to create only one tip per chain since the solver of multi tip (end effector) + // is not yet created. + // Remove this code when this is done + break; + } + } +} + +void FabrikInverseKinematic::update_chain(const Skeleton *p_sk, ChainItem *p_chain_item) { + + if (!p_chain_item) + return; + + p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone); + p_chain_item->current_pos = p_chain_item->initial_transform.origin; + + for (int i = p_chain_item->childs.size() - 1; 0 <= i; --i) { + update_chain(p_sk, &p_chain_item->childs.write[i]); + } +} + +void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet) { + + real_t distance_to_goal(1e4); + real_t previous_distance_to_goal(0); + int can_solve(p_task->max_iterations); + while (distance_to_goal > p_task->min_distance && Math::abs(previous_distance_to_goal - distance_to_goal) > 0.005 && can_solve) { + previous_distance_to_goal = distance_to_goal; + --can_solve; + + solve_simple_backwards(p_task->chain, p_solve_magnet); + solve_simple_forwards(p_task->chain, p_solve_magnet); + + distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length(); + } +} + +void FabrikInverseKinematic::solve_simple_backwards(Chain &r_chain, bool p_solve_magnet) { + + if (p_solve_magnet && !r_chain.middle_chain_item) { + return; + } + + Vector3 goal; + ChainItem *sub_chain_tip; + if (p_solve_magnet) { + goal = r_chain.magnet_position; + sub_chain_tip = r_chain.middle_chain_item; + } else { + goal = r_chain.tips[0].end_effector->goal_transform.origin; + sub_chain_tip = r_chain.tips[0].chain_item; + } + + while (sub_chain_tip) { + sub_chain_tip->current_pos = goal; + + if (sub_chain_tip->parent_item) { + // Not yet in the chain root + // So calculate next goal location + + const Vector3 look_parent((sub_chain_tip->parent_item->current_pos - sub_chain_tip->current_pos).normalized()); + goal = sub_chain_tip->current_pos + (look_parent * sub_chain_tip->length); + + // [TODO] Constraints goes here + } + + sub_chain_tip = sub_chain_tip->parent_item; + } +} + +void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_magnet) { + + if (p_solve_magnet && !r_chain.middle_chain_item) { + return; + } + + ChainItem *sub_chain_root(&r_chain.chain_root); + Vector3 origin(r_chain.chain_root.initial_transform.origin); + + while (sub_chain_root) { // Reach the tip + sub_chain_root->current_pos = origin; + + if (!sub_chain_root->childs.empty()) { + + ChainItem &child(sub_chain_root->childs.write[0]); + + // Is not tip + // So calculate next origin location + + // Look child + sub_chain_root->current_ori = (child.current_pos - sub_chain_root->current_pos).normalized(); + origin = sub_chain_root->current_pos + (sub_chain_root->current_ori * child.length); + + // [TODO] Constraints goes here + + if (p_solve_magnet && sub_chain_root == r_chain.middle_chain_item) { + // In case of magnet solving this is the tip + sub_chain_root = NULL; + } else { + sub_chain_root = &child; + } + } else { + + // Is tip + sub_chain_root = NULL; + } + } +} + +FabrikInverseKinematic::Task *FabrikInverseKinematic::create_simple_task(Skeleton *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform) { + + FabrikInverseKinematic::EndEffector ee; + ee.tip_bone = tip_bone; + + Task *task(memnew(Task)); + task->skeleton = p_sk; + task->root_bone = root_bone; + task->end_effectors.push_back(ee); + task->goal_global_transform = goal_transform; + + build_chain(task); + + return task; +} + +void FabrikInverseKinematic::free_task(Task *p_task) { + if (p_task) + memdelete(p_task); +} + +void FabrikInverseKinematic::set_goal(Task *p_task, const Transform &p_goal) { + p_task->goal_global_transform = p_goal; +} + +void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta) { + + if (blending_delta >= 0.99f) { + // Update the end_effector (local transform) without blending + p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform; + } else { + + // End effector in local transform + const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose(p_task->end_effectors.write[0].tip_bone)); + + // Update the end_effector (local transform) by blending with current pose + p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta); + } +} + +void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool p_use_magnet, const Vector3 &p_magnet_position) { + + if (blending_delta <= 0.01f) { + return; // Skip solving + } + + make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse().scaled(p_task->skeleton->get_global_transform().get_basis().get_scale()), blending_delta); + + update_chain(p_task->skeleton, &p_task->chain.chain_root); + + if (p_use_magnet && p_task->chain.middle_chain_item) { + p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.linear_interpolate(p_magnet_position, blending_delta); + solve_simple(p_task, true); + } + solve_simple(p_task, false); + + // Assign new bone position. + ChainItem *ci(&p_task->chain.chain_root); + while (ci) { + Transform new_bone_pose(ci->initial_transform); + new_bone_pose.origin = ci->current_pos; + + if (!ci->childs.empty()) { + + /// Rotate basis + const Vector3 initial_ori((ci->childs[0].initial_transform.origin - ci->initial_transform.origin).normalized()); + const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized()); + + if (rot_axis[0] != 0 && rot_axis[1] != 0 && rot_axis[2] != 0) { + const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1))); + new_bone_pose.basis.rotate(rot_axis, rot_angle); + } + } else { + // Set target orientation to tip + new_bone_pose.basis = p_task->chain.tips[0].end_effector->goal_transform.basis; + } + + p_task->skeleton->set_bone_global_pose(ci->bone, new_bone_pose); + + if (!ci->childs.empty()) + ci = &ci->childs.write[0]; + else + ci = NULL; + } +} + +void SkeletonIK::_validate_property(PropertyInfo &property) const { + + if (property.name == "root_bone" || property.name == "tip_bone") { + + if (skeleton) { + + String names; + for (int i = 0; i < skeleton->get_bone_count(); i++) { + if (i > 0) + names += ","; + names += skeleton->get_bone_name(i); + } + + property.hint = PROPERTY_HINT_ENUM; + property.hint_string = names; + } else { + + property.hint = PROPERTY_HINT_NONE; + property.hint_string = ""; + } + } +} + +void SkeletonIK::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_root_bone", "root_bone"), &SkeletonIK::set_root_bone); + ClassDB::bind_method(D_METHOD("get_root_bone"), &SkeletonIK::get_root_bone); + + ClassDB::bind_method(D_METHOD("set_tip_bone", "tip_bone"), &SkeletonIK::set_tip_bone); + ClassDB::bind_method(D_METHOD("get_tip_bone"), &SkeletonIK::get_tip_bone); + + ClassDB::bind_method(D_METHOD("set_interpolation", "interpolation"), &SkeletonIK::set_interpolation); + ClassDB::bind_method(D_METHOD("get_interpolation"), &SkeletonIK::get_interpolation); + + ClassDB::bind_method(D_METHOD("set_target_transform", "target"), &SkeletonIK::set_target_transform); + ClassDB::bind_method(D_METHOD("get_target_transform"), &SkeletonIK::get_target_transform); + + ClassDB::bind_method(D_METHOD("set_target_node", "node"), &SkeletonIK::set_target_node); + ClassDB::bind_method(D_METHOD("get_target_node"), &SkeletonIK::get_target_node); + + ClassDB::bind_method(D_METHOD("set_use_magnet", "use"), &SkeletonIK::set_use_magnet); + ClassDB::bind_method(D_METHOD("is_using_magnet"), &SkeletonIK::is_using_magnet); + + ClassDB::bind_method(D_METHOD("set_magnet_position", "local_position"), &SkeletonIK::set_magnet_position); + ClassDB::bind_method(D_METHOD("get_magnet_position"), &SkeletonIK::get_magnet_position); + + ClassDB::bind_method(D_METHOD("get_parent_skeleton"), &SkeletonIK::get_parent_skeleton); + ClassDB::bind_method(D_METHOD("is_running"), &SkeletonIK::is_running); + + ClassDB::bind_method(D_METHOD("set_min_distance", "min_distance"), &SkeletonIK::set_min_distance); + ClassDB::bind_method(D_METHOD("get_min_distance"), &SkeletonIK::get_min_distance); + + ClassDB::bind_method(D_METHOD("set_max_iterations", "iterations"), &SkeletonIK::set_max_iterations); + ClassDB::bind_method(D_METHOD("get_max_iterations"), &SkeletonIK::get_max_iterations); + + ClassDB::bind_method(D_METHOD("start", "one_time"), &SkeletonIK::start, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("stop"), &SkeletonIK::stop); + + ADD_PROPERTY(PropertyInfo(Variant::STRING, "root_bone"), "set_root_bone", "get_root_bone"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "tip_bone"), "set_tip_bone", "get_tip_bone"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "target"), "set_target_transform", "get_target_transform"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "magnet"), "set_magnet_position", "get_magnet_position"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "target_node"), "set_target_node", "get_target_node"); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_distance"), "set_min_distance", "get_min_distance"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_iterations"), "set_max_iterations", "get_max_iterations"); +} + +void SkeletonIK::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + skeleton = Object::cast_to<Skeleton>(get_parent()); + reload_chain(); + } break; + case NOTIFICATION_INTERNAL_PROCESS: { + + if (target_node_override) + reload_goal(); + + _solve_chain(); + + } break; + case NOTIFICATION_EXIT_TREE: { + reload_chain(); + } break; + } +} + +SkeletonIK::SkeletonIK() : + Node(), + interpolation(1), + skeleton(NULL), + target_node_override(NULL), + use_magnet(false), + min_distance(0.01), + max_iterations(10), + task(NULL) { + + set_process_priority(1); +} + +SkeletonIK::~SkeletonIK() { + FabrikInverseKinematic::free_task(task); + task = NULL; +} + +void SkeletonIK::set_root_bone(const StringName &p_root_bone) { + root_bone = p_root_bone; + reload_chain(); +} + +StringName SkeletonIK::get_root_bone() const { + return root_bone; +} + +void SkeletonIK::set_tip_bone(const StringName &p_tip_bone) { + tip_bone = p_tip_bone; + reload_chain(); +} + +StringName SkeletonIK::get_tip_bone() const { + return tip_bone; +} + +void SkeletonIK::set_interpolation(real_t p_interpolation) { + interpolation = p_interpolation; +} + +real_t SkeletonIK::get_interpolation() const { + return interpolation; +} + +void SkeletonIK::set_target_transform(const Transform &p_target) { + target = p_target; + reload_goal(); +} + +const Transform &SkeletonIK::get_target_transform() const { + return target; +} + +void SkeletonIK::set_target_node(const NodePath &p_node) { + target_node_path_override = p_node; + target_node_override = NULL; + reload_goal(); +} + +NodePath SkeletonIK::get_target_node() { + return target_node_path_override; +} + +void SkeletonIK::set_use_magnet(bool p_use) { + use_magnet = p_use; +} + +bool SkeletonIK::is_using_magnet() const { + return use_magnet; +} + +void SkeletonIK::set_magnet_position(const Vector3 &p_local_position) { + magnet_position = p_local_position; +} + +const Vector3 &SkeletonIK::get_magnet_position() const { + return magnet_position; +} + +void SkeletonIK::set_min_distance(real_t p_min_distance) { + min_distance = p_min_distance; +} + +void SkeletonIK::set_max_iterations(int p_iterations) { + max_iterations = p_iterations; +} + +bool SkeletonIK::is_running() { + return is_processing_internal(); +} + +void SkeletonIK::start(bool p_one_time) { + if (p_one_time) { + set_process_internal(false); + _solve_chain(); + } else { + set_process_internal(true); + } +} + +void SkeletonIK::stop() { + set_process_internal(false); +} + +Transform SkeletonIK::_get_target_transform() { + + if (!target_node_override && !target_node_path_override.is_empty()) + target_node_override = Object::cast_to<Spatial>(get_node(target_node_path_override)); + + if (target_node_override) + return target_node_override->get_global_transform(); + else + return target; +} + +void SkeletonIK::reload_chain() { + + FabrikInverseKinematic::free_task(task); + task = NULL; + + if (!skeleton) + return; + + task = FabrikInverseKinematic::create_simple_task(skeleton, skeleton->find_bone(root_bone), skeleton->find_bone(tip_bone), _get_target_transform()); + task->max_iterations = max_iterations; + task->min_distance = min_distance; +} + +void SkeletonIK::reload_goal() { + if (!task) + return; + + FabrikInverseKinematic::set_goal(task, _get_target_transform()); +} + +void SkeletonIK::_solve_chain() { + if (!task) + return; + FabrikInverseKinematic::solve(task, interpolation, use_magnet, magnet_position); +} diff --git a/scene/animation/skeleton_ik.h b/scene/animation/skeleton_ik.h new file mode 100644 index 0000000000..366c599c01 --- /dev/null +++ b/scene/animation/skeleton_ik.h @@ -0,0 +1,212 @@ +/*************************************************************************/ +/* skeleton_ik.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef SKELETON_IK_H +#define SKELETON_IK_H + +/** + * @author AndreaCatania + */ + +#include "core/math/transform.h" +#include "scene/3d/skeleton.h" + +class FabrikInverseKinematic { + + struct EndEffector { + BoneId tip_bone; + Transform goal_transform; + }; + + struct ChainItem { + + Vector<ChainItem> childs; + ChainItem *parent_item; + + // Bone info + BoneId bone; + PhysicalBone *pb; + + real_t length; + /// Positions relative to root bone + Transform initial_transform; + Vector3 current_pos; + // Direction from this bone to child + Vector3 current_ori; + + ChainItem() : + parent_item(NULL), + bone(-1), + pb(NULL), + length(0) {} + + ChainItem *find_child(const BoneId p_bone_id); + ChainItem *add_child(const BoneId p_bone_id); + }; + + struct ChainTip { + ChainItem *chain_item; + const EndEffector *end_effector; + + ChainTip() : + chain_item(NULL), + end_effector(NULL) {} + + ChainTip(ChainItem *p_chain_item, const EndEffector *p_end_effector) : + chain_item(p_chain_item), + end_effector(p_end_effector) {} + + ChainTip(const ChainTip &p_other_ct) : + chain_item(p_other_ct.chain_item), + end_effector(p_other_ct.end_effector) {} + }; + + struct Chain { + ChainItem chain_root; + ChainItem *middle_chain_item; + Vector<ChainTip> tips; + Vector3 magnet_position; + }; + +public: + struct Task : public RID_Data { + RID self; + Skeleton *skeleton; + + Chain chain; + + // Settings + real_t min_distance; + int max_iterations; + + // Bone data + BoneId root_bone; + Vector<EndEffector> end_effectors; + + Transform goal_global_transform; + + Task() : + skeleton(NULL), + min_distance(0.01), + max_iterations(10), + root_bone(-1) {} + }; + +private: + /// Init a chain that starts from the root to tip + static void build_chain(Task *p_task, bool p_force_simple_chain = true); + + static void update_chain(const Skeleton *p_sk, ChainItem *p_chain_item); + + static void solve_simple(Task *p_task, bool p_solve_magnet); + /// Special solvers that solve only chains with one end effector + static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet); + static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet); + +public: + static Task *create_simple_task(Skeleton *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform); + static void free_task(Task *p_task); + // The goal of chain should be always in local space + static void set_goal(Task *p_task, const Transform &p_goal); + static void make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta); + static void solve(Task *p_task, real_t blending_delta, bool p_use_magnet, const Vector3 &p_magnet_position); +}; + +class SkeletonIK : public Node { + GDCLASS(SkeletonIK, Node); + + StringName root_bone; + StringName tip_bone; + real_t interpolation; + Transform target; + NodePath target_node_path_override; + bool use_magnet; + Vector3 magnet_position; + + real_t min_distance; + int max_iterations; + + Skeleton *skeleton; + Spatial *target_node_override; + FabrikInverseKinematic::Task *task; + +protected: + virtual void + _validate_property(PropertyInfo &property) const; + + static void _bind_methods(); + virtual void _notification(int p_notification); + +public: + SkeletonIK(); + virtual ~SkeletonIK(); + + void set_root_bone(const StringName &p_root_bone); + StringName get_root_bone() const; + + void set_tip_bone(const StringName &p_tip_bone); + StringName get_tip_bone() const; + + void set_interpolation(real_t p_interpolation); + real_t get_interpolation() const; + + void set_target_transform(const Transform &p_target); + const Transform &get_target_transform() const; + + void set_target_node(const NodePath &p_node); + NodePath get_target_node(); + + void set_use_magnet(bool p_use); + bool is_using_magnet() const; + + void set_magnet_position(const Vector3 &p_constraint); + const Vector3 &get_magnet_position() const; + + void set_min_distance(real_t p_min_distance); + real_t get_min_distance() const { return min_distance; } + + void set_max_iterations(int p_iterations); + int get_max_iterations() const { return max_iterations; } + + Skeleton *get_parent_skeleton() const { return skeleton; } + + bool is_running(); + + void start(bool p_one_time = false); + void stop(); + +private: + Transform _get_target_transform(); + void reload_chain(); + void reload_goal(); + void _solve_chain(); +}; + +#endif // SKELETON_IK_H diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 9f7503577b..81fdc32788 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -150,7 +150,7 @@ void Tween::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { - if (!processing) { + if (!is_active()) { //make sure that a previous process state was not saved //only process if "processing" is set set_physics_process_internal(false); @@ -164,7 +164,7 @@ void Tween::_notification(int p_what) { if (tween_process_mode == TWEEN_PROCESS_PHYSICS) break; - if (processing) + if (is_active()) _tween_process(get_process_delta_time()); } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { @@ -172,7 +172,7 @@ void Tween::_notification(int p_what) { if (tween_process_mode == TWEEN_PROCESS_IDLE) break; - if (processing) + if (is_active()) _tween_process(get_physics_process_delta_time()); } break; case NOTIFICATION_EXIT_TREE: { @@ -201,7 +201,6 @@ void Tween::_bind_methods() { ClassDB::bind_method(D_METHOD("reset_all"), &Tween::reset_all); ClassDB::bind_method(D_METHOD("stop", "object", "key"), &Tween::stop, DEFVAL("")); ClassDB::bind_method(D_METHOD("stop_all"), &Tween::stop_all); - ClassDB::bind_method(D_METHOD("is_stopped"), &Tween::is_stopped); ClassDB::bind_method(D_METHOD("resume", "object", "key"), &Tween::resume, DEFVAL("")); ClassDB::bind_method(D_METHOD("resume_all"), &Tween::resume_all); ClassDB::bind_method(D_METHOD("remove", "object", "key"), &Tween::remove, DEFVAL("")); @@ -522,8 +521,8 @@ void Tween::_tween_process(float p_delta) { pending_update++; // if repeat and all interpolates was finished then reset all interpolates + bool all_finished = true; if (repeat) { - bool all_finished = true; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -539,9 +538,12 @@ void Tween::_tween_process(float p_delta) { reset_all(); } + all_finished = true; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { InterpolateData &data = E->get(); + all_finished = all_finished && data.finish; + if (!data.active || data.finish) continue; @@ -555,8 +557,8 @@ void Tween::_tween_process(float p_delta) { continue; else if (prev_delaying) { - emit_signal("tween_started", object, NodePath(Vector<StringName>(), data.key, false)); _apply_tween_value(data, data.initial_val); + emit_signal("tween_started", object, NodePath(Vector<StringName>(), data.key, false)); } if (data.elapsed > (data.delay + data.duration)) { @@ -603,32 +605,29 @@ void Tween::_tween_process(float p_delta) { } } else { Variant result = _run_equation(data); - emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result); _apply_tween_value(data, result); + emit_signal("tween_step", object, NodePath(Vector<StringName>(), data.key, false), data.elapsed, result); } if (data.finish) { _apply_tween_value(data, data.final_val); + data.elapsed = 0; emit_signal("tween_completed", object, NodePath(Vector<StringName>(), data.key, false)); // not repeat mode, remove completed action if (!repeat) call_deferred("_remove", object, NodePath(Vector<StringName>(), data.key, false), true); - } + } else if (!repeat) + all_finished = all_finished && data.finish; } pending_update--; + + if (all_finished) + set_active(false); } void Tween::set_tween_process_mode(TweenProcessMode p_mode) { - if (tween_process_mode == p_mode) - return; - - bool pr = processing; - if (pr) - _set_process(false); tween_process_mode = p_mode; - if (pr) - _set_process(true); } Tween::TweenProcessMode Tween::get_tween_process_mode() const { @@ -636,32 +635,21 @@ Tween::TweenProcessMode Tween::get_tween_process_mode() const { return tween_process_mode; } -void Tween::_set_process(bool p_process, bool p_force) { - - if (processing == p_process && !p_force) - return; - - switch (tween_process_mode) { - - case TWEEN_PROCESS_PHYSICS: set_physics_process_internal(p_process && active); break; - case TWEEN_PROCESS_IDLE: set_process_internal(p_process && active); break; - } - - processing = p_process; -} - bool Tween::is_active() const { - return active; + return is_processing_internal() || is_physics_processing_internal(); } void Tween::set_active(bool p_active) { - if (active == p_active) + if (is_active() == p_active) return; - active = p_active; - _set_process(processing, true); + switch (tween_process_mode) { + + case TWEEN_PROCESS_IDLE: set_process_internal(p_active); break; + case TWEEN_PROCESS_PHYSICS: set_physics_process_internal(p_active); break; + } } bool Tween::is_repeat() const { @@ -687,7 +675,6 @@ float Tween::get_speed_scale() const { bool Tween::start() { set_active(true); - _set_process(true); return true; } @@ -744,14 +731,9 @@ bool Tween::stop(Object *p_object, StringName p_key) { return true; } -bool Tween::is_stopped() const { - return tell() >= get_runtime(); -} - bool Tween::stop_all() { set_active(false); - _set_process(false); pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -766,7 +748,6 @@ bool Tween::stop_all() { bool Tween::resume(Object *p_object, StringName p_key) { set_active(true); - _set_process(true); pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -785,7 +766,6 @@ bool Tween::resume(Object *p_object, StringName p_key) { bool Tween::resume_all() { set_active(true); - _set_process(true); pending_update++; for (List<InterpolateData>::Element *E = interpolates.front(); E; E = E->next()) { @@ -834,7 +814,6 @@ bool Tween::remove_all() { return true; } set_active(false); - _set_process(false); interpolates.clear(); return true; } @@ -1425,8 +1404,6 @@ Tween::Tween() { //String autoplay; tween_process_mode = TWEEN_PROCESS_IDLE; - processing = false; - active = false; repeat = false; speed_scale = 1; pending_update = 0; diff --git a/scene/animation/tween.h b/scene/animation/tween.h index 36094bf294..9997349c64 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -104,8 +104,6 @@ private: String autoplay; TweenProcessMode tween_process_mode; - bool processing; - bool active; bool repeat; float speed_scale; mutable int pending_update; @@ -133,7 +131,6 @@ private: bool _apply_tween_value(InterpolateData &p_data, Variant &value); void _tween_process(float p_delta); - void _set_process(bool p_process, bool p_force = false); void _remove(Object *p_object, StringName p_key, bool first_only); protected: @@ -162,7 +159,6 @@ public: bool reset_all(); bool stop(Object *p_object, StringName p_key); bool stop_all(); - bool is_stopped() const; bool resume(Object *p_object, StringName p_key); bool resume_all(); bool remove(Object *p_object, StringName p_key); |