2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2025-11-23 05:12:05 +00:00

Initial commit of BSP compilation

This commit is contained in:
LJW-Dev
2025-07-07 23:04:08 +08:00
parent f3c83f702a
commit c045d54739
14 changed files with 3406 additions and 2 deletions

View File

@@ -159,6 +159,51 @@ namespace T6
AUFT_NUM_FIELD_TYPES, AUFT_NUM_FIELD_TYPES,
}; };
struct customMapVertex
{
vec3_t pos;
float binormalSign;
float color[4];
float texCoord[2];
vec3_t normal;
vec3_t tangent;
unsigned int packedLmapCoord;
};
struct worldSurface
{
char flags;
char lightmapIndex;
std::string materialName;
char primaryLightIndex;
char reflectionProbeIndex;
int triCount;
int firstVertexIndex;
int firstIndex_Index;
};
struct customMapGfx
{
int vertexCount;
customMapVertex* vertices;
int indexCount;
uint16_t* indices;
int surfaceCount;
worldSurface* surfaces;
};
struct customMapInfo
{
std::string name;
std::string bspName;
customMapGfx gfxInfo;
};
using AssetPhysPreset = Asset<ASSET_TYPE_PHYSPRESET, PhysPreset>; using AssetPhysPreset = Asset<ASSET_TYPE_PHYSPRESET, PhysPreset>;
using AssetPhysConstraints = Asset<ASSET_TYPE_PHYSCONSTRAINTS, PhysConstraints>; using AssetPhysConstraints = Asset<ASSET_TYPE_PHYSCONSTRAINTS, PhysConstraints>;
using AssetDestructibleDef = Asset<ASSET_TYPE_DESTRUCTIBLEDEF, DestructibleDef>; using AssetDestructibleDef = Asset<ASSET_TYPE_DESTRUCTIBLEDEF, DestructibleDef>;
@@ -208,6 +253,7 @@ namespace T6
using AssetFootstepTable = Asset<ASSET_TYPE_FOOTSTEP_TABLE, FootstepTableDef>; using AssetFootstepTable = Asset<ASSET_TYPE_FOOTSTEP_TABLE, FootstepTableDef>;
using AssetFootstepFxTable = Asset<ASSET_TYPE_FOOTSTEPFX_TABLE, FootstepFXTableDef>; using AssetFootstepFxTable = Asset<ASSET_TYPE_FOOTSTEPFX_TABLE, FootstepFXTableDef>;
using AssetZBarrier = Asset<ASSET_TYPE_ZBARRIER, ZBarrierDef>; using AssetZBarrier = Asset<ASSET_TYPE_ZBARRIER, ZBarrierDef>;
using AssetCustomMap = Asset<ASSET_TYPE_CUSTOM_MAP, customMapInfo>;
} // namespace T6 } // namespace T6
DEFINE_ASSET_NAME_ACCESSOR(T6::AssetPhysPreset, name); DEFINE_ASSET_NAME_ACCESSOR(T6::AssetPhysPreset, name);

View File

@@ -304,6 +304,8 @@ namespace T6
ASSET_TYPE_REPORT = 0x3E, ASSET_TYPE_REPORT = 0x3E,
ASSET_TYPE_DEPEND = 0x3F, ASSET_TYPE_DEPEND = 0x3F,
ASSET_TYPE_FULL_COUNT = 0x40, ASSET_TYPE_FULL_COUNT = 0x40,
ASSET_TYPE_CUSTOM_MAP = 0x41
}; };
enum XFileBlock enum XFileBlock
@@ -1244,13 +1246,13 @@ namespace T6
struct GfxWorldVertexData0 struct GfxWorldVertexData0
{ {
byte128* data; byte128* data; // GfxPackedWorldVertex
void /*ID3D11Buffer*/* vb; void /*ID3D11Buffer*/* vb;
}; };
struct GfxWorldVertexData1 struct GfxWorldVertexData1
{ {
byte128* data; byte128* data; // GfxPackedWorldVertex
void /*ID3D11Buffer*/* vb; void /*ID3D11Buffer*/* vb;
}; };
@@ -5718,6 +5720,11 @@ namespace T6
unsigned int packed; unsigned int packed;
}; };
union PackedLmapCoords
{
unsigned int packed;
};
struct type_align(16) GfxPackedVertex struct type_align(16) GfxPackedVertex
{ {
vec3_t xyz; vec3_t xyz;
@@ -5728,6 +5735,17 @@ namespace T6
PackedUnitVec tangent; PackedUnitVec tangent;
}; };
struct GfxPackedWorldVertex
{
vec3_t xyz;
float binormalSign;
GfxColor color;
PackedTexCoords texCoord;
PackedUnitVec normal;
PackedUnitVec tangent;
PackedLmapCoords lmapCoord;
};
struct XRigidVertList struct XRigidVertList
{ {
uint16_t boneOffset; uint16_t boneOffset;

View File

@@ -0,0 +1,232 @@
#pragma once
/*
Heavily modified version of https://github.com/sudeshnapal12/Space-Partitioning-Algorithms BSP implementation
Credit to sudeshnapal12
Precalculated, evenly sized BSPs are much more efficient and smaller compared to dynamically creating them
*/
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <map>
#include <sstream>
#include <vector>
#define MAX_AABB_SIZE 512 // maximum size an AABB tree can be before it becomes a node
enum PlaneAxis
{
AXIS_X,
AXIS_Y,
AXIS_Z
};
class Object
{
public:
double low[3];
double high[3];
// custom data
int partitionIndex; // index of the partition the object is based on (custom)
Object(double min_x, double min_y, double min_z, double max_x, double max_y, double max_z, int partition_Index)
{
low[0] = min_x;
low[1] = min_y, low[2] = min_z;
high[0] = max_x;
high[1] = max_y;
high[2] = max_z;
partitionIndex = partition_Index;
}
};
union u_BSPNode;
class BSPTree;
class BSPLeaf
{
private:
std::vector<Object*> objectList;
public:
BSPLeaf()
{
objectList = std::vector<Object*>();
}
~BSPLeaf()
{
objectList.clear();
}
void addToList(Object* object)
{
objectList.push_back(object);
}
int getObjectCount()
{
return objectList.size();
}
Object* getObject(int index)
{
return objectList.at(index);
}
};
enum objectPlaneSide
{
SIDE_FRONT,
SIDE_BACK,
SIDE_INTERSECTS
};
class BSPNode
{
public:
BSPTree* front;
BSPTree* back;
PlaneAxis axis; // axis that the split plane is on
double distance; // distance from the origin (0, 0, 0) to the plane
BSPNode(BSPTree* _front, BSPTree* _back, PlaneAxis _axis, double _distance)
{
front = _front;
back = _back;
axis = _axis;
distance = _distance;
}
objectPlaneSide objectIsInfront(Object* object)
{
double minCoord, maxCoord;
// Select the relevant coordinate based on the plane's axis
switch (axis)
{
case AXIS_X:
minCoord = object->low[0];
maxCoord = object->high[0];
break;
case AXIS_Y:
minCoord = object->low[1];
maxCoord = object->high[1];
break;
case AXIS_Z:
minCoord = object->low[2];
maxCoord = object->high[2];
break;
default:
_ASSERT(false); // this should never be executed
}
// Compare with the plane's distance
if (maxCoord < distance)
{
return SIDE_BACK; // Object is entirely on the negative side
}
else if (minCoord > distance)
{
return SIDE_FRONT; // Object is entirely on the positive side
}
else
{
return SIDE_INTERSECTS;
}
}
};
union u_BSPNode
{
BSPLeaf* leaf;
BSPNode* node;
};
class BSPTree
{
public:
bool isLeaf;
u_BSPNode u;
int level; // level in the BSP tree
double low[3]; // mins
double high[3]; // maxs
BSPTree(double min_x, double min_y, double min_z, double max_x, double max_y, double max_z, int _level)
{
low[0] = min_x;
low[1] = min_y, low[2] = min_z;
high[0] = max_x;
high[1] = max_y;
high[2] = max_z;
level = _level;
splitTree();
}
// For simplicity, only split across the X and Z axis.
// It is unlikely that there are many layers to a map, and is instead mostly flat
void splitTree()
{
BSPTree* front;
BSPTree* back;
double halfLength;
if (high[0] - low[0] > MAX_AABB_SIZE)
{
// split along the x axis
halfLength = (low[0] + high[0]) * 0.5f;
front = new BSPTree(halfLength, low[1], low[2], high[0], high[1], high[2], level + 1);
back = new BSPTree(low[0], low[1], low[2], halfLength, high[1], high[2], level + 1);
isLeaf = false;
u.node = new BSPNode(front, back, AXIS_X, halfLength);
}
else if (high[2] - low[2] > MAX_AABB_SIZE)
{
// split along the z axis
halfLength = (low[2] + high[2]) * 0.5f;
front = new BSPTree(low[0], low[1], halfLength, high[0], high[1], high[2], level + 1);
back = new BSPTree(low[0], low[1], low[2], high[0], high[1], halfLength, level + 1);
isLeaf = false;
u.node = new BSPNode(front, back, AXIS_Z, halfLength);
}
else
{
isLeaf = true;
u.leaf = new BSPLeaf();
}
}
void addObject(Object* object)
{
if (isLeaf)
{
u.leaf->addToList(object);
}
else
{
objectPlaneSide side = u.node->objectIsInfront(object);
if (side == SIDE_FRONT)
{
u.node->front->addObject(object);
}
else if (side == SIDE_BACK)
{
u.node->back->addObject(object);
}
else // intersects
{
u.node->front->addObject(object);
u.node->back->addObject(object);
}
}
}
};

View File

@@ -0,0 +1,20 @@
#include <string>
#define DYN_ENT_COUNT 0
// the clipMap->cmodels[0].leaf.terrainContents takes precendence over leaf and material terrain contents
// material flags determine the features of the surface
// unsure which flag type changes what right now
// -1 results in: no running, water splashes all the time, low friction, slanted angles make you slide very fast
// 1 results in: normal surface features, grenades work,
#define MATERIAL_SURFACE_FLAGS 1
#define MATERIAL_CONTENT_FLAGS 1
// terrain flags: does not change the type of terrain or what features they have
// from testing, as long at it isn't 0 things will work correctly
#define LEAF_TERRAIN_CONTENTS 1 // match all flags
#define WORLD_TERRAIN_CONTENTS 1 // match all flags
// const std::string defaultMaterialName = "wpc/intro_wall_plaster_light_blue_01";
const std::string defaultMaterialName = "wpc/dig_plastic_blue";

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
#include "Game/T6/T6.h"
#include "SearchPath/ISearchPath.h"
#include "Utils/MemoryManager.h"
#include "Asset/IAssetCreator.h"
using namespace T6;
#include "OBJ_Loader.h"
class CustomMapLinker
{
public:
CustomMapLinker(MemoryManager& memory, ISearchPath& searchPath, Zone& zone);
bool linkCustomMap(customMapInfo* projInfo);
};

View File

@@ -0,0 +1,57 @@
#include "LoaderCustomMapT6.h"
#include "ProjectCreator.h"
#include "CustomMapLinker.h"
#include "Game/T6/T6.h"
#include <cstring>
using namespace T6;
namespace
{
class CustomMapLoader final : public AssetCreator<AssetCustomMap>
{
public:
CustomMapLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
: m_memory(memory),
m_search_path(searchPath),
m_zone(zone)
{
}
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
{
auto mapFile = m_search_path.Open("custom_map/map.obj");
if (!mapFile.IsOpen())
return AssetCreationResult::NoAction();
// create map info from the obj file
customMapInfo* mapInfo = CustomMapInfo::createCustomMapInfo(m_zone.m_name, m_search_path);
if (mapInfo == NULL)
return AssetCreationResult::Failure();
// linker will add all the assets needed
CustomMapLinker* linker = new CustomMapLinker(m_memory, m_search_path, m_zone);
bool result = linker->linkCustomMap(mapInfo);
if (result)
return AssetCreationResult::NoAction();
else
return AssetCreationResult::Failure();
}
private:
MemoryManager& m_memory;
ISearchPath& m_search_path;
Zone& m_zone;
};
} // namespace
namespace T6
{
std::unique_ptr<AssetCreator<AssetCustomMap>> CreateCustomMapLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone)
{
return std::make_unique<CustomMapLoader>(memory, searchPath, zone);
}
} // namespace T6

View File

@@ -0,0 +1,13 @@
#pragma once
#include "Asset/IAssetCreator.h"
#include "Game/T6/T6.h"
#include "SearchPath/ISearchPath.h"
#include "Utils/MemoryManager.h"
#include <memory>
namespace T6
{
std::unique_ptr<AssetCreator<AssetCustomMap>> CreateCustomMapLoader(MemoryManager& memory, ISearchPath& searchPath);
} // namespace T6

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,105 @@
#include "ProjectCreator.h"
class CustomMapLoader
{
void parseMesh(objl::Mesh* OBJMesh, worldSurface* meshSurface, customMapInfo* projInfo)
{
int meshVertexCount = OBJMesh->Vertices.size();
int meshIndexCount = OBJMesh->Indices.size();
int surfVertexStart = projInfo->gfxInfo.vertexCount;
int surfIndexStart = projInfo->gfxInfo.indexCount;
projInfo->gfxInfo.vertexCount += meshVertexCount;
projInfo->gfxInfo.indexCount += meshIndexCount;
projInfo->gfxInfo.vertices = (customMapVertex*)realloc(projInfo->gfxInfo.vertices, sizeof(customMapVertex) * projInfo->gfxInfo.vertexCount);
projInfo->gfxInfo.indices = (uint16_t*)realloc(projInfo->gfxInfo.indices, sizeof(uint16_t) * projInfo->gfxInfo.indexCount);
customMapVertex* surfVertices = &projInfo->gfxInfo.vertices[surfVertexStart];
uint16_t* surfIndices = &projInfo->gfxInfo.indices[surfIndexStart];
meshSurface->firstIndex_Index = surfIndexStart;
meshSurface->firstVertexIndex = surfVertexStart;
meshSurface->triCount = meshIndexCount / 3;
_ASSERT(meshIndexCount % 3 == 0);
for (int i = 0; i < meshIndexCount; i++)
{
_ASSERT(OBJMesh->Indices[i] < UINT16_MAX);
surfIndices[i] = OBJMesh->Indices[i];
}
for (int i = 0; i < meshVertexCount; i++)
{
objl::Vertex* meshVertex = &OBJMesh->Vertices[i];
customMapVertex* surfVertex = &surfVertices[i];
surfVertex->pos.x = meshVertex->Position.X;
surfVertex->pos.y = meshVertex->Position.Y;
surfVertex->pos.z = meshVertex->Position.Z;
surfVertex->color[0] = OBJMesh->MeshMaterial.Kd.X;
surfVertex->color[1] = OBJMesh->MeshMaterial.Kd.Y;
surfVertex->color[2] = OBJMesh->MeshMaterial.Kd.Z;
surfVertex->color[3] = 1.0f;
surfVertex->texCoord[0] = meshVertex->TextureCoordinate.X;
surfVertex->texCoord[1] = meshVertex->TextureCoordinate.Y;
surfVertex->normal.x = meshVertex->Normal.X;
surfVertex->normal.y = meshVertex->Normal.Y;
surfVertex->normal.z = meshVertex->Normal.Z;
// TODO: fix tangents, seems to work for now though
surfVertex->tangent.x = 1.0f;
surfVertex->tangent.y = 0.0f;
surfVertex->tangent.z = 0.0f;
surfVertex->packedLmapCoord = 0;
surfVertex->binormalSign = 0.0f;
}
}
customMapInfo* createCustomMapInfo(std::string& projectName, ISearchPath& searchPath)
{
std::string objFilePath = searchPath.GetPath() + "custom_map/world.obj";
objl::Loader OBJloader;
bool isLoaded = OBJloader.LoadFile(objFilePath);
if (!isLoaded)
{
printf("OBJLoader: unable to load obj file %s\n", objFilePath.c_str());
return NULL;
}
customMapInfo* projInfo = new customMapInfo;
projInfo->name = projectName;
projInfo->bspName = "maps/mp/" + projectName + ".d3dbsp";
projInfo->gfxInfo.surfaceCount = OBJloader.LoadedMeshes.size();
projInfo->gfxInfo.surfaces = new worldSurface[projInfo->gfxInfo.surfaceCount];
projInfo->gfxInfo.vertexCount = 0;
projInfo->gfxInfo.indexCount = 0;
projInfo->gfxInfo.vertices = (customMapVertex*)malloc(1);
projInfo->gfxInfo.indices = (uint16_t*)malloc(1);
for (int i = 0; i < projInfo->gfxInfo.surfaceCount; i++)
{
objl::Mesh* currMesh = &OBJloader.LoadedMeshes[i];
worldSurface* currSurface = &projInfo->gfxInfo.surfaces[i];
currSurface->materialName = currMesh->MeshMaterial.name;
currSurface->reflectionProbeIndex = 0;
currSurface->primaryLightIndex = 0;
currSurface->lightmapIndex = 0;
currSurface->flags = 0;
parseMesh(currMesh, currSurface, projInfo);
}
return projInfo;
}
};

View File

@@ -0,0 +1,13 @@
#include "SearchPath/ISearchPath.h"
#include "Utils/MemoryManager.h"
#include "Game/T6/T6.h"
using namespace T6;
#include "OBJ_Loader.h"
class CustomMapInfo
{
public:
static void parseMesh(objl::Mesh* OBJMesh, worldSurface* meshSurface, customMapInfo* projInfo);
static customMapInfo* createCustomMapInfo(std::string& projectName, ISearchPath& searchPath);
};

View File

@@ -0,0 +1,73 @@
#include "Game/T6/T6.h"
using namespace T6;
class CMUtil
{
public:
// BO2 uses a different coordinate system, so this converts it back from OpenGLs default
static vec3_t convertToBO2Coords(vec3_t OGL_coordinate)
{
vec3_t result;
result.x = OGL_coordinate.x;
result.y = -OGL_coordinate.z;
result.z = OGL_coordinate.y;
return result;
}
// BO2 uses a weird coordinate system, so this converts it to OpenGLs default
static vec3_t convertFromBO2Coords(vec3_t bo2_coordinate)
{
vec3_t result;
result.x = bo2_coordinate.x;
result.y = bo2_coordinate.z;
result.z = -bo2_coordinate.y;
return result;
}
static void calcNewBounds(vec3_t* newmins, vec3_t* newmaxs, vec3_t* currmins, vec3_t* currmaxs)
{
if (currmins->x > newmins->x)
currmins->x = newmins->x;
if (newmaxs->x > currmaxs->x)
currmaxs->x = newmaxs->x;
if (currmins->y > newmins->y)
currmins->y = newmins->y;
if (newmaxs->y > currmaxs->y)
currmaxs->y = newmaxs->y;
if (currmins->z > newmins->z)
currmins->z = newmins->z;
if (newmaxs->z > currmaxs->z)
currmaxs->z = newmaxs->z;
}
static void calcNewBoundsWithPoint(vec3_t* point, vec3_t* currmins, vec3_t* currmaxs)
{
if (currmins->x > point->x)
currmins->x = point->x;
if (point->x > currmaxs->x)
currmaxs->x = point->x;
if (currmins->y > point->y)
currmins->y = point->y;
if (point->y > currmaxs->y)
currmaxs->y = point->y;
if (currmins->z > point->z)
currmins->z = point->z;
if (point->z > currmaxs->z)
currmaxs->z = point->z;
}
static int allignBy128(int size)
{
return ((size + 127) & 0xFFFFFF80);
}
};

View File

@@ -40,6 +40,7 @@
#include "Weapon/RawLoaderWeaponT6.h" #include "Weapon/RawLoaderWeaponT6.h"
#include "ZBarrier/GdtLoaderZBarrierT6.h" #include "ZBarrier/GdtLoaderZBarrierT6.h"
#include "ZBarrier/RawLoaderZBarrierT6.h" #include "ZBarrier/RawLoaderZBarrierT6.h"
#include "CustomMap/LoaderCustomMapT6.h"
#include <format> #include <format>
#include <memory> #include <memory>
@@ -334,6 +335,7 @@ namespace T6
collection.AddDefaultAssetCreator(std::make_unique<DefaultAssetCreator<AssetFootstepTable>>(memory)); collection.AddDefaultAssetCreator(std::make_unique<DefaultAssetCreator<AssetFootstepTable>>(memory));
collection.AddDefaultAssetCreator(std::make_unique<DefaultAssetCreator<AssetFootstepFxTable>>(memory)); collection.AddDefaultAssetCreator(std::make_unique<DefaultAssetCreator<AssetFootstepFxTable>>(memory));
collection.AddDefaultAssetCreator(std::make_unique<DefaultAssetCreator<AssetZBarrier>>(memory)); collection.AddDefaultAssetCreator(std::make_unique<DefaultAssetCreator<AssetZBarrier>>(memory));
// custom maps have no default
} }
void ConfigureGlobalAssetPoolsLoaders(AssetCreatorCollection& collection, Zone& zone) void ConfigureGlobalAssetPoolsLoaders(AssetCreatorCollection& collection, Zone& zone)
@@ -386,6 +388,7 @@ namespace T6
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetFootstepTable>>(zone)); collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetFootstepTable>>(zone));
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetFootstepFxTable>>(zone)); collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetFootstepFxTable>>(zone));
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetZBarrier>>(zone)); collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetZBarrier>>(zone));
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetCustomMap>>(zone));
} }
void ConfigureLoaders(AssetCreatorCollection& collection, Zone& zone, ISearchPath& searchPath, IGdtQueryable& gdt) void ConfigureLoaders(AssetCreatorCollection& collection, Zone& zone, ISearchPath& searchPath, IGdtQueryable& gdt)
@@ -446,6 +449,8 @@ namespace T6
// collection.AddAssetCreator(std::make_unique<AssetLoaderFootstepFxTable>(memory)); // collection.AddAssetCreator(std::make_unique<AssetLoaderFootstepFxTable>(memory));
collection.AddAssetCreator(CreateRawZBarrierLoader(memory, searchPath, zone)); collection.AddAssetCreator(CreateRawZBarrierLoader(memory, searchPath, zone));
collection.AddAssetCreator(CreateGdtZBarrierLoader(memory, searchPath, gdt, zone)); collection.AddAssetCreator(CreateGdtZBarrierLoader(memory, searchPath, gdt, zone));
collection.AddAssetCreator(CreateCustomMapLoader(memory, searchPath));
} }
} // namespace } // namespace

View File

@@ -9,6 +9,8 @@ AssetNameResolver::AssetNameResolver()
{ {
for (auto assetType = 0; assetType < ASSET_TYPE_COUNT; assetType++) for (auto assetType = 0; assetType < ASSET_TYPE_COUNT; assetType++)
AddAssetTypeName(assetType, *GameAssetPoolT6::AssetTypeNameByType(assetType)); AddAssetTypeName(assetType, *GameAssetPoolT6::AssetTypeNameByType(assetType));
AddAssetTypeName(ASSET_TYPE_CUSTOM_MAP, "custom_map");
} }
GameId AssetNameResolver::GetGameId() const GameId AssetNameResolver::GetGameId() const