From 3f99099919fb02fc5e217315f4865c1d24c985c3 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Sun, 14 Dec 2025 06:59:12 +0300 Subject: [PATCH] Creating pier --- CMakeLists.txt | 1 + assets/blender/buildings/parts/CMakeLists.txt | 17 + assets/blender/buildings/parts/pier.blend | 3 + resources.cfg | 1 + src/editor/CMakeLists.txt | 4 +- src/gamedata/CMakeLists.txt | 5 +- src/gamedata/GUIModule.cpp | 370 +++++- src/gamedata/GameData.cpp | 2 + src/gamedata/StaticGeometryModule.cpp | 1115 +++++++++++++++++ src/gamedata/StaticGeometryModule.h | 43 + src/gamedata/TerrainModule.cpp | 263 +--- src/gamedata/TerrainModule.h | 14 +- src/physics/physics.cpp | 2 + 13 files changed, 1581 insertions(+), 259 deletions(-) create mode 100644 assets/blender/buildings/parts/CMakeLists.txt create mode 100644 assets/blender/buildings/parts/pier.blend create mode 100644 src/gamedata/StaticGeometryModule.cpp create mode 100644 src/gamedata/StaticGeometryModule.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 89f0715..b7564a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,7 @@ add_subdirectory(src/world) add_subdirectory(src/tests) add_subdirectory(src/physics) add_subdirectory(src/editor) +add_subdirectory(assets/blender/buildings/parts) add_executable(Game Game.cpp ${WATER_SRC}) target_include_directories(Game PRIVATE src/gamedata) diff --git a/assets/blender/buildings/parts/CMakeLists.txt b/assets/blender/buildings/parts/CMakeLists.txt new file mode 100644 index 0000000..f19fe40 --- /dev/null +++ b/assets/blender/buildings/parts/CMakeLists.txt @@ -0,0 +1,17 @@ +project(building-parts) +set(PARTS_FILES pier.blend) +set(PARTS_OUTPUT_DIRS) +foreach(PARTS_FILE ${PARTS_FILES}) + get_filename_component(FILE_NAME ${PARTS_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}/${PARTS_FILE} + -b -Y -P + ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_building_parts.py + -- ${PARTS_OUTPUT_DIR} + 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() +add_custom_target(import_building_parts ALL DEPENDS ${PARTS_OUTPUT_DIRS}) diff --git a/assets/blender/buildings/parts/pier.blend b/assets/blender/buildings/parts/pier.blend new file mode 100644 index 0000000..113db85 --- /dev/null +++ b/assets/blender/buildings/parts/pier.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c5922f4f0259a34484e198e1f111cfe19cd8c326d045fd0f9f1f9767611d1ff +size 132720 diff --git a/resources.cfg b/resources.cfg index d6240fc..f68a48d 100644 --- a/resources.cfg +++ b/resources.cfg @@ -20,6 +20,7 @@ FileSystem=resources/terrain [General] FileSystem=skybox FileSystem=resources/buildings +FileSystem=resources/buildings/parts/pier FileSystem=resources/vehicles FileSystem=resources/debug FileSystem=resources/fonts diff --git a/src/editor/CMakeLists.txt b/src/editor/CMakeLists.txt index 198d7ad..424ca1f 100644 --- a/src/editor/CMakeLists.txt +++ b/src/editor/CMakeLists.txt @@ -9,7 +9,7 @@ find_package(OgreProcedural REQUIRED CONFIG) find_package(pugixml REQUIRED CONFIG) find_package(flecs REQUIRED CONFIG) -set(COPY_DIRECTORIES resources skybox water) +set(COPY_DIRECTORIES resources skybox water resources/buildings/parts) set(INSTALL_DEPS ${CMAKE_CURRENT_BINARY_DIR}/resources.cfg) foreach(DIR_NAME ${COPY_DIRECTORIES}) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME} @@ -19,7 +19,7 @@ foreach(DIR_NAME ${COPY_DIRECTORIES}) list(APPEND INSTALL_DEPS ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME}) endforeach() -add_custom_target(install_resources DEPENDS ${INSTALL_DEPS}) +add_custom_target(install_resources DEPENDS import_buildings import_building_parts ${INSTALL_DEPS}) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/resources.cfg COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/../../resources.cfg ${CMAKE_CURRENT_BINARY_DIR}/resources.cfg DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/../../resources.cfg diff --git a/src/gamedata/CMakeLists.txt b/src/gamedata/CMakeLists.txt index 2a4649d..d1e426c 100644 --- a/src/gamedata/CMakeLists.txt +++ b/src/gamedata/CMakeLists.txt @@ -3,17 +3,18 @@ set(CMAKE_CXX_STANDARD 17) find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG) find_package(Bullet REQUIRED) find_package(nlohmann_json REQUIRED) +find_package(OgreProcedural REQUIRED CONFIG) add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp GUIModule.cpp LuaData.cpp WorldMapModule.cpp BoatModule.cpp EventTriggerModule.cpp CharacterAnimationModule.cpp PhysicsModule.cpp EventModule.cpp CharacterManagerModule.cpp - VehicleManagerModule.cpp AppModule.cpp SmartObject.cpp SlotsModule.cpp goap.cpp) + VehicleManagerModule.cpp AppModule.cpp StaticGeometryModule.cpp SmartObject.cpp SlotsModule.cpp goap.cpp) target_link_libraries(GameData PUBLIC lua flecs::flecs_static nlohmann_json::nlohmann_json OgreMain OgreBites - OgrePaging OgreTerrain OgreOverlay + OgrePaging OgreTerrain OgreOverlay OgreProcedural::OgreProcedural PRIVATE sceneloader world-build physics editor) target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${BULLET_INCLUDE_DIR} ../luaaa) target_compile_definitions(GameData PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION) diff --git a/src/gamedata/GUIModule.cpp b/src/gamedata/GUIModule.cpp index bfd65d0..1a89e45 100644 --- a/src/gamedata/GUIModule.cpp +++ b/src/gamedata/GUIModule.cpp @@ -9,11 +9,16 @@ #include #include #include +#include +#include +#include +#include #include "GameData.h" #include "Components.h" #include "LuaData.h" #include "AppModule.h" #include "TerrainModule.h" +#include "StaticGeometryModule.h" #include "EditorGizmoModule.h" #include "GUIModule.h" namespace ECS @@ -701,6 +706,7 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { float strength = 0.0f; int size = 0; long slot_x, slot_y; + float cursorAngle = 0; void updateWorldTexture() { // Get the hardware pixel buffer @@ -733,21 +739,16 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { ECS::get() .mTerrainGroup->convertWorldPositionToTerrainSlot( worldPos, &x, &y); - int i, j; - for (j = -1; j < 2; j++) - for (i = -1; i < 2; i++) { - Ogre::Terrain *terrain = - ECS::get() - .mTerrainGroup->getTerrain( - x + j, y + i); - if (terrain && terrain->isLoaded()) { - terrain->dirty(); - terrain->update(true); - terrain->waitForDerivedProcesses(); - } - } - - ECS::get().mTerrainGroup->update(true); + for (auto &slot : + ECS::get().mTerrainGroup->getTerrainSlots()) { + Ogre::uint32 page = + ECS::get().mTerrainGroup->packIndex( + slot.second->x, slot.second->y); + ECS::get() + .mTerrainPagedWorldSection->unloadPage(page, + false); + } + ECS::get().mTerrainGroup->update(false); } void setCursorPos(Ogre::Vector3 &cursorPosition, Ogre::Quaternion &orientation) @@ -793,6 +794,229 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { .mCameraGoal->_getDerivedOrientation()); updateHeightmap(); } + void setCursorSelectedPos(flecs::entity e, Ogre::Vector3 &position, + Ogre::Quaternion &orientation) + { + StaticGeometryModule::getItemPositionAndRotation(e, position, + orientation); + selected_x = TerrainModule::get_img_x(position.x); + selected_y = TerrainModule::get_img_y(position.z); + ECS::get().sceneNode->_setDerivedPosition( + position); + ECS::get().sceneNode->_setDerivedOrientation( + orientation); + } + void setCameraSelectedPos(flecs::entity e) + { + Ogre::Vector3 cursorPos; + Ogre::Quaternion cursorOrientation; + setCursorSelectedPos(e, cursorPos, cursorOrientation); + Ogre::Vector3 cameraPos = + ECS::get().mCameraPivot->_getDerivedPosition(); + Ogre::Vector3 cameraOffset = + cursorOrientation * Ogre::Vector3::UNIT_Z * 30.0f; + cameraPos.x = cursorPos.x; + cameraPos.z = cursorPos.z; + cameraPos += cameraOffset; + cameraPos.y = + TerrainModule::get_height( + ECS::get().mTerrainGroup, cameraPos) + + 10.0f; + if (cameraPos.y < 0.0f) + cameraPos.y = 10.0f; + ECS::get().mCameraPivot->_setDerivedPosition(cameraPos); + ECS::get().mCameraPivot->_setDerivedOrientation( + cursorOrientation); + cameraPos = + ECS::get().mCameraGoal->_getDerivedPosition(); + ECS::get().mCameraNode->_setDerivedPosition(cameraPos); + ECS::get().mCameraNode->_setDerivedOrientation( + ECS::get() + .mCameraGoal->_getDerivedOrientation()); + updateHeightmap(); + } + void setSurfaceLevel(int actualSize, int center_x, int center_y, + float level) + { + int i, j; + float original = level; + if (actualSize == 1) + level += 0.4f; + else if (actualSize == 2) + level += 0.35f; + original = Ogre::Math::Clamp(original, 0.0f, 1.0f); + for (i = -actualSize; i < actualSize + 1; i++) + for (j = -actualSize; j < actualSize + 1; j++) { + if (i * i + j * j > actualSize * actualSize) + continue; + if (center_x + j < 0 || + center_x + j >= worldMap->getWidth()) + continue; + if (center_y + i < 0 || + center_y + i >= worldMap->getHeight()) + continue; + Ogre::ColourValue cv = + worldMapImage.getColourAt( + center_x + j, center_y + i, 0); + cv.r = original; + worldMapImage.setColourAt(cv, center_x + j, + center_y + i, 0); + } + updateWorldTexture(); + updateHeightmap(); + } + void setHarbourSurface() + { + int base_size = 3; + float base_height = 0.517f; + float base_step = 0.1f; + float deep = 0.25f; + float shallow = 0.35f; + float maxStep = 600.0f; + Ogre::Vector3 basePos = + ECS::get().sceneNode->_getDerivedPosition(); + Ogre::Quaternion baseRot = + ECS::get() + .sceneNode->_getDerivedOrientation(); + float baseOffset = 200.0f; + Ogre::Vector3 stepOffset = + baseRot * Ogre::Vector3::NEGATIVE_UNIT_Z * baseOffset; + std::vector heights = { deep, shallow, base_height, + base_height + base_step, + base_height + base_step * 2.0f }; + int step_count = 0; + for (float height : heights) { + float localStep = 0.0f; + Ogre::Vector3 currentPosition = + basePos + stepOffset * (float)step_count - + stepOffset * 0.5f; + float goTill = maxStep; + if (step_count < 2) + goTill += 420.0f; + while (localStep < + goTill - 150.0f * (float)step_count) { + localStep += baseOffset; + currentPosition += stepOffset; + int center_x = TerrainModule::get_img_x( + currentPosition.x); + int center_y = TerrainModule::get_img_y( + currentPosition.z); + int size = base_size + 10 - step_count * 2; + if (step_count < 2) + size = base_size + 14 - step_count * 2; + if (step_count == 0) + size += 1; + + setSurfaceLevel(size, center_x, center_y, + height); + } + step_count++; + } + } + /* This is editor function */ + static bool findPierOffset(float &offset) + { + Ogre::Vector3 basePos = + ECS::get().sceneNode->_getDerivedPosition(); + Ogre::Quaternion baseRot = + ECS::get() + .sceneNode->_getDerivedOrientation(); + Ogre::Vector3 direction = baseRot * Ogre::Vector3(0, 0, 1); + float length = 0.0f; + while (length < 250.0f) { + Ogre::Vector3 currentPosition = + basePos + direction * length; + float height = + ECS::get() + .mTerrainGroup->getHeightAtWorldPosition( + currentPosition); + if (height < -4.0f) { + offset = length; + break; + } + length += 2.0f; + } + return length < 250.0f; + } + static bool findPierOffsetAndLengthAndDepth(float &offset, + float &length, float &depth) + { + if (!findPierOffset(offset)) + return false; + Ogre::Vector3 basePos = + ECS::get().sceneNode->_getDerivedPosition(); + Ogre::Quaternion baseRot = + ECS::get() + .sceneNode->_getDerivedOrientation(); + Ogre::Vector3 direction = baseRot * Ogre::Vector3(0, 0, 1); + length = 0.0f; + depth = 4.0f; + while (length < 60.0f) { + Ogre::Vector3 currentPosition = + basePos + direction * (offset + length); + float height = + ECS::get() + .mTerrainGroup->getHeightAtWorldPosition( + currentPosition); + if (depth < -height) + depth = -height; + if (height > -4.0f) + break; + length += 6.0f; + } + return true; + } + static void findPierHeight(float maxLength, float &height) + { + Ogre::Vector3 basePos = + ECS::get().sceneNode->_getDerivedPosition(); + Ogre::Quaternion baseRot = + ECS::get() + .sceneNode->_getDerivedOrientation(); + Ogre::Vector3 direction = baseRot * Ogre::Vector3(0, 0, 1); + float length = 0.0f; + height = 0.0f; + while (length < 60.0f) { + Ogre::Vector3 currentPosition = + basePos + direction * (length); + float dheight = + ECS::get() + .mTerrainGroup->getHeightAtWorldPosition( + currentPosition); + if (height < dheight) + height = dheight; + length += 1.0f; + } + } + void createHarbourItem() + { + Ogre::Vector3 itemPosition = + ECS::get().sceneNode->_getDerivedPosition(); + Ogre::Quaternion itemOrientation = + ECS::get() + .sceneNode->_getDerivedOrientation(); + float pierLength, pierDepth; + float pierOffset; + float pierHeight; + if (!findPierOffsetAndLengthAndDepth(pierOffset, pierLength, + pierDepth)) + return; + findPierHeight(pierOffset + pierLength, pierHeight); + flecs::entity e = StaticGeometryModule::createItem( + itemPosition, itemOrientation, "harbour"); + Ogre::String prop = StaticGeometryModule::getItemProperties(e); + nlohmann::json j = nlohmann::json::parse(prop); + j["pierOffset"] = pierOffset; + j["pierLength"] = pierLength; + j["pierDepth"] = pierDepth; + j["pierHeight"] = pierHeight; + StaticGeometryModule::setItemProperties(e, j.dump()); + // setHarbourSurface(); + StaticGeometryModule::saveItems(); + // updateWorldTexture(); + // updateHeightmap(); + // TerrainModule::save_heightmap(); + } void worldMapView() { OgreAssert(TerrainModule::get_img_x(0) == @@ -885,20 +1109,29 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { 1.0f, IM_COL32(0, 255, 0, 255)); { std::list positions; - TerrainModule::getItemPositions(&positions); + StaticGeometryModule::getItemPositions(&positions); for (auto pos : positions) { int item_x = TerrainModule::get_img_x(pos.x); int item_y = TerrainModule::get_img_y(pos.z); draw_list->AddCircleFilled( ImVec2(top_x + item_x, top_y + item_y), 3.0f, IM_COL32(255, 255, 0, 255)); - std::cout << pos << std::endl; } } if (locationSelected) draw_list->AddCircleFilled( ImVec2(top_x + selected_x, top_y + selected_y), 4.0f, IM_COL32(64, 255, 64, 255)); + { + Ogre::Vector3 cursorPos = + ECS::get() + .sceneNode->_getDerivedPosition(); + int cursor_x = TerrainModule::get_img_x(cursorPos.x); + int cursor_y = TerrainModule::get_img_y(cursorPos.z); + draw_list->AddCircleFilled( + ImVec2(top_x + cursor_x, top_y + cursor_y), + 4.0f, IM_COL32(255, 64, 64, 128)); + } ImGui::PopStyleVar(); ImGui::Spacing(); ImGui::EndChild(); @@ -924,16 +1157,32 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { position.y, position.z); } if (ImGui::Button("Update terrain")) { - ECS::get().mTerrainGroup->update(true); + ECS::get().mTerrainGroup->update(false); } - ImGui::SliderFloat("Strength...", &strength, 0.0f, 0.2f); - ImGui::SliderInt("Size", &size, 0, 8); + if (ImGui::Button("Update cursor position")) { + Ogre::Vector3 position = + ECS::get() + .sceneNode->_getDerivedPosition(); + position.y = + ECS::get() + .mTerrainGroup + ->getHeightAtWorldPosition(position); + ECS::get().sceneNode->_setDerivedPosition( + position); + } + ImGui::SliderFloat("Strength...", &strength, 0.0f, 1.0f); + ImGui::SliderInt("Size", &size, 0, 32); + ImGui::SliderFloat("Cursor Angle...", &cursorAngle, -180.0f, + 180.0f); bool riseLower = false; bool riseLower2 = false; bool smooth = false; bool setLevel = false; float setLevelValue = 0.0f; float riseLowerChange = 0.0f; + ECS::get().sceneNode->_setDerivedOrientation( + Ogre::Quaternion(Ogre::Degree(cursorAngle), + Ogre::Vector3::UNIT_Y)); if (ImGui::Button("Elevate")) { riseLower = true; riseLowerChange = strength; @@ -957,9 +1206,19 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { setLevelValue = 0.0f; } ImGui::SameLine(); - if (ImGui::Button("Highest")) { + if (ImGui::Button("Deep")) { setLevel = true; - setLevelValue = 1.0f; + setLevelValue = 0.25f; + } + ImGui::SameLine(); + if (ImGui::Button("Shallow1")) { + setLevel = true; + setLevelValue = 0.35f; + } + ImGui::SameLine(); + if (ImGui::Button("Shallow2")) { + setLevel = true; + setLevelValue = 0.47f; } ImGui::SameLine(); if (ImGui::Button("Beach")) { @@ -994,16 +1253,56 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { setLevel = true; setLevelValue = 0.646f; } - if (ImGui::Button("Harbour")) { - Ogre::Vector3 itemPosition = - ECS::get() - .sceneNode->_getDerivedPosition(); - Ogre::Quaternion itemOrientation = - ECS::get() - .sceneNode->_getDerivedOrientation(); - TerrainModule::createItem(itemPosition, itemOrientation, - "harbour"); - TerrainModule::saveItems(); + ImGui::SameLine(); + if (ImGui::Button("Highest")) { + setLevel = true; + setLevelValue = 1.0f; + } + if (ImGui::Button("Harbour")) + createHarbourItem(); + flecs::entity selected_item; + bool item_is_selected = false; + { + std::list > items; + StaticGeometryModule::getItemsProperties(&items); + for (const auto &item : items) { + Ogre::String label = + Ogre::StringConverter::toString( + item.first.id()); + label += item.second.substr(0, 32); + if (ImGui::SmallButton( + label.c_str())) { /* select */ + selected_item = item.first; + item_is_selected = true; + setCameraSelectedPos(selected_item); + } + ImGui::SameLine(); + Ogre::String upd_label = + "Update Height##" + + Ogre::StringConverter::toString( + item.first.id()); + if (ImGui::SmallButton(upd_label.c_str())) { + TerrainItem &uitem = + item.first + .get_mut(); + uitem.position.y = + ECS::get() + .mTerrainGroup + ->getHeightAtWorldPosition( + uitem.position); + StaticGeometryModule::saveItems(); + } + ImGui::SameLine(); + Ogre::String del_label = + "delete##" + + Ogre::StringConverter::toString( + item.first.id()); + if (ImGui::SmallButton(del_label.c_str())) { + item.first.destruct(); + StaticGeometryModule::saveItems(); + } + ImGui::Spacing(); + } } if (riseLower) { int actualSize = 1 + size * 2; @@ -1058,12 +1357,6 @@ struct EditorGUIListener : public Ogre::RenderTargetListener { float actualStrength = riseLowerChange / (1.0f + (float)(i * i + j * j)); - if (i * i + j * j == - actualSize * actualSize) { - std::cout << actualStrength - << std::endl; - // OgreAssert(false, "strength"); - } Ogre::ColourValue cv = worldMapImage.getColourAt( selected_x + j, @@ -1597,6 +1890,7 @@ EditorGUIModule::EditorGUIModule(flecs::world &ecs) { ecs.module(); ecs.import (); + ecs.import (); ecs.component() .on_add([](GUI &gui) { gui.enabled = true; diff --git a/src/gamedata/GameData.cpp b/src/gamedata/GameData.cpp index 1591cc7..aa8d086 100644 --- a/src/gamedata/GameData.cpp +++ b/src/gamedata/GameData.cpp @@ -99,6 +99,7 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, nullptr, nullptr, nullptr, + nullptr, false, { 0, 0, 0 } }); if (!ecs.has()) @@ -175,6 +176,7 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, nullptr, nullptr, nullptr, + nullptr, false, { 0, 0, 0 } }); ecs.set({ true, true, true, false, false, "", {}, -1 }); diff --git a/src/gamedata/StaticGeometryModule.cpp b/src/gamedata/StaticGeometryModule.cpp new file mode 100644 index 0000000..6965768 --- /dev/null +++ b/src/gamedata/StaticGeometryModule.cpp @@ -0,0 +1,1115 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "Components.h" +#include "GameData.h" +#include "TerrainModule.h" +#include "StaticGeometryModule.h" + +namespace ECS +{ + +StaticGeometryModule::StaticGeometryModule(flecs::world &ecs) +{ + ecs.module(); + ecs.component(); + ecs.component(); + ecs.component().on_remove( + [](flecs::entity e, TerrainItemNode &item) { + if (item.itemNode) { + item.itemNode->destroyAllChildrenAndObjects(); + item.itemNode->getCreator()->destroySceneNode( + item.itemNode); + item.itemNode = nullptr; + } + }); + ecs.import (); + ecs.observer("LoadTerrainItems") + .event(flecs::OnSet) + .each([](const Terrain &terrain) { + if (terrain.mTerrainGroup) + loadItems(); + }); + if (!Ogre::MeshLodGenerator::getSingletonPtr()) + new Ogre::MeshLodGenerator(); +} +void StaticGeometryModule::addGeometryForSlot(long x, long y) +{ + std::pair slot = { x, y }; + flecs::entity parent = + ECS::get().query_builder().build().find( + [&slot](const TerrainSlotParent &parent) { + return parent.slot == slot; + }); + std::cout << "addGeometryForSlot: " << x << " " << y << std::endl; + std::list items; + if (parent.is_valid()) { + ECS::get() + .query_builder() + .with(flecs::ChildOf, parent) + .without() + .write() + .build() + .each([&](flecs::entity e, const TerrainItem &item) { + std::cout << "item: " << e.id() << std::endl; + items.push_back(e); + }); + for (auto e : items) { + createItemGeometry(e); + } + } +} +void StaticGeometryModule::removeGeometryForSlot(long x, long y) +{ + 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) +{ + 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) +{ + OgreAssert(id.is_valid(), "bad id"); + id.get_mut().properties = properties; + id.modified(); +} +const Ogre::String &StaticGeometryModule::getItemProperties(flecs::entity id) +{ + OgreAssert(id.is_valid(), "bad id"); + return id.get().properties; +} +static void to_json(nlohmann::json &j, const Ogre::Vector3 &position) +{ + j["x"] = position.x; + j["y"] = position.y; + j["z"] = position.z; +} +static void to_json(nlohmann::json &j, const Ogre::Quaternion &orientation) +{ + j["w"] = orientation.w; + j["x"] = orientation.x; + j["y"] = orientation.y; + j["z"] = orientation.z; +} +static void from_json(const nlohmann::json &j, Ogre::Vector3 &position) +{ + position.x = j["x"].get(); + position.y = j["y"].get(); + position.z = j["z"].get(); +} +static void from_json(const nlohmann::json &j, Ogre::Quaternion &orientation) +{ + orientation.w = j["w"].get(); + orientation.x = j["x"].get(); + orientation.y = j["y"].get(); + orientation.z = j["z"].get(); +} +void StaticGeometryModule::saveItems() +{ + 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); + jitemlist.push_back(jitem); + }); + fout << jitemlist.dump(); + fout.close(); +} +void StaticGeometryModule::loadItems() +{ + 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(); + 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); + item.set({ position, orientation, properties }); + std::cout << "createItem: " << x << " " << y << " " << item.id() + << std::endl; + std::cout << "position: " << item.id() << " " << position + << std::endl; + } +} +void StaticGeometryModule::getItemPositionPerSlot( + 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; + }); + 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); + }); +} +void StaticGeometryModule::getItemPositionAndRotation( + flecs::entity e, Ogre::Vector3 &position, Ogre::Quaternion &orientation) +{ + position = e.get().position; + orientation = e.get().orientation; +} +void StaticGeometryModule::getItemsProperties( + std::list > *items) +{ + ECS::get().query_builder().build().each( + [&](flecs::entity e, const TerrainItem &item) { + items->push_back({ e, item.properties }); + }); +} +void StaticGeometryModule::createItemGeometry(flecs::entity e) +{ + OgreAssert(!e.has(), "Geometry already created"); + Ogre::String props = e.get().properties; + nlohmann::json jp = nlohmann::json::parse(props); + Ogre::SceneNode *itemNode = ECS::get() + .mScnMgr->getRootSceneNode() + ->createChildSceneNode(); + itemNode->_setDerivedPosition(e.get().position); + itemNode->_setDerivedOrientation(e.get().orientation); + if (jp.find("staticMesh") != jp.end()) { + Ogre::String meshName = jp["staticMesh"].get(); + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton().getByName(meshName); + if (mesh) { + Ogre::Entity *ent = + ECS::get().mScnMgr->createEntity( + mesh); + itemNode->attachObject(ent); + } + } else if (jp.find("type") != jp.end()) { + Ogre::String itemType = jp["type"].get(); + std::cout << "type: " << itemType << std::endl; + std::cout << "props: " << props << std::endl; + if (itemType == "harbour") { + createHarbour(e, itemNode); + e.set({ itemNode }); + } + } else { + std::cout << "can't build item" << std::endl; + std::cout << "props: " << props << std::endl; + OgreAssert(false, "can't create item"); + } +} +struct harbourMaker { + Ogre::Entity *planks, *pillar, *beam; + harbourMaker(flecs::entity e) + { + planks = ECS::get().mScnMgr->createEntity( + "Plank" + Ogre::StringConverter::toString(e.id()), + "pier-plank.glb"); + beam = ECS::get().mScnMgr->createEntity( + "Beam" + Ogre::StringConverter::toString(e.id()), + "pier-beam.glb"); + pillar = ECS::get().mScnMgr->createEntity( + "Pillar" + Ogre::StringConverter::toString(e.id()), + "pier-pillar.glb"); +#if 1 + Ogre::Entity *entities[] = { planks, pillar, beam }; + for (Ogre::Entity *ent : entities) { + Ogre::MeshPtr mesh = ent->getMesh(); + Ogre::LodConfig config(mesh); + config.advanced.useCompression = true; + config.advanced.useVertexNormals = true; + config.advanced.preventPunchingHoles = true; + config.advanced.preventBreakingLines = true; + config.createGeneratedLodLevel(10, 0.15f); + config.createGeneratedLodLevel(30, 0.25f); +#if 0 + if (ent == planks) + config.createManualLodLevel( + 20, "pier-plank-LOD.glb"); + else +#endif + config.createGeneratedLodLevel(60, 0.36f); + config.createGeneratedLodLevel(150, 0.65f); + // config.createGeneratedLodLevel(20, 0.95); + config.advanced.useBackgroundQueue = false; + Ogre::MeshLodGenerator::getSingleton().generateLodLevels( + config); + } +#endif + } +}; +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)); +#if 0 + auto pos = std::find(tiles[name].positions.begin(), + tiles[name].positions.end(), + packKey(position)); + 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()), + {} }; +#if 0 + std::vector vertices; + std::vector indices; + int count = mesh->getNumSubMeshes(); + int i, j; + int indexCount = 0; + int vertexCount = 0; + int sharedVertexOffset = 0; + for (i = 0; i < count; i++) { + Ogre::SubMesh *submesh = mesh->getSubMesh(i); + indexCount += submesh->indexData->indexCount; + if (submesh->useSharedVertices) + vertexCount += + mesh->sharedVertexData->vertexCount; + else + vertexCount += submesh->vertexData->vertexCount; + } + indices.reserve(indexCount); + vertices.reserve(vertexCount); + size_t currentVertexOffset = 0; + bool added_shared = false; + for (i = 0; i < count; i++) { + Ogre::SubMesh *submesh = mesh->getSubMesh(i); + Ogre::VertexData *vertex_data = + submesh->useSharedVertices ? + mesh->sharedVertexData : + submesh->vertexData; + bool add_vertices = + (submesh->useSharedVertices && !added_shared) || + !submesh->useSharedVertices; + if (add_vertices) { + if (submesh->useSharedVertices) + sharedVertexOffset = vertices.size(); + const Ogre::VertexDeclaration *decl = + vertex_data->vertexDeclaration; + const Ogre::VertexBufferBinding *bind = + vertex_data->vertexBufferBinding; + const Ogre::VertexElement *position_element = + decl->findElementBySemantic( + Ogre::VES_POSITION); + if (!position_element) + continue; + Ogre::HardwareVertexBufferSharedPtr vbuf = + bind->getBuffer( + position_element->getSource()); + unsigned char *vertex_buffer = static_cast< + unsigned char *>(vbuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + int vertexSize = vbuf->getVertexSize(); + for (j = 0; j < vertex_data->vertexCount; j++) { + float *position_data; + position_element + ->baseVertexPointerToElement( + vertex_buffer, + &position_data); + vertices.push_back( + { position_data[0], + position_data[1], + position_data[2] }); + vertex_buffer += vertexSize; + } + if (submesh->useSharedVertices) + added_shared = true; + vbuf->unlock(); + } + Ogre::HardwareIndexBufferSharedPtr ibuf = + submesh->indexData->indexBuffer; + size_t numIndices = submesh->indexData->indexCount; + size_t vertexOffset = submesh->useSharedVertices ? + sharedVertexOffset : + currentVertexOffset; + if (ibuf->getType() == + Ogre::HardwareIndexBuffer::IT_32BIT) { + unsigned int *pIndices = static_cast< + unsigned int *>(ibuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + for (j = 0; j < numIndices; j++) { + indices.push_back( + (uint32_t)pIndices[j] + + vertexOffset); + } + ibuf->unlock(); + } else { + unsigned short *pIndices = static_cast< + unsigned short *>(ibuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + for (j = 0; j < numIndices; j++) { + indices.push_back( + (uint32_t)pIndices[j] + + vertexOffset); + } + ibuf->unlock(); + } + 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; + } + } + 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); +#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() + { + } +}; +void StaticGeometryModule::createHarbour(flecs::entity e, + Ogre::SceneNode *sceneNode) +{ + std::cout << "createHarbour " << e.id() << std::endl; + harbourMaker hm(e); + Ogre::StaticGeometry *geo = + ECS::get().mScnMgr->createStaticGeometry( + "pier_" + Ogre::StringConverter::toString(e.id())); + geo->setRegionDimensions(Ogre::Vector3(140, 140, 140)); + Ogre::Vector3 geoposition = sceneNode->_getDerivedPosition(); + geoposition.y = 0.0f; + geo->setOrigin(geoposition); +#if 1 + Ogre::MaterialPtr pierMaterial; + pierMaterial = Ogre::MaterialManager::getSingleton().getByName( + "proceduralMaterialPier" + + Ogre::StringConverter::toString(e.id())); + if (!pierMaterial) { + Procedural::TextureBuffer wood(128); + Procedural::Colours(&wood) + .setColourBase(Ogre::ColourValue(0.8f, 0.6f, 0, 1)) + .setColourPercent(Ogre::ColourValue(0.15f, 0.1f, 0, 1)) + .process(); + // Procedural::RectangleTexture woodDraw(&wood); + Ogre::TexturePtr pierTexture = wood.createTexture( + "proceduralTexturePier" + + Ogre::StringConverter::toString(e.id())); + pierMaterial = Ogre::MaterialManager::getSingletonPtr()->create( + "proceduralMaterialPier" + + Ogre::StringConverter::toString(e.id()), + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); + pierMaterial->getTechnique(0)->getPass(0)->setShininess(0); + pierMaterial->getTechnique(0)->getPass(0)->setDiffuse( + Ogre::ColourValue::White); + pierMaterial->getTechnique(0)->getPass(0)->setSpecular( + Ogre::ColourValue(1.0f, 1.0f, 0.9f)); + pierMaterial->getTechnique(0) + ->getPass(0) + ->createTextureUnitState( + "proceduralTexturePier" + + Ogre::StringConverter::toString(e.id())); + if (Ogre::RTShader::ShaderGenerator::initialize()) { + pierMaterial->prepare(); + Ogre::RTShader::ShaderGenerator *mShaderGenerator = + Ogre::RTShader::ShaderGenerator:: + getSingletonPtr(); + mShaderGenerator->createShaderBasedTechnique( + *pierMaterial, + Ogre::MaterialManager::DEFAULT_SCHEME_NAME, + Ogre::RTShader::ShaderGenerator:: + DEFAULT_SCHEME_NAME); + Ogre::RTShader::RenderState *pMainRenderState = + mShaderGenerator->getRenderState( + Ogre::RTShader::ShaderGenerator:: + DEFAULT_SCHEME_NAME, + *pierMaterial); + } + } +#endif + + Ogre::Vector3 position = sceneNode->_getDerivedPosition(); + Ogre::String props = e.get().properties; + nlohmann::json jp = nlohmann::json::parse(props); + float pierHeight = 0.0f, pierOffset = 0.0f, pierLength = 6.0f, + pierDepth = 6.0f; + if (jp.find("pierOffset") != jp.end()) + pierOffset = jp["pierOffset"].get(); + if (jp.find("pierLength") != jp.end()) + pierLength = jp["pierLength"].get(); + if (jp.find("pierDepth") != jp.end()) + pierDepth = jp["pierDepth"].get(); + if (jp.find("pierHeight") != jp.end()) + pierHeight = jp["pierHeight"].get(); + Procedural::TriangleBuffer tb; + float plankLength = 2.0f; + float plankWidth = 4.01f; + const float beamLength = 6.0f; + const float beamWidth = 5.5f; + if (pierLength < 12.0f) + pierLength = 12.0f; + auto processGrid = [&sceneNode, &geo](float stepLength, float length, + float zoffset, float xofft, + Ogre::Entity *ent) { + float step = 0.0f; + while (step < length) { + Ogre::Vector3 worldPosition = + sceneNode->_getDerivedPosition() + + sceneNode->_getDerivedOrientation() * + Ogre::Vector3::UNIT_Z * + (step + zoffset); + Ogre::Quaternion worldOrientation = + sceneNode->_getDerivedOrientation(); + Ogre::Vector3 xoffset = worldOrientation * + (Ogre::Vector3::UNIT_X * xofft); + geo->addEntity(ent, worldPosition + xoffset, + worldOrientation, + Ogre::Vector3(1, 1, 1)); + step += stepLength; + } + }; + float xofftPlanks; + for (xofftPlanks = -plankWidth; xofftPlanks <= plankWidth; + xofftPlanks += plankWidth) + processGrid(plankLength, pierLength, + pierOffset + plankLength / 2.0f, xofftPlanks, + hm.planks); + { + float step = 0.0f; + while (step < pierLength) { + // pillars + Procedural::BoxGenerator() + .setSizeX(0.5f) + .setSizeY(pierDepth) + .setSizeZ(0.5f) + .setEnableNormals(true) + .setTextureRectangle( + Ogre::RealRect(0.2f, 0.0f, 0.1f, 0.1f)) + .setPosition(Ogre::Vector3( + -5.0f, -pierDepth / 2.0f + 0.5f, step)) + .addToTriangleBuffer(tb); + Procedural::BoxGenerator() + .setSizeX(0.5f) + .setSizeY(pierDepth) + .setSizeZ(0.5f) + .setEnableNormals(true) + .setTextureRectangle( + Ogre::RealRect(0.2f, 0.0f, 0.1f, 0.1f)) + .setPosition(Ogre::Vector3( + 5.0f, -pierDepth / 2.0f + 0.5f, step)) + .addToTriangleBuffer(tb); + step += 6.0f; + } + step = pierLength - 0.5f; + while (step >= 0.0f) { + // bollards + Procedural::CylinderGenerator() + .setHeight(0.8f) + .setRadius(0.3f) + .setTextureRectangle( + Ogre::RealRect(0.2f, 0.0f, 0.1f, 0.1f)) + .setPosition(Ogre::Vector3( + -5.3f, 0.4f + 0.5f + 0.1f, step)) + .addToTriangleBuffer(tb); + Procedural::CylinderGenerator() + .setHeight(0.8f) + .setRadius(0.3f) + .setTextureRectangle( + Ogre::RealRect(0.2f, 0.0f, 0.1f, 0.1f)) + .setPosition(Ogre::Vector3( + 5.3f, 0.4f + 0.5f + 0.1f, step)) + .addToTriangleBuffer(tb); + step -= 12.0f; + } + // beams + float beamHeight = 0.3f; + Procedural::BoxGenerator() + .setSizeX(0.5f) + .setSizeY(beamHeight) + .setSizeZ(pierLength) + .setEnableNormals(true) + .setTextureRectangle( + Ogre::RealRect(0.2f, 0.0f, 0.1f, 0.1f)) + .setPosition(Ogre::Vector3(-5.0f, + 0.2 + beamHeight / 2.0f, + pierLength / 2.0f)) + .addToTriangleBuffer(tb); + Procedural::BoxGenerator() + .setSizeX(0.5f) + .setSizeY(beamHeight) + .setSizeZ(pierLength) + .setEnableNormals(true) + .setTextureRectangle( + Ogre::RealRect(0.2f, 0.0f, 0.1f, 0.1f)) + .setPosition(Ogre::Vector3(5.0f, + 0.2 + beamHeight / 2.0f, + pierLength / 2.0f)) + .addToTriangleBuffer(tb); + } + Ogre::String meshName = + "pier" + Ogre::StringConverter::toString(e.id()); + { + Ogre::MeshPtr mesh = tb.transformToMesh(meshName); + Ogre::LodConfig config(mesh); + // config.advanced.useCompression = false; + // config.advanced.useVertexNormals = true; + config.advanced.preventPunchingHoles = true; + config.advanced.preventBreakingLines = true; + config.createGeneratedLodLevel(10, 0.15f); + config.createGeneratedLodLevel(30, 0.25f); + config.createGeneratedLodLevel(60, 0.36f); + config.createGeneratedLodLevel(150, 0.65f); + config.advanced.useBackgroundQueue = false; + Ogre::MeshLodGenerator::getSingleton().generateLodLevels( + config); + Ogre::Entity *ent = + ECS::get().mScnMgr->createEntity(mesh); + Ogre::SceneNode *pierNode = sceneNode->createChildSceneNode(); + float xofft = 0.0f; + Ogre::Vector3 worldPosition = + sceneNode->_getDerivedPosition() + + sceneNode->_getDerivedOrientation() * + Ogre::Vector3::UNIT_Z * (pierOffset); + worldPosition.y = 0.0f; + Ogre::Quaternion worldOrientation = + sceneNode->_getDerivedOrientation(); + Ogre::Vector3 xoffset = + worldOrientation * (Ogre::Vector3::UNIT_X * xofft); + pierNode->_setDerivedPosition(worldPosition + xoffset); + ent->setMaterial(pierMaterial); + //pierNode->attachObject(ent); + geo->addEntity(ent, worldPosition + xoffset, worldOrientation, + Ogre::Vector3(1, 1, 1)); + } + geo->build(); + std::cout << meshName << std::endl; + +#if 0 + float step = 0.0f; + float h = Ogre::Math::Sin(step + position.z * 60.0f + + position.x * 60.0f * 60.0f) * + 0.1f; + if (pierLength < 12.0f) + pierLength = 12.0f; + if (pierHeight > 0.0f) { + Procedural::BoxGenerator() + .setSizeX(10.0f) + .setSizeY(0.5f) + .setSizeZ(pierHeight) + .setOrientation(Ogre::Quaternion( + Ogre::Degree(-45), Ogre::Vector3(1, 0, 0))) + .setTextureRectangle( + Ogre::RealRect(0.0f, 0.0f, 0.1f, 0.1f)) + .setPosition(Ogre::Vector3( + 0.0f, + pierHeight + 0.5f - + pierHeight * + Ogre::Math::Sin( + Ogre::Degree(45)) / + 2.0f, + -pierHeight / 2.0f * + Ogre::Math::Cos(Ogre::Degree(45)))) + .addToTriangleBuffer(tb); + } + while (step < pierOffset - 2.0f) { + Procedural::BoxGenerator() + .setSizeX(10.0f) + .setSizeY(0.4f) + .setSizeZ(0.8f) + .setTextureRectangle( + Ogre::RealRect(0.1f, 0.0f, 0.1f, 0.1f)) + .setPosition(Ogre::Vector3(0.0f, 0.5f + h + pierHeight, + step)) + .addToTriangleBuffer(tb); + h = Ogre::Math::Sin(step + position.z * 60.0f + + position.x * 60.0f * 60.0f) * + 0.1f; + step += 1.0f; + } + step = 0.0f; + while (step < pierLength) { + h = Ogre::Math::Sin(step + pierOffset + position.z * 60.0f + + position.x * 60.0f * 60.0f) * + 0.1f; + // pillars + Procedural::BoxGenerator() + .setSizeX(0.5f) + .setSizeY(pierDepth) + .setSizeZ(0.5f) + .setEnableNormals(true) + .setTextureRectangle( + Ogre::RealRect(0.2f, 0.0f, 0.1f, 0.1f)) + .setPosition(Ogre::Vector3( + -4.0f, -position.y - pierDepth / 2.0f + 0.5f, + step + pierOffset)) + .addToTriangleBuffer(tb); + Procedural::BoxGenerator() + .setSizeX(0.5f) + .setSizeY(pierDepth) + .setSizeZ(0.5f) + .setEnableNormals(true) + .setTextureRectangle( + Ogre::RealRect(0.2f, 0.0f, 0.1f, 0.1f)) + .setPosition(Ogre::Vector3( + 4.0f, -position.y - pierDepth / 2.0f + 0.5f, + step + pierOffset)) + .addToTriangleBuffer(tb); + step += 6.0f; + } + step = 0.0f; +#endif +#if 0 + while (step < pierLength) { + h = Ogre::Math::Sin(step + pierOffset + position.z * 60.0f + + position.x * 60.0f * 60.0f) * + 0.01f; + Ogre::Vector3 worldPosition = + sceneNode->_getDerivedPosition() + + sceneNode->_getDerivedOrientation() * + Ogre::Vector3::UNIT_Z * + (step + pierOffset + plankLength / 2.0f); + Ogre::Quaternion worldOrientation = + sceneNode->_getDerivedOrientation(); + float xofft; + for (xofft = -plankWidth - plankLength / 2.0f; + xofft <= plankWidth + plankWidth / 2.0f; + xofft += plankWidth) { + Ogre::Vector3 xoffset = worldOrientation * + (Ogre::Vector3::UNIT_X * xofft); + geo->addEntity(planks, worldPosition + xoffset, + worldOrientation, + Ogre::Vector3(1, 1, 1)); + } + step += plankLength; + } +#endif +#if 0 + float xofftBeam; + for (xofftBeam = -beamWidth; xofftBeam <= beamWidth; + xofftBeam += beamWidth) + processGrid(beamLength, pierLength, + pierOffset + beamLength / 2.0f, xofftBeam, hm.beam); +#endif +#if 0 + step = 0.0f; + while (step < pierLength) { + h = Ogre::Math::Sin(step + pierOffset + position.z * 60.0f + + position.x * 60.0f * 60.0f) * + 0.01f; + Ogre::Vector3 worldPosition = + sceneNode->_getDerivedPosition() + + sceneNode->_getDerivedOrientation() * + Ogre::Vector3::UNIT_Z * + (step + pierOffset + beamLength / 2.0f); + Ogre::Quaternion worldOrientation = + sceneNode->_getDerivedOrientation(); + float xofft; + for (xofft = -beamWidth - beamWidth / 2.0f; + xofft <= beamWidth + beamWidth / 2.0f; + xofft += beamWidth) { + Ogre::Vector3 xoffset = worldOrientation * + (Ogre::Vector3::UNIT_X * xofft); + geo->addEntity(beam, worldPosition + xoffset, + worldOrientation, + Ogre::Vector3(1, 1, 1)); + } + step += beamLength; + } +#endif +#if 0 + Procedural::BoxGenerator() + .setSizeX(0.5f) + .setSizeY(0.5f) + .setSizeZ(pierLength) + .setTextureRectangle(Ogre::RealRect(0.4f, 0.0f, 0.1f, 0.1f)) + .setEnableNormals(true) + .setPosition(Ogre::Vector3(-4.0f, -position.y + 0.5f, + pierOffset + pierLength / 2.0f)) + .addToTriangleBuffer(tb); + Procedural::BoxGenerator() + .setSizeX(0.5f) + .setSizeY(0.5f) + .setSizeZ(pierLength) + .setTextureRectangle(Ogre::RealRect(0.4f, 0.0f, 0.1f, 0.1f)) + .setEnableNormals(true) + .setPosition(Ogre::Vector3(4.0f, -position.y + 0.5f, + pierOffset + pierLength / 2.0f)) + .addToTriangleBuffer(tb); +#endif +} +} diff --git a/src/gamedata/StaticGeometryModule.h b/src/gamedata/StaticGeometryModule.h new file mode 100644 index 0000000..47eccaa --- /dev/null +++ b/src/gamedata/StaticGeometryModule.h @@ -0,0 +1,43 @@ +#ifndef _STATIC_GEOMETRY_MODULE_H_ +#define _STATIC_GEOMETRY_MODULE_H_ +#include +#include +namespace ECS +{ +struct TerrainSlotParent { + std::pair slot; +}; +struct TerrainItem { + Ogre::Vector3 position; + Ogre::Quaternion orientation; + Ogre::String properties; +}; +struct TerrainItemNode { + Ogre::SceneNode *itemNode; +}; + +struct StaticGeometryModule { + StaticGeometryModule(flecs::world &ecs); + static void addGeometryForSlot(long x, long y); + static void removeGeometryForSlot(long x, long y); + static flecs::entity createItem(const Ogre::Vector3 &position, + const Ogre::Quaternion &orientation, + const Ogre::String &type); + static void setItemProperties(flecs::entity id, + Ogre::String properties); + static const Ogre::String &getItemProperties(flecs::entity id); + static void saveItems(); + static void loadItems(); + static void getItemPositionPerSlot(long x, long y, + std::list *positions); + static void getItemPositions(std::list *positions); + static void getItemPositionAndRotation(flecs::entity e, + Ogre::Vector3 &position, + Ogre::Quaternion &orientation); + static void getItemsProperties( + std::list > *items); + static void createItemGeometry(flecs::entity e); + static void createHarbour(flecs::entity e, Ogre::SceneNode *sceneNode); +}; +} +#endif \ No newline at end of file diff --git a/src/gamedata/TerrainModule.cpp b/src/gamedata/TerrainModule.cpp index 58c6a4d..a2a6927 100644 --- a/src/gamedata/TerrainModule.cpp +++ b/src/gamedata/TerrainModule.cpp @@ -16,10 +16,11 @@ #include "CharacterModule.h" #include "SunModule.h" #include "PhysicsModule.h" +#include "StaticGeometryModule.h" #include "TerrainModule.h" -#define TERRAIN_SIZE 129 -#define TERRAIN_WORLD_SIZE 1000.0f +#define TERRAIN_SIZE 65 +#define TERRAIN_WORLD_SIZE 500.0f #define ENDLESS_TERRAIN_FILE_PREFIX Ogre::String("EndlessWorldTerrain") #define ENDLESS_TERRAIN_FILE_SUFFIX Ogre::String("dat") @@ -32,6 +33,12 @@ #define ENDLESS_PAGE_MAX_Y 0x7FFF namespace ECS { +class DummyPageProvider; +/* Components */ +struct TerrainPrivate { + DummyPageProvider *mDummyPageProvider; + Ogre::Timer mSunUpdate; +}; #define BRUSH_SIZE 64 struct HeightData { @@ -83,15 +90,17 @@ struct HeightData { } int get_img_x(float world_x) { - float world_img_x = world_x + img.getWidth() * BRUSH_SIZE / 2; - int ret = world_img_x / BRUSH_SIZE; + float world_img_x = world_x + (float)img.getWidth() * + (float)BRUSH_SIZE / 2.0f; + int ret = (world_img_x + BRUSH_SIZE - 1) / BRUSH_SIZE; // ret = Ogre::Math::Clamp(ret, 0, (int)img.getWidth() - 1); return ret; } int get_img_y(float world_z) { - float world_img_y = world_z + img.getHeight() * BRUSH_SIZE / 2; - int ret = world_img_y / BRUSH_SIZE; + float world_img_y = world_z + (float)img.getHeight() * + (float)BRUSH_SIZE / 2.0f; + int ret = (world_img_y + BRUSH_SIZE - 1) / BRUSH_SIZE; // ret = Ogre::Math::Clamp(ret, 0, (int)img.getHeight() - 1); return ret; } @@ -234,6 +243,7 @@ class FlatTerrainDefiner long y; }; std::deque collider_queue; + std::deque colliderRemove_queue; public: FlatTerrainDefiner(Ogre::SceneManager * @@ -247,10 +257,13 @@ public: } private: + std::mutex mtx; + public: void createTerrainChunk(Ogre::TerrainGroup *terrainGroup, long x, long y) { + std::lock_guard guard(mtx); Ogre::Terrain *terrain = terrainGroup->getTerrain(x, y); float minH = terrain->getMinHeight(); float maxH = terrain->getMaxHeight(); @@ -276,6 +289,7 @@ public: } void define(Ogre::TerrainGroup *terrainGroup, long x, long y) override { + std::lock_guard guard(mtx); uint16_t terrainSize = terrainGroup->getTerrainSize(); float *heightMap = OGRE_ALLOC_T(float, terrainSize *terrainSize, MEMCATEGORY_GEOMETRY); @@ -295,19 +309,9 @@ public: long world_y = (long)(worldPos.z + i - (terrainSize - 1) / 2); float height = 0.0f; - int k, l; - for (l = -1; l < 2; l++) - for (k = -1; k < 2; k++) { - height += - HeightData::get_singleton() - ->get_height( - terrainGroup, - world_x + - 4 * k, - world_y + - 4 * l); - } - height /= 9.0f; + height += + HeightData::get_singleton()->get_height( + terrainGroup, world_x, world_y); // height = -2.0f; heightMap[i * terrainSize + j] = height; @@ -421,6 +425,9 @@ public: what->setOrientation(item.rotation); what->setPosition(item.position); } + /* Spawn items */ + StaticGeometryModule::addGeometryForSlot(x, y); + } else { output.push_back(collider_queue.front()); collider_queue.pop_front(); @@ -453,6 +460,10 @@ public: bool unloadProceduralPage(Ogre::Page *page, Ogre::PagedWorldSection *section) { + long x, y; + ECS::get().mTerrainGroup->unpackIndex(page->CHUNK_ID, + &x, &y); + StaticGeometryModule::removeGeometryForSlot(x, y); return true; } bool unprepareProceduralPage(Ogre::Page *page, @@ -461,20 +472,6 @@ public: return true; } }; -struct TerrainPrivate { - DummyPageProvider *mDummyPageProvider; - Ogre::Timer mSunUpdate; -}; - -struct TerrainSlotParent { - std::pair slot; -}; -struct TerrainItem { - Ogre::Vector3 position; - Ogre::Quaternion orientation; - Ogre::String properties; -}; - TerrainModule::TerrainModule(flecs::world &ecs) { struct CanSetPlayerPosition {}; @@ -482,12 +479,11 @@ TerrainModule::TerrainModule(flecs::world &ecs) ecs.component().add(flecs::Singleton); ecs.component().add(flecs::Singleton); ecs.component().add(flecs::Singleton); - ecs.component(); - ecs.component(); ecs.component(); ecs.component().add(flecs::Singleton); ecs.import (); ecs.import (); + ecs.import (); ecs.set({ nullptr, {} }); ecs.system("SetupUpdateTerrain") @@ -523,11 +519,11 @@ TerrainModule::TerrainModule(flecs::world &ecs) terrain.mTerrainGroup->setOrigin( terrain.mTerrainPos); // Configure global - terrain.mTerrainGlobals->setMaxPixelError(0); + terrain.mTerrainGlobals->setMaxPixelError(1); // testing composite map // mTerrainGlobals->setCompositeMapDistance(30); terrain.mTerrainGlobals->setCompositeMapDistance( - 500); + 300); //mTerrainGlobals->setUseRayBoxDistanceCalculation(true); terrain.mTerrainGlobals ->getDefaultMaterialGenerator() @@ -547,7 +543,8 @@ TerrainModule::TerrainModule(flecs::world &ecs) defaultimp.terrainSize = TERRAIN_SIZE; defaultimp.worldSize = TERRAIN_WORLD_SIZE; defaultimp.inputScale = 1.0f; - defaultimp.minBatchSize = 33; + // defaultimp.minBatchSize = 33; + defaultimp.minBatchSize = 5; defaultimp.maxBatchSize = 65; Ogre::Image combined; combined.loadTwoImagesAsRGBA( @@ -575,24 +572,33 @@ TerrainModule::TerrainModule(flecs::world &ecs) terrain.mPageManager); terrain.mPagedWorld = terrain.mPageManager->createWorld(); +#if 0 + terrain.mTerrainGroup->setAutoUpdateLod( + Ogre::TerrainAutoUpdateLodFactory:: + getAutoUpdateLod( + Ogre::BY_DISTANCE)); +#endif + terrain.mTerrainPagedWorldSection = terrain.mTerrainPaging ->createWorldSection( terrain.mPagedWorld, terrain.mTerrainGroup, - 300, 800, + 300, 500, ENDLESS_PAGE_MIN_X, ENDLESS_PAGE_MIN_Y, ENDLESS_PAGE_MAX_X, ENDLESS_PAGE_MAX_Y); + terrain.definer = OGRE_NEW FlatTerrainDefiner( + eng.mScnMgr /*, eng.mWorld */); terrain.mTerrainPagedWorldSection->setDefiner( - OGRE_NEW FlatTerrainDefiner( - eng.mScnMgr /*, eng.mWorld */)); + terrain.definer); terrain.mTerrainGroup->freeTemporaryResources(); std::cout << "Terrain setup done\n"; ECS::get().set({}); + terrain.mTerrainGroup->loadAllTerrains(true); } if (sun.mSun && priv.mSunUpdate.getMilliseconds() > 1000) { @@ -607,6 +613,7 @@ TerrainModule::TerrainModule(flecs::world &ecs) .getPitch() << "\n"; priv.mSunUpdate.reset(); + //terrain.mTerrainGroup->autoUpdateLodAll() } }); ecs.system("UpdateTerrainStatus") @@ -738,11 +745,13 @@ TerrainModule::TerrainModule(flecs::world &ecs) player.modified(); ECS::get().remove(); }); - ecs.observer("LoadTerrainItems") - .event(flecs::OnSet) + ecs.system("UpdateTerrainGroup") + .kind(flecs::OnUpdate) + .interval(2.0f) .each([](const Terrain &terrain) { - if (terrain.mTerrainGroup) - loadItems(); + if (!terrain.mTerrainGroup + ->isDerivedDataUpdateInProgress()) + terrain.mTerrainGroup->update(false); }); } float TerrainModule::get_height(Ogre::TerrainGroup *group, @@ -783,167 +792,9 @@ void TerrainModule::save_heightmap() { HeightData::get_singleton()->save_heightmap(); } -flecs::entity TerrainModule::createItem(const Ogre::Vector3 &position, - const Ogre::Quaternion &orientation, - const Ogre::String &type) +void TerrainModule::defineTerrain(long x, long y) { - 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().add(); - flecs::entity item = ECS::get().entity().child_of(slot); - nlohmann::json jproperties; - jproperties["type"] = type; - item.set({ position, orientation, jproperties.dump() }); - return item; -} -void TerrainModule::setItemProperties(flecs::entity id, Ogre::String properties) -{ - OgreAssert(id.is_valid(), "bad id"); - id.get_mut().properties = properties; - id.modified(); -} -const Ogre::String &TerrainModule::getItemProperties(flecs::entity id) -{ - OgreAssert(id.is_valid(), "bad id"); - return id.get().properties; -} -static void to_json(nlohmann::json &j, const Ogre::Vector3 &position) -{ - j["x"] = position.x; - j["y"] = position.y; - j["z"] = position.z; -} -static void to_json(nlohmann::json &j, const Ogre::Quaternion &orientation) -{ - j["w"] = orientation.w; - j["x"] = orientation.x; - j["y"] = orientation.y; - j["z"] = orientation.z; -} -static void from_json(const nlohmann::json &j, Ogre::Vector3 &position) -{ - position.x = j["x"].get(); - position.y = j["y"].get(); - position.z = j["z"].get(); -} -static void from_json(const nlohmann::json &j, Ogre::Quaternion &orientation) -{ - orientation.w = j["w"].get(); - orientation.x = j["x"].get(); - orientation.y = j["y"].get(); - orientation.z = j["z"].get(); -} -void TerrainModule::saveItems() -{ - 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); - jitemlist.push_back(jitem); - }); - fout << jitemlist.dump(); - fout.close(); -} -void TerrainModule::loadItems() -{ - 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(); - 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().add(); - flecs::entity item = ECS::get().entity().child_of(slot); - item.set({ position, orientation, properties }); - std::cout << "position: " << item.id() << " " << position - << std::endl; - } -} -void TerrainModule::getItemPositionPerSlot(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; - }); - 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); - std::cout << e.id() << " " << item.position - << std::endl; - }); -} -void TerrainModule::getItemPositions(std::list *positions) -{ - ECS::get().query_builder().build().each( - [&](flecs::entity e, const TerrainItem &item) { - positions->push_back(item.position); - std::cout << e.id() << " " << item.position - << std::endl; - }); + ECS::get().definer->define(ECS::get().mTerrainGroup, + x, y); } } \ No newline at end of file diff --git a/src/gamedata/TerrainModule.h b/src/gamedata/TerrainModule.h index 342ab5a..a47830b 100644 --- a/src/gamedata/TerrainModule.h +++ b/src/gamedata/TerrainModule.h @@ -12,12 +12,14 @@ class TerrainPagedWorldSection; } namespace ECS { +class FlatTerrainDefiner; struct Terrain { Ogre::TerrainGlobalOptions *mTerrainGlobals; Ogre::TerrainGroup *mTerrainGroup; Ogre::TerrainPaging *mTerrainPaging; Ogre::PageManager *mPageManager; Ogre::PagedWorld *mPagedWorld; + FlatTerrainDefiner *definer; Ogre::TerrainPagedWorldSection *mTerrainPagedWorldSection; bool mTerrainReady; @@ -41,17 +43,7 @@ struct TerrainModule { static int get_img_y(float world_z); static void update_heightmap(const Ogre::Image &heightmap); static void save_heightmap(); - static flecs::entity createItem(const Ogre::Vector3 &position, - const Ogre::Quaternion &orientation, - const Ogre::String &type); - static void setItemProperties(flecs::entity id, - Ogre::String properties); - static const Ogre::String &getItemProperties(flecs::entity id); - static void saveItems(); - static void loadItems(); - static void getItemPositionPerSlot(long x, long y, - std::list *positions); - static void getItemPositions(std::list *positions); + static void defineTerrain(long x, long y); }; struct TerrainReady {}; } diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index e5aa843..9b9e1c9 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -852,6 +852,8 @@ public: JPH::BodyInterface &body_interface = physics_system.GetBodyInterface(); JPH::Body *body = body_interface.CreateBody(settings); + if (!body) + return JPH::BodyID(); return body->GetID(); } void removeBody(const JPH::BodyID &id)