From 1dcd5539056174af4436e058730627c3c65842d0 Mon Sep 17 00:00:00 2001 From: LJW-Dev <48092720+LJW-Dev@users.noreply.github.com> Date: Wed, 15 Apr 2026 21:41:13 +0800 Subject: [PATCH] feat: fully customisable entities through gltf --- src/ObjCommon/XModel/Gltf/JsonGltf.h | 2 +- src/ObjLoading/Game/T6/BSP/BSP.h | 48 ++-- src/ObjLoading/Game/T6/BSP/BSPCreator.cpp | 235 ++++++++---------- src/ObjLoading/Game/T6/BSP/BSPUtil.cpp | 52 ++++ src/ObjLoading/Game/T6/BSP/BSPUtil.h | 2 + .../Game/T6/BSP/Linker/MapEntsLinker.cpp | 93 +++---- 6 files changed, 202 insertions(+), 230 deletions(-) diff --git a/src/ObjCommon/XModel/Gltf/JsonGltf.h b/src/ObjCommon/XModel/Gltf/JsonGltf.h index 354d9054..ea6d2ac0 100644 --- a/src/ObjCommon/XModel/Gltf/JsonGltf.h +++ b/src/ObjCommon/XModel/Gltf/JsonGltf.h @@ -125,7 +125,7 @@ namespace gltf std::optional mesh; std::optional extensions; - std::optional extras; + std::optional extras; }; NLOHMANN_DEFINE_TYPE_EXTENSION(JsonNode, name, translation, rotation, scale, matrix, children, skin, mesh, extensions, extras); diff --git a/src/ObjLoading/Game/T6/BSP/BSP.h b/src/ObjLoading/Game/T6/BSP/BSP.h index 80ef1d4c..ca3b9856 100644 --- a/src/ObjLoading/Game/T6/BSP/BSP.h +++ b/src/ObjLoading/Game/T6/BSP/BSP.h @@ -107,44 +107,25 @@ namespace BSP float outerConeAngle; }; - enum BSPSpawnPointType - { - SPAWNPOINT_TYPE_DEFENDER, - SPAWNPOINT_TYPE_ATTACKER, - SPAWNPOINT_TYPE_ALL, - }; - struct BSPSpawnPoint { vec3_t origin; vec3_t forward; - BSPSpawnPointType type; - }; - - struct BSPPathNode - { - vec3_t origin; + std::string spawnpointGroupName; }; struct BSPZoneZM { vec3_t origin; std::string zoneName; - std::string zSpawnerGroupName; + std::string spawnerGroupName; std::string spawnpointGroupName; size_t modelIndex; }; - struct BSPSpawnPointZM - { - std::string spawnpointGroupName; - vec3_t origin; - vec3_t forward; - }; - struct BSPZSpawnerZM { - std::string zSpawnerGroupName; + std::string spawnerGroupName; vec3_t origin; vec3_t forward; }; @@ -160,11 +141,19 @@ namespace BSP size_t brushIndex; }; - struct BSPTriggerBox + struct BSPEntityEntry + { + std::string key; + std::string value; + }; + + struct BSPEntity { vec3_t origin; + vec4_t rotationQuaternion; size_t modelIndex; - std::string triggerName; + + std::vector entries; }; struct BSPData @@ -178,14 +167,9 @@ namespace BSP std::vector lights; std::vector spawnpoints; - std::vector pathnodes; - - std::vector zSpawnPoints; - std::vector zSpawners; - std::vector zZones; - - std::vector useTriggers; - std::vector triggerMultiples; + std::vector zm_zones; + std::vector zm_spawners; + std::vector entities; std::vector models; }; diff --git a/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp b/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp index 40237515..22533892 100644 --- a/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp +++ b/src/ObjLoading/Game/T6/BSP/BSPCreator.cpp @@ -443,10 +443,10 @@ namespace if (primitive.material) { size_t originalMaterialIdx = *primitive.material; - if (node.extras && node.extras->flags) + if (node.extras && node.extras->contains("flags")) { bool isNoDrawFlagSet = false; - materialIndex = createMaterialWithFlags(originalMaterialIdx, *node.extras->flags, isNoDrawFlagSet); + materialIndex = createMaterialWithFlags(originalMaterialIdx, node.extras->at("flags"), isNoDrawFlagSet); if (isNoDrawFlagSet && m_is_world_gfx) continue; // noDraw flag doesn't work, so remove the surface from the graphics data instead } @@ -492,12 +492,12 @@ namespace throw GltfLoadException("Requires primitives attribute POSITION"); // clang-format off - const auto* positionAccessor = GetAccessorForIndex( - "POSITION", - primitive.attributes.POSITION, - { JsonAccessorType::VEC3 }, - { JsonAccessorComponentType::FLOAT } - ).value_or(nullptr); + const auto* positionAccessor = GetAccessorForIndex( + "POSITION", + primitive.attributes.POSITION, + { JsonAccessorType::VEC3 }, + { JsonAccessorComponentType::FLOAT } + ).value_or(nullptr); // clang-format on assert(positionAccessor != nullptr); @@ -539,19 +539,17 @@ namespace bool addXModelNode(const JsonRoot& jRoot, const gltf::JsonNode& node) { assert(node.extras); - assert(node.extras->xmodel); + assert(node.extras->contains("xmodel")); Eigen::Matrix4f nodeMatrix = createNodeMatrix(node); BSPXModel xmodel; - if (node.extras->xmodel->size() == 0) - throw GltfLoadException("Xmodel has no name."); - xmodel.name = *node.extras->xmodel; + xmodel.name = node.extras->at("xmodel"); xmodel.doesCastShadow = true; - if (node.extras->flags) + if (node.extras->contains("flags")) { - std::vector flagStrVec = utils::StringSplit(*node.extras->flags, ','); + std::vector flagStrVec = utils::StringSplit(node.extras->at("flags"), ','); for (std::string& flag : flagStrVec) if (!flag.compare(surfaceTypeToNameMap[SURF_TYPE_NOCASTSHADOW])) { @@ -573,7 +571,7 @@ namespace xmodel.rotationQuaternion.x = rotationQuat.x(); xmodel.rotationQuaternion.y = rotationQuat.y(); xmodel.rotationQuaternion.z = rotationQuat.z(); - xmodel.rotationQuaternion.w = rotationQuat.w(); // Eigen is WXYZ, game is XYZW + xmodel.rotationQuaternion.w = rotationQuat.w(); RhcToLhcQuaternion(xmodel.rotationQuaternion.v); con::warn("XModels don't support scale currently, keep it at 1 in your editor"); @@ -586,30 +584,10 @@ namespace return true; } - bool addPathNode_Node(const gltf::JsonNode& node) - { - assert(node.extras); - assert(node.extras->pathnode); - - BSPPathNode pathnode; - - Eigen::Matrix4f nodeMatrix = createNodeMatrix(node); - Eigen::Vector4f position(0, 0, 0, 1.0f); - Eigen::Vector4f transformedPosition = nodeMatrix * position; - pathnode.origin.x = transformedPosition.x(); - pathnode.origin.y = transformedPosition.y(); - pathnode.origin.z = transformedPosition.z(); - RhcToLhcCoordinates(pathnode.origin.v); - - m_bsp->pathnodes.emplace_back(pathnode); - - return true; - } - bool addSpawnPointNode(const gltf::JsonNode& node) { assert(node.extras); - assert(node.extras->spawnpoint); + assert(node.extras->contains("spawnpoint")); Eigen::Matrix4f nodeMatrix = createNodeMatrix(node); @@ -633,34 +611,18 @@ namespace forward.z = outputDirection.z(); RhcToLhcCoordinates(forward.v); - if (m_bsp->isZombiesMap) + std::string team = node.extras->at("spawnpoint"); + if (!m_bsp->isZombiesMap && team.compare("attacker") && team.compare("defender") && team.compare("all")) { - BSPSpawnPointZM spawnPoint; - spawnPoint.origin = origin; - spawnPoint.forward = forward; - spawnPoint.spawnpointGroupName = *node.extras->spawnpoint; - m_bsp->zSpawnPoints.emplace_back(spawnPoint); + con::warn("Ignoring spawn point with an invalid type (must be attacker, defender or all)"); + return false; } - else - { - BSPSpawnPoint spawnPoint; - spawnPoint.origin = origin; - spawnPoint.forward = forward; - if (!node.extras->spawnpoint->compare("attacker")) - spawnPoint.type = SPAWNPOINT_TYPE_ATTACKER; - else if (!node.extras->spawnpoint->compare("defender")) - spawnPoint.type = SPAWNPOINT_TYPE_DEFENDER; - else if (!node.extras->spawnpoint->compare("all")) - spawnPoint.type = SPAWNPOINT_TYPE_ALL; - else - { - con::warn("Ignoring spawn point with an invalid type (must be attacker, defender or all)"); - return false; - } - - m_bsp->spawnpoints.emplace_back(spawnPoint); - } + BSPSpawnPoint spawnPoint; + spawnPoint.origin = origin; + spawnPoint.forward = forward; + spawnPoint.spawnpointGroupName = team; + m_bsp->spawnpoints.emplace_back(spawnPoint); return true; } @@ -697,12 +659,12 @@ namespace throw GltfLoadException("Requires primitives attribute POSITION"); // clang-format off - const auto* positionAccessor = GetAccessorForIndex( - "POSITION", - primitive.attributes.POSITION, - { JsonAccessorType::VEC3 }, - { JsonAccessorComponentType::FLOAT } - ).value_or(nullptr); + const auto* positionAccessor = GetAccessorForIndex( + "POSITION", + primitive.attributes.POSITION, + { JsonAccessorType::VEC3 }, + { JsonAccessorComponentType::FLOAT } + ).value_or(nullptr); // clang-format on assert(positionAccessor != nullptr); @@ -807,7 +769,13 @@ namespace bool addZoneNode(const JsonRoot& jRoot, const gltf::JsonNode& node) { assert(node.extras); - assert(node.extras->zone); + assert(node.extras->contains("zone")); + + if (!node.extras->contains("spawner_group") || !node.extras->contains("spawnpoint_group")) + { + con::error("ignoring zone: Zone object must have a valid spawner_group and spawnpoint_group property"); + return false; + } Eigen::Matrix4f nodeMatrix = createNodeMatrix(node); @@ -821,11 +789,11 @@ namespace BSPZoneZM zone; zone.origin = origin; - zone.zoneName = *node.extras->zone; - zone.zSpawnerGroupName = node.extras->zspawner_group.value_or(""); - zone.spawnpointGroupName = node.extras->spawnpoint_group.value_or(""); + zone.zoneName = node.extras->at("zone"); + zone.spawnerGroupName = node.extras->at("spawner_group"); + zone.spawnpointGroupName = node.extras->at("spawnpoint_group"); zone.modelIndex = addScriptBrushModel(jRoot, node); - m_bsp->zZones.emplace_back(zone); + m_bsp->zm_zones.emplace_back(zone); return true; } @@ -833,7 +801,7 @@ namespace bool addZSpawnerNode(const gltf::JsonNode& node) { assert(node.extras); - assert(node.extras->zspawner); + assert(node.extras->contains("spawner")); Eigen::Matrix4f nodeMatrix = createNodeMatrix(node); @@ -860,57 +828,55 @@ namespace BSPZSpawnerZM spawner; spawner.origin = origin; spawner.forward = forward; - spawner.zSpawnerGroupName = *node.extras->zspawner; - m_bsp->zSpawners.emplace_back(spawner); + spawner.spawnerGroupName = node.extras->at("spawner"); + m_bsp->zm_spawners.emplace_back(spawner); return true; } - bool addTriggerUseNode(const JsonRoot& jRoot, const gltf::JsonNode& node) + bool addClassNode(const JsonRoot& jRoot, const gltf::JsonNode& node) { - assert(node.extras); - assert(node.extras->trigger_use); + BSPEntity entity; + bool doesClassContainModel = false; + for (auto& element : node.extras->items()) + { + std::string key = element.key(); + if (!key.compare("origin") || !key.compare("angles")) + continue; + + if (!key.compare("model")) + doesClassContainModel = true; + + BSPEntityEntry entry; + entry.key = key; + entry.value = element.value(); + entity.entries.emplace_back(entry); + } + + if (node.mesh && !doesClassContainModel) + entity.modelIndex = addScriptBrushModel(jRoot, node); + else + entity.modelIndex = 0; Eigen::Matrix4f nodeMatrix = createNodeMatrix(node); Eigen::Vector4f position(0, 0, 0, 1.0f); Eigen::Vector4f transformedPosition = nodeMatrix * position; - vec3_t origin; - origin.x = transformedPosition.x(); - origin.y = transformedPosition.y(); - origin.z = transformedPosition.z(); - RhcToLhcCoordinates(origin.v); + entity.origin.x = transformedPosition.x(); + entity.origin.y = transformedPosition.y(); + entity.origin.z = transformedPosition.z(); + RhcToLhcCoordinates(entity.origin.v); - BSPTriggerBox trigger; - trigger.origin = origin; - trigger.triggerName = *node.extras->trigger_use; - trigger.modelIndex = addScriptBrushModel(jRoot, node); - m_bsp->useTriggers.emplace_back(trigger); - - return true; - } - - bool addTriggerMultipleNode(const JsonRoot& jRoot, const gltf::JsonNode& node) - { - assert(node.extras); - assert(node.extras->trigger_multiple); - - Eigen::Matrix4f nodeMatrix = createNodeMatrix(node); - - Eigen::Vector4f position(0, 0, 0, 1.0f); - Eigen::Vector4f transformedPosition = nodeMatrix * position; - vec3_t origin; - origin.x = transformedPosition.x(); - origin.y = transformedPosition.y(); - origin.z = transformedPosition.z(); - RhcToLhcCoordinates(origin.v); - - BSPTriggerBox trigger; - trigger.origin = origin; - trigger.triggerName = *node.extras->trigger_multiple; - trigger.modelIndex = addScriptBrushModel(jRoot, node); - m_bsp->triggerMultiples.emplace_back(trigger); + Eigen::Affine3f affineTransform(nodeMatrix); + Eigen::Quaternionf rotationQuat(affineTransform.rotation()); + rotationQuat.normalize(); + entity.rotationQuaternion.x = rotationQuat.x(); + entity.rotationQuaternion.y = rotationQuat.y(); + entity.rotationQuaternion.z = rotationQuat.z(); + entity.rotationQuaternion.w = rotationQuat.w(); + RhcToLhcQuaternion(entity.rotationQuaternion.v); + m_bsp->entities.emplace_back(entity); return true; } @@ -921,34 +887,31 @@ namespace if (node.extras) { - if (node.extras->xmodel) + if (!m_is_world_gfx) + { + if (node.extras->contains("classname")) + return addClassNode(jRoot, node); + + if (node.extras->contains("spawnpoint")) + return addSpawnPointNode(node); + + if (m_bsp->isZombiesMap) + { + if (node.extras->contains("zone")) + return addZoneNode(jRoot, node); + + if (node.extras->contains("spawner")) + return addZSpawnerNode(node); + } + } + + if (node.extras->contains("xmodel")) return addXModelNode(jRoot, node); - if (!m_is_world_gfx && node.extras->spawnpoint) - return addSpawnPointNode(node); - - if (!m_is_world_gfx && node.extras->pathnode) - return addPathNode_Node(node); - - if (!m_is_world_gfx && node.extras->trigger_use) - return addTriggerUseNode(jRoot, node); - - if (!m_is_world_gfx && node.extras->trigger_multiple) - return addTriggerMultipleNode(jRoot, node); - - if (!m_is_world_gfx && m_bsp->isZombiesMap) - { - if (node.extras->zone) - return addZoneNode(jRoot, node); - - if (node.extras->zspawner) - return addZSpawnerNode(node); - } + if (node.mesh) + return addMeshNode(jRoot, node); } - if (node.mesh) - return addMeshNode(jRoot, node); - return false; } diff --git a/src/ObjLoading/Game/T6/BSP/BSPUtil.cpp b/src/ObjLoading/Game/T6/BSP/BSPUtil.cpp index feb8cb50..20ef6458 100644 --- a/src/ObjLoading/Game/T6/BSP/BSPUtil.cpp +++ b/src/ObjLoading/Game/T6/BSP/BSPUtil.cpp @@ -196,6 +196,17 @@ namespace BSP { vec3_t viewAngles; + if (forwardVec.x == 0.0f && forwardVec.y == 0.0f) + { + if (-forwardVec.z < 0.0f) + viewAngles.x = 270.0f; + else + viewAngles.x = 90.0f; + viewAngles.y = 0.0f; + viewAngles.z = 0.0f; + return viewAngles; + } + float xAndYDist = sqrtf((forwardVec.x * forwardVec.x) + (forwardVec.y * forwardVec.y)); float atanXRadians = atan2f(forwardVec.z, xAndYDist); float atanXDegrees = atanXRadians * (-180.0f / std::numbers::pi_v); @@ -214,6 +225,47 @@ namespace BSP return viewAngles; } + float BSPUtil::getPitchFromVector(vec3_t& vector) + { + if (vector.x == 0.0f && vector.y == 0.0f) + { + if (-vector.z < 0.0f) + return -90.0f; + else + return 90.0f; + } + + float xAndYDist = sqrtf((vector.x * vector.x) + (vector.y * vector.y)); + float pitchRadians = atan2f(vector.z, xAndYDist); + float pitchDegrees = pitchRadians * (-180.0f / std::numbers::pi_v); + return pitchDegrees; + } + + vec3_t BSPUtil::convertAxisToAngles(vec3_t axis[3]) + { + vec3_t tempAngles = convertForwardVectorToViewAngles(axis[0]); + float xRadiansNeg = -tempAngles.x * (std::numbers::pi_v / 180.0f); + float yRadiansNeg = -tempAngles.y * (std::numbers::pi_v / 180.0f); + float cosX = cos(xRadiansNeg); + float sinX = sin(xRadiansNeg); + float cosY = cos(yRadiansNeg); + float sinY = sin(yRadiansNeg); + + vec3_t tempVec; + float tempFloat = (axis[1].x * cosY) - (axis[1].y * sinY); + tempVec.x = (axis[1].z * sinX) + (tempFloat * cosX); + tempVec.y = (axis[1].x * sinY) + (axis[1].y * cosY); + tempVec.z = (axis[1].z * cosX) - (tempFloat * sinX); + float pitch = getPitchFromVector(tempVec); + if (tempVec.y >= 0.0f) + tempAngles.z = -pitch; + else if (pitch >= 0.0f) + tempAngles.z = pitch - 180.0f; + else + tempAngles.z = pitch + 180.0f; + return tempAngles; + } + void BSPUtil::matrixTranspose3x3(const vec3_t* in, vec3_t* out) { out[0].x = in[0].x; diff --git a/src/ObjLoading/Game/T6/BSP/BSPUtil.h b/src/ObjLoading/Game/T6/BSP/BSPUtil.h index 96fd0506..d319748b 100644 --- a/src/ObjLoading/Game/T6/BSP/BSPUtil.h +++ b/src/ObjLoading/Game/T6/BSP/BSPUtil.h @@ -20,6 +20,8 @@ namespace BSP static void convertAnglesToAxis(vec3_t* angles, vec3_t axis[3]); static void convertQuaternionToAxis(vec4_t* quat, vec3_t axis[3]); static vec3_t convertForwardVectorToViewAngles(vec3_t& forwardVec); + static float getPitchFromVector(vec3_t& vector); + static vec3_t convertAxisToAngles(vec3_t axis[3]); static void matrixTranspose3x3(const vec3_t* in, vec3_t* out); static vec3_t convertStringToVec3(std::string& str); static std::string convertVec3ToString(vec3_t& vec); diff --git a/src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp b/src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp index 3c76e6d8..548cff22 100644 --- a/src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp +++ b/src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp @@ -67,22 +67,19 @@ namespace void addSpawnsToEntString(BSP::BSPData* bsp, std::string& entityString) { - if (!bsp->isZombiesMap && bsp->spawnpoints.size() == 0) + if (bsp->isZombiesMap) { - con::info("No spawnpoints found, setting all spawns to (0, 0, 0)"); - BSP::BSPSpawnPoint defaultSpawnPoint; - defaultSpawnPoint.origin.x = 0.0f; - defaultSpawnPoint.origin.y = 0.0f; - defaultSpawnPoint.origin.z = 0.0f; - defaultSpawnPoint.forward.x = 1.0f; - defaultSpawnPoint.forward.y = 0.0f; - defaultSpawnPoint.forward.z = 0.0f; - defaultSpawnPoint.type = BSP::BSPSpawnPointType::SPAWNPOINT_TYPE_DEFENDER; - bsp->spawnpoints.emplace_back(defaultSpawnPoint); - defaultSpawnPoint.type = BSP::BSPSpawnPointType::SPAWNPOINT_TYPE_ATTACKER; - bsp->spawnpoints.emplace_back(defaultSpawnPoint); - defaultSpawnPoint.type = BSP::BSPSpawnPointType::SPAWNPOINT_TYPE_ALL; - bsp->spawnpoints.emplace_back(defaultSpawnPoint); + for (auto& spawnPoint : bsp->spawnpoints) + { + entityString.append("{\n"); + entityString.append("\"classname\" \"script_struct\"\n"); + entityString.append(std::format("\"targetname\" \"{}\"\n", spawnPoint.spawnpointGroupName)); + entityString.append(std::format("\"origin\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(spawnPoint.origin))); + vec3_t angles = BSP::BSPUtil::convertForwardVectorToViewAngles(spawnPoint.forward); + entityString.append(std::format("\"angles\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(angles))); + entityString.append("}\n"); + } + return; } size_t defenderNameCount = std::extent::value; @@ -98,9 +95,9 @@ namespace std::string anglesStr = std::format("\"angles\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(angles)); const std::vector* spawnPointList; - if (spawnPoint.type == BSP::BSPSpawnPointType::SPAWNPOINT_TYPE_DEFENDER) + if (!spawnPoint.spawnpointGroupName.compare("defender")) spawnPointList = &DEFENDER_SPAWN_POINT_NAMES; - else if (spawnPoint.type == BSP::BSPSpawnPointType::SPAWNPOINT_TYPE_ATTACKER) + else if (!spawnPoint.spawnpointGroupName.compare("attacker")) spawnPointList = &ATTACKER_SPAWN_POINT_NAMES; else // SPAWNPOINT_TYPE_ALL spawnPointList = &FFA_SPAWN_POINT_NAMES; @@ -116,26 +113,15 @@ namespace } } - void addPathNodesToEntString(BSP::BSPData* bsp, std::string& entityString) - { - for (auto& pathnode : bsp->pathnodes) - { - entityString.append("{\n"); - entityString.append(std::format("\"origin\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(pathnode.origin))); - entityString.append(std::format("\"classname\" \"{}\"\n", "node_pathnode")); - entityString.append("}\n"); - } - } - void addZombiesEntitiesToEntString(BSP::BSPData* bsp, std::string& entityString) { - for (auto& zone : bsp->zZones) + for (auto& zone : bsp->zm_zones) { entityString.append("{\n"); entityString.append("\"classname\" \"info_volume\"\n"); entityString.append("\"script_noteworthy\" \"player_volume\"\n"); entityString.append(std::format("\"targetname\" \"{}\"\n", zone.zoneName)); - entityString.append(std::format("\"target\" \"{}\"\n", zone.zSpawnerGroupName)); + entityString.append(std::format("\"target\" \"{}\"\n", zone.spawnerGroupName)); entityString.append(std::format("\"origin\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(zone.origin))); entityString.append(std::format("\"model\" \"*{}\"\n", zone.modelIndex)); entityString.append("}\n"); @@ -148,50 +134,37 @@ namespace entityString.append("}\n"); } - for (auto& zSpawner : bsp->zSpawners) + for (auto& zSpawner : bsp->zm_spawners) { entityString.append("{\n"); entityString.append("\"classname\" \"script_struct\"\n"); entityString.append("\"script_noteworthy\" \"riser_location\"\n"); entityString.append("\"script_string\" \"find_flesh\"\n"); - entityString.append(std::format("\"targetname\" \"{}\"\n", zSpawner.zSpawnerGroupName)); + entityString.append(std::format("\"targetname\" \"{}\"\n", zSpawner.spawnerGroupName)); entityString.append(std::format("\"origin\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(zSpawner.origin))); vec3_t angles = BSP::BSPUtil::convertForwardVectorToViewAngles(zSpawner.forward); entityString.append(std::format("\"angles\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(angles))); entityString.append("}\n"); } - - for (auto& spawnPoint : bsp->zSpawnPoints) - { - entityString.append("{\n"); - entityString.append("\"classname\" \"script_struct\"\n"); - entityString.append(std::format("\"targetname\" \"{}\"\n", spawnPoint.spawnpointGroupName)); - entityString.append(std::format("\"origin\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(spawnPoint.origin))); - vec3_t angles = BSP::BSPUtil::convertForwardVectorToViewAngles(spawnPoint.forward); - entityString.append(std::format("\"angles\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(angles))); - entityString.append("}\n"); - } } - void addScriptingToEntString(BSP::BSPData* bsp, std::string& entityString) + void addClassEntitiesToEntString(BSP::BSPData* bsp, std::string& entityString) { - for (auto& useTrigger : bsp->useTriggers) + for (auto& entity : bsp->entities) { - entityString.append("{\n"); - entityString.append("\"classname\" \"trigger_use\"\n"); - entityString.append(std::format("\"targetname\" \"{}\"\n", useTrigger.triggerName)); - entityString.append(std::format("\"origin\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(useTrigger.origin))); - entityString.append(std::format("\"model\" \"*{}\"\n", useTrigger.modelIndex)); - entityString.append("}\n"); - } + vec3_t origin = entity.origin; + vec3_t axis[3]; + BSP::BSPUtil::convertQuaternionToAxis(&entity.rotationQuaternion, axis); + vec3_t angles = BSP::BSPUtil::convertAxisToAngles(axis); - for (auto& useTrigger : bsp->triggerMultiples) - { entityString.append("{\n"); - entityString.append("\"classname\" \"trigger_multiple\"\n"); - entityString.append(std::format("\"targetname\" \"{}\"\n", useTrigger.triggerName)); - entityString.append(std::format("\"origin\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(useTrigger.origin))); - entityString.append(std::format("\"model\" \"*{}\"\n", useTrigger.modelIndex)); + entityString.append(std::format("\"origin\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(origin))); + entityString.append(std::format("\"angles\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(angles))); + for (auto& entry : entity.entries) + entityString.append(std::format("\"{}\" \"{}\"\n", entry.key, entry.value)); + if (entity.modelIndex != 0) + entityString.append(std::format("\"model\" \"*{}\"\n", entity.modelIndex)); + entityString.append("}\n"); } } @@ -247,12 +220,10 @@ namespace BSP addSpawnsToEntString(bsp, entityString); - addPathNodesToEntString(bsp, entityString); - if (bsp->isZombiesMap) addZombiesEntitiesToEntString(bsp, entityString); - addScriptingToEntString(bsp, entityString); + addClassEntitiesToEntString(bsp, entityString); MapEnts* mapEnts = m_memory.Alloc(); mapEnts->name = m_memory.Dup(bsp->bspName.c_str());