示例#1
0
collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
		f32 pos_max_d, const core::aabbox3d<f32> &box_0,
		f32 dtime, v3f &pos_f, v3f &speed_f)
{
	collisionMoveResult final_result;

	// Maximum time increment (for collision detection etc)
	// time = distance / speed
	f32 dtime_max_increment = pos_max_d / speed_f.getLength();
	
	// Maximum time increment is 10ms or lower
	if(dtime_max_increment > 0.01)
		dtime_max_increment = 0.01;
	
	// Don't allow overly huge dtime
	if(dtime > 2.0)
		dtime = 2.0;
	
	f32 dtime_downcount = dtime;

	u32 loopcount = 0;
	do
	{
		loopcount++;

		f32 dtime_part;
		if(dtime_downcount > dtime_max_increment)
		{
			dtime_part = dtime_max_increment;
			dtime_downcount -= dtime_part;
		}
		else
		{
			dtime_part = dtime_downcount;
			/*
				Setting this to 0 (no -=dtime_part) disables an infinite loop
				when dtime_part is so small that dtime_downcount -= dtime_part
				does nothing
			*/
			dtime_downcount = 0;
		}

		collisionMoveResult result = collisionMoveSimple(map, gamedef,
				pos_max_d, box_0, dtime_part, pos_f, speed_f);

		if(result.touching_ground)
			final_result.touching_ground = true;
		if(result.collides)
			final_result.collides = true;
	}
	while(dtime_downcount > 0.001);
		

	return final_result;
}
示例#2
0
collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
		f32 pos_max_d, const aabb3f &box_0,
		f32 stepheight, f32 dtime,
		v3f &pos_f, v3f &speed_f,
		v3f &accel_f,ActiveObject* self,
		bool collideWithObjects)
{
	Map *map = &env->getMap();
	//TimeTaker tt("collisionMoveSimple");
    //ScopeProfiler sp(g_profiler, "collisionMoveSimple avg", SPT_AVG);

	collisionMoveResult result;

	/*
		Calculate new velocity
	*/
	if( dtime > 1 ) {
/*
		infostream<<"collisionMoveSimple: WARNING: maximum step interval exceeded, lost movement details!"<<std::endl;
*/
		dtime = 1;
	}
	speed_f += accel_f * dtime;

	// If there is no speed, there are no collisions
	if(speed_f.getLength() == 0)
		return result;

	// Limit speed for avoiding hangs
	speed_f.Y=rangelim(speed_f.Y,-1000,1000);
	speed_f.X=rangelim(speed_f.X,-1000,1000);
	speed_f.Z=rangelim(speed_f.Z,-1000,1000);

	/*
		Collect node boxes in movement range
	*/
	std::vector<aabb3f> cboxes;
	std::vector<bool> is_unloaded;
	std::vector<bool> is_step_up;
	std::vector<bool> is_object;
	std::vector<int> bouncy_values;
	std::vector<v3s16> node_positions;
	{
	//TimeTaker tt2("collisionMoveSimple collect boxes");
    //ScopeProfiler sp(g_profiler, "collisionMoveSimple collect boxes avg", SPT_AVG);

	v3s16 oldpos_i = floatToInt(pos_f, BS);
	v3s16 newpos_i = floatToInt(pos_f + speed_f * dtime, BS);
	s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
	s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
	s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
	s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1;
	s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
	s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;

	for(s16 x = min_x; x <= max_x; x++)
	for(s16 y = min_y; y <= max_y; y++)
	for(s16 z = min_z; z <= max_z; z++)
	{
		v3s16 p(x,y,z);

		bool is_position_valid;
		MapNode n = map->getNodeNoEx(p, &is_position_valid);

		if (is_position_valid) {
			// Object collides into walkable nodes

			const ContentFeatures &f = gamedef->getNodeDefManager()->get(n);
			if(f.walkable == false)
				continue;
			int n_bouncy_value = itemgroup_get(f.groups, "bouncy");

			std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(gamedef->ndef());
			for(std::vector<aabb3f>::iterator
					i = nodeboxes.begin();
					i != nodeboxes.end(); ++i)
			{
				aabb3f box = *i;
				box.MinEdge += v3f(x, y, z)*BS;
				box.MaxEdge += v3f(x, y, z)*BS;
				cboxes.push_back(box);
				is_unloaded.push_back(false);
				is_step_up.push_back(false);
				bouncy_values.push_back(n_bouncy_value);
				node_positions.push_back(p);
				is_object.push_back(false);
			}
		}
		else {
			// Collide with unloaded nodes
			aabb3f box = getNodeBox(p, BS);
			cboxes.push_back(box);
			is_unloaded.push_back(true);
			is_step_up.push_back(false);
			bouncy_values.push_back(0);
			node_positions.push_back(p);
			is_object.push_back(false);
		}
	}
	} // tt2

	if(collideWithObjects)
	{
		//ScopeProfiler sp(g_profiler, "collisionMoveSimple objects avg", SPT_AVG);
		//TimeTaker tt3("collisionMoveSimple collect object boxes");

		/* add object boxes to cboxes */


		std::vector<ActiveObject*> objects;
#ifndef SERVER
		ClientEnvironment *c_env = dynamic_cast<ClientEnvironment*>(env);
		if (c_env != 0) {
			f32 distance = speed_f.getLength();
			std::vector<DistanceSortedActiveObject> clientobjects;
			c_env->getActiveObjects(pos_f,distance * 1.5,clientobjects);
			for (size_t i=0; i < clientobjects.size(); i++) {
				if ((self == 0) || (self != clientobjects[i].obj)) {
					objects.push_back((ActiveObject*)clientobjects[i].obj);
				}
			}
		}
		else
#endif
		{
			ServerEnvironment *s_env = dynamic_cast<ServerEnvironment*>(env);
			if (s_env != 0) {
				f32 distance = speed_f.getLength();
				std::vector<u16> s_objects;
				s_env->getObjectsInsideRadius(s_objects, pos_f, distance * 1.5);
				for (std::vector<u16>::iterator iter = s_objects.begin(); iter != s_objects.end(); ++iter) {
					ServerActiveObject *current = s_env->getActiveObject(*iter);
					if ((self == 0) || (self != current)) {
						objects.push_back((ActiveObject*)current);
					}
				}
			}
		}

		for (std::vector<ActiveObject*>::const_iterator iter = objects.begin();
				iter != objects.end(); ++iter) {
			ActiveObject *object = *iter;

			if (object != NULL) {
				aabb3f object_collisionbox;
				if (object->getCollisionBox(&object_collisionbox) &&
						object->collideWithObjects()) {
					cboxes.push_back(object_collisionbox);
					is_unloaded.push_back(false);
					is_step_up.push_back(false);
					bouncy_values.push_back(0);
					node_positions.push_back(v3s16(0,0,0));
					is_object.push_back(true);
				}
			}
		}
	} //tt3

/*
	assert(cboxes.size() == is_unloaded.size());    // post-condition
	assert(cboxes.size() == is_step_up.size());     // post-condition
	assert(cboxes.size() == bouncy_values.size());  // post-condition
	assert(cboxes.size() == node_positions.size()); // post-condition
	assert(cboxes.size() == is_object.size());      // post-condition
*/

	/*
		Collision detection
	*/

	/*
		Collision uncertainty radius
		Make it a bit larger than the maximum distance of movement
	*/
	f32 d = pos_max_d * 1.1;
	// A fairly large value in here makes moving smoother
	//f32 d = 0.15*BS;

	// This should always apply, otherwise there are glitches
	if(!(d > pos_max_d))
		return result;

	int loopcount = 0;

	while(dtime > BS*1e-10)
	{
		//TimeTaker tt3("collisionMoveSimple dtime loop");
        //ScopeProfiler sp(g_profiler, "collisionMoveSimple dtime loop avg", SPT_AVG);

		// Avoid infinite loop
		loopcount++;
		if(loopcount >= 100)
		{
			infostream<<"collisionMoveSimple: WARNING: Loop count exceeded, aborting to avoid infiniite loop"<<std::endl;
			dtime = 0;
			break;
		}

		aabb3f movingbox = box_0;
		movingbox.MinEdge += pos_f;
		movingbox.MaxEdge += pos_f;

		int nearest_collided = -1;
		f32 nearest_dtime = dtime;
		u32 nearest_boxindex = -1;

		/*
			Go through every nodebox, find nearest collision
		*/
		for(u32 boxindex = 0; boxindex < cboxes.size(); boxindex++)
		{
			// Ignore if already stepped up this nodebox.
			if(is_step_up[boxindex])
				continue;

			// Find nearest collision of the two boxes (raytracing-like)
			f32 dtime_tmp;
			int collided = axisAlignedCollision(
					cboxes[boxindex], movingbox, speed_f, d, dtime_tmp);

			if(collided == -1 || dtime_tmp >= nearest_dtime)
				continue;

			nearest_dtime = dtime_tmp;
			nearest_collided = collided;
			nearest_boxindex = boxindex;
		}

		if(nearest_collided == -1)
		{
			// No collision with any collision box.
			pos_f += speed_f * dtime;
			dtime = 0;  // Set to 0 to avoid "infinite" loop due to small FP numbers
		}
		else
		{
			// Otherwise, a collision occurred.

			const aabb3f& cbox = cboxes[nearest_boxindex];

			// Check for stairs.
			bool step_up = (nearest_collided != 1) && // must not be Y direction
					(movingbox.MinEdge.Y < cbox.MaxEdge.Y) &&
					(movingbox.MinEdge.Y + stepheight > cbox.MaxEdge.Y) &&
					(!wouldCollideWithCeiling(cboxes, movingbox,
							cbox.MaxEdge.Y - movingbox.MinEdge.Y,
							d));

			// Get bounce multiplier
			bool bouncy = (bouncy_values[nearest_boxindex] >= 1);
			float bounce = -(float)bouncy_values[nearest_boxindex] / 100.0;

			// Move to the point of collision and reduce dtime by nearest_dtime
			if(nearest_dtime < 0)
			{
				// Handle negative nearest_dtime (can be caused by the d allowance)
				if(!step_up)
				{
					if(nearest_collided == 0)
						pos_f.X += speed_f.X * nearest_dtime;
					if(nearest_collided == 1)
						pos_f.Y += speed_f.Y * nearest_dtime;
					if(nearest_collided == 2)
						pos_f.Z += speed_f.Z * nearest_dtime;
				}
			}
			else
			{
				pos_f += speed_f * nearest_dtime;
				dtime -= nearest_dtime;
			}

			bool is_collision = true;
			if(is_unloaded[nearest_boxindex])
				is_collision = false;

			CollisionInfo info;
			if (is_object[nearest_boxindex]) {
				info.type = COLLISION_OBJECT;
			}
			else {
				info.type = COLLISION_NODE;
			}
			info.node_p = node_positions[nearest_boxindex];
			info.bouncy = bouncy;
			info.old_speed = speed_f;

			// Set the speed component that caused the collision to zero
			if(step_up)
			{
				// Special case: Handle stairs
				is_step_up[nearest_boxindex] = true;
				is_collision = false;
			}
			else if(nearest_collided == 0) // X
			{
				if(fabs(speed_f.X) > BS*3)
					speed_f.X *= bounce;
				else
					speed_f.X = 0;
				result.collides = true;
				result.collides_xz = true;
			}
			else if(nearest_collided == 1) // Y
			{
				if(fabs(speed_f.Y) > BS*3)
					speed_f.Y *= bounce;
				else
					speed_f.Y = 0;
				result.collides = true;
			}
			else if(nearest_collided == 2) // Z
			{
				if(fabs(speed_f.Z) > BS*3)
					speed_f.Z *= bounce;
				else
					speed_f.Z = 0;
				result.collides = true;
				result.collides_xz = true;
			}

			info.new_speed = speed_f;
			if(info.new_speed.getDistanceFrom(info.old_speed) < 0.1*BS)
				is_collision = false;

			if(is_collision){
				result.collisions.push_back(info);
			}
		}
	}

	/*
		Final touches: Check if standing on ground, step up stairs.
	*/
	aabb3f box = box_0;
	box.MinEdge += pos_f;
	box.MaxEdge += pos_f;
	for(u32 boxindex = 0; boxindex < cboxes.size(); boxindex++)
	{
		const aabb3f& cbox = cboxes[boxindex];

		/*
			See if the object is touching ground.

			Object touches ground if object's minimum Y is near node's
			maximum Y and object's X-Z-area overlaps with the node's
			X-Z-area.

			Use 0.15*BS so that it is easier to get on a node.
		*/
		if(
				cbox.MaxEdge.X-d > box.MinEdge.X &&
				cbox.MinEdge.X+d < box.MaxEdge.X &&
				cbox.MaxEdge.Z-d > box.MinEdge.Z &&
				cbox.MinEdge.Z+d < box.MaxEdge.Z
		){
			if(is_step_up[boxindex])
			{
				pos_f.Y += (cbox.MaxEdge.Y - box.MinEdge.Y);
				box = box_0;
				box.MinEdge += pos_f;
				box.MaxEdge += pos_f;
			}
			if(fabs(cbox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS)
			{
				result.touching_ground = true;
				if(is_unloaded[boxindex])
					result.standing_on_unloaded = true;
			}
		}
	}

	return result;
}
示例#3
0
// This doesn't seem to work and isn't used
collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
		f32 pos_max_d, const aabb3f &box_0,
		f32 stepheight, f32 dtime,
		v3f &pos_f, v3f &speed_f, v3f &accel_f)
{
	//TimeTaker tt("collisionMovePrecise");
    ScopeProfiler sp(g_profiler, "collisionMovePrecise avg", SPT_AVG);
	
	collisionMoveResult final_result;

	// If there is no speed, there are no collisions
	if(speed_f.getLength() == 0)
		return final_result;

	// Don't allow overly huge dtime
	if(dtime > 2.0)
		dtime = 2.0;

	f32 dtime_downcount = dtime;

	u32 loopcount = 0;
	do
	{
		loopcount++;

		// Maximum time increment (for collision detection etc)
		// time = distance / speed
		f32 dtime_max_increment = 1.0;
		if(speed_f.getLength() != 0)
			dtime_max_increment = pos_max_d / speed_f.getLength();

		// Maximum time increment is 10ms or lower
		if(dtime_max_increment > 0.01)
			dtime_max_increment = 0.01;

		f32 dtime_part;
		if(dtime_downcount > dtime_max_increment)
		{
			dtime_part = dtime_max_increment;
			dtime_downcount -= dtime_part;
		}
		else
		{
			dtime_part = dtime_downcount;
			/*
				Setting this to 0 (no -=dtime_part) disables an infinite loop
				when dtime_part is so small that dtime_downcount -= dtime_part
				does nothing
			*/
			dtime_downcount = 0;
		}

		collisionMoveResult result = collisionMoveSimple(map, gamedef,
				pos_max_d, box_0, stepheight, dtime_part,
				pos_f, speed_f, accel_f);

		if(result.touching_ground)
			final_result.touching_ground = true;
		if(result.collides)
			final_result.collides = true;
		if(result.collides_xz)
			final_result.collides_xz = true;
		if(result.standing_on_unloaded)
			final_result.standing_on_unloaded = true;
	}
	while(dtime_downcount > 0.001);

	return final_result;
}
示例#4
0
collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef,
                                        f32 pos_max_d, const aabb3f &box_0,
                                        f32 stepheight, f32 dtime,
                                        v3f &pos_f, v3f &speed_f, v3f &accel_f)
{
    TimeTaker tt("collisionMoveSimple");
    collisionMoveResult result;

    // If there is no speed, there are no collisions
    if(speed_f.getLength() == 0)
        return result;

    /*
    	Calculate new velocity
    */
    speed_f += accel_f * dtime;

    /*
    	Collect node boxes in movement range
    */
    std::vector<aabb3f> cboxes;
    std::vector<bool> is_unloaded;
    std::vector<bool> is_step_up;
    {
        TimeTaker tt2("collisionMoveSimple collect boxes");

        v3s16 oldpos_i = floatToInt(pos_f, BS);
        v3s16 newpos_i = floatToInt(pos_f + speed_f * dtime, BS);
        s16 min_x = MYMIN(oldpos_i.X, newpos_i.X) + (box_0.MinEdge.X / BS) - 1;
        s16 min_y = MYMIN(oldpos_i.Y, newpos_i.Y) + (box_0.MinEdge.Y / BS) - 1;
        s16 min_z = MYMIN(oldpos_i.Z, newpos_i.Z) + (box_0.MinEdge.Z / BS) - 1;
        s16 max_x = MYMAX(oldpos_i.X, newpos_i.X) + (box_0.MaxEdge.X / BS) + 1;
        s16 max_y = MYMAX(oldpos_i.Y, newpos_i.Y) + (box_0.MaxEdge.Y / BS) + 1;
        s16 max_z = MYMAX(oldpos_i.Z, newpos_i.Z) + (box_0.MaxEdge.Z / BS) + 1;

        for(s16 x = min_x; x <= max_x; x++)
            for(s16 y = min_y; y <= max_y; y++)
                for(s16 z = min_z; z <= max_z; z++)
                {
                    try {
                        // Object collides into walkable nodes
                        MapNode n = map->getNode(v3s16(x,y,z));
                        if(gamedef->getNodeDefManager()->get(n).walkable == false)
                            continue;

                        std::vector<aabb3f> nodeboxes = n.getNodeBoxes(gamedef->ndef());
                        for(std::vector<aabb3f>::iterator
                                i = nodeboxes.begin();
                                i != nodeboxes.end(); i++)
                        {
                            aabb3f box = *i;
                            box.MinEdge += v3f(x, y, z)*BS;
                            box.MaxEdge += v3f(x, y, z)*BS;
                            cboxes.push_back(box);
                            is_unloaded.push_back(false);
                            is_step_up.push_back(false);
                        }
                    }
                    catch(InvalidPositionException &e)
                    {
                        // Collide with unloaded nodes
                        aabb3f box = getNodeBox(v3s16(x,y,z), BS);
                        cboxes.push_back(box);
                        is_unloaded.push_back(true);
                        is_step_up.push_back(false);
                    }
                }
    } // tt2

    assert(cboxes.size() == is_unloaded.size());
    assert(cboxes.size() == is_step_up.size());


    /*
    	Collision detection
    */


    /*
    	Collision uncertainty radius
    	Make it a bit larger than the maximum distance of movement
    */
    f32 d = pos_max_d * 1.1;
    // A fairly large value in here makes moving smoother
    //f32 d = 0.15*BS;

    // This should always apply, otherwise there are glitches
    assert(d > pos_max_d);

    int loopcount = 0;
    while(dtime > BS*1e-10)
    {
        TimeTaker tt3("collisionMoveSimple dtime loop");

        // Avoid infinite loop
        loopcount++;
        if(loopcount >= 100)
        {
            infostream<<"collisionMoveSimple: WARNING: Loop count exceeded, aborting to avoid infiniite loop"<<std::endl;
            dtime = 0;
            break;
        }

        aabb3f movingbox = box_0;
        movingbox.MinEdge += pos_f;
        movingbox.MaxEdge += pos_f;

        int nearest_collided = -1;
        f32 nearest_dtime = dtime;
        u32 nearest_boxindex = -1;

        /*
        	Go through every nodebox, find nearest collision
        */
        for(u32 boxindex = 0; boxindex < cboxes.size(); boxindex++)
        {
            // Ignore if already stepped up this nodebox.
            if(is_step_up[boxindex])
                continue;

            // Find nearest collision of the two boxes (raytracing-like)
            f32 dtime_tmp;
            int collided = axisAlignedCollision(
                               cboxes[boxindex], movingbox, speed_f, d, dtime_tmp);

            if(collided == -1 || dtime_tmp >= nearest_dtime)
                continue;
            nearest_dtime = dtime_tmp;
            nearest_collided = collided;
            nearest_boxindex = boxindex;
        }
        if(nearest_collided == -1)
        {
            // No collision with any collision box.
            pos_f += speed_f * dtime;
            dtime = 0;  // Set to 0 to avoid "infinite" loop due to small FP numbers
        }
        else
        {
            // Otherwise, a collision occurred.

            const aabb3f& cbox = cboxes[nearest_boxindex];

            // Check for stairs.
            bool step_up = (nearest_collided != 1) && // must not be Y direction
                           (movingbox.MinEdge.Y < cbox.MaxEdge.Y) &&
                           (movingbox.MinEdge.Y + stepheight > cbox.MaxEdge.Y) &&
                           (!wouldCollideWithCeiling(cboxes, movingbox,
                                   cbox.MaxEdge.Y - movingbox.MinEdge.Y,
                                   d));

            // Move to the point of collision and reduce dtime by nearest_dtime
            if(nearest_dtime < 0)
            {
                // Handle negative nearest_dtime (can be caused by the d allowance)
                if(!step_up)
                {
                    if(nearest_collided == 0)
                        pos_f.X += speed_f.X * nearest_dtime;
                    if(nearest_collided == 1)
                        pos_f.Y += speed_f.Y * nearest_dtime;
                    if(nearest_collided == 2)
                        pos_f.Z += speed_f.Z * nearest_dtime;
                }
            }
            else
            {
                pos_f += speed_f * nearest_dtime;
                dtime -= nearest_dtime;
            }

            // Set the speed component that caused the collision to zero
            if(step_up)
            {
                // Special case: Handle stairs
                is_step_up[nearest_boxindex] = true;
            }
            else if(nearest_collided == 0) // X
            {
                speed_f.X = 0;
                result.collides = true;
                result.collides_xz = true;
            }
            else if(nearest_collided == 1) // Y
            {
                speed_f.Y = 0;
                result.collides = true;
            }
            else if(nearest_collided == 2) // Z
            {
                speed_f.Z = 0;
                result.collides = true;
                result.collides_xz = true;
            }
        }
    }

    /*
    	Final touches: Check if standing on ground, step up stairs.
    */
    aabb3f box = box_0;
    box.MinEdge += pos_f;
    box.MaxEdge += pos_f;
    for(u32 boxindex = 0; boxindex < cboxes.size(); boxindex++)
    {
        const aabb3f& cbox = cboxes[boxindex];

        /*
        	See if the object is touching ground.

        	Object touches ground if object's minimum Y is near node's
        	maximum Y and object's X-Z-area overlaps with the node's
        	X-Z-area.

        	Use 0.15*BS so that it is easier to get on a node.
        */
        if(
            cbox.MaxEdge.X-d > box.MinEdge.X &&
            cbox.MinEdge.X+d < box.MaxEdge.X &&
            cbox.MaxEdge.Z-d > box.MinEdge.Z &&
            cbox.MinEdge.Z+d < box.MaxEdge.Z
        ) {
            if(is_step_up[boxindex])
            {
                pos_f.Y += (cbox.MaxEdge.Y - box.MinEdge.Y);
                box = box_0;
                box.MinEdge += pos_f;
                box.MaxEdge += pos_f;
            }

            if(fabs(cbox.MaxEdge.Y-box.MinEdge.Y) < 0.15*BS)
            {
                result.touching_ground = true;
                if(is_unloaded[boxindex])
                    result.standing_on_unloaded = true;
            }
        }
    }
    return result;
}
示例#5
0
PointedThing ClientEnvironment::getPointedThing(
	core::line3d<f32> shootline,
	bool liquids_pointable,
	bool look_for_object)
{
	PointedThing result;

	INodeDefManager *nodedef = m_map->getNodeDefManager();

	core::aabbox3d<s16> maximal_exceed = nodedef->getSelectionBoxIntUnion();
	// The code needs to search these nodes
	core::aabbox3d<s16> search_range(-maximal_exceed.MaxEdge,
		-maximal_exceed.MinEdge);
	// If a node is found, there might be a larger node behind.
	// To find it, we have to go further.
	s16 maximal_overcheck =
		std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X))
			+ std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y))
			+ std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z));

	const v3f original_vector = shootline.getVector();
	const f32 original_length = original_vector.getLength();

	f32 min_distance = original_length;

	// First try to find an active object
	if (look_for_object) {
		ClientActiveObject *selected_object = getSelectedActiveObject(
			shootline, &result.intersection_point,
			&result.intersection_normal);

		if (selected_object != NULL) {
			min_distance =
				(result.intersection_point - shootline.start).getLength();

			result.type = POINTEDTHING_OBJECT;
			result.object_id = selected_object->getId();
		}
	}

	// Reduce shootline
	if (original_length > 0) {
		shootline.end = shootline.start
			+ shootline.getVector() / original_length * min_distance;
	}

	// Try to find a node that is closer than the selected active
	// object (if it exists).

	voxalgo::VoxelLineIterator iterator(shootline.start / BS,
		shootline.getVector() / BS);
	v3s16 oldnode = iterator.m_current_node_pos;
	// Indicates that a node was found.
	bool is_node_found = false;
	// If a node is found, it is possible that there's a node
	// behind it with a large nodebox, so continue the search.
	u16 node_foundcounter = 0;
	// If a node is found, this is the center of the
	// first nodebox the shootline meets.
	v3f found_boxcenter(0, 0, 0);
	// The untested nodes are in this range.
	core::aabbox3d<s16> new_nodes;
	while (true) {
		// Test the nodes around the current node in search_range.
		new_nodes = search_range;
		new_nodes.MinEdge += iterator.m_current_node_pos;
		new_nodes.MaxEdge += iterator.m_current_node_pos;

		// Only check new nodes
		v3s16 delta = iterator.m_current_node_pos - oldnode;
		if (delta.X > 0)
			new_nodes.MinEdge.X = new_nodes.MaxEdge.X;
		else if (delta.X < 0)
			new_nodes.MaxEdge.X = new_nodes.MinEdge.X;
		else if (delta.Y > 0)
			new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y;
		else if (delta.Y < 0)
			new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y;
		else if (delta.Z > 0)
			new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z;
		else if (delta.Z < 0)
			new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z;

		// For each untested node
		for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) {
			for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) {
				for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) {
					MapNode n;
					v3s16 np(x, y, z);
					bool is_valid_position;

					n = m_map->getNodeNoEx(np, &is_valid_position);
					if (!(is_valid_position &&
						isPointableNode(n, nodedef, liquids_pointable))) {
						continue;
					}
					std::vector<aabb3f> boxes;
					n.getSelectionBoxes(nodedef, &boxes,
						n.getNeighbors(np, m_map));

					v3f npf = intToFloat(np, BS);
					for (std::vector<aabb3f>::const_iterator i = boxes.begin();
						i != boxes.end(); ++i) {
						aabb3f box = *i;
						box.MinEdge += npf;
						box.MaxEdge += npf;
						v3f intersection_point;
						v3s16 intersection_normal;
						if (!boxLineCollision(box, shootline.start, shootline.getVector(),
							&intersection_point, &intersection_normal)) {
							continue;
						}
						f32 distance = (intersection_point - shootline.start).getLength();
						if (distance >= min_distance) {
							continue;
						}
						result.type = POINTEDTHING_NODE;
						result.node_undersurface = np;
						result.intersection_point = intersection_point;
						result.intersection_normal = intersection_normal;
						found_boxcenter = box.getCenter();
						min_distance = distance;
						is_node_found = true;
					}
				}
			}
		}
		if (is_node_found) {
			node_foundcounter++;
			if (node_foundcounter > maximal_overcheck) {
				break;
			}
		}
		// Next node
		if (iterator.hasNext()) {
			oldnode = iterator.m_current_node_pos;
			iterator.next();
		} else {
			break;
		}
	}

	if (is_node_found) {
		// Set undersurface and abovesurface nodes
		f32 d = 0.002 * BS;
		v3f fake_intersection = result.intersection_point;
		// Move intersection towards its source block.
		if (fake_intersection.X < found_boxcenter.X)
			fake_intersection.X += d;
		else
			fake_intersection.X -= d;

		if (fake_intersection.Y < found_boxcenter.Y)
			fake_intersection.Y += d;
		else
			fake_intersection.Y -= d;

		if (fake_intersection.Z < found_boxcenter.Z)
			fake_intersection.Z += d;
		else
			fake_intersection.Z -= d;

		result.node_real_undersurface = floatToInt(fake_intersection, BS);
		result.node_abovesurface = result.node_real_undersurface
			+ result.intersection_normal;
	}
	return result;
}