Paging module - how to make it stream things other than terrain? colliders?

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

Paging module - how to make it stream things other than terrain? colliders?

Post by slapin »

Hi, all!
I have set up paging terrain and it works very well now. Also I update it with my sun/moon system parameters and it even updates dynamically which is super cool. Now my appetite grows and I want to add buildings and trees and possibly grass to the terrain. So I ask how to handle objects with paging?
Adding to that I will need colliders, have anybody joined Ogre's terrain system with Bullet heightmap collider? Or trimesh collider?
Another thing is of course roads. But that is for another topic.

paroj
OGRE Team Member
OGRE Team Member
Posts: 2204
Joined: Sun Mar 30, 2014 2:51 pm
x 1188

Re: Paging module - how to make it stream things other than terrain? colliders?

Post by paroj »

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

Re: Paging module - how to make it stream things other than terrain? colliders?

Post by slapin »

No, thanks a bunch for the find!

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

Re: Paging module - how to make it stream things other than terrain? colliders?

Post by slapin »

paroj wrote: Thu Jun 12, 2025 2:49 pm

did you see viewtopic.php?t=58756?

Looks like at time when prepareProceduralPage is running no terrain exists,
so the code

Code: Select all

	class DummyPageProvider : public Ogre::PageProvider {
	public:
		DummyPageProvider(btDynamicsWorld *world)
			: Ogre::PageProvider()
			, mBtWorld(world)
		{
		}
		std::unordered_map<Ogre::PageID, btRigidBody *> body;
		btDynamicsWorld *mBtWorld;
		bool prepareProceduralPage(Ogre::Page *page,
					   Ogre::PagedWorldSection *section)
		{
			Ogre::TerrainGroup *pGroup =
				((Ogre::TerrainPagedWorldSection *)section)
					->getTerrainGroup();
			long x, y;
			pGroup->unpackIndex(page->getID(), &x, &y);
			Ogre::Terrain *pTerrain = pGroup->getTerrain(x, y);
			if (!pTerrain)
				return true;
			float *terrainHeightData = pTerrain->getHeightData();
			Ogre::Vector3 terrainPosition = pTerrain->getPosition();

		float *pDataConvert = new float[pTerrain->getSize() *
						pTerrain->getSize()];
		for (int i = 0; i < pTerrain->getSize(); i++)
			memcpy(pDataConvert + pTerrain->getSize() *
						      i, // source
			       terrainHeightData +
				       pTerrain->getSize() *
					       (pTerrain->getSize() -
						i - 1), // target
			       sizeof(float) *
				       (pTerrain->getSize()) // size
			);

		float metersBetweenVertices =
			pTerrain->getWorldSize() /
			(pTerrain->getSize() -
			 1); //edit: fixed 0 -> 1 on 2010-08-13
		btVector3 localScaling(metersBetweenVertices, 1,
				       metersBetweenVertices);

		btHeightfieldTerrainShape *groundShape =
			new btHeightfieldTerrainShape(
				pTerrain->getSize(),
				pTerrain->getSize(), pDataConvert,
				1 /*ignore*/, pTerrain->getMinHeight(),
				pTerrain->getMaxHeight(), 1, PHY_FLOAT,
				true);

		groundShape->setUseDiamondSubdivision(true);
		groundShape->setLocalScaling(localScaling);

		btRigidBody *groundBody = new btRigidBody(
			0, new btDefaultMotionState(), groundShape);

		groundBody->getWorldTransform().setOrigin(btVector3(
			terrainPosition.x,
			terrainPosition.y + (pTerrain->getMaxHeight() -
					     pTerrain->getMinHeight()) /
						    2,
			terrainPosition.z));

		groundBody->getWorldTransform().setRotation(
			btQuaternion(Ogre::Quaternion::IDENTITY.x,
				     Ogre::Quaternion::IDENTITY.y,
				     Ogre::Quaternion::IDENTITY.z,
				     Ogre::Quaternion::IDENTITY.w));

		mBtWorld->addRigidBody(groundBody);
		body[page->getID()] = groundBody;

		return true;
	}
	bool loadProceduralPage(Ogre::Page *page,
				Ogre::PagedWorldSection *section)
	{
		return true;
	}
	bool unloadProceduralPage(Ogre::Page *page,
				  Ogre::PagedWorldSection *section)
	{
		return true;
	}
	bool unprepareProceduralPage(Ogre::Page *page,
				     Ogre::PagedWorldSection *section)
	{
		return true;
	}
};

doesn't work and can't work. My other tries with definers work but consume all of the CPU.
The below code works but is super expensive too. Need some task/workqueue system
for this to work properly.

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++;
		}
		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;
		}