Beispiel #1
0
void AnimationTreePlayer::_update_sources() {

	if (master==NodePath())
		return;
	if (!is_inside_tree())
		return;

	Node *m = get_node(master);
	if (!m) {
		master=NodePath();
		ERR_FAIL_COND(!m);
	}

	AnimationPlayer *ap = m->cast_to<AnimationPlayer>();

	if (!ap) {

		master=NodePath();
		ERR_FAIL_COND(!ap);
	}

	for (Map<StringName,NodeBase*>::Element *E=node_map.front();E;E=E->next()) {

		if (E->get()->type==NODE_ANIMATION) {

			AnimationNode *an = static_cast<AnimationNode*>(E->get());

			if (an->from!="") {

				an->animation = ap->get_animation(an->from);
			}
		}
	}


}
Node* EditorSceneImportPlugin::_fix_node(Node *p_node,Node *p_root,Map<Ref<Mesh>,Ref<Shape> > &collision_map,uint32_t p_flags,Set<Ref<ImageTexture> >& image_map) {

	// children first..
	for(int i=0;i<p_node->get_child_count();i++) {


		Node *r = _fix_node(p_node->get_child(i),p_root,collision_map,p_flags,image_map);
		if (!r) {
			print_line("was erased..");
			i--; //was erased
		}
	}

	String name = p_node->get_name();

	bool isroot = p_node==p_root;


	if (!isroot && p_flags&SCENE_FLAG_REMOVE_NOIMP && _teststr(name,"noimp")) {

		memdelete(p_node);
		return NULL;
	}

	{

		List<PropertyInfo> pl;
		p_node->get_property_list(&pl);
		for(List<PropertyInfo>::Element *E=pl.front();E;E=E->next()) {

			if (E->get().type==Variant::OBJECT || E->get().type==Variant::ARRAY || E->get().type==Variant::DICTIONARY) {
				_find_resources(p_node->get(E->get().name),image_map);
			}
		}

	}




	if (p_flags&SCENE_FLAG_CREATE_BILLBOARDS && p_node->cast_to<MeshInstance>()) {

		MeshInstance *mi = p_node->cast_to<MeshInstance>();

		bool bb=false;

		if ((_teststr(name,"bb"))) {
			bb=true;
		} else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"bb"))) {
			bb=true;

		}

		if (bb) {
			mi->set_flag(GeometryInstance::FLAG_BILLBOARD,true);
			if (mi->get_mesh().is_valid()) {

				Ref<Mesh> m = mi->get_mesh();
				for(int i=0;i<m->get_surface_count();i++) {

					Ref<FixedMaterial> fm = m->surface_get_material(i);
					if (fm.is_valid()) {
						fm->set_flag(Material::FLAG_UNSHADED,true);
						fm->set_flag(Material::FLAG_DOUBLE_SIDED,true);
						fm->set_hint(Material::HINT_NO_DEPTH_DRAW,true);
						fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true);
					}
				}
			}
		}
	}

	if (p_flags&SCENE_FLAG_REMOVE_NOIMP && p_node->cast_to<AnimationPlayer>()) {
		//remove animations referencing non-importable nodes
		AnimationPlayer *ap = p_node->cast_to<AnimationPlayer>();

		List<StringName> anims;
		ap->get_animation_list(&anims);
		for(List<StringName>::Element *E=anims.front();E;E=E->next()) {

			Ref<Animation> anim=ap->get_animation(E->get());
			ERR_CONTINUE(anim.is_null());
			for(int i=0;i<anim->get_track_count();i++) {
				NodePath path = anim->track_get_path(i);

				for(int j=0;j<path.get_name_count();j++) {
					String node = path.get_name(j);
					if (_teststr(node,"noimp")) {
						anim->remove_track(i);
						i--;
						break;
					}
				}
			}

		}
	}


	if (p_flags&SCENE_FLAG_CREATE_IMPOSTORS && p_node->cast_to<MeshInstance>()) {

		MeshInstance *mi = p_node->cast_to<MeshInstance>();

		String str;

		if ((_teststr(name,"imp"))) {
			str=name;
		} else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"imp"))) {
			str=mi->get_mesh()->get_name();

		}


		if (p_node->get_parent() && p_node->get_parent()->cast_to<MeshInstance>()) {
			MeshInstance *mi = p_node->cast_to<MeshInstance>();
			MeshInstance *mip = p_node->get_parent()->cast_to<MeshInstance>();
			String d=str.substr(str.find("imp")+3,str.length());
			if (d!="") {
				if ((d[0]<'0' || d[0]>'9'))
					d=d.substr(1,d.length());
				if (d.length() && d[0]>='0' && d[0]<='9') {
					float dist = d.to_double();
					mi->set_flag(GeometryInstance::FLAG_BILLBOARD,true);
					mi->set_flag(GeometryInstance::FLAG_BILLBOARD_FIX_Y,true);
					mi->set_draw_range_begin(dist);
					mi->set_draw_range_end(100000);

					mip->set_draw_range_begin(0);
					mip->set_draw_range_end(dist);

					if (mi->get_mesh().is_valid()) {

						Ref<Mesh> m = mi->get_mesh();
						for(int i=0;i<m->get_surface_count();i++) {

							Ref<FixedMaterial> fm = m->surface_get_material(i);
							if (fm.is_valid()) {
								fm->set_flag(Material::FLAG_UNSHADED,true);
								fm->set_flag(Material::FLAG_DOUBLE_SIDED,true);
								fm->set_hint(Material::HINT_NO_DEPTH_DRAW,true);
								fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true);
							}
						}
					}
				}
			}
		}
	}

    if (p_flags&SCENE_FLAG_CREATE_LODS && p_node->cast_to<MeshInstance>()) {

	MeshInstance *mi = p_node->cast_to<MeshInstance>();

	String str;

	if ((_teststr(name,"lod"))) {
	    str=name;
	} else if (mi->get_mesh().is_valid() && (_teststr(mi->get_mesh()->get_name(),"lod"))) {
	    str=mi->get_mesh()->get_name();

	}


	if (p_node->get_parent() && p_node->get_parent()->cast_to<MeshInstance>()) {
	    MeshInstance *mi = p_node->cast_to<MeshInstance>();
	    MeshInstance *mip = p_node->get_parent()->cast_to<MeshInstance>();
	    String d=str.substr(str.find("lod")+3,str.length());
	    if (d!="") {
		if ((d[0]<'0' || d[0]>'9'))
		    d=d.substr(1,d.length());
		if (d.length() && d[0]>='0' && d[0]<='9') {
		    float dist = d.to_double();
		    mi->set_draw_range_begin(dist);
		    mi->set_draw_range_end(100000);

		    mip->set_draw_range_begin(0);
		    mip->set_draw_range_end(dist);

		    /*if (mi->get_mesh().is_valid()) {

			Ref<Mesh> m = mi->get_mesh();
			for(int i=0;i<m->get_surface_count();i++) {

			    Ref<FixedMaterial> fm = m->surface_get_material(i);
			    if (fm.is_valid()) {
				fm->set_flag(Material::FLAG_UNSHADED,true);
				fm->set_flag(Material::FLAG_DOUBLE_SIDED,true);
				fm->set_hint(Material::HINT_NO_DEPTH_DRAW,true);
				fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true);
			    }
			}
		    }*/
		}
	    }
	}
    }
	if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(name,"colonly") && p_node->cast_to<MeshInstance>()) {

		if (isroot)
			return p_node;

		MeshInstance *mi = p_node->cast_to<MeshInstance>();
		Node * col = mi->create_trimesh_collision_node();

		ERR_FAIL_COND_V(!col,NULL);
		col->set_name(_fixstr(name,"colonly"));
		col->cast_to<Spatial>()->set_transform(mi->get_transform());
		p_node->replace_by(col);
		memdelete(p_node);
		p_node=col;

	} else if (p_flags&SCENE_FLAG_CREATE_COLLISIONS &&_teststr(name,"col") && p_node->cast_to<MeshInstance>()) {


		MeshInstance *mi = p_node->cast_to<MeshInstance>();

		mi->set_name(_fixstr(name,"col"));
		mi->create_trimesh_collision();
	} else if (p_flags&SCENE_FLAG_CREATE_ROOMS && _teststr(name,"room") && p_node->cast_to<MeshInstance>()) {


		if (isroot)
			return p_node;

		MeshInstance *mi = p_node->cast_to<MeshInstance>();
		DVector<Face3> faces = mi->get_faces(VisualInstance::FACES_SOLID);


		BSP_Tree bsptree(faces);

		Ref<RoomBounds> area = memnew( RoomBounds );
		area->set_bounds(faces);
		area->set_geometry_hint(faces);


		Room * room = memnew( Room );
		room->set_name(_fixstr(name,"room"));
		room->set_transform(mi->get_transform());
		room->set_room(area);

		p_node->replace_by(room);
		memdelete(p_node);
		p_node=room;

	} else if (p_flags&SCENE_FLAG_CREATE_ROOMS &&_teststr(name,"room")) {

		if (isroot)
			return p_node;

		Spatial *dummy = p_node->cast_to<Spatial>();
		ERR_FAIL_COND_V(!dummy,NULL);

		Room * room = memnew( Room );
		room->set_name(_fixstr(name,"room"));
		room->set_transform(dummy->get_transform());

		p_node->replace_by(room);
		memdelete(p_node);
		p_node=room;

		room->compute_room_from_subtree();

	} else if (p_flags&SCENE_FLAG_CREATE_PORTALS &&_teststr(name,"portal") && p_node->cast_to<MeshInstance>()) {

		if (isroot)
			return p_node;

		MeshInstance *mi = p_node->cast_to<MeshInstance>();
		DVector<Face3> faces = mi->get_faces(VisualInstance::FACES_SOLID);

		ERR_FAIL_COND_V(faces.size()==0,NULL);
		//step 1 compute the plane
		Set<Vector3> points;
		Plane plane;

		Vector3 center;

		for(int i=0;i<faces.size();i++) {

			Face3 f = faces.get(i);
			Plane p = f.get_plane();
			plane.normal+=p.normal;
			plane.d+=p.d;

			for(int i=0;i<3;i++) {

				Vector3 v = f.vertex[i].snapped(0.01);
				if (!points.has(v)) {
					points.insert(v);
					center+=v;
				}
			}
		}

		plane.normal.normalize();
		plane.d/=faces.size();
		center/=points.size();

		//step 2, create points

		Transform t;
		t.basis.from_z(plane.normal);
		t.basis.transpose();
		t.origin=center;

		Vector<Point2> portal_points;

		for(Set<Vector3>::Element *E=points.front();E;E=E->next()) {

			Vector3 local = t.xform_inv(E->get());
			portal_points.push_back(Point2(local.x,local.y));
		}
		// step 3 bubbly sort points

		int swaps=0;

		do {
			swaps=0;

			for(int i=0;i<portal_points.size()-1;i++) {

				float a = portal_points[i].atan2();
				float b = portal_points[i+1].atan2();

				if (a>b) {
					SWAP( portal_points[i], portal_points[i+1] );
					swaps++;
				}

			}

		} while(swaps);


		Portal *portal = memnew( Portal );

		portal->set_shape(portal_points);
		portal->set_transform( mi->get_transform() * t);

		p_node->replace_by(portal);
		memdelete(p_node);
		p_node=portal;

	} else if (p_node->cast_to<MeshInstance>()) {

		//last attempt, maybe collision insde the mesh data

		MeshInstance *mi = p_node->cast_to<MeshInstance>();

		Ref<Mesh> mesh = mi->get_mesh();
		if (!mesh.is_null()) {

			if (p_flags&SCENE_FLAG_CREATE_COLLISIONS && _teststr(mesh->get_name(),"col")) {

				mesh->set_name( _fixstr(mesh->get_name(),"col") );
				Ref<Shape> shape;

				if (collision_map.has(mesh)) {
					shape = collision_map[mesh];

				} else {

					shape = mesh->create_trimesh_shape();
					if (!shape.is_null())
						collision_map[mesh]=shape;


				}

				if (!shape.is_null()) {
#if 0
					StaticBody* static_body = memnew( StaticBody );
					ERR_FAIL_COND_V(!static_body,NULL);
					static_body->set_name( String(mesh->get_name()) + "_col" );
					shape->set_name(static_body->get_name());
					static_body->add_shape(shape);

					mi->add_child(static_body);
					if (mi->get_owner())
						static_body->set_owner( mi->get_owner() );
#endif
				}

			}

			for(int i=0;i<mesh->get_surface_count();i++) {

				Ref<FixedMaterial> fm = mesh->surface_get_material(i);
				if (fm.is_valid()) {
					String name = fm->get_name();
					if (_teststr(name,"alpha")) {
						fm->set_fixed_flag(FixedMaterial::FLAG_USE_ALPHA,true);
						name=_fixstr(name,"alpha");
					}

					if (_teststr(name,"vcol")) {
						fm->set_fixed_flag(FixedMaterial::FLAG_USE_COLOR_ARRAY,true);
						name=_fixstr(name,"vcol");
					}
					fm->set_name(name);
				}
			}

		}

	}




	return p_node;
}
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);
	}
}
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());
						}
					}
				}
			}
		}
	}
}
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;
}
Beispiel #6
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
			}
		}
	}
}
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();
}