Ejemplo n.º 1
0
RID DampedSpringJoint2D::_configure_joint(){


	Node *node_a = has_node( get_node_a() ) ? get_node( get_node_a() ) : (Node*)NULL;
	Node *node_b = has_node( get_node_b() ) ? get_node( get_node_b() ) : (Node*)NULL;

	if (!node_a || !node_b)
		return RID();

	PhysicsBody2D *body_a=node_a->cast_to<PhysicsBody2D>();
	PhysicsBody2D *body_b=node_b->cast_to<PhysicsBody2D>();

	if (!body_a || !body_b)
		return RID();

	if (get_exclude_nodes_from_collision())
		Physics2DServer::get_singleton()->body_add_collision_exception(body_a->get_rid(),body_b->get_rid());
	else
		Physics2DServer::get_singleton()->body_remove_collision_exception(body_a->get_rid(),body_b->get_rid());

	Matrix32 gt = get_global_transform();
	Vector2 anchor_A = gt.get_origin();
	Vector2 anchor_B = gt.xform( Vector2(0,length) );

	RID dsj = Physics2DServer::get_singleton()->damped_spring_joint_create(anchor_A,anchor_B,body_a->get_rid(),body_b->get_rid());
	if (rest_length)
		Physics2DServer::get_singleton()->damped_string_joint_set_param(dsj,Physics2DServer::DAMPED_STRING_REST_LENGTH,rest_length);
	Physics2DServer::get_singleton()->damped_string_joint_set_param(dsj,Physics2DServer::DAMPED_STRING_STIFFNESS,stiffness);
	Physics2DServer::get_singleton()->damped_string_joint_set_param(dsj,Physics2DServer::DAMPED_STRING_DAMPING,damping);

	return dsj;
}
Ejemplo n.º 2
0
RID PinJoint2D::_configure_joint() {

	Node *node_a = has_node( get_node_a() ) ? get_node( get_node_a() ) : (Node*)NULL;
	Node *node_b = has_node( get_node_b() ) ? get_node( get_node_b() ) : (Node*)NULL;

	if (!node_a && !node_b)
		return RID();

	PhysicsBody2D *body_a=node_a ? node_a->cast_to<PhysicsBody2D>() : (PhysicsBody2D*)NULL;
	PhysicsBody2D *body_b=node_b ? node_b->cast_to<PhysicsBody2D>() : (PhysicsBody2D*)NULL;

	if (!body_a && !body_b)
		return RID();

	if (!body_a) {
		SWAP(body_a,body_b);
	} else if (body_b) {
		//add a collision exception between both
		if (get_exclude_nodes_from_collision())
			Physics2DServer::get_singleton()->body_add_collision_exception(body_a->get_rid(),body_b->get_rid());
		else
			Physics2DServer::get_singleton()->body_remove_collision_exception(body_a->get_rid(),body_b->get_rid());
	}
	RID pj = Physics2DServer::get_singleton()->pin_joint_create(get_global_transform().get_origin(),body_a->get_rid(),body_b?body_b->get_rid():RID());
	Physics2DServer::get_singleton()->pin_joint_set_param(pj, Physics2DServer::PIN_JOINT_SOFTNESS, softness);
	return pj;

}
Ejemplo n.º 3
0
RID GrooveJoint2D::_configure_joint(){


	Node *node_a = has_node( get_node_a() ) ? get_node( get_node_a() ) : (Node*)NULL;
	Node *node_b = has_node( get_node_b() ) ? get_node( get_node_b() ) : (Node*)NULL;

	if (!node_a || !node_b)
		return RID();

	PhysicsBody2D *body_a=node_a->cast_to<PhysicsBody2D>();
	PhysicsBody2D *body_b=node_b->cast_to<PhysicsBody2D>();

	if (!body_a || !body_b)
		return RID();


	if (get_exclude_nodes_from_collision())
		Physics2DServer::get_singleton()->body_add_collision_exception(body_a->get_rid(),body_b->get_rid());
	else
		Physics2DServer::get_singleton()->body_remove_collision_exception(body_a->get_rid(),body_b->get_rid());

	Matrix32 gt = get_global_transform();
	Vector2 groove_A1 = gt.get_origin();
	Vector2 groove_A2 = gt.xform( Vector2(0,length) );
	Vector2 anchor_B = gt.xform( Vector2(0,initial_offset) );


	return Physics2DServer::get_singleton()->groove_joint_create(groove_A1,groove_A2,anchor_B,body_a->get_rid(),body_b->get_rid());
}
Ejemplo n.º 4
0
void SoftBody::_add_pinned_point(int p_point_index, const NodePath &p_spatial_attachment_path) {
	SoftBody::PinnedPoint *pinned_point;
	if (-1 == _get_pinned_point(p_point_index, pinned_point)) {

		// Create new
		PinnedPoint pp;
		pp.point_index = p_point_index;
		pp.spatial_attachment_path = p_spatial_attachment_path;

		if (!p_spatial_attachment_path.is_empty() && has_node(p_spatial_attachment_path)) {
			pp.spatial_attachment = Object::cast_to<Spatial>(get_node(p_spatial_attachment_path));
			pp.offset = (pp.spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, pp.point_index));
		}

		pinned_points.push_back(pp);

	} else {

		pinned_point->point_index = p_point_index;
		pinned_point->spatial_attachment_path = p_spatial_attachment_path;

		if (!p_spatial_attachment_path.is_empty() && has_node(p_spatial_attachment_path)) {
			pinned_point->spatial_attachment = Object::cast_to<Spatial>(get_node(p_spatial_attachment_path));
			pinned_point->offset = (pinned_point->spatial_attachment->get_global_transform().affine_inverse() * get_global_transform()).xform(PhysicsServer::get_singleton()->soft_body_get_point_global_position(physics_rid, pinned_point->point_index));
		}
	}
}
Ejemplo n.º 5
0
void graph_t::remove_dependency( node_t *s, node_t *d)
{
	RAMEN_ASSERT( s != d);

	if( has_node( s) && has_node( d))
	{
		vertex_desc_type vs = node_to_desc_[s];
		vertex_desc_type vd = node_to_desc_[d];
		boost::remove_edge( vs, vd, graph_);
		valid_order_ = false;
	}
}
Ejemplo n.º 6
0
void AttackNode::update_attack_area() {
    if (is_inside_tree() && attack_area_path != String("") && has_node(attack_area_path)) {
        attack_area = get_node(attack_area_path)->cast_to<AttackArea>();
    }else {
        attack_area = NULL;
    }
}
Ejemplo n.º 7
0
void SceneTreeEditor::_update_selection(TreeItem *item) {

	ERR_FAIL_COND(!item);

	NodePath np = item->get_metadata(0);

	if (!has_node(np))
		return;

	Node *n=get_node(np);

	if (!n)
		return;

	if (editor_selection->is_selected(n))
		item->select(0);
	else
		item->deselect(0);

	TreeItem *c=item->get_children();


	while(c) {

		_update_selection(c);
		c=c->get_next();
	}
}
Ejemplo n.º 8
0
void ScrollBar::set_drag_slave(const NodePath &p_path) {

	if (is_inside_tree()) {

		if (drag_slave) {
			drag_slave->disconnect("gui_input", this, "_drag_slave_input");
			drag_slave->disconnect("tree_exited", this, "_drag_slave_exit");
		}
	}

	drag_slave = NULL;
	drag_slave_path = p_path;

	if (is_inside_tree()) {

		if (has_node(p_path)) {
			Node *n = get_node(p_path);
			drag_slave = n->cast_to<Control>();
		}

		if (drag_slave) {
			drag_slave->connect("gui_input", this, "_drag_slave_input");
			drag_slave->connect("tree_exited", this, "_drag_slave_exit", varray(), CONNECT_ONESHOT);
		}
	}
}
Ejemplo n.º 9
0
void LinkerBNode::update_link_path() {
    if (is_inside_tree() && link_path != NodePath() && has_node(link_path)) {
        link_target = get_node(link_path)->cast_to<BehaviorNode>();
    }else {
        link_target = NULL;
    }
}
Ejemplo n.º 10
0
String ParticleAttractor2D::get_configuration_warning() const {

	if (!has_node(path) || !get_node(path) || !get_node(path)->cast_to<Particles2D>()) {
		return TTR("Path property must point to a valid Particles2D node to work.");
	}

	return String();
}
Ejemplo n.º 11
0
  boost::filesystem::path add_node(T const &key) {
    assert(not has_node(key));
    typename graph_type::vertex_descriptor v = boost::add_vertex(graph_);
    mapping_[key] = v;

    boost::put(boost::vertex_name_t(), graph_, v, key);
    return key;
  }
Ejemplo n.º 12
0
String RemoteTransform2D::get_configuration_warning() const {

	if (!has_node(remote_node) || !Object::cast_to<Node2D>(get_node(remote_node))) {
		return TTR("Path property must point to a valid Node2D node to work.");
	}

	return String();
}
Ejemplo n.º 13
0
String RemoteTransform::get_configuration_warning() const {

	if (!has_node(remote_node) || !get_node(remote_node) || !get_node(remote_node)->cast_to<Spatial>()) {
		return TTR("Path property must point to a valid Spatial node to work.");
	}

	return String();
}
Ejemplo n.º 14
0
void graph_t::add_node( node_t *v)
{
	RAMEN_ASSERT( !has_node( v));

	vertex_desc_type vd = boost::add_vertex( graph_);
	graph_[vd].node = v;
	node_to_desc_[v] = vd;
	valid_order_ = false;
}
Ejemplo n.º 15
0
void graph_t::add_dependency( node_t *s, node_t *d)
{
	RAMEN_ASSERT( has_node( s) && has_node( d));
	RAMEN_ASSERT( s != d);

	boost::add_edge( node_to_desc_[s], node_to_desc_[d], graph_);

	cycle_detector v;
	boost::depth_first_search( graph_, boost::visitor( v));

	if( v.has_cycle)
	{
		remove_dependency( s, d);
		throw cycle_error();
	}

	valid_order_ = false;
}
Ejemplo n.º 16
0
void graph_t::remove_node( node_t *v)
{
	RAMEN_ASSERT( has_node( v));

	vertex_desc_type vd = node_to_desc_[v];
	boost::clear_vertex( vd, graph_);
	boost::remove_vertex( vd, graph_);
	node_to_desc_.erase( v);
	valid_order_ = false;
}
Ejemplo n.º 17
0
void Joint2D::_update_joint(bool p_only_free) {

	if (joint.is_valid()) {
		if (ba.is_valid() && bb.is_valid())
			Physics2DServer::get_singleton()->body_remove_collision_exception(ba, bb);

		Physics2DServer::get_singleton()->free(joint);
		joint = RID();
		ba = RID();
		bb = RID();
	}

	if (p_only_free || !is_inside_tree())
		return;

	Node *node_a = has_node(get_node_a()) ? get_node(get_node_a()) : (Node *)NULL;
	Node *node_b = has_node(get_node_b()) ? get_node(get_node_b()) : (Node *)NULL;

	if (!node_a || !node_b)
		return;

	PhysicsBody2D *body_a = Object::cast_to<PhysicsBody2D>(node_a);
	PhysicsBody2D *body_b = Object::cast_to<PhysicsBody2D>(node_b);

	if (!body_a || !body_b)
		return;

	if (!body_a) {
		SWAP(body_a, body_b);
	}

	joint = _configure_joint(body_a, body_b);

	if (!joint.is_valid())
		return;

	Physics2DServer::get_singleton()->get_singleton()->joint_set_param(joint, Physics2DServer::JOINT_PARAM_BIAS, bias);

	ba = body_a->get_rid();
	bb = body_b->get_rid();

	Physics2DServer::get_singleton()->joint_disable_collisions_between_bodies(joint, exclude_from_collision);
}
Ejemplo n.º 18
0
void RemoteTransform::_update_cache() {
	cache=0;
	if (has_node(remote_node)) {
		Node *node = get_node(remote_node);
		if (!node || this==node || node->is_a_parent_of(this) || this->is_a_parent_of(node)) {
			return;
		}

		cache=node->get_instance_ID();
	}
}
Ejemplo n.º 19
0
void InterpolatedCamera::_notification(int p_what) {

	switch(p_what) {
		case NOTIFICATION_ENTER_SCENE: {

			if (get_scene()->is_editor_hint() && enabled)
				set_fixed_process(false);

		} break;
		case NOTIFICATION_PROCESS: {

			if (!enabled)
				break;
			if (has_node(target)) {

				Spatial *node = get_node(target)->cast_to<Spatial>();
				if (!node)
					break;

				float delta = speed*get_process_delta_time();
				Transform target_xform = node->get_global_transform();
				Transform local_transform = get_transform();
				local_transform = local_transform.interpolate_with(target_xform,delta);
				set_global_transform(local_transform);

				if (node->cast_to<Camera>()) {
					Camera *cam = node->cast_to<Camera>();
					if (cam->get_projection()==get_projection())  {

						float new_near = Math::lerp(get_znear(),cam->get_znear(),delta);
						float new_far = Math::lerp(get_zfar(),cam->get_zfar(),delta);

						if (cam->get_projection()==PROJECTION_ORTHOGONAL) {

							float size = Math::lerp(get_size(),cam->get_size(),delta);
							set_orthogonal(size,new_near,new_far);
						} else {

							float fov = Math::lerp(get_fov(),cam->get_fov(),delta);
							set_perspective(fov,new_near,new_far);
						}
					}
				}


			}

		} break;
	}
}
Ejemplo n.º 20
0
void place_apple(void)
{
    int x;
    int y;

    do
    {
        x = ARENA_START + 1 + (rand() % (ARENA_SIZE - 1));
        y = ARENA_START + 1 + (rand() % (ARENA_SIZE - 1));
    } while (has_node(x, y));

    f_state.apple.x = x;
    f_state.apple.y = y;
    h3m_object_move(f_state.h3m, f_state.apple.od_index, f_state.apple.x, f_state.apple.y, 0);
}
Ejemplo n.º 21
0
void ParticleAttractor2D::_update_owner() {

	if (!is_inside_tree() || !has_node(path)) {
		_set_owner(NULL);
		return;
	}

	Node *n = get_node(path);
	ERR_FAIL_COND(!n);
	Particles2D *pn = n->cast_to<Particles2D>();
	if (!pn) {
		_set_owner(NULL);
		return;
	}

	_set_owner(pn);
}
Ejemplo n.º 22
0
String AnimationTree::get_configuration_warning() const {

	String warning = Node::get_configuration_warning();

	if (!root.is_valid()) {
		if (warning != String()) {
			warning += "\n";
		}
		warning += TTR("A root AnimationNode for the graph is not set.");
	}

	if (!has_node(animation_player)) {

		if (warning != String()) {
			warning += "\n";
		}

		warning += TTR("Path to an AnimationPlayer node containing animations is not set.");
		return warning;
	}

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

	if (!player) {
		if (warning != String()) {
			warning += "\n";
		}

		warning += TTR("Path set for AnimationPlayer does not lead to an AnimationPlayer node.");
		return warning;
	}

	if (!player->has_node(player->get_root())) {
		if (warning != String()) {
			warning += "\n";
		}

		warning += TTR("AnimationPlayer root is not a valid node.");
		return warning;
	}

	return warning;
}
Ejemplo n.º 23
0
String ViewportSprite::get_configuration_warning() const {

	if (!has_node(viewport_path) || !get_node(viewport_path) || !get_node(viewport_path)->cast_to<Viewport>()) {
		return TTR("Path property must point to a valid Viewport node to work. Such Viewport must be set to 'render target' mode.");
	} else {

		Node *n = get_node(viewport_path);
		if (n) {
			Viewport *vp = n->cast_to<Viewport>();
			if (!vp->is_set_as_render_target()) {

				return TTR("The Viewport set in the path property must be set as 'render target' in order for this sprite to work.");
			}
		}
	}

	return String();

}
Ejemplo n.º 24
0
bool Node::has_node_and_resource(const NodePath& p_path) const {

	if (!has_node(p_path))
		return false;
	Node *node = get_node(p_path);

	if (p_path.get_subname_count()) {

		RES r;
		for(int j=0;j<p_path.get_subname_count();j++) {
			r = j==0 ? node->get(p_path.get_subname(j)) : r->get(p_path.get_subname(j));
			if (r.is_null())
				return false;
		}
	}


	return true;
}
Ejemplo n.º 25
0
  /** Remove a node from the graph.
   * @param[in] n Node to be removed
   * @return 1 if old has_node(n), 0 otherwise
   *
   * @post new size() == old size() - result.
   *
   * Can invalidate outstanding iterators. 
   * If old has_node(@a n), then @a n becomes invalid, as do any
   * other Node objects equal to @a n. All other Node objects remain valid.
   *
   * Complexity: O(i2u_edges_[i].size()^2)
   */
  size_type remove_node ( const Node & n){
    if( !has_node(n) )
      return 0;
    
    idx_type idx = n.index();
    size_type uid = i2u_nodes_[idx];
    
    assert( internal_nodes_[ i2u_nodes_[idx] ].idx == idx );

    // Remove all incident edges before the node is removed.
    idx_type x = 0;
    adj_list_valid_edges v = i2u_edges_[idx];
    while(x < v.size()){
      // uid of each element in v
      size_type v_uid = v[x];
    

      adj_list_valid_edges adj = i2u_edges_[ internal_nodes_[v_uid].idx ];
      
      for(size_type adj_uid : adj)
        if(adj_uid == uid){
          remove_edge(Node(this, v_uid), Node(this, adj_uid));
        }

      ++x;
    }

    // Remove the vector from i2i_edges, so i2u_nodes and i2i_edges are in sync by idx
    i2u_edges_.erase(i2u_edges_.begin() + idx);
    
    // Remove the uid from the list of valid nodes
    i2u_nodes_.erase(i2u_nodes_.begin() + idx);

    // Update the idxs for all nodes subsequent to the removed node
    x = idx;
    while(x < i2u_nodes_.size()){
      internal_nodes_[ i2u_nodes_[x] ].idx = x;
      ++x;
    }

    return 1;
  }
Ejemplo n.º 26
0
int _update_nodes(int x_inc, int y_inc)
{
    struct NODE *n = NULL;
    int i = 0;
    int od_index = 0;
    int x_new = f_state.head->x + x_inc;
    int y_new = f_state.head->y + y_inc;

    if (has_node(x_new, y_new) || x_new < ARENA_START + 1 || y_new < ARENA_START + 1
        || x_new >= ARENA_START + ARENA_SIZE || y_new >= ARENA_START + ARENA_SIZE)
    {
        return 1;
    }

    if (f_state.node_count >= f_state.size)
    {
        _new_head(f_state.tail, x_new, y_new);
        f_state.tail = f_state.tail->next;
        h3m_object_move(f_state.h3m, f_state.head->od_index, f_state.head->x, f_state.head->y, 0);
    }
    else
    {
        n = calloc(1, sizeof(*n));
        _new_head(n, x_new, y_new);

        ++f_state.node_count;

        h3m_object_add(f_state.h3m, "Serpent Fly", n->x, n->y, 0, &n->od_index);
    }

    if (has_apple(x_new, y_new))
    {
        place_apple();
        place_caged();
        ++f_state.score;
        ++f_state.size;
    }

    return 0;
}
Ejemplo n.º 27
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);

				ERR_CONTINUE(!track_cache.has(path));

				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 (track->root_motion) {

							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(1, 1, 1);
							}

							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

							if (t->process_pass != process_pass) {

								t->process_pass = process_pass;
								t->loc = loc;
								t->rot = rot;
								t->rot_blend_accum = 0;
								t->scale = scale;
							}

							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) {
								t->value = value;
								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 *F = indices.front(); F; F = F->next()) {

							StringName method = a->method_track_get_name(i, F->get());
							Vector<Variant> params = a->method_track_get_params(i, F->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 = bezier;
							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 *player2 = Object::cast_to<AnimationPlayer>(t->object);

						if (!player2)
							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]" || !player2->has_animation(anim_name))
								continue;

							Ref<Animation> anim = player2->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 (player2->is_playing() || seeked) {
								player2->play(anim_name);
								player2->seek(at_anim_pos);
								t->playing = true;
								playing_caches.insert(t);
							} else {
								player2->set_assigned_animation(anim_name);
								player2->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]" || !player2->has_animation(anim_name)) {

									if (playing_caches.has(t)) {
										playing_caches.erase(t);
										player2->stop();
										t->playing = false;
									}
								} else {
									player2->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;

					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
			}
		}
	}
}
Ejemplo n.º 28
0
void Polygon2D::_notification(int p_what) {

	switch (p_what) {

		case NOTIFICATION_DRAW: {

			if (polygon.size() < 3)
				return;

			Skeleton2D *skeleton_node = NULL;
			if (has_node(skeleton)) {
				skeleton_node = Object::cast_to<Skeleton2D>(get_node(skeleton));
			}

			ObjectID new_skeleton_id = 0;

			if (skeleton_node) {
				VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), skeleton_node->get_skeleton());
				new_skeleton_id = skeleton_node->get_instance_id();
			} else {
				VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID());
			}

			if (new_skeleton_id != current_skeleton_id) {
				Object *old_skeleton = ObjectDB::get_instance(current_skeleton_id);
				if (old_skeleton) {
					old_skeleton->disconnect("bone_setup_changed", this, "_skeleton_bone_setup_changed");
				}

				if (skeleton_node) {
					skeleton_node->connect("bone_setup_changed", this, "_skeleton_bone_setup_changed");
				}

				current_skeleton_id = new_skeleton_id;
			}

			Vector<Vector2> points;
			Vector<Vector2> uvs;
			Vector<int> bones;
			Vector<float> weights;

			int len = polygon.size();
			if ((invert || polygons.size() == 0) && internal_vertices > 0) {
				//if no polygons are around, internal vertices must not be drawn, else let them be
				len -= internal_vertices;
			}

			if (len <= 0) {
				return;
			}
			points.resize(len);

			{

				PoolVector<Vector2>::Read polyr = polygon.read();
				for (int i = 0; i < len; i++) {
					points.write[i] = polyr[i] + offset;
				}
			}

			if (invert) {

				Rect2 bounds;
				int highest_idx = -1;
				float highest_y = -1e20;
				float sum = 0;

				for (int i = 0; i < len; i++) {
					if (i == 0)
						bounds.position = points[i];
					else
						bounds.expand_to(points[i]);
					if (points[i].y > highest_y) {
						highest_idx = i;
						highest_y = points[i].y;
					}
					int ni = (i + 1) % len;
					sum += (points[ni].x - points[i].x) * (points[ni].y + points[i].y);
				}

				bounds = bounds.grow(invert_border);

				Vector2 ep[7] = {
					Vector2(points[highest_idx].x, points[highest_idx].y + invert_border),
					Vector2(bounds.position + bounds.size),
					Vector2(bounds.position + Vector2(bounds.size.x, 0)),
					Vector2(bounds.position),
					Vector2(bounds.position + Vector2(0, bounds.size.y)),
					Vector2(points[highest_idx].x - CMP_EPSILON, points[highest_idx].y + invert_border),
					Vector2(points[highest_idx].x - CMP_EPSILON, points[highest_idx].y),
				};

				if (sum > 0) {
					SWAP(ep[1], ep[4]);
					SWAP(ep[2], ep[3]);
					SWAP(ep[5], ep[0]);
					SWAP(ep[6], points.write[highest_idx]);
				}

				points.resize(points.size() + 7);
				for (int i = points.size() - 1; i >= highest_idx + 7; i--) {

					points.write[i] = points[i - 7];
				}

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

					points.write[highest_idx + i + 1] = ep[i];
				}

				len = points.size();
			}

			if (texture.is_valid()) {

				Transform2D texmat(tex_rot, tex_ofs);
				texmat.scale(tex_scale);
				Size2 tex_size = texture->get_size();

				uvs.resize(len);

				if (points.size() == uv.size()) {

					PoolVector<Vector2>::Read uvr = uv.read();

					for (int i = 0; i < len; i++) {
						uvs.write[i] = texmat.xform(uvr[i]) / tex_size;
					}

				} else {
					for (int i = 0; i < len; i++) {
						uvs.write[i] = texmat.xform(points[i]) / tex_size;
					}
				}
			}

			if (skeleton_node && !invert && bone_weights.size()) {
				//a skeleton is set! fill indices and weights
				int vc = len;
				bones.resize(vc * 4);
				weights.resize(vc * 4);

				int *bonesw = bones.ptrw();
				float *weightsw = weights.ptrw();

				for (int i = 0; i < vc * 4; i++) {
					bonesw[i] = 0;
					weightsw[i] = 0;
				}

				for (int i = 0; i < bone_weights.size(); i++) {
					if (bone_weights[i].weights.size() != points.size()) {
						continue; //different number of vertices, sorry not using.
					}
					if (!skeleton_node->has_node(bone_weights[i].path)) {
						continue; //node does not exist
					}
					Bone2D *bone = Object::cast_to<Bone2D>(skeleton_node->get_node(bone_weights[i].path));
					if (!bone) {
						continue;
					}

					int bone_index = bone->get_index_in_skeleton();
					PoolVector<float>::Read r = bone_weights[i].weights.read();
					for (int j = 0; j < vc; j++) {
						if (r[j] == 0.0)
							continue; //weight is unpainted, skip
						//find an index with a weight
						for (int k = 0; k < 4; k++) {
							if (weightsw[j * 4 + k] < r[j]) {
								//this is less than this weight, insert weight!
								for (int l = 3; l > k; l--) {
									weightsw[j * 4 + l] = weightsw[j * 4 + l - 1];
									bonesw[j * 4 + l] = bonesw[j * 4 + l - 1];
								}
								weightsw[j * 4 + k] = r[j];
								bonesw[j * 4 + k] = bone_index;
								break;
							}
						}
					}
				}

				//normalize the weights
				for (int i = 0; i < vc; i++) {
					float tw = 0;
					for (int j = 0; j < 4; j++) {
						tw += weightsw[i * 4 + j];
					}
					if (tw == 0)
						continue; //unpainted, do nothing

					//normalize
					for (int j = 0; j < 4; j++) {
						weightsw[i * 4 + j] /= tw;
					}
				}
			}

			Vector<Color> colors;
			if (vertex_colors.size() == points.size()) {
				colors.resize(len);
				PoolVector<Color>::Read color_r = vertex_colors.read();
				for (int i = 0; i < len; i++) {
					colors.write[i] = color_r[i];
				}
			} else {
				colors.push_back(color);
			}

			//			Vector<int> indices = Geometry::triangulate_polygon(points);
			//			VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID());

			if (invert || polygons.size() == 0) {
				Vector<int> indices = Geometry::triangulate_polygon(points);
				VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID());
			} else {
				//draw individual polygons
				Vector<int> total_indices;
				for (int i = 0; i < polygons.size(); i++) {
					PoolVector<int> src_indices = polygons[i];
					int ic = src_indices.size();
					if (ic < 3)
						continue;
					PoolVector<int>::Read r = src_indices.read();

					Vector<Vector2> tmp_points;
					tmp_points.resize(ic);

					for (int j = 0; j < ic; j++) {
						int idx = r[j];
						ERR_CONTINUE(idx < 0 || idx >= points.size());
						tmp_points.write[j] = points[r[j]];
					}
					Vector<int> indices = Geometry::triangulate_polygon(tmp_points);
					int ic2 = indices.size();
					const int *r2 = indices.ptr();

					int bic = total_indices.size();
					total_indices.resize(bic + ic2);
					int *w2 = total_indices.ptrw();

					for (int j = 0; j < ic2; j++) {
						w2[j + bic] = r[r2[j]];
					}
				}

				if (total_indices.size()) {
					VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), total_indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID());
				}

#if 0
				//use splits
				Vector<int> loop;
				int sc = splits.size();
				PoolVector<int>::Read r = splits.read();


				print_line("has splits, amount " + itos(splits.size()));
				Vector<Vector<int> > loops;

				// find a point that can be used to begin, must not be in a split, and have to the left and right the same one
				// like this one -> x---o
				//                   \ / \ .
				//                    o---o
				int base_point = -1;
				{
					int current_point = -1;
					int base_point_prev_split = -1;


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

						//find if this point is in a split
						int split_index = -1;
						bool has_prev_split = false;
						int min_dist_to_end = 0x7FFFFFFF;

						for (int j = 0; j < sc; j += 2) {

							int split_pos = -1;
							int split_end = -1;

							if (r[j + 0] == i) { //found split in first point
								split_pos = r[j + 0];
								split_end = r[j + 1];
							} else if (r[j + 1] == i) { //found split in second point
								split_pos = r[j + 1];
								split_end = r[j + 0];
							}

							if (split_pos == split_end) {
								continue; //either nothing found or begin == end, this not a split in either case
							}

							if (j == base_point_prev_split) {
								has_prev_split = true;
							}

							//compute distance from split pos to split end in current traversal direction
							int dist_to_end = split_end > split_pos ? split_end - split_pos : (last - split_pos + split_end);

							if (dist_to_end < min_dist_to_end) {
								//always keep the valid split with the least distance to the loop
								min_dist_to_end = dist_to_end;
								split_index = j;
							}
						}

						if (split_index == -1) {
							current_point = i; //no split here, we are testing this point
						} else if (has_prev_split) {
							base_point = current_point; // there is a split and it contains the previous visited split, success
							break;
						} else {
							//invalidate current point and keep split
							current_point = -1;
							base_point_prev_split = split_index;
						}
					}
				}

				print_line("found base point: " + itos(base_point));

				if (base_point != -1) {

					int point = base_point;
					int last = base_point;
					//go through all the points, find splits
					do {

						int split;
						int last_dist_to_end = -1; //maximum valid distance to end

						do {

							loop.push_back(point); //push current point

							split = -1;
							int end = -1;

							int max_dist_to_end = 0;

							//find if this point is in a split
							for (int j = 0; j < sc; j += 2) {

								int split_pos = -1;
								int split_end = -1;

								if (r[j + 0] == point) { //match first split index
									split_pos = r[j + 0];
									split_end = r[j + 1];
								} else if (r[j + 1] == point) { //match second split index
									split_pos = r[j + 1];
									split_end = r[j + 0];
								}

								if (split_pos == split_end) {
									continue; //either nothing found or begin == end, this not a split in either case
								}

								//compute distance from split pos to split end
								int dist_to_end = split_end > split_pos ? split_end - split_pos : (points.size() - split_pos + split_end);

								if (last_dist_to_end != -1 && dist_to_end >= last_dist_to_end) {
									//distance must be shorter than in last iteration, means we've tested this before so ignore
									continue;
								} else if (dist_to_end > max_dist_to_end) {
									//always keep the valid point with the most distance (as long as it's valid)
									max_dist_to_end = dist_to_end;
									split = split_pos;
									end = split_end;
								}
							}

							if (split != -1) {
								//found a split!
								int from = end;

								//add points until last is reached
								while (true) {
									//find if point is in a split
									loop.push_back(from);

									if (from == last) {
										break;
									}

									from++;
									if (from >= points.size()) { //wrap if reached end
										from = 0;
									}

									if (from == loop[0]) {
										break; //end because we reached split source
									}
								}

								loops.push_back(loop); //done with this loop
								loop.clear();

								last_dist_to_end = max_dist_to_end;
								last = end; //algorithm can safely finish in this split point
							}

						} while (split != -1);

					} while (point != last);
				}

				if (loop.size() >=2 ) { //points remained
					//points remain
					loop.push_back(last); //no splits found, use last
					loops.push_back(loop);
				}

				print_line("total loops: " + itos(loops.size()));

				if (loops.size()) { //loops found
					Vector<int> indices;

					for (int i = 0; i < loops.size(); i++) {
						Vector<int> loop = loops[i];
						Vector<Vector2> vertices;
						vertices.resize(loop.size());
						for (int j = 0; j < vertices.size(); j++) {
							vertices.write[j] = points[loop[j]];
						}
						Vector<int> sub_indices = Geometry::triangulate_polygon(vertices);
						int from = indices.size();
						indices.resize(from + sub_indices.size());
						for (int j = 0; j < sub_indices.size(); j++) {
							indices.write[from + j] = loop[sub_indices[j]];
						}
					}

					VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID());
				}
#endif
			}

		} break;
	}
}
Ejemplo n.º 29
0
 void add_edge(T const &from, T const &to) {
   assert(has_node(from));
   assert(has_node(to));
   boost::add_edge(mapping_[from], mapping_[to], graph_);
 }
Ejemplo n.º 30
0
void ScrollBar::_notification(int p_what) {

	if (p_what == NOTIFICATION_DRAW) {

		RID ci = get_canvas_item();

		Ref<Texture> decr = highlight == HIGHLIGHT_DECR ? get_icon("decrement_highlight") : get_icon("decrement");
		Ref<Texture> incr = highlight == HIGHLIGHT_INCR ? get_icon("increment_highlight") : get_icon("increment");
		Ref<StyleBox> bg = has_focus() ? get_stylebox("scroll_focus") : get_stylebox("scroll");

		Ref<StyleBox> grabber;
		if (drag.active)
			grabber = get_stylebox("grabber_pressed");
		else if (highlight == HIGHLIGHT_RANGE)
			grabber = get_stylebox("grabber_highlight");
		else
			grabber = get_stylebox("grabber");

		Point2 ofs;

		VisualServer *vs = VisualServer::get_singleton();

		vs->canvas_item_add_texture_rect(ci, Rect2(Point2(), decr->get_size()), decr->get_rid());

		if (orientation == HORIZONTAL)
			ofs.x += decr->get_width();
		else
			ofs.y += decr->get_height();

		Size2 area = get_size();

		if (orientation == HORIZONTAL)
			area.width -= incr->get_width() + decr->get_width();
		else
			area.height -= incr->get_height() + decr->get_height();

		bg->draw(ci, Rect2(ofs, area));

		if (orientation == HORIZONTAL)
			ofs.width += area.width;
		else
			ofs.height += area.height;

		vs->canvas_item_add_texture_rect(ci, Rect2(ofs, decr->get_size()), incr->get_rid());
		Rect2 grabber_rect;

		if (orientation == HORIZONTAL) {

			grabber_rect.size.width = get_grabber_size();
			grabber_rect.size.height = get_size().height;
			grabber_rect.position.y = 0;
			grabber_rect.position.x = get_grabber_offset() + decr->get_width() + bg->get_margin(MARGIN_LEFT);
		} else {

			grabber_rect.size.width = get_size().width;
			grabber_rect.size.height = get_grabber_size();
			grabber_rect.position.y = get_grabber_offset() + decr->get_height() + bg->get_margin(MARGIN_TOP);
			grabber_rect.position.x = 0;
		}

		grabber->draw(ci, grabber_rect);
	}

	if (p_what == NOTIFICATION_ENTER_TREE) {

		if (has_node(drag_slave_path)) {
			Node *n = get_node(drag_slave_path);
			drag_slave = n->cast_to<Control>();
		}

		if (drag_slave) {
			drag_slave->connect("gui_input", this, "_drag_slave_input");
			drag_slave->connect("tree_exited", this, "_drag_slave_exit", varray(), CONNECT_ONESHOT);
		}
	}
	if (p_what == NOTIFICATION_EXIT_TREE) {

		if (drag_slave) {
			drag_slave->disconnect("gui_input", this, "_drag_slave_input");
			drag_slave->disconnect("tree_exited", this, "_drag_slave_exit");
		}

		drag_slave = NULL;
	}

	if (p_what == NOTIFICATION_FIXED_PROCESS) {

		if (scrolling) {
			if (get_value() != target_scroll) {
				double target = target_scroll - get_value();
				double dist = sqrt(target * target);
				double vel = ((target / dist) * 500) * get_fixed_process_delta_time();

				if (vel >= dist) {
					set_value(target_scroll);
				} else {
					set_value(get_value() + vel);
				}
			} else {
				scrolling = false;
				set_fixed_process(false);
			}
		} else if (drag_slave_touching) {

			if (drag_slave_touching_deaccel) {

				Vector2 pos = Vector2(orientation == HORIZONTAL ? get_value() : 0, orientation == VERTICAL ? get_value() : 0);
				pos += drag_slave_speed * get_fixed_process_delta_time();

				bool turnoff = false;

				if (orientation == HORIZONTAL) {

					if (pos.x < 0) {
						pos.x = 0;
						turnoff = true;
					}

					if (pos.x > (get_max() - get_page())) {
						pos.x = get_max() - get_page();
						turnoff = true;
					}

					set_value(pos.x);

					float sgn_x = drag_slave_speed.x < 0 ? -1 : 1;
					float val_x = Math::abs(drag_slave_speed.x);
					val_x -= 1000 * get_fixed_process_delta_time();

					if (val_x < 0) {
						turnoff = true;
					}

					drag_slave_speed.x = sgn_x * val_x;

				} else {

					if (pos.y < 0) {
						pos.y = 0;
						turnoff = true;
					}

					if (pos.y > (get_max() - get_page())) {
						pos.y = get_max() - get_page();
						turnoff = true;
					}

					set_value(pos.y);

					float sgn_y = drag_slave_speed.y < 0 ? -1 : 1;
					float val_y = Math::abs(drag_slave_speed.y);
					val_y -= 1000 * get_fixed_process_delta_time();

					if (val_y < 0) {
						turnoff = true;
					}
					drag_slave_speed.y = sgn_y * val_y;
				}

				if (turnoff) {
					set_fixed_process(false);
					drag_slave_touching = false;
					drag_slave_touching_deaccel = false;
				}

			} else {

				if (time_since_motion == 0 || time_since_motion > 0.1) {

					Vector2 diff = drag_slave_accum - last_drag_slave_accum;
					last_drag_slave_accum = drag_slave_accum;
					drag_slave_speed = diff / get_fixed_process_delta_time();
				}

				time_since_motion += get_fixed_process_delta_time();
			}
		}
	}

	if (p_what == NOTIFICATION_MOUSE_EXIT) {

		highlight = HIGHLIGHT_NONE;
		update();
	}
}