void BoneAttachment::_check_bind() { if (get_parent() && get_parent()->cast_to<Skeleton>()) { Skeleton *sk = get_parent()->cast_to<Skeleton>(); int idx = sk->find_bone(bone_name); if (idx!=-1) { sk->bind_child_node_to_bone(idx,this);; bound=true; } } }
void BoneAttachment::_check_unbind() { if (bound) { if (get_parent() && get_parent()->cast_to<Skeleton>()) { Skeleton *sk = get_parent()->cast_to<Skeleton>(); int idx = sk->find_bone(bone_name); if (idx!=-1) { sk->unbind_child_node_from_bone(idx,this);; } } bound=false; } }
bool AnimationTree::_update_caches(AnimationPlayer *player) { setup_pass++; if (!player->has_node(player->get_root())) { ERR_PRINT("AnimationTree: AnimationPlayer root is invalid."); set_active(false); return false; } Node *parent = player->get_node(player->get_root()); List<StringName> sname; player->get_animation_list(&sname); for (List<StringName>::Element *E = sname.front(); E; E = E->next()) { Ref<Animation> anim = player->get_animation(E->get()); for (int i = 0; i < anim->get_track_count(); i++) { NodePath path = anim->track_get_path(i); Animation::TrackType track_type = anim->track_get_type(i); TrackCache *track = NULL; if (track_cache.has(path)) { track = track_cache.get(path); } //if not valid, delete track if (track && (track->type != track_type || ObjectDB::get_instance(track->object_id) == NULL)) { playing_caches.erase(track); memdelete(track); track_cache.erase(path); track = NULL; } if (!track) { RES resource; Vector<StringName> leftover_path; Node *child = parent->get_node_and_resource(path, resource, leftover_path); if (!child) { ERR_PRINTS("AnimationTree: '" + String(E->get()) + "', couldn't resolve track: '" + String(path) + "'"); continue; } if (!child->is_connected("tree_exited", this, "_node_removed")) { child->connect("tree_exited", this, "_node_removed", varray(child)); } switch (track_type) { case Animation::TYPE_VALUE: { TrackCacheValue *track_value = memnew(TrackCacheValue); if (resource.is_valid()) { track_value->object = resource.ptr(); } else { track_value->object = child; } track_value->subpath = leftover_path; track_value->object_id = track_value->object->get_instance_id(); track = track_value; } break; case Animation::TYPE_TRANSFORM: { Spatial *spatial = Object::cast_to<Spatial>(child); if (!spatial) { ERR_PRINTS("AnimationTree: '" + String(E->get()) + "', transform track does not point to spatial: '" + String(path) + "'"); continue; } TrackCacheTransform *track_xform = memnew(TrackCacheTransform); track_xform->spatial = spatial; track_xform->skeleton = NULL; track_xform->bone_idx = -1; if (path.get_subname_count() == 1 && Object::cast_to<Skeleton>(spatial)) { Skeleton *sk = Object::cast_to<Skeleton>(spatial); int bone_idx = sk->find_bone(path.get_subname(0)); if (bone_idx != -1 && !sk->is_bone_ignore_animation(bone_idx)) { track_xform->skeleton = sk; track_xform->bone_idx = bone_idx; } } track_xform->object = spatial; track_xform->object_id = track_xform->object->get_instance_id(); track = track_xform; } break; case Animation::TYPE_METHOD: { TrackCacheMethod *track_method = memnew(TrackCacheMethod); if (resource.is_valid()) { track_method->object = resource.ptr(); } else { track_method->object = child; } track_method->object_id = track_method->object->get_instance_id(); track = track_method; } break; case Animation::TYPE_BEZIER: { TrackCacheBezier *track_bezier = memnew(TrackCacheBezier); if (resource.is_valid()) { track_bezier->object = resource.ptr(); } else { track_bezier->object = child; } track_bezier->subpath = leftover_path; track_bezier->object_id = track_bezier->object->get_instance_id(); track = track_bezier; } break; case Animation::TYPE_AUDIO: { TrackCacheAudio *track_audio = memnew(TrackCacheAudio); track_audio->object = child; track_audio->object_id = track_audio->object->get_instance_id(); track = track_audio; } break; case Animation::TYPE_ANIMATION: { TrackCacheAnimation *track_animation = memnew(TrackCacheAnimation); track_animation->object = child; track_animation->object_id = track_animation->object->get_instance_id(); track = track_animation; } break; default: { ERR_PRINT("Animation corrupted (invalid track type)"); continue; } } track_cache[path] = track; } track->setup_pass = setup_pass; } } List<NodePath> to_delete; const NodePath *K = NULL; while ((K = track_cache.next(K))) { TrackCache *tc = track_cache[*K]; if (tc->setup_pass != setup_pass) { to_delete.push_back(*K); } } while (to_delete.front()) { NodePath np = to_delete.front()->get(); memdelete(track_cache[np]); track_cache.erase(np); to_delete.pop_front(); } state.track_map.clear(); K = NULL; int idx = 0; while ((K = track_cache.next(K))) { state.track_map[*K] = idx; idx++; } state.track_count = idx; cache_valid = true; return true; }
void AnimationCache::_update_cache() { cache_valid=false; ERR_FAIL_COND(!root); ERR_FAIL_COND(!root->is_inside_tree()); ERR_FAIL_COND(animation.is_null()); for(int i=0;i<animation->get_track_count();i++) { NodePath np = animation->track_get_path(i); Node *node = root->get_node(np); if (!node) { path_cache.push_back(Path()); ERR_EXPLAIN("Invalid Track Path in Animation: "+np); ERR_CONTINUE(!node); } Path path; Ref<Resource> res; if (np.get_subname_count()) { if (animation->track_get_type(i)==Animation::TYPE_TRANSFORM) { path_cache.push_back(Path()); ERR_EXPLAIN("Transform tracks can't have a subpath: "+np); ERR_CONTINUE(animation->track_get_type(i)==Animation::TYPE_TRANSFORM); } RES res; for(int j=0;j<np.get_subname_count();j++) { res = j==0 ? node->get(np.get_subname(j)) : res->get(np.get_subname(j)); if (res.is_null()) break; } if (res.is_null()) { path_cache.push_back(Path()); ERR_EXPLAIN("Invalid Track SubPath in Animation: "+np); ERR_CONTINUE(res.is_null()); } path.resource=res; path.object=res.ptr(); } else { if (animation->track_get_type(i)==Animation::TYPE_TRANSFORM) { StringName property = np.get_property(); String ps = property; Spatial *sp = node->cast_to<Spatial>(); if (!sp) { path_cache.push_back(Path()); ERR_EXPLAIN("Transform track not of type Spatial: "+np); ERR_CONTINUE(!sp); } if (ps!="") { Skeleton *sk = node->cast_to<Skeleton>(); if (!sk) { path_cache.push_back(Path()); ERR_EXPLAIN("Property defined in Transform track, but not a Skeleton!: "+np); ERR_CONTINUE(!sk); } int idx = sk->find_bone(ps); if (idx==-1) { path_cache.push_back(Path()); ERR_EXPLAIN("Property defined in Transform track, but not a Skeleton Bone!: "+np); ERR_CONTINUE(idx==-1); } path.bone_idx=idx; path.skeleton=sk; } path.spatial=sp; } path.node=node; path.object=node; } if (animation->track_get_type(i)==Animation::TYPE_VALUE) { if (np.get_property().operator String()=="") { path_cache.push_back(Path()); ERR_EXPLAIN("Value Track lacks property: "+np); ERR_CONTINUE(np.get_property().operator String()==""); } path.property=np.get_property(); } else if (animation->track_get_type(i)==Animation::TYPE_METHOD) { if (np.get_property().operator String()!="") { path_cache.push_back(Path()); ERR_EXPLAIN("Method Track has property: "+np); ERR_CONTINUE(np.get_property().operator String()!=""); } } path.valid=true; path_cache.push_back(path); if (!connected_nodes.has(path.node)) { connected_nodes.insert(path.node); path.node->connect("exit_tree",this,"_node_exit_tree",Node::make_binds(path.node),CONNECT_ONESHOT); } } cache_dirty=false; cache_valid=true; }
void AnimationTreeEditor::_edit_filters() { filter_dialog->popup_centered_ratio(); filter->clear(); Set<String> npb; _find_paths_for_filter(edited_node,npb); TreeItem *root = filter->create_item(); filter->set_hide_root(true); Map<String,TreeItem*> pm; Node *base = anim_tree->get_node( anim_tree->get_base_path() ); for(Set<String>::Element *E=npb.front();E;E=E->next()) { TreeItem *parent=root; String descr=E->get(); if (base) { NodePath np = E->get(); if (np.get_property()!=StringName()) { Node *n = base->get_node(np); Skeleton *s = n->cast_to<Skeleton>(); if (s) { String skelbase = E->get().substr(0,E->get().find(":")); int bidx = s->find_bone(np.get_property()); if (bidx!=-1) { int bparent = s->get_bone_parent(bidx); // if (bparent!=-1) { String bpn = skelbase+":"+s->get_bone_name(bparent); if (pm.has(bpn)) { parent=pm[bpn]; descr=np.get_property(); } } else { if (pm.has(skelbase)) { parent=pm[skelbase]; } } } } } } TreeItem *it = filter->create_item(parent); it->set_cell_mode(0,TreeItem::CELL_MODE_CHECK); it->set_text(0,descr); it->set_metadata(0,NodePath(E->get())); it->set_editable(0,true); if (anim_tree->node_get_type(edited_node)==AnimationTreePlayer::NODE_ONESHOT) { it->set_checked(0, anim_tree->oneshot_node_is_path_filtered(edited_node,E->get())); } else if (anim_tree->node_get_type(edited_node)==AnimationTreePlayer::NODE_BLEND2) { it->set_checked(0, anim_tree->blend2_node_is_path_filtered(edited_node,E->get())); } pm[E->get()]=it; } }
bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &anode) { if (updating || _filter_edit != anode) return false; NodePath player_path = anode->get_tree()->get_animation_player(); if (!anode->get_tree()->has_node(player_path)) { EditorNode::get_singleton()->show_warning(TTR("No animation player set, so unable to retrieve track names.")); return false; } AnimationPlayer *player = Object::cast_to<AnimationPlayer>(anode->get_tree()->get_node(player_path)); if (!player) { EditorNode::get_singleton()->show_warning(TTR("Player path set is invalid, so unable to retrieve track names.")); return false; } Node *base = player->get_node(player->get_root()); if (!base) { EditorNode::get_singleton()->show_warning(TTR("Animation player has no valid root node path, so unable to retrieve track names.")); return false; } updating = true; Set<String> paths; { List<StringName> animations; player->get_animation_list(&animations); for (List<StringName>::Element *E = animations.front(); E; E = E->next()) { Ref<Animation> anim = player->get_animation(E->get()); for (int i = 0; i < anim->get_track_count(); i++) { paths.insert(anim->track_get_path(i)); } } } filter_enabled->set_pressed(anode->is_filter_enabled()); filters->clear(); TreeItem *root = filters->create_item(); Map<String, TreeItem *> parenthood; for (Set<String>::Element *E = paths.front(); E; E = E->next()) { NodePath path = E->get(); TreeItem *ti = NULL; String accum; for (int i = 0; i < path.get_name_count(); i++) { String name = path.get_name(i); if (accum != String()) { accum += "/"; } accum += name; if (!parenthood.has(accum)) { if (ti) { ti = filters->create_item(ti); } else { ti = filters->create_item(root); } parenthood[accum] = ti; ti->set_text(0, name); ti->set_selectable(0, false); ti->set_editable(0, false); if (base->has_node(accum)) { Node *node = base->get_node(accum); if (has_icon(node->get_class(), "EditorIcons")) { ti->set_icon(0, get_icon(node->get_class(), "EditorIcons")); } else { ti->set_icon(0, get_icon("Node", "EditorIcons")); } } } else { ti = parenthood[accum]; } } Node *node = NULL; if (base->has_node(accum)) { node = base->get_node(accum); } if (!node) continue; //no node, cant edit if (path.get_subname_count()) { String concat = path.get_concatenated_subnames(); Skeleton *skeleton = Object::cast_to<Skeleton>(node); if (skeleton && skeleton->find_bone(concat) != -1) { //path in skeleton String bone = concat; int idx = skeleton->find_bone(bone); List<String> bone_path; while (idx != -1) { bone_path.push_front(skeleton->get_bone_name(idx)); idx = skeleton->get_bone_parent(idx); } accum += ":"; for (List<String>::Element *F = bone_path.front(); F; F = F->next()) { if (F != bone_path.front()) { accum += "/"; } accum += F->get(); if (!parenthood.has(accum)) { ti = filters->create_item(ti); parenthood[accum] = ti; ti->set_text(0, F->get()); ti->set_selectable(0, false); ti->set_editable(0, false); ti->set_icon(0, get_icon("BoneAttachment", "EditorIcons")); } else { ti = parenthood[accum]; } } ti->set_editable(0, true); ti->set_selectable(0, true); ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); ti->set_text(0, concat); ti->set_checked(0, anode->is_path_filtered(path)); ti->set_icon(0, get_icon("BoneAttachment", "EditorIcons")); ti->set_metadata(0, path); } else { //just a property ti = filters->create_item(ti); ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); ti->set_text(0, concat); ti->set_editable(0, true); ti->set_selectable(0, true); ti->set_checked(0, anode->is_path_filtered(path)); ti->set_metadata(0, path); } } else { if (ti) { //just a node, likely call or animation track ti->set_editable(0, true); ti->set_selectable(0, true); ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); ti->set_checked(0, anode->is_path_filtered(path)); ti->set_metadata(0, path); } } } updating = false; return true; }
void EditorPropertyRootMotion::_node_assign() { NodePath current = get_edited_object()->get(get_edited_property()); AnimationTree *atree = Object::cast_to<AnimationTree>(get_edited_object()); if (!atree->has_node(atree->get_animation_player())) { EditorNode::get_singleton()->show_warning(TTR("AnimationTree has no path set to an AnimationPlayer")); return; } AnimationPlayer *player = Object::cast_to<AnimationPlayer>(atree->get_node(atree->get_animation_player())); if (!player) { EditorNode::get_singleton()->show_warning(TTR("Path to AnimationPlayer is invalid")); return; } Node *base = player->get_node(player->get_root()); if (!base) { EditorNode::get_singleton()->show_warning(TTR("Animation player has no valid root node path, so unable to retrieve track names.")); return; } Set<String> paths; { List<StringName> animations; player->get_animation_list(&animations); for (List<StringName>::Element *E = animations.front(); E; E = E->next()) { Ref<Animation> anim = player->get_animation(E->get()); for (int i = 0; i < anim->get_track_count(); i++) { paths.insert(anim->track_get_path(i)); } } } filters->clear(); TreeItem *root = filters->create_item(); Map<String, TreeItem *> parenthood; for (Set<String>::Element *E = paths.front(); E; E = E->next()) { NodePath path = E->get(); TreeItem *ti = NULL; String accum; for (int i = 0; i < path.get_name_count(); i++) { String name = path.get_name(i); if (accum != String()) { accum += "/"; } accum += name; if (!parenthood.has(accum)) { if (ti) { ti = filters->create_item(ti); } else { ti = filters->create_item(root); } parenthood[accum] = ti; ti->set_text(0, name); ti->set_selectable(0, false); ti->set_editable(0, false); if (base->has_node(accum)) { Node *node = base->get_node(accum); if (has_icon(node->get_class(), "EditorIcons")) { ti->set_icon(0, get_icon(node->get_class(), "EditorIcons")); } else { ti->set_icon(0, get_icon("Node", "EditorIcons")); } } } else { ti = parenthood[accum]; } } Node *node = NULL; if (base->has_node(accum)) { node = base->get_node(accum); } if (!node) continue; //no node, cant edit if (path.get_subname_count()) { String concat = path.get_concatenated_subnames(); Skeleton *skeleton = Object::cast_to<Skeleton>(node); if (skeleton && skeleton->find_bone(concat) != -1) { //path in skeleton String bone = concat; int idx = skeleton->find_bone(bone); List<String> bone_path; while (idx != -1) { bone_path.push_front(skeleton->get_bone_name(idx)); idx = skeleton->get_bone_parent(idx); } accum += ":"; for (List<String>::Element *F = bone_path.front(); F; F = F->next()) { if (F != bone_path.front()) { accum += "/"; } accum += F->get(); if (!parenthood.has(accum)) { ti = filters->create_item(ti); parenthood[accum] = ti; ti->set_text(0, F->get()); ti->set_selectable(0, true); ti->set_editable(0, false); ti->set_icon(0, get_icon("BoneAttachment", "EditorIcons")); ti->set_metadata(0, accum); } else { ti = parenthood[accum]; } } ti->set_selectable(0, true); ti->set_text(0, concat); ti->set_icon(0, get_icon("BoneAttachment", "EditorIcons")); ti->set_metadata(0, path); if (path == current) { ti->select(0); } } else { //just a property ti = filters->create_item(ti); ti->set_text(0, concat); ti->set_selectable(0, true); ti->set_metadata(0, path); if (path == current) { ti->select(0); } } } else { if (ti) { //just a node, likely call or animation track ti->set_selectable(0, true); ti->set_metadata(0, path); if (path == current) { ti->select(0); } } } } filters->ensure_cursor_is_visible(); filter_dialog->popup_centered_ratio(); }
void AnimationCache::_update_cache() { cache_valid = false; ERR_FAIL_COND(!root); ERR_FAIL_COND(!root->is_inside_tree()); ERR_FAIL_COND(animation.is_null()); for (int i = 0; i < animation->get_track_count(); i++) { NodePath np = animation->track_get_path(i); Node *node = root->get_node(np); if (!node) { path_cache.push_back(Path()); ERR_EXPLAIN("Invalid Track Path in Animation: " + np); ERR_CONTINUE(!node); } Path path; Ref<Resource> res; if (animation->track_get_type(i) == Animation::TYPE_TRANSFORM) { if (np.get_subname_count() > 1) { path_cache.push_back(Path()); ERR_EXPLAIN("Transform tracks can't have a subpath: " + np); ERR_CONTINUE(animation->track_get_type(i) == Animation::TYPE_TRANSFORM); } Spatial *sp = Object::cast_to<Spatial>(node); if (!sp) { path_cache.push_back(Path()); ERR_EXPLAIN("Transform track not of type Spatial: " + np); ERR_CONTINUE(!sp); } if (np.get_subname_count() == 1) { StringName property = np.get_subname(0); String ps = property; Skeleton *sk = Object::cast_to<Skeleton>(node); if (!sk) { path_cache.push_back(Path()); ERR_EXPLAIN("Property defined in Transform track, but not a Skeleton!: " + np); ERR_CONTINUE(!sk); } int idx = sk->find_bone(ps); if (idx == -1) { path_cache.push_back(Path()); ERR_EXPLAIN("Property defined in Transform track, but not a Skeleton Bone!: " + np); ERR_CONTINUE(idx == -1); } path.bone_idx = idx; path.skeleton = sk; } path.spatial = sp; } else { if (np.get_subname_count() > 0) { RES res2; Vector<StringName> leftover_subpath; // We don't want to cache the last resource unless it is a method call bool is_method = animation->track_get_type(i) == Animation::TYPE_METHOD; root->get_node_and_resource(np, res2, leftover_subpath, is_method); if (res2.is_valid()) { path.resource = res2; } else { path.node = node; } path.object = res2.is_valid() ? res2.ptr() : (Object *)node; path.subpath = leftover_subpath; } else { path.node = node; path.object = node; path.subpath = np.get_subnames(); } } if (animation->track_get_type(i) == Animation::TYPE_VALUE) { if (np.get_subname_count() == 0) { path_cache.push_back(Path()); ERR_EXPLAIN("Value Track lacks property: " + np); ERR_CONTINUE(np.get_subname_count() == 0); } } else if (animation->track_get_type(i) == Animation::TYPE_METHOD) { if (path.subpath.size() != 0) { // Trying to call a method of a non-resource path_cache.push_back(Path()); ERR_EXPLAIN("Method Track has property: " + np); ERR_CONTINUE(path.subpath.size() != 0); } } path.valid = true; path_cache.push_back(path); if (!connected_nodes.has(path.node)) { connected_nodes.insert(path.node); path.node->connect("tree_exiting", this, "_node_exit_tree", Node::make_binds(path.node), CONNECT_ONESHOT); } } cache_dirty = false; cache_valid = true; }
Error EditorSceneImporterFBXConv::_parse_animations(State& state) { AnimationPlayer *ap = memnew( AnimationPlayer ); state.scene->add_child(ap); ap->set_owner(state.scene); for(int i=0;i<state.animations.size();i++) { Dictionary anim = state.animations[i]; ERR_CONTINUE(!anim.has("id")); Ref<Animation> an = memnew( Animation ); an->set_name(_id(anim["id"])); if (anim.has("bones")) { Array bone_tracks = anim["bones"]; for(int j=0;j<bone_tracks.size();j++) { Dictionary bone_track=bone_tracks[j]; String bone = bone_track["boneId"]; if (!bone_track.has("keyframes")) continue; if (!state.bones.has(bone)) continue; Skeleton *sk = state.bones[bone].skeleton; if (!sk) continue; int bone_idx=sk->find_bone(bone); if (bone_idx==-1) continue; String path = state.scene->get_path_to(sk); path+=":"+bone; an->add_track(Animation::TYPE_TRANSFORM); int tidx = an->get_track_count()-1; an->track_set_path(tidx,path); Dictionary parent_xform_dict; Dictionary xform_dict; if (state.bones.has(bone)) { xform_dict=state.bones[bone].node; } Array parent_keyframes; if (sk->get_bone_parent(bone_idx)!=-1) { String parent_name = sk->get_bone_name(sk->get_bone_parent(bone_idx)); if (state.bones.has(parent_name)) { parent_xform_dict=state.bones[parent_name].node; } print_line("parent for "+bone+"? "+parent_name+" XFD: "+String(Variant(parent_xform_dict))); for(int k=0;k<bone_tracks.size();k++) { Dictionary d = bone_tracks[k]; if (d["boneId"]==parent_name) { parent_keyframes=d["keyframes"]; print_line("found keyframes"); break; } } } print_line("BONE XFD "+String(Variant(xform_dict))); Array keyframes=bone_track["keyframes"]; for(int k=0;k<keyframes.size();k++) { Dictionary key=keyframes[k]; Transform xform=_get_transform_mixed(key,xform_dict); float time = key["keytime"]; time=time/1000.0; #if 0 if (parent_keyframes.size()) { //localize print_line(itos(k)+" localizate for: "+bone); float prev_kt=-1; float kt; int idx=0; for(int l=0;l<parent_keyframes.size();l++) { Dictionary d=parent_keyframes[l]; kt=d["keytime"]; kt=kt/1000.0; if (kt>time) break; prev_kt=kt; idx++; } Transform t; if (idx==0) { t=_get_transform_mixed(parent_keyframes[0],parent_xform_dict); } else if (idx==parent_keyframes.size()){ t=_get_transform_mixed(parent_keyframes[idx-1],parent_xform_dict); } else { t=_get_transform_mixed(parent_keyframes[idx-1],parent_xform_dict); float d = (time-prev_kt)/(kt-prev_kt); if (d>0) { Transform t2=_get_transform_mixed(parent_keyframes[idx],parent_xform_dict); t=t.interpolate_with(t2,d); } else { print_line("exact: "+rtos(kt)); } } xform = t.affine_inverse() * xform; //localize } else if (!parent_xform_dict.empty()) { Transform t = _get_transform(parent_xform_dict); xform = t.affine_inverse() * xform; //localize } #endif xform = sk->get_bone_rest(bone_idx).affine_inverse() * xform; Quat q = xform.basis; q.normalize(); Vector3 s = xform.basis.get_scale(); Vector3 l = xform.origin; an->transform_track_insert_key(tidx,time,l,q,s); } } } ap->add_animation(_id(anim["id"]),an); } return OK; }