#include #include #include #include #include #include #include #include #include #include #include "Components.h" #include "GameData.h" #include "TerrainModule.h" #include "physics.h" #include "PhysicsModule.h" #include "CharacterManagerModule.h" #include "items.h" #include "StaticGeometryModule.h" #include "CharacterAIModule.h" #include namespace ECS { static bool itemsLoaded = false; static bool furnitureLoaded = false; static bool templatesLoaded = false; static std::list > addQueue; StaticGeometryModule::StaticGeometryModule(flecs::world &ecs) { ZoneScoped; ecs.module(); ecs.import (); ecs.import (); ecs.component(); ecs.component(); ecs.component(); ecs.component() .on_remove([](flecs::entity e, FurnitureInstance &instance) { ZoneScoped; if (instance.furniture) { instance.furniture ->destroyAllChildrenAndObjects(); instance.furniture->getCreator() ->destroySceneNode(instance.furniture); instance.furniture = nullptr; } }) .on_set([](flecs::entity e, FurnitureInstance &instance) { ZoneScoped; if (instance.furniture != e.get().furniture) { FurnitureInstance &f = e.get_mut(); if (f.furniture) { f.furniture ->destroyAllChildrenAndObjects(); f.furniture->getCreator() ->destroySceneNode(f.furniture); } } }) .on_add([](flecs::entity e, FurnitureInstance &instance) { ZoneScoped; instance.furniture = nullptr; }); ecs.component().on_remove([](flecs::entity e, TerrainItemNode &item) { ZoneScoped; if (item.itemNode) { item.itemNode->destroyAllChildrenAndObjects(); item.itemNode->getCreator()->destroySceneNode( item.itemNode); item.itemNode = nullptr; } if (item.geo) { ECS::get().mScnMgr->destroyStaticGeometry( item.geo); item.geo = nullptr; } }); ecs.component(); ecs.component(); ecs.import (); ecs.observer("LoadTerrainItems") .event(flecs::OnSet) .each([&](const Terrain &terrain) { ZoneScopedN("LoadTerrainItems"); if (terrain.mTerrainGroup && !itemsLoaded) { loadItems(); itemsLoaded = true; } }); if (!Ogre::MeshLodGenerator::getSingletonPtr()) new Ogre::MeshLodGenerator(); ecs.system("SetupTowns") .kind(flecs::OnUpdate) .without() .without() .each([&](flecs::entity e, TerrainItem &item) { Ogre::String props = item.properties; nlohmann::json jp = nlohmann::json::parse(props); if (jp.find("type") == jp.end()) return; Ogre::String itemType = jp["type"].get(); if (itemType == "town") Geometry::registerTownItem(e); }); ecs.system("AddGeometryQueue").kind(flecs::OnUpdate).run([&](flecs::iter &it) { ZoneScopedN("AddGeometryQueue"); std::list items; if (!ECS::get().has()) return; if (!ECS::get().mTerrainGroup) return; if (!itemsLoaded) { loadItems(); itemsLoaded = true; return; } if (!furnitureLoaded) { loadFurniture(); furnitureLoaded = true; } if (!templatesLoaded) { loadTemplates(); templatesLoaded = true; } std::list > output; while (!addQueue.empty()) { std::pair item = addQueue.front(); std::pair slot = { item.first, item.second }; flecs::entity parent = ECS::get() .query_builder() .build() .find([&slot](const TerrainSlotParent &parent) { return parent.slot == slot; }); if (parent.is_valid()) { items.clear(); ECS::get() .query_builder() .with(flecs::ChildOf, parent) .without() .write() .build() .each([&](flecs::entity e, const TerrainItem &item) { items.push_back(e); }); for (auto e : items) createItemGeometry(e); addQueue.pop_front(); } else { output.push_back(item); addQueue.pop_front(); } } OgreAssert(addQueue.empty(), "queue is not empty"); if (!output.empty()) addQueue = output; }); } void StaticGeometryModule::addGeometryForSlot(long x, long y) { ZoneScoped; addQueue.push_back({ x, y }); } void StaticGeometryModule::removeGeometryForSlot(long x, long y) { ZoneScoped; std::pair slot = { x, y }; flecs::entity parent = ECS::get().query_builder().build().find( [&slot](const TerrainSlotParent &parent) { return parent.slot == slot; }); if (parent.is_valid()) { ECS::get() .query_builder() .with(flecs::ChildOf, parent) .without() .build() .each([](flecs::entity e, const TerrainItem &item) { OgreAssert(false, "Implement item geo destroy" + item.properties); }); } } flecs::entity StaticGeometryModule::createItem(const Ogre::Vector3 &position, const Ogre::Quaternion &orientation, const Ogre::String &type) { ZoneScoped; long x, y; ECS::get().mTerrainGroup->convertWorldPositionToTerrainSlot( position, &x, &y); std::pair pos{ x, y }; flecs::entity slot = ECS::get().query_builder().build().find( [&](const TerrainSlotParent &slot) -> bool { return slot.slot == pos; }); if (!slot.is_valid()) slot = ECS::get().entity().set({ pos }); flecs::entity item = ECS::get().entity().child_of(slot); nlohmann::json jproperties; jproperties["type"] = type; item.set({ position, orientation, jproperties.dump() }); std::cout << "createItem: " << x << " " << y << " " << item.id() << std::endl; return item; } void StaticGeometryModule::setItemProperties(flecs::entity id, Ogre::String properties) { ZoneScoped; OgreAssert(id.is_valid(), "bad id"); id.get_mut().properties = properties; id.modified(); } const Ogre::String &StaticGeometryModule::getItemProperties(flecs::entity id) { ZoneScoped; OgreAssert(id.is_valid(), "bad id"); return id.get().properties; } nlohmann::json templates; void StaticGeometryModule::loadTemplates() { ZoneScoped; if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup( "templates.list")) return; Ogre::String group = Ogre::ResourceGroupManager::getSingleton() .findGroupContainingResource("templates.list"); Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton().openResource( "templates.list", group); Ogre::String json = stream->getAsString(); nlohmann::json jtemplates = nlohmann::json::parse(json); templates = jtemplates; } void StaticGeometryModule::saveTemplates() { ZoneScoped; Ogre::String path = "resources/buildings/templates.list"; if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup( "templates.list")) { Ogre::String group = Ogre::ResourceGroupManager::getSingleton() .findGroupContainingResource("templates.list"); Ogre::FileInfoListPtr fileInfoList( Ogre::ResourceGroupManager::getSingleton() .findResourceFileInfo(group, "templates.list")); OgreAssert(fileInfoList->size() == 1, "templates.list should be there and only once"); path = fileInfoList->at(0).archive->getName() + "/" + "templates.list"; Ogre::FileSystemLayer::removeFile(path); } std::fstream fout(path.c_str(), std::ios::out); fout << templates.dump(); fout.close(); } void StaticGeometryModule::saveItems() { ZoneScoped; Ogre::String path = "resources/buildings/items.list"; if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup( "items.list")) { Ogre::String group = Ogre::ResourceGroupManager::getSingleton() .findGroupContainingResource("items.list"); Ogre::FileInfoListPtr fileInfoList( Ogre::ResourceGroupManager::getSingleton() .findResourceFileInfo(group, "items.list")); OgreAssert(fileInfoList->size() == 1, "worpd_map.png should be there and only once"); path = fileInfoList->at(0).archive->getName() + "/" + "items.list"; Ogre::FileSystemLayer::removeFile(path); } std::fstream fout(path.c_str(), std::ios::out); nlohmann::json jitemlist; ECS::get().query_builder().build().each( [&](flecs::entity e, const TerrainItem &item) { nlohmann::json jitem; to_json(jitem["position"], item.position); to_json(jitem["orientation"], item.orientation); to_json(jitem["properties"], item.properties); jitem["objects"] = nlohmann::json::array(); ECS::get() .query_builder() .with(flecs::ChildOf, e) .build() .each([&](flecs::entity obje, const TerrainItem &objItem) { nlohmann::json jobjitem; to_json(jobjitem["position"], objItem.position); to_json(jobjitem["orientation"], objItem.orientation); to_json(jobjitem["properties"], objItem.properties); jitem["objects"].push_back(jobjitem); }); jitemlist.push_back(jitem); }); fout << jitemlist.dump(); fout.close(); } void StaticGeometryModule::loadItems() { ZoneScoped; if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup( "items.list")) return; Ogre::String group = Ogre::ResourceGroupManager::getSingleton() .findGroupContainingResource("items.list"); Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton().openResource( "items.list", group); Ogre::String json = stream->getAsString(); nlohmann::json jlist = nlohmann::json::parse(json); ECS::get().delete_with(); ECS::get().delete_with(); for (const auto &v : jlist) { Ogre::Vector3 position; Ogre::Quaternion orientation; Ogre::String properties; from_json(v["position"], position); from_json(v["orientation"], orientation); properties = v["properties"].get(); std::cout << v.dump(4) << std::endl; long x, y; ECS::get() .mTerrainGroup->convertWorldPositionToTerrainSlot( position, &x, &y); std::pair pos{ x, y }; flecs::entity slot = ECS::get() .query_builder() .build() .find([&](const TerrainSlotParent &slot) -> bool { return slot.slot == pos; }); if (!slot.is_valid()) slot = ECS::get().entity().set( { pos }); OgreAssert(slot.is_valid(), "Failed to create slot"); flecs::entity item = ECS::get().entity().child_of(slot).set( { position, orientation, properties }); if (v.find("objects") != v.end()) { for (auto &obj : v["objects"]) { Ogre::Vector3 objposition; Ogre::Quaternion objorientation; Ogre::String objproperties; from_json(obj["position"], objposition); from_json(obj["orientation"], objorientation); objproperties = obj["properties"].get(); flecs::entity itemObj = ECS::get() .entity() .child_of(item) .set( { objposition, objorientation, objproperties }); } } std::cout << "createItem: " << x << " " << y << " " << item.id() << std::endl; std::cout << "position: " << item.id() << " " << position << std::endl; } } void StaticGeometryModule::saveFurniture() { /* No saving - furniture is generated by blender */ } void StaticGeometryModule::loadFurniture() { ZoneScoped; ECS::get().delete_with(); static std::vector glb_names; const std::vector &groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups(); if (glb_names.size() == 0) { int i; for (i = 0; i < groups.size(); i++) { std::vector names = *Ogre::ResourceGroupManager::getSingleton() .findResourceNames( groups[i], "furniture-*.glb.json"); glb_names.insert(glb_names.end(), names.begin(), names.end()); } } for (auto &g : glb_names) { Ogre::String group = Ogre::ResourceGroupManager::getSingleton() .findGroupContainingResource(g); Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton().openResource( g, group); Ogre::String json = stream->getAsString(); nlohmann::json jdata = nlohmann::json::parse(json); std::vector tags; for (auto &tag : jdata["tags"]) { Ogre::String stag = tag.get(); tags.push_back(stag); } Ogre::String meshName = jdata["mesh"].get(); Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().getByName(meshName); if (mesh) { Ogre::LodConfig meshconf(mesh); Geometry::setupLods(meshconf); } ECS::get().entity().set({ json, tags }); std::cout << "path: " << g << std::endl; } } void StaticGeometryModule::getItemPositionPerSlot( long x, long y, std::list *positions) { ZoneScoped; std::pair pos{ x, y }; if (!positions) return; flecs::entity slot = ECS::get().query_builder().build().find( [&](const TerrainSlotParent &slot) -> bool { return slot.slot == pos; }); if (!slot.is_valid()) return; ECS::get() .query_builder() .with(flecs::ChildOf, slot) .build() .each([&](flecs::entity e, const TerrainItem &item) { positions->push_back(item.position); }); } void StaticGeometryModule::getItemPositions(std::list *positions) { ZoneScoped; ECS::get().query_builder().build().each( [&](flecs::entity e, const TerrainItem &item) { positions->push_back(item.position); }); } void StaticGeometryModule::getItemPositionAndRotation( flecs::entity e, Ogre::Vector3 &position, Ogre::Quaternion &orientation) { ZoneScoped; position = e.get().position; orientation = e.get().orientation; } void StaticGeometryModule::getItemsProperties( std::list > *items) { ZoneScoped; ECS::get().query_builder().build().each( [&](flecs::entity e, const TerrainItem &item) { items->push_back({ e, item.properties }); }); } void StaticGeometryModule::createItemGeometry(flecs::entity e) { ZoneScoped; Geometry::createItemGeometry(e); } void StaticGeometryModule::destroyItemGeometry(flecs::entity e) { ZoneScoped; Geometry::destroyItemGeometry(e); } nlohmann::json &StaticGeometryModule::getTemplates() { return templates; } void StaticGeometryModule::updateItemGeometry(flecs::entity e) { ZoneScoped; // We add this as task to reduce UI load Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([e]() { ZoneScopedN("updateItemGeometry"); if (e.has()) return; e.add(); Geometry::updateItemGeometry(e); e.remove(); }); } void StaticGeometryModule::addTriangleBufferWork( const Ogre::String &meshName, Ogre::StaticGeometry *geo, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, const Procedural::TriangleBuffer &tb) { ZoneScoped; struct WorkData { Ogre::String meshName; Ogre::StaticGeometry *geo; Ogre::Vector3 position; Ogre::Quaternion rotation; Procedural::TriangleBuffer tb; }; WorkData data = { meshName, geo, position, rotation, tb }; // We add this as task to reduce UI load Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([data]() { ZoneScopedN("addTriangleBufferWork"); Ogre::MeshPtr mesh = data.tb.transformToMesh(data.meshName); Ogre::Entity *ent = ECS::get().mScnMgr->createEntity(mesh); data.geo->addEntity(ent, data.position, data.rotation); ECS::get().mScnMgr->destroyEntity(ent); }); } struct TiledMeshes { struct Tile { Ogre::String materialName; std::shared_ptr vertexData; std::shared_ptr indexData; std::set positions; }; std::map tiles; uint32_t packKey(const Ogre::Vector3i &position) { uint32_t key = 0; key |= (uint32_t)(position[2] + 512) << 20; key |= (uint32_t)(position[1] + 512) << 10; key |= (uint32_t)(position[0] + 512) << 0; return key; } void unpackKey(uint32_t key, Ogre::Vector3i &position) { uint32_t mask = 0x3ff; position[0] = (int)(key & mask) - 512; position[1] = (int)((key >> 10) & mask) - 512; position[2] = (int)((key >> 20) & mask) - 512; } void setTile(const Ogre::String &name, const Ogre::Vector3i &position) { if (tiles.find(name) == tiles.end()) return; tiles[name].positions.insert(packKey(position)); } void clearTile(const Ogre::String &name, const Ogre::Vector3i &position) { if (tiles.find(name) == tiles.end()) return; tiles[name].positions.erase(packKey(position)); } void addTile(const Ogre::String &name, Ogre::MeshPtr mesh) { if (mesh->getSubMeshes().size() != 1) return; Ogre::SubMesh *submesh = mesh->getSubMesh(0); Ogre::VertexData *vertexData; if (submesh->useSharedVertices) vertexData = mesh->sharedVertexData->clone(); else vertexData = submesh->vertexData->clone(); tiles[name] = { submesh->getMaterialName(), std::shared_ptr(vertexData), std::shared_ptr( submesh->indexData->clone()), {} }; } struct buildSettings { Ogre::String meshName; Ogre::String materialName; Ogre::MeshPtr mesh; Ogre::SubMesh *sm; int vertexCount, vertexOffset; int indexCount, indexOffset; Ogre::VertexDeclaration *vdecl; Ogre::HardwareVertexBufferSharedPtr vbuf; Ogre::AxisAlignedBox bounds; bool setBounds; }; void configureSettings(struct buildSettings &settings) { settings.mesh = Ogre::MeshManager::getSingleton().createManual( settings.meshName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); settings.mesh->createVertexData(); settings.sm = settings.mesh->createSubMesh(); settings.vertexCount = 0; settings.indexCount = 0; settings.vdecl = nullptr; for (const auto &tile : tiles) { settings.vertexCount += tile.second.vertexData->vertexCount * tile.second.positions.size(); settings.indexCount += tile.second.indexData->indexCount * tile.second.positions.size(); if (!settings.vdecl) { settings.vdecl = tile.second.vertexData ->vertexDeclaration->clone(); settings.materialName = tile.second.materialName; } } settings.mesh->sharedVertexData->vertexStart = 0; settings.mesh->sharedVertexData->vertexCount = settings.vertexCount; settings.mesh->sharedVertexData->vertexDeclaration = settings.vdecl; settings.vbuf = Ogre::HardwareBufferManager::getSingleton() .createVertexBuffer( settings.vdecl->getVertexSize(0), settings.vertexCount, Ogre::HBU_GPU_ONLY); settings.mesh->sharedVertexData->vertexBufferBinding->setBinding( 0, settings.vbuf); settings.sm->indexData->indexStart = 0; settings.sm->indexData->indexCount = settings.indexCount; settings.sm->indexData->indexBuffer = Ogre::HardwareBufferManager::getSingleton() .createIndexBuffer( Ogre::HardwareIndexBuffer::IT_32BIT, settings.sm->indexData->indexCount * 8, Ogre::HBU_GPU_ONLY); settings.sm->setMaterialName(settings.materialName); settings.setBounds = true; } void processIndex(struct buildSettings &settings, const struct Tile &tile, unsigned int *dstIndices) { int j; std::shared_ptr srcIndexData = tile.indexData; int srcIndexCount = srcIndexData->indexCount; std::shared_ptr srcVertexData = tile.vertexData; int srcVertexCount = srcVertexData->vertexCount; Ogre::HardwareIndexBufferSharedPtr srcIbuf = srcIndexData->indexBuffer; Ogre::HardwareBufferLockGuard srcIndexLock( srcIbuf, Ogre::HardwareBuffer::HBL_READ_ONLY); if (srcIndexData->indexBuffer->getType() == Ogre::HardwareIndexBuffer::IT_32BIT) { unsigned int *indices = static_cast(srcIndexLock.pData); for (j = 0; j < srcIndexCount; j++) dstIndices[settings.indexOffset + j] = indices[j] + settings.vertexOffset; } else if (srcIndexData->indexBuffer->getType() == Ogre::HardwareIndexBuffer::IT_16BIT) { unsigned short *indices = static_cast( srcIndexLock.pData); for (j = 0; j < srcIndexCount; j++) dstIndices[settings.indexOffset + j] = indices[j] + settings.vertexOffset; } } void processSingleVertex( struct buildSettings &settings, const Ogre::VertexDeclaration::VertexElementList &srcElements, uint32_t offset, unsigned char *srcData, unsigned char *dstData) { for (const auto &srcElement : srcElements) { unsigned char *srcPtr, *dstPtr; const Ogre::VertexElement *destElement = settings.vdecl->findElementBySemantic( srcElement.getSemantic(), srcElement.getIndex()); if (!destElement) goto out; if (srcElement.getType() != destElement->getType() || srcElement.getSize() != destElement->getSize()) goto out; srcPtr = srcData + srcElement.getOffset(); dstPtr = dstData + destElement->getOffset(); if (destElement->getSemantic() == Ogre::VES_POSITION) { float *srcPositionData = reinterpret_cast(srcPtr); float *dstPositionData = reinterpret_cast(dstPtr); Ogre::Vector3 position(srcPositionData[0], srcPositionData[1], srcPositionData[2]); Ogre::Vector3i offsetv; unpackKey(offset, offsetv); position.x += (float)offsetv[0]; position.y += (float)offsetv[1]; position.z += (float)offsetv[2]; dstPositionData[0] = position.x; dstPositionData[1] = position.y; dstPositionData[2] = position.z; if (settings.setBounds) { settings.bounds.setMinimum(position); settings.bounds.setMaximum(position); settings.setBounds = false; } else settings.bounds.merge(position); } else if (destElement->getSemantic() == Ogre::VES_NORMAL) { float *srcNormalData = reinterpret_cast(srcPtr); float *dstNormalData = reinterpret_cast(dstPtr); Ogre::Vector3 normal(srcNormalData[0], srcNormalData[1], srcNormalData[2]); dstNormalData[0] = normal.x; dstNormalData[1] = normal.y; dstNormalData[2] = normal.z; } else memcpy(dstPtr, srcPtr, srcElement.getSize()); out:; } } void processTile(struct buildSettings &settings, const struct Tile &tile, uint32_t position, unsigned char *dstpData) { std::shared_ptr srcVertexData = tile.vertexData; const Ogre::VertexDeclaration *srcDecl = srcVertexData->vertexDeclaration; const Ogre::VertexBufferBinding *srcBind = srcVertexData->vertexBufferBinding; int srcVertexCount = srcVertexData->vertexCount; Ogre::HardwareVertexBufferSharedPtr srcVbuf = srcBind->getBuffer(0); std::shared_ptr srcIndexData = tile.indexData; int srcIndexCount = srcIndexData->indexCount; Ogre::HardwareBufferLockGuard srcVertexLock( srcVbuf, 0, srcVertexCount * srcDecl->getVertexSize(0), Ogre::HardwareBuffer::HBL_READ_ONLY); const Ogre::VertexDeclaration::VertexElementList &srcElements = srcDecl->getElements(); int j; unsigned char *srcpData = static_cast(srcVertexLock.pData); for (j = 0; j < srcVertexCount; j++) { unsigned char *srcData = srcpData + j * srcVbuf->getVertexSize(); unsigned char *dstData = dstpData + (settings.vertexOffset + j) * settings.vbuf->getVertexSize(); processSingleVertex(settings, srcElements, position, srcData, dstData); } } Ogre::MeshPtr build(const Ogre::String &meshName) { buildSettings settings; settings.meshName = meshName; configureSettings(settings); { Ogre::HardwareBufferLockGuard vertexLock( settings.vbuf, 0, settings.vertexCount * settings.vdecl->getVertexSize(0), Ogre::HardwareBuffer::HBL_NO_OVERWRITE); Ogre::HardwareBufferLockGuard indexLock( settings.sm->indexData->indexBuffer, Ogre::HardwareBuffer::HBL_NO_OVERWRITE); settings.vertexOffset = 0; settings.indexOffset = 0; unsigned char *dstpData = static_cast(vertexLock.pData); unsigned int *dstIndices = static_cast(indexLock.pData); for (const auto &tile : tiles) { std::shared_ptr srcIndexData = tile.second.indexData; int srcIndexCount = srcIndexData->indexCount; std::shared_ptr srcVertexData = tile.second.vertexData; int srcVertexCount = srcVertexData->vertexCount; for (const auto &position : tile.second.positions) { processTile(settings, tile.second, position, dstpData); processIndex(settings, tile.second, dstIndices); settings.vertexOffset += srcVertexCount; settings.indexOffset += srcIndexCount; Ogre::Vector3i vposition; unpackKey(position, vposition); std::cout << "position: " << position << " " << vposition << std::endl; } } settings.mesh->_setBounds(settings.bounds); } Ogre::LodConfig config(settings.mesh); // config.advanced.useCompression = false; // config.advanced.useVertexNormals = true; config.advanced.preventPunchingHoles = true; config.advanced.preventBreakingLines = true; config.createGeneratedLodLevel(2, 0.15f); config.createGeneratedLodLevel(20, 0.49f); config.createGeneratedLodLevel(15, 0.49f); config.createGeneratedLodLevel(150, 0.75f); config.advanced.useBackgroundQueue = false; Ogre::MeshLodGenerator::getSingleton().generateLodLevels( config); return settings.mesh; } void removeTile(const Ogre::String &name) { tiles.erase(name); } TiledMeshes() { } }; }