From fed218ac5785e79b2d85827320050b2b9cb8e7be Mon Sep 17 00:00:00 2001 From: LJW-Dev <48092720+LJW-Dev@users.noreply.github.com> Date: Thu, 23 Apr 2026 17:48:51 +0800 Subject: [PATCH] feat: removed ents file, worldspawn and intermission are added through gltf nodes now --- src/ObjLoading/Game/T6/BSP/BSP.h | 10 +- src/ObjLoading/Game/T6/BSP/BSPCreator.cpp | 37 +++++- .../Game/T6/BSP/Linker/MapEntsLinker.cpp | 113 ++++-------------- 3 files changed, 63 insertions(+), 97 deletions(-) diff --git a/src/ObjLoading/Game/T6/BSP/BSP.h b/src/ObjLoading/Game/T6/BSP/BSP.h index 18c49e63..3cf05271 100644 --- a/src/ObjLoading/Game/T6/BSP/BSP.h +++ b/src/ObjLoading/Game/T6/BSP/BSP.h @@ -164,14 +164,18 @@ namespace BSP BSPWorld gfxWorld; BSPWorld colWorld; + bool hasSunlightBeenSet; + BSPLight sunlight; std::vector lights; + std::vector spawnpoints; std::vector zm_zones; std::vector zm_spawners; - std::vector entities; - bool hasSunlightBeenSet; - BSPLight sunlight; + bool containsWorldspawn; + bool containsIntermssion; + std::vector entities; + BSPEntity worldspawn; std::vector models; }; diff --git a/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp b/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp index e527ad59..b99a34c2 100644 --- a/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp +++ b/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp @@ -374,10 +374,7 @@ namespace light.innerConeAngle = 0.0f; if (jsLight.type == JsonPunctualLightType::DIRECTIONAL) { - if (m_bsp->hasSunlightBeenSet) - con::warn("WARNING: multiple sunlight nodes found, only one will be used as the sun."); light.type = LIGHT_TYPE_DIRECTIONAL; - m_bsp->hasSunlightBeenSet = true; } else if (jsLight.type == JsonPunctualLightType::POINT) { @@ -435,7 +432,12 @@ namespace RhcToLhcCoordinates(light.direction.v); if (jsLight.type == JsonPunctualLightType::DIRECTIONAL) + { + if (m_bsp->hasSunlightBeenSet) + con::warn("WARNING: multiple sunlight nodes found, only one will be used as the sun."); m_bsp->sunlight = light; + m_bsp->hasSunlightBeenSet = true; + } else m_bsp->lights.emplace_back(light); @@ -949,7 +951,21 @@ namespace entity.rotationQuaternion.w = rotationQuat.w(); RhcToLhcQuaternion(entity.rotationQuaternion.v); - m_bsp->entities.emplace_back(entity); + if (!classname.compare("worldspawn")) + { + if (m_bsp->containsWorldspawn) + con::warn("WARNING: multiple worldspawn classes found, only one will be used."); + m_bsp->worldspawn = entity; + m_bsp->containsWorldspawn = true; + } + else if (!classname.compare("mp_global_intermission")) + { + if (m_bsp->containsIntermssion) + con::warn("WARNING: multiple mp_global_intermission classes found, only one will be used."); + m_bsp->containsIntermssion = true; + } + else + m_bsp->entities.emplace_back(entity); return true; } @@ -1296,6 +1312,8 @@ namespace BSP bsp->bspName = "maps/mp/" + mapName + ".d3dbsp"; bsp->isZombiesMap = isZombiesMap; bsp->hasSunlightBeenSet = false; + bsp->containsIntermssion = false; + bsp->containsWorldspawn = false; con::warn("XModels don't support scale currently, keep it at 1 in your editor"); @@ -1356,6 +1374,17 @@ namespace BSP bsp->sunlight.outerConeAngle = 0.0f; } + if (!bsp->containsIntermssion) + { + con::error("Map does not contain a mp_global_intermission class"); + return nullptr; + } + if (!bsp->containsWorldspawn) + { + con::error("Map does not contain a worldspawn class"); + return nullptr; + } + return bsp; } } // namespace BSP diff --git a/src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp b/src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp index 943b773e..7556e3ce 100644 --- a/src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp +++ b/src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp @@ -7,38 +7,6 @@ using namespace nlohmann; namespace { - bool addJSONToEntString(json& entArrayJs, std::string& entityString) - { - for (size_t entIdx = 0; entIdx < entArrayJs.size(); entIdx++) - { - auto& entity = entArrayJs[entIdx]; - - if (entIdx == 0) - { - std::string className; - entity.at("classname").get_to(className); - if (className.compare("worldspawn") != 0) - { - con::error("ERROR: first entity in the map entity string must be the worldspawn class!"); - return false; - } - } - - entityString.append("{\n"); - - for (auto& element : entity.items()) - { - std::string key = element.key(); - std::string value = element.value(); - entityString.append(std::format("\"{}\" \"{}\"\n", key, value)); - } - - entityString.append("}\n"); - } - - return true; - } - inline const std::vector DEFENDER_SPAWN_POINT_NAMES = {"mp_ctf_spawn_allies", "mp_ctf_spawn_allies_start", "mp_sd_spawn_defender", @@ -170,24 +138,6 @@ namespace entityString.append("}\n"); } } - - constexpr const char* DEFAULT_MAP_ENTS_STRING = R"({ - "entities": [ - { - "classname": "worldspawn" - }, - { - "angles": "0 0 0", - "classname": "info_player_start", - "origin": "0 0 0" - }, - { - "angles": "0 0 0", - "classname": "mp_global_intermission", - "origin": "0 0 0" - } - ] - })"; } // namespace namespace BSP @@ -201,52 +151,35 @@ namespace BSP MapEnts* MapEntsLinker::linkMapEnts(BSPData* bsp) { - try - { - json entJs; - std::string entityFileName = "entities.json"; - std::string entityFilePath = BSPUtil::getFileNameForBSPAsset(entityFileName); - const auto entFile = m_search_path.Open(entityFilePath); - if (!entFile.IsOpen()) - { - con::warn("Can't find entity file {}, using default entities instead", entityFilePath); - entJs = json::parse(DEFAULT_MAP_ENTS_STRING); - } - else - { - entJs = json::parse(*entFile.m_stream); - } - std::string entityString; - if (!addJSONToEntString(entJs["entities"], entityString)) - return nullptr; + std::string entityString; - addSpawnsToEntString(bsp, entityString); + // worldspawn must be the first entity + entityString.append("{\n"); + for (auto& entry : bsp->worldspawn.entries) + entityString.append(std::format("\"{}\" \"{}\"\n", entry.key, entry.value)); + entityString.append("}\n"); - if (bsp->isZombiesMap) - addZombiesEntitiesToEntString(bsp, entityString); + addSpawnsToEntString(bsp, entityString); - addClassEntitiesToEntString(bsp, entityString); + if (bsp->isZombiesMap) + addZombiesEntitiesToEntString(bsp, entityString); - MapEnts* mapEnts = m_memory.Alloc(); - mapEnts->name = m_memory.Dup(bsp->bspName.c_str()); + addClassEntitiesToEntString(bsp, entityString); - mapEnts->entityString = m_memory.Dup(entityString.c_str()); - mapEnts->numEntityChars = static_cast(entityString.length() + 1); // numEntityChars includes the null character + MapEnts* mapEnts = m_memory.Alloc(); + mapEnts->name = m_memory.Dup(bsp->bspName.c_str()); - // don't need these, unused by the game - mapEnts->trigger.count = 0; - mapEnts->trigger.models = nullptr; - mapEnts->trigger.hullCount = 0; - mapEnts->trigger.hulls = nullptr; - mapEnts->trigger.slabCount = 0; - mapEnts->trigger.slabs = nullptr; + mapEnts->entityString = m_memory.Dup(entityString.c_str()); + mapEnts->numEntityChars = static_cast(entityString.length() + 1); // numEntityChars includes the null character - return mapEnts; - } - catch (const json::exception& e) - { - con::error("JSON error when parsing map ents and spawns: {}", e.what()); - return nullptr; - } + // don't need these, unused by the game + mapEnts->trigger.count = 0; + mapEnts->trigger.models = nullptr; + mapEnts->trigger.hullCount = 0; + mapEnts->trigger.hulls = nullptr; + mapEnts->trigger.slabCount = 0; + mapEnts->trigger.slabs = nullptr; + + return mapEnts; } } // namespace BSP