void AnimationNodeBlendTreeEditor::_notification(int p_what) {

	if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {

		goto_parent->set_icon(get_icon("MoveUp", "EditorIcons"));

		error_panel->add_style_override("panel", get_stylebox("bg", "Tree"));
		error_label->add_color_override("font_color", get_color("error_color", "Editor"));
	}

	if (p_what == NOTIFICATION_PROCESS) {

		String error;

		if (!blend_tree->get_tree()) {
			error = TTR("BlendTree does not belong to an AnimationTree node.");
		} else if (!blend_tree->get_tree()->is_active()) {
			error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails.");
		} else if (blend_tree->get_tree()->is_state_invalid()) {
			error = blend_tree->get_tree()->get_invalid_state_reason();
		}

		if (error != error_label->get_text()) {
			error_label->set_text(error);
			if (error != String()) {
				error_panel->show();
			} else {
				error_panel->hide();
			}
		}

		List<AnimationNodeBlendTree::NodeConnection> conns;
		blend_tree->get_node_connections(&conns);
		for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = conns.front(); E; E = E->next()) {
			float activity = 0;
			if (blend_tree->get_tree() && !blend_tree->get_tree()->is_state_invalid()) {
				activity = blend_tree->get_connection_activity(E->get().input_node, E->get().input_index);
			}
			graph->set_connection_activity(E->get().output_node, 0, E->get().input_node, E->get().input_index, activity);
		}

		AnimationTree *graph_player = blend_tree->get_tree();
		AnimationPlayer *player = NULL;
		if (graph_player->has_node(graph_player->get_animation_player())) {
			player = Object::cast_to<AnimationPlayer>(graph_player->get_node(graph_player->get_animation_player()));
		}

		if (player) {
			for (Map<StringName, ProgressBar *>::Element *E = animations.front(); E; E = E->next()) {
				Ref<AnimationNodeAnimation> an = blend_tree->get_node(E->key());
				if (an.is_valid()) {
					if (player->has_animation(an->get_animation())) {
						Ref<Animation> anim = player->get_animation(an->get_animation());
						if (anim.is_valid()) {
							E->get()->set_max(anim->get_length());
							E->get()->set_value(an->get_playback_time());
						}
					}
				}
			}
		}
	}
}
void AnimationNodeBlendTreeEditor::_update_graph() {

	if (updating)
		return;

	graph->set_scroll_ofs(blend_tree->get_graph_offset() * EDSCALE);

	if (blend_tree->get_parent().is_valid()) {
		goto_parent->show();
	} else {
		goto_parent->hide();
	}
	graph->clear_connections();
	//erase all nodes
	for (int i = 0; i < graph->get_child_count(); i++) {

		if (Object::cast_to<GraphNode>(graph->get_child(i))) {
			memdelete(graph->get_child(i));
			i--;
		}
	}

	animations.clear();

	List<StringName> nodes;
	blend_tree->get_node_list(&nodes);

	for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) {

		GraphNode *node = memnew(GraphNode);
		graph->add_child(node);

		Ref<AnimationNode> agnode = blend_tree->get_node(E->get());

		if (!agnode->is_connected("changed", this, "_node_changed")) {
			agnode->connect("changed", this, "_node_changed", varray(agnode->get_instance_id()), CONNECT_DEFERRED);
		}

		node->set_offset(agnode->get_position() * EDSCALE);

		node->set_title(agnode->get_caption());
		node->set_name(E->get());

		int base = 0;
		if (String(E->get()) != "output") {
			LineEdit *name = memnew(LineEdit);
			name->set_text(E->get());
			name->set_expand_to_text_length(true);
			node->add_child(name);
			node->set_slot(0, false, 0, Color(), true, 0, get_color("font_color", "Label"));
			name->connect("text_entered", this, "_node_renamed", varray(agnode));
			name->connect("focus_exited", this, "_node_renamed_focus_out", varray(name, agnode));
			base = 1;
			node->set_show_close_button(true);
			node->connect("close_request", this, "_delete_request", varray(E->get()), CONNECT_DEFERRED);
		}

		for (int i = 0; i < agnode->get_input_count(); i++) {
			Label *in_name = memnew(Label);
			node->add_child(in_name);
			in_name->set_text(agnode->get_input_name(i));
			node->set_slot(base + i, true, 0, get_color("font_color", "Label"), false, 0, Color());
		}

		node->connect("dragged", this, "_node_dragged", varray(agnode));

		if (EditorNode::get_singleton()->item_has_editor(agnode.ptr())) {
			node->add_child(memnew(HSeparator));
			Button *open_in_editor = memnew(Button);
			open_in_editor->set_text(TTR("Open Editor"));
			open_in_editor->set_icon(get_icon("Edit", "EditorIcons"));
			node->add_child(open_in_editor);
			open_in_editor->connect("pressed", this, "_open_in_editor", varray(E->get()), CONNECT_DEFERRED);
			open_in_editor->set_h_size_flags(SIZE_SHRINK_CENTER);
		}

		if (agnode->has_filter()) {

			node->add_child(memnew(HSeparator));
			Button *edit_filters = memnew(Button);
			edit_filters->set_text(TTR("Edit Filters"));
			edit_filters->set_icon(get_icon("AnimationFilter", "EditorIcons"));
			node->add_child(edit_filters);
			edit_filters->connect("pressed", this, "_edit_filters", varray(E->get()), CONNECT_DEFERRED);
			edit_filters->set_h_size_flags(SIZE_SHRINK_CENTER);
		}

		Ref<AnimationNodeAnimation> anim = agnode;
		if (anim.is_valid()) {

			MenuButton *mb = memnew(MenuButton);
			mb->set_text(anim->get_animation());
			mb->set_icon(get_icon("Animation", "EditorIcons"));
			Array options;

			node->add_child(memnew(HSeparator));
			node->add_child(mb);

			ProgressBar *pb = memnew(ProgressBar);

			AnimationTree *player = anim->get_tree();
			if (player->has_node(player->get_animation_player())) {
				AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(player->get_node(player->get_animation_player()));
				if (ap) {
					List<StringName> anims;
					ap->get_animation_list(&anims);

					for (List<StringName>::Element *F = anims.front(); F; F = F->next()) {
						mb->get_popup()->add_item(F->get());
						options.push_back(F->get());
					}

					if (ap->has_animation(anim->get_animation())) {
						pb->set_max(ap->get_animation(anim->get_animation())->get_length());
					}
				}
			}

			pb->set_percent_visible(false);
			animations[E->get()] = pb;
			node->add_child(pb);

			mb->get_popup()->connect("index_pressed", this, "_anim_selected", varray(options, E->get()), CONNECT_DEFERRED);
		}

		Ref<AnimationNodeOneShot> oneshot = agnode;
		if (oneshot.is_valid()) {

			HBoxContainer *play_stop = memnew(HBoxContainer);
			play_stop->add_spacer();
			Button *play = memnew(Button);
			play->set_icon(get_icon("Play", "EditorIcons"));
			play->connect("pressed", this, "_oneshot_start", varray(E->get()), CONNECT_DEFERRED);
			play_stop->add_child(play);
			Button *stop = memnew(Button);
			stop->set_icon(get_icon("Stop", "EditorIcons"));
			stop->connect("pressed", this, "_oneshot_stop", varray(E->get()), CONNECT_DEFERRED);
			play_stop->add_child(stop);
			play_stop->add_spacer();
			node->add_child(play_stop);
		}
	}

	List<AnimationNodeBlendTree::NodeConnection> connections;
	blend_tree->get_node_connections(&connections);

	for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = connections.front(); E; E = E->next()) {

		StringName from = E->get().output_node;
		StringName to = E->get().input_node;
		int to_idx = E->get().input_index;

		graph->connect_node(from, 0, to, to_idx);
	}
}
Exemplo n.º 3
0
void AnimationTree::_process_graph(float p_delta) {

	_update_properties(); //if properties need updating, update them

	//check all tracks, see if they need modification

	root_motion_transform = Transform();

	if (!root.is_valid()) {
		ERR_PRINT("AnimationTree: root AnimationNode is not set, disabling playback.");
		set_active(false);
		cache_valid = false;
		return;
	}

	if (!has_node(animation_player)) {
		ERR_PRINT("AnimationTree: no valid AnimationPlayer path set, disabling playback");
		set_active(false);
		cache_valid = false;
		return;
	}

	AnimationPlayer *player = Object::cast_to<AnimationPlayer>(get_node(animation_player));

	ObjectID current_animation_player = 0;

	if (player) {
		current_animation_player = player->get_instance_id();
	}

	if (last_animation_player != current_animation_player) {

		if (last_animation_player) {
			Object *old_player = ObjectDB::get_instance(last_animation_player);
			if (old_player) {
				old_player->disconnect("caches_cleared", this, "_clear_caches");
			}
		}

		if (player) {
			player->connect("caches_cleared", this, "_clear_caches");
		}

		last_animation_player = current_animation_player;
	}

	if (!player) {
		ERR_PRINT("AnimationTree: path points to a node not an AnimationPlayer, disabling playback");
		set_active(false);
		cache_valid = false;
		return;
	}

	if (!cache_valid) {
		if (!_update_caches(player)) {
			return;
		}
	}

	{ //setup

		process_pass++;

		state.valid = true;
		state.invalid_reasons = "";
		state.animation_states.clear(); //will need to be re-created
		state.valid = true;
		state.player = player;
		state.last_pass = process_pass;
		state.tree = this;

		// root source blends

		root->blends.resize(state.track_count);
		float *src_blendsw = root->blends.ptrw();
		for (int i = 0; i < state.track_count; i++) {
			src_blendsw[i] = 1.0; //by default all go to 1 for the root input
		}
	}

	//process

	{

		if (started) {
			//if started, seek
			root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, NULL, &state, 0, true, Vector<StringName>());
			started = false;
		}

		root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path, NULL, &state, p_delta, false, Vector<StringName>());
	}

	if (!state.valid) {
		return; //state is not valid. do nothing.
	}
	//apply value/transform/bezier blends to track caches and execute method/audio/animation tracks

	{

		bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint();

		for (List<AnimationNode::AnimationState>::Element *E = state.animation_states.front(); E; E = E->next()) {

			const AnimationNode::AnimationState &as = E->get();

			Ref<Animation> a = as.animation;
			float time = as.time;
			float delta = as.delta;
			bool seeked = as.seeked;

			for (int i = 0; i < a->get_track_count(); i++) {

				NodePath path = a->track_get_path(i);
				TrackCache *track = track_cache[path];
				if (track->type != a->track_get_type(i)) {
					continue; //may happen should not
				}

				track->root_motion = root_motion_track == path;

				ERR_CONTINUE(!state.track_map.has(path));
				int blend_idx = state.track_map[path];

				ERR_CONTINUE(blend_idx < 0 || blend_idx >= state.track_count);

				float blend = (*as.track_blends)[blend_idx];

				if (blend < CMP_EPSILON)
					continue; //nothing to blend

				switch (track->type) {

					case Animation::TYPE_TRANSFORM: {

						TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);

						if (t->process_pass != process_pass) {

							t->process_pass = process_pass;
							t->loc = Vector3();
							t->rot = Quat();
							t->rot_blend_accum = 0;
							t->scale = Vector3();
						}

						if (track->root_motion) {

							float prev_time = time - delta;
							if (prev_time < 0) {
								if (!a->has_loop()) {
									prev_time = 0;
								} else {
									prev_time = a->get_length() + prev_time;
								}
							}

							Vector3 loc[2];
							Quat rot[2];
							Vector3 scale[2];

							if (prev_time > time) {

								Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
								if (err != OK) {
									continue;
								}

								a->transform_track_interpolate(i, a->get_length(), &loc[1], &rot[1], &scale[1]);

								t->loc += (loc[1] - loc[0]) * blend;
								t->scale += (scale[1] - scale[0]) * blend;
								Quat q = Quat().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
								t->rot = (t->rot * q).normalized();

								prev_time = 0;
							}

							Error err = a->transform_track_interpolate(i, prev_time, &loc[0], &rot[0], &scale[0]);
							if (err != OK) {
								continue;
							}

							a->transform_track_interpolate(i, time, &loc[1], &rot[1], &scale[1]);

							t->loc += (loc[1] - loc[0]) * blend;
							t->scale += (scale[1] - scale[0]) * blend;
							Quat q = Quat().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
							t->rot = (t->rot * q).normalized();

							prev_time = 0;

						} else {
							Vector3 loc;
							Quat rot;
							Vector3 scale;

							Error err = a->transform_track_interpolate(i, time, &loc, &rot, &scale);
							//ERR_CONTINUE(err!=OK); //used for testing, should be removed

							scale -= Vector3(1.0, 1.0, 1.0); //helps make it work properly with Add nodes

							if (err != OK)
								continue;

							t->loc = t->loc.linear_interpolate(loc, blend);
							if (t->rot_blend_accum == 0) {
								t->rot = rot;
								t->rot_blend_accum = blend;
							} else {
								float rot_total = t->rot_blend_accum + blend;
								t->rot = rot.slerp(t->rot, t->rot_blend_accum / rot_total).normalized();
								t->rot_blend_accum = rot_total;
							}
							t->scale = t->scale.linear_interpolate(scale, blend);
						}

					} break;
					case Animation::TYPE_VALUE: {

						TrackCacheValue *t = static_cast<TrackCacheValue *>(track);

						Animation::UpdateMode update_mode = a->value_track_get_update_mode(i);

						if (update_mode == Animation::UPDATE_CONTINUOUS || update_mode == Animation::UPDATE_CAPTURE) { //delta == 0 means seek

							Variant value = a->value_track_interpolate(i, time);

							if (value == Variant())
								continue;

							if (t->process_pass != process_pass) {
								Variant::CallError ce;
								t->value = Variant::construct(value.get_type(), NULL, 0, ce); //reset
								t->process_pass = process_pass;
							}

							Variant::interpolate(t->value, value, blend, t->value);

						} else if (delta != 0) {

							List<int> indices;
							a->value_track_get_key_indices(i, time, delta, &indices);

							for (List<int>::Element *F = indices.front(); F; F = F->next()) {

								Variant value = a->track_get_key_value(i, F->get());
								t->object->set_indexed(t->subpath, value);
							}
						}

					} break;
					case Animation::TYPE_METHOD: {

						if (delta == 0) {
							continue;
						}
						TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track);

						List<int> indices;

						a->method_track_get_key_indices(i, time, delta, &indices);

						for (List<int>::Element *E = indices.front(); E; E = E->next()) {

							StringName method = a->method_track_get_name(i, E->get());
							Vector<Variant> params = a->method_track_get_params(i, E->get());

							int s = params.size();

							ERR_CONTINUE(s > VARIANT_ARG_MAX);
							if (can_call) {
								t->object->call_deferred(
										method,
										s >= 1 ? params[0] : Variant(),
										s >= 2 ? params[1] : Variant(),
										s >= 3 ? params[2] : Variant(),
										s >= 4 ? params[3] : Variant(),
										s >= 5 ? params[4] : Variant());
							}
						}

					} break;
					case Animation::TYPE_BEZIER: {

						TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);

						float bezier = a->bezier_track_interpolate(i, time);

						if (t->process_pass != process_pass) {
							t->value = 0;
							t->process_pass = process_pass;
						}

						t->value = Math::lerp(t->value, bezier, blend);

					} break;
					case Animation::TYPE_AUDIO: {

						TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track);

						if (seeked) {
							//find whathever should be playing
							int idx = a->track_find_key(i, time);
							if (idx < 0)
								continue;

							Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
							if (!stream.is_valid()) {
								t->object->call("stop");
								t->playing = false;
								playing_caches.erase(t);
							} else {
								float start_ofs = a->audio_track_get_key_start_offset(i, idx);
								start_ofs += time - a->track_get_key_time(i, idx);
								float end_ofs = a->audio_track_get_key_end_offset(i, idx);
								float len = stream->get_length();

								if (start_ofs > len - end_ofs) {
									t->object->call("stop");
									t->playing = false;
									playing_caches.erase(t);
									continue;
								}

								t->object->call("set_stream", stream);
								t->object->call("play", start_ofs);

								t->playing = true;
								playing_caches.insert(t);
								if (len && end_ofs > 0) { //force a end at a time
									t->len = len - start_ofs - end_ofs;
								} else {
									t->len = 0;
								}

								t->start = time;
							}

						} else {
							//find stuff to play
							List<int> to_play;
							a->track_get_key_indices_in_range(i, time, delta, &to_play);
							if (to_play.size()) {
								int idx = to_play.back()->get();

								Ref<AudioStream> stream = a->audio_track_get_key_stream(i, idx);
								if (!stream.is_valid()) {
									t->object->call("stop");
									t->playing = false;
									playing_caches.erase(t);
								} else {
									float start_ofs = a->audio_track_get_key_start_offset(i, idx);
									float end_ofs = a->audio_track_get_key_end_offset(i, idx);
									float len = stream->get_length();

									t->object->call("set_stream", stream);
									t->object->call("play", start_ofs);

									t->playing = true;
									playing_caches.insert(t);
									if (len && end_ofs > 0) { //force a end at a time
										t->len = len - start_ofs - end_ofs;
									} else {
										t->len = 0;
									}

									t->start = time;
								}
							} else if (t->playing) {

								bool loop = a->has_loop();

								bool stop = false;

								if (!loop && time < t->start) {
									stop = true;
								} else if (t->len > 0) {
									float len = t->start > time ? (a->get_length() - t->start) + time : time - t->start;

									if (len > t->len) {
										stop = true;
									}
								}

								if (stop) {
									//time to stop
									t->object->call("stop");
									t->playing = false;
									playing_caches.erase(t);
								}
							}
						}

						float db = Math::linear2db(MAX(blend, 0.00001));
						if (t->object->has_method("set_unit_db")) {
							t->object->call("set_unit_db", db);
						} else {
							t->object->call("set_volume_db", db);
						}
					} break;
					case Animation::TYPE_ANIMATION: {

						TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track);

						AnimationPlayer *player = Object::cast_to<AnimationPlayer>(t->object);

						if (!player)
							continue;

						if (delta == 0 || seeked) {
							//seek
							int idx = a->track_find_key(i, time);
							if (idx < 0)
								continue;

							float pos = a->track_get_key_time(i, idx);

							StringName anim_name = a->animation_track_get_key_animation(i, idx);
							if (String(anim_name) == "[stop]" || !player->has_animation(anim_name))
								continue;

							Ref<Animation> anim = player->get_animation(anim_name);

							float at_anim_pos;

							if (anim->has_loop()) {
								at_anim_pos = Math::fposmod(time - pos, anim->get_length()); //seek to loop
							} else {
								at_anim_pos = MAX(anim->get_length(), time - pos); //seek to end
							}

							if (player->is_playing() || seeked) {
								player->play(anim_name);
								player->seek(at_anim_pos);
								t->playing = true;
								playing_caches.insert(t);
							} else {
								player->set_assigned_animation(anim_name);
								player->seek(at_anim_pos, true);
							}
						} else {
							//find stuff to play
							List<int> to_play;
							a->track_get_key_indices_in_range(i, time, delta, &to_play);
							if (to_play.size()) {
								int idx = to_play.back()->get();

								StringName anim_name = a->animation_track_get_key_animation(i, idx);
								if (String(anim_name) == "[stop]" || !player->has_animation(anim_name)) {

									if (playing_caches.has(t)) {
										playing_caches.erase(t);
										player->stop();
										t->playing = false;
									}
								} else {
									player->play(anim_name);
									t->playing = true;
									playing_caches.insert(t);
								}
							}
						}

					} break;
				}
			}
		}
	}

	{
		// finally, set the tracks
		const NodePath *K = NULL;
		while ((K = track_cache.next(K))) {
			TrackCache *track = track_cache[*K];
			if (track->process_pass != process_pass)
				continue; //not processed, ignore

			switch (track->type) {

				case Animation::TYPE_TRANSFORM: {

					TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track);

					Transform xform;
					xform.origin = t->loc;

					t->scale += Vector3(1.0, 1.0, 1.0); //helps make it work properly with Add nodes and root motion

					xform.basis.set_quat_scale(t->rot, t->scale);

					if (t->root_motion) {

						root_motion_transform = xform;

						if (t->skeleton && t->bone_idx >= 0) {
							root_motion_transform = (t->skeleton->get_bone_rest(t->bone_idx) * root_motion_transform) * t->skeleton->get_bone_rest(t->bone_idx).affine_inverse();
						}
					} else if (t->skeleton && t->bone_idx >= 0) {

						t->skeleton->set_bone_pose(t->bone_idx, xform);

					} else {

						t->spatial->set_transform(xform);
					}

				} break;
				case Animation::TYPE_VALUE: {

					TrackCacheValue *t = static_cast<TrackCacheValue *>(track);

					t->object->set_indexed(t->subpath, t->value);

				} break;
				case Animation::TYPE_BEZIER: {

					TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track);

					t->object->set_indexed(t->subpath, t->value);

				} break;
				default: {} //the rest don't matter
			}
		}
	}
}