mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-11-22 21:02:07 +00:00
WIP: Converted custom map linker into different asset files
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
#include "Utils/Logging/Log.h"
|
#include "Utils/Logging/Log.h"
|
||||||
|
|
||||||
|
|||||||
80
src/ObjLoading/Game/T6/BSP/Linker/BSPLinker.cpp
Normal file
80
src/ObjLoading/Game/T6/BSP/Linker/BSPLinker.cpp
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#include "BSPLinker.h"
|
||||||
|
|
||||||
|
#include "ComWorldLinker.h"
|
||||||
|
#include "ClipMapLinker.h"
|
||||||
|
#include "GameWorldMpLinker.h"
|
||||||
|
#include "GfxWorldLinker.h"
|
||||||
|
#include "MapEntsLinker.h"
|
||||||
|
#include "SkinnedVertsLinker.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
FootstepTableDef* BSPLinker::addEmptyFootstepTableAsset(std::string assetName)
|
||||||
|
{
|
||||||
|
if (assetName.length() == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
FootstepTableDef* footstepTable = m_memory.Alloc<FootstepTableDef>();
|
||||||
|
footstepTable->name = m_memory.Dup(assetName.c_str());
|
||||||
|
memset(footstepTable->sndAliasTable, 0, sizeof(footstepTable->sndAliasTable));
|
||||||
|
|
||||||
|
m_context.AddAsset<AssetFootstepTable>(assetName, footstepTable);
|
||||||
|
|
||||||
|
return footstepTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BSPLinker::addDefaultRequiredAssets(BSPData* bsp)
|
||||||
|
{
|
||||||
|
if (m_context.LoadDependency<AssetScript>("maps/mp/" + bsp->name + ".gsc") == nullptr)
|
||||||
|
return false;
|
||||||
|
if (m_context.LoadDependency<AssetScript>("maps/mp/" + bsp->name + "_amb.gsc") == nullptr)
|
||||||
|
return false;
|
||||||
|
if (m_context.LoadDependency<AssetScript>("maps/mp/" + bsp->name + "_fx.gsc") == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_context.LoadDependency<AssetScript>("clientscripts/mp/" + bsp->name + ".csc") == nullptr)
|
||||||
|
return false;
|
||||||
|
if (m_context.LoadDependency<AssetScript>("clientscripts/mp/" + bsp->name + "_amb.csc") == nullptr)
|
||||||
|
return false;
|
||||||
|
if (m_context.LoadDependency<AssetScript>("clientscripts/mp/" + bsp->name + "_fx.csc") == nullptr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
addEmptyFootstepTableAsset("default_1st_person");
|
||||||
|
addEmptyFootstepTableAsset("default_3rd_person");
|
||||||
|
addEmptyFootstepTableAsset("default_1st_person_quiet");
|
||||||
|
addEmptyFootstepTableAsset("default_3rd_person_quiet");
|
||||||
|
addEmptyFootstepTableAsset("default_3rd_person_loud");
|
||||||
|
addEmptyFootstepTableAsset("default_ai");
|
||||||
|
|
||||||
|
if (m_context.LoadDependency<AssetRawFile>("animtrees/fxanim_props.atr") == nullptr)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BSPLinker::BSPLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context)
|
||||||
|
: m_memory(memory),
|
||||||
|
m_search_path(searchPath),
|
||||||
|
m_context(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetCreationResult BSPLinker::linkBSP(BSPData* bsp)
|
||||||
|
{
|
||||||
|
if (!addDefaultRequiredAssets(bsp))
|
||||||
|
return AssetCreationResult::Failure();
|
||||||
|
|
||||||
|
ComWorldLinker comWorldLinker(m_memory, m_search_path, m_context);
|
||||||
|
ClipMapLinker clipMapLinker(m_memory, m_search_path, m_context);
|
||||||
|
GameWorldMpLinker gameWorldMpLinker(m_memory, m_search_path, m_context);
|
||||||
|
GfxWorldLinker gfxWorldLinker(m_memory, m_search_path, m_context);
|
||||||
|
MapEntsLinker mapEntsLinker(m_memory, m_search_path, m_context);
|
||||||
|
SkinnedVertsLinker skinnedVertsLinker(m_memory, m_search_path, m_context);
|
||||||
|
|
||||||
|
comWorldLinker.linkComWorld(bsp);
|
||||||
|
mapEntsLinker.linkMapEnts(bsp);
|
||||||
|
gameWorldMpLinker.linkGameWorldMp(bsp);
|
||||||
|
skinnedVertsLinker.linkSkinnedVerts(bsp);
|
||||||
|
gfxWorldLinker.linkGfxWorld(bsp); // requires mapents asset
|
||||||
|
clipMapLinker.linkClipMap(bsp); // requires gfxworld and mapents asset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
24
src/ObjLoading/Game/T6/BSP/Linker/BSPLinker.h
Normal file
24
src/ObjLoading/Game/T6/BSP/Linker/BSPLinker.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../BSP.h"
|
||||||
|
#include "Asset/IAssetCreator.h"
|
||||||
|
#include "SearchPath/ISearchPath.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
class BSPLinker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BSPLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context);
|
||||||
|
AssetCreationResult linkBSP(BSPData* bsp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FootstepTableDef* addEmptyFootstepTableAsset(std::string assetName);
|
||||||
|
bool addDefaultRequiredAssets(BSPData* bsp);
|
||||||
|
|
||||||
|
MemoryManager& m_memory;
|
||||||
|
ISearchPath& m_search_path;
|
||||||
|
AssetCreationContext& m_context;
|
||||||
|
};
|
||||||
|
}
|
||||||
763
src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.cpp
Normal file
763
src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.cpp
Normal file
@@ -0,0 +1,763 @@
|
|||||||
|
#include "ClipMapLinker.h"
|
||||||
|
#include "../BSPUtil.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
ClipMapLinker::ClipMapLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context)
|
||||||
|
: m_memory(memory),
|
||||||
|
m_search_path(searchPath),
|
||||||
|
m_context(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClipMapLinker::loadDynEnts(clipMap_t* clipMap)
|
||||||
|
{
|
||||||
|
int dynEntCount = 0;
|
||||||
|
clipMap->originalDynEntCount = dynEntCount;
|
||||||
|
clipMap->dynEntCount[0] = clipMap->originalDynEntCount + 256; // the game allocs 256 empty dynents, as they may be used ingame
|
||||||
|
clipMap->dynEntCount[1] = 0;
|
||||||
|
clipMap->dynEntCount[2] = 0;
|
||||||
|
clipMap->dynEntCount[3] = 0;
|
||||||
|
|
||||||
|
clipMap->dynEntClientList[0] = m_memory.Alloc<DynEntityClient>(clipMap->dynEntCount[0]);
|
||||||
|
clipMap->dynEntClientList[1] = nullptr;
|
||||||
|
memset(clipMap->dynEntClientList[0], 0, sizeof(DynEntityClient) * clipMap->dynEntCount[0]);
|
||||||
|
|
||||||
|
clipMap->dynEntServerList[0] = nullptr;
|
||||||
|
clipMap->dynEntServerList[1] = nullptr;
|
||||||
|
|
||||||
|
clipMap->dynEntCollList[0] = m_memory.Alloc<DynEntityColl>(clipMap->dynEntCount[0]);
|
||||||
|
clipMap->dynEntCollList[1] = nullptr;
|
||||||
|
clipMap->dynEntCollList[2] = nullptr;
|
||||||
|
clipMap->dynEntCollList[3] = nullptr;
|
||||||
|
memset(clipMap->dynEntCollList[0], 0, sizeof(DynEntityColl) * clipMap->dynEntCount[0]);
|
||||||
|
|
||||||
|
clipMap->dynEntPoseList[0] = m_memory.Alloc<DynEntityPose>(clipMap->dynEntCount[0]);
|
||||||
|
clipMap->dynEntPoseList[1] = nullptr;
|
||||||
|
memset(clipMap->dynEntPoseList[0], 0, sizeof(DynEntityPose) * clipMap->dynEntCount[0]);
|
||||||
|
|
||||||
|
clipMap->dynEntDefList[0] = m_memory.Alloc<DynEntityDef>(clipMap->dynEntCount[0]);
|
||||||
|
clipMap->dynEntDefList[1] = nullptr;
|
||||||
|
memset(clipMap->dynEntDefList[0], 0, sizeof(DynEntityDef) * clipMap->dynEntCount[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClipMapLinker::loadVisibility(clipMap_t* clipMap)
|
||||||
|
{
|
||||||
|
// Only use one visbility cluster for the entire map
|
||||||
|
clipMap->numClusters = 1;
|
||||||
|
clipMap->vised = 0;
|
||||||
|
clipMap->clusterBytes = ((clipMap->numClusters + 63) >> 3) & 0xFFFFFFF8;
|
||||||
|
clipMap->visibility = m_memory.Alloc<char>(clipMap->clusterBytes);
|
||||||
|
// Official maps set visibility to all 0xFF
|
||||||
|
memset(clipMap->visibility, 0xFF, clipMap->clusterBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClipMapLinker::loadBoxData(clipMap_t* clipMap)
|
||||||
|
{
|
||||||
|
// box_model and box_brush are what are used by game traces as "temporary" collision when
|
||||||
|
// no brush or model is specified to do the trace with.
|
||||||
|
// All values in this function are taken from official map BSPs
|
||||||
|
|
||||||
|
// for some reason the maxs are negative, and mins are positive
|
||||||
|
// float box_mins = 3.4028235e38;
|
||||||
|
// float box_maxs = -3.4028235e38;
|
||||||
|
// hack: the floats above can't be safely converted to 32 bit floats, and the game requires them to be exact
|
||||||
|
// so we use the hex representation and set it using int pointers.
|
||||||
|
unsigned int box_mins = 0x7F7FFFFF;
|
||||||
|
unsigned int box_maxs = 0xFF7FFFFF;
|
||||||
|
*(reinterpret_cast<unsigned int*>(&clipMap->box_model.leaf.mins.x)) = box_mins;
|
||||||
|
*(reinterpret_cast<unsigned int*>(&clipMap->box_model.leaf.mins.y)) = box_mins;
|
||||||
|
*(reinterpret_cast<unsigned int*>(&clipMap->box_model.leaf.mins.z)) = box_mins;
|
||||||
|
*(reinterpret_cast<unsigned int*>(&clipMap->box_model.leaf.maxs.x)) = box_maxs;
|
||||||
|
*(reinterpret_cast<unsigned int*>(&clipMap->box_model.leaf.maxs.y)) = box_maxs;
|
||||||
|
*(reinterpret_cast<unsigned int*>(&clipMap->box_model.leaf.maxs.z)) = box_maxs;
|
||||||
|
|
||||||
|
clipMap->box_model.leaf.brushContents = -1;
|
||||||
|
clipMap->box_model.leaf.terrainContents = 0;
|
||||||
|
clipMap->box_model.leaf.cluster = 0;
|
||||||
|
clipMap->box_model.leaf.collAabbCount = 0;
|
||||||
|
clipMap->box_model.leaf.firstCollAabbIndex = 0;
|
||||||
|
clipMap->box_model.leaf.leafBrushNode = 0;
|
||||||
|
clipMap->box_model.mins.x = 0.0f;
|
||||||
|
clipMap->box_model.mins.y = 0.0f;
|
||||||
|
clipMap->box_model.mins.z = 0.0f;
|
||||||
|
clipMap->box_model.maxs.x = 0.0f;
|
||||||
|
clipMap->box_model.maxs.y = 0.0f;
|
||||||
|
clipMap->box_model.maxs.z = 0.0f;
|
||||||
|
clipMap->box_model.radius = 0.0f;
|
||||||
|
clipMap->box_model.info = nullptr;
|
||||||
|
|
||||||
|
clipMap->box_brush = m_memory.Alloc<cbrush_t>();
|
||||||
|
clipMap->box_brush->axial_sflags[0][0] = -1;
|
||||||
|
clipMap->box_brush->axial_sflags[0][1] = -1;
|
||||||
|
clipMap->box_brush->axial_sflags[0][2] = -1;
|
||||||
|
clipMap->box_brush->axial_sflags[1][0] = -1;
|
||||||
|
clipMap->box_brush->axial_sflags[1][1] = -1;
|
||||||
|
clipMap->box_brush->axial_sflags[1][2] = -1;
|
||||||
|
clipMap->box_brush->axial_cflags[0][0] = -1;
|
||||||
|
clipMap->box_brush->axial_cflags[0][1] = -1;
|
||||||
|
clipMap->box_brush->axial_cflags[0][2] = -1;
|
||||||
|
clipMap->box_brush->axial_cflags[1][0] = -1;
|
||||||
|
clipMap->box_brush->axial_cflags[1][1] = -1;
|
||||||
|
clipMap->box_brush->axial_cflags[1][2] = -1;
|
||||||
|
clipMap->box_brush->contents = -1;
|
||||||
|
clipMap->box_brush->mins.x = 0.0f;
|
||||||
|
clipMap->box_brush->mins.y = 0.0f;
|
||||||
|
clipMap->box_brush->mins.z = 0.0f;
|
||||||
|
clipMap->box_brush->maxs.x = 0.0f;
|
||||||
|
clipMap->box_brush->maxs.y = 0.0f;
|
||||||
|
clipMap->box_brush->maxs.z = 0.0f;
|
||||||
|
clipMap->box_brush->numsides = 0;
|
||||||
|
clipMap->box_brush->numverts = 0;
|
||||||
|
clipMap->box_brush->sides = nullptr;
|
||||||
|
clipMap->box_brush->verts = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClipMapLinker::loadRopesAndConstraints(clipMap_t* clipMap)
|
||||||
|
{
|
||||||
|
clipMap->num_constraints = 0; // max 511
|
||||||
|
clipMap->constraints = NULL;
|
||||||
|
|
||||||
|
// The game allocates 32 empty ropes
|
||||||
|
clipMap->max_ropes = 32; // max 300
|
||||||
|
clipMap->ropes = m_memory.Alloc <rope_t> (clipMap->max_ropes);
|
||||||
|
memset(clipMap->ropes, 0, sizeof(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)
|
||||||
|
auto gfxWorldAsset = m_context.LoadDependency<AssetGfxWorld>(bsp->bspName);
|
||||||
|
assert(gfxWorldAsset != NULL);
|
||||||
|
GfxWorld* gfxWorld = gfxWorldAsset->Asset();
|
||||||
|
|
||||||
|
// Right now there is only one submodel, the world sub model
|
||||||
|
assert(gfxWorld->modelCount == 1);
|
||||||
|
|
||||||
|
clipMap->numSubModels = 1;
|
||||||
|
clipMap->cmodels = m_memory.Alloc<cmodel_t>(clipMap->numSubModels);
|
||||||
|
|
||||||
|
GfxBrushModel* gfxModel = &gfxWorld->models[0];
|
||||||
|
clipMap->cmodels[0].mins.x = gfxModel->bounds[0].x;
|
||||||
|
clipMap->cmodels[0].mins.y = gfxModel->bounds[0].y;
|
||||||
|
clipMap->cmodels[0].mins.z = gfxModel->bounds[0].z;
|
||||||
|
clipMap->cmodels[0].maxs.x = gfxModel->bounds[1].x;
|
||||||
|
clipMap->cmodels[0].maxs.y = gfxModel->bounds[1].y;
|
||||||
|
clipMap->cmodels[0].maxs.z = gfxModel->bounds[1].z;
|
||||||
|
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 = NULL; // always set to 0
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClipMapLinker::loadXModelCollision(clipMap_t* clipMap)
|
||||||
|
{
|
||||||
|
// Right now XModels aren't supported
|
||||||
|
clipMap->numStaticModels = 0;
|
||||||
|
clipMap->staticModelList = nullptr;
|
||||||
|
|
||||||
|
// WIP code left in for future support
|
||||||
|
/*
|
||||||
|
auto gfxWorldAsset = m_context.LoadDependency<AssetGfxWorld>(bsp->bspName);
|
||||||
|
assert(gfxWorldAsset != NULL);
|
||||||
|
GfxWorld* gfxWorld = gfxWorldAsset->Asset();
|
||||||
|
|
||||||
|
clipMap->numStaticModels = gfxWorld->dpvs.smodelCount;
|
||||||
|
clipMap->staticModelList = new cStaticModel_s[clipMap->numStaticModels];
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < clipMap->numStaticModels; i++)
|
||||||
|
{
|
||||||
|
GfxStaticModelDrawInst* gfxModelDrawInst = &gfxWorld->dpvs.smodelDrawInsts[i];
|
||||||
|
GfxStaticModelInst* gfxModelInst = &gfxWorld->dpvs.smodelInsts[i];
|
||||||
|
cStaticModel_s* currModel = &clipMap->staticModelList[i];
|
||||||
|
|
||||||
|
memset(&currModel->writable, 0, sizeof(cStaticModelWritable));
|
||||||
|
currModel->xmodel = gfxModelDrawInst->model;
|
||||||
|
currModel->contents = gfxModelDrawInst->model->contents;
|
||||||
|
currModel->origin.x = gfxModelDrawInst->placement.origin.x;
|
||||||
|
currModel->origin.y = gfxModelDrawInst->placement.origin.y;
|
||||||
|
currModel->origin.z = gfxModelDrawInst->placement.origin.z;
|
||||||
|
|
||||||
|
// TODO: this does not account for model rotation or scale
|
||||||
|
currModel->absmin.x = gfxModelInst->mins.x;
|
||||||
|
currModel->absmin.y = gfxModelInst->mins.y;
|
||||||
|
currModel->absmin.z = gfxModelInst->mins.z;
|
||||||
|
currModel->absmax.x = gfxModelInst->maxs.x;
|
||||||
|
currModel->absmax.y = gfxModelInst->maxs.y;
|
||||||
|
currModel->absmax.z = gfxModelInst->maxs.z;
|
||||||
|
|
||||||
|
BSPUtil::matrixTranspose3x3(gfxModelDrawInst->placement.axis, currModel->invScaledAxis);
|
||||||
|
currModel->invScaledAxis[0].x = (1.0f / gfxModelDrawInst->placement.scale) * currModel->invScaledAxis[0].x;
|
||||||
|
currModel->invScaledAxis[0].y = (1.0f / gfxModelDrawInst->placement.scale) * currModel->invScaledAxis[0].y;
|
||||||
|
currModel->invScaledAxis[0].z = (1.0f / gfxModelDrawInst->placement.scale) * currModel->invScaledAxis[0].z;
|
||||||
|
currModel->invScaledAxis[1].x = (1.0f / gfxModelDrawInst->placement.scale) * currModel->invScaledAxis[1].x;
|
||||||
|
currModel->invScaledAxis[1].y = (1.0f / gfxModelDrawInst->placement.scale) * currModel->invScaledAxis[1].y;
|
||||||
|
currModel->invScaledAxis[1].z = (1.0f / gfxModelDrawInst->placement.scale) * currModel->invScaledAxis[1].z;
|
||||||
|
currModel->invScaledAxis[2].x = (1.0f / gfxModelDrawInst->placement.scale) * currModel->invScaledAxis[2].x;
|
||||||
|
currModel->invScaledAxis[2].y = (1.0f / gfxModelDrawInst->placement.scale) * currModel->invScaledAxis[2].y;
|
||||||
|
currModel->invScaledAxis[2].z = (1.0f / gfxModelDrawInst->placement.scale) * currModel->invScaledAxis[2].z;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void aabbCalcOriginAndHalfSize(vec3_t* mins, vec3_t* maxs, vec3_t* out_origin, vec3_t* out_halfSize)
|
||||||
|
{
|
||||||
|
// Origin is the midpoint: (min + max) / 2
|
||||||
|
vec3_t temp;
|
||||||
|
temp.x = mins->x + maxs->x;
|
||||||
|
temp.y = mins->y + maxs->y;
|
||||||
|
temp.z = mins->z + maxs->z;
|
||||||
|
out_origin->x = temp.x * 0.5f;
|
||||||
|
out_origin->y = temp.y * 0.5f;
|
||||||
|
out_origin->z = temp.z * 0.5f;
|
||||||
|
|
||||||
|
// Half-size is half the difference: (max - min) / 2
|
||||||
|
temp.x = maxs->x - mins->x;
|
||||||
|
temp.y = maxs->y - mins->y;
|
||||||
|
temp.z = maxs->z - mins->z;
|
||||||
|
out_halfSize->x = temp.x * 0.5f;
|
||||||
|
out_halfSize->y = temp.y * 0.5f;
|
||||||
|
out_halfSize->z = temp.z * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void traverseBSPTreeForCounts(BSPTree* node, size_t* numPlanes, size_t* numNodes, size_t* numLeafs, size_t* numAABBTrees, size_t* maxObjsPerLeaf)
|
||||||
|
{
|
||||||
|
if (node->isLeaf)
|
||||||
|
{
|
||||||
|
(*numLeafs)++;
|
||||||
|
// there won't be an AABB tree when objectList is empty
|
||||||
|
if (node->leaf->getObjectCount() > 0)
|
||||||
|
{
|
||||||
|
*numAABBTrees += node->leaf->getObjectCount() + 1;
|
||||||
|
|
||||||
|
if (node->leaf->getObjectCount() > *maxObjsPerLeaf)
|
||||||
|
*maxObjsPerLeaf = node->leaf->getObjectCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(*numPlanes)++;
|
||||||
|
(*numNodes)++;
|
||||||
|
traverseBSPTreeForCounts(node->node->front.get(), numPlanes, numNodes, numLeafs, numAABBTrees, maxObjsPerLeaf);
|
||||||
|
traverseBSPTreeForCounts(node->node->back.get(), numPlanes, numNodes, numLeafs, numAABBTrees, maxObjsPerLeaf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3_t normalX = { 1.0f, 0.0f, 0.0f };
|
||||||
|
vec3_t normalY = { 0.0f, 1.0f, 0.0f };
|
||||||
|
vec3_t normalZ = { 0.0f, 0.0f, 1.0f };
|
||||||
|
|
||||||
|
int currPlaneCount = 0;
|
||||||
|
int currNodeCount = 0;
|
||||||
|
int currLeafCount = 0;
|
||||||
|
int currAABBCount = 0;
|
||||||
|
|
||||||
|
int addAABBTreeFromLeaf(BSPTree* node, clipMap_t* clipMap)
|
||||||
|
{
|
||||||
|
assert(node->isLeaf);
|
||||||
|
|
||||||
|
int objectCount = node->leaf->getObjectCount();
|
||||||
|
int firstAABBIndex = currAABBCount;
|
||||||
|
currAABBCount += objectCount + 1;
|
||||||
|
|
||||||
|
// calculate root AABB node mins and maxs
|
||||||
|
// cannot convert mins and maxs coord to BO2 directly as this will result in incorrect mins and maxs
|
||||||
|
// so we have to recompute every min and max, not hard just tedious
|
||||||
|
int firstPartitionIndex = node->leaf->getObject(0)->partitionIndex;
|
||||||
|
auto firstPartition = &clipMap->partitions[firstPartitionIndex];
|
||||||
|
uint16_t* firstTri = clipMap->triIndices[firstPartition->firstTri];
|
||||||
|
vec3_t* firstVert = &clipMap->verts[firstTri[0]];
|
||||||
|
vec3_t aabbMins;
|
||||||
|
vec3_t aabbMaxs;
|
||||||
|
aabbMins.x = firstVert->x;
|
||||||
|
aabbMins.y = firstVert->y;
|
||||||
|
aabbMins.z = firstVert->z;
|
||||||
|
aabbMaxs.x = firstVert->x;
|
||||||
|
aabbMaxs.y = firstVert->y;
|
||||||
|
aabbMaxs.z = firstVert->z;
|
||||||
|
for (int i = 0; i < objectCount; i++)
|
||||||
|
{
|
||||||
|
int currPartitionIndex = node->leaf->getObject(i)->partitionIndex;
|
||||||
|
auto currPartition = &clipMap->partitions[currPartitionIndex];
|
||||||
|
|
||||||
|
for (int k = 0; k < currPartition->triCount; k++)
|
||||||
|
{
|
||||||
|
uint16_t* tri = clipMap->triIndices[currPartition->firstTri + k];
|
||||||
|
for (int l = 0; l < 3; l++)
|
||||||
|
{
|
||||||
|
uint16_t vertIndex = tri[l];
|
||||||
|
vec3_t vertCoord = clipMap->verts[vertIndex];
|
||||||
|
BSPUtil::calcNewBoundsWithPoint(&vertCoord, &aabbMins, &aabbMaxs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CollisionAabbTree* rootAABB = &clipMap->aabbTrees[firstAABBIndex];
|
||||||
|
aabbCalcOriginAndHalfSize(&aabbMins, &aabbMaxs, &rootAABB->origin, &rootAABB->halfSize);
|
||||||
|
rootAABB->materialIndex = 0;
|
||||||
|
rootAABB->childCount = objectCount;
|
||||||
|
rootAABB->u.firstChildIndex = firstAABBIndex + 1;
|
||||||
|
|
||||||
|
// populate child AABB nodes
|
||||||
|
for (int i = 0; i < objectCount; i++)
|
||||||
|
{
|
||||||
|
CollisionAabbTree* currAabbTree = &clipMap->aabbTrees[rootAABB->u.firstChildIndex + i];
|
||||||
|
int currPartitionIndex = node->leaf->getObject(i)->partitionIndex;
|
||||||
|
|
||||||
|
currAabbTree->materialIndex = 0;
|
||||||
|
currAabbTree->childCount = 0;
|
||||||
|
currAabbTree->u.partitionIndex = currPartitionIndex;
|
||||||
|
|
||||||
|
// calculate partition origin and half size
|
||||||
|
CollisionPartition* aabbPartition = &clipMap->partitions[currPartitionIndex];
|
||||||
|
uint16_t firstUind = clipMap->info.uinds[aabbPartition->fuind];
|
||||||
|
vec3_t* firstVertex = &clipMap->verts[firstUind];
|
||||||
|
vec3_t mins;
|
||||||
|
vec3_t maxs;
|
||||||
|
mins.x = firstVertex->x;
|
||||||
|
mins.y = firstVertex->y;
|
||||||
|
mins.z = firstVertex->z;
|
||||||
|
maxs.x = firstVertex->x;
|
||||||
|
maxs.y = firstVertex->y;
|
||||||
|
maxs.z = firstVertex->z;
|
||||||
|
for (int i = 1; i < aabbPartition->nuinds; i++)
|
||||||
|
{
|
||||||
|
uint16_t currUind = clipMap->info.uinds[aabbPartition->fuind + i];
|
||||||
|
vec3_t* currVertex = &clipMap->verts[currUind];
|
||||||
|
|
||||||
|
BSPUtil::calcNewBoundsWithPoint(currVertex, &mins, &maxs);
|
||||||
|
}
|
||||||
|
|
||||||
|
aabbCalcOriginAndHalfSize(&mins, &maxs, &currAabbTree->origin, &currAabbTree->halfSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return firstAABBIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the index corresponding to the BSPTree* node parsed
|
||||||
|
int16_t populateBSPTree_r(clipMap_t* clipMap, BSPTree* node)
|
||||||
|
{
|
||||||
|
if (node->isLeaf)
|
||||||
|
{
|
||||||
|
int currLeafIndex = currLeafCount;
|
||||||
|
currLeafCount++;
|
||||||
|
cLeaf_s* currLeaf = &clipMap->leafs[currLeafIndex];
|
||||||
|
|
||||||
|
currLeaf->cluster = 0;
|
||||||
|
currLeaf->brushContents = 0; // no brushes used so contents is 0
|
||||||
|
currLeaf->terrainContents = BSPEditableConstants::LEAF_TERRAIN_CONTENTS; // clipMap->cmodels[0].leaf.terrainContents takes prescedence
|
||||||
|
|
||||||
|
// unused when leafBrushNode == 0
|
||||||
|
currLeaf->mins.x = 0.0f;
|
||||||
|
currLeaf->mins.y = 0.0f;
|
||||||
|
currLeaf->mins.z = 0.0f;
|
||||||
|
currLeaf->maxs.x = 0.0f;
|
||||||
|
currLeaf->maxs.y = 0.0f;
|
||||||
|
currLeaf->maxs.z = 0.0f;
|
||||||
|
currLeaf->leafBrushNode = 0;
|
||||||
|
|
||||||
|
if (node->leaf->getObjectCount() > 0)
|
||||||
|
{
|
||||||
|
currLeaf->firstCollAabbIndex = addAABBTreeFromLeaf(node, clipMap);
|
||||||
|
currLeaf->collAabbCount = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currLeaf->firstCollAabbIndex = 0;
|
||||||
|
currLeaf->collAabbCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1 - currLeafIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cplane_s* currPlane = &clipMap->info.planes[currPlaneCount];
|
||||||
|
currPlaneCount++;
|
||||||
|
|
||||||
|
if (node->node->axis == AXIS_X)
|
||||||
|
{
|
||||||
|
// X is unchanged when going from OGL x -> BO2 x
|
||||||
|
currPlane->normal = normalX;
|
||||||
|
|
||||||
|
// converting OGL -> BO2 X coords doesn't change the x coords at all, so
|
||||||
|
// the dist stays the same
|
||||||
|
currPlane->dist = (float)node->node->distance;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// converting OGL -> BO2 Z coords negates the z coords and sets it to the y coord.
|
||||||
|
// convert the z normal to the y normal, but don't negate it. Negative normals don't do
|
||||||
|
// what is expected when the game uses them
|
||||||
|
assert(node->node->axis == AXIS_Z);
|
||||||
|
currPlane->normal = normalY;
|
||||||
|
|
||||||
|
// converting OGL -> BO2 Z coords negates the z coords and sets it to the y coord.
|
||||||
|
// just negate it here as it is just the distance from the orgin along the axis
|
||||||
|
currPlane->dist = (float)(-node->node->distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool foundType = false;
|
||||||
|
if (currPlane->normal.x == 1.0f)
|
||||||
|
{
|
||||||
|
assert(!foundType);
|
||||||
|
foundType = true;
|
||||||
|
currPlane->type = 0;
|
||||||
|
}
|
||||||
|
else if (currPlane->normal.y == 1.0f)
|
||||||
|
{
|
||||||
|
assert(!foundType);
|
||||||
|
foundType = true;
|
||||||
|
currPlane->type = 1;
|
||||||
|
}
|
||||||
|
else if (currPlane->normal.z == 1.0f)
|
||||||
|
{
|
||||||
|
assert(!foundType);
|
||||||
|
foundType = true;
|
||||||
|
currPlane->type = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
assert(foundType);
|
||||||
|
|
||||||
|
currPlane->signbits = 0;
|
||||||
|
if (currPlane->normal.x < 0.0f)
|
||||||
|
currPlane->signbits |= 1;
|
||||||
|
if (currPlane->normal.y < 0.0f)
|
||||||
|
currPlane->signbits |= 2;
|
||||||
|
if (currPlane->normal.z < 0.0f)
|
||||||
|
currPlane->signbits |= 4;
|
||||||
|
|
||||||
|
currPlane->pad[0] = 0;
|
||||||
|
currPlane->pad[1] = 0;
|
||||||
|
|
||||||
|
int currNodeIndex = currNodeCount;
|
||||||
|
currNodeCount++;
|
||||||
|
cNode_t* currNode = &clipMap->nodes[currNodeIndex];
|
||||||
|
|
||||||
|
currNode->plane = currPlane;
|
||||||
|
// Reason for the front and back flip (due to the hacky nature of making the mins and maxs work (see createClipMap)):
|
||||||
|
// after converting between OGL and BO2 coords and when and updating the normal from Z -> Y,
|
||||||
|
// the normal vector flips and objects behind the plane are now in front, and vise versa
|
||||||
|
// so the back node now represents the front, and the front node represents the back.
|
||||||
|
// Do the OGL -> Bo2 coord change on paper and it will make sense
|
||||||
|
if (currPlane->type == 1)
|
||||||
|
{
|
||||||
|
currNode->children[1] = populateBSPTree_r(clipMap, node->node->front.get());
|
||||||
|
currNode->children[0] = populateBSPTree_r(clipMap, node->node->back.get());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currNode->children[0] = populateBSPTree_r(clipMap, node->node->front.get());
|
||||||
|
currNode->children[1] = populateBSPTree_r(clipMap, node->node->back.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return currNodeIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClipMapLinker::populateBSPTree(clipMap_t* clipMap, BSPTree* tree)
|
||||||
|
{
|
||||||
|
size_t numPlanes = 0;
|
||||||
|
size_t numNodes = 0;
|
||||||
|
size_t numLeafs = 0;
|
||||||
|
size_t numAABBTrees = 0;
|
||||||
|
size_t maxObjsPerLeaf = 0;
|
||||||
|
|
||||||
|
traverseBSPTreeForCounts(tree, &numPlanes, &numNodes, &numLeafs, &numAABBTrees, &maxObjsPerLeaf);
|
||||||
|
|
||||||
|
printf("Max Objects per leaf: %i\n", maxObjsPerLeaf);
|
||||||
|
|
||||||
|
clipMap->info.planeCount = numPlanes;
|
||||||
|
clipMap->info.planes = m_memory.Alloc<cplane_s>(clipMap->info.planeCount);
|
||||||
|
clipMap->numNodes = numNodes;
|
||||||
|
clipMap->nodes = m_memory.Alloc<cNode_t>(clipMap->numNodes);
|
||||||
|
// aabb trees: each leaf will have their own AABB tree of the objects within it, and the root aabb node will be the parent of every other aabb node.
|
||||||
|
// therefore, each aabb tree will be of size (numObjects + 1) as the tree needs a root aabb node to reference it's children.
|
||||||
|
clipMap->aabbTreeCount = numAABBTrees;
|
||||||
|
clipMap->aabbTrees = m_memory.Alloc<CollisionAabbTree>(clipMap->aabbTreeCount);
|
||||||
|
|
||||||
|
currPlaneCount = 0;
|
||||||
|
currNodeCount = 0;
|
||||||
|
currAABBCount = 0;
|
||||||
|
|
||||||
|
// first leaf is always empty
|
||||||
|
clipMap->numLeafs = numLeafs + 1;
|
||||||
|
clipMap->leafs = m_memory.Alloc<cLeaf_s>(clipMap->numLeafs);
|
||||||
|
memset(&clipMap->leafs[0], 0, sizeof(cLeaf_s));
|
||||||
|
currLeafCount = 1;
|
||||||
|
|
||||||
|
populateBSPTree_r(clipMap, tree);
|
||||||
|
|
||||||
|
assert(clipMap->info.planeCount == currPlaneCount);
|
||||||
|
assert(clipMap->numNodes == currNodeCount);
|
||||||
|
assert(clipMap->numLeafs == currLeafCount);
|
||||||
|
assert(clipMap->aabbTreeCount == currAABBCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClipMapLinker::createPartitions(clipMap_t* clipMap, BSPData* bsp)
|
||||||
|
{
|
||||||
|
int collisionVertexCount = bsp->colWorld.vertices.size();
|
||||||
|
std::vector<vec3_t> collisionVertVec;
|
||||||
|
for (int i = 0; i < collisionVertexCount; i++)
|
||||||
|
{
|
||||||
|
collisionVertVec.push_back(BSPUtil::convertToBO2Coords(bsp->colWorld.vertices[i].pos));
|
||||||
|
//collisionVertVec.push_back(bsp->colWorld.vertices[i].pos);
|
||||||
|
}
|
||||||
|
clipMap->vertCount = collisionVertexCount;
|
||||||
|
clipMap->verts = m_memory.Alloc<vec3_t>(collisionVertexCount);
|
||||||
|
memcpy(clipMap->verts, &collisionVertVec[0], sizeof(vec3_t) * collisionVertexCount);
|
||||||
|
|
||||||
|
// due to tris using uint16_t as the type for indexing the vert array,
|
||||||
|
// any vertex count over the uint16_t max means the vertices above it can't be indexed
|
||||||
|
if (collisionVertexCount > BSPGameConstants::MAX_COLLISION_VERTS)
|
||||||
|
{
|
||||||
|
printf("ERROR: collision vertex count %i exceeds the maximum number: %i!\n", collisionVertexCount, BSPGameConstants::MAX_COLLISION_VERTS);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint16_t> triIndexVec;
|
||||||
|
for (size_t i = 0; i < bsp->colWorld.surfaces.size(); i++)
|
||||||
|
{
|
||||||
|
BSPSurface* currSurface = &bsp->colWorld.surfaces[i];
|
||||||
|
int triCount = currSurface->triCount;
|
||||||
|
|
||||||
|
for (int k = 0; k < triCount * 3; k += 3)
|
||||||
|
{
|
||||||
|
int firstIndex_Index = currSurface->indexOfFirstIndex;
|
||||||
|
int firstVertexIndex = currSurface->indexOfFirstVertex;
|
||||||
|
|
||||||
|
// gfx index bufer starts at 0 for each new mesh, while the clipmap index buffer indexes the entire
|
||||||
|
// clipmap verts buffer, so this code updates the indexes to follow that.
|
||||||
|
int triIndex0 = bsp->colWorld.indices[firstIndex_Index + (k + 0)] + firstVertexIndex;
|
||||||
|
int triIndex1 = bsp->colWorld.indices[firstIndex_Index + (k + 1)] + firstVertexIndex;
|
||||||
|
int triIndex2 = bsp->colWorld.indices[firstIndex_Index + (k + 2)] + firstVertexIndex;
|
||||||
|
|
||||||
|
// triangle index ordering is opposite to blenders, so its converted here
|
||||||
|
triIndexVec.push_back(triIndex2);
|
||||||
|
triIndexVec.push_back(triIndex1);
|
||||||
|
triIndexVec.push_back(triIndex0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(triIndexVec.size() % 3 == 0);
|
||||||
|
|
||||||
|
// 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 = 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 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
|
||||||
|
std::vector<CollisionPartition> partitionVec;
|
||||||
|
for (size_t i = 0; i < bsp->colWorld.surfaces.size(); i++)
|
||||||
|
{
|
||||||
|
int triCount = bsp->colWorld.surfaces[i].triCount;
|
||||||
|
int firstTriIndex = bsp->colWorld.surfaces[i].indexOfFirstIndex / 3;
|
||||||
|
for (int k = 0; k < triCount; k++)
|
||||||
|
{
|
||||||
|
CollisionPartition newPartition;
|
||||||
|
newPartition.nuinds = 0; // initialised later
|
||||||
|
newPartition.fuind = 0; // initialised later
|
||||||
|
newPartition.triCount = 1;
|
||||||
|
newPartition.firstTri = firstTriIndex;
|
||||||
|
firstTriIndex += 1;
|
||||||
|
|
||||||
|
partitionVec.push_back(newPartition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clipMap->partitionCount = partitionVec.size();
|
||||||
|
clipMap->partitions = m_memory.Alloc<CollisionPartition>(clipMap->partitionCount);
|
||||||
|
memcpy(clipMap->partitions, &partitionVec[0], sizeof(CollisionPartition) * clipMap->partitionCount);
|
||||||
|
|
||||||
|
int totalUindCount = 0;
|
||||||
|
std::vector<uint16_t> uindVec;
|
||||||
|
for (int i = 0; i < clipMap->partitionCount; i++)
|
||||||
|
{
|
||||||
|
CollisionPartition* currPartition = &clipMap->partitions[i];
|
||||||
|
std::vector<uint16_t> uniqueVertVec;
|
||||||
|
for (int k = 0; k < currPartition->triCount; k++)
|
||||||
|
{
|
||||||
|
uint16_t* tri = clipMap->triIndices[currPartition->firstTri + k];
|
||||||
|
for (int l = 0; l < 3; l++)
|
||||||
|
{
|
||||||
|
bool isVertexIndexUnique = true;
|
||||||
|
uint16_t vertIndex = tri[l];
|
||||||
|
|
||||||
|
for (size_t m = 0; m < uniqueVertVec.size(); m++)
|
||||||
|
{
|
||||||
|
if (uniqueVertVec[m] == vertIndex)
|
||||||
|
{
|
||||||
|
isVertexIndexUnique = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVertexIndexUnique)
|
||||||
|
uniqueVertVec.push_back(vertIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currPartition->fuind = totalUindCount;
|
||||||
|
currPartition->nuinds = (int)uniqueVertVec.size();
|
||||||
|
uindVec.insert(uindVec.end(), uniqueVertVec.begin(), uniqueVertVec.end());
|
||||||
|
totalUindCount += currPartition->nuinds;
|
||||||
|
}
|
||||||
|
clipMap->info.nuinds = totalUindCount;
|
||||||
|
clipMap->info.uinds = m_memory.Alloc<uint16_t>(totalUindCount);
|
||||||
|
memcpy(clipMap->info.uinds, &uindVec[0], sizeof(uint16_t) * totalUindCount);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClipMapLinker::loadBrushCollision(clipMap_t* clipMap, BSPData* bsp)
|
||||||
|
{
|
||||||
|
// No support for brushes, only tris right now
|
||||||
|
clipMap->info.numBrushSides = 0;
|
||||||
|
clipMap->info.brushsides = NULL;
|
||||||
|
clipMap->info.leafbrushNodesCount = 0;
|
||||||
|
clipMap->info.leafbrushNodes = NULL;
|
||||||
|
clipMap->info.numLeafBrushes = 0;
|
||||||
|
clipMap->info.leafbrushes = NULL;
|
||||||
|
clipMap->info.numBrushVerts = 0;
|
||||||
|
clipMap->info.brushVerts = NULL;
|
||||||
|
clipMap->info.numBrushes = NULL;
|
||||||
|
clipMap->info.brushes = NULL;
|
||||||
|
clipMap->info.brushBounds = NULL;
|
||||||
|
clipMap->info.brushContents = NULL;
|
||||||
|
|
||||||
|
// clipmap BSP creation must go last as it depends on unids, tris and verts already being populated
|
||||||
|
// HACK:
|
||||||
|
// the BSP tree creation does not work when BO2's coordinate system is used for mins and maxs.
|
||||||
|
// Workaround is to convert every BO2 coordinate to OGL's before it is added into the BSP tree,
|
||||||
|
// and then convert them back when it is being parsed into the clipmap. Requires some hacky
|
||||||
|
// logic, check populateBSPTree_r and addAABBTreeFromLeaf
|
||||||
|
|
||||||
|
if (!createPartitions(clipMap, bsp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
vec3_t* firstVert = &clipMap->verts[0];
|
||||||
|
vec3_t clipMins;
|
||||||
|
vec3_t clipMaxs;
|
||||||
|
clipMins.x = firstVert->x;
|
||||||
|
clipMins.y = firstVert->y;
|
||||||
|
clipMins.z = firstVert->z;
|
||||||
|
clipMaxs.x = firstVert->x;
|
||||||
|
clipMaxs.y = firstVert->y;
|
||||||
|
clipMaxs.z = firstVert->z;
|
||||||
|
clipMins = BSPUtil::convertFromBO2Coords(clipMins);
|
||||||
|
clipMaxs = BSPUtil::convertFromBO2Coords(clipMaxs);
|
||||||
|
for (unsigned int i = 1; i < clipMap->vertCount; i++)
|
||||||
|
{
|
||||||
|
vec3_t vertCoord = BSPUtil::convertFromBO2Coords(clipMap->verts[i]);
|
||||||
|
BSPUtil::calcNewBoundsWithPoint(&vertCoord, &clipMins, &clipMaxs);
|
||||||
|
}
|
||||||
|
|
||||||
|
BSPTree* tree = new BSPTree(clipMins.x, clipMins.y, clipMins.z, clipMaxs.x, clipMaxs.y, clipMaxs.z, 0);
|
||||||
|
|
||||||
|
assert(!tree->isLeaf);
|
||||||
|
|
||||||
|
for (int i = 0; i < clipMap->partitionCount; i++)
|
||||||
|
{
|
||||||
|
auto currPartition = &clipMap->partitions[i];
|
||||||
|
|
||||||
|
uint16_t* firstTri = clipMap->triIndices[currPartition->firstTri];
|
||||||
|
vec3_t* firstVert = &clipMap->verts[firstTri[0]];
|
||||||
|
vec3_t mins;
|
||||||
|
vec3_t maxs;
|
||||||
|
mins.x = firstVert->x;
|
||||||
|
mins.y = firstVert->y;
|
||||||
|
mins.z = firstVert->z;
|
||||||
|
maxs.x = firstVert->x;
|
||||||
|
maxs.y = firstVert->y;
|
||||||
|
maxs.z = firstVert->z;
|
||||||
|
mins = BSPUtil::convertFromBO2Coords(mins);
|
||||||
|
maxs = BSPUtil::convertFromBO2Coords(maxs);
|
||||||
|
for (int k = 0; k < currPartition->triCount; k++)
|
||||||
|
{
|
||||||
|
uint16_t* tri = clipMap->triIndices[currPartition->firstTri + k];
|
||||||
|
for (int l = 0; l < 3; l++)
|
||||||
|
{
|
||||||
|
uint16_t vertIndex = tri[l];
|
||||||
|
vec3_t vertCoord = BSPUtil::convertFromBO2Coords(clipMap->verts[vertIndex]);
|
||||||
|
BSPUtil::calcNewBoundsWithPoint(&vertCoord, &mins, &maxs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<BSPObject> currObject = std::make_shared<BSPObject>(mins.x, mins.y, mins.z, maxs.x, maxs.y, maxs.z, i);
|
||||||
|
|
||||||
|
tree->addObjectToTree(std::move(currObject));
|
||||||
|
}
|
||||||
|
|
||||||
|
populateBSPTree(clipMap, tree);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetCreationResult ClipMapLinker::linkClipMap(BSPData* bsp)
|
||||||
|
{
|
||||||
|
clipMap_t* clipMap = m_memory.Alloc<clipMap_t>();
|
||||||
|
clipMap->name = m_memory.Dup(bsp->bspName.c_str());
|
||||||
|
|
||||||
|
clipMap->isInUse = true;
|
||||||
|
clipMap->checksum = 0;
|
||||||
|
clipMap->pInfo = nullptr;
|
||||||
|
|
||||||
|
std::string mapEntsName = bsp->bspName;
|
||||||
|
auto mapEntsAsset = m_context.LoadDependency<AssetMapEnts>(mapEntsName);
|
||||||
|
assert(mapEntsAsset != nullptr);
|
||||||
|
clipMap->mapEnts = mapEntsAsset->Asset();
|
||||||
|
|
||||||
|
loadBoxData(clipMap);
|
||||||
|
|
||||||
|
loadVisibility(clipMap);
|
||||||
|
|
||||||
|
loadRopesAndConstraints(clipMap);
|
||||||
|
|
||||||
|
loadSubModelCollision(clipMap, bsp);
|
||||||
|
|
||||||
|
loadDynEnts(clipMap);
|
||||||
|
|
||||||
|
loadXModelCollision(clipMap);
|
||||||
|
|
||||||
|
// Clipmap materials define the properties of a material (bullet penetration, no collision, water, etc)
|
||||||
|
// Right now there is no way to define properties per material so only one material is used
|
||||||
|
clipMap->info.numMaterials = 1;
|
||||||
|
clipMap->info.materials = m_memory.Alloc<ClipMaterial>(clipMap->info.numMaterials);
|
||||||
|
clipMap->info.materials[0].name = m_memory.Dup(BSPLinkingConstants::MISSING_IMAGE_NAME);
|
||||||
|
clipMap->info.materials[0].contentFlags = BSPEditableConstants::MATERIAL_CONTENT_FLAGS;
|
||||||
|
clipMap->info.materials[0].surfaceFlags = BSPEditableConstants::MATERIAL_SURFACE_FLAGS;
|
||||||
|
|
||||||
|
// set all edges to walkable
|
||||||
|
// might do weird stuff on walls, but from testing doesnt seem to do anything
|
||||||
|
int walkableEdgeSize = (3 * clipMap->triCount + 31) / 32 * 4;
|
||||||
|
clipMap->triEdgeIsWalkable = new char[walkableEdgeSize];
|
||||||
|
memset(clipMap->triEdgeIsWalkable, 1, walkableEdgeSize * sizeof(char));
|
||||||
|
|
||||||
|
if (!loadBrushCollision(clipMap, bsp))
|
||||||
|
return AssetCreationResult::Failure();
|
||||||
|
|
||||||
|
m_context.AddAsset<AssetClipMapPvs>(clipMap->name, clipMap);
|
||||||
|
|
||||||
|
auto clipMapAsset = m_context.AddAsset<AssetClipMap>(clipMap->name, clipMap);
|
||||||
|
return AssetCreationResult::Success(clipMapAsset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
33
src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.h
Normal file
33
src/ObjLoading/Game/T6/BSP/Linker/ClipMapLinker.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../BSP.h"
|
||||||
|
#include "Asset/IAssetCreator.h"
|
||||||
|
#include "SearchPath/ISearchPath.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
#include "../BSPCalculation.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
class ClipMapLinker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ClipMapLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context);
|
||||||
|
AssetCreationResult linkClipMap(BSPData* bsp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
MemoryManager& m_memory;
|
||||||
|
ISearchPath& m_search_path;
|
||||||
|
AssetCreationContext& m_context;
|
||||||
|
|
||||||
|
void loadBoxData(clipMap_t* clipMap);
|
||||||
|
void loadVisibility(clipMap_t* clipMap);
|
||||||
|
void loadDynEnts(clipMap_t* clipMap);
|
||||||
|
void loadRopesAndConstraints(clipMap_t* clipMap);
|
||||||
|
void loadSubModelCollision(clipMap_t* clipMap, BSPData* bsp);
|
||||||
|
void loadXModelCollision(clipMap_t* clipMap);
|
||||||
|
|
||||||
|
bool loadBrushCollision(clipMap_t* clipMap, BSPData* bsp);
|
||||||
|
void populateBSPTree(clipMap_t* clipMap, BSPTree* tree);
|
||||||
|
bool createPartitions(clipMap_t* clipMap, BSPData* bsp);
|
||||||
|
};
|
||||||
|
}
|
||||||
40
src/ObjLoading/Game/T6/BSP/Linker/ComWorldLinker.cpp
Normal file
40
src/ObjLoading/Game/T6/BSP/Linker/ComWorldLinker.cpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#include "ComWorldLinker.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
ComWorldLinker::ComWorldLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context)
|
||||||
|
: m_memory(memory),
|
||||||
|
m_search_path(searchPath),
|
||||||
|
m_context(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetCreationResult ComWorldLinker::linkComWorld(BSPData* bsp)
|
||||||
|
{
|
||||||
|
// all lights that aren't the sunlight or default light need their own GfxLightDef asset
|
||||||
|
ComWorld* comWorld = m_memory.Alloc<ComWorld>();
|
||||||
|
comWorld->name = m_memory.Dup(bsp->bspName.c_str());
|
||||||
|
comWorld->isInUse = 1;
|
||||||
|
comWorld->primaryLightCount = 2;
|
||||||
|
comWorld->primaryLights = m_memory.Alloc<ComPrimaryLight>(comWorld->primaryLightCount);
|
||||||
|
|
||||||
|
// static light is always empty
|
||||||
|
ComPrimaryLight* staticLight = &comWorld->primaryLights[0];
|
||||||
|
memset(staticLight, 0, sizeof(ComPrimaryLight));
|
||||||
|
|
||||||
|
ComPrimaryLight* sunLight = &comWorld->primaryLights[1];
|
||||||
|
memset(sunLight, 0, sizeof(ComPrimaryLight));
|
||||||
|
sunLight->type = 1;
|
||||||
|
sunLight->diffuseColor.r = 0.75f;
|
||||||
|
sunLight->diffuseColor.g = 0.75f;
|
||||||
|
sunLight->diffuseColor.b = 0.75f;
|
||||||
|
sunLight->diffuseColor.a = 1.0f;
|
||||||
|
sunLight->dir.x = 0.0f;
|
||||||
|
sunLight->dir.y = 0.0f;
|
||||||
|
sunLight->dir.z = 0.0f;
|
||||||
|
|
||||||
|
auto comWorldAsset = m_context.AddAsset<AssetComWorld>(comWorld->name, comWorld);
|
||||||
|
return AssetCreationResult::Success(comWorldAsset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
21
src/ObjLoading/Game/T6/BSP/Linker/ComWorldLinker.h
Normal file
21
src/ObjLoading/Game/T6/BSP/Linker/ComWorldLinker.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../BSP.h"
|
||||||
|
#include "Asset/IAssetCreator.h"
|
||||||
|
#include "SearchPath/ISearchPath.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
class ComWorldLinker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ComWorldLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context);
|
||||||
|
AssetCreationResult linkComWorld(BSPData* bsp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
MemoryManager& m_memory;
|
||||||
|
ISearchPath& m_search_path;
|
||||||
|
AssetCreationContext& m_context;
|
||||||
|
};
|
||||||
|
}
|
||||||
38
src/ObjLoading/Game/T6/BSP/Linker/GameWorldMpLinker.cpp
Normal file
38
src/ObjLoading/Game/T6/BSP/Linker/GameWorldMpLinker.cpp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#include "GameWorldMpLinker.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
GameWorldMpLinker::GameWorldMpLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context)
|
||||||
|
: m_memory(memory),
|
||||||
|
m_search_path(searchPath),
|
||||||
|
m_context(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetCreationResult GameWorldMpLinker::linkGameWorldMp(BSPData* bsp)
|
||||||
|
{
|
||||||
|
GameWorldMp* gameWorldMp = m_memory.Alloc<GameWorldMp>();
|
||||||
|
|
||||||
|
gameWorldMp->name = m_memory.Dup(bsp->bspName.c_str());
|
||||||
|
|
||||||
|
gameWorldMp->path.nodeCount = 0;
|
||||||
|
gameWorldMp->path.originalNodeCount = 0;
|
||||||
|
gameWorldMp->path.visBytes = 0;
|
||||||
|
gameWorldMp->path.smoothBytes = 0;
|
||||||
|
gameWorldMp->path.nodeTreeCount = 0;
|
||||||
|
|
||||||
|
int nodeCount = gameWorldMp->path.nodeCount + 128;
|
||||||
|
gameWorldMp->path.nodes = m_memory.Alloc<pathnode_t>(nodeCount);
|
||||||
|
gameWorldMp->path.basenodes = m_memory.Alloc<pathbasenode_t>(nodeCount);
|
||||||
|
memset(gameWorldMp->path.nodes, 0, nodeCount * sizeof(pathnode_t));
|
||||||
|
memset(gameWorldMp->path.basenodes, 0, nodeCount * sizeof(pathbasenode_t));
|
||||||
|
|
||||||
|
gameWorldMp->path.pathVis = nullptr;
|
||||||
|
gameWorldMp->path.smoothCache = nullptr;
|
||||||
|
gameWorldMp->path.nodeTree = nullptr;
|
||||||
|
|
||||||
|
auto gameWorldMpAsset = m_context.AddAsset<AssetGameWorldMp>(gameWorldMp->name, gameWorldMp);
|
||||||
|
return AssetCreationResult::Success(gameWorldMpAsset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
21
src/ObjLoading/Game/T6/BSP/Linker/GameWorldMpLinker.h
Normal file
21
src/ObjLoading/Game/T6/BSP/Linker/GameWorldMpLinker.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../BSP.h"
|
||||||
|
#include "Asset/IAssetCreator.h"
|
||||||
|
#include "SearchPath/ISearchPath.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
class GameWorldMpLinker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GameWorldMpLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context);
|
||||||
|
AssetCreationResult linkGameWorldMp(BSPData* bsp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
MemoryManager& m_memory;
|
||||||
|
ISearchPath& m_search_path;
|
||||||
|
AssetCreationContext& m_context;
|
||||||
|
};
|
||||||
|
}
|
||||||
17
src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp
Normal file
17
src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include "GfxWorldLinker.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
GfxWorldLinker::GfxWorldLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context)
|
||||||
|
: m_memory(memory),
|
||||||
|
m_search_path(searchPath),
|
||||||
|
m_context(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetCreationResult GfxWorldLinker::linkGfxWorld(BSPData* bsp)
|
||||||
|
{
|
||||||
|
return AssetCreationResult::Failure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
21
src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.h
Normal file
21
src/ObjLoading/Game/T6/BSP/Linker/GfxWorldLinker.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../BSP.h"
|
||||||
|
#include "Asset/IAssetCreator.h"
|
||||||
|
#include "SearchPath/ISearchPath.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
class GfxWorldLinker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GfxWorldLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context);
|
||||||
|
AssetCreationResult linkGfxWorld(BSPData* bsp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
MemoryManager& m_memory;
|
||||||
|
ISearchPath& m_search_path;
|
||||||
|
AssetCreationContext& m_context;
|
||||||
|
};
|
||||||
|
}
|
||||||
128
src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp
Normal file
128
src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.cpp
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#include "MapEntsLinker.h"
|
||||||
|
#include "../BSPUtil.h"
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
using namespace nlohmann;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool parseMapEntsJSON(json& entArrayJs, std::string& entityString)
|
||||||
|
{
|
||||||
|
int entityCount = entArrayJs.size();
|
||||||
|
for (int i = 0; i < entityCount; i++)
|
||||||
|
{
|
||||||
|
auto currEntity = entArrayJs[i];
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
std::string className = currEntity["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 : currEntity.items())
|
||||||
|
{
|
||||||
|
std::string key = element.key();
|
||||||
|
std::string value = element.value();
|
||||||
|
entityString.append(std::format("\"{}\" \"{}\"\n", key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
entityString.append("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseSpawnpointJSON(json& entArrayJs, std::string& entityString, const char* spawnpointNames[], int nameCount)
|
||||||
|
{
|
||||||
|
int entityCount = entArrayJs.size();
|
||||||
|
for (int i = 0; i < entityCount; i++)
|
||||||
|
{
|
||||||
|
auto currEntity = entArrayJs[i];
|
||||||
|
|
||||||
|
std::string origin = currEntity["origin"];
|
||||||
|
std::string angles = currEntity["angles"];
|
||||||
|
|
||||||
|
for (int k = 0; k < nameCount; k++)
|
||||||
|
{
|
||||||
|
entityString.append("{\n");
|
||||||
|
entityString.append(std::format("\"origin\" \"{}\"\n", origin));
|
||||||
|
entityString.append(std::format("\"angles\" \"{}\"\n", angles));
|
||||||
|
entityString.append(std::format("\"classname\" \"{}\"\n", spawnpointNames[k]));
|
||||||
|
entityString.append("}\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
MapEntsLinker::MapEntsLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context)
|
||||||
|
: m_memory(memory),
|
||||||
|
m_search_path(searchPath),
|
||||||
|
m_context(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetCreationResult MapEntsLinker::linkMapEnts(BSPData* bsp)
|
||||||
|
{
|
||||||
|
std::string entityString;
|
||||||
|
|
||||||
|
json entJs;
|
||||||
|
std::string entityFilePath = BSPUtil::getFileNameForBSPAsset("entities.json");
|
||||||
|
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(BSPLinkingConstants::DEFAULT_MAP_ENTS_STRING);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entJs = json::parse(*entFile.m_stream);
|
||||||
|
}
|
||||||
|
if (!parseMapEntsJSON(entJs["entities"], entityString))
|
||||||
|
return AssetCreationResult::Failure();
|
||||||
|
|
||||||
|
json spawnJs;
|
||||||
|
std::string spawnFilePath = BSPUtil::getFileNameForBSPAsset("spawns.json");
|
||||||
|
const auto spawnFile = m_search_path.Open(spawnFilePath);
|
||||||
|
if (!spawnFile.IsOpen())
|
||||||
|
{
|
||||||
|
con::warn("Cant find spawn file {}, setting spawns to 0 0 0", spawnFilePath);
|
||||||
|
spawnJs = json::parse(BSPLinkingConstants::DEFAULT_SPAWN_POINT_STRING);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
spawnJs = json::parse(*spawnFile.m_stream);
|
||||||
|
}
|
||||||
|
int defenderNameCount = std::extent<decltype(BSPGameConstants::DEFENDER_SPAWN_POINT_NAMES)>::value;
|
||||||
|
int attackerNameCount = std::extent<decltype(BSPGameConstants::ATTACKER_SPAWN_POINT_NAMES)>::value;
|
||||||
|
int ffaNameCount = std::extent<decltype(BSPGameConstants::FFA_SPAWN_POINT_NAMES)>::value;
|
||||||
|
parseSpawnpointJSON(spawnJs["attackers"], entityString, BSPGameConstants::DEFENDER_SPAWN_POINT_NAMES, defenderNameCount);
|
||||||
|
parseSpawnpointJSON(spawnJs["defenders"], entityString, BSPGameConstants::ATTACKER_SPAWN_POINT_NAMES, attackerNameCount);
|
||||||
|
parseSpawnpointJSON(spawnJs["FFA"], entityString, BSPGameConstants::FFA_SPAWN_POINT_NAMES, ffaNameCount);
|
||||||
|
|
||||||
|
MapEnts* mapEnts = m_memory.Alloc<MapEnts>();
|
||||||
|
mapEnts->name = m_memory.Dup(bsp->bspName.c_str());
|
||||||
|
|
||||||
|
mapEnts->entityString = m_memory.Dup(entityString.c_str());
|
||||||
|
mapEnts->numEntityChars = entityString.length() + 1; // numEntityChars includes the null character
|
||||||
|
|
||||||
|
// don't need these
|
||||||
|
mapEnts->trigger.count = 0;
|
||||||
|
mapEnts->trigger.models = nullptr;
|
||||||
|
mapEnts->trigger.hullCount = 0;
|
||||||
|
mapEnts->trigger.hulls = nullptr;
|
||||||
|
mapEnts->trigger.slabCount = 0;
|
||||||
|
mapEnts->trigger.slabs = nullptr;
|
||||||
|
|
||||||
|
auto mapEntsAsset = m_context.AddAsset<AssetMapEnts>(mapEnts->name, mapEnts);
|
||||||
|
return AssetCreationResult::Success(mapEntsAsset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
21
src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.h
Normal file
21
src/ObjLoading/Game/T6/BSP/Linker/MapEntsLinker.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../BSP.h"
|
||||||
|
#include "Asset/IAssetCreator.h"
|
||||||
|
#include "SearchPath/ISearchPath.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
class MapEntsLinker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MapEntsLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context);
|
||||||
|
AssetCreationResult linkMapEnts(BSPData* bsp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
MemoryManager& m_memory;
|
||||||
|
ISearchPath& m_search_path;
|
||||||
|
AssetCreationContext& m_context;
|
||||||
|
};
|
||||||
|
}
|
||||||
26
src/ObjLoading/Game/T6/BSP/Linker/SkinnedVertsLinker.cpp
Normal file
26
src/ObjLoading/Game/T6/BSP/Linker/SkinnedVertsLinker.cpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#include "SkinnedVertsLinker.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
SkinnedVertsLinker::SkinnedVertsLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context)
|
||||||
|
: m_memory(memory),
|
||||||
|
m_search_path(searchPath),
|
||||||
|
m_context(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetCreationResult SkinnedVertsLinker::linkSkinnedVerts(BSPData* bsp)
|
||||||
|
{
|
||||||
|
std::string assetName = "skinnedverts";
|
||||||
|
|
||||||
|
// I'm pretty sure maxSkinnedVerts relates to the max amount of xmodel skinned verts a map will have
|
||||||
|
// But setting it to the world vertex count seems to work
|
||||||
|
SkinnedVertsDef* skinnedVerts = m_memory.Alloc<SkinnedVertsDef>();
|
||||||
|
skinnedVerts->name = m_memory.Dup(assetName.c_str());
|
||||||
|
skinnedVerts->maxSkinnedVerts = static_cast<unsigned int>(bsp->gfxWorld.vertices.size());
|
||||||
|
|
||||||
|
auto skinnedVertsAsset = m_context.AddAsset<AssetSkinnedVerts>(assetName, skinnedVerts);
|
||||||
|
return AssetCreationResult::Success(skinnedVertsAsset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
21
src/ObjLoading/Game/T6/BSP/Linker/SkinnedVertsLinker.h
Normal file
21
src/ObjLoading/Game/T6/BSP/Linker/SkinnedVertsLinker.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../BSP.h"
|
||||||
|
#include "Asset/IAssetCreator.h"
|
||||||
|
#include "SearchPath/ISearchPath.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
namespace BSP
|
||||||
|
{
|
||||||
|
class SkinnedVertsLinker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SkinnedVertsLinker(MemoryManager& memory, ISearchPath& searchPath, AssetCreationContext& context);
|
||||||
|
AssetCreationResult linkSkinnedVerts(BSPData* bsp);
|
||||||
|
|
||||||
|
private:
|
||||||
|
MemoryManager& m_memory;
|
||||||
|
ISearchPath& m_search_path;
|
||||||
|
AssetCreationContext& m_context;
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user