Problem using part of height map to create btConvexHullShape

Post Reply
Zaladen
Posts: 25
Joined: Thu May 14, 2015 10:21 am

Problem using part of height map to create btConvexHullShape

Post by Zaladen »

I have a height map which obviously consists of a bunch of vertices, and I want to take an area of those vertices and make a btConvexHullShape from them. I store all my vertices in an std vector, and I create the shape using AddPoint:

Code: Select all

btRigidBody* Bullet::CreateConvexHullShape(std::vector<Vertex> vertices, float scale, CollisionTypes type, int collidesWith)
{
	btConvexHullShape *shape = new btConvexHullShape();

	for (auto it = vertices.begin(); it != vertices.end(); ++it)
		shape->addPoint(btVector3(it->Position.x * scale, it->Position.y, it->Position.z * scale));

	btDefaultMotionState *motionState =
		new btDefaultMotionState(btTransform(btQuaternion(1, 0, 0, 0), btVector3(0, 0, 0)));
	int mass = 0;

	btVector3 fallInertia(0, 0, 0);
	shape->calculateLocalInertia(mass, fallInertia);

	btRigidBody::btRigidBodyConstructionInfo
		bodyCl(mass, motionState, shape, fallInertia);
	btRigidBody *body = new btRigidBody(bodyCl);

	dynamicsWorld->addRigidBody(body, type, collidesWith);
	dynamicsWorld->updateSingleAabb(body);

	return body;
}
So I basically send a vector which contains the vertices of the part of the height map that I want to use to this function, which then creates the shape using AddPoint. The shape gets added to the world correctly, but there is no collision at all. What could be the problem?
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Problem using part of height map to create btConvexHullS

Post by drleviathan »

Some things that I notice about your code (not all relate to your question):

(1) The name of your function seems incorrect. You have it as CreateConvexHullShape() but it returns a btRigidBody* so maybe it should be called CreateBodyWithConvexHullShape().

(2) The vertices you add to the convex hull shape... are they in the world-frame or in the RigidBody's local-frame? In general they should be in the local-frame, however the world transform for the RigidBody comes not from the btRigidBodyConstructionInfo but from btDefaultMotionstate::getWorldTransform() which was initialized with zero everything so... I would expect the world-frame to work in this case.

(3) Since you don't call body->setCollisionFlags() then the object will be added as static by default. This means you don't need to compute the inertia tensor.

(4) When adding a point to btConvexHullShape it will, by default, recompute the local Aabb. It is inefficient to do this for every point you add. Alternatively you can set the second parameter to the addPoint() call to false and then manually call shape->recalcLocalAabb() when you're all done.

(5) You set the collision group and mask of the body. Are you sure you set those up correctly? If not then collisions may not work like you expect.
Zaladen
Posts: 25
Joined: Thu May 14, 2015 10:21 am

Re: Problem using part of height map to create btConvexHullS

Post by Zaladen »

Haha yea I'm really bad at function names, might change it.

Not completely sure what you mean by world-frame and local frame, could you elaborate on that?

Do you think calculating the inertia tensor creates a problem in this case or it doesn't matter?

Ok I'll try that out too.

I set the masks to 255 specifically to avoid that type of problem.
benelot
Posts: 350
Joined: Sat Jul 04, 2015 10:33 am
Location: Bern, Switzerland
Contact:

Re: Problem using part of height map to create btConvexHullS

Post by benelot »

Drleviathan is talking about coordinate frames of reference. The world frame of reference (or world frame) is the dynamic world's frame. It has its origin at the world's origin and world axis aligned axes. The local frame of reference (or local frame) has its origin at the referenced object's origin and the coordinate axes are oriented according to the object's orientation.

As an example for the local reference frame you can take your own body or especially your head. If you measure distances between you and other objects, you measure usually starting at your feet. You set the origin to your position. Also you say, an object is in front of you. If you rotate yourself, the object might suddenly be to your left or right. Therefore your local frame of reference rotates with you.

Now, convex hull points should be referenced from the convex hull object's origin. But if your object is at the world origin, it does not matter because the reference frames are aligned.
Zaladen
Posts: 25
Joined: Thu May 14, 2015 10:21 am

Re: Problem using part of height map to create btConvexHullS

Post by Zaladen »

Ok thanks, actually I fixed my debug drawer and I found that the coordinate system in bullet was inverted in the x and y axes compared to my rendering so the body was in the wrong place, but even when I fixed that and I now see that the body is correctly aligned, there's still no collision. :/ Collisions are detected in the collision callback though, but there is no collision response. Could there be a problem with the number of vertices? There're over 2000 vertices in the shape.

Edit:

Ok i managed to get it to collide but, collisions are all over the place and it's not working properly. Maybe it's not ideal to take part of a height map and make a convex hull shape out of it, so I thought I'd try out the btHeightfieldTerrainShape. The problem with that though, is that I don't know how the data is supposed to be represented. since I store my vertices in an std vector, how do I send this data to the constructor? All it does is take a void* to some data, I assume it is float if you pass PHY_FLOAT, and it seems it only needs the height value and not the x y coordinates. When I pass the height data and create the shape though, the program gets stuck at stepSimulation either with an exception or as if in an infinite loop.
benelot
Posts: 350
Joined: Sat Jul 04, 2015 10:33 am
Location: Bern, Switzerland
Contact:

Re: Problem using part of height map to create btConvexHullS

Post by benelot »

A covex hull is not appropriate in the case of your terrain not being convex. Therefore the btHeightfieldTerrainShape is a better choice.

The terrain loading is included in the benchmark demo of the example browser, have a look at it and also its parent folder:
https://github.com/bulletphysics/bullet ... rkDemo.cpp

On a side note: can you post your debugdrawer and mention what rendering engine it is for? I would like to collect debug drawer implementations for different engines on the bullet wiki. They might help a lot of people in their debugging adventures.
Zaladen
Posts: 25
Joined: Thu May 14, 2015 10:21 am

Re: Problem using part of height map to create btConvexHullS

Post by Zaladen »

Ok I managed to set up the btHeightfieldTerrainShape and it is displayed in the debug draw. I have the problem of it not generating a collision response though, but I can see that it detects intersection. This is the setup:

Code: Select all

btRigidBody* Bullet::CreateHeightFieldShape(float *vertices, btVector3 &position, int XWidth, int YWidth, float minHeight, float maxHeight, float scale, CollisionTypes type, int collidesWith)
{
	btHeightfieldTerrainShape *shape = new btHeightfieldTerrainShape(XWidth, YWidth, vertices, 1.0f, minHeight, maxHeight, 1, PHY_FLOAT, false);

	btVector3 scaling = btVector3(scale, 1.0f, scale);	
	shape->setLocalScaling(scaling);
	shape->setUseDiamondSubdivision(true);

	btRigidBody *body = new btRigidBody(0, new btDefaultMotionState, shape);
	body->getWorldTransform().setOrigin(btVector3(position.getX(), position.getY() - (maxHeight - minHeight) / 2, position.getZ()));
	body->getWorldTransform().setRotation(btQuaternion(1,0,0,0));

	dynamicsWorld->addRigidBody(body, type, collidesWith);
	dynamicsWorld->updateSingleAabb(body);

	return body;
}
I use OpenGL 3.3 for rendering, nothing else. I have a class set up with VBO and VAO that draws a line between 2 points that I use in the debug draw.
benelot
Posts: 350
Joined: Sat Jul 04, 2015 10:33 am
Location: Bern, Switzerland
Contact:

Re: Problem using part of height map to create btConvexHullS

Post by benelot »

How do you see that it detects intersection? Are your collidesWith bits correctly set? You can start from the bullet example browser demo and then modify it until it does what you want. That is usually what works best. Then you will usually see what you do wrong.
Zaladen
Posts: 25
Joined: Thu May 14, 2015 10:21 am

Re: Problem using part of height map to create btConvexHullS

Post by Zaladen »

I actually got the collision to work, and first I thought there was no problem, but there still is. I create a body around the player and recreate the body every time the player moves a certain distance from the body, and for the most part it works fine and the body aligns to the terrain, but sometimes it just doesn't match the terrain at all and the body looks like it was just created from random values that has nothing to do with the terrain. Any idea what could cause that?
benelot
Posts: 350
Joined: Sat Jul 04, 2015 10:33 am
Location: Bern, Switzerland
Contact:

Re: Problem using part of height map to create btConvexHullS

Post by benelot »

Does your player need to act like a physics object in a way? Then you should try to move it via forces. Usually the player is a static object and you use raycasting to get the height on the terrain, the position the player accordingly. Some terrains even have a method to get the height from coordinates, I do not remember if bullet has this (could be a good feature if not).
Zaladen
Posts: 25
Joined: Thu May 14, 2015 10:21 am

Re: Problem using part of height map to create btConvexHullS

Post by Zaladen »

I already use the player as a physics object, although that has nothing to do with the problem. The problem is that at times when the terrain object gets initialized it doesn't look anything like a part of the terrain, rather like it got initialized by some random values that isn't related to the tarrain data at all.

Code: Select all

void Ground::ReinitializeBody(bool reCreateBody)
{
	if (body != nullptr && reCreateBody)
	{
		bullet->dynamicsWorld->removeRigidBody(body);
		delete body->getMotionState();
		delete body;
	}

	int X = (int)playerPos.getX();
	int Z = (int)playerPos.getZ();

	int vertCount = 0;

	int size = 20;

	if (reCreateBody)
	{
		delete[]floatVerts;
		floatVerts = new float[1000];
	}

	for (int i = 0; i < 1000; ++i)
		floatVerts[i] = 0.0f;

	int count = 0;

	for (int i = 0; i < vertices.size(); ++i)
	{
		glm::vec2 vertPos;
		vertPos = glm::vec2(vertices[i].Position.x * scale, vertices[i].Position.z * scale);

		Rectangle rect(glm::vec2(X - (size / 2) * scale, Z - (size / 2) * scale), size * scale, size * scale);

		if (!rect.PointToRectInterSection(vertPos))
			continue;

		floatVerts[vertCount] = vertices[i].Position.y;

		vertCount++;
	}
		
	if (reCreateBody)
	{

		body = bullet->CreateHeightFieldShape(floatVerts, btVector3(X, 0.0f, Z), size, size, -1000, 1000, 4.0f, COL_WALL, COL_PLAYER | COL_ENEMY | COL_RAY);
		body->setCollisionFlags(body->getCollisionFlags() | btCollisionObject::CF_STATIC_OBJECT);
		body->setActivationState(DISABLE_DEACTIVATION);

		for (auto it = userPtr.begin(); it != userPtr.end();)
		{
			delete (*it);
			it = userPtr.erase(it);
		}
		userPtr.clear();

		SUserPointer *val = new SUserPointer();
		val->ptr = this;
		val->name = "GroundBody";
		body->setUserPointer(val);
		userPtr.push_back(val);
	}
	else
		body->getWorldTransform().setOrigin(btVector3(X, 0.0f, Z));
	
}
Post Reply