Load items vegetation multithreaded

Discussion area about developing with Ogre-Next (2.1, 2.2 and beyond)


Lax
Gnoll
Posts: 675
Joined: Mon Aug 06, 2007 12:53 pm
Location: Saarland, Germany
x 64

Load items vegetation multithreaded

Post by Lax »

Hi,

I'm loading vegetation data in multiple threads. It is really fast.
But if the scene is loaded, I just see one bush. If I place e.g. another object in my editor into the scene or make some other rendering operation, suddenly all bushes are rendered. I'm not able to find out, what is causing this really weird behavior.

Maybe someone run already into the same issue and could help out here.

This is the code:

Code: Select all

std::vector<Ogre::MeshPtr> meshes;
meshes.reserve(this->vegetationMeshNames.size());

for (const auto& meshName : this->vegetationMeshNames)
{
	if (meshName->getString().empty())
	{
		meshes.emplace_back(nullptr);
		continue;
	}

try
{
	// Load V1 mesh and convert to V2
	auto v1Mesh = Ogre::v1::MeshManager::getSingletonPtr()->load(meshName->getString(),
		Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME,
		Ogre::v1::HardwareBuffer::HBU_STATIC,
		Ogre::v1::HardwareBuffer::HBU_STATIC);

	// Clean up any existing V2 mesh to prevent naming conflicts
	Ogre::ResourcePtr resourceV2 = Ogre::MeshManager::getSingletonPtr()->getResourceByName(meshName->getString());
	if (resourceV2)
	{
		Ogre::MeshManager::getSingletonPtr()->destroyResourcePool(meshName->getString());
		Ogre::MeshManager::getSingletonPtr()->remove(resourceV2->getHandle());
	}

	auto v2Mesh = Ogre::MeshManager::getSingletonPtr()->createByImportingV1(meshName->getString(),
		Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
		v1Mesh.get(),
		true, true, true);

	v1Mesh->unload();
	v1Mesh.setNull();

	meshes.emplace_back(v2Mesh);
}
catch (const Ogre::Exception& e)
{
	Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL,"[VegetationComponent] Mesh loading error: " + e.getDescription());
	meshes.emplace_back(nullptr);
}
}

// Compute generation parameters
unsigned int vegetationQueryFlags = AppStateManager::getSingletonPtr()->getGameObjectController()->generateCategoryId(this->gameObjectPtr->getCategory());
const Ogre::Real size = Ogre::Math::Abs(this->minimumBounds.x) * this->scale->getReal() +
	this->positionXZ->getVector2().x +
	Ogre::Math::Abs(this->maximumBounds.x) * this->scale->getReal() +
	this->positionXZ->getVector2().x;

// Parallel generation with improved thread distribution
const size_t numThreads = std::max<size_t>(1, Ogre::PlatformInformation::getNumLogicalCores());
const Ogre::Real step = 1.0f / this->density->getReal();
std::atomic<unsigned long> createdCount{ 0 };
std::vector<std::thread> threads;
std::mutex vegetationMutex;

auto start = std::chrono::high_resolution_clock::now();

Ogre::Camera* camera = AppStateManager::getSingletonPtr()->getCameraManager()->getActiveCamera();

auto generateVegetation = [&](size_t threadIndex, size_t totalThreads) {
	std::vector<Ogre::Item*> localVegetationItems;

for (Ogre::Real x = this->minimumBounds.x * this->scale->getReal() + this->positionXZ->getVector2().x +
	(threadIndex * size / totalThreads);
	x < this->minimumBounds.x * this->scale->getReal() + this->positionXZ->getVector2().x +
	((threadIndex + 1) * size / totalThreads);
	x += step)
{
	for (Ogre::Real z = this->minimumBounds.z * this->scale->getReal() + this->positionXZ->getVector2().y;
		z < this->maximumBounds.z * this->scale->getReal() + this->positionXZ->getVector2().y;
		z += step)
	{
		std::lock_guard<std::mutex> lock(vegetationMutex);
		Ogre::Vector3 objPosition = Vector3(Math::RangeRandom(-size + x, size + x), 0.0f, Math::RangeRandom(-size + z, size + z));
		
		Ogre::MovableObject* hitMovableObject = nullptr;
		Ogre::Real closestDistance = 0.0f;
		Ogre::Vector3 normal = Ogre::Vector3::ZERO;
		Ogre::Vector3 internalHitPoint = Ogre::Vector3::ZERO;
		
		// Shoot ray at object position down
		Ogre::Ray hitRay = Ogre::Ray(objPosition + Ogre::Vector3(0.0f, 10000.0f, 0.0f), Ogre::Vector3::NEGATIVE_UNIT_Y);
		// Check if there is an hit with an polygon of an entity, item, terra etc.
		this->raySceneQuery->setRay(hitRay);
		
		std::vector<Ogre::MovableObject*> excludeMovableObjects(0);
		/*excludeMovableObjects[0] = gameObject->getMovableObject<Ogre::v1::Entity>();
		excludeMovableObjects[1] = this->gizmo->getArrowEntityX();
		excludeMovableObjects[2] = this->gizmo->getArrowEntityY();
		excludeMovableObjects[3] = this->gizmo->getArrowEntityZ();
		excludeMovableObjects[4] = this->gizmo->getSphereEntity();*/
		
		MathHelper::getInstance()->getRaycastFromPoint(this->raySceneQuery, camera, internalHitPoint, (size_t&)hitMovableObject, closestDistance, normal, &excludeMovableObjects);
		
		objPosition.y += internalHitPoint.y;
		
		if (nullptr == hitMovableObject)
		{
			// Nothing found for given category, skip this quadrant
			continue;
		}
		
		if (0.0f != this->maxHeight->getReal())
		{
			if (internalHitPoint.y > this->maxHeight->getReal())
			{
				continue;
			}
		}
		
		if (nullptr != terra)
		{
			bool layerMatches = true;
			// Exclude maybe terra layers from vegetation placing
			std::vector<int> layers = terra->getLayerAt(objPosition);
		
			// Ogre::String str;
			for (size_t i = 0; i < this->terraLayerList.size(); i++)
			{ 
				layerMatches &= layers[i] <= this->terraLayerList[i];
				// str += Ogre::StringConverter::toString(layers[i]) + " ";
			}
		
			//  Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL, "layers: " + str);
			if (false == layerMatches)
			{
				// Skip this vegetation position
				continue;
			}
		}
		
		// Create Mesh
		Ogre::Item* item = nullptr;
		
		int rndIndexMeshName = MathHelper::getInstance()->getRandomNumber<int>(0, static_cast<unsigned int>(this->vegetationMeshNames.size() - 1));
		
		Ogre::MeshPtr meshPtr = meshes[rndIndexMeshName];
		
		try
		{
			item = this->gameObjectPtr->getSceneManager()->createItem(meshPtr, Ogre::SCENE_STATIC);
			// After terrain
			item->setRenderQueueGroup(NOWA::RENDER_QUEUE_V2_MESH);
			item->setQueryFlags(vegetationQueryFlags);
			item->setCastShadows(false);
			if (0.0f != this->renderDistance->getReal())
			{
				item->setRenderingDistance(this->renderDistance->getReal());
			}
		}
		catch (...)
		{
			Ogre::LogManager::getSingletonPtr()->logMessage(Ogre::LML_CRITICAL, "[VegetationComponent] Could not create item for mesh: " + meshPtr->getName() + " because the mesh is invalid for game object: " + this->gameObjectPtr->getName());
			return;
		}
		
		SceneNode* node = nullptr;
		
		if (nullptr != item)
		{
			if (nullptr == targetGameObjectPtr)
			{
				node = this->gameObjectPtr->getSceneManager()->getRootSceneNode()->createChildSceneNode(Ogre::SCENE_STATIC);
			}
			else
			{
				node = targetGameObjectPtr->getSceneNode()->createChildSceneNode(Ogre::SCENE_STATIC);
			}
			node->attachObject(item);
		
			this->vegetationItemList.emplace_back(item);
		
			// VegetationScaleMin/Max as attribute?
			//  scale
			Ogre::Real s = Math::RangeRandom(0.5f, 1.2f);
			node->scale(s, s, s);
		
			// objPosition.y += std::min(item->getLocalAabb().getMinimum().y, Real(0.0f)) * -0.1f /*+ lay.down*/;  //par
		
			node->setPosition(objPosition);
		
			if (true == this->autoOrientation->getBool())
			{
				node->setDirection(normal, Ogre::Node::TS_PARENT, Ogre::Vector3::NEGATIVE_UNIT_Y);
			}
		
			Ogre::Degree angle(Math::RangeRandom(0.0f, 360.f));
			Quaternion p = node->getOrientation();
			Quaternion q;
			q.FromAngleAxis(angle, Vector3::UNIT_Y);
			node->setOrientation(p * q);
		
		}
													
		createdCount++;
	}
}

// Batch update the shared vegetation list
{
	std::lock_guard<std::mutex> lock(vegetationMutex);
	this->vegetationItemList.insert(this->vegetationItemList.end(), localVegetationItems.begin(), localVegetationItems.end());
}
};

// Create and start threads
for (size_t i = 0; i < numThreads; ++i)
{
	threads.emplace_back(generateVegetation, i, numThreads);
}

// Wait for all threads to complete
for (auto& thread : threads)
{
	thread.join();
}

auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = finish - start;

Best Regards
Lax

http://www.lukas-kalinowski.com/Homepage/?page_id=1631
Please support Second Earth Technic Base built of Lego bricks for Lego ideas: https://ideas.lego.com/projects/81b9bd1 ... b97b79be62