Sphere - triangle intersections

Started by
15 comments, last by Pilpel 8 years, 9 months ago

The Plane class:


class Plane
{
public:
	Plane(const Vec3 &normal, Real distance) : _normal(glm::normalize(normal)), _distance(distance) {}
	Plane(const Vec3 &p1, const Vec3 &p2, const Vec3 &p3) {
		_normal = glm::cross(p2-p1, p3-p1);
		_normal = glm::normalize(_normal);

		_distance = _normal.x*p1.x + _normal.y*p1.y + _normal.z*p1.z;
	}
	Plane(const Vec3 point, const Vec3 &normal) {
		_normal = glm::normalize(normal);
		_distance = _normal.x*point.x + _normal.y*point.y + _normal.z*point.z;
	}

	bool isFrontFacingTo(const Vec3 &direction) const {
		Real dot = glm::dot(_normal, glm::normalize(direction));
		return dot <= 0;
	}

	Real signedDistanceTo(const Vec3 &point) const {
		return glm::dot(point, _normal) - _distance;
	}
	Real distanceTo(const Vec3 &point) const {
		return std::fabs(this->signedDistanceTo(point));
	}

	const Vec3& normal() const { return _normal; }
	Real distance() const { return _distance; }

	friend ostream& operator<<(ostream& os, const Plane &plane);

	bool operator==(const Plane &other) {
		return (this->_normal == other._normal) && (this->_distance == other._distance);
	}

private:
	Vec3 _normal;
	Real _distance;
};

What happens if you remove the collision response and just stop once you collide?

The collision detection routine sets colData->nearestDistance to something between [0,0.15] (0.15 is the initial velocity, set by the wasd keys) , and then my character stops at what seems to be a reasonable spot, but I'm sure that if I indeed resolve that collision, the next nearestDistance will be just a little be smaller than its predecessor, and not 0 as wanted.

Here's my "simulate" function which calls all the collision routined. It's the last piece of code I didn't share until now, and it's a bit hackish, sorry for that.


void Physics::simulate(const std::vector<glm::vec3> &vertices, const glm::vec3 &velocity)
{
	std::cout << "simulating...\n";
	CollisionPacket col;
	col.nearestDistance = (Real)UINT_MAX;
	col.velocity = velocity;
	col.basePoint = cam.getPosition();

	int i = 0;

	do {
		cout << "running for the " << ++i << " time" << endl;
		col.foundCollision = false;

		//find nearest collision
		for(size_t i = 0; i < vertices.size(); i+=1000) //i+=100 so only the first triangle of the scene will be checked
		{
			_sphereTriangleCollision(&col, (Vec3)vertices[i], (Vec3)vertices[i+1], (Vec3)vertices[i+2]);
		}
		
		//solve that collision
		if(col.foundCollision)
			solveCollision(&col);
	} while(col.foundCollision);

	col.finalPosition = (Vec3)cam.getPosition() + col.velocity;
	cam.setPosition((glm::vec3)col.finalPosition);
}
Advertisement

//slide plane from point and normal
Plane slidePlane(colPacket->intersectionPoint, colPacket->basePoint - colPacket->intersectionPoint);

Are you sure the normal is correct? I think you want to use something like (basePoint + t*velocity) - intersectionPoint instead of basePoint - intersectionPoint.

Also this line:

colPacket->velocity = colPacket->velocity * colPacket->nearestDistance + intersectionToNewDestination;

nearestDistance is the actual distance, i.e. multiplied with the length of velocity, but here you are multiplying it again with the velocity. I think your intersectionToNewDestination is the new velocity you want.

Thanks a lot! I got it (almost) working now, here's what I did:


void solveCollision(CollisionPacket *colData)
{
	Vec3 normalizedVelocity = glm::normalize(colData->velocity);
	Vec3 newBasePoint = colData->basePoint + normalizedVelocity * colData->nearestDistance;

	//slide plane from point and normal
	Plane slidePlane(colData->intersectionPoint, newBasePoint - colData->intersectionPoint);

	//destination point as if there was no collision response
	Vec3 destinationPoint = colData->basePoint + colData->velocity;
	
	//new destination point
	Vec3 newDestinationPoint = destinationPoint - slidePlane.signedDistanceTo(destinationPoint) * slidePlane.normal();
	
	colData->basePoint = newBasePoint;
	colData->velocity = newDestinationPoint - colData->intersectionPoint;
}

void Physics::simulate(const std::vector<glm::vec3> &vertices, const glm::vec3 &velocity)
{
	CollisionPacket col;
	col.nearestDistance = (Real)UINT_MAX;
	col.velocity = velocity;
	col.basePoint = cam.getPosition();

	int iter = 0;
	const int maxIterations = 5;

	do {
		col.foundCollision = false;

		//find nearest collision
		for(size_t i = 0; i < vertices.size(); i+=3)
		{
			_sphereTriangleCollision(&col, (Vec3)vertices[i], (Vec3)vertices[i+1], (Vec3)vertices[i+2]);
		}
		
		//solve that collision
		if(col.foundCollision)
		{
			solveCollision(&col);
			iter++;
		}
		
		col.basePoint += col.velocity;
	} while(col.foundCollision && iter <= maxIterations);

	cam.setPosition(col.basePoint);
}

---

Two more issues:

1. In the collision response routine on page 47, why does the author moves the sphere slightly less than the collision detection routine suggests?

2. I played with the scene as much as I could in order to find bugs, and I found one:

The sphere (located on my camera) can sometimes bypass an edge of a triangle and get into the triangle, if approaching in an angle that is pretty parallel to the triangle plane. This happens around 50% of the times. (note that collisions inside the triangle are handled well)

Could it be the cause of the first issue that I noted? Debugging tells me that when the sphere "gets into" the triangle, the collision detection function indeed finds it and sets the variables to some reasonable values.

Do you see anything wrong with my code?

Here's a video of the bug: (the centered sphere is just for debugging. it's placed exactly 1 unit in front of the camera, which is the real colliding sphere)

Thanks again!

You're weclome, feel free to up vote any post you deem helpful :D.


1. In the collision response routine on page 47, why does the author moves the sphere slightly less than the collision detection routine suggests?

This is to avoid floating point issues, trying to move exactly on to a plane might cause it to be seen as inside the plane on your next iteration. Moving slightly less than the exact distance will prevent any bugs concerning pushing the sphere out of plane into another one.

Your second issue is caused by an illegal move in your simulate method:

col.basePoint += col.velocity;

You haven't checked collision for that, so this will cause you to go through planes. You already move in your solveCollision function, so removing that line should fix your problem.

Removing that line makes my characters unable to move.

solveCollision doesn't fully move my basePoint, it only moves it to the point of intersection and changes the velocity vector to a "slide" vector, so (basePoint + velcoity) will make the sphere slide.

if solveCollision is indeed called, then a new intersection check loop must run, so there's no way that (basePoint + velocity) will intersect anything, if calculated outside the loop.

Here's a more reasonable simulate() function, but this still has the bug I described. (I only changed the last 3 lines)


void Physics::simulate(const std::vector<glm::vec3> &vertices, const glm::vec3 &velocity)
{
	CollisionPacket col;
	col.nearestDistance = (Real)UINT_MAX;
	col.velocity = velocity;
	col.basePoint = cam.getPosition();

	int iter = 0;
	const int maxIterations = 5;

	do {
		col.foundCollision = false;

		//find nearest collision
		for(size_t i = 0; i < vertices.size(); i+=3)
		{
			_sphereTriangleCollision(&col, (Vec3)vertices[i], (Vec3)vertices[i+1], (Vec3)vertices[i+2]);
		}
		
		//solve that collision
		if(col.foundCollision)
		{
			solveCollision(&col);
			iter++;
		}		
	} while(col.foundCollision);//no max iterations for now

	col.basePoint += col.velocity;
	cam.setPosition(col.basePoint);
}

What am I doing wrong?

Removing that line makes my characters unable to move.

solveCollision doesn't fully move my basePoint, it only moves it to the point of intersection and changes the velocity vector to a "slide" vector, so (basePoint + velcoity) will make the sphere slide.

Ah I see, it should be placed in an else block then:


//solve that collision
if(col.foundCollision)
{
    solveCollision(&col);
    iter++;
} 
else
{
    col.basePoint += col.velocity;
}

As for your other problem, have you tried not positioning exactly onto the plane by moving slightly less?

I got it working now. The bug occurred because of floating point issues. I adjusted colData->nearestDistance and colData->intersectionPoint by 0.005.

Thanks!

This topic is closed to new replies.

Advertisement