From f86e7fd96cf661cc2706228f05d4382a7a765d57 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Fri, 9 Jan 2026 22:27:51 +0300 Subject: [PATCH] Furniture placement --- assets/blender/buildings/parts/CMakeLists.txt | 14 + .../buildings/parts/export_furniture_parts.py | 91 +++ .../buildings/parts/furniture-full.blend | 3 + .../blender/buildings/parts/furniture.blend | 3 + .../blender/scripts/export_building_parts.py | 71 ++ resources.cfg | 1 + src/gamedata/GUIModule.cpp | 77 +- src/gamedata/StaticGeometryModule.cpp | 679 ++++++++++-------- src/gamedata/StaticGeometryModule.h | 7 + src/gamedata/items/items.cpp | 6 +- src/gamedata/items/town.cpp | 463 +++++++++++- 11 files changed, 1089 insertions(+), 326 deletions(-) create mode 100644 assets/blender/buildings/parts/export_furniture_parts.py create mode 100644 assets/blender/buildings/parts/furniture-full.blend create mode 100644 assets/blender/buildings/parts/furniture.blend create mode 100644 assets/blender/scripts/export_building_parts.py diff --git a/assets/blender/buildings/parts/CMakeLists.txt b/assets/blender/buildings/parts/CMakeLists.txt index f19fe40..c0360db 100644 --- a/assets/blender/buildings/parts/CMakeLists.txt +++ b/assets/blender/buildings/parts/CMakeLists.txt @@ -1,5 +1,6 @@ project(building-parts) set(PARTS_FILES pier.blend) +set(FURNITURE_FILES furniture.blend) set(PARTS_OUTPUT_DIRS) foreach(PARTS_FILE ${PARTS_FILES}) get_filename_component(FILE_NAME ${PARTS_FILE} NAME_WE) @@ -14,4 +15,17 @@ foreach(PARTS_FILE ${PARTS_FILES}) DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PARTS_FILE} ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_building_parts.py) list(APPEND PARTS_OUTPUT_DIRS ${PARTS_OUTPUT_DIR}) endforeach() +foreach(FURNITURE_FILE ${FURNITURE_FILES}) + get_filename_component(FILE_NAME ${FURNITURE_FILE} NAME_WE) + set(PARTS_OUTPUT_DIR ${CMAKE_BINARY_DIR}/resources/buildings/parts/${FILE_NAME}) + add_custom_command( + OUTPUT ${PARTS_OUTPUT_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${PARTS_OUTPUT_DIR} + COMMAND ${BLENDER} ${CMAKE_CURRENT_SOURCE_DIR}/${FURNITURE_FILE} + -b -Y -P + ${CMAKE_CURRENT_SOURCE_DIR}/export_furniture_parts.py + -- ${PARTS_OUTPUT_DIR} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PARTS_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/export_furniture_parts.py) + list(APPEND PARTS_OUTPUT_DIRS ${PARTS_OUTPUT_DIR}) +endforeach() add_custom_target(import_building_parts ALL DEPENDS ${PARTS_OUTPUT_DIRS}) diff --git a/assets/blender/buildings/parts/export_furniture_parts.py b/assets/blender/buildings/parts/export_furniture_parts.py new file mode 100644 index 0000000..65e4133 --- /dev/null +++ b/assets/blender/buildings/parts/export_furniture_parts.py @@ -0,0 +1,91 @@ +import bpy +import os, sys, time +import json +argv = sys.argv +argv = argv[argv.index("--") + 1:] + +incpath = os.path.dirname(__file__) + +sys.path.insert(0, incpath) +outdir = argv[0] + +def export_root_objects_to_gltf(output_dir): + # Ensure the output directory exists + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + # Deselect all objects + bpy.ops.object.select_all(action='DESELECT') + + # Iterate through all objects in the scene + for obj in bpy.context.scene.objects: + # Check if the object has no parent and has children + # The original request specifies "objects having no parent with children". + # This condition captures those that are explicitly root objects of a hierarchy. + if obj.parent is None: + # Select the root object and all its children + + print(obj.name) + if not obj.name.startswith("furniture-"): + continue + desc = {} + desc["name"] = obj.name.replace("furniture-","") + desc["mesh"] = obj.name + ".glb" + if "furniture_tags" in obj: + mtags = obj["furniture_tags"] + if "," in mtags: + atags = [m.strip() for m in mtags.split(",")] + desc["tags"] = atags + else: + desc["tags"] = [mtags] + else: + desc["tags"] = [] + obj.select_set(True) + for child in obj.children_recursive: + child.select_set(True) + + obj.location = (0.0, 0.0, 0.0) + + # Set the root object as the active object for the export operator + bpy.context.view_layer.objects.active = obj + + # Define the output file path + file_path = os.path.join(output_dir, f"{obj.name}.glb") + + # Export the selected objects to a glTF file + bpy.ops.export_scene.gltf(filepath=file_path, + use_selection=True, + check_existing=False, + export_format='GLB', + export_texture_dir='textures', export_texcoords=True, + export_normals=True, + export_tangents=True, + export_materials='EXPORT', + export_colors=True, + use_mesh_edges=False, + use_mesh_vertices=False, + export_cameras=False, + use_visible=False, + use_renderable=False, + export_yup=True, + export_apply=True, + export_animations=True, + export_force_sampling=True, + export_def_bones=False, + export_current_frame=False, + export_morph=True, + export_morph_animation=False, + export_morph_normal=True, + export_morph_tangent=True, + export_lights=False, + export_skins=True) + + # Deselect all objects for the next iteration + bpy.ops.object.select_all(action='DESELECT') + + print(f"Exported {obj.name} and its children to {file_path}") + with open(file_path + ".json", "w") as json_data: + json.dump(desc, json_data) + +export_root_objects_to_gltf(outdir) + diff --git a/assets/blender/buildings/parts/furniture-full.blend b/assets/blender/buildings/parts/furniture-full.blend new file mode 100644 index 0000000..71924d9 --- /dev/null +++ b/assets/blender/buildings/parts/furniture-full.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27785a1009f5e5c8bfcdf4208b50196dae09a7840b445ae95f4cfbf5010cbe3f +size 1893374 diff --git a/assets/blender/buildings/parts/furniture.blend b/assets/blender/buildings/parts/furniture.blend new file mode 100644 index 0000000..5b9e993 --- /dev/null +++ b/assets/blender/buildings/parts/furniture.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6316ed1835394a3346eb57be2e55520b8513473f348c90bf871c2375e62aae6a +size 1986948 diff --git a/assets/blender/scripts/export_building_parts.py b/assets/blender/scripts/export_building_parts.py new file mode 100644 index 0000000..9ef294f --- /dev/null +++ b/assets/blender/scripts/export_building_parts.py @@ -0,0 +1,71 @@ +import bpy +import os, sys, time +argv = sys.argv +argv = argv[argv.index("--") + 1:] + +incpath = os.path.dirname(__file__) + +sys.path.insert(0, incpath) +outdir = argv[0] + +def export_root_objects_to_gltf(output_dir): + # Ensure the output directory exists + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + # Deselect all objects + bpy.ops.object.select_all(action='DESELECT') + + # Iterate through all objects in the scene + for obj in bpy.context.scene.objects: + # Check if the object has no parent and has children + # The original request specifies "objects having no parent with children". + # This condition captures those that are explicitly root objects of a hierarchy. + if obj.parent is None: + # Select the root object and all its children + print(obj.name) + obj.select_set(True) + for child in obj.children_recursive: + child.select_set(True) + + # Set the root object as the active object for the export operator + bpy.context.view_layer.objects.active = obj + + # Define the output file path + file_path = os.path.join(output_dir, f"{obj.name}.glb") + + # Export the selected objects to a glTF file + bpy.ops.export_scene.gltf(filepath=file_path, + use_selection=True, + check_existing=False, + export_format='GLB', + export_texture_dir='textures', export_texcoords=True, + export_normals=True, + export_tangents=True, + export_materials='EXPORT', + export_colors=True, + use_mesh_edges=False, + use_mesh_vertices=False, + export_cameras=False, + use_visible=False, + use_renderable=False, + export_yup=True, + export_apply=True, + export_animations=True, + export_force_sampling=True, + export_def_bones=False, + export_current_frame=False, + export_morph=True, + export_morph_animation=False, + export_morph_normal=True, + export_morph_tangent=True, + export_lights=False, + export_skins=True) + + # Deselect all objects for the next iteration + bpy.ops.object.select_all(action='DESELECT') + + print(f"Exported {obj.name} and its children to {file_path}") + +export_root_objects_to_gltf(outdir) + diff --git a/resources.cfg b/resources.cfg index f68a48d..a7670e0 100644 --- a/resources.cfg +++ b/resources.cfg @@ -21,6 +21,7 @@ FileSystem=resources/terrain FileSystem=skybox FileSystem=resources/buildings FileSystem=resources/buildings/parts/pier +FileSystem=resources/buildings/parts/furniture FileSystem=resources/vehicles FileSystem=resources/debug FileSystem=resources/fonts diff --git a/src/gamedata/GUIModule.cpp b/src/gamedata/GUIModule.cpp index 7024b05..91e9d89 100644 --- a/src/gamedata/GUIModule.cpp +++ b/src/gamedata/GUIModule.cpp @@ -1372,8 +1372,81 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { cursorPosition); modified = false; } - } - ImGui::EndMenuBar(); + } + if (ImGui::BeginMenu("Furniture")) { + if (ImGui::BeginMenu("Create New Furniture")) { + static char nameBuffer[32]; + static int current_mesh = 0; + static std::vector + glb_names; + const std::vector &groups = + Ogre::ResourceGroupManager:: + getSingleton() + .getResourceGroups(); + if (glb_names.size() == 0) { + int i; + glb_names.push_back(""); + for (i = 0; i < groups.size(); + i++) { + std::vector names = + *Ogre::ResourceGroupManager::getSingleton() + .findResourceNames( + groups[i], + "furniture-*.glb"); + glb_names.insert( + glb_names.end(), + names.begin(), + names.end()); + } + } + ImGui::InputText( + "Furniture Name", nameBuffer, + IM_ARRAYSIZE(nameBuffer)); + if (glb_names.size() > 0) { + if (ImGui::BeginCombo( + "Furniture Mesh", + glb_names[current_mesh] + .c_str())) { + int i; + for (i = 0; + i < + glb_names.size(); + i++) { + bool isSelected = + i == + current_mesh; + if (ImGui::Selectable( + (glb_names[i] + + "##select" + + Ogre::StringConverter:: + toString( + i)) + .c_str(), + isSelected)) { + current_mesh = + i; + } + if (isSelected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndCombo(); + } + } else + ImGui::Text( + "No furniture meshes found"); + if (ImGui::MenuItem( + "Create Furniture")) { + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Edit Furniture")) { + if (ImGui::MenuItem("Edit Furniture")) { + } + ImGui::EndMenu(); + } + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); } ImGui::Spacing(); ImGui::BeginChild("WorldMap...", ImVec2(480, 480), diff --git a/src/gamedata/StaticGeometryModule.cpp b/src/gamedata/StaticGeometryModule.cpp index d1276fb..5b8b6b5 100644 --- a/src/gamedata/StaticGeometryModule.cpp +++ b/src/gamedata/StaticGeometryModule.cpp @@ -19,13 +19,15 @@ namespace ECS { static bool itemsLoaded = false; +static bool furnitureLoaded = false; static std::list > addQueue; StaticGeometryModule::StaticGeometryModule(flecs::world &ecs) { ecs.module(); ecs.component(); ecs.component(); - ecs.component().on_remove([](flecs::entity e, + ecs.component(); + ecs.component().on_remove([](flecs::entity e, TerrainItemNode &item) { if (item.itemNode) { item.itemNode->destroyAllChildrenAndObjects(); @@ -60,6 +62,10 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs) itemsLoaded = true; return; } + if (!furnitureLoaded) { + loadFurniture(); + furnitureLoaded = true; + } std::list > output; while (!addQueue.empty()) { std::pair item = addQueue.front(); @@ -271,93 +277,141 @@ void StaticGeometryModule::loadItems() << std::endl; std::cout << "position: " << item.id() << " " << position << std::endl; - } + } +} + +void StaticGeometryModule::saveFurniture() +{ + /* No saving - furniture is generated by blender */ +} + +void StaticGeometryModule::loadFurniture() +{ + 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) + long x, long y, std::list *positions) { - 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; + 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); - }); + 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) { - ECS::get().query_builder().build().each( - [&](flecs::entity e, const TerrainItem &item) { - positions->push_back(item.position); - }); + 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) + flecs::entity e, Ogre::Vector3 &position, Ogre::Quaternion &orientation) { - position = e.get().position; - orientation = e.get().orientation; + position = e.get().position; + orientation = e.get().orientation; } void StaticGeometryModule::getItemsProperties( - std::list > *items) + std::list > *items) { - ECS::get().query_builder().build().each( - [&](flecs::entity e, const TerrainItem &item) { - items->push_back({ e, item.properties }); - }); + ECS::get().query_builder().build().each( + [&](flecs::entity e, const TerrainItem &item) { + items->push_back({ e, item.properties }); + }); } void StaticGeometryModule::createItemGeometry(flecs::entity e) { - Geometry::createItemGeometry(e); + Geometry::createItemGeometry(e); } void StaticGeometryModule::destroyItemGeometry(flecs::entity e) { - Geometry::destroyItemGeometry(e); + Geometry::destroyItemGeometry(e); } 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) + 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; + 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) + 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; + 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) + void setTile(const Ogre::String &name, const Ogre::Vector3i &position) { - if (tiles.find(name) == tiles.end()) - return; - tiles[name].positions.insert(packKey(position)); + if (tiles.find(name) == tiles.end()) + return; + tiles[name].positions.insert(packKey(position)); } - void clearTile(const Ogre::String &name, const Ogre::Vector3i &position) + void clearTile(const Ogre::String &name, const Ogre::Vector3i &position) { - if (tiles.find(name) == tiles.end()) - return; - tiles[name].positions.erase(packKey(position)); + if (tiles.find(name) == tiles.end()) + return; + tiles[name].positions.erase(packKey(position)); #if 0 auto pos = std::find(tiles[name].positions.begin(), tiles[name].positions.end(), @@ -365,22 +419,22 @@ struct TiledMeshes { if (pos != tiles[name].positions.end()) tiles[name].positions.erase(pos); #endif - } - 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()), - {} }; + } + 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()), + {} }; #if 0 std::vector vertices; std::vector indices; @@ -477,257 +531,256 @@ struct TiledMeshes { currentVertexOffset = vertices.size(); } #endif - } - 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; + } + 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; + 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()); + 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; + } + } + 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); + 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); + 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::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); + 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; + 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); + } + 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); #if 0 config.createGeneratedLodLevel(15, 0.49f); config.createGeneratedLodLevel(150, 0.75f); #endif - config.advanced.useBackgroundQueue = false; - Ogre::MeshLodGenerator::getSingleton().generateLodLevels( - config); - return settings.mesh; - } - void removeTile(const Ogre::String &name) - { - tiles.erase(name); - } - TiledMeshes() - { - } + config.advanced.useBackgroundQueue = false; + Ogre::MeshLodGenerator::getSingleton().generateLodLevels( + config); + return settings.mesh; + } + void removeTile(const Ogre::String &name) + { + tiles.erase(name); + } + TiledMeshes() + { + } }; - } diff --git a/src/gamedata/StaticGeometryModule.h b/src/gamedata/StaticGeometryModule.h index d79b1e4..f9c1c10 100644 --- a/src/gamedata/StaticGeometryModule.h +++ b/src/gamedata/StaticGeometryModule.h @@ -25,6 +25,11 @@ struct TerrainItemMeshNode { Ogre::SceneNode *itemNode; Ogre::StaticGeometry *geo; }; +struct FurnitureItem { + Ogre::String properties; + std::vector tags; +}; + struct TownCollider {}; struct StaticGeometryModule { @@ -39,6 +44,8 @@ struct StaticGeometryModule { static const Ogre::String &getItemProperties(flecs::entity id); static void saveItems(); static void loadItems(); + static void saveFurniture(); + static void loadFurniture(); static void getItemPositionPerSlot(long x, long y, std::list *positions); static void getItemPositions(std::list *positions); diff --git a/src/gamedata/items/items.cpp b/src/gamedata/items/items.cpp index cb5b895..b5abe58 100644 --- a/src/gamedata/items/items.cpp +++ b/src/gamedata/items/items.cpp @@ -199,9 +199,9 @@ void setupLods(Ogre::LodConfig &config) config.advanced.preventPunchingHoles = true; config.advanced.preventBreakingLines = true; config.createGeneratedLodLevel(10, 0.15f); - config.createGeneratedLodLevel(50, 0.25f); - config.createGeneratedLodLevel(100, 0.36f); - config.createGeneratedLodLevel(200, 0.50f); + // config.createGeneratedLodLevel(50, 0.25f); + // config.createGeneratedLodLevel(100, 0.36f); + // config.createGeneratedLodLevel(200, 0.50f); config.createGeneratedLodLevel(500, 0.85f); config.advanced.useBackgroundQueue = false; Ogre::MeshLodGenerator::getSingleton().generateLodLevels(config); diff --git a/src/gamedata/items/town.cpp b/src/gamedata/items/town.cpp index f2774d4..bf327cb 100644 --- a/src/gamedata/items/town.cpp +++ b/src/gamedata/items/town.cpp @@ -19,7 +19,7 @@ #include "items.h" #include "town.h" -/* TODO: Create roofs with the same Lua script as tiles. +/* * TODO: Create doors and handle them via script. * TODO: Handle doors direction. * TODO: Hide hero mesh if camera is too close. @@ -174,6 +174,7 @@ struct CellsScript { Ogre::String cellScript; nlohmann::json &lot; nlohmann::json &cells; + nlohmann::json &fucells; CellsScript(const Ogre::String &cellScript, nlohmann::json &lot) : L(luaL_newstate()) , currentX(0) @@ -182,6 +183,7 @@ struct CellsScript { , cellScript(cellScript) , lot(lot) , cells(lot["cells"]) + , fucells(lot["furniture_cells"]) { lua_pushlightuserdata(L, this); lua_pushcclosure( @@ -277,6 +279,19 @@ struct CellsScript { 1); lua_setglobal(L, "roof"); lua_pushlightuserdata(L, this); + lua_pushcclosure( + L, + [](lua_State *L) -> int { + CellsScript *_this = static_cast( + lua_touserdata(L, lua_upvalueindex(1))); + int room = lua_tointeger(L, 1); + Ogre::String tags = lua_tostring(L, 2); + _this->place_furniture(room, tags); + return 0; + }, + 1); + lua_setglobal(L, "place_furniture"); + lua_pushlightuserdata(L, this); lua_pushcclosure( L, [](lua_State *L) -> int { @@ -292,6 +307,22 @@ struct CellsScript { 1); lua_setglobal(L, "clear_area"); lua_pushlightuserdata(L, this); + lua_pushcclosure( + L, + [](lua_State *L) -> int { + CellsScript *_this = static_cast( + lua_touserdata(L, lua_upvalueindex(1))); + int minX = lua_tointeger(L, 1); + int minZ = lua_tointeger(L, 2); + int sizeX = lua_tointeger(L, 3); + int sizeZ = lua_tointeger(L, 4); + _this->clear_furniture_area(minX, minZ, sizeX, + sizeZ); + return 0; + }, + 1); + lua_setglobal(L, "clear_furniture_area"); + lua_pushlightuserdata(L, this); lua_pushcclosure( L, [](lua_State *L) -> int { @@ -430,6 +461,11 @@ struct CellsScript { for (auto &cell : cells) cell["id"] = makeCellKey(cell); } + void buildFCellIndex() + { + for (auto &fucell : fucells) + fucell["id"] = makeCellKey(fucell); + } int findCell(int X, int Y, int Z) { int64_t key = makeCellKey(X, Y, Z); @@ -444,6 +480,20 @@ struct CellsScript { } return result; } + int findFCell(int X, int Y, int Z) + { + int64_t key = makeCellKey(X, Y, Z); + int result = -1; + int i; + for (i = 0; i < fucells.size(); i++) { + int64_t ckey = makeCellKey(fucells[i]); + if (key == ckey) { + result = i; + break; + } + } + return result; + } void bitCmd(const Ogre::String &cmd, const Ogre::String &bit) { uint64_t flags = 0; @@ -560,6 +610,22 @@ struct CellsScript { clear(); } } + void clear_furniture() + { + int index = findFCell(currentX, currentY, currentZ); + if (index >= 0) + fucells.erase(index); + } + void clear_furniture_area(int minX, int minZ, int sizeX, int sizeZ) + { + int i, j; + for (i = 0; i < sizeZ; i++) + for (j = 0; j < sizeX; j++) { + currentX = minX + j; + currentZ = minZ + i; + clear_furniture(); + } + } int room(int minX, int minZ, int sizeX, int sizeZ) { int i, j, ret; @@ -621,6 +687,250 @@ struct CellsScript { { lot["roofs"] = nlohmann::json::array(); } + void fcell(const std::vector &tags, + nlohmann::json furniture, int rotation) + { + nlohmann::json jtags = nlohmann::json::array(); + for (const auto &tag : tags) + jtags.push_back(tag); + if (findFCell(currentX, currentY, currentZ) == -1) { + nlohmann::json fucell; + buildFCellIndex(); + fucell["x"] = currentX; + fucell["y"] = currentY; + fucell["z"] = currentZ; + fucell["tags"] = jtags; + fucell["id"] = makeCellKey(fucell); + fucell["furniture"] = furniture; + fucell["rotation"] = rotation; + fucells.push_back(fucell); + } else { + int index = findFCell(currentX, currentY, currentZ); + if (index >= 0) { + fucells[index]["tags"] = jtags; + fucells[index]["furniture"] = furniture; + fucells[index]["rotation"] = rotation; + } + } + } + nlohmann::json select_furniture(const std::vector &tags) + { + nlohmann::json adata = nlohmann::json::array(); + ECS::get().query_builder().build().each( + [&](flecs::entity e, const FurnitureItem &item) { + bool apply = true; + for (auto &t : tags) { + if (std::find(item.tags.begin(), + item.tags.end(), + t) == item.tags.end()) { + apply = false; + break; + } + } + if (apply) { + nlohmann::json jdata = + nlohmann::json::parse( + item.properties); + adata.push_back(jdata); + } + }); + // FIXME: use noise function + return adata[0]; + } + void place_furniture(int room, Ogre::String tags) + { + std::vector atags = split_and_trim(tags, ','); + int i, j; + int minX = rooms[room].minX; + int minZ = rooms[room].minZ; + int sizeX = rooms[room].sizeX; + int sizeZ = rooms[room].sizeZ; + int midX = minX + sizeX / 2; + int midZ = minZ + sizeZ / 2; + std::cout << "room: " << room << " " << minX << " " << minZ + << " " << sizeX << " " << sizeZ << std::endl; + clear_furniture_area(minX, minZ, sizeX, sizeZ); + currentX = midX; + currentZ = minZ; + bool placed = false; + if (isBit("iwallz-") && !isBit("idoorz+") && + !isBit("idoorz-") && !isBit("idoorx+") && + !isBit("idoorx-") && !placed) { + std::vector ptags = atags; + ptags.push_back("essential"); + nlohmann::json furniture = select_furniture(ptags); + std::cout << currentX << " " << currentY << " " + << currentZ << std::endl; + fcell(ptags, furniture, 2); + placed = true; + } + currentX = midX; + currentZ = minZ + sizeZ - 1; + if (isBit("iwallz+") && !isBit("idoorz+") && + !isBit("idoorz-") && !isBit("idoorx+") && + !isBit("idoorx-") && !placed) { + std::vector ptags = atags; + ptags.push_back("essential"); + nlohmann::json furniture = select_furniture(ptags); + std::cout << currentX << " " << currentY << " " + << currentZ << std::endl; + fcell(ptags, furniture, 0); + placed = true; + } + currentX = midX; + currentZ = midZ; + if (isBit("iwallx-") && !isBit("idoorz+") && + !isBit("idoorz-") && !isBit("idoorx+") && + !isBit("idoorx-") && !placed) { + std::vector ptags = atags; + ptags.push_back("essential"); + nlohmann::json furniture = select_furniture(ptags); + std::cout << currentX << " " << currentY << " " + << currentZ << std::endl; + fcell(ptags, furniture, 1); + placed = true; + } + currentX = midX + sizeX - 1; + currentZ = midZ; + if (isBit("iwallx+") && !isBit("idoorz+") && + !isBit("idoorz-") && !isBit("idoorx+") && + !isBit("idoorx-") && !placed) { + std::vector ptags = atags; + ptags.push_back("essential"); + nlohmann::json furniture = select_furniture(ptags); + std::cout << currentX << " " << currentY << " " + << currentZ << std::endl; + fcell(ptags, furniture, 3); + placed = true; + } + if (placed) + return; + if (isBit("windowz-") && !isBit("idoorz+") && + !isBit("idoorz-") && !isBit("idoorx+") && + !isBit("idoorx-") && !placed) { + std::vector ptags = atags; + ptags.push_back("essential"); + nlohmann::json furniture = select_furniture(ptags); + std::cout << currentX << " " << currentY << " " + << currentZ << std::endl; + fcell(ptags, furniture, 2); + placed = true; + } + currentX = midX; + currentZ = minZ + sizeZ - 1; + if (isBit("windowz+") && !isBit("idoorz+") && + !isBit("idoorz-") && !isBit("idoorx+") && + !isBit("idoorx-") && !placed) { + std::vector ptags = atags; + ptags.push_back("essential"); + nlohmann::json furniture = select_furniture(ptags); + std::cout << currentX << " " << currentY << " " + << currentZ << std::endl; + fcell(ptags, furniture, 0); + placed = true; + } + currentX = midX; + currentZ = midZ; + if (isBit("windowx-") && !isBit("idoorz+") && + !isBit("idoorz-") && !isBit("idoorx+") && + !isBit("idoorx-") && !placed) { + std::vector ptags = atags; + ptags.push_back("essential"); + nlohmann::json furniture = select_furniture(ptags); + std::cout << currentX << " " << currentY << " " + << currentZ << std::endl; + fcell(ptags, furniture, 1); + placed = true; + } + currentX = midX + sizeX - 1; + currentZ = midZ; + if (isBit("windowx+") && !isBit("idoorz+") && + !isBit("idoorz-") && !isBit("idoorx+") && + !isBit("idoorx-") && !placed) { + std::vector ptags = atags; + ptags.push_back("essential"); + nlohmann::json furniture = select_furniture(ptags); + std::cout << currentX << " " << currentY << " " + << currentZ << std::endl; + fcell(ptags, furniture, 3); + placed = true; + } + if (placed) + return; + for (i = 1; i < sizeX - 1; i++) { + currentX = minX + i; + currentZ = minZ; + if (isBit("iwallz-") && !isBit("idoorz+") && + !isBit("idoorz-") && !isBit("idoorx+") && + !isBit("idoorx-") && !placed) { + std::vector ptags = atags; + ptags.push_back("essential"); + nlohmann::json furniture = + select_furniture(ptags); + std::cout << currentX << " " << currentY << " " + << currentZ << std::endl; + fcell(ptags, furniture, 2); + placed = true; + break; + } + } + if (placed) + return; + for (i = 1; i < sizeX - 1; i++) { + currentX = minX + i; + currentZ = minZ + sizeZ - 1; + if (isBit("iwallz+") && !isBit("idoorz+") && + !isBit("idoorz-") && !isBit("idoorx+") && + !isBit("idoorx-") && !placed) { + std::vector ptags = atags; + ptags.push_back("essential"); + nlohmann::json furniture = + select_furniture(ptags); + std::cout << currentX << " " << currentY << " " + << currentZ << std::endl; + fcell(ptags, furniture, 0); + placed = true; + break; + } + } + if (placed) + return; + for (i = 1; i < sizeZ - 1; i++) { + currentX = minX; + currentZ = minZ + i; + if (isBit("iwallx-") && !isBit("idoorz+") && + !isBit("idoorz-") && !isBit("idoorx+") && + !isBit("idoorx-") && !placed) { + std::vector ptags = atags; + ptags.push_back("essential"); + nlohmann::json furniture = + select_furniture(ptags); + std::cout << currentX << " " << currentY << " " + << currentZ << std::endl; + fcell(ptags, furniture, 1); + placed = true; + break; + } + } + for (i = 1; i < sizeZ - 1; i++) { + currentX = minX + sizeX - 1; + currentZ = minZ + i; + if (isBit("iwallx+") && !isBit("idoorz+") && + !isBit("idoorz-") && !isBit("idoorx+") && + !isBit("idoorx-") && !placed) { + std::vector ptags = atags; + ptags.push_back("essential"); + nlohmann::json furniture = + select_furniture(ptags); + std::cout << currentX << " " << currentY << " " + << currentZ << std::endl; + fcell(ptags, furniture, 3); + placed = true; + break; + } + } + } + std::vector > roomEdge(int room) { std::vector > seg; @@ -1235,16 +1545,21 @@ bool editLot(const Ogre::String &lotLabel, nlohmann::json &lot) nlohmann::json cells = nlohmann::json::array(); static char text_buffer[16384]; strcpy(text_buffer, ""); + /* copy script data from json to a buffer */ if (lot.find("cellScript") != lot.end()) { Ogre::String script = lot["cellScript"].get(); strncpy(text_buffer, script.c_str(), sizeof(text_buffer)); } + /* edit a buffer and if changed, copy a buffer back to json */ if (ImGui::InputTextMultiline(("Cells Script##" + lotLabel).c_str(), text_buffer, IM_ARRAYSIZE(text_buffer), ImVec2(1000, ImGui::GetTextLineHeight() * 20), ImGuiInputTextFlags_AllowTabInput)) { lot["cellScript"] = Ogre::String(text_buffer); + } + if (ImGui::IsItemDeactivatedAfterEdit()) { + lot["cellScript"] = Ogre::String(text_buffer); changed = true; } if (ImGui::SmallButton(("Execute script...##" + lotLabel).c_str())) { @@ -1252,6 +1567,7 @@ bool editLot(const Ogre::String &lotLabel, nlohmann::json &lot) script.run(); changed = true; } + for (const auto &cell : lot["cells"]) cells.push_back(cell); if (ImGui::CollapsingHeader(("Cells...##" + lotLabel).c_str())) { @@ -1380,11 +1696,15 @@ bool editDistrict(const Ogre::String &districtLabel, nlohmann::json &district) districtLabel + "_lot" + Ogre::StringConverter::toString(lotCount); ImGui::Text("%s", lotLabel.c_str()); - changed = changed || editLot(lotLabel, lot); - ImGui::Separator(); - if (ImGui::SmallButton(("Lot Delete##" + lotLabel).c_str())) { - lots.erase(lotCount); - break; + if (ImGui::CollapsingHeader( + ("Edit lot... ##" + lotLabel).c_str())) { + changed = changed || editLot(lotLabel, lot); + ImGui::Separator(); + if (ImGui::SmallButton( + ("Lot Delete##" + lotLabel).c_str())) { + lots.erase(lotCount); + break; + } } lotCount++; } @@ -1400,9 +1720,12 @@ bool editDistrict(const Ogre::String &districtLabel, nlohmann::json &district) changed = true; } district["lots"] = lots; + float radius = 50.0f; bool plazza = false; if (district.find("plazza") != district.end()) plazza = district["plazza"].get(); + if (district.find("radius") != district.end()) + radius = district["radius"].get(); if (ImGui::Checkbox("Plazza", &plazza)) { changed = true; district["plazza"] = plazza; @@ -1412,6 +1735,10 @@ bool editDistrict(const Ogre::String &districtLabel, nlohmann::json &district) elevation = district["elevation"].get(); if (district.find("height") != district.end()) height = district["height"].get(); + if (ImGui::SliderFloat("Radius", &radius, 20.0f, 100.0f)) { + district["radius"] = radius; + changed = true; + } if (ImGui::SliderFloat("Height", &height, 0.1f, 10.0f)) { district["height"] = height; changed = true; @@ -1535,8 +1862,10 @@ void createTownPopup(const std::pair item) nlohmann::json colorRects = nlohmann::json::object(); if (j.find("colorRects") != j.end()) colorRects = j["colorRects"]; - changed = changed || editColorRects(colorRects); - ImGui::Separator(); + if (ImGui::CollapsingHeader("Edit Color Rects")) { + changed = changed || editColorRects(colorRects); + ImGui::Separator(); + } nlohmann::json districts = nlohmann::json::array(); for (auto &district : j["districts"]) districts.push_back(district); @@ -2565,6 +2894,122 @@ void createTownDoors(flecs::entity e) } } } +void createDecorateFurniture(flecs::entity e, const nlohmann::json &jdistrict, + int index, Ogre::SceneNode *sceneNode, + Ogre::StaticGeometry *geo) +{ + Ogre::MaterialPtr townMaterial = createTownMaterial(e); + const nlohmann::json &jp = jdistrict; + nlohmann::json jlots = nlohmann::json::array(); + float baseHeight = 4.0f; + Ogre::Vector3 localPosition(0, 0, 0); + Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY; + Ogre::Vector3 centerPosition = sceneNode->_getDerivedPosition(); + Ogre::Quaternion centerOrientation = + sceneNode->_getDerivedOrientation(); + float delevation = 0.0f; + float radius = 50.0f; + if (jp.find("elevation") != jp.end()) + delevation = jp["elevation"].get(); + if (jp.find("radius") != jp.end()) + radius = jp["radius"].get(); + from_json(jp["position"], localPosition); + from_json(jp["rotation"], localRotation); + centerPosition = centerPosition + localPosition + + Ogre::Vector3(0, delevation, 0); + centerOrientation = centerOrientation * localRotation; + if (jdistrict.find("lots") != jdistrict.end()) + jlots = jdistrict["lots"]; + for (const auto &jb : jlots) { + float angle = 0.0f; + int depth = 10; + int width = 10; + float distance = radius; + float elevation = 0.0f; + std::cout << jb.dump() << std::endl; + if (jb.find("angle") != jb.end()) + angle = jb["angle"].get(); + if (jb.find("depth") != jb.end()) + depth = jb["depth"].get(); + if (jb.find("width") != jb.end()) + width = jb["width"].get(); + if (jb.find("elevation") != jb.end()) + elevation = jb["elevation"].get(); + + OgreAssert(width > 1 && depth > 1 && baseHeight > 1, + "Bad stuff happen"); + + Ogre::Quaternion rotation = Ogre::Quaternion( + Ogre::Degree(angle), Ogre::Vector3::UNIT_Y); + Ogre::Vector3 offset = centerOrientation * rotation * + (Ogre::Vector3::UNIT_Z * distance); + Ogre::Vector3 worldCenterPosition = + centerPosition + offset + + Ogre::Vector3(0, elevation, 0); + Ogre::Quaternion worldCenterOrientation = + centerOrientation * rotation; + float outOffset = 1.05f; + if (jb.find("furniture_cells") != jb.end()) { + for (auto &jfcell : jb["furniture_cells"]) { + int x = jfcell["x"].get(); + int y = jfcell["y"].get(); + int z = jfcell["z"].get(); + nlohmann::json furniture = jfcell["furniture"]; + Ogre::Vector3 cellOffset(x * 2.0f, y * 4.0f, + z * 2.0f); + Ogre::Vector3 offsetX = worldCenterOrientation * + Ogre::Vector3::UNIT_X * + (float)x * 2.0f; + Ogre::Vector3 offsetZ = worldCenterOrientation * + Ogre::Vector3::UNIT_Z * + (float)z * 2.0f; + Ogre::Vector3 offsetY(0, y * 4.0f, 0); + if (furniture.find("mesh") != furniture.end()) { + Ogre::String meshName = + furniture["mesh"] + .get(); + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton() + .getByName(meshName); + if (!mesh) { + mesh = Ogre::MeshManager::getSingleton() + .load(meshName, + "General"); + Ogre::LodConfig meshconf(mesh); + setupLods(meshconf); + } + int rotation = 2; + if (jfcell.find("rotation") != + jfcell.end()) + rotation = jfcell["rotation"] + .get(); + + Ogre::Vector3 offset = + worldCenterOrientation * + Ogre::Vector3::UNIT_Z * 0.0f; + Ogre::Entity *ent = + ECS::get() + .mScnMgr->createEntity( + meshName); + geo->addEntity( + ent, + worldCenterPosition + offsetX + + offsetZ + offsetY + + offset, + worldCenterOrientation * + Ogre::Quaternion( + Ogre::Degree( + 90.0f * + (float)rotation), + Ogre::Vector3:: + UNIT_Y)); + ECS::get() + .mScnMgr->destroyEntity(ent); + } + } + } + } +} void createDecorateDoors(flecs::entity e, const nlohmann::json &jdistrict, int index, Ogre::SceneNode *sceneNode, @@ -3049,6 +3494,8 @@ void createTown(flecs::entity e, Ogre::SceneNode *sceneNode, geo); createDecorateDoors(e, jdistrict, index, sceneNode, geo); + createDecorateFurniture(e, jdistrict, index, sceneNode, + geo); index++; } }