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

all known bugs fixed and loaded maps work correctly now.

This commit is contained in:
LJW-Dev
2025-08-28 23:34:06 +08:00
parent 9941d02bcc
commit a825bf965b
16 changed files with 40189 additions and 356 deletions

View File

@@ -159,7 +159,6 @@ namespace T6
AUFT_NUM_FIELD_TYPES, AUFT_NUM_FIELD_TYPES,
}; };
struct customMapVertex struct customMapVertex
{ {
vec3_t pos; vec3_t pos;
@@ -171,13 +170,27 @@ namespace T6
unsigned int packedLmapCoord; unsigned int packedLmapCoord;
}; };
enum CM_MATERIAL_TYPE
{
NO_COLOUR_OR_TEXTURE,
CM_MATERIAL_COLOUR,
CM_MATERIAL_TEXTURE
};
struct customMapMaterial
{
CM_MATERIAL_TYPE materialType;
const char* materialName;
};
struct worldSurface struct worldSurface
{ {
char flags; char flags;
char lightmapIndex; customMapMaterial material;
std::string materialName;
char primaryLightIndex; //char lightmapIndex;
char reflectionProbeIndex; //char primaryLightIndex;
//char reflectionProbeIndex;
int triCount; int triCount;
int firstVertexIndex; int firstVertexIndex;
@@ -196,12 +209,49 @@ namespace T6
worldSurface* surfaces; worldSurface* surfaces;
}; };
struct customMapColVertex
{
vec3_t pos;
};
struct collisionSurface
{
int triCount;
int firstVertexIndex;
int firstIndex_Index;
};
struct customMapCol
{
int vertexCount;
customMapColVertex* vertices;
int indexCount;
uint16_t* indices;
int surfaceCount;
collisionSurface* surfaces;
};
struct customMapModel
{
std::string name;
vec3_t origin;
vec3_t rotation; // euler rotation in degrees
float scale;
};
struct customMapInfo struct customMapInfo
{ {
std::string name; std::string name;
std::string bspName; std::string bspName;
customMapGfx gfxInfo; customMapGfx gfxInfo;
customMapGfx colInfo;
size_t modelCount;
customMapModel* models;
}; };
using AssetPhysPreset = Asset<ASSET_TYPE_PHYSPRESET, PhysPreset>; using AssetPhysPreset = Asset<ASSET_TYPE_PHYSPRESET, PhysPreset>;

View File

@@ -16,7 +16,7 @@ Precalculated, evenly sized BSPs are much more efficient and smaller compared to
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#define MAX_AABB_SIZE 512 // maximum size an AABB tree can be before it becomes a node #include "CustomMapConsts.h"
enum PlaneAxis enum PlaneAxis
{ {

View File

@@ -10,14 +10,37 @@
// material flags determine the features of the surface // material flags determine the features of the surface
// unsure which flag type changes what right now // 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: no running, water splashes all the time, low friction, slanted angles make you slide very fast
// 1 results in: normal surface features, grenades work, // 1 results in: normal surface features, grenades work, seems normal
#define MATERIAL_SURFACE_FLAGS 1 #define MATERIAL_SURFACE_FLAGS 1
#define MATERIAL_CONTENT_FLAGS 1 #define MATERIAL_CONTENT_FLAGS 1
// terrain flags: does not change the type of terrain or what features they have // terrain/world 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 // from testing, as long at it isn't 0 things will work correctly
#define LEAF_TERRAIN_CONTENTS 1 // match all flags #define LEAF_TERRAIN_CONTENTS 1
#define WORLD_TERRAIN_CONTENTS 1 // match all flags #define WORLD_TERRAIN_CONTENTS 1
// const std::string defaultMaterialName = "wpc/intro_wall_plaster_light_blue_01"; const std::string missingMaterialName = "custom/missing_material";
const std::string defaultMaterialName = "wpc/dig_plastic_blue"; const std::string colourOnlyMaterialName = "custom/colour_only_material";
const std::string templateMaterialName = "custom/material_template";
#define DEFAULT_SURFACE_LIGHT 1
#define DEFAULT_SURFACE_LIGHTMAP 0
#define DEFAULT_SURFACE_REFLECTION_PROBE 0
#define MAX_AABB_SIZE 512 // maximum size a BSP node can be before it becomes a leaf
// do not change
#define MAX_COL_VERTS (UINT16_MAX - 1) // max amount of verts a map can have
// do not change
#define DEFAULT_LIGHT_INDEX 0
#define SUN_LIGHT_INDEX 1
// do not change
#define SMODEL_FLAG_NO_SHADOW 1
#define SMODEL_FLAG_IS_LIT 2
#define DEFAULT_SMODEL_CULL_DIST 10000.0f
#define DEFAULT_SMODEL_FLAGS SMODEL_FLAG_NO_SHADOW
#define DEFAULT_SMODEL_LIGHT 1
#define DEFAULT_SMODEL_REFLECTION_PROBE 0

View File

@@ -1,9 +1,11 @@
#pragma once #pragma once
#include "BinarySpacePartitionTreePreCalc.h" #include "BinarySpacePartitionTreePreCalc.h"
#include "TriangleSort.h"
#include "CustomMapConsts.h" #include "CustomMapConsts.h"
#include "Util.h" #include "Util.h"
#include "Utils/Pack.h" #include "Utils/Pack.h"
#include <float.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
using json = nlohmann::json; using json = nlohmann::json;
@@ -68,6 +70,7 @@ private:
GfxPackedWorldVertex* GfxVertex = &vertexBuffer[i]; GfxPackedWorldVertex* GfxVertex = &vertexBuffer[i];
GfxVertex->xyz = CMUtil::convertToBO2Coords(WorldVertex->pos); GfxVertex->xyz = CMUtil::convertToBO2Coords(WorldVertex->pos);
//GfxVertex->xyz = WorldVertex->pos;
GfxVertex->binormalSign = WorldVertex->binormalSign; GfxVertex->binormalSign = WorldVertex->binormalSign;
@@ -76,8 +79,10 @@ private:
GfxVertex->texCoord.packed = pack32::Vec2PackTexCoordsUV(WorldVertex->texCoord); GfxVertex->texCoord.packed = pack32::Vec2PackTexCoordsUV(WorldVertex->texCoord);
GfxVertex->normal.packed = pack32::Vec3PackUnitVecThirdBased(CMUtil::convertToBO2Coords(WorldVertex->normal).v); GfxVertex->normal.packed = pack32::Vec3PackUnitVecThirdBased(CMUtil::convertToBO2Coords(WorldVertex->normal).v);
//GfxVertex->normal.packed = pack32::Vec3PackUnitVecThirdBased(WorldVertex->normal.v);
GfxVertex->tangent.packed = pack32::Vec3PackUnitVecThirdBased(CMUtil::convertToBO2Coords(WorldVertex->tangent).v); GfxVertex->tangent.packed = pack32::Vec3PackUnitVecThirdBased(CMUtil::convertToBO2Coords(WorldVertex->tangent).v);
//GfxVertex->tangent.packed = pack32::Vec3PackUnitVecThirdBased(WorldVertex->tangent.v);
GfxVertex->lmapCoord.packed = WorldVertex->packedLmapCoord; GfxVertex->lmapCoord.packed = WorldVertex->packedLmapCoord;
} }
@@ -125,14 +130,14 @@ private:
gfxWorld->surfaceCount = surfaceCount; gfxWorld->surfaceCount = surfaceCount;
gfxWorld->dpvs.staticSurfaceCount = surfaceCount; gfxWorld->dpvs.staticSurfaceCount = surfaceCount;
gfxWorld->dpvs.surfaces = new GfxSurface[surfaceCount]; gfxWorld->dpvs.surfaces = new GfxSurface[surfaceCount];
for (int i = 0; i < surfaceCount; i++) for (unsigned int i = 0; i < surfaceCount; i++)
{ {
auto currSurface = &gfxWorld->dpvs.surfaces[i]; auto currSurface = &gfxWorld->dpvs.surfaces[i];
auto objSurface = &projInfo->gfxInfo.surfaces[i]; auto objSurface = &projInfo->gfxInfo.surfaces[i];
currSurface->lightmapIndex = objSurface->lightmapIndex; currSurface->primaryLightIndex = DEFAULT_SURFACE_LIGHT;
currSurface->primaryLightIndex = objSurface->primaryLightIndex; currSurface->lightmapIndex = DEFAULT_SURFACE_LIGHTMAP;
currSurface->reflectionProbeIndex = objSurface->reflectionProbeIndex; currSurface->reflectionProbeIndex = DEFAULT_SURFACE_REFLECTION_PROBE;
currSurface->flags = objSurface->flags; currSurface->flags = objSurface->flags;
currSurface->tris.triCount = objSurface->triCount; currSurface->tris.triCount = objSurface->triCount;
@@ -141,12 +146,31 @@ private:
currSurface->tris.vertexDataOffset0 = objSurface->firstVertexIndex * sizeof(GfxPackedWorldVertex); currSurface->tris.vertexDataOffset0 = objSurface->firstVertexIndex * sizeof(GfxPackedWorldVertex);
currSurface->tris.vertexDataOffset1 = 0; currSurface->tris.vertexDataOffset1 = 0;
auto* assetInfo = m_context.LoadDependency<AssetMaterial>(objSurface->materialName); std::string surfMaterialName;
switch (objSurface->material.materialType)
{
case CM_MATERIAL_TEXTURE:
surfMaterialName = objSurface->material.materialName;
break;
case CM_MATERIAL_COLOUR:
case NO_COLOUR_OR_TEXTURE:
surfMaterialName = colourOnlyMaterialName;
break;
default:
_ASSERT(false);
}
auto* assetInfo = m_context.LoadDependency<AssetMaterial>(surfMaterialName);
if (assetInfo == NULL) if (assetInfo == NULL)
{ {
printf("Warning: unable to load surface material %s, replacing with the default texture\n", objSurface->materialName.c_str()); assetInfo = m_context.LoadDependency<AssetMaterial>(missingMaterialName);
assetInfo = m_context.LoadDependency<AssetMaterial>(defaultMaterialName); if (assetInfo == NULL)
_ASSERT(assetInfo != NULL); {
printf("Error: unable to find the default texture!\n");
hasLinkFailed = true;
return;
}
} }
currSurface->material = assetInfo->Asset(); currSurface->material = assetInfo->Asset();
@@ -159,7 +183,8 @@ private:
currSurface->bounds[1].z = firstVert[0].xyz.z; currSurface->bounds[1].z = firstVert[0].xyz.z;
for (int k = 0; k < currSurface->tris.triCount * 3; k++) for (int k = 0; k < currSurface->tris.triCount * 3; k++)
{ {
CMUtil::calcNewBoundsWithPoint(&firstVert[k].xyz, &currSurface->bounds[0], &currSurface->bounds[1]); uint16_t vertIndex = gfxWorld->draw.indices[currSurface->tris.baseIndex + k];
CMUtil::calcNewBoundsWithPoint(&firstVert[vertIndex].xyz, &currSurface->bounds[0], &currSurface->bounds[1]);
} }
// unused values // unused values
@@ -176,7 +201,7 @@ private:
// doesn't seem to matter what order the sorted surfs go in // doesn't seem to matter what order the sorted surfs go in
gfxWorld->dpvs.sortedSurfIndex = new uint16_t[surfaceCount]; gfxWorld->dpvs.sortedSurfIndex = new uint16_t[surfaceCount];
for (int i = 0; i < surfaceCount; i++) for (unsigned int i = 0; i < surfaceCount; i++)
{ {
gfxWorld->dpvs.sortedSurfIndex[i] = i; gfxWorld->dpvs.sortedSurfIndex[i] = i;
} }
@@ -209,51 +234,38 @@ private:
gfxWorld->dpvs.litTransSurfsEnd = surfaceCount; gfxWorld->dpvs.litTransSurfsEnd = surfaceCount;
} }
#define SMODEL_FLAG_NO_SHADOW 1 void overwriteMapSModels(customMapInfo* projInfo, GfxWorld* gfxWorld)
#define SMODEL_FLAG_IS_LIT 2
void overwriteMapSModels(GfxWorld* gfxWorld)
{ {
unsigned int modelCount = 0; unsigned int modelCount = projInfo->modelCount;
gfxWorld->dpvs.smodelCount = modelCount; gfxWorld->dpvs.smodelCount = modelCount;
gfxWorld->dpvs.smodelInsts = new GfxStaticModelInst[modelCount]; gfxWorld->dpvs.smodelInsts = new GfxStaticModelInst[modelCount];
gfxWorld->dpvs.smodelDrawInsts = new GfxStaticModelDrawInst[modelCount]; gfxWorld->dpvs.smodelDrawInsts = new GfxStaticModelDrawInst[modelCount];
/*
for (unsigned int i = 0; i < modelCount; i++) for (unsigned int i = 0; i < modelCount; i++)
{ {
auto currModel = &gfxWorld->dpvs.smodelDrawInsts[i]; auto currModel = &gfxWorld->dpvs.smodelDrawInsts[i];
auto currModelInst = &gfxWorld->dpvs.smodelInsts[i]; auto currModelInst = &gfxWorld->dpvs.smodelInsts[i];
json currModelJs = js["models"][i]; customMapModel* inModel = &projInfo->models[i];
// TODO: load custom xmodels auto xModelAsset = m_context.LoadDependency<AssetXModel>(inModel->name);
std::string modelName = currModelJs["model"];
auto xModelAsset = findAsset(assetPool, ASSET_TYPE_XMODEL, modelName);
if (xModelAsset == NULL) if (xModelAsset == NULL)
{ {
printf("Custom model (%s) not supported!\n", modelName.c_str()); printf("XModel %s not found!\n", inModel->name.c_str());
hasLinkFailed = true; currModel->model = NULL;
return;
} }
currModel->model = (XModel*)xModelAsset->m_ptr; else
currModel->model = (XModel*)xModelAsset->Asset();
currModel->placement.origin.x = currModelJs["origin"]["x"]; currModel->placement.origin.x = inModel->origin.x;
currModel->placement.origin.y = currModelJs["origin"]["y"]; currModel->placement.origin.y = inModel->origin.y;
currModel->placement.origin.z = currModelJs["origin"]["z"]; currModel->placement.origin.z = inModel->origin.z;
currModel->placement.origin = CMUtil::convertToBO2Coords(currModel->placement.origin); currModel->placement.origin = CMUtil::convertToBO2Coords(currModel->placement.origin);
currModel->placement.axis[0].x = currModelJs["forward"]["x"]; currModel->placement.scale = inModel->scale;
currModel->placement.axis[0].y = currModelJs["forward"]["y"];
currModel->placement.axis[0].z = currModelJs["forward"]["z"]; CMUtil::convertAnglesToAxis(&inModel->rotation, currModel->placement.axis);
currModel->placement.axis[1].x = currModelJs["right"]["x"];
currModel->placement.axis[1].y = currModelJs["right"]["y"];
currModel->placement.axis[1].z = currModelJs["right"]["z"];
currModel->placement.axis[2].x = currModelJs["up"]["x"];
currModel->placement.axis[2].y = currModelJs["up"]["y"];
currModel->placement.axis[2].z = currModelJs["up"]["z"];
currModel->placement.scale = currModelJs["scale"];
// mins and maxs are calculated in world space not local space // mins and maxs are calculated in world space not local space
// TODO: mins and maxs are slightly different to what bo2 uses, which results in some wierd culling ingame // TODO: this does not account for model rotation or scale
currModelInst->mins.x = currModel->model->mins.x + currModel->placement.origin.x; currModelInst->mins.x = currModel->model->mins.x + currModel->placement.origin.x;
currModelInst->mins.y = currModel->model->mins.y + currModel->placement.origin.y; currModelInst->mins.y = currModel->model->mins.y + currModel->placement.origin.y;
currModelInst->mins.z = currModel->model->mins.z + currModel->placement.origin.z; currModelInst->mins.z = currModel->model->mins.z + currModel->placement.origin.z;
@@ -261,10 +273,10 @@ private:
currModelInst->maxs.y = currModel->model->maxs.y + currModel->placement.origin.y; currModelInst->maxs.y = currModel->model->maxs.y + currModel->placement.origin.y;
currModelInst->maxs.z = currModel->model->maxs.z + currModel->placement.origin.z; currModelInst->maxs.z = currModel->model->maxs.z + currModel->placement.origin.z;
currModel->cullDist = currModelJs["cullDist"]; currModel->cullDist = DEFAULT_SMODEL_CULL_DIST;
currModel->flags = currModelJs["flags"]; currModel->flags = DEFAULT_SMODEL_FLAGS;
currModel->primaryLightIndex = (unsigned char)currModelJs["primaryLightIndex"]; currModel->primaryLightIndex = DEFAULT_SMODEL_LIGHT;
currModel->reflectionProbeIndex = (unsigned char)currModelJs["reflectionProbeIndex"]; currModel->reflectionProbeIndex = DEFAULT_SMODEL_REFLECTION_PROBE;
// unknown use / unused // unknown use / unused
currModel->smid = i; currModel->smid = i;
@@ -285,7 +297,7 @@ private:
currModel->lmapVertexInfo[3].numLmapVertexColors = 0; currModel->lmapVertexInfo[3].numLmapVertexColors = 0;
currModel->lmapVertexInfo[3].lmapVertexColors = NULL; currModel->lmapVertexInfo[3].lmapVertexColors = NULL;
} }
*/
// all visdata is alligned by 128 // all visdata is alligned by 128
gfxWorld->dpvs.smodelVisDataCount = CMUtil::allignBy128(modelCount); gfxWorld->dpvs.smodelVisDataCount = CMUtil::allignBy128(modelCount);
@@ -399,10 +411,10 @@ private:
{ {
// there must be 2 or more lights, first is the default light and second is the sun // there must be 2 or more lights, first is the default light and second is the sun
gfxWorld->primaryLightCount = 2; gfxWorld->primaryLightCount = 2;
gfxWorld->sunPrimaryLightIndex = 1; // the sun is always index 1 gfxWorld->sunPrimaryLightIndex = SUN_LIGHT_INDEX; // the sun is always index 1
gfxWorld->shadowGeom = new GfxShadowGeometry[gfxWorld->primaryLightCount]; gfxWorld->shadowGeom = new GfxShadowGeometry[gfxWorld->primaryLightCount];
for (int i = 0; i < gfxWorld->primaryLightCount; i++) for (unsigned int i = 0; i < gfxWorld->primaryLightCount; i++)
{ {
gfxWorld->shadowGeom[i].smodelCount = 0; gfxWorld->shadowGeom[i].smodelCount = 0;
gfxWorld->shadowGeom[i].surfaceCount = 0; gfxWorld->shadowGeom[i].surfaceCount = 0;
@@ -411,7 +423,7 @@ private:
} }
gfxWorld->lightRegion = new GfxLightRegion[gfxWorld->primaryLightCount]; gfxWorld->lightRegion = new GfxLightRegion[gfxWorld->primaryLightCount];
for (int i = 0; i < gfxWorld->primaryLightCount; i++) for (unsigned int i = 0; i < gfxWorld->primaryLightCount; i++)
{ {
gfxWorld->lightRegion[i].hullCount = 0; gfxWorld->lightRegion[i].hullCount = 0;
gfxWorld->lightRegion[i].hulls = NULL; gfxWorld->lightRegion[i].hulls = NULL;
@@ -440,7 +452,7 @@ private:
gfxWorld->lightGrid.rowAxis = 0; // default value gfxWorld->lightGrid.rowAxis = 0; // default value
gfxWorld->lightGrid.colAxis = 1; // default value gfxWorld->lightGrid.colAxis = 1; // default value
gfxWorld->lightGrid.sunPrimaryLightIndex = 1; // the sun is always index 1 gfxWorld->lightGrid.sunPrimaryLightIndex = SUN_LIGHT_INDEX; // the sun is always index 1
gfxWorld->lightGrid.offset = 0.0f; gfxWorld->lightGrid.offset = 0.0f;
// if rowDataStart's first value is -1, it will not look up any lightmap data // if rowDataStart's first value is -1, it will not look up any lightmap data
@@ -518,9 +530,11 @@ private:
// Nodes mnode_t.cellIndex indexes gfxWorld->cells // Nodes mnode_t.cellIndex indexes gfxWorld->cells
// and (mnode_t.cellIndex - (world->dpvsPlanes.cellCount + 1) indexes world->dpvsPlanes.planes // and (mnode_t.cellIndex - (world->dpvsPlanes.cellCount + 1) indexes world->dpvsPlanes.planes
gfxWorld->nodeCount = 1; gfxWorld->nodeCount = 1;
gfxWorld->planeCount = 0;
gfxWorld->dpvsPlanes.nodes = new uint16_t[gfxWorld->nodeCount]; gfxWorld->dpvsPlanes.nodes = new uint16_t[gfxWorld->nodeCount];
gfxWorld->dpvsPlanes.nodes[0] = 1; // nodes reference cells by index + 1 gfxWorld->dpvsPlanes.nodes[0] = 1; // nodes reference cells by index + 1
// planes are overwritten by the clipmap loading code ingame
gfxWorld->planeCount = 0;
gfxWorld->dpvsPlanes.planes = NULL; gfxWorld->dpvsPlanes.planes = NULL;
} }
@@ -541,10 +555,13 @@ private:
void overwriteModels(GfxWorld* gfxWorld) void overwriteModels(GfxWorld* gfxWorld)
{ {
// these models are the collision for the entities defined in the mapents asset
// used for triggers and stuff
gfxWorld->modelCount = 1; gfxWorld->modelCount = 1;
gfxWorld->models = new GfxBrushModel[gfxWorld->modelCount]; gfxWorld->models = new GfxBrushModel[gfxWorld->modelCount];
// first model is the world model, the world json doesn't include it so its added here // first model is always the world model
gfxWorld->models[0].startSurfIndex = 0; gfxWorld->models[0].startSurfIndex = 0;
gfxWorld->models[0].surfaceCount = gfxWorld->surfaceCount; gfxWorld->models[0].surfaceCount = gfxWorld->surfaceCount;
gfxWorld->models[0].bounds[0].x = gfxWorld->mins.x; gfxWorld->models[0].bounds[0].x = gfxWorld->mins.x;
@@ -576,7 +593,7 @@ private:
void updateSunData(GfxWorld* gfxWorld) void updateSunData(GfxWorld* gfxWorld)
{ {
// default values taken from mp_dig // default values taken from mp_dig
gfxWorld->sunParse.fogTransitionTime = 0.001; gfxWorld->sunParse.fogTransitionTime = (float)0.001;
gfxWorld->sunParse.name[0] = 0x00; gfxWorld->sunParse.name[0] = 0x00;
gfxWorld->sunParse.initWorldSun->control = 0; gfxWorld->sunParse.initWorldSun->control = 0;
@@ -783,7 +800,7 @@ private:
overwriteMapSurfaces(projInfo, gfxWorld); overwriteMapSurfaces(projInfo, gfxWorld);
overwriteMapSModels(gfxWorld); overwriteMapSModels(projInfo, gfxWorld);
overwriteLightmapData(gfxWorld); overwriteLightmapData(gfxWorld);
@@ -813,6 +830,49 @@ private:
m_context.AddAsset<AssetGfxWorld>(gfxWorld->name, gfxWorld); m_context.AddAsset<AssetGfxWorld>(gfxWorld->name, gfxWorld);
} }
void addXModelsToCollision(customMapInfo* projInfo, clipMap_t* clipMap)
{
auto gfxWorldAsset = m_context.LoadDependency<AssetGfxWorld>(projInfo->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;
CMUtil::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) void aabbCalcOriginAndHalfSize(vec3_t* mins, vec3_t* maxs, vec3_t* out_origin, vec3_t* out_halfSize)
{ {
// Origin is the midpoint: (min + max) / 2 // Origin is the midpoint: (min + max) / 2
@@ -841,14 +901,6 @@ private:
return (edgeBitMask & clipMap->triEdgeIsWalkable[(triIndex + edgeIndex + 2 * triIndex) >> 3]) != 0; return (edgeBitMask & clipMap->triEdgeIsWalkable[(triIndex + edgeIndex + 2 * triIndex) >> 3]) != 0;
} }
float distBetweenPoints(vec3_t p1, vec3_t p2)
{
float x = p2.x - p1.x;
float y = p2.y - p1.y;
float z = p2.z - p1.z;
return sqrtf((x * x) + (y * y) + (z * z));
}
void traverseBSPTreeForCounts(BSPTree* node, int* numPlanes, int* numNodes, int* numLeafs, int* numAABBTrees, int* maxObjsPerLeaf) void traverseBSPTreeForCounts(BSPTree* node, int* numPlanes, int* numNodes, int* numLeafs, int* numAABBTrees, int* maxObjsPerLeaf)
{ {
if (node->isLeaf) if (node->isLeaf)
@@ -920,7 +972,6 @@ private:
} }
} }
} }
// create root AABB node
CollisionAabbTree* rootAABB = &clipMap->aabbTrees[firstAABBIndex]; CollisionAabbTree* rootAABB = &clipMap->aabbTrees[firstAABBIndex];
aabbCalcOriginAndHalfSize(&aabbMins, &aabbMaxs, &rootAABB->origin, &rootAABB->halfSize); aabbCalcOriginAndHalfSize(&aabbMins, &aabbMaxs, &rootAABB->origin, &rootAABB->halfSize);
rootAABB->materialIndex = 0; rootAABB->materialIndex = 0;
@@ -1015,7 +1066,7 @@ private:
// converting OGL -> BO2 X coords doesn't change the x coords at all, so // converting OGL -> BO2 X coords doesn't change the x coords at all, so
// the dist stays the same // the dist stays the same
currPlane->dist = node->u.node->distance; currPlane->dist = (float)node->u.node->distance;
} }
else else
{ {
@@ -1027,7 +1078,7 @@ private:
// converting OGL -> BO2 Z coords negates the z coords and sets it to the y coord. // 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 // just negate it here as it is just the distance from the orgin along the axis
currPlane->dist = -node->u.node->distance; currPlane->dist = (float)(-node->u.node->distance);
} }
bool foundType = false; bool foundType = false;
@@ -1127,6 +1178,118 @@ private:
_ASSERT(clipMap->aabbTreeCount == currAABBCount); _ASSERT(clipMap->aabbTreeCount == currAABBCount);
} }
void createPartitions(customMapInfo* projInfo, clipMap_t* clipMap)
{
int collisionVertexCount = projInfo->colInfo.vertexCount;
std::vector<vec3_t> collisionVertVec;
for (int i = 0; i < collisionVertexCount; i++)
{
collisionVertVec.push_back(CMUtil::convertToBO2Coords(projInfo->colInfo.vertices[i].pos));
//collisionVertVec.push_back(projInfo->colInfo.vertices[i].pos);
}
clipMap->vertCount = collisionVertexCount;
clipMap->verts = new 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 > MAX_COL_VERTS)
{
printf("ERROR: collision vertex count %i exceeds the maximum number: %i!\n", collisionVertexCount, MAX_COL_VERTS);
hasLinkFailed = true;
return;
}
std::vector<uint16_t> triIndexVec;
for (int i = 0; i < projInfo->colInfo.surfaceCount; i++)
{
worldSurface* currSurface = &projInfo->colInfo.surfaces[i];
int triCount = currSurface->triCount;
for (int k = 0; k < triCount * 3; k += 3)
{
int firstIndex_Index = currSurface->firstIndex_Index;
int firstVertexIndex = currSurface->firstVertexIndex;
// 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 = projInfo->colInfo.indices[firstIndex_Index + (k + 0)] + firstVertexIndex;
int triIndex1 = projInfo->colInfo.indices[firstIndex_Index + (k + 1)] + firstVertexIndex;
int triIndex2 = projInfo->colInfo.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);
clipMap->triCount = triIndexVec.size() / 3;
clipMap->triIndices = (uint16_t(*)[3])(new 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 (int i = 0; i < projInfo->colInfo.surfaceCount; i++)
{
int triCount = projInfo->colInfo.surfaces[i].triCount;
int firstTriIndex = projInfo->colInfo.surfaces[i].firstIndex_Index / 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 = new 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 = new uint16_t[totalUindCount];
memcpy(clipMap->info.uinds, &uindVec[0], sizeof(uint16_t) * totalUindCount);
}
void createClipMap(customMapInfo* projInfo) void createClipMap(customMapInfo* projInfo)
{ {
clipMap_t* clipMap = new clipMap_t; clipMap_t* clipMap = new clipMap_t;
@@ -1148,12 +1311,21 @@ private:
clipMap->box_model.maxs.z = 0.0f; clipMap->box_model.maxs.z = 0.0f;
clipMap->box_model.radius = 0.0f; clipMap->box_model.radius = 0.0f;
clipMap->box_model.info = NULL; clipMap->box_model.info = NULL;
clipMap->box_model.leaf.mins.x = 3.4028235e38;
clipMap->box_model.leaf.mins.y = 3.4028235e38; // for some reason the maxs are negative, and mins are positive
clipMap->box_model.leaf.mins.z = 3.4028235e38; // float box_mins = 3.4028235e38;
clipMap->box_model.leaf.maxs.x = -3.4028235e38; // for some reason the maxs are negative, and mins are positive // float box_maxs = -3.4028235e38;
clipMap->box_model.leaf.maxs.y = -3.4028235e38; // hack: the floats above can't be converted to 32 bit floats, and the game requires them to be exact
clipMap->box_model.leaf.maxs.z = -3.4028235e38; // so we use the hex representation and set it using int pointers
unsigned int box_mins = 0x7F7FFFFF;
unsigned int box_maxs = 0xFF7FFFFF;
*((unsigned int*)&clipMap->box_model.leaf.mins.x) = box_mins;
*((unsigned int*)&clipMap->box_model.leaf.mins.y) = box_mins;
*((unsigned int*)&clipMap->box_model.leaf.mins.z) = box_mins;
*((unsigned int*)&clipMap->box_model.leaf.maxs.x) = box_maxs;
*((unsigned int*)&clipMap->box_model.leaf.maxs.y) = box_maxs;
*((unsigned int*)&clipMap->box_model.leaf.maxs.z) = box_maxs;
clipMap->box_model.leaf.brushContents = -1; clipMap->box_model.leaf.brushContents = -1;
clipMap->box_model.leaf.terrainContents = 0; clipMap->box_model.leaf.terrainContents = 0;
clipMap->box_model.leaf.cluster = 0; clipMap->box_model.leaf.cluster = 0;
@@ -1250,6 +1422,11 @@ private:
currDef->physConstraints[3] = 0x1FF; currDef->physConstraints[3] = 0x1FF;
} }
// cmodels is the collision for mapents
auto gfxWorldAsset = m_context.LoadDependency<AssetGfxWorld>(projInfo->bspName);
_ASSERT(gfxWorldAsset != NULL);
GfxWorld* gfxWorld = gfxWorldAsset->Asset();
_ASSERT(gfxWorld->modelCount == 1);
clipMap->numSubModels = 1; clipMap->numSubModels = 1;
clipMap->cmodels = new cmodel_t[clipMap->numSubModels]; clipMap->cmodels = new cmodel_t[clipMap->numSubModels];
clipMap->cmodels[0].leaf.firstCollAabbIndex = 0; clipMap->cmodels[0].leaf.firstCollAabbIndex = 0;
@@ -1265,166 +1442,38 @@ private:
clipMap->cmodels[0].leaf.leafBrushNode = 0; clipMap->cmodels[0].leaf.leafBrushNode = 0;
clipMap->cmodels[0].leaf.cluster = 0; clipMap->cmodels[0].leaf.cluster = 0;
clipMap->cmodels[0].info = NULL; clipMap->cmodels[0].info = NULL;
auto gfxWorldAsset = m_context.LoadDependency<AssetGfxWorld>(projInfo->bspName);
_ASSERT(gfxWorldAsset != NULL);
GfxWorld* gfxWorld = gfxWorldAsset->Asset();
clipMap->cmodels[0].mins.x = gfxWorld->models[0].bounds[0].x; clipMap->cmodels[0].mins.x = gfxWorld->models[0].bounds[0].x;
clipMap->cmodels[0].mins.y = gfxWorld->models[0].bounds[0].y; clipMap->cmodels[0].mins.y = gfxWorld->models[0].bounds[0].y;
clipMap->cmodels[0].mins.z = gfxWorld->models[0].bounds[0].z; clipMap->cmodels[0].mins.z = gfxWorld->models[0].bounds[0].z;
clipMap->cmodels[0].maxs.x = gfxWorld->models[0].bounds[1].x; clipMap->cmodels[0].maxs.x = gfxWorld->models[0].bounds[1].x;
clipMap->cmodels[0].maxs.y = gfxWorld->models[0].bounds[1].y; clipMap->cmodels[0].maxs.y = gfxWorld->models[0].bounds[1].y;
clipMap->cmodels[0].maxs.z = gfxWorld->models[0].bounds[1].z; clipMap->cmodels[0].maxs.z = gfxWorld->models[0].bounds[1].z;
clipMap->cmodels[0].radius = distBetweenPoints(clipMap->cmodels[0].mins, clipMap->cmodels[0].maxs) / 2; clipMap->cmodels[0].radius = CMUtil::distBetweenPoints(clipMap->cmodels[0].mins, clipMap->cmodels[0].maxs) / 2;
clipMap->numStaticModels = 0;
clipMap->staticModelList = NULL; addXModelsToCollision(projInfo, clipMap);
clipMap->info.numMaterials = 1; clipMap->info.numMaterials = 1;
clipMap->info.materials = new ClipMaterial[clipMap->info.numMaterials]; clipMap->info.materials = new ClipMaterial[clipMap->info.numMaterials];
clipMap->info.materials[0].name = _strdup(defaultMaterialName.c_str()); clipMap->info.materials[0].name = _strdup(missingMaterialName.c_str());
clipMap->info.materials[0].contentFlags = MATERIAL_CONTENT_FLAGS; clipMap->info.materials[0].contentFlags = MATERIAL_CONTENT_FLAGS;
clipMap->info.materials[0].surfaceFlags = MATERIAL_SURFACE_FLAGS; clipMap->info.materials[0].surfaceFlags = MATERIAL_SURFACE_FLAGS;
int collisionVertexCount = projInfo->gfxInfo.vertexCount;
vec3_t* convertedCollisionVerts = new vec3_t[collisionVertexCount];
for (int i = 0; i < collisionVertexCount; i++)
{
convertedCollisionVerts[i] = CMUtil::convertToBO2Coords(projInfo->gfxInfo.vertices[i].pos);
}
clipMap->vertCount = collisionVertexCount;
clipMap->verts = convertedCollisionVerts;
// 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
const int maxVerts = UINT16_MAX - 1;
if (collisionVertexCount > maxVerts)
{
printf("ERROR: collision vertex count %i exceeds the maximum number: %i!\n", collisionVertexCount, maxVerts);
hasLinkFailed = true;
return;
}
clipMap->triCount = projInfo->gfxInfo.indexCount / 3;
clipMap->triIndices = (uint16_t(*)[3])(new uint16_t[projInfo->gfxInfo.indexCount]); // 3 indexes per tri
int triIndex = 0;
for (int i = 0; i < projInfo->gfxInfo.indexCount; i += 3)
{
clipMap->triIndices[triIndex][2] = projInfo->gfxInfo.indices[i + 0];
clipMap->triIndices[triIndex][1] = projInfo->gfxInfo.indices[i + 1];
clipMap->triIndices[triIndex][0] = projInfo->gfxInfo.indices[i + 2];
triIndex++;
}
int partitionCount = 0;
for (int i = 0; i < projInfo->gfxInfo.surfaceCount; i++)
{
int triCount = projInfo->gfxInfo.surfaces[i].triCount;
int subdividedSize = triCount / 16; // integer division used
partitionCount += subdividedSize;
// add another partition if the triangle count doesn't add up to a multiple of 16
if (triCount % 16 != 0)
partitionCount++;
}
clipMap->partitionCount = partitionCount;
clipMap->partitions = new CollisionPartition[clipMap->partitionCount];
int partitionIndex = 0;
for (int i = 0; i < projInfo->gfxInfo.surfaceCount; i++)
{
int triCount = projInfo->gfxInfo.surfaces[i].triCount;
int firstIndex = projInfo->gfxInfo.surfaces[i].firstIndex_Index;
int firstVertex = projInfo->gfxInfo.surfaces[i].firstVertexIndex;
int firstTri = firstIndex / 3;
////////////////////////////////////////////////////////////////////////////////////////////////////
// TODO: fixup tri indices does not fully work correctly, and leaves some gaps or removes faces.
// NEED TO FIX
// fixup the tri indices.
// GfxWorld calculates what vertex to render through vertexBuffer[indexBuffer[index] + firstVertex]
// while clipmap caclulates it through vertexBuffer[indexBuffer[index]]
// this loop fixes the index buffer by adding the firstVertex to each index
for (int k = 0; k < triCount; k++)
{
clipMap->triIndices[k + firstTri][0] += firstVertex;
clipMap->triIndices[k + firstTri][1] += firstVertex;
clipMap->triIndices[k + firstTri][2] += firstVertex;
}
while (triCount > 0)
{
if (triCount > 16)
clipMap->partitions[partitionIndex].triCount = 16;
else
clipMap->partitions[partitionIndex].triCount = triCount;
clipMap->partitions[partitionIndex].firstTri = firstTri;
triCount -= 16;
firstIndex += 16 * 3;
partitionIndex++;
}
}
int uindCount = 0;
clipMap->info.uinds = new uint16_t[3 * 16 * clipMap->partitionCount]; // 3 vertexes per tri, and max 16 tris per partition
for (int i = 0; i < clipMap->partitionCount; i++)
{
auto currPartition = &clipMap->partitions[i];
int uniqueVertBufferCount = 0;
uint16_t uniqueVertBuffer[3 * 16]; // 3 vertexes per tri, and max 16 tris
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 (int m = 0; m < uniqueVertBufferCount; m++)
{
if (uniqueVertBuffer[m] == vertIndex)
{
isVertexIndexUnique = false;
break;
}
}
if (isVertexIndexUnique)
{
uniqueVertBuffer[uniqueVertBufferCount] = vertIndex;
uniqueVertBufferCount++;
}
}
}
_ASSERT(uniqueVertBufferCount <= 3 * 16);
currPartition->fuind = uindCount;
currPartition->nuinds = uniqueVertBufferCount;
memcpy(&clipMap->info.uinds[uindCount], uniqueVertBuffer, uniqueVertBufferCount * sizeof(uint16_t));
uindCount += uniqueVertBufferCount;
}
_ASSERT(uindCount < 3 * 16 * clipMap->partitionCount);
clipMap->info.nuinds = uindCount;
// set all edges to walkable (all walkable edge bits are set to 1, see isEdgeWalkable) until changing it is a possiblility // set all edges to walkable (all walkable edge bits are set to 1, see isEdgeWalkable) until changing it is a possiblility
// might do weird stuff on walls // might do weird stuff on walls, but from testing doesnt seem to do much
int walkableEdgeSize = (3 * clipMap->triCount + 31) / 32 * 4; int walkableEdgeSize = (3 * clipMap->triCount + 31) / 32 * 4;
clipMap->triEdgeIsWalkable = new char[walkableEdgeSize]; clipMap->triEdgeIsWalkable = new char[walkableEdgeSize];
memset(clipMap->triEdgeIsWalkable, 0xFF, walkableEdgeSize * sizeof(char)); memset(clipMap->triEdgeIsWalkable, 1, walkableEdgeSize * sizeof(char));
// BSP creation must go last as it depends on unids, tris and verts already being populated // clipmap BSP creation must go last as it depends on unids, tris and verts already being populated
// HACK: // HACK:
// the BSP tree creation does not work when BO2's coordinate system is used for mins and maxs. // 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, // 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 // and then convert them back when it is being parsed into the clipmap. Requires some hacky
// logic, check populateBSPTree_r and addAABBTreeFromLeaf // logic, check populateBSPTree_r and addAABBTreeFromLeaf
createPartitions(projInfo, clipMap);
vec3_t* firstVert = &clipMap->verts[0]; vec3_t* firstVert = &clipMap->verts[0];
vec3_t clipMins; vec3_t clipMins;
vec3_t clipMaxs; vec3_t clipMaxs;
@@ -1436,13 +1485,16 @@ private:
clipMaxs.z = firstVert->z; clipMaxs.z = firstVert->z;
clipMins = CMUtil::convertFromBO2Coords(clipMins); clipMins = CMUtil::convertFromBO2Coords(clipMins);
clipMaxs = CMUtil::convertFromBO2Coords(clipMaxs); clipMaxs = CMUtil::convertFromBO2Coords(clipMaxs);
for (int i = 1; i < clipMap->vertCount; i++) for (unsigned int i = 1; i < clipMap->vertCount; i++)
{ {
vec3_t vertCoord = CMUtil::convertFromBO2Coords(clipMap->verts[i]); vec3_t vertCoord = CMUtil::convertFromBO2Coords(clipMap->verts[i]);
CMUtil::calcNewBoundsWithPoint(&vertCoord, &clipMins, &clipMaxs); CMUtil::calcNewBoundsWithPoint(&vertCoord, &clipMins, &clipMaxs);
} }
BSPTree* tree = new BSPTree(clipMins.x, clipMins.y, clipMins.z, clipMaxs.x, clipMaxs.y, clipMaxs.z, 0); 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++) for (int i = 0; i < clipMap->partitionCount; i++)
{ {
auto currPartition = &clipMap->partitions[i]; auto currPartition = &clipMap->partitions[i];
@@ -1489,20 +1541,20 @@ private:
comWorld->primaryLightCount = 2; comWorld->primaryLightCount = 2;
comWorld->primaryLights = new ComPrimaryLight[comWorld->primaryLightCount]; comWorld->primaryLights = new ComPrimaryLight[comWorld->primaryLightCount];
// default light is always empty
ComPrimaryLight* defaultLight = &comWorld->primaryLights[0]; ComPrimaryLight* defaultLight = &comWorld->primaryLights[0];
memset(defaultLight, 0, sizeof(ComPrimaryLight)); memset(defaultLight, 0, sizeof(ComPrimaryLight));
ComPrimaryLight* sunLight = &comWorld->primaryLights[1]; ComPrimaryLight* sunLight = &comWorld->primaryLights[1];
memset(sunLight, 0, sizeof(ComPrimaryLight)); memset(sunLight, 0, sizeof(ComPrimaryLight));
sunLight->type = 1; sunLight->type = 1;
// Below are default values taken from mp_dig sunLight->diffuseColor.r = 0.75f;
sunLight->diffuseColor.r = 1.0f; sunLight->diffuseColor.g = 0.75f;
sunLight->diffuseColor.g = 1.0f; sunLight->diffuseColor.b = 0.75f;
sunLight->diffuseColor.b = 1.0f; sunLight->diffuseColor.a = 1.0f;
sunLight->diffuseColor.a = 0.0f; sunLight->dir.x = 0.0f;
sunLight->dir.x = -0.2410777360200882; sunLight->dir.y = 0.0f;
sunLight->dir.y = -0.8407384753227234; sunLight->dir.z = 0.0f;
sunLight->dir.z = 0.48480960726737976;
m_context.AddAsset<AssetComWorld>(comWorld->name, comWorld); m_context.AddAsset<AssetComWorld>(comWorld->name, comWorld);
} }
@@ -1616,42 +1668,42 @@ private:
void checkAndAddDefaultRequiredAssets(customMapInfo* projectInfo) void checkAndAddDefaultRequiredAssets(customMapInfo* projectInfo)
{ {
if (m_context.LoadDependency<AssetScript>("maps/mp/mp_dig.gsc") == NULL) if (m_context.LoadDependency<AssetScript>("maps/mp/mod.gsc") == NULL)
{ {
hasLinkFailed = true; hasLinkFailed = true;
return; return;
} }
if (m_context.LoadDependency<AssetScript>("maps/mp/mp_dig_amb.gsc") == NULL) if (m_context.LoadDependency<AssetScript>("maps/mp/mod_amb.gsc") == NULL)
{ {
hasLinkFailed = true; hasLinkFailed = true;
return; return;
} }
if (m_context.LoadDependency<AssetScript>("maps/mp/mp_dig_fx.gsc") == NULL) if (m_context.LoadDependency<AssetScript>("maps/mp/mod_fx.gsc") == NULL)
{ {
hasLinkFailed = true; hasLinkFailed = true;
return; return;
} }
if (m_context.LoadDependency<AssetScript>("maps/mp/createfx/mp_dig_fx.gsc") == NULL) if (m_context.LoadDependency<AssetScript>("maps/mp/createfx/mod_fx.gsc") == NULL)
{ {
hasLinkFailed = true; hasLinkFailed = true;
return; return;
} }
if (m_context.LoadDependency<AssetScript>("clientscripts/mp/mp_dig.csc") == NULL) if (m_context.LoadDependency<AssetScript>("clientscripts/mp/mod.csc") == NULL)
{ {
hasLinkFailed = true; hasLinkFailed = true;
return; return;
} }
if (m_context.LoadDependency<AssetScript>("clientscripts/mp/mp_dig_amb.csc") == NULL) if (m_context.LoadDependency<AssetScript>("clientscripts/mp/mod_amb.csc") == NULL)
{ {
hasLinkFailed = true; hasLinkFailed = true;
return; return;
} }
if (m_context.LoadDependency<AssetScript>("clientscripts/mp/mp_dig_fx.csc") == NULL) if (m_context.LoadDependency<AssetScript>("clientscripts/mp/mod_fx.csc") == NULL)
{ {
hasLinkFailed = true; hasLinkFailed = true;
return; return;
} }
if (m_context.LoadDependency<AssetScript>("clientscripts/mp/createfx/mp_dig_fx.csc") == NULL) if (m_context.LoadDependency<AssetScript>("clientscripts/mp/createfx/mod_fx.csc") == NULL)
{ {
hasLinkFailed = true; hasLinkFailed = true;
return; return;

View File

@@ -22,21 +22,32 @@ namespace
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
{ {
auto mapFile = m_search_path.Open("custom_map/map.obj"); auto mapGfxFile = m_search_path.Open("custom_map/map_gfx.fbx");
if (!mapFile.IsOpen()) if (!mapGfxFile.IsOpen())
return AssetCreationResult::NoAction(); return AssetCreationResult::NoAction();
printf("Loading map data...\n");
// create map info from the obj file // create map info from the obj file
customMapInfo* mapInfo = CustomMapInfo::createCustomMapInfo(m_zone.m_name, m_search_path); customMapInfo* mapInfo = CustomMapInfo::createCustomMapInfo(m_zone.m_name, m_search_path);
if (mapInfo == NULL) if (mapInfo == NULL)
return AssetCreationResult::Failure(); return AssetCreationResult::Failure();
printf("Creating map from data...\n");
// linker will add all the assets needed // linker will add all the assets needed
CustomMapLinker* linker = new CustomMapLinker(m_memory, m_search_path, m_zone, context); CustomMapLinker* linker = new CustomMapLinker(m_memory, m_search_path, m_zone, context);
bool result = linker->linkCustomMap(mapInfo); bool result = linker->linkCustomMap(mapInfo);
//auto gfxWorldAsset = context.LoadDependency<AssetFootstepTable>("default_1st_person");
//return AssetCreationResult::Success(gfxWorldAsset);
if (result) if (result)
return AssetCreationResult::NoAction(); {
auto gfxWorldAsset = context.LoadDependency<AssetGfxWorld>(mapInfo->bspName);
_ASSERT(gfxWorldAsset != NULL);
return AssetCreationResult::Success(gfxWorldAsset);
}
else else
return AssetCreationResult::Failure(); return AssetCreationResult::Failure();
} }

View File

@@ -1,4 +1,5 @@
// OBJ_Loader.h - A Single Header OBJ Model Loader // OBJ_Loader.h - A Single Header OBJ Model Loader
// modified to work better with OpenAssetTools
#pragma once #pragma once
@@ -17,6 +18,8 @@
// Math.h - STD math Library // Math.h - STD math Library
#include <math.h> #include <math.h>
#include "SearchPath/ISearchPath.h"
// Print progress to console while loading (large models) // Print progress to console while loading (large models)
// #define OBJL_CONSOLE_OUTPUT // #define OBJL_CONSOLE_OUTPUT
@@ -432,15 +435,11 @@ namespace objl
// //
// If the file is unable to be found // If the file is unable to be found
// or unable to be loaded return false // or unable to be loaded return false
bool LoadFile(std::string Path) bool LoadFile(ISearchPath& searchPath, std::string fileName)
{ {
// If the file is not an .obj file return false auto file = searchPath.Open(fileName);
if (Path.substr(Path.size() - 4, 4) != ".obj")
return false;
std::ifstream file(Path); if (!file.IsOpen())
if (!file.is_open())
return false; return false;
LoadedMeshes.clear(); LoadedMeshes.clear();
@@ -467,7 +466,7 @@ namespace objl
#endif #endif
std::string curline; std::string curline;
while (std::getline(file, curline)) while (std::getline(*file.m_stream, curline))
{ {
#ifdef OBJL_CONSOLE_OUTPUT #ifdef OBJL_CONSOLE_OUTPUT
if ((outputIndicator = ((outputIndicator + 1) % outputEveryNth)) == 1) if ((outputIndicator = ((outputIndicator + 1) % outputEveryNth)) == 1)
@@ -642,13 +641,13 @@ namespace objl
// Generate a path to the material file // Generate a path to the material file
std::vector<std::string> temp; std::vector<std::string> temp;
algorithm::split(Path, temp, "\\"); // update: use windows file seperators algorithm::split(fileName, temp, "/"); // update: use windows file seperators
std::string pathtomat = ""; std::string pathtomat = "";
if (temp.size() != 1) if (temp.size() != 1)
{ {
for (int i = 0; i < temp.size() - 1; i++) for (size_t i = 0; i < temp.size() - 1; i++)
{ {
pathtomat += temp[i] + "/"; pathtomat += temp[i] + "/";
} }
@@ -661,7 +660,7 @@ namespace objl
#endif #endif
// Load Materials // Load Materials
LoadMaterials(pathtomat); LoadMaterials(searchPath, pathtomat);
} }
} }
@@ -681,16 +680,14 @@ namespace objl
LoadedMeshes.push_back(tempMesh); LoadedMeshes.push_back(tempMesh);
} }
file.close();
// Set Materials for each Mesh // Set Materials for each Mesh
for (int i = 0; i < MeshMatNames.size(); i++) for (size_t i = 0; i < MeshMatNames.size(); i++)
{ {
std::string matname = MeshMatNames[i]; std::string matname = MeshMatNames[i];
// Find corresponding material name in loaded materials // Find corresponding material name in loaded materials
// when found copy material variables into mesh material // when found copy material variables into mesh material
for (int j = 0; j < LoadedMaterials.size(); j++) for (size_t j = 0; j < LoadedMaterials.size(); j++)
{ {
if (LoadedMaterials[j].name == matname) if (LoadedMaterials[j].name == matname)
{ {
@@ -941,7 +938,7 @@ namespace objl
} }
// If Vertex is not an interior vertex // If Vertex is not an interior vertex
float angle = math::AngleBetweenV3(pPrev.Position - pCur.Position, pNext.Position - pCur.Position) * (180 / 3.14159265359); float angle = math::AngleBetweenV3(pPrev.Position - pCur.Position, pNext.Position - pCur.Position) * (float)(180 / 3.14159265359);
if (angle <= 0 && angle >= 180) if (angle <= 0 && angle >= 180)
continue; continue;
@@ -996,16 +993,12 @@ namespace objl
} }
// Load Materials from .mtl file // Load Materials from .mtl file
bool LoadMaterials(std::string path) bool LoadMaterials(ISearchPath& searchPath, std::string fileName)
{ {
// If the file is not a material file return false auto file = searchPath.Open(fileName);
if (path.substr(path.size() - 4, path.size()) != ".mtl")
return false;
std::ifstream file(path);
// If the file is not found return false // If the file is not found return false
if (!file.is_open()) if (!file.IsOpen())
return false; return false;
Material tempMaterial; Material tempMaterial;
@@ -1014,7 +1007,7 @@ namespace objl
// Go through each line looking for material variables // Go through each line looking for material variables
std::string curline; std::string curline;
while (std::getline(file, curline)) while (std::getline(*file.m_stream, curline))
{ {
// new material and material name // new material and material name
if (algorithm::firstToken(curline) == "newmtl") if (algorithm::firstToken(curline) == "newmtl")

View File

@@ -1,105 +1,413 @@
#include "ProjectCreator.h" #include "ProjectCreator.h"
#include "OBJ_Loader.h" #include "fbx/ufbx.h"
void parseMesh(objl::Mesh* OBJMesh, worldSurface* meshSurface, customMapInfo* projInfo) std::vector<customMapVertex> vertexVec;
std::vector<uint16_t> indexVec;
std::vector<worldSurface> surfaceVec;
std::vector<customMapModel> modelVec;
bool hasTangentSpace = true;
bool loadFBXMesh(ufbx_node* node)
{ {
int meshVertexCount = OBJMesh->Vertices.size(); if (node->attrib_type != UFBX_ELEMENT_MESH)
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); printf("ignoring non-mesh node \"%s\"\n", node->name.data);
surfIndices[i] = OBJMesh->Indices[i]; return false;
}
ufbx_mesh* mesh = node->mesh;
if (mesh->instances.count != 1)
printf("node %s has %i instances, only the 1st instace will be used.\n", node->name.data, mesh->instances.count);
if (mesh->num_triangles == 0)
{
printf("ignoring mesh %s: triangle count is 0.\n", node->name.data);
return false;
} }
for (int i = 0; i < meshVertexCount; i++) if (mesh->num_indices % 3 != 0)
{ {
objl::Vertex* meshVertex = &OBJMesh->Vertices[i]; printf("ignoring mesh %s: it is not triangulated.\n", node->name.data);
customMapVertex* surfVertex = &surfVertices[i]; return false;
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;
} }
for (size_t k = 0; k < mesh->num_indices; k++)
{
if (mesh->vertex_indices[k] > UINT16_MAX)
{
printf("ignoring mesh %s, it has more than %i indices.\n", node->name.data, UINT16_MAX);
return false;
}
}
_ASSERT(mesh->vertex_position.unique_per_vertex == true);
_ASSERT(mesh->vertex_color.unique_per_vertex == false);
_ASSERT(mesh->vertex_uv.unique_per_vertex == false);
_ASSERT(mesh->vertex_normal.unique_per_vertex == false);
_ASSERT(mesh->vertex_tangent.unique_per_vertex == false);
if (mesh->vertex_tangent.exists == false)
hasTangentSpace = false;
// UFBX stores the transform data in units that are 100x larger than what blender uses, so this converts them back
ufbx_transform origTransform = node->local_transform;
origTransform.translation.x /= 100.0f;
origTransform.translation.y /= 100.0f;
origTransform.translation.z /= 100.0f;
origTransform.scale.x /= 100.0f;
origTransform.scale.y /= 100.0f;
origTransform.scale.z /= 100.0f;
ufbx_matrix meshMatrix = ufbx_transform_to_matrix(&origTransform);
CM_MATERIAL_TYPE meshMaterialType;
if (mesh->materials.count == 0)
{
if (!mesh->vertex_color.exists)
{
printf("mesh with no colour/texture data: %s\n", node->name.data);
meshMaterialType = NO_COLOUR_OR_TEXTURE;
}
else
{
printf("colour only mesh %s\n", node->name.data);
meshMaterialType = CM_MATERIAL_COLOUR;
}
}
else
{
meshMaterialType = CM_MATERIAL_TEXTURE;
}
// FBX loading code modified from https://ufbx.github.io/elements/meshes/
// Seems like I have to use this exact way of loading, otherwise some values become incorrect
for (int i = 0; i < mesh->material_parts.count; i++)
{
ufbx_mesh_part* meshPart = &mesh->material_parts.data[i];
if (meshPart->num_faces == 0)
continue;
worldSurface surface;
surface.flags = 0;
surface.triCount = meshPart->num_triangles;
surface.firstVertexIndex = vertexVec.size();
surface.firstIndex_Index = indexVec.size();
surface.material.materialType = meshMaterialType;
switch (meshMaterialType)
{
case CM_MATERIAL_TEXTURE:
surface.material.materialName = _strdup(mesh->materials.data[i]->name.data);
break;
case CM_MATERIAL_COLOUR:
case NO_COLOUR_OR_TEXTURE:
surface.material.materialName = "";
break;
default:
_ASSERT(false);
}
size_t num_triangles = meshPart->num_triangles;
customMapVertex* vertices = (customMapVertex*)calloc(num_triangles * 3, sizeof(customMapVertex));
size_t num_vertices = 0;
// Reserve space for the maximum triangle indices.
size_t num_tri_indices = mesh->max_face_triangles * 3;
uint32_t* tri_indices = (uint32_t*)calloc(num_tri_indices, sizeof(uint32_t));
_ASSERT(meshPart->num_triangles == meshPart->num_faces);
for (size_t face_ix = 0; face_ix < meshPart->num_faces; face_ix++)
{
ufbx_face face = mesh->faces.data[meshPart->face_indices.data[face_ix]];
// Triangulate the face into `tri_indices[]`.
uint32_t num_tris = ufbx_triangulate_face(tri_indices, num_tri_indices, mesh, face);
// Iterate over each triangle corner contiguously.
for (size_t q = 0; q < num_tris * 3; q++)
{
uint32_t index = tri_indices[q];
customMapVertex* vertex = &vertices[num_vertices++];
ufbx_vec3 transformedPos = ufbx_transform_position(&meshMatrix, ufbx_get_vertex_vec3(&mesh->vertex_position, index));
vertex->pos.x = transformedPos.x;
vertex->pos.y = transformedPos.y;
vertex->pos.z = transformedPos.z;
// textured and missing materials are set to white
switch (meshMaterialType)
{
case CM_MATERIAL_TEXTURE:
case NO_COLOUR_OR_TEXTURE:
vertex->color[0] = 1.0f;
vertex->color[1] = 1.0f;
vertex->color[2] = 1.0f;
vertex->color[3] = 1.0f;
break;
case CM_MATERIAL_COLOUR:
vertex->color[0] = ufbx_get_vertex_vec4(&mesh->vertex_color, index).x;
vertex->color[1] = ufbx_get_vertex_vec4(&mesh->vertex_color, index).y;
vertex->color[2] = ufbx_get_vertex_vec4(&mesh->vertex_color, index).z;
vertex->color[3] = ufbx_get_vertex_vec4(&mesh->vertex_color, index).w;
break;
default:
_ASSERT(false);
}
// 1.0f - uv.v:
// https://gamedev.stackexchange.com/questions/92886/fbx-uv-coordinates-is-strange
vertex->texCoord[0] = (float)(ufbx_get_vertex_vec2(&mesh->vertex_uv, index).x);
vertex->texCoord[1] = (float)(1.0f - ufbx_get_vertex_vec2(&mesh->vertex_uv, index).y);
vertex->normal.x = ufbx_get_vertex_vec3(&mesh->vertex_normal, index).x;
vertex->normal.y = ufbx_get_vertex_vec3(&mesh->vertex_normal, index).y;
vertex->normal.z = ufbx_get_vertex_vec3(&mesh->vertex_normal, index).z;
if (mesh->vertex_tangent.exists)
{
vertex->tangent.x = ufbx_get_vertex_vec3(&mesh->vertex_tangent, index).x;
vertex->tangent.y = ufbx_get_vertex_vec3(&mesh->vertex_tangent, index).y;
vertex->tangent.z = ufbx_get_vertex_vec3(&mesh->vertex_tangent, index).z;
}
else
{
vertex->tangent.x = 0.0f;
vertex->tangent.y = 0.0f;
vertex->tangent.z = 0.0f;
}
vertex->packedLmapCoord = 0;
// possibly bitangent, unsure what the sign part means though
vertex->binormalSign = 0.0f;
}
}
_ASSERT(num_vertices == num_triangles * 3);
// Generate the index buffer.
ufbx_vertex_stream streams[1] = {
{vertices, num_vertices, sizeof(customMapVertex)},
};
size_t num_indices = num_triangles * 3;
uint32_t* indices = (uint32_t*)calloc(num_indices, sizeof(uint32_t));
// This call will deduplicate vertices, modifying the arrays passed in `streams[]`,
// indices are written in `indices[]` and the number of unique vertices is returned.
num_vertices = ufbx_generate_indices(streams, 1, indices, num_indices, NULL, NULL);
_ASSERT(num_vertices != 0);
vertexVec.insert(vertexVec.end(), &vertices[0], &vertices[num_vertices]);
size_t currIndexVecSize = indexVec.size();
indexVec.resize(indexVec.size() + num_indices);
for (size_t m = 0; m < num_indices; m++)
{
indexVec[currIndexVecSize + m] = (uint16_t)indices[m];
}
surfaceVec.push_back(surface);
}
return true;
}
bool loadFBXModel(ufbx_node* node)
{
customMapModel model;
model.name = node->name.data;
ufbx_transform origTransform = node->local_transform;
origTransform.translation.x /= 100.0f;
origTransform.translation.y /= 100.0f;
origTransform.translation.z /= 100.0f;
origTransform.scale.x /= 100.0f;
origTransform.scale.y /= 100.0f;
origTransform.scale.z /= 100.0f;
ufbx_matrix meshMatrix = ufbx_transform_to_matrix(&origTransform);
model.origin.x = node->local_transform.translation.x / 100.0f;
model.origin.y = node->local_transform.translation.y / 100.0f;
model.origin.z = node->local_transform.translation.z / 100.0f;
model.rotation.x = node->euler_rotation.x;
model.rotation.y = node->euler_rotation.y;
model.rotation.z = node->euler_rotation.z;
model.scale = node->local_transform.scale.x / 100.0f;
if (model.scale == 0.0f)
{
printf("WARN: Ignoring model %s: has a scale of 0!\n", node->name.data);
return false;
}
if (node->local_transform.scale.x != node->local_transform.scale.y || node->local_transform.scale.x != node->local_transform.scale.z)
printf("WARNING: model %s uses non-uniform scaling! Only the X axis will be used for the scale value.\n", node->name.data);
modelVec.push_back(model);
return true;
}
void parseGFXData(ufbx_scene* scene, customMapInfo* projInfo)
{
vertexVec.clear();
indexVec.clear();
surfaceVec.clear();
modelVec.clear();
hasTangentSpace = true;
for (size_t i = 0; i < scene->nodes.count; i++)
{
ufbx_node* node = scene->nodes.data[i];
switch (node->attrib_type)
{
case UFBX_ELEMENT_MESH:
loadFBXMesh(node);
break;
case UFBX_ELEMENT_EMPTY:
// loadFBXModel(node);
break;
default:
printf("ignoring node type %i: %s\n", node->attrib_type, node->name.data);
break;
}
}
projInfo->gfxInfo.surfaceCount = surfaceVec.size();
projInfo->gfxInfo.vertexCount = vertexVec.size();
projInfo->gfxInfo.indexCount = indexVec.size();
projInfo->modelCount = modelVec.size();
projInfo->gfxInfo.surfaces = new worldSurface[surfaceVec.size()];
projInfo->gfxInfo.vertices = new customMapVertex[vertexVec.size()];
projInfo->gfxInfo.indices = new uint16_t[indexVec.size()];
projInfo->models = new customMapModel[modelVec.size()];
memcpy(projInfo->gfxInfo.surfaces, &surfaceVec[0], surfaceVec.size() * sizeof(worldSurface));
memcpy(projInfo->gfxInfo.vertices, &vertexVec[0], vertexVec.size() * sizeof(customMapVertex));
memcpy(projInfo->gfxInfo.indices, &indexVec[0], indexVec.size() * sizeof(uint16_t));
// memcpy(projInfo->models, &modelVec[0], modelVec.size() * sizeof(customMapModel));
if (hasTangentSpace == false)
printf("warning: one or more meshes have no tangent space. Be sure to select the tangent space box when exporting the FBX from blender.\n");
}
void parseCollisionData(ufbx_scene* scene, customMapInfo* projInfo)
{
// hack: cbf changing the code for collision data, so just load collision dada as if it was gfx data
vertexVec.clear();
indexVec.clear();
surfaceVec.clear();
modelVec.clear();
hasTangentSpace = true;
for (size_t i = 0; i < scene->nodes.count; i++)
{
ufbx_node* node = scene->nodes.data[i];
switch (node->attrib_type)
{
case UFBX_ELEMENT_MESH:
loadFBXMesh(node);
break;
case UFBX_ELEMENT_EMPTY:
// loadFBXModel(node);
break;
default:
printf("ignoring node type %i: %s\n", node->attrib_type, node->name.data);
break;
}
}
projInfo->colInfo.surfaceCount = surfaceVec.size();
projInfo->colInfo.vertexCount = vertexVec.size();
projInfo->colInfo.indexCount = indexVec.size();
projInfo->modelCount = modelVec.size();
projInfo->colInfo.surfaces = new worldSurface[surfaceVec.size()];
projInfo->colInfo.vertices = new customMapVertex[vertexVec.size()];
projInfo->colInfo.indices = new uint16_t[indexVec.size()];
projInfo->models = new customMapModel[modelVec.size()];
memcpy(projInfo->colInfo.surfaces, &surfaceVec[0], surfaceVec.size() * sizeof(worldSurface));
memcpy(projInfo->colInfo.vertices, &vertexVec[0], vertexVec.size() * sizeof(customMapVertex));
memcpy(projInfo->colInfo.indices, &indexVec[0], indexVec.size() * sizeof(uint16_t));
// memcpy(projInfo->models, &modelVec[0], modelVec.size() * sizeof(customMapModel));
if (hasTangentSpace == false)
printf("warning: one or more meshes have no tangent space. Be sure to select the tangent space box when exporting the FBX from blender.\n");
} }
customMapInfo* CustomMapInfo::createCustomMapInfo(std::string& projectName, ISearchPath& searchPath) customMapInfo* CustomMapInfo::createCustomMapInfo(std::string& projectName, ISearchPath& searchPath)
{ {
std::string objFilePath = searchPath.GetPath() + "custom_map/world.obj"; ufbx_scene* gfxScene;
ufbx_scene* colScene;
objl::Loader OBJloader; std::string gfxFbxPath = "custom_map/map_gfx.fbx";
bool isLoaded = OBJloader.LoadFile(objFilePath); auto gfxFile = searchPath.Open(gfxFbxPath);
if (!isLoaded) if (!gfxFile.IsOpen())
{ {
printf("OBJLoader: unable to load obj file %s\n", objFilePath.c_str()); printf("Failed to open map gfx fbx file: %s\n", gfxFbxPath.c_str());
return NULL; return NULL;
} }
char* gfxMapData = new char[gfxFile.m_length];
gfxFile.m_stream->seekg(0);
gfxFile.m_stream->read(gfxMapData, gfxFile.m_length);
ufbx_error error;
ufbx_load_opts opts;
opts.target_axes = ufbx_axes_right_handed_y_up;
opts.generate_missing_normals = true;
opts.allow_missing_vertex_position = false;
gfxScene = ufbx_load_memory(gfxMapData, gfxFile.m_length, NULL, &error);
if (!gfxScene)
{
fprintf(stderr, "Failed to load map gfx fbx file: %s\n", error.description.data);
return NULL;
}
std::string colFbxPath = "custom_map/map_col.fbx";
auto colFile = searchPath.Open(colFbxPath);
if (!colFile.IsOpen())
{
printf("Failed to open map collison fbx file: %s. map gfx will be used for collision instead.\n", colFbxPath.c_str());
colScene = gfxScene;
}
else
{
char* colMapData = new char[colFile.m_length];
colFile.m_stream->seekg(0);
colFile.m_stream->read(colMapData, colFile.m_length);
ufbx_error error;
ufbx_load_opts opts;
opts.target_axes = ufbx_axes_right_handed_y_up;
opts.generate_missing_normals = true;
opts.allow_missing_vertex_position = false;
colScene = ufbx_load_memory(colMapData, colFile.m_length, NULL, &error);
if (!colScene)
{
fprintf(stderr, "Failed to load map collision fbx file: %s\n", error.description.data);
return NULL;
}
}
customMapInfo* projInfo = new customMapInfo; customMapInfo* projInfo = new customMapInfo;
projInfo->name = projectName; projInfo->name = projectName;
projInfo->bspName = "maps/mp/" + projectName + ".d3dbsp"; projInfo->bspName = "maps/mp/" + projectName + ".d3dbsp";
projInfo->gfxInfo.surfaceCount = OBJloader.LoadedMeshes.size(); parseGFXData(gfxScene, projInfo);
projInfo->gfxInfo.surfaces = new worldSurface[projInfo->gfxInfo.surfaceCount]; parseCollisionData(colScene, projInfo);
projInfo->gfxInfo.vertexCount = 0; ufbx_free_scene(gfxScene);
projInfo->gfxInfo.indexCount = 0; if (gfxScene != colScene)
projInfo->gfxInfo.vertices = (customMapVertex*)malloc(1); ufbx_free_scene(colScene);
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; return projInfo;
} }

View File

@@ -0,0 +1,99 @@
#pragma once
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <algorithm>
#include "Util.h"
#include "CustomMapConsts.h"
using TriangleGroup = std::vector<unsigned int>; // Group of triangle indices
using AdjacencyList = std::vector<TriangleGroup>; // Triangle-to-triangle connections
class TriangleSort
{
private:
static void computeTriCentroid(vec3_t& result, std::vector<vec3_t>& vertices, uint16_t i0, uint16_t i1, uint16_t i2)
{
result.x = 0.0f;
result.y = 0.0f;
result.z = 0.0f;
result.x += vertices[i0].x;
result.y += vertices[i0].y;
result.z += vertices[i0].z;
result.x += vertices[i1].x;
result.y += vertices[i1].y;
result.z += vertices[i1].z;
result.x += vertices[i2].x;
result.y += vertices[i2].y;
result.z += vertices[i2].z;
result.x /= 3.0f;
result.y /= 3.0f;
result.z /= 3.0f;
}
static AdjacencyList buildAdjacencyList(const std::vector<uint16_t>& indices, std::vector<vec3_t>& vertices, int maxTrianglesPerGroup)
{
size_t numTriangles = indices.size() / 3;
AdjacencyList adjList(numTriangles);
std::vector<vec3_t> centroids;
for (size_t i = 0; i < numTriangles; i++)
{
vec3_t center;
computeTriCentroid(center, vertices, indices[i * 3], indices[i * 3 + 1], indices[i * 3 + 2]);
centroids.push_back(center);
}
std::vector<bool> visited(numTriangles, false);
for (size_t i = 0; i < numTriangles; ++i)
{
_ASSERT(i < MAX_COL_VERTS);
if (visited[i] == true)
continue;
visited[i] = true;
// Store distances to other triangles
std::vector<std::pair<float, unsigned int>> distances;
for (size_t j = 0; j < numTriangles; ++j)
{
if (visited[j] == true)
continue;
if (i != j)
{
float dist = CMUtil::distBetweenPoints(centroids[i], centroids[j]);
distances.emplace_back(dist, j);
}
}
// Sort by distance and take the closest maxNeighbors
std::sort(distances.begin(), distances.end());
for (size_t k = 0; k < std::min((size_t)maxTrianglesPerGroup, distances.size()); ++k)
{
unsigned int neighbour = distances[k].second;
adjList[i].push_back(neighbour);
visited[neighbour] = true;
}
}
for (size_t i = 0; i < visited.size(); i++)
{
if (visited[i] == false)
printf("WARN: missing triangle: %i\n", i);
}
return adjList;
}
public:
static AdjacencyList groupTriangles(std::vector<uint16_t>& indices, std::vector<vec3_t>& vertices, int maxTrianglesPerGroup)
{
return buildAdjacencyList(indices, vertices, maxTrianglesPerGroup);
}
};

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include <cmath>
#include "Game/T6/T6.h" #include "Game/T6/T6.h"
using namespace T6; using namespace T6;
@@ -73,4 +74,50 @@ public:
{ {
return ((size + 127) & 0xFFFFFF80); return ((size + 127) & 0xFFFFFF80);
} }
static float distBetweenPoints(vec3_t p1, vec3_t p2)
{
float x = p2.x - p1.x;
float y = p2.y - p1.y;
float z = p2.z - p1.z;
return sqrtf((x * x) + (y * y) + (z * z));
}
// angles are in euler degrees
static void convertAnglesToAxis(vec3_t* angles, vec3_t* axis)
{
float xRadians = angles->x * 0.017453292; // M_PI / 180.0f
float yRadians = angles->y * 0.017453292; // M_PI / 180.0f
float zRadians = angles->z * 0.017453292; // M_PI / 180.0f
float cosX = cos(xRadians);
float sinX = sin(xRadians);
float cosY = cos(yRadians);
float sinY = sin(yRadians);
float cosZ = cos(zRadians);
float sinZ = sin(zRadians);
axis[0].x = cosX * cosY;
axis[0].y = cosX * sinY;
axis[0].z = -sinX;
axis[1].x = (sinZ * sinX * cosY) - (cosZ * sinY);
axis[1].y = (sinZ * sinX * sinY) + (cosZ * cosY);
axis[1].z = sinZ * cosX;
axis[2].x = (cosZ * sinX * cosY) + (sinZ * sinY);
axis[2].y = (cosZ * sinX * sinY) - (sinZ * cosY);
axis[2].z = cosZ * cosX;
}
static void matrixTranspose3x3(const vec3_t* in, vec3_t* out)
{
out[0].x = in[0].x;
out[0].y = in[1].x;
out[0].z = in[2].x;
out[1].x = in[0].y;
out[1].y = in[1].y;
out[1].z = in[2].y;
out[2].x = in[0].z;
out[2].y = in[1].z;
out[2].z = in[2].z;
}
}; };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
#include "Game/T6/CommonT6.h" #include "Game/T6/CommonT6.h"
#include "Game/T6/T6.h" #include "Game/T6/T6.h"
#include "Image/IwiLoader.h" #include "Image/IwiLoader.h"
#include "Image/IwiTypes.h"
#include <cstring> #include <cstring>
#include <format> #include <format>
@@ -25,7 +26,12 @@ namespace
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
{ {
const auto fileName = std::format("images/{}.iwi", assetName); auto fileName = std::format("images/{}.iwi", assetName);
for (size_t i = 0; i < fileName.size(); i++)
if (fileName[i] == '*')
fileName[i] = '_';
const auto file = m_search_path.Open(fileName); const auto file = m_search_path.Open(fileName);
if (!file.IsOpen()) if (!file.IsOpen())
return AssetCreationResult::NoAction(); return AssetCreationResult::NoAction();
@@ -53,11 +59,47 @@ namespace
image->height = static_cast<uint16_t>(texture->GetHeight()); image->height = static_cast<uint16_t>(texture->GetHeight());
image->depth = static_cast<uint16_t>(texture->GetDepth()); image->depth = static_cast<uint16_t>(texture->GetDepth());
image->streaming = 1; if (texture->GetTextureType() == TextureType::T_2D)
image->streamedParts[0].levelCount = 1; image->mapType = 3;
image->streamedParts[0].levelSize = static_cast<uint32_t>(fileSize); else if (texture->GetTextureType() == TextureType::T_3D)
image->streamedParts[0].hash = dataHash & 0x1FFFFFFF; image->mapType = 4;
image->streamedPartCount = 1; else if (texture->GetTextureType() == TextureType::T_CUBE)
image->mapType = 5;
else
_ASSERT(false);
//image->streaming = 1;
//image->streamedParts[0].levelCount = 1;
//image->streamedParts[0].levelSize = static_cast<uint32_t>(fileSize);
//image->streamedParts[0].hash = dataHash & 0x1FFFFFFF;
//image->streamedPartCount = 1;
int mipMapCount = texture->HasMipMaps() ? texture->GetMipMapCount() : 1;
size_t textureSize = 0;
for (int previousMipLevel = 0; previousMipLevel < mipMapCount; previousMipLevel++)
{
textureSize += texture->GetSizeOfMipLevel(previousMipLevel) * texture->GetFaceCount();
}
image->streaming = 0;
image->texture.loadDef = (GfxImageLoadDef*)malloc(sizeof(GfxImageLoadDef) + textureSize);
memset(image->texture.loadDef, 0, sizeof(GfxImageLoadDef) + textureSize);
image->texture.loadDef->format = texture->GetFormat()->GetDxgiFormat();
image->texture.loadDef->levelCount = 1;
image->texture.loadDef->resourceSize = textureSize;
memcpy(image->texture.loadDef->data,
texture->GetBufferForMipLevel(0),
textureSize); // GetBufferForMipLevel(0) returns a pointer to the start of the image
image->texture.loadDef->flags = 0;
if (image->noPicmip)
image->texture.loadDef->flags |= iwi27::IMG_FLAG_NOMIPMAPS;
if (texture->GetTextureType() == TextureType::T_3D)
image->texture.loadDef->flags |= iwi27::IMG_FLAG_VOLMAP;
if (texture->GetTextureType() == TextureType::T_CUBE)
image->texture.loadDef->flags |= iwi27::IMG_FLAG_CUBEMAP;
return AssetCreationResult::Success(context.AddAsset<AssetImage>(assetName, image)); return AssetCreationResult::Success(context.AddAsset<AssetImage>(assetName, image));
} }

View File

@@ -134,8 +134,8 @@ namespace
printf("ERROR: Cant find pixel shader %s\n", psFileName.c_str()); printf("ERROR: Cant find pixel shader %s\n", psFileName.c_str());
return AssetCreationResult::Failure(); return AssetCreationResult::Failure();
} }
currPass->pixelShader->prog.loadDef.programSize = psFile.m_length; currPass->pixelShader->prog.loadDef.programSize = (unsigned int)psFile.m_length;
currPass->pixelShader->prog.loadDef.program = new char[psFile.m_length]; currPass->pixelShader->prog.loadDef.program = new char[(unsigned int)psFile.m_length];
psFile.m_stream->read(currPass->pixelShader->prog.loadDef.program, psFile.m_length); psFile.m_stream->read(currPass->pixelShader->prog.loadDef.program, psFile.m_length);
} }
@@ -158,8 +158,8 @@ namespace
printf("ERROR: Cant find vertex shader %s\n", vsFileName.c_str()); printf("ERROR: Cant find vertex shader %s\n", vsFileName.c_str());
return AssetCreationResult::Failure(); return AssetCreationResult::Failure();
} }
currPass->vertexShader->prog.loadDef.programSize = vsFile.m_length; currPass->vertexShader->prog.loadDef.programSize = (unsigned int)vsFile.m_length;
currPass->vertexShader->prog.loadDef.program = new char[vsFile.m_length]; currPass->vertexShader->prog.loadDef.program = new char[(unsigned int)vsFile.m_length];
vsFile.m_stream->read(currPass->vertexShader->prog.loadDef.program, vsFile.m_length); vsFile.m_stream->read(currPass->vertexShader->prog.loadDef.program, vsFile.m_length);
} }
} }

View File

@@ -14,6 +14,7 @@
#include "Writing/Steps/StepWriteZoneContentToFile.h" #include "Writing/Steps/StepWriteZoneContentToFile.h"
#include "Writing/Steps/StepWriteZoneContentToMemory.h" #include "Writing/Steps/StepWriteZoneContentToMemory.h"
#include "Writing/Steps/StepWriteZoneHeader.h" #include "Writing/Steps/StepWriteZoneHeader.h"
#include "Writing/Steps/StepWriteZoneRSA.h"
#include "Writing/Steps/StepWriteZoneSizes.h" #include "Writing/Steps/StepWriteZoneSizes.h"
#include "Zone/XChunk/XChunkProcessorDeflate.h" #include "Zone/XChunk/XChunkProcessorDeflate.h"
#include "Zone/XChunk/XChunkProcessorSalsa20Encryption.h" #include "Zone/XChunk/XChunkProcessorSalsa20Encryption.h"
@@ -99,9 +100,9 @@ std::unique_ptr<ZoneWriter> ZoneWriterFactory::CreateWriter(const Zone& zone) co
{ {
auto writer = std::make_unique<ZoneWriter>(); auto writer = std::make_unique<ZoneWriter>();
// TODO Support signed fastfiles bool isSecure = true;
bool isSecure = false;
bool isEncrypted = true; bool isEncrypted = true;
bool isOfficial = true;
SetupBlocks(*writer); SetupBlocks(*writer);
@@ -111,7 +112,11 @@ std::unique_ptr<ZoneWriter> ZoneWriterFactory::CreateWriter(const Zone& zone) co
writer->AddWritingStep(std::move(contentInMemory)); writer->AddWritingStep(std::move(contentInMemory));
// Write zone header // Write zone header
writer->AddWritingStep(std::make_unique<StepWriteZoneHeader>(CreateHeaderForParams(isSecure, false, isEncrypted))); writer->AddWritingStep(std::make_unique<StepWriteZoneHeader>(CreateHeaderForParams(isSecure, isOfficial, isEncrypted)));
// write RSA
if (isSecure)
writer->AddWritingStep(std::make_unique<StepWriteZoneRSA>(zone.m_name));
// Setup loading XChunks from the zone from this point on. // Setup loading XChunks from the zone from this point on.
ICapturedDataProvider* dataToSignProvider; ICapturedDataProvider* dataToSignProvider;

View File

@@ -0,0 +1,35 @@
#include "StepWriteZoneRSA.h"
#include "Game/T6/ZoneConstantsT6.h"
StepWriteZoneRSA::StepWriteZoneRSA(std::string zoneName)
: m_zoneName(zoneName)
{
}
void StepWriteZoneRSA::PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream)
{
stream->Write(T6::ZoneConstants::MAGIC_AUTH_HEADER, strlen(T6::ZoneConstants::MAGIC_AUTH_HEADER));
uint32_t loadFlags = 0;
stream->Write(&loadFlags, sizeof(uint32_t));
char fileName[32];
memset(fileName, 0, 32);
strncpy(fileName, m_zoneName.c_str(), 31);
stream->Write(fileName, 32);
char data[256] = {0x75, 0xc2, 0xca, 0x61, 0x15, 0xef, 0xcb, 0xde, 0x42, 0xfc, 0xa8, 0xed, 0xb1, 0x77, 0x79, 0x93, 0x73, 0x28, 0x3e, 0x7f, 0xca,
0x76, 0x48, 0xc3, 0x21, 0x23, 0x86, 0xc3, 0x0f, 0xb6, 0xa6, 0xb5, 0xe9, 0xab, 0x40, 0x29, 0xb8, 0x3c, 0x03, 0xc4, 0xe0, 0x99, 0xed,
0xf2, 0x96, 0xd7, 0xb3, 0x95, 0x0d, 0x2e, 0xdd, 0xf0, 0x08, 0x6d, 0x5a, 0x95, 0x0b, 0x61, 0xd9, 0xde, 0xb4, 0x9d, 0x8a, 0x1b, 0x19,
0xa8, 0x88, 0xb4, 0x35, 0xe8, 0x25, 0x78, 0x21, 0x04, 0xbf, 0x36, 0x13, 0x9b, 0xf6, 0x10, 0x12, 0x8a, 0x08, 0x98, 0xf0, 0xb5, 0xdc,
0x8c, 0xd6, 0x37, 0x6d, 0x9a, 0xd8, 0xe4, 0x62, 0x5d, 0x02, 0xc1, 0xf6, 0xf1, 0xa1, 0x95, 0x93, 0x42, 0xee, 0xc2, 0x1a, 0xd9, 0xf0,
0x36, 0x36, 0x23, 0x50, 0x8b, 0x11, 0x90, 0x6a, 0xa1, 0x8d, 0xf6, 0xd0, 0xe4, 0xb5, 0x0f, 0xfd, 0x87, 0x2f, 0x46, 0xb9, 0x08, 0x3e,
0x38, 0xf9, 0x81, 0xaa, 0x39, 0x2b, 0xf7, 0x44, 0x44, 0x75, 0x0e, 0x8a, 0x09, 0x6c, 0x6f, 0x6e, 0xea, 0xd0, 0x32, 0x62, 0xfd, 0x98,
0x65, 0xb5, 0xbd, 0xc0, 0xae, 0x63, 0xf1, 0xe9, 0x24, 0x03, 0xfc, 0x34, 0xed, 0xb6, 0xbf, 0x0e, 0xd2, 0x56, 0x43, 0xea, 0xde, 0xff,
0x51, 0xa8, 0xb1, 0x93, 0x47, 0xe3, 0xc3, 0xee, 0xc2, 0xa3, 0x0a, 0x93, 0x14, 0x8f, 0x98, 0x7c, 0xaf, 0x2d, 0xa2, 0x2c, 0x71, 0x23,
0x60, 0x6a, 0x66, 0xd1, 0x6b, 0x55, 0xc0, 0x5d, 0x9b, 0xad, 0x18, 0xc5, 0xac, 0x2f, 0xa4, 0x00, 0xe8, 0xd0, 0xa6, 0xb4, 0x67, 0xa7,
0xbb, 0x7d, 0x4a, 0xbe, 0x02, 0xd0, 0xb6, 0xe0, 0xc6, 0xac, 0x1e, 0x59, 0x88, 0xcd, 0x26, 0x41, 0x73, 0x10, 0x65, 0x13, 0x79, 0x72,
0x5a, 0x26, 0x41, 0xe9, 0x89, 0x51, 0xc3, 0x79, 0x7d, 0x70, 0x3a, 0x5b, 0x94, 0x5d, 0xdd};
stream->Write(data, 256);
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "Writing/IWritingStep.h"
class StepWriteZoneRSA final : public IWritingStep
{
std::string m_zoneName;
public:
explicit StepWriteZoneRSA(std::string zoneName);
void PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) override;
};