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