mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-06-06 08:42:35 +00:00
Addition of zombie spawns, player spawns and zones for zombies. Clipmap AABB generation largely modified.
This commit is contained in:
@@ -69,6 +69,9 @@ namespace BSP
|
|||||||
std::vector<uint16_t> indices;
|
std::vector<uint16_t> indices;
|
||||||
std::vector<BSPMaterial> materials;
|
std::vector<BSPMaterial> materials;
|
||||||
std::vector<BSPXModel> xmodels;
|
std::vector<BSPXModel> xmodels;
|
||||||
|
|
||||||
|
size_t staticSurfaceCount;
|
||||||
|
std::vector<BSPSurface> internal_scriptSurfaces;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum BSPLightType
|
enum BSPLightType
|
||||||
@@ -113,6 +116,37 @@ namespace BSP
|
|||||||
vec3_t origin;
|
vec3_t origin;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BSPZoneZM
|
||||||
|
{
|
||||||
|
vec3_t origin;
|
||||||
|
std::string zoneName;
|
||||||
|
std::string zSpawnerGroupName;
|
||||||
|
std::string spawnpointGroupName;
|
||||||
|
size_t modelIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BSPSpawnPointZM
|
||||||
|
{
|
||||||
|
std::string spawnpointGroupName;
|
||||||
|
vec3_t origin;
|
||||||
|
vec3_t forward;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BSPZSpawnerZM
|
||||||
|
{
|
||||||
|
std::string zSpawnerGroupName;
|
||||||
|
vec3_t origin;
|
||||||
|
vec3_t forward;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BSPModel
|
||||||
|
{
|
||||||
|
bool isGfxModel;
|
||||||
|
|
||||||
|
size_t surfaceIndex;
|
||||||
|
size_t surfaceCount;
|
||||||
|
};
|
||||||
|
|
||||||
struct BSPData
|
struct BSPData
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
@@ -125,6 +159,12 @@ namespace BSP
|
|||||||
std::vector<BSPLight> lights;
|
std::vector<BSPLight> lights;
|
||||||
std::vector<BSPSpawnPoint> spawnpoints;
|
std::vector<BSPSpawnPoint> spawnpoints;
|
||||||
std::vector<BSPPathNode> pathnodes;
|
std::vector<BSPPathNode> pathnodes;
|
||||||
|
|
||||||
|
std::vector<BSPSpawnPointZM> zSpawnPoints;
|
||||||
|
std::vector<BSPZSpawnerZM> zSpawners;
|
||||||
|
std::vector<BSPZoneZM> zZones;
|
||||||
|
|
||||||
|
std::vector<BSPModel> models;
|
||||||
};
|
};
|
||||||
|
|
||||||
// BSPGameConstants:
|
// BSPGameConstants:
|
||||||
|
|||||||
@@ -612,26 +612,14 @@ namespace
|
|||||||
assert(node.extras->spawnpoint);
|
assert(node.extras->spawnpoint);
|
||||||
|
|
||||||
Eigen::Matrix4f nodeMatrix = createNodeMatrix(node);
|
Eigen::Matrix4f nodeMatrix = createNodeMatrix(node);
|
||||||
BSPSpawnPoint spawnPoint;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
Eigen::Vector4f position(0, 0, 0, 1.0f);
|
Eigen::Vector4f position(0, 0, 0, 1.0f);
|
||||||
Eigen::Vector4f transformedPosition = nodeMatrix * position;
|
Eigen::Vector4f transformedPosition = nodeMatrix * position;
|
||||||
spawnPoint.origin.x = transformedPosition.x();
|
vec3_t origin;
|
||||||
spawnPoint.origin.y = transformedPosition.y();
|
origin.x = transformedPosition.x();
|
||||||
spawnPoint.origin.z = transformedPosition.z();
|
origin.y = transformedPosition.y();
|
||||||
RhcToLhcCoordinates(spawnPoint.origin.v);
|
origin.z = transformedPosition.z();
|
||||||
|
RhcToLhcCoordinates(origin.v);
|
||||||
|
|
||||||
// GLTF default direction is +Y up
|
// GLTF default direction is +Y up
|
||||||
Eigen::Vector3f defaultDirection(0.0f, 1.0f, 0.0f);
|
Eigen::Vector3f defaultDirection(0.0f, 1.0f, 0.0f);
|
||||||
@@ -639,12 +627,152 @@ namespace
|
|||||||
Eigen::Matrix3f rotationMatrix = affineTransform.rotation();
|
Eigen::Matrix3f rotationMatrix = affineTransform.rotation();
|
||||||
Eigen::Vector3f outputDirection = rotationMatrix * defaultDirection;
|
Eigen::Vector3f outputDirection = rotationMatrix * defaultDirection;
|
||||||
outputDirection.normalize();
|
outputDirection.normalize();
|
||||||
spawnPoint.forward.x = outputDirection.x();
|
vec3_t forward;
|
||||||
spawnPoint.forward.y = outputDirection.y();
|
forward.x = outputDirection.x();
|
||||||
spawnPoint.forward.z = outputDirection.z();
|
forward.y = outputDirection.y();
|
||||||
RhcToLhcCoordinates(spawnPoint.forward.v);
|
forward.z = outputDirection.z();
|
||||||
|
RhcToLhcCoordinates(forward.v);
|
||||||
|
|
||||||
m_bsp->spawnpoints.emplace_back(spawnPoint);
|
if (m_bsp->isZombiesMap)
|
||||||
|
{
|
||||||
|
BSPSpawnPointZM spawnPoint;
|
||||||
|
spawnPoint.origin = origin;
|
||||||
|
spawnPoint.forward = forward;
|
||||||
|
spawnPoint.spawnpointGroupName = *node.extras->spawnpoint;
|
||||||
|
m_bsp->zSpawnPoints.emplace_back(spawnPoint);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t addScriptModel(const JsonRoot& jRoot, const gltf::JsonNode& node)
|
||||||
|
{
|
||||||
|
if (!node.mesh || !jRoot.meshes)
|
||||||
|
throw new GltfLoadException("Script model created with no mesh data");
|
||||||
|
|
||||||
|
Eigen::Matrix4f nodeMatrix = createNodeMatrix(node);
|
||||||
|
const auto& mesh = jRoot.meshes.value()[node.mesh.value()];
|
||||||
|
|
||||||
|
if (mesh.primitives.size() == 0)
|
||||||
|
throw new GltfLoadException("Script model created with no mesh data");
|
||||||
|
|
||||||
|
BSPModel model;
|
||||||
|
model.isGfxModel = m_is_world_gfx;
|
||||||
|
model.surfaceIndex = m_curr_bsp_world->internal_scriptSurfaces.size();
|
||||||
|
model.surfaceCount = mesh.primitives.size();
|
||||||
|
m_bsp->models.emplace_back(model);
|
||||||
|
|
||||||
|
for (const auto& primitive : mesh.primitives)
|
||||||
|
{
|
||||||
|
if (!primitive.indices)
|
||||||
|
throw GltfLoadException("Requires primitives indices");
|
||||||
|
if (primitive.mode.value_or(JsonMeshPrimitivesMode::TRIANGLES) != JsonMeshPrimitivesMode::TRIANGLES)
|
||||||
|
throw GltfLoadException("Only triangles are supported");
|
||||||
|
if (!primitive.attributes.POSITION)
|
||||||
|
throw GltfLoadException("Requires primitives attribute POSITION");
|
||||||
|
if (!primitive.attributes.NORMAL)
|
||||||
|
throw GltfLoadException("Requires primitives attribute NORMAL");
|
||||||
|
|
||||||
|
const AccessorsForVertex accessorsForVertex{
|
||||||
|
.m_position_accessor = *primitive.attributes.POSITION,
|
||||||
|
.m_normal_accessor = *primitive.attributes.NORMAL,
|
||||||
|
.m_color_accessor = primitive.attributes.COLOR_0,
|
||||||
|
.m_uv_accessor = primitive.attributes.TEXCOORD_0,
|
||||||
|
.m_index_accessor = *primitive.indices,
|
||||||
|
};
|
||||||
|
|
||||||
|
BSPSurface surface;
|
||||||
|
if (primitive.material)
|
||||||
|
surface.materialIndex = *primitive.material;
|
||||||
|
else
|
||||||
|
surface.materialIndex = m_emptyMaterialIndex;
|
||||||
|
vec4_t vertexColour = m_curr_bsp_world->materials.at(surface.materialIndex).materialColour;
|
||||||
|
CreateVertices(accessorsForVertex, nodeMatrix, surface, vertexColour);
|
||||||
|
|
||||||
|
m_curr_bsp_world->internal_scriptSurfaces.emplace_back(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_bsp->models.size(); // script model index starts at 1
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addZoneNode(const JsonRoot& jRoot, const gltf::JsonNode& node)
|
||||||
|
{
|
||||||
|
assert(node.extras);
|
||||||
|
assert(node.extras->zone);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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.modelIndex = addScriptModel(jRoot, node);
|
||||||
|
m_bsp->zZones.emplace_back(zone);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addZSpawnerNode(const gltf::JsonNode& node)
|
||||||
|
{
|
||||||
|
assert(node.extras);
|
||||||
|
assert(node.extras->zspawner);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// GLTF default direction is +Y up
|
||||||
|
Eigen::Vector3f defaultDirection(0.0f, 1.0f, 0.0f);
|
||||||
|
Eigen::Affine3f affineTransform(nodeMatrix);
|
||||||
|
Eigen::Matrix3f rotationMatrix = affineTransform.rotation();
|
||||||
|
Eigen::Vector3f outputDirection = rotationMatrix * defaultDirection;
|
||||||
|
outputDirection.normalize();
|
||||||
|
vec3_t forward;
|
||||||
|
forward.x = outputDirection.x();
|
||||||
|
forward.y = outputDirection.y();
|
||||||
|
forward.z = outputDirection.z();
|
||||||
|
RhcToLhcCoordinates(forward.v);
|
||||||
|
|
||||||
|
BSPZSpawnerZM spawner;
|
||||||
|
spawner.origin = origin;
|
||||||
|
spawner.forward = forward;
|
||||||
|
spawner.zSpawnerGroupName = *node.extras->zspawner;
|
||||||
|
m_bsp->zSpawners.emplace_back(spawner);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -659,11 +787,20 @@ namespace
|
|||||||
if (node.extras->xmodel)
|
if (node.extras->xmodel)
|
||||||
return addXModelNode(jRoot, node);
|
return addXModelNode(jRoot, node);
|
||||||
|
|
||||||
if (m_is_world_gfx && node.extras->spawnpoint)
|
if (!m_is_world_gfx && node.extras->spawnpoint)
|
||||||
return addSpawnPointNode(node);
|
return addSpawnPointNode(node);
|
||||||
|
|
||||||
if (m_is_world_gfx && node.extras->pathnode)
|
if (!m_is_world_gfx && node.extras->pathnode)
|
||||||
return addPathNode_Node(node);
|
return addPathNode_Node(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)
|
if (node.mesh)
|
||||||
@@ -1017,6 +1154,13 @@ namespace
|
|||||||
|
|
||||||
LoadMaterials(jRoot);
|
LoadMaterials(jRoot);
|
||||||
TraverseNodes(jRoot); // requires materials and lights
|
TraverseNodes(jRoot); // requires materials and lights
|
||||||
|
|
||||||
|
size_t staticSurfaceCount = m_curr_bsp_world->surfaces.size();
|
||||||
|
for (auto& model : m_bsp->models)
|
||||||
|
model.surfaceIndex += staticSurfaceCount;
|
||||||
|
m_curr_bsp_world->staticSurfaceCount = staticSurfaceCount;
|
||||||
|
m_curr_bsp_world->surfaces.insert(
|
||||||
|
m_curr_bsp_world->surfaces.end(), m_curr_bsp_world->internal_scriptSurfaces.begin(), m_curr_bsp_world->internal_scriptSurfaces.end());
|
||||||
}
|
}
|
||||||
catch (const GltfLoadException& e)
|
catch (const GltfLoadException& e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -167,17 +167,17 @@ namespace BSP
|
|||||||
float quatZ = quat->v[2];
|
float quatZ = quat->v[2];
|
||||||
float quatW = quat->v[3];
|
float quatW = quat->v[3];
|
||||||
|
|
||||||
float xx = (quatX * 2.0) * quatX;
|
float xx = (quatX * 2.0f) * quatX;
|
||||||
float xy = (quatX * 2.0) * quatY;
|
float xy = (quatX * 2.0f) * quatY;
|
||||||
float xz = (quatX * 2.0) * quatZ;
|
float xz = (quatX * 2.0f) * quatZ;
|
||||||
float xw = (quatX * 2.0) * quatW;
|
float xw = (quatX * 2.0f) * quatW;
|
||||||
|
|
||||||
float yy = (quatY * 2.0) * quatY;
|
float yy = (quatY * 2.0f) * quatY;
|
||||||
float yz = (quatY * 2.0) * quatZ;
|
float yz = (quatY * 2.0f) * quatZ;
|
||||||
float yw = (quatY * 2.0) * quatW;
|
float yw = (quatY * 2.0f) * quatW;
|
||||||
|
|
||||||
float zz = (quatZ * 2.0) * quatZ;
|
float zz = (quatZ * 2.0f) * quatZ;
|
||||||
float zw = (quatZ * 2.0) * quatW;
|
float zw = (quatZ * 2.0f) * quatW;
|
||||||
|
|
||||||
axis->x = 1.0f - (zz + yy);
|
axis->x = 1.0f - (zz + yy);
|
||||||
axis->y = zw + xy;
|
axis->y = zw + xy;
|
||||||
|
|||||||
@@ -120,40 +120,6 @@ namespace BSP
|
|||||||
clipMap->ropes = m_memory.Alloc<rope_t>(clipMap->max_ropes);
|
clipMap->ropes = m_memory.Alloc<rope_t>(clipMap->max_ropes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClipMapLinker::loadSubModelCollision(clipMap_t* clipMap, BSPData* bsp)
|
|
||||||
{
|
|
||||||
// Submodels are used for the world and map ent collision (triggers, bomb zones, etc)
|
|
||||||
clipMap->numSubModels = 1;
|
|
||||||
clipMap->cmodels = m_memory.Alloc<cmodel_t>(clipMap->numSubModels);
|
|
||||||
for (unsigned int vertIdx = 0; vertIdx < clipMap->vertCount; vertIdx++)
|
|
||||||
{
|
|
||||||
vec3_t vertex = clipMap->verts[vertIdx];
|
|
||||||
if (vertIdx == 0)
|
|
||||||
{
|
|
||||||
clipMap->cmodels[0].mins = vertex;
|
|
||||||
clipMap->cmodels[0].maxs = vertex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
BSPUtil::updateAABBWithPoint(vertex, clipMap->cmodels[0].mins, clipMap->cmodels[0].maxs);
|
|
||||||
}
|
|
||||||
clipMap->cmodels[0].radius = BSPUtil::distBetweenPoints(clipMap->cmodels[0].mins, clipMap->cmodels[0].maxs) / 2;
|
|
||||||
|
|
||||||
// The world sub model has no leafs associated with it
|
|
||||||
clipMap->cmodels[0].leaf.firstCollAabbIndex = 0;
|
|
||||||
clipMap->cmodels[0].leaf.collAabbCount = 0;
|
|
||||||
clipMap->cmodels[0].leaf.brushContents = 0;
|
|
||||||
clipMap->cmodels[0].leaf.terrainContents = 0;
|
|
||||||
clipMap->cmodels[0].leaf.mins.x = 0.0f;
|
|
||||||
clipMap->cmodels[0].leaf.mins.y = 0.0f;
|
|
||||||
clipMap->cmodels[0].leaf.mins.z = 0.0f;
|
|
||||||
clipMap->cmodels[0].leaf.maxs.x = 0.0f;
|
|
||||||
clipMap->cmodels[0].leaf.maxs.y = 0.0f;
|
|
||||||
clipMap->cmodels[0].leaf.maxs.z = 0.0f;
|
|
||||||
clipMap->cmodels[0].leaf.leafBrushNode = 0;
|
|
||||||
clipMap->cmodels[0].leaf.cluster = 0;
|
|
||||||
clipMap->cmodels[0].info = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClipMapLinker::loadXModelCollision(clipMap_t* clipMap, BSPData* bsp)
|
bool ClipMapLinker::loadXModelCollision(clipMap_t* clipMap, BSPData* bsp)
|
||||||
{
|
{
|
||||||
// it seems like for players to be able to collide with xmodels, it requires xmodel->collmaps to be valid.
|
// it seems like for players to be able to collide with xmodels, it requires xmodel->collmaps to be valid.
|
||||||
@@ -236,6 +202,115 @@ namespace BSP
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void calculatePartitionAABB(clipMap_t* clipMap, CollisionPartition* partition, vec3_t& out_mins, vec3_t& out_maxs);
|
||||||
|
void addAABBTreeFromPartitions(
|
||||||
|
clipMap_t* clipMap, std::vector<int>& partitions, size_t* out_parentCount, size_t* out_parentStartIndex, int* out_treeContents);
|
||||||
|
|
||||||
|
void ClipMapLinker::loadSubModelCollision(clipMap_t* clipMap, BSPData* bsp)
|
||||||
|
{
|
||||||
|
// Submodels are used for the world and map ent collision (triggers, bomb zones, etc)
|
||||||
|
clipMap->numSubModels = static_cast<unsigned int>(bsp->models.size() + 1);
|
||||||
|
clipMap->cmodels = m_memory.Alloc<cmodel_t>(clipMap->numSubModels);
|
||||||
|
|
||||||
|
// first model is always the world model
|
||||||
|
for (unsigned int vertIdx = 0; vertIdx < clipMap->vertCount; vertIdx++)
|
||||||
|
{
|
||||||
|
vec3_t vertex = clipMap->verts[vertIdx];
|
||||||
|
if (vertIdx == 0)
|
||||||
|
{
|
||||||
|
clipMap->cmodels[0].mins = vertex;
|
||||||
|
clipMap->cmodels[0].maxs = vertex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
BSPUtil::updateAABBWithPoint(vertex, clipMap->cmodels[0].mins, clipMap->cmodels[0].maxs);
|
||||||
|
}
|
||||||
|
clipMap->cmodels[0].radius = BSPUtil::distBetweenPoints(clipMap->cmodels[0].mins, clipMap->cmodels[0].maxs) / 2;
|
||||||
|
// The world sub model has no data apart from the bounds
|
||||||
|
clipMap->cmodels[0].leaf.firstCollAabbIndex = 0;
|
||||||
|
clipMap->cmodels[0].leaf.collAabbCount = 0;
|
||||||
|
clipMap->cmodels[0].leaf.brushContents = 0;
|
||||||
|
clipMap->cmodels[0].leaf.terrainContents = 0;
|
||||||
|
clipMap->cmodels[0].leaf.mins.x = 0.0f;
|
||||||
|
clipMap->cmodels[0].leaf.mins.y = 0.0f;
|
||||||
|
clipMap->cmodels[0].leaf.mins.z = 0.0f;
|
||||||
|
clipMap->cmodels[0].leaf.maxs.x = 0.0f;
|
||||||
|
clipMap->cmodels[0].leaf.maxs.y = 0.0f;
|
||||||
|
clipMap->cmodels[0].leaf.maxs.z = 0.0f;
|
||||||
|
clipMap->cmodels[0].leaf.leafBrushNode = 0;
|
||||||
|
clipMap->cmodels[0].leaf.cluster = 0;
|
||||||
|
clipMap->cmodels[0].info = nullptr;
|
||||||
|
|
||||||
|
for (size_t modelIdx = 0; modelIdx < bsp->models.size(); modelIdx++)
|
||||||
|
{
|
||||||
|
auto clipModel = &clipMap->cmodels[modelIdx + 1];
|
||||||
|
auto& bspMpdel = bsp->models.at(modelIdx);
|
||||||
|
|
||||||
|
if (bspMpdel.isGfxModel)
|
||||||
|
{
|
||||||
|
clipModel->mins.x = 0.0f;
|
||||||
|
clipModel->mins.y = 0.0f;
|
||||||
|
clipModel->mins.z = 0.0f;
|
||||||
|
clipModel->maxs.x = 0.0f;
|
||||||
|
clipModel->maxs.y = 0.0f;
|
||||||
|
clipModel->maxs.z = 0.0f;
|
||||||
|
clipModel->radius = 0.0f;
|
||||||
|
clipModel->leaf.brushContents = 0;
|
||||||
|
clipModel->leaf.mins.x = 0.0f;
|
||||||
|
clipModel->leaf.mins.y = 0.0f;
|
||||||
|
clipModel->leaf.mins.z = 0.0f;
|
||||||
|
clipModel->leaf.maxs.x = 0.0f;
|
||||||
|
clipModel->leaf.maxs.y = 0.0f;
|
||||||
|
clipModel->leaf.maxs.z = 0.0f;
|
||||||
|
clipModel->leaf.leafBrushNode = 0;
|
||||||
|
clipModel->leaf.cluster = 0;
|
||||||
|
clipModel->info = nullptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> partitionIndexes;
|
||||||
|
for (size_t surfIdx = 0; surfIdx < bspMpdel.surfaceCount; surfIdx++)
|
||||||
|
{
|
||||||
|
ColSurface& surf = collisionSurfaceVec.at(bspMpdel.surfaceIndex + surfIdx);
|
||||||
|
for (size_t partitionIdx = 0; partitionIdx < surf.partitionCount; partitionIdx++)
|
||||||
|
{
|
||||||
|
size_t clipMapPartitionIndex = surf.partitionStartIndex + partitionIdx;
|
||||||
|
partitionIndexes.emplace_back(clipMapPartitionIndex);
|
||||||
|
vec3_t mins;
|
||||||
|
vec3_t maxs;
|
||||||
|
calculatePartitionAABB(clipMap, &clipMap->partitions[clipMapPartitionIndex], mins, maxs);
|
||||||
|
if (surfIdx == 0 && partitionIdx == 0)
|
||||||
|
{
|
||||||
|
clipModel->mins = mins;
|
||||||
|
clipModel->maxs = maxs;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
BSPUtil::updateAABB(mins, maxs, clipModel->mins, clipModel->maxs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clipModel->radius = BSPUtil::distBetweenPoints(clipModel->mins, clipModel->maxs) / 2;
|
||||||
|
|
||||||
|
int terrainContents = 0;
|
||||||
|
size_t firstCollAabbIndex = 0;
|
||||||
|
size_t collAabbCount = 0;
|
||||||
|
addAABBTreeFromPartitions(clipMap, partitionIndexes, &collAabbCount, &firstCollAabbIndex, &terrainContents);
|
||||||
|
clipModel->leaf.terrainContents = terrainContents;
|
||||||
|
clipModel->leaf.firstCollAabbIndex = static_cast<uint16_t>(firstCollAabbIndex);
|
||||||
|
clipModel->leaf.collAabbCount = static_cast<uint16_t>(collAabbCount);
|
||||||
|
|
||||||
|
// no brush
|
||||||
|
clipModel->leaf.brushContents = 0;
|
||||||
|
clipModel->leaf.mins.x = 0.0f;
|
||||||
|
clipModel->leaf.mins.y = 0.0f;
|
||||||
|
clipModel->leaf.mins.z = 0.0f;
|
||||||
|
clipModel->leaf.maxs.x = 0.0f;
|
||||||
|
clipModel->leaf.maxs.y = 0.0f;
|
||||||
|
clipModel->leaf.maxs.z = 0.0f;
|
||||||
|
clipModel->leaf.leafBrushNode = 0;
|
||||||
|
clipModel->leaf.cluster = 0;
|
||||||
|
clipModel->info = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// out_mins and out_maxs are initialised in the function
|
// out_mins and out_maxs are initialised in the function
|
||||||
void calculatePartitionAABB(clipMap_t* clipMap, CollisionPartition* partition, vec3_t& out_mins, vec3_t& out_maxs)
|
void calculatePartitionAABB(clipMap_t* clipMap, CollisionPartition* partition, vec3_t& out_mins, vec3_t& out_maxs)
|
||||||
{
|
{
|
||||||
@@ -257,10 +332,129 @@ namespace BSP
|
|||||||
|
|
||||||
struct uniqueMatData
|
struct uniqueMatData
|
||||||
{
|
{
|
||||||
unsigned int materialIndex;
|
size_t materialIndex;
|
||||||
std::vector<size_t> objectIndexes;
|
std::vector<int> partitionIndexes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void ClipMapLinker::addAABBTreeFromPartitions(
|
||||||
|
clipMap_t* clipMap, std::vector<int>& partitions, size_t* out_parentCount, size_t* out_parentStartIndex, int* out_treeContents)
|
||||||
|
{
|
||||||
|
size_t partitionCount = partitions.size();
|
||||||
|
assert(partitionCount > 0);
|
||||||
|
if (partitionCount > highestPartitionCountForAABB)
|
||||||
|
highestPartitionCountForAABB = partitionCount;
|
||||||
|
|
||||||
|
std::vector<uniqueMatData> uniqueMaterials;
|
||||||
|
for (size_t partArrayIdx = 0; partArrayIdx < partitionCount; partArrayIdx++)
|
||||||
|
{
|
||||||
|
int partitionIdx = partitions.at(partArrayIdx);
|
||||||
|
size_t materialIndex = collisionSurfaceVec.at(partitionToColSurfaceMap.at(partitionIdx)).materialIndex;
|
||||||
|
bool foundIdx = false;
|
||||||
|
for (auto& uniqueMat : uniqueMaterials)
|
||||||
|
{
|
||||||
|
if (uniqueMat.materialIndex == materialIndex)
|
||||||
|
{
|
||||||
|
uniqueMat.partitionIndexes.emplace_back(partitionIdx);
|
||||||
|
foundIdx = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundIdx)
|
||||||
|
{
|
||||||
|
uniqueMatData data;
|
||||||
|
data.materialIndex = materialIndex;
|
||||||
|
data.partitionIndexes.emplace_back(partitionIdx);
|
||||||
|
uniqueMaterials.emplace_back(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BO2 has a maximum limit of 128 children per AABB tree (essentially),
|
||||||
|
// so this is fixed by adding multiple parent AABB trees that hold 128 children each
|
||||||
|
size_t totalParentCount = 0;
|
||||||
|
for (auto& matData : uniqueMaterials)
|
||||||
|
{
|
||||||
|
size_t objCount = matData.partitionIndexes.size();
|
||||||
|
size_t result = objCount / BSPGameConstants::MAX_AABB_TREE_CHILDREN;
|
||||||
|
size_t remainder = objCount % BSPGameConstants::MAX_AABB_TREE_CHILDREN;
|
||||||
|
if (remainder > 0)
|
||||||
|
result++;
|
||||||
|
totalParentCount += result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// every parent node needs to be contiguous in memory
|
||||||
|
size_t parentAABBArrayIndex = AABBTreeVec.size();
|
||||||
|
AABBTreeVec.resize(AABBTreeVec.size() + totalParentCount);
|
||||||
|
*out_parentCount = totalParentCount;
|
||||||
|
*out_parentStartIndex = parentAABBArrayIndex;
|
||||||
|
|
||||||
|
for (auto& matData : uniqueMaterials)
|
||||||
|
{
|
||||||
|
size_t matPartCount = matData.partitionIndexes.size();
|
||||||
|
size_t parentCount = matPartCount / BSPGameConstants::MAX_AABB_TREE_CHILDREN;
|
||||||
|
size_t remainder = matPartCount % BSPGameConstants::MAX_AABB_TREE_CHILDREN;
|
||||||
|
if (remainder > 0)
|
||||||
|
parentCount++;
|
||||||
|
|
||||||
|
size_t unaddedObjectCount = matPartCount;
|
||||||
|
size_t addedObjectCount = 0;
|
||||||
|
for (size_t parentIdx = 0; parentIdx < parentCount; parentIdx++)
|
||||||
|
{
|
||||||
|
size_t currChildObjectCount = BSPGameConstants::MAX_AABB_TREE_CHILDREN;
|
||||||
|
if (unaddedObjectCount <= BSPGameConstants::MAX_AABB_TREE_CHILDREN)
|
||||||
|
currChildObjectCount = unaddedObjectCount;
|
||||||
|
else
|
||||||
|
unaddedObjectCount -= BSPGameConstants::MAX_AABB_TREE_CHILDREN;
|
||||||
|
|
||||||
|
vec3_t parentMins;
|
||||||
|
vec3_t parentMaxs;
|
||||||
|
size_t childObjectStartIndex = AABBTreeVec.size();
|
||||||
|
for (size_t objectIdx = 0; objectIdx < currChildObjectCount; objectIdx++)
|
||||||
|
{
|
||||||
|
// create a child AABBTree with the partition and add it to AABBTreeVec
|
||||||
|
int partitionIndex = matData.partitionIndexes.at(addedObjectCount + objectIdx);
|
||||||
|
CollisionPartition* partition = &clipMap->partitions[partitionIndex];
|
||||||
|
vec3_t childMins;
|
||||||
|
vec3_t childMaxs;
|
||||||
|
calculatePartitionAABB(clipMap, partition, childMins, childMaxs);
|
||||||
|
|
||||||
|
CollisionAabbTree childAABBTree;
|
||||||
|
childAABBTree.materialIndex = static_cast<uint16_t>(matData.materialIndex);
|
||||||
|
childAABBTree.childCount = 0;
|
||||||
|
childAABBTree.u.partitionIndex = partitionIndex;
|
||||||
|
childAABBTree.origin = BSPUtil::calcMiddleOfAABB(childMins, childMaxs);
|
||||||
|
childAABBTree.halfSize = BSPUtil::calcHalfSizeOfAABB(childMins, childMaxs);
|
||||||
|
AABBTreeVec.emplace_back(childAABBTree);
|
||||||
|
|
||||||
|
// update the parent AABB with the child AABB
|
||||||
|
if (objectIdx == 0)
|
||||||
|
{
|
||||||
|
parentMins = childMins;
|
||||||
|
parentMaxs = childMaxs;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
BSPUtil::updateAABB(childMins, childMaxs, parentMins, parentMaxs);
|
||||||
|
}
|
||||||
|
|
||||||
|
CollisionAabbTree parentAABB;
|
||||||
|
parentAABB.materialIndex = static_cast<uint16_t>(matData.materialIndex);
|
||||||
|
parentAABB.origin = BSPUtil::calcMiddleOfAABB(parentMins, parentMaxs);
|
||||||
|
parentAABB.halfSize = BSPUtil::calcHalfSizeOfAABB(parentMins, parentMaxs);
|
||||||
|
parentAABB.childCount = static_cast<uint16_t>(currChildObjectCount);
|
||||||
|
parentAABB.u.firstChildIndex = static_cast<int>(childObjectStartIndex);
|
||||||
|
AABBTreeVec.at(parentAABBArrayIndex + parentIdx) = parentAABB;
|
||||||
|
|
||||||
|
addedObjectCount += currChildObjectCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
parentAABBArrayIndex += parentCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_treeContents = 0;
|
||||||
|
for (auto& matData : uniqueMaterials)
|
||||||
|
*out_treeContents |= clipMap->info.materials[matData.materialIndex].contentFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
void ClipMapLinker::addAABBTreeFromLeaf(clipMap_t* clipMap, BSPTree* tree, size_t* out_parentCount, size_t* out_parentStartIndex, int* out_treeContents)
|
void ClipMapLinker::addAABBTreeFromLeaf(clipMap_t* clipMap, BSPTree* tree, size_t* out_parentCount, size_t* out_parentStartIndex, int* out_treeContents)
|
||||||
{
|
{
|
||||||
assert(tree->isLeaf);
|
assert(tree->isLeaf);
|
||||||
@@ -268,15 +462,15 @@ namespace BSP
|
|||||||
|
|
||||||
size_t leafObjectCount = bspLeaf->getObjectCount();
|
size_t leafObjectCount = bspLeaf->getObjectCount();
|
||||||
assert(leafObjectCount > 0);
|
assert(leafObjectCount > 0);
|
||||||
if (leafObjectCount > highestLeafObjectCount)
|
if (leafObjectCount > highestPartitionCountForAABB)
|
||||||
highestLeafObjectCount = leafObjectCount;
|
highestPartitionCountForAABB = leafObjectCount;
|
||||||
|
|
||||||
// the material index of the AABB tree is only checked for the parent node, so each parent has only children with the same material
|
// the material index of the AABB tree is only checked for the parent node, so each parent has only children with the same material
|
||||||
std::vector<uniqueMatData> uniqueMaterials;
|
std::vector<uniqueMatData> uniqueMaterials;
|
||||||
for (size_t objIdx = 0; objIdx < leafObjectCount; objIdx++)
|
for (size_t objIdx = 0; objIdx < leafObjectCount; objIdx++)
|
||||||
{
|
{
|
||||||
int partitionIdx = bspLeaf->getObject(objIdx)->partitionIndex;
|
int partitionIdx = bspLeaf->getObject(objIdx)->partitionIndex;
|
||||||
unsigned materialIndex = partitionToMaterialMap[partitionIdx];
|
size_t materialIndex = collisionSurfaceVec.at(partitionToColSurfaceMap.at(partitionIdx)).materialIndex;
|
||||||
bool foundIdx = false;
|
bool foundIdx = false;
|
||||||
for (auto& uniqueMat : uniqueMaterials)
|
for (auto& uniqueMat : uniqueMaterials)
|
||||||
{
|
{
|
||||||
@@ -382,6 +576,7 @@ namespace BSP
|
|||||||
for (auto& matData : uniqueMaterials)
|
for (auto& matData : uniqueMaterials)
|
||||||
*out_treeContents |= clipMap->info.materials[matData.materialIndex].contentFlags;
|
*out_treeContents |= clipMap->info.materials[matData.materialIndex].contentFlags;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
constexpr vec3_t normalX = {1.0f, 0.0f, 0.0f};
|
constexpr vec3_t normalX = {1.0f, 0.0f, 0.0f};
|
||||||
constexpr vec3_t normalY = {0.0f, 1.0f, 0.0f};
|
constexpr vec3_t normalY = {0.0f, 1.0f, 0.0f};
|
||||||
@@ -432,7 +627,10 @@ namespace BSP
|
|||||||
size_t parentCount = 0;
|
size_t parentCount = 0;
|
||||||
size_t parentStartIndex = 0;
|
size_t parentStartIndex = 0;
|
||||||
int treeContents = 0;
|
int treeContents = 0;
|
||||||
addAABBTreeFromLeaf(clipMap, tree, &parentCount, &parentStartIndex, &treeContents);
|
std::vector<int> partitions;
|
||||||
|
for (size_t objIdx = 0; objIdx < tree->leaf->getObjectCount(); objIdx++)
|
||||||
|
partitions.emplace_back(tree->leaf->getObject(objIdx)->partitionIndex);
|
||||||
|
addAABBTreeFromPartitions(clipMap, partitions, &parentCount, &parentStartIndex, &treeContents);
|
||||||
leaf.collAabbCount = static_cast<uint16_t>(parentCount);
|
leaf.collAabbCount = static_cast<uint16_t>(parentCount);
|
||||||
leaf.firstCollAabbIndex = static_cast<uint16_t>(parentStartIndex);
|
leaf.firstCollAabbIndex = static_cast<uint16_t>(parentStartIndex);
|
||||||
leaf.terrainContents = treeContents;
|
leaf.terrainContents = treeContents;
|
||||||
@@ -514,15 +712,20 @@ namespace BSP
|
|||||||
}
|
}
|
||||||
std::unique_ptr<BSPTree> tree = std::make_unique<BSPTree>(worldMins.x, worldMins.y, worldMins.z, worldMaxs.x, worldMaxs.y, worldMaxs.z, 0);
|
std::unique_ptr<BSPTree> tree = std::make_unique<BSPTree>(worldMins.x, worldMins.y, worldMins.z, worldMaxs.x, worldMaxs.y, worldMaxs.z, 0);
|
||||||
|
|
||||||
for (int partitionIdx = 0; partitionIdx < clipMap->partitionCount; partitionIdx++)
|
for (size_t surfIdx = 0; surfIdx < bsp->colWorld.staticSurfaceCount; surfIdx++) // only add the surfaces the player will collide with
|
||||||
{
|
{
|
||||||
vec3_t partitionMins;
|
ColSurface& colSurface = collisionSurfaceVec.at(surfIdx);
|
||||||
vec3_t partitionMaxs;
|
for (size_t partitionIdx = 0; partitionIdx < colSurface.partitionCount; partitionIdx++)
|
||||||
CollisionPartition* partition = &clipMap->partitions[partitionIdx];
|
{
|
||||||
calculatePartitionAABB(clipMap, partition, partitionMins, partitionMaxs);
|
vec3_t partitionMins;
|
||||||
std::shared_ptr<BSPObject> currObject =
|
vec3_t partitionMaxs;
|
||||||
std::make_shared<BSPObject>(partitionMins.x, partitionMins.y, partitionMins.z, partitionMaxs.x, partitionMaxs.y, partitionMaxs.z, partitionIdx);
|
size_t clipMapPartitionIdx = colSurface.partitionStartIndex + partitionIdx;
|
||||||
tree->addObjectToTree(std::move(currObject));
|
CollisionPartition* partition = &clipMap->partitions[clipMapPartitionIdx];
|
||||||
|
calculatePartitionAABB(clipMap, partition, partitionMins, partitionMaxs);
|
||||||
|
std::shared_ptr<BSPObject> currObject = std::make_shared<BSPObject>(
|
||||||
|
partitionMins.x, partitionMins.y, partitionMins.z, partitionMaxs.x, partitionMaxs.y, partitionMaxs.z, clipMapPartitionIdx);
|
||||||
|
tree->addObjectToTree(std::move(currObject));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load planes, nodes, leafs, and AABB trees
|
// load planes, nodes, leafs, and AABB trees
|
||||||
@@ -540,15 +743,11 @@ namespace BSP
|
|||||||
clipMap->leafs = m_memory.Alloc<cLeaf_s>(leafVec.size());
|
clipMap->leafs = m_memory.Alloc<cLeaf_s>(leafVec.size());
|
||||||
memcpy(clipMap->leafs, leafVec.data(), sizeof(cLeaf_s) * leafVec.size());
|
memcpy(clipMap->leafs, leafVec.data(), sizeof(cLeaf_s) * leafVec.size());
|
||||||
|
|
||||||
clipMap->aabbTreeCount = static_cast<unsigned int>(AABBTreeVec.size());
|
|
||||||
clipMap->aabbTrees = m_memory.Alloc<CollisionAabbTree>(AABBTreeVec.size());
|
|
||||||
memcpy(clipMap->aabbTrees, AABBTreeVec.data(), sizeof(CollisionAabbTree) * AABBTreeVec.size());
|
|
||||||
|
|
||||||
// The plane of each node have the same index
|
// The plane of each node have the same index
|
||||||
for (size_t nodeIdx = 0; nodeIdx < nodeVec.size(); nodeIdx++)
|
for (size_t nodeIdx = 0; nodeIdx < nodeVec.size(); nodeIdx++)
|
||||||
clipMap->nodes[nodeIdx].plane = &clipMap->info.planes[nodeIdx];
|
clipMap->nodes[nodeIdx].plane = &clipMap->info.planes[nodeIdx];
|
||||||
|
|
||||||
con::info("Highest leaf object count: {}", highestLeafObjectCount);
|
con::info("Highest AABB tree partition count: {}", highestPartitionCountForAABB);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -568,38 +767,36 @@ namespace BSP
|
|||||||
for (unsigned int vertIdx = 0; vertIdx < clipMap->vertCount; vertIdx++)
|
for (unsigned int vertIdx = 0; vertIdx < clipMap->vertCount; vertIdx++)
|
||||||
clipMap->verts[vertIdx] = bsp->colWorld.vertices[vertIdx].pos;
|
clipMap->verts[vertIdx] = bsp->colWorld.vertices[vertIdx].pos;
|
||||||
|
|
||||||
// The clipmap index buffer has a unique index for each vertex in the world, compared to the gfxworld's
|
|
||||||
// index buffer having a unique index for each vertex on a surface. This code converts gfxworld indices to clipmap indices.
|
|
||||||
std::vector<uint16_t> triIndexVec;
|
std::vector<uint16_t> triIndexVec;
|
||||||
|
std::vector<CollisionPartition> partitionVec;
|
||||||
|
std::vector<uint16_t> uniqueIndicesVec;
|
||||||
for (BSPSurface& surface : bsp->colWorld.surfaces)
|
for (BSPSurface& surface : bsp->colWorld.surfaces)
|
||||||
{
|
{
|
||||||
int indexOfFirstIndex = surface.indexOfFirstIndex;
|
int indexOfFirstIndex = surface.indexOfFirstIndex;
|
||||||
int indexOfFirstVertex = surface.indexOfFirstVertex;
|
|
||||||
for (int indexIdx = 0; indexIdx < surface.triCount * 3; indexIdx++)
|
|
||||||
{
|
|
||||||
int triIndex = bsp->colWorld.indices[indexOfFirstIndex + indexIdx] + indexOfFirstVertex;
|
|
||||||
triIndexVec.emplace_back(triIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// the reinterpret_cast is used as triIndices is just a pointer to an array of indicies, and static_cast can't safely do the conversion
|
|
||||||
clipMap->triCount = static_cast<int>(triIndexVec.size() / 3);
|
|
||||||
clipMap->triIndices = reinterpret_cast<uint16_t(*)[3]>(m_memory.Alloc<uint16_t>(triIndexVec.size()));
|
|
||||||
memcpy(clipMap->triIndices, &triIndexVec[0], sizeof(uint16_t) * triIndexVec.size());
|
|
||||||
|
|
||||||
// partitions are "containers" for vertices. BSP tree leafs contain a list of these partitions to determine the collision within a leaf.
|
|
||||||
std::vector<CollisionPartition> partitionVec;
|
|
||||||
std::vector<uint16_t> uniqueIndicesVec;
|
|
||||||
for (size_t surfIdx = 0; surfIdx < bsp->colWorld.surfaces.size(); surfIdx++)
|
|
||||||
{
|
|
||||||
BSPSurface& surface = bsp->colWorld.surfaces[surfIdx];
|
|
||||||
|
|
||||||
// partitions are made for each triangle, not one for each surface.
|
|
||||||
// one for each surface causes physics bugs, as the entire bounding box is considered solid instead of the surface itself (for some reason).
|
|
||||||
// so a partition is made for each triangle which removes the physics bugs but likely makes the game run slower
|
|
||||||
int indexOfFirstTri = surface.indexOfFirstIndex / 3;
|
int indexOfFirstTri = surface.indexOfFirstIndex / 3;
|
||||||
int indexOfFirstVertex = surface.indexOfFirstVertex;
|
int indexOfFirstVertex = surface.indexOfFirstVertex;
|
||||||
|
|
||||||
|
ColSurface colSurface;
|
||||||
|
colSurface.materialIndex = surface.materialIndex;
|
||||||
|
colSurface.partitionCount = surface.triCount;
|
||||||
|
colSurface.partitionStartIndex = partitionVec.size();
|
||||||
|
collisionSurfaceVec.emplace_back(colSurface);
|
||||||
|
|
||||||
for (int triIdx = 0; triIdx < surface.triCount; triIdx++)
|
for (int triIdx = 0; triIdx < surface.triCount; triIdx++)
|
||||||
{
|
{
|
||||||
|
// The clipmap index buffer has a unique index for each vertex in the world, compared to the gfxworld's
|
||||||
|
// index buffer having a unique index for each vertex on a surface. This code converts gfxworld indices to clipmap indices.
|
||||||
|
int triIndex0 = bsp->colWorld.indices[indexOfFirstIndex + (triIdx * 3) + 0] + indexOfFirstVertex;
|
||||||
|
int triIndex1 = bsp->colWorld.indices[indexOfFirstIndex + (triIdx * 3) + 1] + indexOfFirstVertex;
|
||||||
|
int triIndex2 = bsp->colWorld.indices[indexOfFirstIndex + (triIdx * 3) + 2] + indexOfFirstVertex;
|
||||||
|
triIndexVec.emplace_back(triIndex0);
|
||||||
|
triIndexVec.emplace_back(triIndex1);
|
||||||
|
triIndexVec.emplace_back(triIndex2);
|
||||||
|
|
||||||
|
// partitions are "containers" for vertices. BSP tree leafs contain a list of these partitions to determine the collision within a leaf.
|
||||||
|
// partitions are made for each triangle, not one for each surface.
|
||||||
|
// one for each surface causes physics bugs, as the entire bounding box is considered solid instead of the surface itself (for some reason).
|
||||||
|
// so a partition is made for each triangle which removes the physics bugs but likely makes the game run slower
|
||||||
CollisionPartition partition;
|
CollisionPartition partition;
|
||||||
partition.triCount = 1;
|
partition.triCount = 1;
|
||||||
partition.firstTri = indexOfFirstTri + triIdx;
|
partition.firstTri = indexOfFirstTri + triIdx;
|
||||||
@@ -608,16 +805,20 @@ namespace BSP
|
|||||||
partition.fuind = static_cast<int>(uniqueIndicesVec.size());
|
partition.fuind = static_cast<int>(uniqueIndicesVec.size());
|
||||||
|
|
||||||
// All tri indices are unique since there is only one tri per partition
|
// All tri indices are unique since there is only one tri per partition
|
||||||
uint16_t* tri = clipMap->triIndices[partition.firstTri];
|
int indexOfTriIndex = partition.firstTri * 3;
|
||||||
uniqueIndicesVec.emplace_back(tri[0]);
|
uniqueIndicesVec.emplace_back(triIndex0);
|
||||||
uniqueIndicesVec.emplace_back(tri[1]);
|
uniqueIndicesVec.emplace_back(triIndex1);
|
||||||
uniqueIndicesVec.emplace_back(tri[2]);
|
uniqueIndicesVec.emplace_back(triIndex2);
|
||||||
|
|
||||||
partitionVec.emplace_back(partition);
|
partitionVec.emplace_back(partition);
|
||||||
|
partitionToColSurfaceMap.emplace_back(collisionSurfaceVec.size() - 1); // -1 as the colSurface has already been added
|
||||||
partitionToMaterialMap.emplace_back(surface.materialIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clipMap->triCount = static_cast<int>(triIndexVec.size() / 3);
|
||||||
|
clipMap->triIndices = reinterpret_cast<uint16_t(*)[3]>(m_memory.Alloc<uint16_t>(triIndexVec.size()));
|
||||||
|
memcpy(clipMap->triIndices, &triIndexVec[0], sizeof(uint16_t) * triIndexVec.size());
|
||||||
|
|
||||||
clipMap->partitionCount = static_cast<int>(partitionVec.size());
|
clipMap->partitionCount = static_cast<int>(partitionVec.size());
|
||||||
clipMap->partitions = m_memory.Alloc<CollisionPartition>(clipMap->partitionCount);
|
clipMap->partitions = m_memory.Alloc<CollisionPartition>(clipMap->partitionCount);
|
||||||
memcpy(clipMap->partitions, partitionVec.data(), sizeof(CollisionPartition) * partitionVec.size());
|
memcpy(clipMap->partitions, partitionVec.data(), sizeof(CollisionPartition) * partitionVec.size());
|
||||||
@@ -628,6 +829,64 @@ namespace BSP
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
//// The clipmap index buffer has a unique index for each vertex in the world, compared to the gfxworld's
|
||||||
|
//// index buffer having a unique index for each vertex on a surface. This code converts gfxworld indices to clipmap indices.
|
||||||
|
// std::vector<uint16_t> triIndexVec;
|
||||||
|
// for (BSPSurface& surface : bsp->colWorld.surfaces)
|
||||||
|
//{
|
||||||
|
// int indexOfFirstIndex = surface.indexOfFirstIndex;
|
||||||
|
// int indexOfFirstVertex = surface.indexOfFirstVertex;
|
||||||
|
// for (int indexIdx = 0; indexIdx < surface.triCount * 3; indexIdx++)
|
||||||
|
// {
|
||||||
|
// int triIndex = bsp->colWorld.indices[indexOfFirstIndex + indexIdx] + indexOfFirstVertex;
|
||||||
|
// triIndexVec.emplace_back(triIndex);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//// the reinterpret_cast is used as triIndices is just a pointer to an array of indicies, and static_cast can't safely do the conversion
|
||||||
|
// clipMap->triCount = static_cast<int>(triIndexVec.size() / 3);
|
||||||
|
// clipMap->triIndices = reinterpret_cast<uint16_t(*)[3]>(m_memory.Alloc<uint16_t>(triIndexVec.size()));
|
||||||
|
// memcpy(clipMap->triIndices, &triIndexVec[0], sizeof(uint16_t) * triIndexVec.size());
|
||||||
|
//
|
||||||
|
//// partitions are "containers" for vertices. BSP tree leafs contain a list of these partitions to determine the collision within a leaf.
|
||||||
|
// std::vector<CollisionPartition> partitionVec;
|
||||||
|
// std::vector<uint16_t> uniqueIndicesVec;
|
||||||
|
// for (size_t surfIdx = 0; surfIdx < bsp->colWorld.surfaces.size(); surfIdx++)
|
||||||
|
//{
|
||||||
|
// BSPSurface& surface = bsp->colWorld.surfaces[surfIdx];
|
||||||
|
//
|
||||||
|
// // partitions are made for each triangle, not one for each surface.
|
||||||
|
// // one for each surface causes physics bugs, as the entire bounding box is considered solid instead of the surface itself (for some reason).
|
||||||
|
// // so a partition is made for each triangle which removes the physics bugs but likely makes the game run slower
|
||||||
|
// int indexOfFirstTri = surface.indexOfFirstIndex / 3;
|
||||||
|
// int indexOfFirstVertex = surface.indexOfFirstVertex;
|
||||||
|
// for (int triIdx = 0; triIdx < surface.triCount; triIdx++)
|
||||||
|
// {
|
||||||
|
// CollisionPartition partition;
|
||||||
|
// partition.triCount = 1;
|
||||||
|
// partition.firstTri = indexOfFirstTri + triIdx;
|
||||||
|
//
|
||||||
|
// partition.nuinds = 3;
|
||||||
|
// partition.fuind = static_cast<int>(uniqueIndicesVec.size());
|
||||||
|
//
|
||||||
|
// // All tri indices are unique since there is only one tri per partition
|
||||||
|
// uint16_t* tri = clipMap->triIndices[partition.firstTri];
|
||||||
|
// uniqueIndicesVec.emplace_back(tri[0]);
|
||||||
|
// uniqueIndicesVec.emplace_back(tri[1]);
|
||||||
|
// uniqueIndicesVec.emplace_back(tri[2]);
|
||||||
|
//
|
||||||
|
// partitionVec.emplace_back(partition);
|
||||||
|
//
|
||||||
|
// partitionToMaterialMap.emplace_back(surface.materialIndex);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// clipMap->partitionCount = static_cast<int>(partitionVec.size());
|
||||||
|
// clipMap->partitions = m_memory.Alloc<CollisionPartition>(clipMap->partitionCount);
|
||||||
|
// memcpy(clipMap->partitions, partitionVec.data(), sizeof(CollisionPartition) * partitionVec.size());
|
||||||
|
//
|
||||||
|
// clipMap->info.nuinds = static_cast<int>(uniqueIndicesVec.size());
|
||||||
|
// clipMap->info.uinds = m_memory.Alloc<uint16_t>(uniqueIndicesVec.size());
|
||||||
|
// memcpy(clipMap->info.uinds, uniqueIndicesVec.data(), sizeof(uint16_t) * uniqueIndicesVec.size());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Proper unique index creation code kept for future use
|
// Proper unique index creation code kept for future use
|
||||||
int totalUindCount = 0;
|
int totalUindCount = 0;
|
||||||
@@ -759,6 +1018,11 @@ namespace BSP
|
|||||||
clipMap->triEdgeIsWalkable = m_memory.Alloc<char>(walkableEdgeSize);
|
clipMap->triEdgeIsWalkable = m_memory.Alloc<char>(walkableEdgeSize);
|
||||||
memset(clipMap->triEdgeIsWalkable, 1, walkableEdgeSize * sizeof(char));
|
memset(clipMap->triEdgeIsWalkable, 1, walkableEdgeSize * sizeof(char));
|
||||||
|
|
||||||
|
// multiple functions add to AABBTreeVec, so it is added to the clipmap last
|
||||||
|
clipMap->aabbTreeCount = static_cast<unsigned int>(AABBTreeVec.size());
|
||||||
|
clipMap->aabbTrees = m_memory.Alloc<CollisionAabbTree>(AABBTreeVec.size());
|
||||||
|
memcpy(clipMap->aabbTrees, AABBTreeVec.data(), sizeof(CollisionAabbTree) * AABBTreeVec.size());
|
||||||
|
|
||||||
return clipMap;
|
return clipMap;
|
||||||
}
|
}
|
||||||
} // namespace BSP
|
} // namespace BSP
|
||||||
|
|||||||
@@ -8,6 +8,13 @@
|
|||||||
|
|
||||||
namespace BSP
|
namespace BSP
|
||||||
{
|
{
|
||||||
|
struct ColSurface
|
||||||
|
{
|
||||||
|
size_t materialIndex;
|
||||||
|
size_t partitionCount;
|
||||||
|
size_t partitionStartIndex;
|
||||||
|
};
|
||||||
|
|
||||||
class ClipMapLinker
|
class ClipMapLinker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -30,9 +37,13 @@ namespace BSP
|
|||||||
std::vector<cNode_t> nodeVec;
|
std::vector<cNode_t> nodeVec;
|
||||||
std::vector<cLeaf_s> leafVec;
|
std::vector<cLeaf_s> leafVec;
|
||||||
std::vector<CollisionAabbTree> AABBTreeVec;
|
std::vector<CollisionAabbTree> AABBTreeVec;
|
||||||
size_t highestLeafObjectCount = 0;
|
size_t highestPartitionCountForAABB = 0;
|
||||||
std::vector<size_t> partitionToMaterialMap;
|
|
||||||
void addAABBTreeFromLeaf(clipMap_t* clipMap, BSPTree* tree, size_t* out_parentCount, size_t* out_parentStartIndex, int* out_treeContents);
|
std::vector<ColSurface> collisionSurfaceVec;
|
||||||
|
std::vector<size_t> partitionToColSurfaceMap;
|
||||||
|
|
||||||
|
void addAABBTreeFromPartitions(
|
||||||
|
clipMap_t* clipMap, std::vector<int>& partitions, size_t* out_parentCount, size_t* out_parentStartIndex, int* out_treeContents);
|
||||||
int16_t loadBSPNode(clipMap_t* clipMap, BSPTree* tree, bool isRoot);
|
int16_t loadBSPNode(clipMap_t* clipMap, BSPTree* tree, bool isRoot);
|
||||||
bool loadBSPTree(clipMap_t* clipMap, BSPData* bsp);
|
bool loadBSPTree(clipMap_t* clipMap, BSPData* bsp);
|
||||||
bool loadPartitions(clipMap_t* clipMap, BSPData* bsp);
|
bool loadPartitions(clipMap_t* clipMap, BSPData* bsp);
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
#include "../BSPUtil.h"
|
#include "../BSPUtil.h"
|
||||||
|
|
||||||
#define _USE_MATH_DEFINES
|
#include <numbers>
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
namespace BSP
|
namespace BSP
|
||||||
{
|
{
|
||||||
@@ -184,9 +183,9 @@ namespace BSP
|
|||||||
light->type = GFX_LIGHT_TYPE_OMNI;
|
light->type = GFX_LIGHT_TYPE_OMNI;
|
||||||
light->defName = "white_light_cube";
|
light->defName = "white_light_cube";
|
||||||
light->roundness = 0.0f;
|
light->roundness = 0.0f;
|
||||||
light->cosHalfFovInner = cosf(30.0f * (M_PI / 180.0f));
|
light->cosHalfFovInner = cosf(30.0f * (std::numbers::pi_v<float> / 180.0f));
|
||||||
light->cosHalfFovOuter = cosf(55.0f * (M_PI / 180.0f));
|
light->cosHalfFovOuter = cosf(55.0f * (std::numbers::pi_v<float> / 180.0f));
|
||||||
light->cosHalfFovExpanded = cosf(55.0f * (M_PI / 180.0f));
|
light->cosHalfFovExpanded = cosf(55.0f * (std::numbers::pi_v<float> / 180.0f));
|
||||||
}
|
}
|
||||||
setLightCommonValues(light, bspLight);
|
setLightCommonValues(light, bspLight);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -541,11 +541,10 @@ namespace BSP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GfxWorldLinker::loadModels(GfxWorld* gfxWorld)
|
void GfxWorldLinker::loadModels(BSPData* bsp, GfxWorld* gfxWorld)
|
||||||
{
|
{
|
||||||
// Models (Submodels in the clipmap code) are used for the world and map ent collision (triggers, bomb zones, etc)
|
// Models (Submodels in the clipmap code) are used for the world and map ent collision (triggers, bomb zones, etc)
|
||||||
// Right now there is only one submodel, the world sub model
|
gfxWorld->modelCount = static_cast<int>(bsp->models.size() + 1);
|
||||||
gfxWorld->modelCount = 1;
|
|
||||||
gfxWorld->models = m_memory.Alloc<GfxBrushModel>(gfxWorld->modelCount);
|
gfxWorld->models = m_memory.Alloc<GfxBrushModel>(gfxWorld->modelCount);
|
||||||
|
|
||||||
// first model is always the world model
|
// first model is always the world model
|
||||||
@@ -559,23 +558,40 @@ namespace BSP
|
|||||||
gfxWorld->models[0].bounds[1].z = gfxWorld->maxs.z;
|
gfxWorld->models[0].bounds[1].z = gfxWorld->maxs.z;
|
||||||
memset(&gfxWorld->models[0].writable, 0, sizeof(GfxBrushModelWritable));
|
memset(&gfxWorld->models[0].writable, 0, sizeof(GfxBrushModelWritable));
|
||||||
|
|
||||||
// Other models aren't implemented yet
|
for (size_t modelIdx = 0; modelIdx < bsp->models.size(); modelIdx++)
|
||||||
// Code kept for future use
|
{
|
||||||
// for (size_t i = 0; i < entityModelList.size(); i++)
|
auto currEntModel = &gfxWorld->models[modelIdx + 1];
|
||||||
//{
|
auto& bspMpdel = bsp->models.at(modelIdx);
|
||||||
// auto currEntModel = &gfxWorld->models[i + 1];
|
|
||||||
// entModelBounds currEntModelBounds = entityModelList[i];
|
if (bspMpdel.isGfxModel)
|
||||||
//
|
{
|
||||||
// currEntModel->startSurfIndex = 0;
|
currEntModel->startSurfIndex = bspMpdel.surfaceIndex;
|
||||||
// currEntModel->surfaceCount = -1; // -1 when it doesn't use map surfaces
|
currEntModel->surfaceCount = bspMpdel.surfaceCount;
|
||||||
// currEntModel->bounds[0].x = currEntModelBounds.mins.x;
|
for (size_t surfIdx = 0; surfIdx < bspMpdel.surfaceCount; surfIdx++)
|
||||||
// currEntModel->bounds[0].y = currEntModelBounds.mins.y;
|
{
|
||||||
// currEntModel->bounds[0].z = currEntModelBounds.mins.z;
|
GfxSurface* surf = &gfxWorld->dpvs.surfaces[bspMpdel.surfaceIndex + surfIdx];
|
||||||
// currEntModel->bounds[1].x = currEntModelBounds.maxs.x;
|
if (surfIdx == 0)
|
||||||
// currEntModel->bounds[1].y = currEntModelBounds.maxs.y;
|
{
|
||||||
// currEntModel->bounds[1].z = currEntModelBounds.maxs.z;
|
currEntModel->bounds[0] = surf->bounds[0];
|
||||||
// memset(&gfxWorld->models[0].writable, 0, sizeof(GfxBrushModelWritable));
|
currEntModel->bounds[1] = surf->bounds[1];
|
||||||
//}
|
}
|
||||||
|
else
|
||||||
|
BSPUtil::updateAABB(surf->bounds[0], surf->bounds[1], currEntModel->bounds[0], currEntModel->bounds[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currEntModel->startSurfIndex = 0;
|
||||||
|
currEntModel->surfaceCount = -1; // -1 when it doesn't use map surfaces
|
||||||
|
currEntModel->bounds[0].x = 0.0f;
|
||||||
|
currEntModel->bounds[0].y = 0.0f;
|
||||||
|
currEntModel->bounds[0].z = 0.0f;
|
||||||
|
currEntModel->bounds[1].x = 0.0f;
|
||||||
|
currEntModel->bounds[1].y = 0.0f;
|
||||||
|
currEntModel->bounds[1].z = 0.0f;
|
||||||
|
}
|
||||||
|
memset(&gfxWorld->models[0].writable, 0, sizeof(GfxBrushModelWritable));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GfxWorldLinker::loadSunData(GfxWorld* gfxWorld)
|
void GfxWorldLinker::loadSunData(GfxWorld* gfxWorld)
|
||||||
@@ -687,7 +703,8 @@ namespace BSP
|
|||||||
|
|
||||||
void GfxWorldLinker::loadSkyBox(BSPData* projInfo, GfxWorld* gfxWorld)
|
void GfxWorldLinker::loadSkyBox(BSPData* projInfo, GfxWorld* gfxWorld)
|
||||||
{
|
{
|
||||||
std::string skyBoxName = "skybox_" + projInfo->name;
|
// std::string skyBoxName = "skybox_" + projInfo->name;
|
||||||
|
std::string skyBoxName = "skybox_zm_transit";
|
||||||
gfxWorld->skyBoxModel = m_memory.Dup(skyBoxName.c_str());
|
gfxWorld->skyBoxModel = m_memory.Dup(skyBoxName.c_str());
|
||||||
|
|
||||||
if (m_context.LoadDependency<AssetXModel>(skyBoxName) == nullptr)
|
if (m_context.LoadDependency<AssetXModel>(skyBoxName) == nullptr)
|
||||||
@@ -806,7 +823,7 @@ namespace BSP
|
|||||||
|
|
||||||
loadGfxLights(bsp, gfxWorld); // requires xmodels and surfaces
|
loadGfxLights(bsp, gfxWorld); // requires xmodels and surfaces
|
||||||
|
|
||||||
loadModels(gfxWorld);
|
loadModels(bsp, gfxWorld); // requires surfaces
|
||||||
|
|
||||||
loadSunData(gfxWorld);
|
loadSunData(gfxWorld);
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace BSP
|
|||||||
void loadGfxLights(BSPData* bsp, GfxWorld* gfxWorld);
|
void loadGfxLights(BSPData* bsp, GfxWorld* gfxWorld);
|
||||||
void loadLightGrid(GfxWorld* gfxWorld);
|
void loadLightGrid(GfxWorld* gfxWorld);
|
||||||
void loadGfxCells(GfxWorld* gfxWorld);
|
void loadGfxCells(GfxWorld* gfxWorld);
|
||||||
void loadModels(GfxWorld* gfxWorld);
|
void loadModels(BSPData* bsp, GfxWorld* gfxWorld);
|
||||||
bool loadReflectionProbeData(GfxWorld* gfxWorld);
|
bool loadReflectionProbeData(GfxWorld* gfxWorld);
|
||||||
bool loadLightmapData(GfxWorld* gfxWorld);
|
bool loadLightmapData(GfxWorld* gfxWorld);
|
||||||
void loadSkyBox(BSPData* projInfo, GfxWorld* gfxWorld);
|
void loadSkyBox(BSPData* projInfo, GfxWorld* gfxWorld);
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ namespace
|
|||||||
|
|
||||||
void addSpawnsToEntString(BSP::BSPData* bsp, std::string& entityString)
|
void addSpawnsToEntString(BSP::BSPData* bsp, std::string& entityString)
|
||||||
{
|
{
|
||||||
if (bsp->spawnpoints.size() == 0)
|
if (!bsp->isZombiesMap && bsp->spawnpoints.size() == 0)
|
||||||
{
|
{
|
||||||
con::info("No spawnpoints found, setting all spawns to (0, 0, 0)");
|
con::info("No spawnpoints found, setting all spawns to (0, 0, 0)");
|
||||||
BSP::BSPSpawnPoint defaultSpawnPoint;
|
BSP::BSPSpawnPoint defaultSpawnPoint;
|
||||||
@@ -127,6 +127,52 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addZombiesEntitiesToEntString(BSP::BSPData* bsp, std::string& entityString)
|
||||||
|
{
|
||||||
|
for (auto& zone : bsp->zZones)
|
||||||
|
{
|
||||||
|
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("\"origin\" \"{}\"\n", BSP::BSPUtil::convertVec3ToString(zone.origin)));
|
||||||
|
entityString.append(std::format("\"model\" \"*{}\"\n", zone.modelIndex));
|
||||||
|
entityString.append("}\n");
|
||||||
|
|
||||||
|
entityString.append("{\n");
|
||||||
|
entityString.append("\"classname\" \"script_struct\"\n");
|
||||||
|
entityString.append(std::format("\"targetname\" \"player_respawn_point\"\n"));
|
||||||
|
entityString.append(std::format("\"script_noteworthy\" \"{}\"\n", zone.zoneName));
|
||||||
|
entityString.append(std::format("\"target\" \"{}\"\n", zone.spawnpointGroupName));
|
||||||
|
entityString.append("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& zSpawner : bsp->zSpawners)
|
||||||
|
{
|
||||||
|
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("\"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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constexpr const char* DEFAULT_MAP_ENTS_STRING = R"({
|
constexpr const char* DEFAULT_MAP_ENTS_STRING = R"({
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
@@ -180,6 +226,9 @@ namespace BSP
|
|||||||
|
|
||||||
addPathNodesToEntString(bsp, entityString);
|
addPathNodesToEntString(bsp, entityString);
|
||||||
|
|
||||||
|
if (bsp->isZombiesMap)
|
||||||
|
addZombiesEntitiesToEntString(bsp, entityString);
|
||||||
|
|
||||||
MapEnts* mapEnts = m_memory.Alloc<MapEnts>();
|
MapEnts* mapEnts = m_memory.Alloc<MapEnts>();
|
||||||
mapEnts->name = m_memory.Dup(bsp->bspName.c_str());
|
mapEnts->name = m_memory.Dup(bsp->bspName.c_str());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user