Collider generation for paged terrain.

Problems building or running the engine, queries about how to use features etc.
slapin
Bronze Sponsor
Bronze Sponsor
Posts: 146
Joined: Fri May 23, 2025 5:04 pm
x 5

Collider generation for paged terrain.

Post by slapin »

Ogre Version: master branch
Operating System: Ubuntu 22.04
Render System: OpenGL ES 2.0

Hi, all!

I please help me how to generate colliders with Bullet on paged terrain.
All the suggestions I got on this forum did not work for me. Looking at Ogre source I found the working recipe but it consumes all CPU
resources running each frame:

Code: Select all

	std::unordered_set<long> col_terrains;
	std::unordered_map<long, btRigidBody *> col_bodies;
	void create_colliders()
	{
		btDynamicsWorld *world = mDynWorld->getBtWorld();
		std::unordered_set<long> new_terrains;
		Ogre::TerrainGroup::TerrainSlotMap slots =
			mTerrainGroup->getTerrainSlots();
		auto slot_it = slots.begin();
		while (slot_it != slots.end()) {
			Ogre::Terrain *terrain = slot_it->second->instance;
			long x = slot_it->second->x;
			long y = slot_it->second->y;
			long key = y * 1024 + y;
			std::cout << "x: " << x << "\n";
			std::cout << "y: " << y << "\n";
			if (!terrain) {
				slot_it++;
				continue;
			}
			if (new_terrains.find(key) != new_terrains.end()) {
				slot_it++;
				continue;
			}
			if (col_terrains.find(key) != col_terrains.end()) {
				slot_it++;
				continue;
			}
			new_terrains.insert(key);
			col_terrains.insert(key);
			btRigidBody *body = createHeightfieldShape(
				terrain->getSize(), terrain->getHeightData(),
				terrain->getMinHeight(),
				terrain->getMaxHeight(), terrain->getPosition(),
				terrain->getWorldSize() /
					(terrain->getSize() - 1),
				world);
			OgreAssert(body, "Could not allocate body");
			col_bodies[key] = body;
			std::cout << "adding terrain body: " << terrain << " "
				  << col_bodies[key] << "\n";
			slot_it++;
		}
		auto col_it = col_terrains.begin();
		std::list<long> rm_bodies;
		while (col_it != col_terrains.end()) {
			std::cout
				<< "checking: terrain still exists: " << *col_it
				<< "\n";
			/* no terrain */
			if (new_terrains.find(*col_it) == new_terrains.end()) {
				std::cout << "was removed: "
					  << col_bodies[*col_it] << " "
					  << *col_it << "\n";
				rm_bodies.push_back(*col_it);
				/* free shapes and bodies */
			}
			col_it++;
		}
#if 0
		while (!rm_bodies.empty()) {
			long key = rm_bodies.front();
			rm_bodies.pop_front();
			btRigidBody *body = col_bodies[key];
			OgreAssert(body, "No body :(");
			btCollisionShape *shape = body->getCollisionShape();
			col_terrains.erase(key);
			mDynWorld->getBtWorld()->removeRigidBody(body);
			delete shape;
			delete body;
		}
#endif
	}
	btRigidBody *createHeightfieldShape(int size, float *data,
					    const Ogre::Real &minHeight,
					    const Ogre::Real &maxHeight,
					    const Ogre::Vector3 &position,
					    const Ogre::Real &scale,
					    btDynamicsWorld *world)
	{
		// Convert height data in a format suitable for the physics engine
		float *terrainHeights = new float[size * size];
		assert(terrainHeights != 0);

	for (int i = 0; i < size; i++) {
		memcpy(terrainHeights + size * i,
		       data + size * (size - i - 1),
		       sizeof(float) * size);
	}

	btScalar heightScale = 1.f;

	btVector3 localScaling(scale, heightScale, scale);

	btHeightfieldTerrainShape *terrainShape =
		new btHeightfieldTerrainShape(
			size, size, terrainHeights, 1 /*ignore*/,
			minHeight, maxHeight, 1, PHY_FLOAT, true);
	terrainShape->setUseDiamondSubdivision(true);
	terrainShape->setLocalScaling(localScaling);

	//Create Rigid Body using 0 mass so it is static
	btRigidBody *body = new btRigidBody(BT_LARGE_FLOAT,
					    new btDefaultMotionState(),
					    terrainShape);
	body->setFriction(0.8f);
	body->setHitFraction(0.8f);
	body->setRestitution(0.6f);
	body->getWorldTransform().setOrigin(btVector3(
		position.x, position.y + (maxHeight - minHeight) / 2,
		position.z));
	body->getWorldTransform().setRotation(
		Ogre::Bullet::convert(Ogre::Quaternion::IDENTITY));
	body->setCollisionFlags(body->getCollisionFlags() |
				btCollisionObject::CF_STATIC_OBJECT);

	world->addRigidBody(body);
	return body;
}
rpgplayerrobin
Orc Shaman
Posts: 752
Joined: Wed Mar 18, 2009 3:03 am
x 421

Re: Collider generation for paged terrain.

Post by rpgplayerrobin »

I have not tried that code myself, but should it be running each frame? It seems weird to me to have to create a bullet collision each frame.
I would only create the collision once and then if the camera moves a couple of units/meters away it should update to create a new one (since the landscape might change).

The bottlneck in the code could also be that it is using "cout" everywhere, which does not really make any sense to me as that would not really do anything except for debugging purposes, and it may be a big slowdown.

slapin
Bronze Sponsor
Bronze Sponsor
Posts: 146
Joined: Fri May 23, 2025 5:04 pm
x 5

Re: Collider generation for paged terrain.

Post by slapin »

Well, with this code we run at 3 FPS, and I check that the collider is already created... I guess traversal of all terrains is wrong thing to do.
I was hoping paging module itself will help me, but it looks like it is not the case as when Paging module is active the terrain does not exist and there is nothing to create collider from.

rpgplayerrobin
Orc Shaman
Posts: 752
Joined: Wed Mar 18, 2009 3:03 am
x 421

Re: Collider generation for paged terrain.

Post by rpgplayerrobin »

Cout might also be a culprit.
Simply looping through terrain should not have any effect on FPS at all.

Also, do you know for sure that the FPS is caused by running this code? Because running it simply once (when pressing a key for example) might still make the FPS low because of how many collision shapes/bodies created, which bullet then might have to handle each frame, which can pull down the performance.
So in short, this code might be fine, it might instead be the bullet update that is slow after you have ran this code just once.

slapin
Bronze Sponsor
Bronze Sponsor
Posts: 146
Joined: Fri May 23, 2025 5:04 pm
x 5

Re: Collider generation for paged terrain.

Post by slapin »

Well, I did similar stuff on other engines and Bullet performed quite well, so it is not likely, however I'll check how many colliders I can create...

rpgplayerrobin
Orc Shaman
Posts: 752
Joined: Wed Mar 18, 2009 3:03 am
x 421

Re: Collider generation for paged terrain.

Post by rpgplayerrobin »

It is even more simple, just check if a key is down, and if it is, stop updating it. Then see if the FPS is increased or not.

slapin
Bronze Sponsor
Bronze Sponsor
Posts: 146
Joined: Fri May 23, 2025 5:04 pm
x 5

Re: Collider generation for paged terrain.

Post by slapin »

Found that a problem is debug drawing, not colliders themselves. When used Urho3d it was not that slow, I don't know what is going on.
Anyway now I run physics on the terrain. I wonder how to check if terrain is gone and I need to remove the collider. Hope page unload handler will help me.
Still, people say that trimesh collider is faster and it is better to generate navmeshes around agents, but here I'll go one step at time.