From 54114a96ae94b14afe25885c2f40ddada80cc855 Mon Sep 17 00:00:00 2001 From: LJW-Dev Date: Mon, 7 Jul 2025 23:54:05 +0800 Subject: [PATCH] WIP fix, loading is currently not working correctly --- .../Game/T6/CustomMap/CustomMapConsts.h | 3 + .../Game/T6/CustomMap/CustomMapLinker.cpp | 3294 +++++++++-------- .../Game/T6/CustomMap/CustomMapLinker.h | 1669 ++++++++- .../Game/T6/CustomMap/LoaderCustomMapT6.cpp | 6 +- .../Game/T6/CustomMap/LoaderCustomMapT6.h | 2 +- .../Game/T6/CustomMap/ProjectCreator.cpp | 180 +- .../Game/T6/CustomMap/ProjectCreator.h | 5 +- src/ObjLoading/Game/T6/CustomMap/Util.h | 3 + src/ObjLoading/Game/T6/ObjLoaderT6.cpp | 4 +- 9 files changed, 3413 insertions(+), 1753 deletions(-) diff --git a/src/ObjLoading/Game/T6/CustomMap/CustomMapConsts.h b/src/ObjLoading/Game/T6/CustomMap/CustomMapConsts.h index a8b1d3e0..ccda4ba6 100644 --- a/src/ObjLoading/Game/T6/CustomMap/CustomMapConsts.h +++ b/src/ObjLoading/Game/T6/CustomMap/CustomMapConsts.h @@ -1,3 +1,6 @@ +#pragma once + + #include #define DYN_ENT_COUNT 0 diff --git a/src/ObjLoading/Game/T6/CustomMap/CustomMapLinker.cpp b/src/ObjLoading/Game/T6/CustomMap/CustomMapLinker.cpp index 798438e7..e865f6ba 100644 --- a/src/ObjLoading/Game/T6/CustomMap/CustomMapLinker.cpp +++ b/src/ObjLoading/Game/T6/CustomMap/CustomMapLinker.cpp @@ -1,1646 +1,1648 @@ -#include "CustomMapLinker.h" - -#include "Utils/Pack.h" -#include "Util.h" -#include "CustomMapConsts.h" -#include "BinarySpacePartitionTreePreCalc.h" - -#include -#include -using json = nlohmann::json; - -class CustomMapLinker -{ - MemoryManager& m_memory; - ISearchPath& m_search_path; - Zone& m_zone; - AssetCreationContext& m_context; - - bool hasLinkFailed; - - CustomMapLinker(MemoryManager& memory, ISearchPath& searchPath, Zone& zone, AssetCreationContext& context) - : m_memory(memory), - m_search_path(searchPath), - m_zone(zone), - m_context(context) - { - hasLinkFailed = false; - } - - // TODO vd1: - // used for UVs of sub-textures, when it is set to empty all of them turn a blank colour - // could fix by removing sub textures or figure out how they are created and redo that - // its not an important issue though - bool overwriteDrawData(customMapInfo* projInfo, GfxWorld* gfxWorld) - { - int vertexCount = projInfo->gfxInfo.vertexCount; - customMapVertex* worldVertices = projInfo->gfxInfo.vertices; - - gfxWorld->draw.vertexCount = vertexCount; - gfxWorld->draw.vertexDataSize0 = vertexCount * sizeof(GfxPackedWorldVertex); - GfxPackedWorldVertex* vertexBuffer = new GfxPackedWorldVertex[vertexCount]; - for (int i = 0; i < vertexCount; i++) - { - customMapVertex* WorldVertex = &worldVertices[i]; - GfxPackedWorldVertex* GfxVertex = &vertexBuffer[i]; - - GfxVertex->xyz = CMUtil::convertToBO2Coords(WorldVertex->pos); - - GfxVertex->binormalSign = WorldVertex->binormalSign; - - GfxVertex->color.packed = pack32::Vec4PackGfxColor(WorldVertex->color); - - GfxVertex->texCoord.packed = pack32::Vec2PackTexCoordsUV(WorldVertex->texCoord); - - GfxVertex->normal.packed = pack32::Vec3PackUnitVecThirdBased(CMUtil::convertToBO2Coords(WorldVertex->normal).v); - - GfxVertex->tangent.packed = pack32::Vec3PackUnitVecThirdBased(CMUtil::convertToBO2Coords(WorldVertex->tangent).v); - - GfxVertex->lmapCoord.packed = WorldVertex->packedLmapCoord; - } - gfxWorld->draw.vd0.data = (char*)vertexBuffer; - - // we don't use vd1 but still needs to be initialised - // the data type varies and 0x20 is enough for all types - gfxWorld->draw.vertexDataSize1 = 0x20; - gfxWorld->draw.vd1.data = new char[gfxWorld->draw.vertexDataSize1]; - memset(gfxWorld->draw.vd1.data, 0, gfxWorld->draw.vertexDataSize1); - - int indexCount = projInfo->gfxInfo.indexCount; - _ASSERT(indexCount % 3 == 0); - gfxWorld->draw.indexCount = indexCount; - gfxWorld->draw.indices = new uint16_t[indexCount]; - for (int i = 0; i < indexCount; i += 3) - { - // the editor orders their vertices opposite to bo2, so its converted here - gfxWorld->draw.indices[i + 2] = projInfo->gfxInfo.indices[i + 0]; - gfxWorld->draw.indices[i + 1] = projInfo->gfxInfo.indices[i + 1]; - gfxWorld->draw.indices[i + 0] = projInfo->gfxInfo.indices[i + 2]; - } - - return true; - } - - struct s_sortedSurf - { - int surfaceIndex; - int vertexCount; - }; - - bool compareSurfaces(s_sortedSurf& surf0, s_sortedSurf& surf1) - { - return surf0.vertexCount > surf1.vertexCount; - } - - void overwriteMapSurfaces(customMapInfo* projInfo, GfxWorld* gfxWorld) - { - bool overwriteResult = overwriteDrawData(projInfo, gfxWorld); - if (!overwriteResult) - return; - - unsigned int surfaceCount = projInfo->gfxInfo.surfaceCount; - gfxWorld->surfaceCount = surfaceCount; - gfxWorld->dpvs.staticSurfaceCount = surfaceCount; - gfxWorld->dpvs.surfaces = new GfxSurface[surfaceCount]; - for (int i = 0; i < surfaceCount; i++) - { - auto currSurface = &gfxWorld->dpvs.surfaces[i]; - auto objSurface = &projInfo->gfxInfo.surfaces[i]; - - currSurface->lightmapIndex = objSurface->lightmapIndex; - currSurface->primaryLightIndex = objSurface->primaryLightIndex; - currSurface->reflectionProbeIndex = objSurface->reflectionProbeIndex; - currSurface->flags = objSurface->flags; - - currSurface->tris.triCount = objSurface->triCount; - currSurface->tris.baseIndex = objSurface->firstIndex_Index; - - currSurface->tris.vertexDataOffset0 = objSurface->firstVertexIndex * sizeof(GfxPackedWorldVertex); - currSurface->tris.vertexDataOffset1 = 0; - - auto* assetInfo = m_context.LoadDependency(objSurface->materialName); - 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(defaultMaterialName); - _ASSERT(assetInfo != NULL); - } - currSurface->material = assetInfo->Asset(); - - GfxPackedWorldVertex* firstVert = (GfxPackedWorldVertex*)&gfxWorld->draw.vd0.data[currSurface->tris.vertexDataOffset0]; - currSurface->bounds[0].x = firstVert[0].xyz.x; - currSurface->bounds[0].y = firstVert[0].xyz.y; - currSurface->bounds[0].z = firstVert[0].xyz.z; - currSurface->bounds[1].x = firstVert[0].xyz.x; - currSurface->bounds[1].y = firstVert[0].xyz.y; - currSurface->bounds[1].z = firstVert[0].xyz.z; - for (int k = 0; k < currSurface->tris.triCount * 3; k++) - { - CMUtil::calcNewBoundsWithPoint(&firstVert[k].xyz, &currSurface->bounds[0], &currSurface->bounds[1]); - } - - // unused values - currSurface->tris.mins.x = 0.0f; - currSurface->tris.mins.y = 0.0f; - currSurface->tris.mins.z = 0.0f; - currSurface->tris.maxs.x = 0.0f; - currSurface->tris.maxs.y = 0.0f; - currSurface->tris.maxs.z = 0.0f; - currSurface->tris.himipRadiusInvSq = 0.0f; - currSurface->tris.vertexCount = 0; - currSurface->tris.firstVertex = 0; - } - - // sort all vertexes by vertex count - // unsure if this does anything - s_sortedSurf* sortedSurfs = new s_sortedSurf[surfaceCount]; - for (int i = 0; i < surfaceCount; i++) - { - sortedSurfs[i].surfaceIndex = i; - sortedSurfs[i].vertexCount = gfxWorld->dpvs.surfaces[i].tris.triCount * 3; - } - std::sort(sortedSurfs, &sortedSurfs[surfaceCount], compareSurfaces); - - gfxWorld->dpvs.sortedSurfIndex = new uint16_t[surfaceCount]; - for (int i = 0; i < surfaceCount; i++) - { - gfxWorld->dpvs.sortedSurfIndex[i] = sortedSurfs[i].surfaceIndex; - } - - gfxWorld->dpvs.surfaceMaterials = new GfxDrawSurf_align4[surfaceCount]; - memset(gfxWorld->dpvs.surfaceMaterials, 0, sizeof(GfxDrawSurf_align4) * surfaceCount); - - // all visdata is alligned by 128 - gfxWorld->dpvs.surfaceVisDataCount = CMUtil::allignBy128(surfaceCount); - gfxWorld->dpvs.surfaceVisData[0] = new char[surfaceCount]; - gfxWorld->dpvs.surfaceVisData[1] = new char[surfaceCount]; - gfxWorld->dpvs.surfaceVisData[2] = new char[surfaceCount]; - gfxWorld->dpvs.surfaceVisDataCameraSaved = new char[surfaceCount]; - gfxWorld->dpvs.surfaceCastsShadow = new char[surfaceCount]; - gfxWorld->dpvs.surfaceCastsSunShadow = new char[surfaceCount]; - memset(gfxWorld->dpvs.surfaceVisData[0], 0, surfaceCount); - memset(gfxWorld->dpvs.surfaceVisData[1], 0, surfaceCount); - memset(gfxWorld->dpvs.surfaceVisData[2], 0, surfaceCount); - memset(gfxWorld->dpvs.surfaceVisDataCameraSaved, 0, surfaceCount); - memset(gfxWorld->dpvs.surfaceCastsShadow, 0, surfaceCount); - memset(gfxWorld->dpvs.surfaceCastsSunShadow, 0, surfaceCount); - - gfxWorld->dpvs.litSurfsBegin = 0; - gfxWorld->dpvs.litSurfsEnd = surfaceCount; - gfxWorld->dpvs.emissiveOpaqueSurfsBegin = surfaceCount; - gfxWorld->dpvs.emissiveOpaqueSurfsEnd = surfaceCount; - gfxWorld->dpvs.emissiveTransSurfsBegin = surfaceCount; - gfxWorld->dpvs.emissiveTransSurfsEnd = surfaceCount; - gfxWorld->dpvs.litTransSurfsBegin = surfaceCount; - gfxWorld->dpvs.litTransSurfsEnd = surfaceCount; - } - -#define SMODEL_FLAG_NO_SHADOW 1 -#define SMODEL_FLAG_IS_LIT 2 - - void overwriteMapSModels(GfxWorld* gfxWorld) - { - unsigned int modelCount = 0; - gfxWorld->dpvs.smodelCount = modelCount; - - gfxWorld->dpvs.smodelInsts = new GfxStaticModelInst[modelCount]; - gfxWorld->dpvs.smodelDrawInsts = new GfxStaticModelDrawInst[modelCount]; - /* - for (unsigned int i = 0; i < modelCount; i++) - { - auto currModel = &gfxWorld->dpvs.smodelDrawInsts[i]; - auto currModelInst = &gfxWorld->dpvs.smodelInsts[i]; - json currModelJs = js["models"][i]; - - // TODO: load custom xmodels - std::string modelName = currModelJs["model"]; - auto xModelAsset = findAsset(assetPool, ASSET_TYPE_XMODEL, modelName); - if (xModelAsset == NULL) - { - printf("Custom model (%s) not supported!\n", modelName.c_str()); - hasLinkFailed = true; - return; - } - currModel->model = (XModel*)xModelAsset->m_ptr; - - currModel->placement.origin.x = currModelJs["origin"]["x"]; - currModel->placement.origin.y = currModelJs["origin"]["y"]; - currModel->placement.origin.z = currModelJs["origin"]["z"]; - currModel->placement.origin = CMUtil::convertToBO2Coords(currModel->placement.origin); - currModel->placement.axis[0].x = currModelJs["forward"]["x"]; - currModel->placement.axis[0].y = currModelJs["forward"]["y"]; - currModel->placement.axis[0].z = currModelJs["forward"]["z"]; - 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 - // TODO: mins and maxs are slightly different to what bo2 uses, which results in some wierd culling ingame - currModelInst->mins.x = currModel->model->mins.x + currModel->placement.origin.x; - currModelInst->mins.y = currModel->model->mins.y + currModel->placement.origin.y; - currModelInst->mins.z = currModel->model->mins.z + currModel->placement.origin.z; - currModelInst->maxs.x = currModel->model->maxs.x + currModel->placement.origin.x; - currModelInst->maxs.y = currModel->model->maxs.y + currModel->placement.origin.y; - currModelInst->maxs.z = currModel->model->maxs.z + currModel->placement.origin.z; - - currModel->cullDist = currModelJs["cullDist"]; - currModel->flags = currModelJs["flags"]; - currModel->primaryLightIndex = (unsigned char)currModelJs["primaryLightIndex"]; - currModel->reflectionProbeIndex = (unsigned char)currModelJs["reflectionProbeIndex"]; - - // unknown use / unused - currModel->smid = i; - memset(&currModel->lightingSH, 0, sizeof(GfxLightingSHQuantized)); - currModel->invScaleSq = 0.0f; - currModel->lightingHandle = 0; - currModel->colorsIndex = 0; - currModel->visibility = 0; - - // setting these to NULL makes any static/baked lighting go black when not rendered by real-time lighting or in a shadow - // TODO: calculate lighting and store it here - currModel->lmapVertexInfo[0].numLmapVertexColors = 0; - currModel->lmapVertexInfo[0].lmapVertexColors = NULL; - currModel->lmapVertexInfo[1].numLmapVertexColors = 0; - currModel->lmapVertexInfo[1].lmapVertexColors = NULL; - currModel->lmapVertexInfo[2].numLmapVertexColors = 0; - currModel->lmapVertexInfo[2].lmapVertexColors = NULL; - currModel->lmapVertexInfo[3].numLmapVertexColors = 0; - currModel->lmapVertexInfo[3].lmapVertexColors = NULL; - } - */ - - // all visdata is alligned by 128 - gfxWorld->dpvs.smodelVisDataCount = CMUtil::allignBy128(modelCount); - gfxWorld->dpvs.smodelVisData[0] = new char[modelCount]; - gfxWorld->dpvs.smodelVisData[1] = new char[modelCount]; - gfxWorld->dpvs.smodelVisData[2] = new char[modelCount]; - gfxWorld->dpvs.smodelVisDataCameraSaved = new char[modelCount]; - gfxWorld->dpvs.smodelCastsShadow = new char[modelCount]; - for (unsigned int i = 0; i < modelCount; i++) - { - if ((gfxWorld->dpvs.smodelDrawInsts[i].flags & SMODEL_FLAG_NO_SHADOW) == 0) - gfxWorld->dpvs.smodelCastsShadow[i] = 1; - else - gfxWorld->dpvs.smodelCastsShadow[i] = 0; - } - memset(gfxWorld->dpvs.smodelVisData[0], 0, modelCount); - memset(gfxWorld->dpvs.smodelVisData[1], 0, modelCount); - memset(gfxWorld->dpvs.smodelVisData[2], 0, modelCount); - memset(gfxWorld->dpvs.smodelVisDataCameraSaved, 0, modelCount); - - gfxWorld->dpvs.usageCount = 0; - } - - void cleanGfxWorld(GfxWorld* gfxWorld) - { - gfxWorld->checksum = 0; - - // Remove Coronas - gfxWorld->coronaCount = 0; - gfxWorld->coronas = NULL; - - // Remove exposure volumes - gfxWorld->exposureVolumeCount = 0; - gfxWorld->exposureVolumes = NULL; - gfxWorld->exposureVolumePlaneCount = 0; - gfxWorld->exposureVolumePlanes = NULL; - - // Remove hero lights - gfxWorld->heroLightCount = 0; - gfxWorld->heroLights = NULL; - gfxWorld->heroLightTreeCount = 0; - gfxWorld->heroLightTree = NULL; - - // remove LUT data - gfxWorld->lutVolumeCount = 0; - gfxWorld->lutVolumes = NULL; - gfxWorld->lutVolumePlaneCount = 0; - gfxWorld->lutVolumePlanes = NULL; - - // remove occluders - gfxWorld->numOccluders = 0; - gfxWorld->occluders = NULL; - - // remove Siege Skins - gfxWorld->numSiegeSkinInsts = 0; - gfxWorld->siegeSkinInsts = NULL; - - // remove outdoor bounds - gfxWorld->numOutdoorBounds = 0; - gfxWorld->outdoorBounds = NULL; - - // remove materials - gfxWorld->ropeMaterial = NULL; - gfxWorld->lutMaterial = NULL; - gfxWorld->waterMaterial = NULL; - gfxWorld->coronaMaterial = NULL; - - // remove shadow maps - gfxWorld->shadowMapVolumeCount = 0; - gfxWorld->shadowMapVolumes = NULL; - gfxWorld->shadowMapVolumePlaneCount = 0; - gfxWorld->shadowMapVolumePlanes = NULL; - - // remove stream info - gfxWorld->streamInfo.aabbTreeCount = 0; - gfxWorld->streamInfo.aabbTrees = NULL; - gfxWorld->streamInfo.leafRefCount = 0; - gfxWorld->streamInfo.leafRefs = NULL; - - // remove sun data - memset(&gfxWorld->sun, 0, sizeof(sunflare_t)); - gfxWorld->sun.hasValidData = false; - - // Remove Water - gfxWorld->waterDirection = 0.0f; - gfxWorld->waterBuffers[0].buffer = NULL; - gfxWorld->waterBuffers[0].bufferSize = NULL; - gfxWorld->waterBuffers[1].buffer = NULL; - gfxWorld->waterBuffers[1].bufferSize = NULL; - - // Remove Fog - gfxWorld->worldFogModifierVolumeCount = 0; - gfxWorld->worldFogModifierVolumes = NULL; - gfxWorld->worldFogModifierVolumePlaneCount = 0; - gfxWorld->worldFogModifierVolumePlanes = NULL; - gfxWorld->worldFogVolumeCount = 0; - gfxWorld->worldFogVolumes = NULL; - gfxWorld->worldFogVolumePlaneCount = 0; - gfxWorld->worldFogVolumePlanes = NULL; - - // materialMemory is unused - gfxWorld->materialMemoryCount = 0; - gfxWorld->materialMemory = NULL; - - // sunLight is overwritten by the game, just needs to be a valid pointer - gfxWorld->sunLight = new GfxLight; - memset(gfxWorld->sunLight, 0, sizeof(GfxLight)); - } - - void overwriteGfxLights(GfxWorld* gfxWorld) - { - // there must be 2 or more lights, first is the default light and second is the sun - gfxWorld->primaryLightCount = 2; - gfxWorld->sunPrimaryLightIndex = 1; // the sun is always index 1 - - gfxWorld->shadowGeom = new GfxShadowGeometry[gfxWorld->primaryLightCount]; - for (int i = 0; i < gfxWorld->primaryLightCount; i++) - { - gfxWorld->shadowGeom[i].smodelCount = 0; - gfxWorld->shadowGeom[i].surfaceCount = 0; - gfxWorld->shadowGeom[i].smodelIndex = NULL; - gfxWorld->shadowGeom[i].sortedSurfIndex = NULL; - } - - gfxWorld->lightRegion = new GfxLightRegion[gfxWorld->primaryLightCount]; - for (int i = 0; i < gfxWorld->primaryLightCount; i++) - { - gfxWorld->lightRegion[i].hullCount = 0; - gfxWorld->lightRegion[i].hulls = NULL; - } - - int lightEntShadowVisSize = (gfxWorld->primaryLightCount - gfxWorld->sunPrimaryLightIndex - 1) * 8192; - if (lightEntShadowVisSize != 0) - { - gfxWorld->primaryLightEntityShadowVis = new unsigned int[lightEntShadowVisSize]; - memset(gfxWorld->primaryLightEntityShadowVis, 0, lightEntShadowVisSize * sizeof(unsigned int)); - } - else - { - gfxWorld->primaryLightEntityShadowVis = NULL; - } - } - - void overwriteLightGrid(GfxWorld* gfxWorld) - { - gfxWorld->lightGrid.mins[0] = 0; - gfxWorld->lightGrid.mins[1] = 0; - gfxWorld->lightGrid.mins[2] = 0; - gfxWorld->lightGrid.maxs[0] = 0; - gfxWorld->lightGrid.maxs[1] = 0; - gfxWorld->lightGrid.maxs[2] = 0; - - gfxWorld->lightGrid.rowAxis = 0; // default value - gfxWorld->lightGrid.colAxis = 1; // default value - gfxWorld->lightGrid.sunPrimaryLightIndex = 1; // the sun is always index 1 - gfxWorld->lightGrid.offset = 0.0f; - - // if rowDataStart's first value is -1, it will not look up any lightmap data - gfxWorld->lightGrid.rowDataStart = new uint16_t[1]; - *gfxWorld->lightGrid.rowDataStart = (uint16_t)(-1); - - gfxWorld->lightGrid.rawRowDataSize = 0; - gfxWorld->lightGrid.rawRowData = NULL; - - gfxWorld->lightGrid.colorCount = 0; - gfxWorld->lightGrid.colors = NULL; - - gfxWorld->lightGrid.coeffCount = 0; - gfxWorld->lightGrid.coeffs = NULL; - - gfxWorld->lightGrid.entryCount = 0; - gfxWorld->lightGrid.entries = NULL; - - gfxWorld->lightGrid.skyGridVolumeCount = 0; - gfxWorld->lightGrid.skyGridVolumes = NULL; - } - - void updateGfxCells(GfxWorld* gfxWorld) - { - // Cells are basically data used to determine what can be seen and what cant be seen - // Right now custom maps have no optimisation so there is only 1 cell - int cellCount = 1; - - gfxWorld->cellBitsCount = ((cellCount + 127) >> 3) & 0x1FFFFFF0; - - int cellCasterBitsCount = cellCount * ((cellCount + 31) / 32); - gfxWorld->cellCasterBits = new unsigned int[cellCasterBitsCount]; - memset(gfxWorld->cellCasterBits, 0x00, cellCasterBitsCount * sizeof(unsigned int)); - - gfxWorld->cells = new GfxCell[cellCount]; - gfxWorld->cells[0].portalCount = 0; - gfxWorld->cells[0].portals = NULL; - gfxWorld->cells[0].reflectionProbeCount = 0; - gfxWorld->cells[0].reflectionProbes = NULL; - gfxWorld->cells[0].mins.x = gfxWorld->mins.x; - gfxWorld->cells[0].mins.y = gfxWorld->mins.y; - gfxWorld->cells[0].mins.z = gfxWorld->mins.z; - gfxWorld->cells[0].maxs.x = gfxWorld->maxs.x; - gfxWorld->cells[0].maxs.y = gfxWorld->maxs.y; - gfxWorld->cells[0].maxs.z = gfxWorld->maxs.z; - - // AABB trees are used to detect what should be rendered and what shouldn't - // Just use the first AABB node to hold all models, no optimisation but all models/surfaces wil lbe drawn - gfxWorld->cells[0].aabbTreeCount = 1; - gfxWorld->cells[0].aabbTree = new GfxAabbTree[gfxWorld->cells[0].aabbTreeCount]; - gfxWorld->cells[0].aabbTree[0].childCount = 0; - gfxWorld->cells[0].aabbTree[0].childrenOffset = 0; - gfxWorld->cells[0].aabbTree[0].startSurfIndex = 0; - gfxWorld->cells[0].aabbTree[0].surfaceCount = gfxWorld->surfaceCount; - gfxWorld->cells[0].aabbTree[0].smodelIndexCount = gfxWorld->dpvs.smodelCount; - gfxWorld->cells[0].aabbTree[0].smodelIndexes = new unsigned short[gfxWorld->dpvs.smodelCount]; - for (unsigned short i = 0; i < gfxWorld->dpvs.smodelCount; i++) - { - gfxWorld->cells[0].aabbTree[0].smodelIndexes[i] = i; - } - gfxWorld->cells[0].aabbTree[0].mins.x = gfxWorld->mins.x; - gfxWorld->cells[0].aabbTree[0].mins.y = gfxWorld->mins.y; - gfxWorld->cells[0].aabbTree[0].mins.z = gfxWorld->mins.z; - gfxWorld->cells[0].aabbTree[0].maxs.x = gfxWorld->maxs.x; - gfxWorld->cells[0].aabbTree[0].maxs.y = gfxWorld->maxs.y; - gfxWorld->cells[0].aabbTree[0].maxs.z = gfxWorld->maxs.z; - - gfxWorld->dpvsPlanes.cellCount = cellCount; - - int sceneEntCellBitsCount = cellCount * 512; - gfxWorld->dpvsPlanes.sceneEntCellBits = new unsigned int[sceneEntCellBitsCount]; - memset(gfxWorld->dpvsPlanes.sceneEntCellBits, 0x00, sceneEntCellBitsCount * sizeof(unsigned int)); - - // nodes have the struct mnode_t, and there must be at least 1 node - // Nodes mnode_t.cellIndex indexes gfxWorld->cells - // and (mnode_t.cellIndex - (world->dpvsPlanes.cellCount + 1) indexes world->dpvsPlanes.planes - gfxWorld->nodeCount = 1; - gfxWorld->planeCount = 0; - gfxWorld->dpvsPlanes.nodes = new uint16_t[gfxWorld->nodeCount]; - gfxWorld->dpvsPlanes.nodes[0] = 1; // nodes reference cells by index + 1 - gfxWorld->dpvsPlanes.planes = NULL; - } - - void updateWorldBounds(GfxWorld* gfxWorld) - { - gfxWorld->mins.x = 0.0f; - gfxWorld->mins.y = 0.0f; - gfxWorld->mins.z = 0.0f; - gfxWorld->maxs.x = 0.0f; - gfxWorld->maxs.y = 0.0f; - gfxWorld->maxs.z = 0.0f; - - for (int i = 0; i < gfxWorld->surfaceCount; i++) - { - CMUtil::calcNewBounds(&gfxWorld->dpvs.surfaces[i].bounds[0], &gfxWorld->dpvs.surfaces[i].bounds[1], &gfxWorld->mins, &gfxWorld->maxs); - } - } - - void overwriteModels(GfxWorld* gfxWorld) - { - gfxWorld->modelCount = 1; - gfxWorld->models = new GfxBrushModel[gfxWorld->modelCount]; - - // first model is the world model, the world json doesn't include it so its added here - gfxWorld->models[0].startSurfIndex = 0; - gfxWorld->models[0].surfaceCount = gfxWorld->surfaceCount; - gfxWorld->models[0].bounds[0].x = gfxWorld->mins.x; - gfxWorld->models[0].bounds[0].y = gfxWorld->mins.y; - gfxWorld->models[0].bounds[0].z = gfxWorld->mins.z; - gfxWorld->models[0].bounds[1].x = gfxWorld->maxs.x; - gfxWorld->models[0].bounds[1].y = gfxWorld->maxs.y; - gfxWorld->models[0].bounds[1].z = gfxWorld->maxs.z; - memset(&gfxWorld->models[0].writable, 0, sizeof(GfxBrushModelWritable)); - - // for (int i = 1; i < gfxWorld->modelCount; i++) - //{ - // auto currEntModel = &gfxWorld->models[i]; - // json currEntModelJs = js["entityModels"][i - 1]; - // - // currEntModel->bounds[0].x = currEntModelJs["mins"]["x"]; - // currEntModel->bounds[0].y = currEntModelJs["mins"]["y"]; - // currEntModel->bounds[0].z = currEntModelJs["mins"]["z"]; - // currEntModel->bounds[1].x = currEntModelJs["maxs"]["x"]; - // currEntModel->bounds[1].y = currEntModelJs["maxs"]["y"]; - // currEntModel->bounds[1].z = currEntModelJs["maxs"]["z"]; - // - // currEntModel->surfaceCount = 0; - // currEntModel->startSurfIndex = (unsigned int)(-1); - // memset(&currEntModel->writable, 0, sizeof(GfxBrushModelWritable)); - // } - } - - void updateSunData(GfxWorld* gfxWorld) - { - // default values taken from mp_dig - gfxWorld->sunParse.fogTransitionTime = 0.001; - gfxWorld->sunParse.name[0] = 0x00; - - gfxWorld->sunParse.initWorldSun->control = 0; - gfxWorld->sunParse.initWorldSun->exposure = 2.5f; - gfxWorld->sunParse.initWorldSun->angles.x = -29.0f; - gfxWorld->sunParse.initWorldSun->angles.y = 254.0f; - gfxWorld->sunParse.initWorldSun->angles.z = 0.0f; - gfxWorld->sunParse.initWorldSun->sunCd.x = 1.0f; - gfxWorld->sunParse.initWorldSun->sunCd.y = 0.89f; - gfxWorld->sunParse.initWorldSun->sunCd.z = 0.69f; - gfxWorld->sunParse.initWorldSun->sunCd.w = 13.5f; - gfxWorld->sunParse.initWorldSun->ambientColor.x = 0.0f; - gfxWorld->sunParse.initWorldSun->ambientColor.y = 0.0f; - gfxWorld->sunParse.initWorldSun->ambientColor.z = 0.0f; - gfxWorld->sunParse.initWorldSun->ambientColor.w = 0.0f; - gfxWorld->sunParse.initWorldSun->skyColor.x = 0.0f; - gfxWorld->sunParse.initWorldSun->skyColor.y = 0.0f; - gfxWorld->sunParse.initWorldSun->skyColor.z = 0.0f; - gfxWorld->sunParse.initWorldSun->skyColor.w = 0.0f; - gfxWorld->sunParse.initWorldSun->sunCs.x = 0.0f; - gfxWorld->sunParse.initWorldSun->sunCs.y = 0.0f; - gfxWorld->sunParse.initWorldSun->sunCs.z = 0.0f; - gfxWorld->sunParse.initWorldSun->sunCs.w = 0.0f; - - gfxWorld->sunParse.initWorldFog->baseDist = 150.0f; - gfxWorld->sunParse.initWorldFog->baseHeight = -100.0f; - gfxWorld->sunParse.initWorldFog->fogColor.x = 2.35f; - gfxWorld->sunParse.initWorldFog->fogColor.y = 3.10f; - gfxWorld->sunParse.initWorldFog->fogColor.z = 3.84f; - gfxWorld->sunParse.initWorldFog->fogOpacity = 0.52f; - gfxWorld->sunParse.initWorldFog->halfDist = 4450.f; - gfxWorld->sunParse.initWorldFog->halfHeight = 2000.f; - gfxWorld->sunParse.initWorldFog->sunFogColor.x = 5.27f; - gfxWorld->sunParse.initWorldFog->sunFogColor.y = 4.73f; - gfxWorld->sunParse.initWorldFog->sunFogColor.z = 3.88f; - gfxWorld->sunParse.initWorldFog->sunFogInner = 0.0f; - gfxWorld->sunParse.initWorldFog->sunFogOpacity = 0.67f; - gfxWorld->sunParse.initWorldFog->sunFogOuter = 80.84f; - gfxWorld->sunParse.initWorldFog->sunFogPitch = -29.0f; - gfxWorld->sunParse.initWorldFog->sunFogYaw = 254.0f; - } - - void updateReflectionProbeData(GfxWorld* gfxWorld) - { - gfxWorld->draw.reflectionProbeCount = 1; - - gfxWorld->draw.reflectionProbeTextures = new GfxTexture[gfxWorld->draw.reflectionProbeCount]; - memset(gfxWorld->draw.reflectionProbeTextures, 0, sizeof(GfxTexture) * gfxWorld->draw.reflectionProbeCount); - - gfxWorld->draw.reflectionProbes = new GfxReflectionProbe[gfxWorld->draw.reflectionProbeCount]; - - // default values taken from mp_dig - gfxWorld->draw.reflectionProbes[0].mipLodBias = -8.0; - - gfxWorld->draw.reflectionProbes[0].origin.x = 0.0f; - gfxWorld->draw.reflectionProbes[0].origin.y = 0.0f; - gfxWorld->draw.reflectionProbes[0].origin.z = 0.0f; - - gfxWorld->draw.reflectionProbes[0].lightingSH.V0.x = 0.0f; - gfxWorld->draw.reflectionProbes[0].lightingSH.V0.y = 0.0f; - gfxWorld->draw.reflectionProbes[0].lightingSH.V0.z = 0.0f; - gfxWorld->draw.reflectionProbes[0].lightingSH.V0.w = 0.0f; - gfxWorld->draw.reflectionProbes[0].lightingSH.V1.x = 0.0f; - gfxWorld->draw.reflectionProbes[0].lightingSH.V1.y = 0.0f; - gfxWorld->draw.reflectionProbes[0].lightingSH.V1.z = 0.0f; - gfxWorld->draw.reflectionProbes[0].lightingSH.V1.w = 0.0f; - gfxWorld->draw.reflectionProbes[0].lightingSH.V2.x = 0.0f; - gfxWorld->draw.reflectionProbes[0].lightingSH.V2.y = 0.0f; - gfxWorld->draw.reflectionProbes[0].lightingSH.V2.z = 0.0f; - gfxWorld->draw.reflectionProbes[0].lightingSH.V2.w = 0.0f; - - gfxWorld->draw.reflectionProbes[0].probeVolumeCount = 0; - gfxWorld->draw.reflectionProbes[0].probeVolumes = NULL; - - std::string probeImageName = "*reflection_probe0"; - auto probeImageAsset = m_context.LoadDependency(probeImageName); - if (probeImageAsset == NULL) - { - printf("ERROR! unable to find image %s!\n", probeImageName.c_str()); - hasLinkFailed = true; - return; - } - gfxWorld->draw.reflectionProbes[0].reflectionImage = probeImageAsset->Asset(); - } - - void overwriteLightmapData(GfxWorld* gfxWorld) - { - gfxWorld->draw.lightmapCount = 1; - - gfxWorld->draw.lightmapPrimaryTextures = new GfxTexture[gfxWorld->draw.lightmapCount]; - gfxWorld->draw.lightmapSecondaryTextures = new GfxTexture[gfxWorld->draw.lightmapCount]; - gfxWorld->draw.lightmaps = new GfxLightmapArray[gfxWorld->draw.lightmapCount]; - - // always set to 0 - memset(gfxWorld->draw.lightmapPrimaryTextures, 0, sizeof(GfxTexture) * gfxWorld->draw.lightmapCount); - memset(gfxWorld->draw.lightmapSecondaryTextures, 0, sizeof(GfxTexture) * gfxWorld->draw.lightmapCount); - - std::string secondaryTexture = "*lightmap0_secondary"; - auto secondaryTextureAsset = m_context.LoadDependency(secondaryTexture); - if (secondaryTextureAsset == NULL) - { - printf("ERROR! unable to find lightmap image %s!\n", secondaryTexture.c_str()); - hasLinkFailed = true; - return; - } - gfxWorld->draw.lightmaps[0].primary = NULL; // always NULL - gfxWorld->draw.lightmaps[0].secondary = secondaryTextureAsset->Asset(); - } - - void overwriteSkyBox(GfxWorld* gfxWorld) - { - const char* skyBoxName = "skybox_mp_dig"; - gfxWorld->skyBoxModel = skyBoxName; - - if (m_context.LoadDependency(skyBoxName) == NULL) - { - printf("WARN: Unable to find the skybox model %s\n", skyBoxName); - } - - // default skybox values from mp_dig - gfxWorld->skyDynIntensity.angle0 = 0.0f; - gfxWorld->skyDynIntensity.angle1 = 0.0f; - gfxWorld->skyDynIntensity.factor0 = 1.0f; - gfxWorld->skyDynIntensity.factor1 = 1.0f; - } - - void updateDynEntData(GfxWorld* gfxWorld) - { - gfxWorld->dpvsDyn.dynEntClientCount[0] = DYN_ENT_COUNT + 256; - gfxWorld->dpvsDyn.dynEntClientCount[1] = 0; - gfxWorld->dpvsDyn.dynEntClientWordCount[0] = 1; // needs to be at least 1 - gfxWorld->dpvsDyn.dynEntClientWordCount[1] = 0; - gfxWorld->dpvsDyn.usageCount = 0; - - int dynEntCellBitsSize = gfxWorld->dpvsDyn.dynEntClientWordCount[0] * gfxWorld->dpvsPlanes.cellCount; - gfxWorld->dpvsDyn.dynEntCellBits[0] = new unsigned int[dynEntCellBitsSize]; - gfxWorld->dpvsDyn.dynEntCellBits[1] = NULL; - memset(gfxWorld->dpvsDyn.dynEntCellBits[0], 0, sizeof(unsigned int) * dynEntCellBitsSize); - - int dynEntVisData0Size = gfxWorld->dpvsDyn.dynEntClientWordCount[0] * 32; - gfxWorld->dpvsDyn.dynEntVisData[0][0] = new char[dynEntVisData0Size]; - gfxWorld->dpvsDyn.dynEntVisData[0][1] = new char[dynEntVisData0Size]; - gfxWorld->dpvsDyn.dynEntVisData[0][2] = new char[dynEntVisData0Size]; - gfxWorld->dpvsDyn.dynEntVisData[1][0] = NULL; - gfxWorld->dpvsDyn.dynEntVisData[1][1] = NULL; - gfxWorld->dpvsDyn.dynEntVisData[1][2] = NULL; - memset(gfxWorld->dpvsDyn.dynEntVisData[0][0], 0, dynEntVisData0Size); - memset(gfxWorld->dpvsDyn.dynEntVisData[0][1], 0, dynEntVisData0Size); - memset(gfxWorld->dpvsDyn.dynEntVisData[0][2], 0, dynEntVisData0Size); - - int dynEntShadowVisCount = gfxWorld->dpvsDyn.dynEntClientCount[0] * (gfxWorld->primaryLightCount - gfxWorld->sunPrimaryLightIndex - 1); - gfxWorld->primaryLightDynEntShadowVis[0] = new unsigned int[dynEntShadowVisCount]; - gfxWorld->primaryLightDynEntShadowVis[1] = NULL; - memset(gfxWorld->primaryLightDynEntShadowVis[0], 0, sizeof(unsigned int) * dynEntShadowVisCount); - - gfxWorld->sceneDynModel = new GfxSceneDynModel[gfxWorld->dpvsDyn.dynEntClientCount[0]]; - gfxWorld->sceneDynBrush = NULL; - memset(gfxWorld->sceneDynModel, 0, sizeof(GfxSceneDynModel) * gfxWorld->dpvsDyn.dynEntClientCount[0]); - } - - void updateOutdoors(GfxWorld* gfxWorld) - { - float xRecip = 1.0f / (gfxWorld->maxs.x - gfxWorld->mins.x); - float xScale = -(xRecip * gfxWorld->mins.x); - - float yRecip = 1.0f / (gfxWorld->maxs.y - gfxWorld->mins.y); - float yScale = -(yRecip * gfxWorld->mins.y); - - float zRecip = 1.0f / (gfxWorld->maxs.z - gfxWorld->mins.z); - float zScale = -(zRecip * gfxWorld->mins.z); - - memset(gfxWorld->outdoorLookupMatrix, 0, sizeof(gfxWorld->outdoorLookupMatrix)); - - gfxWorld->outdoorLookupMatrix[0].x = xRecip; - gfxWorld->outdoorLookupMatrix[1].y = yRecip; - gfxWorld->outdoorLookupMatrix[2].z = zRecip; - gfxWorld->outdoorLookupMatrix[3].x = xScale; - gfxWorld->outdoorLookupMatrix[3].y = yScale; - gfxWorld->outdoorLookupMatrix[3].z = zScale; - gfxWorld->outdoorLookupMatrix[3].w = 1.0f; - - std::string outdoorImageName = std::string("$outdoor"); - auto outdoorImageAsset = m_context.LoadDependency(outdoorImageName); - if (outdoorImageAsset == NULL) - { - printf("ERROR! unable to find image $outdoor, this will crash your game!\n"); - hasLinkFailed = true; - return; - } - gfxWorld->outdoorImage = outdoorImageAsset->Asset(); - } - - void createGfxWorld(customMapInfo* projInfo) - { - GfxWorld* gfxWorld = new GfxWorld; - gfxWorld->baseName = _strdup(projInfo->name.c_str()); - gfxWorld->name = _strdup(projInfo->bspName.c_str()); - - // Default values taken from mp_dig - gfxWorld->lightingFlags = 0; - gfxWorld->lightingQuality = 4096; - - cleanGfxWorld(gfxWorld); - - overwriteMapSurfaces(projInfo, gfxWorld); - - overwriteMapSModels(gfxWorld); - - overwriteLightmapData(gfxWorld); - - overwriteSkyBox(gfxWorld); - - updateReflectionProbeData(gfxWorld); - - // world bounds are based on surface mins/maxs - // Other update functions depend on the bounds being set first - updateWorldBounds(gfxWorld); - - updateOutdoors(gfxWorld); - - // gfx cells depend on surface/smodel count - updateGfxCells(gfxWorld); - - overwriteLightGrid(gfxWorld); - - overwriteGfxLights(gfxWorld); - - overwriteModels(gfxWorld); - - updateSunData(gfxWorld); - - updateDynEntData(gfxWorld); - - m_context.AddAsset(gfxWorld->name, gfxWorld); - } - - void aabbCalcOriginAndHalfSize(vec3_t* mins, vec3_t* maxs, vec3_t* out_origin, vec3_t* out_halfSize) - { - // Origin is the midpoint: (min + max) / 2 - vec3_t temp; - temp.x = mins->x + maxs->x; - temp.y = mins->y + maxs->y; - temp.z = mins->z + maxs->z; - out_origin->x = temp.x * 0.5f; - out_origin->y = temp.y * 0.5f; - out_origin->z = temp.z * 0.5f; - - // Half-size is half the difference: (max - min) / 2 - temp.x = maxs->x - mins->x; - temp.y = maxs->y - mins->y; - temp.z = maxs->z - mins->z; - out_halfSize->x = temp.x * 0.5f; - out_halfSize->y = temp.y * 0.5f; - out_halfSize->z = temp.z * 0.5f; - } - - bool CM_IsEdgeWalkable(clipMap_t* clipMap, int triIndex, int edgeIndex) - { - - unsigned __int8 edgeBitMask = 1 << ((triIndex + edgeIndex + 2 * triIndex) & 7); - - 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) - { - if (node->isLeaf) - { - (*numLeafs)++; - // there won't be an AABB tree when objectList is empty - if (node->u.leaf->getObjectCount() > 0) - { - *numAABBTrees += node->u.leaf->getObjectCount() + 1; - - if (node->u.leaf->getObjectCount() > *maxObjsPerLeaf) - *maxObjsPerLeaf = node->u.leaf->getObjectCount(); - } - } - else - { - (*numPlanes)++; - (*numNodes)++; - traverseBSPTreeForCounts(node->u.node->front, numPlanes, numNodes, numLeafs, numAABBTrees, maxObjsPerLeaf); - traverseBSPTreeForCounts(node->u.node->back, numPlanes, numNodes, numLeafs, numAABBTrees, maxObjsPerLeaf); - } - } - - vec3_t normalX = {1.0f, 0.0f, 0.0f}; - vec3_t normalY = {0.0f, 1.0f, 0.0f}; - vec3_t normalZ = {0.0f, 0.0f, 1.0f}; - - int currPlaneCount = 0; - int currNodeCount = 0; - int currLeafCount = 0; - int currAABBCount = 0; - - int addAABBTreeFromLeaf(BSPTree* node, clipMap_t* clipMap) - { - _ASSERT(node->isLeaf); - - int objectCount = node->u.leaf->getObjectCount(); - int firstAABBIndex = currAABBCount; - currAABBCount += objectCount + 1; - - // calculate root AABB node mins and maxs - // cannot convert mins and maxs coord to BO2 directly as this will result in incorrect mins and maxs - // so we have to recompute every min and max, not hard just tedious - int firstPartitionIndex = node->u.leaf->getObject(0)->partitionIndex; - auto firstPartition = &clipMap->partitions[firstPartitionIndex]; - uint16_t* firstTri = clipMap->triIndices[firstPartition->firstTri]; - vec3_t* firstVert = &clipMap->verts[firstTri[0]]; - vec3_t aabbMins; - vec3_t aabbMaxs; - aabbMins.x = firstVert->x; - aabbMins.y = firstVert->y; - aabbMins.z = firstVert->z; - aabbMaxs.x = firstVert->x; - aabbMaxs.y = firstVert->y; - aabbMaxs.z = firstVert->z; - for (int i = 0; i < objectCount; i++) - { - int currPartitionIndex = node->u.leaf->getObject(i)->partitionIndex; - auto currPartition = &clipMap->partitions[currPartitionIndex]; - - for (int k = 0; k < currPartition->triCount; k++) - { - uint16_t* tri = clipMap->triIndices[currPartition->firstTri + k]; - for (int l = 0; l < 3; l++) - { - uint16_t vertIndex = tri[l]; - vec3_t vertCoord = clipMap->verts[vertIndex]; - CMUtil::calcNewBoundsWithPoint(&vertCoord, &aabbMins, &aabbMaxs); - } - } - } - // create root AABB node - CollisionAabbTree* rootAABB = &clipMap->aabbTrees[firstAABBIndex]; - aabbCalcOriginAndHalfSize(&aabbMins, &aabbMaxs, &rootAABB->origin, &rootAABB->halfSize); - rootAABB->materialIndex = 0; - rootAABB->childCount = objectCount; - rootAABB->u.firstChildIndex = firstAABBIndex + 1; - - // populate child AABB nodes - for (int i = 0; i < objectCount; i++) - { - CollisionAabbTree* currAabbTree = &clipMap->aabbTrees[rootAABB->u.firstChildIndex + i]; - int currPartitionIndex = node->u.leaf->getObject(i)->partitionIndex; - - currAabbTree->materialIndex = 0; - currAabbTree->childCount = 0; - currAabbTree->u.partitionIndex = currPartitionIndex; - - // calculate partition origin and half size - CollisionPartition* aabbPartition = &clipMap->partitions[currPartitionIndex]; - uint16_t firstUind = clipMap->info.uinds[aabbPartition->fuind]; - vec3_t* firstVertex = &clipMap->verts[firstUind]; - vec3_t mins; - vec3_t maxs; - mins.x = firstVertex->x; - mins.y = firstVertex->y; - mins.z = firstVertex->z; - maxs.x = firstVertex->x; - maxs.y = firstVertex->y; - maxs.z = firstVertex->z; - for (int i = 1; i < aabbPartition->nuinds; i++) - { - uint16_t currUind = clipMap->info.uinds[aabbPartition->fuind + i]; - vec3_t* currVertex = &clipMap->verts[currUind]; - - CMUtil::calcNewBoundsWithPoint(currVertex, &mins, &maxs); - } - - aabbCalcOriginAndHalfSize(&mins, &maxs, &currAabbTree->origin, &currAabbTree->halfSize); - - } - - return firstAABBIndex; - } - - // returns the index corresponding to the BSPTree* node parsed - int16_t populateBSPTree_r(clipMap_t* clipMap, BSPTree* node) - { - new cplane_s; - new cNode_t; - new cLeaf_s; - new CollisionAabbTree; - - if (node->isLeaf) - { - int currLeafIndex = currLeafCount; - currLeafCount++; - cLeaf_s* currLeaf = &clipMap->leafs[currLeafIndex]; - - currLeaf->cluster = 0; - currLeaf->brushContents = 0; // no brushes used so contents is 0 - currLeaf->terrainContents = LEAF_TERRAIN_CONTENTS; // clipMap->cmodels[0].leaf.terrainContents takes prescedence - - // unused when leafBrushNode == 0 - currLeaf->mins.x = 0.0f; - currLeaf->mins.y = 0.0f; - currLeaf->mins.z = 0.0f; - currLeaf->maxs.x = 0.0f; - currLeaf->maxs.y = 0.0f; - currLeaf->maxs.z = 0.0f; - currLeaf->leafBrushNode = 0; - - if (node->u.leaf->getObjectCount() > 0) - { - currLeaf->firstCollAabbIndex = addAABBTreeFromLeaf(node, clipMap); - currLeaf->collAabbCount = 1; - } - else - { - currLeaf->firstCollAabbIndex = 0; - currLeaf->collAabbCount = 0; - } - - return -1 - currLeafIndex; - } - else - { - cplane_s* currPlane = &clipMap->info.planes[currPlaneCount]; - currPlaneCount++; - - if (node->u.node->axis == AXIS_X) - { - // X is unchanged when going from OGL x -> BO2 x - currPlane->normal = normalX; - - // converting OGL -> BO2 X coords doesn't change the x coords at all, so - // the dist stays the same - currPlane->dist = node->u.node->distance; - } - else - { - // converting OGL -> BO2 Z coords negates the z coords and sets it to the y coord. - // convert the z normal to the y normal, but don't negate it. Negative normals don't do - // what is expected when the game uses them - _ASSERT(node->u.node->axis == AXIS_Z); - currPlane->normal = normalY; - - // converting OGL -> BO2 Z coords negates the z coords and sets it to the y coord. - // just negate it here as it is just the distance from the orgin along the axis - currPlane->dist = -node->u.node->distance; - } - - bool foundType = false; - if (currPlane->normal.x == 1.0f) - { - _ASSERT(!foundType); - foundType = true; - currPlane->type = 0; - } - else if (currPlane->normal.y == 1.0f) - { - _ASSERT(!foundType); - foundType = true; - currPlane->type = 1; - } - else if (currPlane->normal.z == 1.0f) - { - _ASSERT(!foundType); - foundType = true; - currPlane->type = 2; - } - else - _ASSERT(foundType); - - currPlane->signbits = 0; - if (currPlane->normal.x < 0.0f) - currPlane->signbits |= 1; - if (currPlane->normal.y < 0.0f) - currPlane->signbits |= 2; - if (currPlane->normal.z < 0.0f) - currPlane->signbits |= 4; - - currPlane->pad[0] = 0; - currPlane->pad[1] = 0; - - int currNodeIndex = currNodeCount; - currNodeCount++; - cNode_t* currNode = &clipMap->nodes[currNodeIndex]; - - currNode->plane = currPlane; - // Reason for the front and back flip (due to the hacky nature of making the mins and maxs work (see createClipMap)): - // after converting between OGL and BO2 coords and when and updating the normal from Z -> Y, - // the normal vector flips and objects behind the plane are now in front, and vise versa - // so the back node now represents the front, and the front node represents the back. - // Do the OGL -> Bo2 coord change on paper and it will make sense - if (currPlane->type == 1) - { - currNode->children[1] = populateBSPTree_r(clipMap, node->u.node->front); - currNode->children[0] = populateBSPTree_r(clipMap, node->u.node->back); - } - else - { - currNode->children[0] = populateBSPTree_r(clipMap, node->u.node->front); - currNode->children[1] = populateBSPTree_r(clipMap, node->u.node->back); - } - - return currNodeIndex; - } - } - - void populateBSPTree(clipMap_t* clipMap, BSPTree* tree) - { - int numPlanes = 0; - int numNodes = 0; - int numLeafs = 0; - int numAABBTrees = 0; - int maxObjsPerLeaf = 0; - - traverseBSPTreeForCounts(tree, &numPlanes, &numNodes, &numLeafs, &numAABBTrees, &maxObjsPerLeaf); - - printf("Max Objects per leaf: %i\n", maxObjsPerLeaf); - - clipMap->info.planeCount = numPlanes; - clipMap->info.planes = new cplane_s[clipMap->info.planeCount]; - clipMap->numNodes = numNodes; - clipMap->nodes = new cNode_t[clipMap->numNodes]; - // aabb trees: each leaf will have their own AABB tree of the objects within it, and the root aabb node will be the parent of every other aabb node. - // therefore, each aabb tree will be of size (numObjects + 1) as the tree needs a root aabb node to reference it's children. - clipMap->aabbTreeCount = numAABBTrees; - clipMap->aabbTrees = new CollisionAabbTree[clipMap->aabbTreeCount]; - - currPlaneCount = 0; - currNodeCount = 0; - currAABBCount = 0; - - // first leaf is always empty - clipMap->numLeafs = numLeafs + 1; - clipMap->leafs = new cLeaf_s[clipMap->numLeafs]; - memset(&clipMap->leafs[0], 0, sizeof(cLeaf_s)); - currLeafCount = 1; - - populateBSPTree_r(clipMap, tree); - - _ASSERT(clipMap->info.planeCount == currPlaneCount); - _ASSERT(clipMap->numNodes == currNodeCount); - _ASSERT(clipMap->numLeafs == currLeafCount); - _ASSERT(clipMap->aabbTreeCount == currAABBCount); - } - - void createClipMap(customMapInfo* projInfo) - { - clipMap_t* clipMap = new clipMap_t; - - clipMap->name = _strdup(projInfo->bspName.c_str()); - - std::string mapEntsName = projInfo->bspName.c_str(); - auto mapEntsAsset = m_context.LoadDependency(mapEntsName); - _ASSERT(mapEntsAsset != NULL); - clipMap->mapEnts = mapEntsAsset->Asset(); - - clipMap->pInfo = NULL; - - clipMap->box_model.mins.x = 0.0f; - clipMap->box_model.mins.y = 0.0f; - clipMap->box_model.mins.z = 0.0f; - clipMap->box_model.maxs.x = 0.0f; - clipMap->box_model.maxs.y = 0.0f; - clipMap->box_model.maxs.z = 0.0f; - clipMap->box_model.radius = 0.0f; - clipMap->box_model.info = NULL; - clipMap->box_model.leaf.mins.x = 3.4028235e38; - clipMap->box_model.leaf.mins.y = 3.4028235e38; - clipMap->box_model.leaf.mins.z = 3.4028235e38; - clipMap->box_model.leaf.maxs.x = -3.4028235e38; // for some reason the maxs are negative, and mins are positive - clipMap->box_model.leaf.maxs.y = -3.4028235e38; - clipMap->box_model.leaf.maxs.z = -3.4028235e38; - clipMap->box_model.leaf.brushContents = -1; - clipMap->box_model.leaf.terrainContents = 0; - clipMap->box_model.leaf.cluster = 0; - clipMap->box_model.leaf.collAabbCount = 0; - clipMap->box_model.leaf.firstCollAabbIndex = 0; - clipMap->box_model.leaf.leafBrushNode = 0; - - clipMap->box_brush = new cbrush_t; - clipMap->box_brush->axial_sflags[0][0] = -1; - clipMap->box_brush->axial_sflags[0][1] = -1; - clipMap->box_brush->axial_sflags[0][2] = -1; - clipMap->box_brush->axial_sflags[1][0] = -1; - clipMap->box_brush->axial_sflags[1][1] = -1; - clipMap->box_brush->axial_sflags[1][2] = -1; - clipMap->box_brush->axial_cflags[0][0] = -1; - clipMap->box_brush->axial_cflags[0][1] = -1; - clipMap->box_brush->axial_cflags[0][2] = -1; - clipMap->box_brush->axial_cflags[1][0] = -1; - clipMap->box_brush->axial_cflags[1][1] = -1; - clipMap->box_brush->axial_cflags[1][2] = -1; - clipMap->box_brush->contents = -1; - clipMap->box_brush->mins.x = 0.0f; - clipMap->box_brush->mins.y = 0.0f; - clipMap->box_brush->mins.z = 0.0f; - clipMap->box_brush->maxs.x = 0.0f; - clipMap->box_brush->maxs.y = 0.0f; - clipMap->box_brush->maxs.z = 0.0f; - clipMap->box_brush->numsides = 0; - clipMap->box_brush->numverts = 0; - clipMap->box_brush->sides = NULL; - clipMap->box_brush->verts = NULL; - - clipMap->numClusters = 1; - clipMap->vised = 0; - clipMap->clusterBytes = ((clipMap->numClusters + 63) >> 3) & 0xFFFFFFF8; - clipMap->visibility = new char[clipMap->clusterBytes]; - memset(clipMap->visibility, 0xFF, clipMap->clusterBytes); - - clipMap->isInUse = true; - clipMap->checksum = 0; - - clipMap->num_constraints = 0; - clipMap->constraints = NULL; - clipMap->max_ropes = 32; - clipMap->ropes = new rope_t[clipMap->max_ropes]; - memset(clipMap->ropes, 0, sizeof(rope_t) * clipMap->max_ropes); - - clipMap->info.numBrushSides = 0; - clipMap->info.brushsides = NULL; - clipMap->info.leafbrushNodesCount = 0; - clipMap->info.leafbrushNodes = NULL; - clipMap->info.numLeafBrushes = 0; - clipMap->info.leafbrushes = NULL; - clipMap->info.numBrushVerts = 0; - clipMap->info.brushVerts = NULL; - clipMap->info.numBrushes = NULL; - clipMap->info.brushes = NULL; - clipMap->info.brushBounds = NULL; - clipMap->info.brushContents = NULL; - - clipMap->originalDynEntCount = DYN_ENT_COUNT; - clipMap->dynEntCount[0] = clipMap->originalDynEntCount + 256; - clipMap->dynEntCount[1] = 0; - clipMap->dynEntCount[2] = 0; - clipMap->dynEntCount[3] = 0; - - // assume that there are 0 dyn ents from here on - clipMap->dynEntClientList[0] = new DynEntityClient[clipMap->dynEntCount[0]]; - clipMap->dynEntClientList[1] = NULL; - memset(clipMap->dynEntClientList[0], 0, sizeof(DynEntityClient) * clipMap->dynEntCount[0]); - - clipMap->dynEntServerList[0] = NULL; - clipMap->dynEntServerList[1] = NULL; - - clipMap->dynEntCollList[0] = new DynEntityColl[clipMap->dynEntCount[0]]; - clipMap->dynEntCollList[1] = NULL; - clipMap->dynEntCollList[2] = NULL; - clipMap->dynEntCollList[3] = NULL; - memset(clipMap->dynEntCollList[0], 0, sizeof(DynEntityColl) * clipMap->dynEntCount[0]); - - clipMap->dynEntPoseList[0] = new DynEntityPose[clipMap->dynEntCount[0]]; - clipMap->dynEntPoseList[1] = NULL; - memset(clipMap->dynEntPoseList[0], 0, sizeof(DynEntityPose) * clipMap->dynEntCount[0]); - - clipMap->dynEntDefList[0] = new DynEntityDef[clipMap->dynEntCount[0]]; - clipMap->dynEntDefList[1] = NULL; - memset(clipMap->dynEntDefList[0], 0, sizeof(DynEntityDef) * clipMap->dynEntCount[0]); - for (int i = 0; i < clipMap->dynEntCount[0]; i++) - { - DynEntityDef* currDef = &clipMap->dynEntDefList[0][i]; - currDef->physConstraints[0] = 0x1FF; - currDef->physConstraints[1] = 0x1FF; - currDef->physConstraints[2] = 0x1FF; - currDef->physConstraints[3] = 0x1FF; - } - - clipMap->numSubModels = 1; - clipMap->cmodels = new cmodel_t[clipMap->numSubModels]; - clipMap->cmodels[0].leaf.firstCollAabbIndex = 0; - clipMap->cmodels[0].leaf.collAabbCount = 0; - clipMap->cmodels[0].leaf.brushContents = 0; - clipMap->cmodels[0].leaf.terrainContents = WORLD_TERRAIN_CONTENTS; - clipMap->cmodels[0].leaf.mins.x = 0.0f; - clipMap->cmodels[0].leaf.mins.y = 0.0f; - clipMap->cmodels[0].leaf.mins.z = 0.0f; - clipMap->cmodels[0].leaf.maxs.x = 0.0f; - clipMap->cmodels[0].leaf.maxs.y = 0.0f; - clipMap->cmodels[0].leaf.maxs.z = 0.0f; - clipMap->cmodels[0].leaf.leafBrushNode = 0; - clipMap->cmodels[0].leaf.cluster = 0; - clipMap->cmodels[0].info = NULL; - - auto gfxWorldAsset = m_context.LoadDependency(projInfo->bspName); - _ASSERT(gfxWorldAsset != NULL); - GfxWorld* gfxWorld = gfxWorldAsset->Asset(); - - 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.z = gfxWorld->models[0].bounds[0].z; - 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.z = gfxWorld->models[0].bounds[1].z; - clipMap->cmodels[0].radius = distBetweenPoints(clipMap->cmodels[0].mins, clipMap->cmodels[0].maxs) / 2; - - clipMap->numStaticModels = 0; - clipMap->staticModelList = NULL; - - clipMap->info.numMaterials = 1; - clipMap->info.materials = new ClipMaterial[clipMap->info.numMaterials]; - clipMap->info.materials[0].name = _strdup(defaultMaterialName.c_str()); - clipMap->info.materials[0].contentFlags = MATERIAL_CONTENT_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 - // might do weird stuff on walls - int walkableEdgeSize = (3 * clipMap->triCount + 31) / 32 * 4; - clipMap->triEdgeIsWalkable = new char[walkableEdgeSize]; - memset(clipMap->triEdgeIsWalkable, 0xFF, walkableEdgeSize * sizeof(char)); - - // BSP creation must go last as it depends on unids, tris and verts already being populated - // HACK: - // the BSP tree creation does not work when BO2's coordinate system is used for mins and maxs. - // Workaround is to convert every BO2 coordinate to OGL's before it is added into the BSP tree, - // and then convert them back when it is being parsed into the clipmap. Requires some hacky - // logic, check populateBSPTree_r and addAABBTreeFromLeaf - - vec3_t* firstVert = &clipMap->verts[0]; - vec3_t clipMins; - vec3_t clipMaxs; - clipMins.x = firstVert->x; - clipMins.y = firstVert->y; - clipMins.z = firstVert->z; - clipMaxs.x = firstVert->x; - clipMaxs.y = firstVert->y; - clipMaxs.z = firstVert->z; - clipMins = CMUtil::convertFromBO2Coords(clipMins); - clipMaxs = CMUtil::convertFromBO2Coords(clipMaxs); - for (int i = 1; i < clipMap->vertCount; i++) - { - vec3_t vertCoord = CMUtil::convertFromBO2Coords(clipMap->verts[i]); - CMUtil::calcNewBoundsWithPoint(&vertCoord, &clipMins, &clipMaxs); - } - - BSPTree* tree = new BSPTree(clipMins.x, clipMins.y, clipMins.z, clipMaxs.x, clipMaxs.y, clipMaxs.z, 0); - for (int i = 0; i < clipMap->partitionCount; i++) - { - auto currPartition = &clipMap->partitions[i]; - - uint16_t* firstTri = clipMap->triIndices[currPartition->firstTri]; - vec3_t* firstVert = &clipMap->verts[firstTri[0]]; - vec3_t mins; - vec3_t maxs; - mins.x = firstVert->x; - mins.y = firstVert->y; - mins.z = firstVert->z; - maxs.x = firstVert->x; - maxs.y = firstVert->y; - maxs.z = firstVert->z; - mins = CMUtil::convertFromBO2Coords(mins); - maxs = CMUtil::convertFromBO2Coords(maxs); - for (int k = 0; k < currPartition->triCount; k++) - { - uint16_t* tri = clipMap->triIndices[currPartition->firstTri + k]; - for (int l = 0; l < 3; l++) - { - uint16_t vertIndex = tri[l]; - vec3_t vertCoord = CMUtil::convertFromBO2Coords(clipMap->verts[vertIndex]); - CMUtil::calcNewBoundsWithPoint(&vertCoord, &mins, &maxs); - } - } - - Object* currObject = new Object(mins.x, mins.y, mins.z, maxs.x, maxs.y, maxs.z, i); - - tree->addObject(currObject); - } - - populateBSPTree(clipMap, tree); - - m_context.AddAsset(clipMap->name, clipMap); - } - - void createComWorld(customMapInfo* projInfo) - { - // all lights that aren't the sunlight or default light need their own GfxLightDef asset - ComWorld* comWorld = new ComWorld; - comWorld->name = _strdup(projInfo->bspName.c_str()); - comWorld->isInUse = 1; - comWorld->primaryLightCount = 2; - comWorld->primaryLights = new ComPrimaryLight[comWorld->primaryLightCount]; - - ComPrimaryLight* defaultLight = &comWorld->primaryLights[0]; - memset(defaultLight, 0, sizeof(ComPrimaryLight)); - - ComPrimaryLight* sunLight = &comWorld->primaryLights[1]; - memset(sunLight, 0, sizeof(ComPrimaryLight)); - sunLight->type = 1; - // Below are default values taken from mp_dig - sunLight->diffuseColor.r = 1.0f; - sunLight->diffuseColor.g = 1.0f; - sunLight->diffuseColor.b = 1.0f; - sunLight->diffuseColor.a = 0.0f; - sunLight->dir.x = -0.2410777360200882; - sunLight->dir.y = -0.8407384753227234; - sunLight->dir.z = 0.48480960726737976; - - m_context.AddAsset(comWorld->name, comWorld); - } - - void createMapEnts(customMapInfo* projInfo) - { - MapEnts* mapEnts = new MapEnts; - - mapEnts->name = _strdup(projInfo->bspName.c_str()); - - // don't need these - mapEnts->trigger.count = 0; - mapEnts->trigger.models = NULL; - mapEnts->trigger.hullCount = 0; - mapEnts->trigger.hulls = NULL; - mapEnts->trigger.slabCount = 0; - mapEnts->trigger.slabs = NULL; - - const auto file = m_search_path.Open("custom_map/entities.json"); - if (!file.IsOpen()) - { - printf("WARN: can't find entity json!\n"); - return; - } - - json entJs = json::parse(*file.m_stream); - - std::string entityString; - int entityCount = entJs["entityCount"]; - for (int i = 0; i < entityCount; i++) - { - auto currEntity = entJs["entities"][i]; - - if (i == 0) - { - std::string className = currEntity["classname"]; - if (className.compare("worldspawn") != 0) - { - printf("ERROR: first entity in the map entity string must be the worldspawn class!"); - hasLinkFailed = true; - return; - } - } - - entityString.append("{\n"); - - for (auto& element : currEntity.items()) - { - std::string key = element.key(); - std::string value = element.value(); - entityString.append(std::format("\"{}\" \"{}\"\n", key, value)); - } - - entityString.append("}\n"); - } - entityString.pop_back(); // remove newline character from the end of the string - - mapEnts->entityString = _strdup(entityString.c_str()); - mapEnts->numEntityChars = entityString.length() + 1; // numEntityChars includes the null character - - m_context.AddAsset(mapEnts->name, mapEnts); - } - - void createGameWorldMp(customMapInfo* projInfo) - { - GameWorldMp* gameWorldMp = new GameWorldMp; - - gameWorldMp->name = _strdup(projInfo->bspName.c_str()); - - gameWorldMp->path.nodeCount = 0; - gameWorldMp->path.originalNodeCount = 0; - gameWorldMp->path.visBytes = 0; - gameWorldMp->path.smoothBytes = 0; - gameWorldMp->path.nodeTreeCount = 0; - - int nodeCount = gameWorldMp->path.nodeCount + 128; - gameWorldMp->path.nodes = new pathnode_t[nodeCount]; - gameWorldMp->path.basenodes = new pathbasenode_t[nodeCount]; - memset(gameWorldMp->path.nodes, 0, nodeCount * sizeof(pathnode_t)); - memset(gameWorldMp->path.basenodes, 0, nodeCount * sizeof(pathbasenode_t)); - - gameWorldMp->path.pathVis = NULL; - gameWorldMp->path.smoothCache = NULL; - gameWorldMp->path.nodeTree = NULL; - - m_context.AddAsset(gameWorldMp->name, gameWorldMp); - } - - void createSkinnedVerts(customMapInfo* projInfo) - { - SkinnedVertsDef* skinnedVerts = new SkinnedVertsDef; - skinnedVerts->name = "skinnedverts"; - skinnedVerts->maxSkinnedVerts = projInfo->gfxInfo.vertexCount; - - m_context.AddAsset("skinnedverts", skinnedVerts); - } - - FootstepTableDef* addEmptyFootstepTableAsset(std::string assetName) - { - if (assetName.length() == 0) - return NULL; - - FootstepTableDef* footstepTable = new FootstepTableDef; - footstepTable->name = _strdup(assetName.c_str()); - memset(footstepTable->sndAliasTable, 0, sizeof(footstepTable->sndAliasTable)); - - m_context.AddAsset(assetName, footstepTable); - - return footstepTable; - } - - void checkAndAddDefaultRequiredAssets(customMapInfo* projectInfo) - { - if (m_context.LoadDependency("maps/mp/mp_dig.gsc") == NULL) { hasLinkFailed = true; return; } - if (m_context.LoadDependency("maps/mp/mp_dig_amb.gsc") == NULL) { hasLinkFailed = true; return; } - if (m_context.LoadDependency("maps/mp/mp_dig_fx.gsc") == NULL) { hasLinkFailed = true; return; } - if (m_context.LoadDependency("maps/mp/createfx/mp_dig_fx.gsc") == NULL) { hasLinkFailed = true; return; } - if (m_context.LoadDependency("clientscripts/mp/mp_dig.csc") == NULL) { hasLinkFailed = true; return; } - if (m_context.LoadDependency("clientscripts/mp/mp_dig_amb.csc") == NULL) { hasLinkFailed = true; return; } - if (m_context.LoadDependency("clientscripts/mp/mp_dig_fx.csc") == NULL) { hasLinkFailed = true; return; } - if (m_context.LoadDependency("clientscripts/mp/createfx/mp_dig_fx.csc") == NULL) { hasLinkFailed = true; return; } - - addEmptyFootstepTableAsset("default_1st_person"); - addEmptyFootstepTableAsset("default_3rd_person"); - addEmptyFootstepTableAsset("default_1st_person_quiet"); - addEmptyFootstepTableAsset("default_3rd_person_quiet"); - addEmptyFootstepTableAsset("default_3rd_person_loud"); - addEmptyFootstepTableAsset("default_ai"); - } - - bool linkCustomMap(customMapInfo* projInfo) - { - _ASSERT(projInfo != NULL); - - createGfxWorld(projInfo); - createComWorld(projInfo); - createMapEnts(projInfo); - createGameWorldMp(projInfo); - createSkinnedVerts(projInfo); - createClipMap(projInfo); // must go last (requires gfx and mapents asset) - - checkAndAddDefaultRequiredAssets(projInfo); - - if (hasLinkFailed) - { - printf("Custom Map link has failed.\n"); - return false; - } - - return true; - } -}; +//#include "CustomMapLinker.h" +// +//#include "Utils/Pack.h" +//#include "Util.h" +//#include "CustomMapConsts.h" +//#include "BinarySpacePartitionTreePreCalc.h" +// +//#include +//#include +//using json = nlohmann::json; +// +//class CustomMapLinker +//{ +//public: +// CustomMapLinker(MemoryManager& memory, ISearchPath& searchPath, Zone& zone, AssetCreationContext& context) +// : m_memory(memory), +// m_search_path(searchPath), +// m_zone(zone), +// m_context(context) +// { +// hasLinkFailed = false; +// } +// +// bool linkCustomMap(customMapInfo* projInfo) +// { +// _ASSERT(projInfo != NULL); +// +// createGfxWorld(projInfo); +// createComWorld(projInfo); +// createMapEnts(projInfo); +// createGameWorldMp(projInfo); +// createSkinnedVerts(projInfo); +// createClipMap(projInfo); // must go last (requires gfx and mapents asset) +// +// checkAndAddDefaultRequiredAssets(projInfo); +// +// if (hasLinkFailed) +// { +// printf("Custom Map link has failed.\n"); +// return false; +// } +// +// return true; +// } +// +//private: +// MemoryManager& m_memory; +// ISearchPath& m_search_path; +// Zone& m_zone; +// AssetCreationContext& m_context; +// +// bool hasLinkFailed; +// +// // TODO vd1: +// // used for UVs of sub-textures, when it is set to empty all of them turn a blank colour +// // could fix by removing sub textures or figure out how they are created and redo that +// // its not an important issue though +// bool overwriteDrawData(customMapInfo* projInfo, GfxWorld* gfxWorld) +// { +// int vertexCount = projInfo->gfxInfo.vertexCount; +// customMapVertex* worldVertices = projInfo->gfxInfo.vertices; +// +// gfxWorld->draw.vertexCount = vertexCount; +// gfxWorld->draw.vertexDataSize0 = vertexCount * sizeof(GfxPackedWorldVertex); +// GfxPackedWorldVertex* vertexBuffer = new GfxPackedWorldVertex[vertexCount]; +// for (int i = 0; i < vertexCount; i++) +// { +// customMapVertex* WorldVertex = &worldVertices[i]; +// GfxPackedWorldVertex* GfxVertex = &vertexBuffer[i]; +// +// GfxVertex->xyz = CMUtil::convertToBO2Coords(WorldVertex->pos); +// +// GfxVertex->binormalSign = WorldVertex->binormalSign; +// +// GfxVertex->color.packed = pack32::Vec4PackGfxColor(WorldVertex->color); +// +// GfxVertex->texCoord.packed = pack32::Vec2PackTexCoordsUV(WorldVertex->texCoord); +// +// GfxVertex->normal.packed = pack32::Vec3PackUnitVecThirdBased(CMUtil::convertToBO2Coords(WorldVertex->normal).v); +// +// GfxVertex->tangent.packed = pack32::Vec3PackUnitVecThirdBased(CMUtil::convertToBO2Coords(WorldVertex->tangent).v); +// +// GfxVertex->lmapCoord.packed = WorldVertex->packedLmapCoord; +// } +// gfxWorld->draw.vd0.data = (char*)vertexBuffer; +// +// // we don't use vd1 but still needs to be initialised +// // the data type varies and 0x20 is enough for all types +// gfxWorld->draw.vertexDataSize1 = 0x20; +// gfxWorld->draw.vd1.data = new char[gfxWorld->draw.vertexDataSize1]; +// memset(gfxWorld->draw.vd1.data, 0, gfxWorld->draw.vertexDataSize1); +// +// int indexCount = projInfo->gfxInfo.indexCount; +// _ASSERT(indexCount % 3 == 0); +// gfxWorld->draw.indexCount = indexCount; +// gfxWorld->draw.indices = new uint16_t[indexCount]; +// for (int i = 0; i < indexCount; i += 3) +// { +// // the editor orders their vertices opposite to bo2, so its converted here +// gfxWorld->draw.indices[i + 2] = projInfo->gfxInfo.indices[i + 0]; +// gfxWorld->draw.indices[i + 1] = projInfo->gfxInfo.indices[i + 1]; +// gfxWorld->draw.indices[i + 0] = projInfo->gfxInfo.indices[i + 2]; +// } +// +// return true; +// } +// +// struct s_sortedSurf +// { +// int surfaceIndex; +// int vertexCount; +// }; +// +// bool compareSurfaces(s_sortedSurf& surf0, s_sortedSurf& surf1) +// { +// return surf0.vertexCount > surf1.vertexCount; +// } +// +// void overwriteMapSurfaces(customMapInfo* projInfo, GfxWorld* gfxWorld) +// { +// bool overwriteResult = overwriteDrawData(projInfo, gfxWorld); +// if (!overwriteResult) +// return; +// +// unsigned int surfaceCount = projInfo->gfxInfo.surfaceCount; +// gfxWorld->surfaceCount = surfaceCount; +// gfxWorld->dpvs.staticSurfaceCount = surfaceCount; +// gfxWorld->dpvs.surfaces = new GfxSurface[surfaceCount]; +// for (int i = 0; i < surfaceCount; i++) +// { +// auto currSurface = &gfxWorld->dpvs.surfaces[i]; +// auto objSurface = &projInfo->gfxInfo.surfaces[i]; +// +// currSurface->lightmapIndex = objSurface->lightmapIndex; +// currSurface->primaryLightIndex = objSurface->primaryLightIndex; +// currSurface->reflectionProbeIndex = objSurface->reflectionProbeIndex; +// currSurface->flags = objSurface->flags; +// +// currSurface->tris.triCount = objSurface->triCount; +// currSurface->tris.baseIndex = objSurface->firstIndex_Index; +// +// currSurface->tris.vertexDataOffset0 = objSurface->firstVertexIndex * sizeof(GfxPackedWorldVertex); +// currSurface->tris.vertexDataOffset1 = 0; +// +// auto* assetInfo = m_context.LoadDependency(objSurface->materialName); +// 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(defaultMaterialName); +// _ASSERT(assetInfo != NULL); +// } +// currSurface->material = assetInfo->Asset(); +// +// GfxPackedWorldVertex* firstVert = (GfxPackedWorldVertex*)&gfxWorld->draw.vd0.data[currSurface->tris.vertexDataOffset0]; +// currSurface->bounds[0].x = firstVert[0].xyz.x; +// currSurface->bounds[0].y = firstVert[0].xyz.y; +// currSurface->bounds[0].z = firstVert[0].xyz.z; +// currSurface->bounds[1].x = firstVert[0].xyz.x; +// currSurface->bounds[1].y = firstVert[0].xyz.y; +// currSurface->bounds[1].z = firstVert[0].xyz.z; +// for (int k = 0; k < currSurface->tris.triCount * 3; k++) +// { +// CMUtil::calcNewBoundsWithPoint(&firstVert[k].xyz, &currSurface->bounds[0], &currSurface->bounds[1]); +// } +// +// // unused values +// currSurface->tris.mins.x = 0.0f; +// currSurface->tris.mins.y = 0.0f; +// currSurface->tris.mins.z = 0.0f; +// currSurface->tris.maxs.x = 0.0f; +// currSurface->tris.maxs.y = 0.0f; +// currSurface->tris.maxs.z = 0.0f; +// currSurface->tris.himipRadiusInvSq = 0.0f; +// currSurface->tris.vertexCount = 0; +// currSurface->tris.firstVertex = 0; +// } +// +// // sort all vertexes by vertex count +// // unsure if this does anything +// s_sortedSurf* sortedSurfs = new s_sortedSurf[surfaceCount]; +// for (int i = 0; i < surfaceCount; i++) +// { +// sortedSurfs[i].surfaceIndex = i; +// sortedSurfs[i].vertexCount = gfxWorld->dpvs.surfaces[i].tris.triCount * 3; +// } +// std::sort(sortedSurfs, &sortedSurfs[surfaceCount], compareSurfaces); +// +// gfxWorld->dpvs.sortedSurfIndex = new uint16_t[surfaceCount]; +// for (int i = 0; i < surfaceCount; i++) +// { +// gfxWorld->dpvs.sortedSurfIndex[i] = sortedSurfs[i].surfaceIndex; +// } +// +// gfxWorld->dpvs.surfaceMaterials = new GfxDrawSurf_align4[surfaceCount]; +// memset(gfxWorld->dpvs.surfaceMaterials, 0, sizeof(GfxDrawSurf_align4) * surfaceCount); +// +// // all visdata is alligned by 128 +// gfxWorld->dpvs.surfaceVisDataCount = CMUtil::allignBy128(surfaceCount); +// gfxWorld->dpvs.surfaceVisData[0] = new char[surfaceCount]; +// gfxWorld->dpvs.surfaceVisData[1] = new char[surfaceCount]; +// gfxWorld->dpvs.surfaceVisData[2] = new char[surfaceCount]; +// gfxWorld->dpvs.surfaceVisDataCameraSaved = new char[surfaceCount]; +// gfxWorld->dpvs.surfaceCastsShadow = new char[surfaceCount]; +// gfxWorld->dpvs.surfaceCastsSunShadow = new char[surfaceCount]; +// memset(gfxWorld->dpvs.surfaceVisData[0], 0, surfaceCount); +// memset(gfxWorld->dpvs.surfaceVisData[1], 0, surfaceCount); +// memset(gfxWorld->dpvs.surfaceVisData[2], 0, surfaceCount); +// memset(gfxWorld->dpvs.surfaceVisDataCameraSaved, 0, surfaceCount); +// memset(gfxWorld->dpvs.surfaceCastsShadow, 0, surfaceCount); +// memset(gfxWorld->dpvs.surfaceCastsSunShadow, 0, surfaceCount); +// +// gfxWorld->dpvs.litSurfsBegin = 0; +// gfxWorld->dpvs.litSurfsEnd = surfaceCount; +// gfxWorld->dpvs.emissiveOpaqueSurfsBegin = surfaceCount; +// gfxWorld->dpvs.emissiveOpaqueSurfsEnd = surfaceCount; +// gfxWorld->dpvs.emissiveTransSurfsBegin = surfaceCount; +// gfxWorld->dpvs.emissiveTransSurfsEnd = surfaceCount; +// gfxWorld->dpvs.litTransSurfsBegin = surfaceCount; +// gfxWorld->dpvs.litTransSurfsEnd = surfaceCount; +// } +// +//#define SMODEL_FLAG_NO_SHADOW 1 +//#define SMODEL_FLAG_IS_LIT 2 +// +// void overwriteMapSModels(GfxWorld* gfxWorld) +// { +// unsigned int modelCount = 0; +// gfxWorld->dpvs.smodelCount = modelCount; +// +// gfxWorld->dpvs.smodelInsts = new GfxStaticModelInst[modelCount]; +// gfxWorld->dpvs.smodelDrawInsts = new GfxStaticModelDrawInst[modelCount]; +// /* +// for (unsigned int i = 0; i < modelCount; i++) +// { +// auto currModel = &gfxWorld->dpvs.smodelDrawInsts[i]; +// auto currModelInst = &gfxWorld->dpvs.smodelInsts[i]; +// json currModelJs = js["models"][i]; +// +// // TODO: load custom xmodels +// std::string modelName = currModelJs["model"]; +// auto xModelAsset = findAsset(assetPool, ASSET_TYPE_XMODEL, modelName); +// if (xModelAsset == NULL) +// { +// printf("Custom model (%s) not supported!\n", modelName.c_str()); +// hasLinkFailed = true; +// return; +// } +// currModel->model = (XModel*)xModelAsset->m_ptr; +// +// currModel->placement.origin.x = currModelJs["origin"]["x"]; +// currModel->placement.origin.y = currModelJs["origin"]["y"]; +// currModel->placement.origin.z = currModelJs["origin"]["z"]; +// currModel->placement.origin = CMUtil::convertToBO2Coords(currModel->placement.origin); +// currModel->placement.axis[0].x = currModelJs["forward"]["x"]; +// currModel->placement.axis[0].y = currModelJs["forward"]["y"]; +// currModel->placement.axis[0].z = currModelJs["forward"]["z"]; +// 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 +// // TODO: mins and maxs are slightly different to what bo2 uses, which results in some wierd culling ingame +// currModelInst->mins.x = currModel->model->mins.x + currModel->placement.origin.x; +// currModelInst->mins.y = currModel->model->mins.y + currModel->placement.origin.y; +// currModelInst->mins.z = currModel->model->mins.z + currModel->placement.origin.z; +// currModelInst->maxs.x = currModel->model->maxs.x + currModel->placement.origin.x; +// currModelInst->maxs.y = currModel->model->maxs.y + currModel->placement.origin.y; +// currModelInst->maxs.z = currModel->model->maxs.z + currModel->placement.origin.z; +// +// currModel->cullDist = currModelJs["cullDist"]; +// currModel->flags = currModelJs["flags"]; +// currModel->primaryLightIndex = (unsigned char)currModelJs["primaryLightIndex"]; +// currModel->reflectionProbeIndex = (unsigned char)currModelJs["reflectionProbeIndex"]; +// +// // unknown use / unused +// currModel->smid = i; +// memset(&currModel->lightingSH, 0, sizeof(GfxLightingSHQuantized)); +// currModel->invScaleSq = 0.0f; +// currModel->lightingHandle = 0; +// currModel->colorsIndex = 0; +// currModel->visibility = 0; +// +// // setting these to NULL makes any static/baked lighting go black when not rendered by real-time lighting or in a shadow +// // TODO: calculate lighting and store it here +// currModel->lmapVertexInfo[0].numLmapVertexColors = 0; +// currModel->lmapVertexInfo[0].lmapVertexColors = NULL; +// currModel->lmapVertexInfo[1].numLmapVertexColors = 0; +// currModel->lmapVertexInfo[1].lmapVertexColors = NULL; +// currModel->lmapVertexInfo[2].numLmapVertexColors = 0; +// currModel->lmapVertexInfo[2].lmapVertexColors = NULL; +// currModel->lmapVertexInfo[3].numLmapVertexColors = 0; +// currModel->lmapVertexInfo[3].lmapVertexColors = NULL; +// } +// */ +// +// // all visdata is alligned by 128 +// gfxWorld->dpvs.smodelVisDataCount = CMUtil::allignBy128(modelCount); +// gfxWorld->dpvs.smodelVisData[0] = new char[modelCount]; +// gfxWorld->dpvs.smodelVisData[1] = new char[modelCount]; +// gfxWorld->dpvs.smodelVisData[2] = new char[modelCount]; +// gfxWorld->dpvs.smodelVisDataCameraSaved = new char[modelCount]; +// gfxWorld->dpvs.smodelCastsShadow = new char[modelCount]; +// for (unsigned int i = 0; i < modelCount; i++) +// { +// if ((gfxWorld->dpvs.smodelDrawInsts[i].flags & SMODEL_FLAG_NO_SHADOW) == 0) +// gfxWorld->dpvs.smodelCastsShadow[i] = 1; +// else +// gfxWorld->dpvs.smodelCastsShadow[i] = 0; +// } +// memset(gfxWorld->dpvs.smodelVisData[0], 0, modelCount); +// memset(gfxWorld->dpvs.smodelVisData[1], 0, modelCount); +// memset(gfxWorld->dpvs.smodelVisData[2], 0, modelCount); +// memset(gfxWorld->dpvs.smodelVisDataCameraSaved, 0, modelCount); +// +// gfxWorld->dpvs.usageCount = 0; +// } +// +// void cleanGfxWorld(GfxWorld* gfxWorld) +// { +// gfxWorld->checksum = 0; +// +// // Remove Coronas +// gfxWorld->coronaCount = 0; +// gfxWorld->coronas = NULL; +// +// // Remove exposure volumes +// gfxWorld->exposureVolumeCount = 0; +// gfxWorld->exposureVolumes = NULL; +// gfxWorld->exposureVolumePlaneCount = 0; +// gfxWorld->exposureVolumePlanes = NULL; +// +// // Remove hero lights +// gfxWorld->heroLightCount = 0; +// gfxWorld->heroLights = NULL; +// gfxWorld->heroLightTreeCount = 0; +// gfxWorld->heroLightTree = NULL; +// +// // remove LUT data +// gfxWorld->lutVolumeCount = 0; +// gfxWorld->lutVolumes = NULL; +// gfxWorld->lutVolumePlaneCount = 0; +// gfxWorld->lutVolumePlanes = NULL; +// +// // remove occluders +// gfxWorld->numOccluders = 0; +// gfxWorld->occluders = NULL; +// +// // remove Siege Skins +// gfxWorld->numSiegeSkinInsts = 0; +// gfxWorld->siegeSkinInsts = NULL; +// +// // remove outdoor bounds +// gfxWorld->numOutdoorBounds = 0; +// gfxWorld->outdoorBounds = NULL; +// +// // remove materials +// gfxWorld->ropeMaterial = NULL; +// gfxWorld->lutMaterial = NULL; +// gfxWorld->waterMaterial = NULL; +// gfxWorld->coronaMaterial = NULL; +// +// // remove shadow maps +// gfxWorld->shadowMapVolumeCount = 0; +// gfxWorld->shadowMapVolumes = NULL; +// gfxWorld->shadowMapVolumePlaneCount = 0; +// gfxWorld->shadowMapVolumePlanes = NULL; +// +// // remove stream info +// gfxWorld->streamInfo.aabbTreeCount = 0; +// gfxWorld->streamInfo.aabbTrees = NULL; +// gfxWorld->streamInfo.leafRefCount = 0; +// gfxWorld->streamInfo.leafRefs = NULL; +// +// // remove sun data +// memset(&gfxWorld->sun, 0, sizeof(sunflare_t)); +// gfxWorld->sun.hasValidData = false; +// +// // Remove Water +// gfxWorld->waterDirection = 0.0f; +// gfxWorld->waterBuffers[0].buffer = NULL; +// gfxWorld->waterBuffers[0].bufferSize = NULL; +// gfxWorld->waterBuffers[1].buffer = NULL; +// gfxWorld->waterBuffers[1].bufferSize = NULL; +// +// // Remove Fog +// gfxWorld->worldFogModifierVolumeCount = 0; +// gfxWorld->worldFogModifierVolumes = NULL; +// gfxWorld->worldFogModifierVolumePlaneCount = 0; +// gfxWorld->worldFogModifierVolumePlanes = NULL; +// gfxWorld->worldFogVolumeCount = 0; +// gfxWorld->worldFogVolumes = NULL; +// gfxWorld->worldFogVolumePlaneCount = 0; +// gfxWorld->worldFogVolumePlanes = NULL; +// +// // materialMemory is unused +// gfxWorld->materialMemoryCount = 0; +// gfxWorld->materialMemory = NULL; +// +// // sunLight is overwritten by the game, just needs to be a valid pointer +// gfxWorld->sunLight = new GfxLight; +// memset(gfxWorld->sunLight, 0, sizeof(GfxLight)); +// } +// +// void overwriteGfxLights(GfxWorld* gfxWorld) +// { +// // there must be 2 or more lights, first is the default light and second is the sun +// gfxWorld->primaryLightCount = 2; +// gfxWorld->sunPrimaryLightIndex = 1; // the sun is always index 1 +// +// gfxWorld->shadowGeom = new GfxShadowGeometry[gfxWorld->primaryLightCount]; +// for (int i = 0; i < gfxWorld->primaryLightCount; i++) +// { +// gfxWorld->shadowGeom[i].smodelCount = 0; +// gfxWorld->shadowGeom[i].surfaceCount = 0; +// gfxWorld->shadowGeom[i].smodelIndex = NULL; +// gfxWorld->shadowGeom[i].sortedSurfIndex = NULL; +// } +// +// gfxWorld->lightRegion = new GfxLightRegion[gfxWorld->primaryLightCount]; +// for (int i = 0; i < gfxWorld->primaryLightCount; i++) +// { +// gfxWorld->lightRegion[i].hullCount = 0; +// gfxWorld->lightRegion[i].hulls = NULL; +// } +// +// int lightEntShadowVisSize = (gfxWorld->primaryLightCount - gfxWorld->sunPrimaryLightIndex - 1) * 8192; +// if (lightEntShadowVisSize != 0) +// { +// gfxWorld->primaryLightEntityShadowVis = new unsigned int[lightEntShadowVisSize]; +// memset(gfxWorld->primaryLightEntityShadowVis, 0, lightEntShadowVisSize * sizeof(unsigned int)); +// } +// else +// { +// gfxWorld->primaryLightEntityShadowVis = NULL; +// } +// } +// +// void overwriteLightGrid(GfxWorld* gfxWorld) +// { +// gfxWorld->lightGrid.mins[0] = 0; +// gfxWorld->lightGrid.mins[1] = 0; +// gfxWorld->lightGrid.mins[2] = 0; +// gfxWorld->lightGrid.maxs[0] = 0; +// gfxWorld->lightGrid.maxs[1] = 0; +// gfxWorld->lightGrid.maxs[2] = 0; +// +// gfxWorld->lightGrid.rowAxis = 0; // default value +// gfxWorld->lightGrid.colAxis = 1; // default value +// gfxWorld->lightGrid.sunPrimaryLightIndex = 1; // the sun is always index 1 +// gfxWorld->lightGrid.offset = 0.0f; +// +// // if rowDataStart's first value is -1, it will not look up any lightmap data +// gfxWorld->lightGrid.rowDataStart = new uint16_t[1]; +// *gfxWorld->lightGrid.rowDataStart = (uint16_t)(-1); +// +// gfxWorld->lightGrid.rawRowDataSize = 0; +// gfxWorld->lightGrid.rawRowData = NULL; +// +// gfxWorld->lightGrid.colorCount = 0; +// gfxWorld->lightGrid.colors = NULL; +// +// gfxWorld->lightGrid.coeffCount = 0; +// gfxWorld->lightGrid.coeffs = NULL; +// +// gfxWorld->lightGrid.entryCount = 0; +// gfxWorld->lightGrid.entries = NULL; +// +// gfxWorld->lightGrid.skyGridVolumeCount = 0; +// gfxWorld->lightGrid.skyGridVolumes = NULL; +// } +// +// void updateGfxCells(GfxWorld* gfxWorld) +// { +// // Cells are basically data used to determine what can be seen and what cant be seen +// // Right now custom maps have no optimisation so there is only 1 cell +// int cellCount = 1; +// +// gfxWorld->cellBitsCount = ((cellCount + 127) >> 3) & 0x1FFFFFF0; +// +// int cellCasterBitsCount = cellCount * ((cellCount + 31) / 32); +// gfxWorld->cellCasterBits = new unsigned int[cellCasterBitsCount]; +// memset(gfxWorld->cellCasterBits, 0x00, cellCasterBitsCount * sizeof(unsigned int)); +// +// gfxWorld->cells = new GfxCell[cellCount]; +// gfxWorld->cells[0].portalCount = 0; +// gfxWorld->cells[0].portals = NULL; +// gfxWorld->cells[0].reflectionProbeCount = 0; +// gfxWorld->cells[0].reflectionProbes = NULL; +// gfxWorld->cells[0].mins.x = gfxWorld->mins.x; +// gfxWorld->cells[0].mins.y = gfxWorld->mins.y; +// gfxWorld->cells[0].mins.z = gfxWorld->mins.z; +// gfxWorld->cells[0].maxs.x = gfxWorld->maxs.x; +// gfxWorld->cells[0].maxs.y = gfxWorld->maxs.y; +// gfxWorld->cells[0].maxs.z = gfxWorld->maxs.z; +// +// // AABB trees are used to detect what should be rendered and what shouldn't +// // Just use the first AABB node to hold all models, no optimisation but all models/surfaces wil lbe drawn +// gfxWorld->cells[0].aabbTreeCount = 1; +// gfxWorld->cells[0].aabbTree = new GfxAabbTree[gfxWorld->cells[0].aabbTreeCount]; +// gfxWorld->cells[0].aabbTree[0].childCount = 0; +// gfxWorld->cells[0].aabbTree[0].childrenOffset = 0; +// gfxWorld->cells[0].aabbTree[0].startSurfIndex = 0; +// gfxWorld->cells[0].aabbTree[0].surfaceCount = gfxWorld->surfaceCount; +// gfxWorld->cells[0].aabbTree[0].smodelIndexCount = gfxWorld->dpvs.smodelCount; +// gfxWorld->cells[0].aabbTree[0].smodelIndexes = new unsigned short[gfxWorld->dpvs.smodelCount]; +// for (unsigned short i = 0; i < gfxWorld->dpvs.smodelCount; i++) +// { +// gfxWorld->cells[0].aabbTree[0].smodelIndexes[i] = i; +// } +// gfxWorld->cells[0].aabbTree[0].mins.x = gfxWorld->mins.x; +// gfxWorld->cells[0].aabbTree[0].mins.y = gfxWorld->mins.y; +// gfxWorld->cells[0].aabbTree[0].mins.z = gfxWorld->mins.z; +// gfxWorld->cells[0].aabbTree[0].maxs.x = gfxWorld->maxs.x; +// gfxWorld->cells[0].aabbTree[0].maxs.y = gfxWorld->maxs.y; +// gfxWorld->cells[0].aabbTree[0].maxs.z = gfxWorld->maxs.z; +// +// gfxWorld->dpvsPlanes.cellCount = cellCount; +// +// int sceneEntCellBitsCount = cellCount * 512; +// gfxWorld->dpvsPlanes.sceneEntCellBits = new unsigned int[sceneEntCellBitsCount]; +// memset(gfxWorld->dpvsPlanes.sceneEntCellBits, 0x00, sceneEntCellBitsCount * sizeof(unsigned int)); +// +// // nodes have the struct mnode_t, and there must be at least 1 node +// // Nodes mnode_t.cellIndex indexes gfxWorld->cells +// // and (mnode_t.cellIndex - (world->dpvsPlanes.cellCount + 1) indexes world->dpvsPlanes.planes +// gfxWorld->nodeCount = 1; +// gfxWorld->planeCount = 0; +// gfxWorld->dpvsPlanes.nodes = new uint16_t[gfxWorld->nodeCount]; +// gfxWorld->dpvsPlanes.nodes[0] = 1; // nodes reference cells by index + 1 +// gfxWorld->dpvsPlanes.planes = NULL; +// } +// +// void updateWorldBounds(GfxWorld* gfxWorld) +// { +// gfxWorld->mins.x = 0.0f; +// gfxWorld->mins.y = 0.0f; +// gfxWorld->mins.z = 0.0f; +// gfxWorld->maxs.x = 0.0f; +// gfxWorld->maxs.y = 0.0f; +// gfxWorld->maxs.z = 0.0f; +// +// for (int i = 0; i < gfxWorld->surfaceCount; i++) +// { +// CMUtil::calcNewBounds(&gfxWorld->dpvs.surfaces[i].bounds[0], &gfxWorld->dpvs.surfaces[i].bounds[1], &gfxWorld->mins, &gfxWorld->maxs); +// } +// } +// +// void overwriteModels(GfxWorld* gfxWorld) +// { +// gfxWorld->modelCount = 1; +// gfxWorld->models = new GfxBrushModel[gfxWorld->modelCount]; +// +// // first model is the world model, the world json doesn't include it so its added here +// gfxWorld->models[0].startSurfIndex = 0; +// gfxWorld->models[0].surfaceCount = gfxWorld->surfaceCount; +// gfxWorld->models[0].bounds[0].x = gfxWorld->mins.x; +// gfxWorld->models[0].bounds[0].y = gfxWorld->mins.y; +// gfxWorld->models[0].bounds[0].z = gfxWorld->mins.z; +// gfxWorld->models[0].bounds[1].x = gfxWorld->maxs.x; +// gfxWorld->models[0].bounds[1].y = gfxWorld->maxs.y; +// gfxWorld->models[0].bounds[1].z = gfxWorld->maxs.z; +// memset(&gfxWorld->models[0].writable, 0, sizeof(GfxBrushModelWritable)); +// +// // for (int i = 1; i < gfxWorld->modelCount; i++) +// //{ +// // auto currEntModel = &gfxWorld->models[i]; +// // json currEntModelJs = js["entityModels"][i - 1]; +// // +// // currEntModel->bounds[0].x = currEntModelJs["mins"]["x"]; +// // currEntModel->bounds[0].y = currEntModelJs["mins"]["y"]; +// // currEntModel->bounds[0].z = currEntModelJs["mins"]["z"]; +// // currEntModel->bounds[1].x = currEntModelJs["maxs"]["x"]; +// // currEntModel->bounds[1].y = currEntModelJs["maxs"]["y"]; +// // currEntModel->bounds[1].z = currEntModelJs["maxs"]["z"]; +// // +// // currEntModel->surfaceCount = 0; +// // currEntModel->startSurfIndex = (unsigned int)(-1); +// // memset(&currEntModel->writable, 0, sizeof(GfxBrushModelWritable)); +// // } +// } +// +// void updateSunData(GfxWorld* gfxWorld) +// { +// // default values taken from mp_dig +// gfxWorld->sunParse.fogTransitionTime = 0.001; +// gfxWorld->sunParse.name[0] = 0x00; +// +// gfxWorld->sunParse.initWorldSun->control = 0; +// gfxWorld->sunParse.initWorldSun->exposure = 2.5f; +// gfxWorld->sunParse.initWorldSun->angles.x = -29.0f; +// gfxWorld->sunParse.initWorldSun->angles.y = 254.0f; +// gfxWorld->sunParse.initWorldSun->angles.z = 0.0f; +// gfxWorld->sunParse.initWorldSun->sunCd.x = 1.0f; +// gfxWorld->sunParse.initWorldSun->sunCd.y = 0.89f; +// gfxWorld->sunParse.initWorldSun->sunCd.z = 0.69f; +// gfxWorld->sunParse.initWorldSun->sunCd.w = 13.5f; +// gfxWorld->sunParse.initWorldSun->ambientColor.x = 0.0f; +// gfxWorld->sunParse.initWorldSun->ambientColor.y = 0.0f; +// gfxWorld->sunParse.initWorldSun->ambientColor.z = 0.0f; +// gfxWorld->sunParse.initWorldSun->ambientColor.w = 0.0f; +// gfxWorld->sunParse.initWorldSun->skyColor.x = 0.0f; +// gfxWorld->sunParse.initWorldSun->skyColor.y = 0.0f; +// gfxWorld->sunParse.initWorldSun->skyColor.z = 0.0f; +// gfxWorld->sunParse.initWorldSun->skyColor.w = 0.0f; +// gfxWorld->sunParse.initWorldSun->sunCs.x = 0.0f; +// gfxWorld->sunParse.initWorldSun->sunCs.y = 0.0f; +// gfxWorld->sunParse.initWorldSun->sunCs.z = 0.0f; +// gfxWorld->sunParse.initWorldSun->sunCs.w = 0.0f; +// +// gfxWorld->sunParse.initWorldFog->baseDist = 150.0f; +// gfxWorld->sunParse.initWorldFog->baseHeight = -100.0f; +// gfxWorld->sunParse.initWorldFog->fogColor.x = 2.35f; +// gfxWorld->sunParse.initWorldFog->fogColor.y = 3.10f; +// gfxWorld->sunParse.initWorldFog->fogColor.z = 3.84f; +// gfxWorld->sunParse.initWorldFog->fogOpacity = 0.52f; +// gfxWorld->sunParse.initWorldFog->halfDist = 4450.f; +// gfxWorld->sunParse.initWorldFog->halfHeight = 2000.f; +// gfxWorld->sunParse.initWorldFog->sunFogColor.x = 5.27f; +// gfxWorld->sunParse.initWorldFog->sunFogColor.y = 4.73f; +// gfxWorld->sunParse.initWorldFog->sunFogColor.z = 3.88f; +// gfxWorld->sunParse.initWorldFog->sunFogInner = 0.0f; +// gfxWorld->sunParse.initWorldFog->sunFogOpacity = 0.67f; +// gfxWorld->sunParse.initWorldFog->sunFogOuter = 80.84f; +// gfxWorld->sunParse.initWorldFog->sunFogPitch = -29.0f; +// gfxWorld->sunParse.initWorldFog->sunFogYaw = 254.0f; +// } +// +// void updateReflectionProbeData(GfxWorld* gfxWorld) +// { +// gfxWorld->draw.reflectionProbeCount = 1; +// +// gfxWorld->draw.reflectionProbeTextures = new GfxTexture[gfxWorld->draw.reflectionProbeCount]; +// memset(gfxWorld->draw.reflectionProbeTextures, 0, sizeof(GfxTexture) * gfxWorld->draw.reflectionProbeCount); +// +// gfxWorld->draw.reflectionProbes = new GfxReflectionProbe[gfxWorld->draw.reflectionProbeCount]; +// +// // default values taken from mp_dig +// gfxWorld->draw.reflectionProbes[0].mipLodBias = -8.0; +// +// gfxWorld->draw.reflectionProbes[0].origin.x = 0.0f; +// gfxWorld->draw.reflectionProbes[0].origin.y = 0.0f; +// gfxWorld->draw.reflectionProbes[0].origin.z = 0.0f; +// +// gfxWorld->draw.reflectionProbes[0].lightingSH.V0.x = 0.0f; +// gfxWorld->draw.reflectionProbes[0].lightingSH.V0.y = 0.0f; +// gfxWorld->draw.reflectionProbes[0].lightingSH.V0.z = 0.0f; +// gfxWorld->draw.reflectionProbes[0].lightingSH.V0.w = 0.0f; +// gfxWorld->draw.reflectionProbes[0].lightingSH.V1.x = 0.0f; +// gfxWorld->draw.reflectionProbes[0].lightingSH.V1.y = 0.0f; +// gfxWorld->draw.reflectionProbes[0].lightingSH.V1.z = 0.0f; +// gfxWorld->draw.reflectionProbes[0].lightingSH.V1.w = 0.0f; +// gfxWorld->draw.reflectionProbes[0].lightingSH.V2.x = 0.0f; +// gfxWorld->draw.reflectionProbes[0].lightingSH.V2.y = 0.0f; +// gfxWorld->draw.reflectionProbes[0].lightingSH.V2.z = 0.0f; +// gfxWorld->draw.reflectionProbes[0].lightingSH.V2.w = 0.0f; +// +// gfxWorld->draw.reflectionProbes[0].probeVolumeCount = 0; +// gfxWorld->draw.reflectionProbes[0].probeVolumes = NULL; +// +// std::string probeImageName = "*reflection_probe0"; +// auto probeImageAsset = m_context.LoadDependency(probeImageName); +// if (probeImageAsset == NULL) +// { +// printf("ERROR! unable to find image %s!\n", probeImageName.c_str()); +// hasLinkFailed = true; +// return; +// } +// gfxWorld->draw.reflectionProbes[0].reflectionImage = probeImageAsset->Asset(); +// } +// +// void overwriteLightmapData(GfxWorld* gfxWorld) +// { +// gfxWorld->draw.lightmapCount = 1; +// +// gfxWorld->draw.lightmapPrimaryTextures = new GfxTexture[gfxWorld->draw.lightmapCount]; +// gfxWorld->draw.lightmapSecondaryTextures = new GfxTexture[gfxWorld->draw.lightmapCount]; +// gfxWorld->draw.lightmaps = new GfxLightmapArray[gfxWorld->draw.lightmapCount]; +// +// // always set to 0 +// memset(gfxWorld->draw.lightmapPrimaryTextures, 0, sizeof(GfxTexture) * gfxWorld->draw.lightmapCount); +// memset(gfxWorld->draw.lightmapSecondaryTextures, 0, sizeof(GfxTexture) * gfxWorld->draw.lightmapCount); +// +// std::string secondaryTexture = "*lightmap0_secondary"; +// auto secondaryTextureAsset = m_context.LoadDependency(secondaryTexture); +// if (secondaryTextureAsset == NULL) +// { +// printf("ERROR! unable to find lightmap image %s!\n", secondaryTexture.c_str()); +// hasLinkFailed = true; +// return; +// } +// gfxWorld->draw.lightmaps[0].primary = NULL; // always NULL +// gfxWorld->draw.lightmaps[0].secondary = secondaryTextureAsset->Asset(); +// } +// +// void overwriteSkyBox(GfxWorld* gfxWorld) +// { +// const char* skyBoxName = "skybox_mp_dig"; +// gfxWorld->skyBoxModel = skyBoxName; +// +// if (m_context.LoadDependency(skyBoxName) == NULL) +// { +// printf("WARN: Unable to find the skybox model %s\n", skyBoxName); +// } +// +// // default skybox values from mp_dig +// gfxWorld->skyDynIntensity.angle0 = 0.0f; +// gfxWorld->skyDynIntensity.angle1 = 0.0f; +// gfxWorld->skyDynIntensity.factor0 = 1.0f; +// gfxWorld->skyDynIntensity.factor1 = 1.0f; +// } +// +// void updateDynEntData(GfxWorld* gfxWorld) +// { +// gfxWorld->dpvsDyn.dynEntClientCount[0] = DYN_ENT_COUNT + 256; +// gfxWorld->dpvsDyn.dynEntClientCount[1] = 0; +// gfxWorld->dpvsDyn.dynEntClientWordCount[0] = 1; // needs to be at least 1 +// gfxWorld->dpvsDyn.dynEntClientWordCount[1] = 0; +// gfxWorld->dpvsDyn.usageCount = 0; +// +// int dynEntCellBitsSize = gfxWorld->dpvsDyn.dynEntClientWordCount[0] * gfxWorld->dpvsPlanes.cellCount; +// gfxWorld->dpvsDyn.dynEntCellBits[0] = new unsigned int[dynEntCellBitsSize]; +// gfxWorld->dpvsDyn.dynEntCellBits[1] = NULL; +// memset(gfxWorld->dpvsDyn.dynEntCellBits[0], 0, sizeof(unsigned int) * dynEntCellBitsSize); +// +// int dynEntVisData0Size = gfxWorld->dpvsDyn.dynEntClientWordCount[0] * 32; +// gfxWorld->dpvsDyn.dynEntVisData[0][0] = new char[dynEntVisData0Size]; +// gfxWorld->dpvsDyn.dynEntVisData[0][1] = new char[dynEntVisData0Size]; +// gfxWorld->dpvsDyn.dynEntVisData[0][2] = new char[dynEntVisData0Size]; +// gfxWorld->dpvsDyn.dynEntVisData[1][0] = NULL; +// gfxWorld->dpvsDyn.dynEntVisData[1][1] = NULL; +// gfxWorld->dpvsDyn.dynEntVisData[1][2] = NULL; +// memset(gfxWorld->dpvsDyn.dynEntVisData[0][0], 0, dynEntVisData0Size); +// memset(gfxWorld->dpvsDyn.dynEntVisData[0][1], 0, dynEntVisData0Size); +// memset(gfxWorld->dpvsDyn.dynEntVisData[0][2], 0, dynEntVisData0Size); +// +// int dynEntShadowVisCount = gfxWorld->dpvsDyn.dynEntClientCount[0] * (gfxWorld->primaryLightCount - gfxWorld->sunPrimaryLightIndex - 1); +// gfxWorld->primaryLightDynEntShadowVis[0] = new unsigned int[dynEntShadowVisCount]; +// gfxWorld->primaryLightDynEntShadowVis[1] = NULL; +// memset(gfxWorld->primaryLightDynEntShadowVis[0], 0, sizeof(unsigned int) * dynEntShadowVisCount); +// +// gfxWorld->sceneDynModel = new GfxSceneDynModel[gfxWorld->dpvsDyn.dynEntClientCount[0]]; +// gfxWorld->sceneDynBrush = NULL; +// memset(gfxWorld->sceneDynModel, 0, sizeof(GfxSceneDynModel) * gfxWorld->dpvsDyn.dynEntClientCount[0]); +// } +// +// void updateOutdoors(GfxWorld* gfxWorld) +// { +// float xRecip = 1.0f / (gfxWorld->maxs.x - gfxWorld->mins.x); +// float xScale = -(xRecip * gfxWorld->mins.x); +// +// float yRecip = 1.0f / (gfxWorld->maxs.y - gfxWorld->mins.y); +// float yScale = -(yRecip * gfxWorld->mins.y); +// +// float zRecip = 1.0f / (gfxWorld->maxs.z - gfxWorld->mins.z); +// float zScale = -(zRecip * gfxWorld->mins.z); +// +// memset(gfxWorld->outdoorLookupMatrix, 0, sizeof(gfxWorld->outdoorLookupMatrix)); +// +// gfxWorld->outdoorLookupMatrix[0].x = xRecip; +// gfxWorld->outdoorLookupMatrix[1].y = yRecip; +// gfxWorld->outdoorLookupMatrix[2].z = zRecip; +// gfxWorld->outdoorLookupMatrix[3].x = xScale; +// gfxWorld->outdoorLookupMatrix[3].y = yScale; +// gfxWorld->outdoorLookupMatrix[3].z = zScale; +// gfxWorld->outdoorLookupMatrix[3].w = 1.0f; +// +// std::string outdoorImageName = std::string("$outdoor"); +// auto outdoorImageAsset = m_context.LoadDependency(outdoorImageName); +// if (outdoorImageAsset == NULL) +// { +// printf("ERROR! unable to find image $outdoor, this will crash your game!\n"); +// hasLinkFailed = true; +// return; +// } +// gfxWorld->outdoorImage = outdoorImageAsset->Asset(); +// } +// +// void createGfxWorld(customMapInfo* projInfo) +// { +// GfxWorld* gfxWorld = new GfxWorld; +// gfxWorld->baseName = _strdup(projInfo->name.c_str()); +// gfxWorld->name = _strdup(projInfo->bspName.c_str()); +// +// // Default values taken from mp_dig +// gfxWorld->lightingFlags = 0; +// gfxWorld->lightingQuality = 4096; +// +// cleanGfxWorld(gfxWorld); +// +// overwriteMapSurfaces(projInfo, gfxWorld); +// +// overwriteMapSModels(gfxWorld); +// +// overwriteLightmapData(gfxWorld); +// +// overwriteSkyBox(gfxWorld); +// +// updateReflectionProbeData(gfxWorld); +// +// // world bounds are based on surface mins/maxs +// // Other update functions depend on the bounds being set first +// updateWorldBounds(gfxWorld); +// +// updateOutdoors(gfxWorld); +// +// // gfx cells depend on surface/smodel count +// updateGfxCells(gfxWorld); +// +// overwriteLightGrid(gfxWorld); +// +// overwriteGfxLights(gfxWorld); +// +// overwriteModels(gfxWorld); +// +// updateSunData(gfxWorld); +// +// updateDynEntData(gfxWorld); +// +// m_context.AddAsset(gfxWorld->name, gfxWorld); +// } +// +// void aabbCalcOriginAndHalfSize(vec3_t* mins, vec3_t* maxs, vec3_t* out_origin, vec3_t* out_halfSize) +// { +// // Origin is the midpoint: (min + max) / 2 +// vec3_t temp; +// temp.x = mins->x + maxs->x; +// temp.y = mins->y + maxs->y; +// temp.z = mins->z + maxs->z; +// out_origin->x = temp.x * 0.5f; +// out_origin->y = temp.y * 0.5f; +// out_origin->z = temp.z * 0.5f; +// +// // Half-size is half the difference: (max - min) / 2 +// temp.x = maxs->x - mins->x; +// temp.y = maxs->y - mins->y; +// temp.z = maxs->z - mins->z; +// out_halfSize->x = temp.x * 0.5f; +// out_halfSize->y = temp.y * 0.5f; +// out_halfSize->z = temp.z * 0.5f; +// } +// +// bool CM_IsEdgeWalkable(clipMap_t* clipMap, int triIndex, int edgeIndex) +// { +// +// unsigned __int8 edgeBitMask = 1 << ((triIndex + edgeIndex + 2 * triIndex) & 7); +// +// 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) +// { +// if (node->isLeaf) +// { +// (*numLeafs)++; +// // there won't be an AABB tree when objectList is empty +// if (node->u.leaf->getObjectCount() > 0) +// { +// *numAABBTrees += node->u.leaf->getObjectCount() + 1; +// +// if (node->u.leaf->getObjectCount() > *maxObjsPerLeaf) +// *maxObjsPerLeaf = node->u.leaf->getObjectCount(); +// } +// } +// else +// { +// (*numPlanes)++; +// (*numNodes)++; +// traverseBSPTreeForCounts(node->u.node->front, numPlanes, numNodes, numLeafs, numAABBTrees, maxObjsPerLeaf); +// traverseBSPTreeForCounts(node->u.node->back, numPlanes, numNodes, numLeafs, numAABBTrees, maxObjsPerLeaf); +// } +// } +// +// vec3_t normalX = {1.0f, 0.0f, 0.0f}; +// vec3_t normalY = {0.0f, 1.0f, 0.0f}; +// vec3_t normalZ = {0.0f, 0.0f, 1.0f}; +// +// int currPlaneCount = 0; +// int currNodeCount = 0; +// int currLeafCount = 0; +// int currAABBCount = 0; +// +// int addAABBTreeFromLeaf(BSPTree* node, clipMap_t* clipMap) +// { +// _ASSERT(node->isLeaf); +// +// int objectCount = node->u.leaf->getObjectCount(); +// int firstAABBIndex = currAABBCount; +// currAABBCount += objectCount + 1; +// +// // calculate root AABB node mins and maxs +// // cannot convert mins and maxs coord to BO2 directly as this will result in incorrect mins and maxs +// // so we have to recompute every min and max, not hard just tedious +// int firstPartitionIndex = node->u.leaf->getObject(0)->partitionIndex; +// auto firstPartition = &clipMap->partitions[firstPartitionIndex]; +// uint16_t* firstTri = clipMap->triIndices[firstPartition->firstTri]; +// vec3_t* firstVert = &clipMap->verts[firstTri[0]]; +// vec3_t aabbMins; +// vec3_t aabbMaxs; +// aabbMins.x = firstVert->x; +// aabbMins.y = firstVert->y; +// aabbMins.z = firstVert->z; +// aabbMaxs.x = firstVert->x; +// aabbMaxs.y = firstVert->y; +// aabbMaxs.z = firstVert->z; +// for (int i = 0; i < objectCount; i++) +// { +// int currPartitionIndex = node->u.leaf->getObject(i)->partitionIndex; +// auto currPartition = &clipMap->partitions[currPartitionIndex]; +// +// for (int k = 0; k < currPartition->triCount; k++) +// { +// uint16_t* tri = clipMap->triIndices[currPartition->firstTri + k]; +// for (int l = 0; l < 3; l++) +// { +// uint16_t vertIndex = tri[l]; +// vec3_t vertCoord = clipMap->verts[vertIndex]; +// CMUtil::calcNewBoundsWithPoint(&vertCoord, &aabbMins, &aabbMaxs); +// } +// } +// } +// // create root AABB node +// CollisionAabbTree* rootAABB = &clipMap->aabbTrees[firstAABBIndex]; +// aabbCalcOriginAndHalfSize(&aabbMins, &aabbMaxs, &rootAABB->origin, &rootAABB->halfSize); +// rootAABB->materialIndex = 0; +// rootAABB->childCount = objectCount; +// rootAABB->u.firstChildIndex = firstAABBIndex + 1; +// +// // populate child AABB nodes +// for (int i = 0; i < objectCount; i++) +// { +// CollisionAabbTree* currAabbTree = &clipMap->aabbTrees[rootAABB->u.firstChildIndex + i]; +// int currPartitionIndex = node->u.leaf->getObject(i)->partitionIndex; +// +// currAabbTree->materialIndex = 0; +// currAabbTree->childCount = 0; +// currAabbTree->u.partitionIndex = currPartitionIndex; +// +// // calculate partition origin and half size +// CollisionPartition* aabbPartition = &clipMap->partitions[currPartitionIndex]; +// uint16_t firstUind = clipMap->info.uinds[aabbPartition->fuind]; +// vec3_t* firstVertex = &clipMap->verts[firstUind]; +// vec3_t mins; +// vec3_t maxs; +// mins.x = firstVertex->x; +// mins.y = firstVertex->y; +// mins.z = firstVertex->z; +// maxs.x = firstVertex->x; +// maxs.y = firstVertex->y; +// maxs.z = firstVertex->z; +// for (int i = 1; i < aabbPartition->nuinds; i++) +// { +// uint16_t currUind = clipMap->info.uinds[aabbPartition->fuind + i]; +// vec3_t* currVertex = &clipMap->verts[currUind]; +// +// CMUtil::calcNewBoundsWithPoint(currVertex, &mins, &maxs); +// } +// +// aabbCalcOriginAndHalfSize(&mins, &maxs, &currAabbTree->origin, &currAabbTree->halfSize); +// +// } +// +// return firstAABBIndex; +// } +// +// // returns the index corresponding to the BSPTree* node parsed +// int16_t populateBSPTree_r(clipMap_t* clipMap, BSPTree* node) +// { +// new cplane_s; +// new cNode_t; +// new cLeaf_s; +// new CollisionAabbTree; +// +// if (node->isLeaf) +// { +// int currLeafIndex = currLeafCount; +// currLeafCount++; +// cLeaf_s* currLeaf = &clipMap->leafs[currLeafIndex]; +// +// currLeaf->cluster = 0; +// currLeaf->brushContents = 0; // no brushes used so contents is 0 +// currLeaf->terrainContents = LEAF_TERRAIN_CONTENTS; // clipMap->cmodels[0].leaf.terrainContents takes prescedence +// +// // unused when leafBrushNode == 0 +// currLeaf->mins.x = 0.0f; +// currLeaf->mins.y = 0.0f; +// currLeaf->mins.z = 0.0f; +// currLeaf->maxs.x = 0.0f; +// currLeaf->maxs.y = 0.0f; +// currLeaf->maxs.z = 0.0f; +// currLeaf->leafBrushNode = 0; +// +// if (node->u.leaf->getObjectCount() > 0) +// { +// currLeaf->firstCollAabbIndex = addAABBTreeFromLeaf(node, clipMap); +// currLeaf->collAabbCount = 1; +// } +// else +// { +// currLeaf->firstCollAabbIndex = 0; +// currLeaf->collAabbCount = 0; +// } +// +// return -1 - currLeafIndex; +// } +// else +// { +// cplane_s* currPlane = &clipMap->info.planes[currPlaneCount]; +// currPlaneCount++; +// +// if (node->u.node->axis == AXIS_X) +// { +// // X is unchanged when going from OGL x -> BO2 x +// currPlane->normal = normalX; +// +// // converting OGL -> BO2 X coords doesn't change the x coords at all, so +// // the dist stays the same +// currPlane->dist = node->u.node->distance; +// } +// else +// { +// // converting OGL -> BO2 Z coords negates the z coords and sets it to the y coord. +// // convert the z normal to the y normal, but don't negate it. Negative normals don't do +// // what is expected when the game uses them +// _ASSERT(node->u.node->axis == AXIS_Z); +// currPlane->normal = normalY; +// +// // converting OGL -> BO2 Z coords negates the z coords and sets it to the y coord. +// // just negate it here as it is just the distance from the orgin along the axis +// currPlane->dist = -node->u.node->distance; +// } +// +// bool foundType = false; +// if (currPlane->normal.x == 1.0f) +// { +// _ASSERT(!foundType); +// foundType = true; +// currPlane->type = 0; +// } +// else if (currPlane->normal.y == 1.0f) +// { +// _ASSERT(!foundType); +// foundType = true; +// currPlane->type = 1; +// } +// else if (currPlane->normal.z == 1.0f) +// { +// _ASSERT(!foundType); +// foundType = true; +// currPlane->type = 2; +// } +// else +// _ASSERT(foundType); +// +// currPlane->signbits = 0; +// if (currPlane->normal.x < 0.0f) +// currPlane->signbits |= 1; +// if (currPlane->normal.y < 0.0f) +// currPlane->signbits |= 2; +// if (currPlane->normal.z < 0.0f) +// currPlane->signbits |= 4; +// +// currPlane->pad[0] = 0; +// currPlane->pad[1] = 0; +// +// int currNodeIndex = currNodeCount; +// currNodeCount++; +// cNode_t* currNode = &clipMap->nodes[currNodeIndex]; +// +// currNode->plane = currPlane; +// // Reason for the front and back flip (due to the hacky nature of making the mins and maxs work (see createClipMap)): +// // after converting between OGL and BO2 coords and when and updating the normal from Z -> Y, +// // the normal vector flips and objects behind the plane are now in front, and vise versa +// // so the back node now represents the front, and the front node represents the back. +// // Do the OGL -> Bo2 coord change on paper and it will make sense +// if (currPlane->type == 1) +// { +// currNode->children[1] = populateBSPTree_r(clipMap, node->u.node->front); +// currNode->children[0] = populateBSPTree_r(clipMap, node->u.node->back); +// } +// else +// { +// currNode->children[0] = populateBSPTree_r(clipMap, node->u.node->front); +// currNode->children[1] = populateBSPTree_r(clipMap, node->u.node->back); +// } +// +// return currNodeIndex; +// } +// } +// +// void populateBSPTree(clipMap_t* clipMap, BSPTree* tree) +// { +// int numPlanes = 0; +// int numNodes = 0; +// int numLeafs = 0; +// int numAABBTrees = 0; +// int maxObjsPerLeaf = 0; +// +// traverseBSPTreeForCounts(tree, &numPlanes, &numNodes, &numLeafs, &numAABBTrees, &maxObjsPerLeaf); +// +// printf("Max Objects per leaf: %i\n", maxObjsPerLeaf); +// +// clipMap->info.planeCount = numPlanes; +// clipMap->info.planes = new cplane_s[clipMap->info.planeCount]; +// clipMap->numNodes = numNodes; +// clipMap->nodes = new cNode_t[clipMap->numNodes]; +// // aabb trees: each leaf will have their own AABB tree of the objects within it, and the root aabb node will be the parent of every other aabb node. +// // therefore, each aabb tree will be of size (numObjects + 1) as the tree needs a root aabb node to reference it's children. +// clipMap->aabbTreeCount = numAABBTrees; +// clipMap->aabbTrees = new CollisionAabbTree[clipMap->aabbTreeCount]; +// +// currPlaneCount = 0; +// currNodeCount = 0; +// currAABBCount = 0; +// +// // first leaf is always empty +// clipMap->numLeafs = numLeafs + 1; +// clipMap->leafs = new cLeaf_s[clipMap->numLeafs]; +// memset(&clipMap->leafs[0], 0, sizeof(cLeaf_s)); +// currLeafCount = 1; +// +// populateBSPTree_r(clipMap, tree); +// +// _ASSERT(clipMap->info.planeCount == currPlaneCount); +// _ASSERT(clipMap->numNodes == currNodeCount); +// _ASSERT(clipMap->numLeafs == currLeafCount); +// _ASSERT(clipMap->aabbTreeCount == currAABBCount); +// } +// +// void createClipMap(customMapInfo* projInfo) +// { +// clipMap_t* clipMap = new clipMap_t; +// +// clipMap->name = _strdup(projInfo->bspName.c_str()); +// +// std::string mapEntsName = projInfo->bspName.c_str(); +// auto mapEntsAsset = m_context.LoadDependency(mapEntsName); +// _ASSERT(mapEntsAsset != NULL); +// clipMap->mapEnts = mapEntsAsset->Asset(); +// +// clipMap->pInfo = NULL; +// +// clipMap->box_model.mins.x = 0.0f; +// clipMap->box_model.mins.y = 0.0f; +// clipMap->box_model.mins.z = 0.0f; +// clipMap->box_model.maxs.x = 0.0f; +// clipMap->box_model.maxs.y = 0.0f; +// clipMap->box_model.maxs.z = 0.0f; +// clipMap->box_model.radius = 0.0f; +// clipMap->box_model.info = NULL; +// clipMap->box_model.leaf.mins.x = 3.4028235e38; +// clipMap->box_model.leaf.mins.y = 3.4028235e38; +// clipMap->box_model.leaf.mins.z = 3.4028235e38; +// clipMap->box_model.leaf.maxs.x = -3.4028235e38; // for some reason the maxs are negative, and mins are positive +// clipMap->box_model.leaf.maxs.y = -3.4028235e38; +// clipMap->box_model.leaf.maxs.z = -3.4028235e38; +// clipMap->box_model.leaf.brushContents = -1; +// clipMap->box_model.leaf.terrainContents = 0; +// clipMap->box_model.leaf.cluster = 0; +// clipMap->box_model.leaf.collAabbCount = 0; +// clipMap->box_model.leaf.firstCollAabbIndex = 0; +// clipMap->box_model.leaf.leafBrushNode = 0; +// +// clipMap->box_brush = new cbrush_t; +// clipMap->box_brush->axial_sflags[0][0] = -1; +// clipMap->box_brush->axial_sflags[0][1] = -1; +// clipMap->box_brush->axial_sflags[0][2] = -1; +// clipMap->box_brush->axial_sflags[1][0] = -1; +// clipMap->box_brush->axial_sflags[1][1] = -1; +// clipMap->box_brush->axial_sflags[1][2] = -1; +// clipMap->box_brush->axial_cflags[0][0] = -1; +// clipMap->box_brush->axial_cflags[0][1] = -1; +// clipMap->box_brush->axial_cflags[0][2] = -1; +// clipMap->box_brush->axial_cflags[1][0] = -1; +// clipMap->box_brush->axial_cflags[1][1] = -1; +// clipMap->box_brush->axial_cflags[1][2] = -1; +// clipMap->box_brush->contents = -1; +// clipMap->box_brush->mins.x = 0.0f; +// clipMap->box_brush->mins.y = 0.0f; +// clipMap->box_brush->mins.z = 0.0f; +// clipMap->box_brush->maxs.x = 0.0f; +// clipMap->box_brush->maxs.y = 0.0f; +// clipMap->box_brush->maxs.z = 0.0f; +// clipMap->box_brush->numsides = 0; +// clipMap->box_brush->numverts = 0; +// clipMap->box_brush->sides = NULL; +// clipMap->box_brush->verts = NULL; +// +// clipMap->numClusters = 1; +// clipMap->vised = 0; +// clipMap->clusterBytes = ((clipMap->numClusters + 63) >> 3) & 0xFFFFFFF8; +// clipMap->visibility = new char[clipMap->clusterBytes]; +// memset(clipMap->visibility, 0xFF, clipMap->clusterBytes); +// +// clipMap->isInUse = true; +// clipMap->checksum = 0; +// +// clipMap->num_constraints = 0; +// clipMap->constraints = NULL; +// clipMap->max_ropes = 32; +// clipMap->ropes = new rope_t[clipMap->max_ropes]; +// memset(clipMap->ropes, 0, sizeof(rope_t) * clipMap->max_ropes); +// +// clipMap->info.numBrushSides = 0; +// clipMap->info.brushsides = NULL; +// clipMap->info.leafbrushNodesCount = 0; +// clipMap->info.leafbrushNodes = NULL; +// clipMap->info.numLeafBrushes = 0; +// clipMap->info.leafbrushes = NULL; +// clipMap->info.numBrushVerts = 0; +// clipMap->info.brushVerts = NULL; +// clipMap->info.numBrushes = NULL; +// clipMap->info.brushes = NULL; +// clipMap->info.brushBounds = NULL; +// clipMap->info.brushContents = NULL; +// +// clipMap->originalDynEntCount = DYN_ENT_COUNT; +// clipMap->dynEntCount[0] = clipMap->originalDynEntCount + 256; +// clipMap->dynEntCount[1] = 0; +// clipMap->dynEntCount[2] = 0; +// clipMap->dynEntCount[3] = 0; +// +// // assume that there are 0 dyn ents from here on +// clipMap->dynEntClientList[0] = new DynEntityClient[clipMap->dynEntCount[0]]; +// clipMap->dynEntClientList[1] = NULL; +// memset(clipMap->dynEntClientList[0], 0, sizeof(DynEntityClient) * clipMap->dynEntCount[0]); +// +// clipMap->dynEntServerList[0] = NULL; +// clipMap->dynEntServerList[1] = NULL; +// +// clipMap->dynEntCollList[0] = new DynEntityColl[clipMap->dynEntCount[0]]; +// clipMap->dynEntCollList[1] = NULL; +// clipMap->dynEntCollList[2] = NULL; +// clipMap->dynEntCollList[3] = NULL; +// memset(clipMap->dynEntCollList[0], 0, sizeof(DynEntityColl) * clipMap->dynEntCount[0]); +// +// clipMap->dynEntPoseList[0] = new DynEntityPose[clipMap->dynEntCount[0]]; +// clipMap->dynEntPoseList[1] = NULL; +// memset(clipMap->dynEntPoseList[0], 0, sizeof(DynEntityPose) * clipMap->dynEntCount[0]); +// +// clipMap->dynEntDefList[0] = new DynEntityDef[clipMap->dynEntCount[0]]; +// clipMap->dynEntDefList[1] = NULL; +// memset(clipMap->dynEntDefList[0], 0, sizeof(DynEntityDef) * clipMap->dynEntCount[0]); +// for (int i = 0; i < clipMap->dynEntCount[0]; i++) +// { +// DynEntityDef* currDef = &clipMap->dynEntDefList[0][i]; +// currDef->physConstraints[0] = 0x1FF; +// currDef->physConstraints[1] = 0x1FF; +// currDef->physConstraints[2] = 0x1FF; +// currDef->physConstraints[3] = 0x1FF; +// } +// +// clipMap->numSubModels = 1; +// clipMap->cmodels = new cmodel_t[clipMap->numSubModels]; +// clipMap->cmodels[0].leaf.firstCollAabbIndex = 0; +// clipMap->cmodels[0].leaf.collAabbCount = 0; +// clipMap->cmodels[0].leaf.brushContents = 0; +// clipMap->cmodels[0].leaf.terrainContents = WORLD_TERRAIN_CONTENTS; +// clipMap->cmodels[0].leaf.mins.x = 0.0f; +// clipMap->cmodels[0].leaf.mins.y = 0.0f; +// clipMap->cmodels[0].leaf.mins.z = 0.0f; +// clipMap->cmodels[0].leaf.maxs.x = 0.0f; +// clipMap->cmodels[0].leaf.maxs.y = 0.0f; +// clipMap->cmodels[0].leaf.maxs.z = 0.0f; +// clipMap->cmodels[0].leaf.leafBrushNode = 0; +// clipMap->cmodels[0].leaf.cluster = 0; +// clipMap->cmodels[0].info = NULL; +// +// auto gfxWorldAsset = m_context.LoadDependency(projInfo->bspName); +// _ASSERT(gfxWorldAsset != NULL); +// GfxWorld* gfxWorld = gfxWorldAsset->Asset(); +// +// 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.z = gfxWorld->models[0].bounds[0].z; +// 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.z = gfxWorld->models[0].bounds[1].z; +// clipMap->cmodels[0].radius = distBetweenPoints(clipMap->cmodels[0].mins, clipMap->cmodels[0].maxs) / 2; +// +// clipMap->numStaticModels = 0; +// clipMap->staticModelList = NULL; +// +// clipMap->info.numMaterials = 1; +// clipMap->info.materials = new ClipMaterial[clipMap->info.numMaterials]; +// clipMap->info.materials[0].name = _strdup(defaultMaterialName.c_str()); +// clipMap->info.materials[0].contentFlags = MATERIAL_CONTENT_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 +// // might do weird stuff on walls +// int walkableEdgeSize = (3 * clipMap->triCount + 31) / 32 * 4; +// clipMap->triEdgeIsWalkable = new char[walkableEdgeSize]; +// memset(clipMap->triEdgeIsWalkable, 0xFF, walkableEdgeSize * sizeof(char)); +// +// // BSP creation must go last as it depends on unids, tris and verts already being populated +// // HACK: +// // the BSP tree creation does not work when BO2's coordinate system is used for mins and maxs. +// // Workaround is to convert every BO2 coordinate to OGL's before it is added into the BSP tree, +// // and then convert them back when it is being parsed into the clipmap. Requires some hacky +// // logic, check populateBSPTree_r and addAABBTreeFromLeaf +// +// vec3_t* firstVert = &clipMap->verts[0]; +// vec3_t clipMins; +// vec3_t clipMaxs; +// clipMins.x = firstVert->x; +// clipMins.y = firstVert->y; +// clipMins.z = firstVert->z; +// clipMaxs.x = firstVert->x; +// clipMaxs.y = firstVert->y; +// clipMaxs.z = firstVert->z; +// clipMins = CMUtil::convertFromBO2Coords(clipMins); +// clipMaxs = CMUtil::convertFromBO2Coords(clipMaxs); +// for (int i = 1; i < clipMap->vertCount; i++) +// { +// vec3_t vertCoord = CMUtil::convertFromBO2Coords(clipMap->verts[i]); +// CMUtil::calcNewBoundsWithPoint(&vertCoord, &clipMins, &clipMaxs); +// } +// +// BSPTree* tree = new BSPTree(clipMins.x, clipMins.y, clipMins.z, clipMaxs.x, clipMaxs.y, clipMaxs.z, 0); +// for (int i = 0; i < clipMap->partitionCount; i++) +// { +// auto currPartition = &clipMap->partitions[i]; +// +// uint16_t* firstTri = clipMap->triIndices[currPartition->firstTri]; +// vec3_t* firstVert = &clipMap->verts[firstTri[0]]; +// vec3_t mins; +// vec3_t maxs; +// mins.x = firstVert->x; +// mins.y = firstVert->y; +// mins.z = firstVert->z; +// maxs.x = firstVert->x; +// maxs.y = firstVert->y; +// maxs.z = firstVert->z; +// mins = CMUtil::convertFromBO2Coords(mins); +// maxs = CMUtil::convertFromBO2Coords(maxs); +// for (int k = 0; k < currPartition->triCount; k++) +// { +// uint16_t* tri = clipMap->triIndices[currPartition->firstTri + k]; +// for (int l = 0; l < 3; l++) +// { +// uint16_t vertIndex = tri[l]; +// vec3_t vertCoord = CMUtil::convertFromBO2Coords(clipMap->verts[vertIndex]); +// CMUtil::calcNewBoundsWithPoint(&vertCoord, &mins, &maxs); +// } +// } +// +// Object* currObject = new Object(mins.x, mins.y, mins.z, maxs.x, maxs.y, maxs.z, i); +// +// tree->addObject(currObject); +// } +// +// populateBSPTree(clipMap, tree); +// +// m_context.AddAsset(clipMap->name, clipMap); +// } +// +// void createComWorld(customMapInfo* projInfo) +// { +// // all lights that aren't the sunlight or default light need their own GfxLightDef asset +// ComWorld* comWorld = new ComWorld; +// comWorld->name = _strdup(projInfo->bspName.c_str()); +// comWorld->isInUse = 1; +// comWorld->primaryLightCount = 2; +// comWorld->primaryLights = new ComPrimaryLight[comWorld->primaryLightCount]; +// +// ComPrimaryLight* defaultLight = &comWorld->primaryLights[0]; +// memset(defaultLight, 0, sizeof(ComPrimaryLight)); +// +// ComPrimaryLight* sunLight = &comWorld->primaryLights[1]; +// memset(sunLight, 0, sizeof(ComPrimaryLight)); +// sunLight->type = 1; +// // Below are default values taken from mp_dig +// sunLight->diffuseColor.r = 1.0f; +// sunLight->diffuseColor.g = 1.0f; +// sunLight->diffuseColor.b = 1.0f; +// sunLight->diffuseColor.a = 0.0f; +// sunLight->dir.x = -0.2410777360200882; +// sunLight->dir.y = -0.8407384753227234; +// sunLight->dir.z = 0.48480960726737976; +// +// m_context.AddAsset(comWorld->name, comWorld); +// } +// +// void createMapEnts(customMapInfo* projInfo) +// { +// MapEnts* mapEnts = new MapEnts; +// +// mapEnts->name = _strdup(projInfo->bspName.c_str()); +// +// // don't need these +// mapEnts->trigger.count = 0; +// mapEnts->trigger.models = NULL; +// mapEnts->trigger.hullCount = 0; +// mapEnts->trigger.hulls = NULL; +// mapEnts->trigger.slabCount = 0; +// mapEnts->trigger.slabs = NULL; +// +// const auto file = m_search_path.Open("custom_map/entities.json"); +// if (!file.IsOpen()) +// { +// printf("WARN: can't find entity json!\n"); +// return; +// } +// +// json entJs = json::parse(*file.m_stream); +// +// std::string entityString; +// int entityCount = entJs["entityCount"]; +// for (int i = 0; i < entityCount; i++) +// { +// auto currEntity = entJs["entities"][i]; +// +// if (i == 0) +// { +// std::string className = currEntity["classname"]; +// if (className.compare("worldspawn") != 0) +// { +// printf("ERROR: first entity in the map entity string must be the worldspawn class!"); +// hasLinkFailed = true; +// return; +// } +// } +// +// entityString.append("{\n"); +// +// for (auto& element : currEntity.items()) +// { +// std::string key = element.key(); +// std::string value = element.value(); +// entityString.append(std::format("\"{}\" \"{}\"\n", key, value)); +// } +// +// entityString.append("}\n"); +// } +// entityString.pop_back(); // remove newline character from the end of the string +// +// mapEnts->entityString = _strdup(entityString.c_str()); +// mapEnts->numEntityChars = entityString.length() + 1; // numEntityChars includes the null character +// +// m_context.AddAsset(mapEnts->name, mapEnts); +// } +// +// void createGameWorldMp(customMapInfo* projInfo) +// { +// GameWorldMp* gameWorldMp = new GameWorldMp; +// +// gameWorldMp->name = _strdup(projInfo->bspName.c_str()); +// +// gameWorldMp->path.nodeCount = 0; +// gameWorldMp->path.originalNodeCount = 0; +// gameWorldMp->path.visBytes = 0; +// gameWorldMp->path.smoothBytes = 0; +// gameWorldMp->path.nodeTreeCount = 0; +// +// int nodeCount = gameWorldMp->path.nodeCount + 128; +// gameWorldMp->path.nodes = new pathnode_t[nodeCount]; +// gameWorldMp->path.basenodes = new pathbasenode_t[nodeCount]; +// memset(gameWorldMp->path.nodes, 0, nodeCount * sizeof(pathnode_t)); +// memset(gameWorldMp->path.basenodes, 0, nodeCount * sizeof(pathbasenode_t)); +// +// gameWorldMp->path.pathVis = NULL; +// gameWorldMp->path.smoothCache = NULL; +// gameWorldMp->path.nodeTree = NULL; +// +// m_context.AddAsset(gameWorldMp->name, gameWorldMp); +// } +// +// void createSkinnedVerts(customMapInfo* projInfo) +// { +// SkinnedVertsDef* skinnedVerts = new SkinnedVertsDef; +// skinnedVerts->name = "skinnedverts"; +// skinnedVerts->maxSkinnedVerts = projInfo->gfxInfo.vertexCount; +// +// m_context.AddAsset("skinnedverts", skinnedVerts); +// } +// +// FootstepTableDef* addEmptyFootstepTableAsset(std::string assetName) +// { +// if (assetName.length() == 0) +// return NULL; +// +// FootstepTableDef* footstepTable = new FootstepTableDef; +// footstepTable->name = _strdup(assetName.c_str()); +// memset(footstepTable->sndAliasTable, 0, sizeof(footstepTable->sndAliasTable)); +// +// m_context.AddAsset(assetName, footstepTable); +// +// return footstepTable; +// } +// +// void checkAndAddDefaultRequiredAssets(customMapInfo* projectInfo) +// { +// if (m_context.LoadDependency("maps/mp/mp_dig.gsc") == NULL) { hasLinkFailed = true; return; } +// if (m_context.LoadDependency("maps/mp/mp_dig_amb.gsc") == NULL) { hasLinkFailed = true; return; } +// if (m_context.LoadDependency("maps/mp/mp_dig_fx.gsc") == NULL) { hasLinkFailed = true; return; } +// if (m_context.LoadDependency("maps/mp/createfx/mp_dig_fx.gsc") == NULL) { hasLinkFailed = true; return; } +// if (m_context.LoadDependency("clientscripts/mp/mp_dig.csc") == NULL) { hasLinkFailed = true; return; } +// if (m_context.LoadDependency("clientscripts/mp/mp_dig_amb.csc") == NULL) { hasLinkFailed = true; return; } +// if (m_context.LoadDependency("clientscripts/mp/mp_dig_fx.csc") == NULL) { hasLinkFailed = true; return; } +// if (m_context.LoadDependency("clientscripts/mp/createfx/mp_dig_fx.csc") == NULL) { hasLinkFailed = true; return; } +// +// addEmptyFootstepTableAsset("default_1st_person"); +// addEmptyFootstepTableAsset("default_3rd_person"); +// addEmptyFootstepTableAsset("default_1st_person_quiet"); +// addEmptyFootstepTableAsset("default_3rd_person_quiet"); +// addEmptyFootstepTableAsset("default_3rd_person_loud"); +// addEmptyFootstepTableAsset("default_ai"); +// } +//}; diff --git a/src/ObjLoading/Game/T6/CustomMap/CustomMapLinker.h b/src/ObjLoading/Game/T6/CustomMap/CustomMapLinker.h index 6dcefb44..e66c1caa 100644 --- a/src/ObjLoading/Game/T6/CustomMap/CustomMapLinker.h +++ b/src/ObjLoading/Game/T6/CustomMap/CustomMapLinker.h @@ -1,14 +1,1667 @@ -#include "Game/T6/T6.h" -#include "SearchPath/ISearchPath.h" -#include "Utils/MemoryManager.h" -#include "Asset/IAssetCreator.h" -using namespace T6; +#pragma once -#include "OBJ_Loader.h" +#include "BinarySpacePartitionTreePreCalc.h" +#include "CustomMapConsts.h" +#include "Util.h" +#include "Utils/Pack.h" + +#include +using json = nlohmann::json; class CustomMapLinker { public: - CustomMapLinker(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); - bool linkCustomMap(customMapInfo* projInfo); + CustomMapLinker(MemoryManager& memory, ISearchPath& searchPath, Zone& zone, AssetCreationContext& context) + : m_memory(memory), + m_search_path(searchPath), + m_zone(zone), + m_context(context) + { + hasLinkFailed = false; + } + + bool linkCustomMap(customMapInfo* projInfo) + { + _ASSERT(projInfo != NULL); + + createGfxWorld(projInfo); + createComWorld(projInfo); + createMapEnts(projInfo); + createGameWorldMp(projInfo); + createSkinnedVerts(projInfo); + createClipMap(projInfo); // must go last (requires gfx and mapents asset) + + checkAndAddDefaultRequiredAssets(projInfo); + + if (hasLinkFailed) + { + printf("Custom Map link has failed.\n"); + return false; + } + + return true; + } + +private: + MemoryManager& m_memory; + ISearchPath& m_search_path; + Zone& m_zone; + AssetCreationContext& m_context; + + bool hasLinkFailed; + + // TODO vd1: + // used for UVs of sub-textures, when it is set to empty all of them turn a blank colour + // could fix by removing sub textures or figure out how they are created and redo that + // its not an important issue though + bool overwriteDrawData(customMapInfo* projInfo, GfxWorld* gfxWorld) + { + int vertexCount = projInfo->gfxInfo.vertexCount; + customMapVertex* worldVertices = projInfo->gfxInfo.vertices; + + gfxWorld->draw.vertexCount = vertexCount; + gfxWorld->draw.vertexDataSize0 = vertexCount * sizeof(GfxPackedWorldVertex); + GfxPackedWorldVertex* vertexBuffer = new GfxPackedWorldVertex[vertexCount]; + for (int i = 0; i < vertexCount; i++) + { + customMapVertex* WorldVertex = &worldVertices[i]; + GfxPackedWorldVertex* GfxVertex = &vertexBuffer[i]; + + GfxVertex->xyz = CMUtil::convertToBO2Coords(WorldVertex->pos); + + GfxVertex->binormalSign = WorldVertex->binormalSign; + + GfxVertex->color.packed = pack32::Vec4PackGfxColor(WorldVertex->color); + + GfxVertex->texCoord.packed = pack32::Vec2PackTexCoordsUV(WorldVertex->texCoord); + + GfxVertex->normal.packed = pack32::Vec3PackUnitVecThirdBased(CMUtil::convertToBO2Coords(WorldVertex->normal).v); + + GfxVertex->tangent.packed = pack32::Vec3PackUnitVecThirdBased(CMUtil::convertToBO2Coords(WorldVertex->tangent).v); + + GfxVertex->lmapCoord.packed = WorldVertex->packedLmapCoord; + } + gfxWorld->draw.vd0.data = (char*)vertexBuffer; + + // we don't use vd1 but still needs to be initialised + // the data type varies and 0x20 is enough for all types + gfxWorld->draw.vertexDataSize1 = 0x20; + gfxWorld->draw.vd1.data = new char[gfxWorld->draw.vertexDataSize1]; + memset(gfxWorld->draw.vd1.data, 0, gfxWorld->draw.vertexDataSize1); + + int indexCount = projInfo->gfxInfo.indexCount; + _ASSERT(indexCount % 3 == 0); + gfxWorld->draw.indexCount = indexCount; + gfxWorld->draw.indices = new uint16_t[indexCount]; + for (int i = 0; i < indexCount; i += 3) + { + // the editor orders their vertices opposite to bo2, so its converted here + gfxWorld->draw.indices[i + 2] = projInfo->gfxInfo.indices[i + 0]; + gfxWorld->draw.indices[i + 1] = projInfo->gfxInfo.indices[i + 1]; + gfxWorld->draw.indices[i + 0] = projInfo->gfxInfo.indices[i + 2]; + } + + return true; + } + + struct s_sortedSurf + { + int surfaceIndex; + int vertexCount; + }; + + bool compareSurfaces(s_sortedSurf& surf0, s_sortedSurf& surf1) + { + return surf0.vertexCount > surf1.vertexCount; + } + + void overwriteMapSurfaces(customMapInfo* projInfo, GfxWorld* gfxWorld) + { + bool overwriteResult = overwriteDrawData(projInfo, gfxWorld); + if (!overwriteResult) + return; + + unsigned int surfaceCount = projInfo->gfxInfo.surfaceCount; + gfxWorld->surfaceCount = surfaceCount; + gfxWorld->dpvs.staticSurfaceCount = surfaceCount; + gfxWorld->dpvs.surfaces = new GfxSurface[surfaceCount]; + for (int i = 0; i < surfaceCount; i++) + { + auto currSurface = &gfxWorld->dpvs.surfaces[i]; + auto objSurface = &projInfo->gfxInfo.surfaces[i]; + + currSurface->lightmapIndex = objSurface->lightmapIndex; + currSurface->primaryLightIndex = objSurface->primaryLightIndex; + currSurface->reflectionProbeIndex = objSurface->reflectionProbeIndex; + currSurface->flags = objSurface->flags; + + currSurface->tris.triCount = objSurface->triCount; + currSurface->tris.baseIndex = objSurface->firstIndex_Index; + + currSurface->tris.vertexDataOffset0 = objSurface->firstVertexIndex * sizeof(GfxPackedWorldVertex); + currSurface->tris.vertexDataOffset1 = 0; + + auto* assetInfo = m_context.LoadDependency(objSurface->materialName); + 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(defaultMaterialName); + _ASSERT(assetInfo != NULL); + } + currSurface->material = assetInfo->Asset(); + + GfxPackedWorldVertex* firstVert = (GfxPackedWorldVertex*)&gfxWorld->draw.vd0.data[currSurface->tris.vertexDataOffset0]; + currSurface->bounds[0].x = firstVert[0].xyz.x; + currSurface->bounds[0].y = firstVert[0].xyz.y; + currSurface->bounds[0].z = firstVert[0].xyz.z; + currSurface->bounds[1].x = firstVert[0].xyz.x; + currSurface->bounds[1].y = firstVert[0].xyz.y; + currSurface->bounds[1].z = firstVert[0].xyz.z; + for (int k = 0; k < currSurface->tris.triCount * 3; k++) + { + CMUtil::calcNewBoundsWithPoint(&firstVert[k].xyz, &currSurface->bounds[0], &currSurface->bounds[1]); + } + + // unused values + currSurface->tris.mins.x = 0.0f; + currSurface->tris.mins.y = 0.0f; + currSurface->tris.mins.z = 0.0f; + currSurface->tris.maxs.x = 0.0f; + currSurface->tris.maxs.y = 0.0f; + currSurface->tris.maxs.z = 0.0f; + currSurface->tris.himipRadiusInvSq = 0.0f; + currSurface->tris.vertexCount = 0; + currSurface->tris.firstVertex = 0; + } + + // doesn't seem to matter what order the sorted surfs go in + gfxWorld->dpvs.sortedSurfIndex = new uint16_t[surfaceCount]; + for (int i = 0; i < surfaceCount; i++) + { + gfxWorld->dpvs.sortedSurfIndex[i] = i; + } + + gfxWorld->dpvs.surfaceMaterials = new GfxDrawSurf_align4[surfaceCount]; + memset(gfxWorld->dpvs.surfaceMaterials, 0, sizeof(GfxDrawSurf_align4) * surfaceCount); + + // all visdata is alligned by 128 + gfxWorld->dpvs.surfaceVisDataCount = CMUtil::allignBy128(surfaceCount); + gfxWorld->dpvs.surfaceVisData[0] = new char[surfaceCount]; + gfxWorld->dpvs.surfaceVisData[1] = new char[surfaceCount]; + gfxWorld->dpvs.surfaceVisData[2] = new char[surfaceCount]; + gfxWorld->dpvs.surfaceVisDataCameraSaved = new char[surfaceCount]; + gfxWorld->dpvs.surfaceCastsShadow = new char[surfaceCount]; + gfxWorld->dpvs.surfaceCastsSunShadow = new char[surfaceCount]; + memset(gfxWorld->dpvs.surfaceVisData[0], 0, surfaceCount); + memset(gfxWorld->dpvs.surfaceVisData[1], 0, surfaceCount); + memset(gfxWorld->dpvs.surfaceVisData[2], 0, surfaceCount); + memset(gfxWorld->dpvs.surfaceVisDataCameraSaved, 0, surfaceCount); + memset(gfxWorld->dpvs.surfaceCastsShadow, 0, surfaceCount); + memset(gfxWorld->dpvs.surfaceCastsSunShadow, 0, surfaceCount); + + gfxWorld->dpvs.litSurfsBegin = 0; + gfxWorld->dpvs.litSurfsEnd = surfaceCount; + gfxWorld->dpvs.emissiveOpaqueSurfsBegin = surfaceCount; + gfxWorld->dpvs.emissiveOpaqueSurfsEnd = surfaceCount; + gfxWorld->dpvs.emissiveTransSurfsBegin = surfaceCount; + gfxWorld->dpvs.emissiveTransSurfsEnd = surfaceCount; + gfxWorld->dpvs.litTransSurfsBegin = surfaceCount; + gfxWorld->dpvs.litTransSurfsEnd = surfaceCount; + } + +#define SMODEL_FLAG_NO_SHADOW 1 +#define SMODEL_FLAG_IS_LIT 2 + + void overwriteMapSModels(GfxWorld* gfxWorld) + { + unsigned int modelCount = 0; + gfxWorld->dpvs.smodelCount = modelCount; + + gfxWorld->dpvs.smodelInsts = new GfxStaticModelInst[modelCount]; + gfxWorld->dpvs.smodelDrawInsts = new GfxStaticModelDrawInst[modelCount]; + /* + for (unsigned int i = 0; i < modelCount; i++) + { + auto currModel = &gfxWorld->dpvs.smodelDrawInsts[i]; + auto currModelInst = &gfxWorld->dpvs.smodelInsts[i]; + json currModelJs = js["models"][i]; + + // TODO: load custom xmodels + std::string modelName = currModelJs["model"]; + auto xModelAsset = findAsset(assetPool, ASSET_TYPE_XMODEL, modelName); + if (xModelAsset == NULL) + { + printf("Custom model (%s) not supported!\n", modelName.c_str()); + hasLinkFailed = true; + return; + } + currModel->model = (XModel*)xModelAsset->m_ptr; + + currModel->placement.origin.x = currModelJs["origin"]["x"]; + currModel->placement.origin.y = currModelJs["origin"]["y"]; + currModel->placement.origin.z = currModelJs["origin"]["z"]; + currModel->placement.origin = CMUtil::convertToBO2Coords(currModel->placement.origin); + currModel->placement.axis[0].x = currModelJs["forward"]["x"]; + currModel->placement.axis[0].y = currModelJs["forward"]["y"]; + currModel->placement.axis[0].z = currModelJs["forward"]["z"]; + 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 + // TODO: mins and maxs are slightly different to what bo2 uses, which results in some wierd culling ingame + currModelInst->mins.x = currModel->model->mins.x + currModel->placement.origin.x; + currModelInst->mins.y = currModel->model->mins.y + currModel->placement.origin.y; + currModelInst->mins.z = currModel->model->mins.z + currModel->placement.origin.z; + currModelInst->maxs.x = currModel->model->maxs.x + currModel->placement.origin.x; + currModelInst->maxs.y = currModel->model->maxs.y + currModel->placement.origin.y; + currModelInst->maxs.z = currModel->model->maxs.z + currModel->placement.origin.z; + + currModel->cullDist = currModelJs["cullDist"]; + currModel->flags = currModelJs["flags"]; + currModel->primaryLightIndex = (unsigned char)currModelJs["primaryLightIndex"]; + currModel->reflectionProbeIndex = (unsigned char)currModelJs["reflectionProbeIndex"]; + + // unknown use / unused + currModel->smid = i; + memset(&currModel->lightingSH, 0, sizeof(GfxLightingSHQuantized)); + currModel->invScaleSq = 0.0f; + currModel->lightingHandle = 0; + currModel->colorsIndex = 0; + currModel->visibility = 0; + + // setting these to NULL makes any static/baked lighting go black when not rendered by real-time lighting or in a shadow + // TODO: calculate lighting and store it here + currModel->lmapVertexInfo[0].numLmapVertexColors = 0; + currModel->lmapVertexInfo[0].lmapVertexColors = NULL; + currModel->lmapVertexInfo[1].numLmapVertexColors = 0; + currModel->lmapVertexInfo[1].lmapVertexColors = NULL; + currModel->lmapVertexInfo[2].numLmapVertexColors = 0; + currModel->lmapVertexInfo[2].lmapVertexColors = NULL; + currModel->lmapVertexInfo[3].numLmapVertexColors = 0; + currModel->lmapVertexInfo[3].lmapVertexColors = NULL; + } + */ + + // all visdata is alligned by 128 + gfxWorld->dpvs.smodelVisDataCount = CMUtil::allignBy128(modelCount); + gfxWorld->dpvs.smodelVisData[0] = new char[modelCount]; + gfxWorld->dpvs.smodelVisData[1] = new char[modelCount]; + gfxWorld->dpvs.smodelVisData[2] = new char[modelCount]; + gfxWorld->dpvs.smodelVisDataCameraSaved = new char[modelCount]; + gfxWorld->dpvs.smodelCastsShadow = new char[modelCount]; + for (unsigned int i = 0; i < modelCount; i++) + { + if ((gfxWorld->dpvs.smodelDrawInsts[i].flags & SMODEL_FLAG_NO_SHADOW) == 0) + gfxWorld->dpvs.smodelCastsShadow[i] = 1; + else + gfxWorld->dpvs.smodelCastsShadow[i] = 0; + } + memset(gfxWorld->dpvs.smodelVisData[0], 0, modelCount); + memset(gfxWorld->dpvs.smodelVisData[1], 0, modelCount); + memset(gfxWorld->dpvs.smodelVisData[2], 0, modelCount); + memset(gfxWorld->dpvs.smodelVisDataCameraSaved, 0, modelCount); + + gfxWorld->dpvs.usageCount = 0; + } + + void cleanGfxWorld(GfxWorld* gfxWorld) + { + gfxWorld->checksum = 0; + + // Remove Coronas + gfxWorld->coronaCount = 0; + gfxWorld->coronas = NULL; + + // Remove exposure volumes + gfxWorld->exposureVolumeCount = 0; + gfxWorld->exposureVolumes = NULL; + gfxWorld->exposureVolumePlaneCount = 0; + gfxWorld->exposureVolumePlanes = NULL; + + // Remove hero lights + gfxWorld->heroLightCount = 0; + gfxWorld->heroLights = NULL; + gfxWorld->heroLightTreeCount = 0; + gfxWorld->heroLightTree = NULL; + + // remove LUT data + gfxWorld->lutVolumeCount = 0; + gfxWorld->lutVolumes = NULL; + gfxWorld->lutVolumePlaneCount = 0; + gfxWorld->lutVolumePlanes = NULL; + + // remove occluders + gfxWorld->numOccluders = 0; + gfxWorld->occluders = NULL; + + // remove Siege Skins + gfxWorld->numSiegeSkinInsts = 0; + gfxWorld->siegeSkinInsts = NULL; + + // remove outdoor bounds + gfxWorld->numOutdoorBounds = 0; + gfxWorld->outdoorBounds = NULL; + + // remove materials + gfxWorld->ropeMaterial = NULL; + gfxWorld->lutMaterial = NULL; + gfxWorld->waterMaterial = NULL; + gfxWorld->coronaMaterial = NULL; + + // remove shadow maps + gfxWorld->shadowMapVolumeCount = 0; + gfxWorld->shadowMapVolumes = NULL; + gfxWorld->shadowMapVolumePlaneCount = 0; + gfxWorld->shadowMapVolumePlanes = NULL; + + // remove stream info + gfxWorld->streamInfo.aabbTreeCount = 0; + gfxWorld->streamInfo.aabbTrees = NULL; + gfxWorld->streamInfo.leafRefCount = 0; + gfxWorld->streamInfo.leafRefs = NULL; + + // remove sun data + memset(&gfxWorld->sun, 0, sizeof(sunflare_t)); + gfxWorld->sun.hasValidData = false; + + // Remove Water + gfxWorld->waterDirection = 0.0f; + gfxWorld->waterBuffers[0].buffer = NULL; + gfxWorld->waterBuffers[0].bufferSize = NULL; + gfxWorld->waterBuffers[1].buffer = NULL; + gfxWorld->waterBuffers[1].bufferSize = NULL; + + // Remove Fog + gfxWorld->worldFogModifierVolumeCount = 0; + gfxWorld->worldFogModifierVolumes = NULL; + gfxWorld->worldFogModifierVolumePlaneCount = 0; + gfxWorld->worldFogModifierVolumePlanes = NULL; + gfxWorld->worldFogVolumeCount = 0; + gfxWorld->worldFogVolumes = NULL; + gfxWorld->worldFogVolumePlaneCount = 0; + gfxWorld->worldFogVolumePlanes = NULL; + + // materialMemory is unused + gfxWorld->materialMemoryCount = 0; + gfxWorld->materialMemory = NULL; + + // sunLight is overwritten by the game, just needs to be a valid pointer + gfxWorld->sunLight = new GfxLight; + memset(gfxWorld->sunLight, 0, sizeof(GfxLight)); + } + + void overwriteGfxLights(GfxWorld* gfxWorld) + { + // there must be 2 or more lights, first is the default light and second is the sun + gfxWorld->primaryLightCount = 2; + gfxWorld->sunPrimaryLightIndex = 1; // the sun is always index 1 + + gfxWorld->shadowGeom = new GfxShadowGeometry[gfxWorld->primaryLightCount]; + for (int i = 0; i < gfxWorld->primaryLightCount; i++) + { + gfxWorld->shadowGeom[i].smodelCount = 0; + gfxWorld->shadowGeom[i].surfaceCount = 0; + gfxWorld->shadowGeom[i].smodelIndex = NULL; + gfxWorld->shadowGeom[i].sortedSurfIndex = NULL; + } + + gfxWorld->lightRegion = new GfxLightRegion[gfxWorld->primaryLightCount]; + for (int i = 0; i < gfxWorld->primaryLightCount; i++) + { + gfxWorld->lightRegion[i].hullCount = 0; + gfxWorld->lightRegion[i].hulls = NULL; + } + + int lightEntShadowVisSize = (gfxWorld->primaryLightCount - gfxWorld->sunPrimaryLightIndex - 1) * 8192; + if (lightEntShadowVisSize != 0) + { + gfxWorld->primaryLightEntityShadowVis = new unsigned int[lightEntShadowVisSize]; + memset(gfxWorld->primaryLightEntityShadowVis, 0, lightEntShadowVisSize * sizeof(unsigned int)); + } + else + { + gfxWorld->primaryLightEntityShadowVis = NULL; + } + } + + void overwriteLightGrid(GfxWorld* gfxWorld) + { + gfxWorld->lightGrid.mins[0] = 0; + gfxWorld->lightGrid.mins[1] = 0; + gfxWorld->lightGrid.mins[2] = 0; + gfxWorld->lightGrid.maxs[0] = 0; + gfxWorld->lightGrid.maxs[1] = 0; + gfxWorld->lightGrid.maxs[2] = 0; + + gfxWorld->lightGrid.rowAxis = 0; // default value + gfxWorld->lightGrid.colAxis = 1; // default value + gfxWorld->lightGrid.sunPrimaryLightIndex = 1; // the sun is always index 1 + gfxWorld->lightGrid.offset = 0.0f; + + // if rowDataStart's first value is -1, it will not look up any lightmap data + gfxWorld->lightGrid.rowDataStart = new uint16_t[1]; + *gfxWorld->lightGrid.rowDataStart = (uint16_t)(-1); + + gfxWorld->lightGrid.rawRowDataSize = 0; + gfxWorld->lightGrid.rawRowData = NULL; + + gfxWorld->lightGrid.colorCount = 0; + gfxWorld->lightGrid.colors = NULL; + + gfxWorld->lightGrid.coeffCount = 0; + gfxWorld->lightGrid.coeffs = NULL; + + gfxWorld->lightGrid.entryCount = 0; + gfxWorld->lightGrid.entries = NULL; + + gfxWorld->lightGrid.skyGridVolumeCount = 0; + gfxWorld->lightGrid.skyGridVolumes = NULL; + } + + void updateGfxCells(GfxWorld* gfxWorld) + { + // Cells are basically data used to determine what can be seen and what cant be seen + // Right now custom maps have no optimisation so there is only 1 cell + int cellCount = 1; + + gfxWorld->cellBitsCount = ((cellCount + 127) >> 3) & 0x1FFFFFF0; + + int cellCasterBitsCount = cellCount * ((cellCount + 31) / 32); + gfxWorld->cellCasterBits = new unsigned int[cellCasterBitsCount]; + memset(gfxWorld->cellCasterBits, 0x00, cellCasterBitsCount * sizeof(unsigned int)); + + gfxWorld->cells = new GfxCell[cellCount]; + gfxWorld->cells[0].portalCount = 0; + gfxWorld->cells[0].portals = NULL; + gfxWorld->cells[0].reflectionProbeCount = 0; + gfxWorld->cells[0].reflectionProbes = NULL; + gfxWorld->cells[0].mins.x = gfxWorld->mins.x; + gfxWorld->cells[0].mins.y = gfxWorld->mins.y; + gfxWorld->cells[0].mins.z = gfxWorld->mins.z; + gfxWorld->cells[0].maxs.x = gfxWorld->maxs.x; + gfxWorld->cells[0].maxs.y = gfxWorld->maxs.y; + gfxWorld->cells[0].maxs.z = gfxWorld->maxs.z; + + // AABB trees are used to detect what should be rendered and what shouldn't + // Just use the first AABB node to hold all models, no optimisation but all models/surfaces wil lbe drawn + gfxWorld->cells[0].aabbTreeCount = 1; + gfxWorld->cells[0].aabbTree = new GfxAabbTree[gfxWorld->cells[0].aabbTreeCount]; + gfxWorld->cells[0].aabbTree[0].childCount = 0; + gfxWorld->cells[0].aabbTree[0].childrenOffset = 0; + gfxWorld->cells[0].aabbTree[0].startSurfIndex = 0; + gfxWorld->cells[0].aabbTree[0].surfaceCount = gfxWorld->surfaceCount; + gfxWorld->cells[0].aabbTree[0].smodelIndexCount = gfxWorld->dpvs.smodelCount; + gfxWorld->cells[0].aabbTree[0].smodelIndexes = new unsigned short[gfxWorld->dpvs.smodelCount]; + for (unsigned short i = 0; i < gfxWorld->dpvs.smodelCount; i++) + { + gfxWorld->cells[0].aabbTree[0].smodelIndexes[i] = i; + } + gfxWorld->cells[0].aabbTree[0].mins.x = gfxWorld->mins.x; + gfxWorld->cells[0].aabbTree[0].mins.y = gfxWorld->mins.y; + gfxWorld->cells[0].aabbTree[0].mins.z = gfxWorld->mins.z; + gfxWorld->cells[0].aabbTree[0].maxs.x = gfxWorld->maxs.x; + gfxWorld->cells[0].aabbTree[0].maxs.y = gfxWorld->maxs.y; + gfxWorld->cells[0].aabbTree[0].maxs.z = gfxWorld->maxs.z; + + gfxWorld->dpvsPlanes.cellCount = cellCount; + + int sceneEntCellBitsCount = cellCount * 512; + gfxWorld->dpvsPlanes.sceneEntCellBits = new unsigned int[sceneEntCellBitsCount]; + memset(gfxWorld->dpvsPlanes.sceneEntCellBits, 0x00, sceneEntCellBitsCount * sizeof(unsigned int)); + + // nodes have the struct mnode_t, and there must be at least 1 node + // Nodes mnode_t.cellIndex indexes gfxWorld->cells + // and (mnode_t.cellIndex - (world->dpvsPlanes.cellCount + 1) indexes world->dpvsPlanes.planes + gfxWorld->nodeCount = 1; + gfxWorld->planeCount = 0; + gfxWorld->dpvsPlanes.nodes = new uint16_t[gfxWorld->nodeCount]; + gfxWorld->dpvsPlanes.nodes[0] = 1; // nodes reference cells by index + 1 + gfxWorld->dpvsPlanes.planes = NULL; + } + + void updateWorldBounds(GfxWorld* gfxWorld) + { + gfxWorld->mins.x = 0.0f; + gfxWorld->mins.y = 0.0f; + gfxWorld->mins.z = 0.0f; + gfxWorld->maxs.x = 0.0f; + gfxWorld->maxs.y = 0.0f; + gfxWorld->maxs.z = 0.0f; + + for (int i = 0; i < gfxWorld->surfaceCount; i++) + { + CMUtil::calcNewBounds(&gfxWorld->dpvs.surfaces[i].bounds[0], &gfxWorld->dpvs.surfaces[i].bounds[1], &gfxWorld->mins, &gfxWorld->maxs); + } + } + + void overwriteModels(GfxWorld* gfxWorld) + { + gfxWorld->modelCount = 1; + gfxWorld->models = new GfxBrushModel[gfxWorld->modelCount]; + + // first model is the world model, the world json doesn't include it so its added here + gfxWorld->models[0].startSurfIndex = 0; + gfxWorld->models[0].surfaceCount = gfxWorld->surfaceCount; + gfxWorld->models[0].bounds[0].x = gfxWorld->mins.x; + gfxWorld->models[0].bounds[0].y = gfxWorld->mins.y; + gfxWorld->models[0].bounds[0].z = gfxWorld->mins.z; + gfxWorld->models[0].bounds[1].x = gfxWorld->maxs.x; + gfxWorld->models[0].bounds[1].y = gfxWorld->maxs.y; + gfxWorld->models[0].bounds[1].z = gfxWorld->maxs.z; + memset(&gfxWorld->models[0].writable, 0, sizeof(GfxBrushModelWritable)); + + // for (int i = 1; i < gfxWorld->modelCount; i++) + //{ + // auto currEntModel = &gfxWorld->models[i]; + // json currEntModelJs = js["entityModels"][i - 1]; + // + // currEntModel->bounds[0].x = currEntModelJs["mins"]["x"]; + // currEntModel->bounds[0].y = currEntModelJs["mins"]["y"]; + // currEntModel->bounds[0].z = currEntModelJs["mins"]["z"]; + // currEntModel->bounds[1].x = currEntModelJs["maxs"]["x"]; + // currEntModel->bounds[1].y = currEntModelJs["maxs"]["y"]; + // currEntModel->bounds[1].z = currEntModelJs["maxs"]["z"]; + // + // currEntModel->surfaceCount = 0; + // currEntModel->startSurfIndex = (unsigned int)(-1); + // memset(&currEntModel->writable, 0, sizeof(GfxBrushModelWritable)); + // } + } + + void updateSunData(GfxWorld* gfxWorld) + { + // default values taken from mp_dig + gfxWorld->sunParse.fogTransitionTime = 0.001; + gfxWorld->sunParse.name[0] = 0x00; + + gfxWorld->sunParse.initWorldSun->control = 0; + gfxWorld->sunParse.initWorldSun->exposure = 2.5f; + gfxWorld->sunParse.initWorldSun->angles.x = -29.0f; + gfxWorld->sunParse.initWorldSun->angles.y = 254.0f; + gfxWorld->sunParse.initWorldSun->angles.z = 0.0f; + gfxWorld->sunParse.initWorldSun->sunCd.x = 1.0f; + gfxWorld->sunParse.initWorldSun->sunCd.y = 0.89f; + gfxWorld->sunParse.initWorldSun->sunCd.z = 0.69f; + gfxWorld->sunParse.initWorldSun->sunCd.w = 13.5f; + gfxWorld->sunParse.initWorldSun->ambientColor.x = 0.0f; + gfxWorld->sunParse.initWorldSun->ambientColor.y = 0.0f; + gfxWorld->sunParse.initWorldSun->ambientColor.z = 0.0f; + gfxWorld->sunParse.initWorldSun->ambientColor.w = 0.0f; + gfxWorld->sunParse.initWorldSun->skyColor.x = 0.0f; + gfxWorld->sunParse.initWorldSun->skyColor.y = 0.0f; + gfxWorld->sunParse.initWorldSun->skyColor.z = 0.0f; + gfxWorld->sunParse.initWorldSun->skyColor.w = 0.0f; + gfxWorld->sunParse.initWorldSun->sunCs.x = 0.0f; + gfxWorld->sunParse.initWorldSun->sunCs.y = 0.0f; + gfxWorld->sunParse.initWorldSun->sunCs.z = 0.0f; + gfxWorld->sunParse.initWorldSun->sunCs.w = 0.0f; + + gfxWorld->sunParse.initWorldFog->baseDist = 150.0f; + gfxWorld->sunParse.initWorldFog->baseHeight = -100.0f; + gfxWorld->sunParse.initWorldFog->fogColor.x = 2.35f; + gfxWorld->sunParse.initWorldFog->fogColor.y = 3.10f; + gfxWorld->sunParse.initWorldFog->fogColor.z = 3.84f; + gfxWorld->sunParse.initWorldFog->fogOpacity = 0.52f; + gfxWorld->sunParse.initWorldFog->halfDist = 4450.f; + gfxWorld->sunParse.initWorldFog->halfHeight = 2000.f; + gfxWorld->sunParse.initWorldFog->sunFogColor.x = 5.27f; + gfxWorld->sunParse.initWorldFog->sunFogColor.y = 4.73f; + gfxWorld->sunParse.initWorldFog->sunFogColor.z = 3.88f; + gfxWorld->sunParse.initWorldFog->sunFogInner = 0.0f; + gfxWorld->sunParse.initWorldFog->sunFogOpacity = 0.67f; + gfxWorld->sunParse.initWorldFog->sunFogOuter = 80.84f; + gfxWorld->sunParse.initWorldFog->sunFogPitch = -29.0f; + gfxWorld->sunParse.initWorldFog->sunFogYaw = 254.0f; + } + + void updateReflectionProbeData(GfxWorld* gfxWorld) + { + gfxWorld->draw.reflectionProbeCount = 1; + + gfxWorld->draw.reflectionProbeTextures = new GfxTexture[gfxWorld->draw.reflectionProbeCount]; + memset(gfxWorld->draw.reflectionProbeTextures, 0, sizeof(GfxTexture) * gfxWorld->draw.reflectionProbeCount); + + gfxWorld->draw.reflectionProbes = new GfxReflectionProbe[gfxWorld->draw.reflectionProbeCount]; + + // default values taken from mp_dig + gfxWorld->draw.reflectionProbes[0].mipLodBias = -8.0; + + gfxWorld->draw.reflectionProbes[0].origin.x = 0.0f; + gfxWorld->draw.reflectionProbes[0].origin.y = 0.0f; + gfxWorld->draw.reflectionProbes[0].origin.z = 0.0f; + + gfxWorld->draw.reflectionProbes[0].lightingSH.V0.x = 0.0f; + gfxWorld->draw.reflectionProbes[0].lightingSH.V0.y = 0.0f; + gfxWorld->draw.reflectionProbes[0].lightingSH.V0.z = 0.0f; + gfxWorld->draw.reflectionProbes[0].lightingSH.V0.w = 0.0f; + gfxWorld->draw.reflectionProbes[0].lightingSH.V1.x = 0.0f; + gfxWorld->draw.reflectionProbes[0].lightingSH.V1.y = 0.0f; + gfxWorld->draw.reflectionProbes[0].lightingSH.V1.z = 0.0f; + gfxWorld->draw.reflectionProbes[0].lightingSH.V1.w = 0.0f; + gfxWorld->draw.reflectionProbes[0].lightingSH.V2.x = 0.0f; + gfxWorld->draw.reflectionProbes[0].lightingSH.V2.y = 0.0f; + gfxWorld->draw.reflectionProbes[0].lightingSH.V2.z = 0.0f; + gfxWorld->draw.reflectionProbes[0].lightingSH.V2.w = 0.0f; + + gfxWorld->draw.reflectionProbes[0].probeVolumeCount = 0; + gfxWorld->draw.reflectionProbes[0].probeVolumes = NULL; + + std::string probeImageName = "*reflection_probe0"; + auto probeImageAsset = m_context.LoadDependency(probeImageName); + if (probeImageAsset == NULL) + { + printf("ERROR! unable to find image %s!\n", probeImageName.c_str()); + hasLinkFailed = true; + return; + } + gfxWorld->draw.reflectionProbes[0].reflectionImage = probeImageAsset->Asset(); + } + + void overwriteLightmapData(GfxWorld* gfxWorld) + { + gfxWorld->draw.lightmapCount = 1; + + gfxWorld->draw.lightmapPrimaryTextures = new GfxTexture[gfxWorld->draw.lightmapCount]; + gfxWorld->draw.lightmapSecondaryTextures = new GfxTexture[gfxWorld->draw.lightmapCount]; + gfxWorld->draw.lightmaps = new GfxLightmapArray[gfxWorld->draw.lightmapCount]; + + // always set to 0 + memset(gfxWorld->draw.lightmapPrimaryTextures, 0, sizeof(GfxTexture) * gfxWorld->draw.lightmapCount); + memset(gfxWorld->draw.lightmapSecondaryTextures, 0, sizeof(GfxTexture) * gfxWorld->draw.lightmapCount); + + std::string secondaryTexture = "*lightmap0_secondary"; + auto secondaryTextureAsset = m_context.LoadDependency(secondaryTexture); + if (secondaryTextureAsset == NULL) + { + printf("ERROR! unable to find lightmap image %s!\n", secondaryTexture.c_str()); + hasLinkFailed = true; + return; + } + gfxWorld->draw.lightmaps[0].primary = NULL; // always NULL + gfxWorld->draw.lightmaps[0].secondary = secondaryTextureAsset->Asset(); + } + + void overwriteSkyBox(GfxWorld* gfxWorld) + { + const char* skyBoxName = "skybox_mp_dig"; + gfxWorld->skyBoxModel = skyBoxName; + + if (m_context.LoadDependency(skyBoxName) == NULL) + { + printf("WARN: Unable to find the skybox model %s\n", skyBoxName); + } + + // default skybox values from mp_dig + gfxWorld->skyDynIntensity.angle0 = 0.0f; + gfxWorld->skyDynIntensity.angle1 = 0.0f; + gfxWorld->skyDynIntensity.factor0 = 1.0f; + gfxWorld->skyDynIntensity.factor1 = 1.0f; + } + + void updateDynEntData(GfxWorld* gfxWorld) + { + gfxWorld->dpvsDyn.dynEntClientCount[0] = DYN_ENT_COUNT + 256; + gfxWorld->dpvsDyn.dynEntClientCount[1] = 0; + gfxWorld->dpvsDyn.dynEntClientWordCount[0] = 1; // needs to be at least 1 + gfxWorld->dpvsDyn.dynEntClientWordCount[1] = 0; + gfxWorld->dpvsDyn.usageCount = 0; + + int dynEntCellBitsSize = gfxWorld->dpvsDyn.dynEntClientWordCount[0] * gfxWorld->dpvsPlanes.cellCount; + gfxWorld->dpvsDyn.dynEntCellBits[0] = new unsigned int[dynEntCellBitsSize]; + gfxWorld->dpvsDyn.dynEntCellBits[1] = NULL; + memset(gfxWorld->dpvsDyn.dynEntCellBits[0], 0, sizeof(unsigned int) * dynEntCellBitsSize); + + int dynEntVisData0Size = gfxWorld->dpvsDyn.dynEntClientWordCount[0] * 32; + gfxWorld->dpvsDyn.dynEntVisData[0][0] = new char[dynEntVisData0Size]; + gfxWorld->dpvsDyn.dynEntVisData[0][1] = new char[dynEntVisData0Size]; + gfxWorld->dpvsDyn.dynEntVisData[0][2] = new char[dynEntVisData0Size]; + gfxWorld->dpvsDyn.dynEntVisData[1][0] = NULL; + gfxWorld->dpvsDyn.dynEntVisData[1][1] = NULL; + gfxWorld->dpvsDyn.dynEntVisData[1][2] = NULL; + memset(gfxWorld->dpvsDyn.dynEntVisData[0][0], 0, dynEntVisData0Size); + memset(gfxWorld->dpvsDyn.dynEntVisData[0][1], 0, dynEntVisData0Size); + memset(gfxWorld->dpvsDyn.dynEntVisData[0][2], 0, dynEntVisData0Size); + + int dynEntShadowVisCount = gfxWorld->dpvsDyn.dynEntClientCount[0] * (gfxWorld->primaryLightCount - gfxWorld->sunPrimaryLightIndex - 1); + gfxWorld->primaryLightDynEntShadowVis[0] = new unsigned int[dynEntShadowVisCount]; + gfxWorld->primaryLightDynEntShadowVis[1] = NULL; + memset(gfxWorld->primaryLightDynEntShadowVis[0], 0, sizeof(unsigned int) * dynEntShadowVisCount); + + gfxWorld->sceneDynModel = new GfxSceneDynModel[gfxWorld->dpvsDyn.dynEntClientCount[0]]; + gfxWorld->sceneDynBrush = NULL; + memset(gfxWorld->sceneDynModel, 0, sizeof(GfxSceneDynModel) * gfxWorld->dpvsDyn.dynEntClientCount[0]); + } + + void updateOutdoors(GfxWorld* gfxWorld) + { + float xRecip = 1.0f / (gfxWorld->maxs.x - gfxWorld->mins.x); + float xScale = -(xRecip * gfxWorld->mins.x); + + float yRecip = 1.0f / (gfxWorld->maxs.y - gfxWorld->mins.y); + float yScale = -(yRecip * gfxWorld->mins.y); + + float zRecip = 1.0f / (gfxWorld->maxs.z - gfxWorld->mins.z); + float zScale = -(zRecip * gfxWorld->mins.z); + + memset(gfxWorld->outdoorLookupMatrix, 0, sizeof(gfxWorld->outdoorLookupMatrix)); + + gfxWorld->outdoorLookupMatrix[0].x = xRecip; + gfxWorld->outdoorLookupMatrix[1].y = yRecip; + gfxWorld->outdoorLookupMatrix[2].z = zRecip; + gfxWorld->outdoorLookupMatrix[3].x = xScale; + gfxWorld->outdoorLookupMatrix[3].y = yScale; + gfxWorld->outdoorLookupMatrix[3].z = zScale; + gfxWorld->outdoorLookupMatrix[3].w = 1.0f; + + std::string outdoorImageName = std::string("$outdoor"); + auto outdoorImageAsset = m_context.LoadDependency(outdoorImageName); + if (outdoorImageAsset == NULL) + { + printf("ERROR! unable to find image $outdoor, this will crash your game!\n"); + hasLinkFailed = true; + return; + } + gfxWorld->outdoorImage = outdoorImageAsset->Asset(); + } + + void createGfxWorld(customMapInfo* projInfo) + { + GfxWorld* gfxWorld = new GfxWorld; + gfxWorld->baseName = _strdup(projInfo->name.c_str()); + gfxWorld->name = _strdup(projInfo->bspName.c_str()); + + // Default values taken from mp_dig + gfxWorld->lightingFlags = 0; + gfxWorld->lightingQuality = 4096; + + cleanGfxWorld(gfxWorld); + + overwriteMapSurfaces(projInfo, gfxWorld); + + overwriteMapSModels(gfxWorld); + + overwriteLightmapData(gfxWorld); + + overwriteSkyBox(gfxWorld); + + updateReflectionProbeData(gfxWorld); + + // world bounds are based on surface mins/maxs + // Other update functions depend on the bounds being set first + updateWorldBounds(gfxWorld); + + updateOutdoors(gfxWorld); + + // gfx cells depend on surface/smodel count + updateGfxCells(gfxWorld); + + overwriteLightGrid(gfxWorld); + + overwriteGfxLights(gfxWorld); + + overwriteModels(gfxWorld); + + updateSunData(gfxWorld); + + updateDynEntData(gfxWorld); + + m_context.AddAsset(gfxWorld->name, gfxWorld); + } + + void aabbCalcOriginAndHalfSize(vec3_t* mins, vec3_t* maxs, vec3_t* out_origin, vec3_t* out_halfSize) + { + // Origin is the midpoint: (min + max) / 2 + vec3_t temp; + temp.x = mins->x + maxs->x; + temp.y = mins->y + maxs->y; + temp.z = mins->z + maxs->z; + out_origin->x = temp.x * 0.5f; + out_origin->y = temp.y * 0.5f; + out_origin->z = temp.z * 0.5f; + + // Half-size is half the difference: (max - min) / 2 + temp.x = maxs->x - mins->x; + temp.y = maxs->y - mins->y; + temp.z = maxs->z - mins->z; + out_halfSize->x = temp.x * 0.5f; + out_halfSize->y = temp.y * 0.5f; + out_halfSize->z = temp.z * 0.5f; + } + + bool CM_IsEdgeWalkable(clipMap_t* clipMap, int triIndex, int edgeIndex) + { + + unsigned __int8 edgeBitMask = 1 << ((triIndex + edgeIndex + 2 * triIndex) & 7); + + 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) + { + if (node->isLeaf) + { + (*numLeafs)++; + // there won't be an AABB tree when objectList is empty + if (node->u.leaf->getObjectCount() > 0) + { + *numAABBTrees += node->u.leaf->getObjectCount() + 1; + + if (node->u.leaf->getObjectCount() > *maxObjsPerLeaf) + *maxObjsPerLeaf = node->u.leaf->getObjectCount(); + } + } + else + { + (*numPlanes)++; + (*numNodes)++; + traverseBSPTreeForCounts(node->u.node->front, numPlanes, numNodes, numLeafs, numAABBTrees, maxObjsPerLeaf); + traverseBSPTreeForCounts(node->u.node->back, numPlanes, numNodes, numLeafs, numAABBTrees, maxObjsPerLeaf); + } + } + + vec3_t normalX = {1.0f, 0.0f, 0.0f}; + vec3_t normalY = {0.0f, 1.0f, 0.0f}; + vec3_t normalZ = {0.0f, 0.0f, 1.0f}; + + int currPlaneCount = 0; + int currNodeCount = 0; + int currLeafCount = 0; + int currAABBCount = 0; + + int addAABBTreeFromLeaf(BSPTree* node, clipMap_t* clipMap) + { + _ASSERT(node->isLeaf); + + int objectCount = node->u.leaf->getObjectCount(); + int firstAABBIndex = currAABBCount; + currAABBCount += objectCount + 1; + + // calculate root AABB node mins and maxs + // cannot convert mins and maxs coord to BO2 directly as this will result in incorrect mins and maxs + // so we have to recompute every min and max, not hard just tedious + int firstPartitionIndex = node->u.leaf->getObject(0)->partitionIndex; + auto firstPartition = &clipMap->partitions[firstPartitionIndex]; + uint16_t* firstTri = clipMap->triIndices[firstPartition->firstTri]; + vec3_t* firstVert = &clipMap->verts[firstTri[0]]; + vec3_t aabbMins; + vec3_t aabbMaxs; + aabbMins.x = firstVert->x; + aabbMins.y = firstVert->y; + aabbMins.z = firstVert->z; + aabbMaxs.x = firstVert->x; + aabbMaxs.y = firstVert->y; + aabbMaxs.z = firstVert->z; + for (int i = 0; i < objectCount; i++) + { + int currPartitionIndex = node->u.leaf->getObject(i)->partitionIndex; + auto currPartition = &clipMap->partitions[currPartitionIndex]; + + for (int k = 0; k < currPartition->triCount; k++) + { + uint16_t* tri = clipMap->triIndices[currPartition->firstTri + k]; + for (int l = 0; l < 3; l++) + { + uint16_t vertIndex = tri[l]; + vec3_t vertCoord = clipMap->verts[vertIndex]; + CMUtil::calcNewBoundsWithPoint(&vertCoord, &aabbMins, &aabbMaxs); + } + } + } + // create root AABB node + CollisionAabbTree* rootAABB = &clipMap->aabbTrees[firstAABBIndex]; + aabbCalcOriginAndHalfSize(&aabbMins, &aabbMaxs, &rootAABB->origin, &rootAABB->halfSize); + rootAABB->materialIndex = 0; + rootAABB->childCount = objectCount; + rootAABB->u.firstChildIndex = firstAABBIndex + 1; + + // populate child AABB nodes + for (int i = 0; i < objectCount; i++) + { + CollisionAabbTree* currAabbTree = &clipMap->aabbTrees[rootAABB->u.firstChildIndex + i]; + int currPartitionIndex = node->u.leaf->getObject(i)->partitionIndex; + + currAabbTree->materialIndex = 0; + currAabbTree->childCount = 0; + currAabbTree->u.partitionIndex = currPartitionIndex; + + // calculate partition origin and half size + CollisionPartition* aabbPartition = &clipMap->partitions[currPartitionIndex]; + uint16_t firstUind = clipMap->info.uinds[aabbPartition->fuind]; + vec3_t* firstVertex = &clipMap->verts[firstUind]; + vec3_t mins; + vec3_t maxs; + mins.x = firstVertex->x; + mins.y = firstVertex->y; + mins.z = firstVertex->z; + maxs.x = firstVertex->x; + maxs.y = firstVertex->y; + maxs.z = firstVertex->z; + for (int i = 1; i < aabbPartition->nuinds; i++) + { + uint16_t currUind = clipMap->info.uinds[aabbPartition->fuind + i]; + vec3_t* currVertex = &clipMap->verts[currUind]; + + CMUtil::calcNewBoundsWithPoint(currVertex, &mins, &maxs); + } + + aabbCalcOriginAndHalfSize(&mins, &maxs, &currAabbTree->origin, &currAabbTree->halfSize); + } + + return firstAABBIndex; + } + + // returns the index corresponding to the BSPTree* node parsed + int16_t populateBSPTree_r(clipMap_t* clipMap, BSPTree* node) + { + new cplane_s; + new cNode_t; + new cLeaf_s; + new CollisionAabbTree; + + if (node->isLeaf) + { + int currLeafIndex = currLeafCount; + currLeafCount++; + cLeaf_s* currLeaf = &clipMap->leafs[currLeafIndex]; + + currLeaf->cluster = 0; + currLeaf->brushContents = 0; // no brushes used so contents is 0 + currLeaf->terrainContents = LEAF_TERRAIN_CONTENTS; // clipMap->cmodels[0].leaf.terrainContents takes prescedence + + // unused when leafBrushNode == 0 + currLeaf->mins.x = 0.0f; + currLeaf->mins.y = 0.0f; + currLeaf->mins.z = 0.0f; + currLeaf->maxs.x = 0.0f; + currLeaf->maxs.y = 0.0f; + currLeaf->maxs.z = 0.0f; + currLeaf->leafBrushNode = 0; + + if (node->u.leaf->getObjectCount() > 0) + { + currLeaf->firstCollAabbIndex = addAABBTreeFromLeaf(node, clipMap); + currLeaf->collAabbCount = 1; + } + else + { + currLeaf->firstCollAabbIndex = 0; + currLeaf->collAabbCount = 0; + } + + return -1 - currLeafIndex; + } + else + { + cplane_s* currPlane = &clipMap->info.planes[currPlaneCount]; + currPlaneCount++; + + if (node->u.node->axis == AXIS_X) + { + // X is unchanged when going from OGL x -> BO2 x + currPlane->normal = normalX; + + // converting OGL -> BO2 X coords doesn't change the x coords at all, so + // the dist stays the same + currPlane->dist = node->u.node->distance; + } + else + { + // converting OGL -> BO2 Z coords negates the z coords and sets it to the y coord. + // convert the z normal to the y normal, but don't negate it. Negative normals don't do + // what is expected when the game uses them + _ASSERT(node->u.node->axis == AXIS_Z); + currPlane->normal = normalY; + + // converting OGL -> BO2 Z coords negates the z coords and sets it to the y coord. + // just negate it here as it is just the distance from the orgin along the axis + currPlane->dist = -node->u.node->distance; + } + + bool foundType = false; + if (currPlane->normal.x == 1.0f) + { + _ASSERT(!foundType); + foundType = true; + currPlane->type = 0; + } + else if (currPlane->normal.y == 1.0f) + { + _ASSERT(!foundType); + foundType = true; + currPlane->type = 1; + } + else if (currPlane->normal.z == 1.0f) + { + _ASSERT(!foundType); + foundType = true; + currPlane->type = 2; + } + else + _ASSERT(foundType); + + currPlane->signbits = 0; + if (currPlane->normal.x < 0.0f) + currPlane->signbits |= 1; + if (currPlane->normal.y < 0.0f) + currPlane->signbits |= 2; + if (currPlane->normal.z < 0.0f) + currPlane->signbits |= 4; + + currPlane->pad[0] = 0; + currPlane->pad[1] = 0; + + int currNodeIndex = currNodeCount; + currNodeCount++; + cNode_t* currNode = &clipMap->nodes[currNodeIndex]; + + currNode->plane = currPlane; + // Reason for the front and back flip (due to the hacky nature of making the mins and maxs work (see createClipMap)): + // after converting between OGL and BO2 coords and when and updating the normal from Z -> Y, + // the normal vector flips and objects behind the plane are now in front, and vise versa + // so the back node now represents the front, and the front node represents the back. + // Do the OGL -> Bo2 coord change on paper and it will make sense + if (currPlane->type == 1) + { + currNode->children[1] = populateBSPTree_r(clipMap, node->u.node->front); + currNode->children[0] = populateBSPTree_r(clipMap, node->u.node->back); + } + else + { + currNode->children[0] = populateBSPTree_r(clipMap, node->u.node->front); + currNode->children[1] = populateBSPTree_r(clipMap, node->u.node->back); + } + + return currNodeIndex; + } + } + + void populateBSPTree(clipMap_t* clipMap, BSPTree* tree) + { + int numPlanes = 0; + int numNodes = 0; + int numLeafs = 0; + int numAABBTrees = 0; + int maxObjsPerLeaf = 0; + + traverseBSPTreeForCounts(tree, &numPlanes, &numNodes, &numLeafs, &numAABBTrees, &maxObjsPerLeaf); + + printf("Max Objects per leaf: %i\n", maxObjsPerLeaf); + + clipMap->info.planeCount = numPlanes; + clipMap->info.planes = new cplane_s[clipMap->info.planeCount]; + clipMap->numNodes = numNodes; + clipMap->nodes = new cNode_t[clipMap->numNodes]; + // aabb trees: each leaf will have their own AABB tree of the objects within it, and the root aabb node will be the parent of every other aabb node. + // therefore, each aabb tree will be of size (numObjects + 1) as the tree needs a root aabb node to reference it's children. + clipMap->aabbTreeCount = numAABBTrees; + clipMap->aabbTrees = new CollisionAabbTree[clipMap->aabbTreeCount]; + + currPlaneCount = 0; + currNodeCount = 0; + currAABBCount = 0; + + // first leaf is always empty + clipMap->numLeafs = numLeafs + 1; + clipMap->leafs = new cLeaf_s[clipMap->numLeafs]; + memset(&clipMap->leafs[0], 0, sizeof(cLeaf_s)); + currLeafCount = 1; + + populateBSPTree_r(clipMap, tree); + + _ASSERT(clipMap->info.planeCount == currPlaneCount); + _ASSERT(clipMap->numNodes == currNodeCount); + _ASSERT(clipMap->numLeafs == currLeafCount); + _ASSERT(clipMap->aabbTreeCount == currAABBCount); + } + + void createClipMap(customMapInfo* projInfo) + { + clipMap_t* clipMap = new clipMap_t; + + clipMap->name = _strdup(projInfo->bspName.c_str()); + + std::string mapEntsName = projInfo->bspName.c_str(); + auto mapEntsAsset = m_context.LoadDependency(mapEntsName); + _ASSERT(mapEntsAsset != NULL); + clipMap->mapEnts = mapEntsAsset->Asset(); + + clipMap->pInfo = NULL; + + clipMap->box_model.mins.x = 0.0f; + clipMap->box_model.mins.y = 0.0f; + clipMap->box_model.mins.z = 0.0f; + clipMap->box_model.maxs.x = 0.0f; + clipMap->box_model.maxs.y = 0.0f; + clipMap->box_model.maxs.z = 0.0f; + clipMap->box_model.radius = 0.0f; + clipMap->box_model.info = NULL; + clipMap->box_model.leaf.mins.x = 3.4028235e38; + clipMap->box_model.leaf.mins.y = 3.4028235e38; + clipMap->box_model.leaf.mins.z = 3.4028235e38; + clipMap->box_model.leaf.maxs.x = -3.4028235e38; // for some reason the maxs are negative, and mins are positive + clipMap->box_model.leaf.maxs.y = -3.4028235e38; + clipMap->box_model.leaf.maxs.z = -3.4028235e38; + clipMap->box_model.leaf.brushContents = -1; + clipMap->box_model.leaf.terrainContents = 0; + clipMap->box_model.leaf.cluster = 0; + clipMap->box_model.leaf.collAabbCount = 0; + clipMap->box_model.leaf.firstCollAabbIndex = 0; + clipMap->box_model.leaf.leafBrushNode = 0; + + clipMap->box_brush = new cbrush_t; + clipMap->box_brush->axial_sflags[0][0] = -1; + clipMap->box_brush->axial_sflags[0][1] = -1; + clipMap->box_brush->axial_sflags[0][2] = -1; + clipMap->box_brush->axial_sflags[1][0] = -1; + clipMap->box_brush->axial_sflags[1][1] = -1; + clipMap->box_brush->axial_sflags[1][2] = -1; + clipMap->box_brush->axial_cflags[0][0] = -1; + clipMap->box_brush->axial_cflags[0][1] = -1; + clipMap->box_brush->axial_cflags[0][2] = -1; + clipMap->box_brush->axial_cflags[1][0] = -1; + clipMap->box_brush->axial_cflags[1][1] = -1; + clipMap->box_brush->axial_cflags[1][2] = -1; + clipMap->box_brush->contents = -1; + clipMap->box_brush->mins.x = 0.0f; + clipMap->box_brush->mins.y = 0.0f; + clipMap->box_brush->mins.z = 0.0f; + clipMap->box_brush->maxs.x = 0.0f; + clipMap->box_brush->maxs.y = 0.0f; + clipMap->box_brush->maxs.z = 0.0f; + clipMap->box_brush->numsides = 0; + clipMap->box_brush->numverts = 0; + clipMap->box_brush->sides = NULL; + clipMap->box_brush->verts = NULL; + + clipMap->numClusters = 1; + clipMap->vised = 0; + clipMap->clusterBytes = ((clipMap->numClusters + 63) >> 3) & 0xFFFFFFF8; + clipMap->visibility = new char[clipMap->clusterBytes]; + memset(clipMap->visibility, 0xFF, clipMap->clusterBytes); + + clipMap->isInUse = true; + clipMap->checksum = 0; + + clipMap->num_constraints = 0; + clipMap->constraints = NULL; + clipMap->max_ropes = 32; + clipMap->ropes = new rope_t[clipMap->max_ropes]; + memset(clipMap->ropes, 0, sizeof(rope_t) * clipMap->max_ropes); + + clipMap->info.numBrushSides = 0; + clipMap->info.brushsides = NULL; + clipMap->info.leafbrushNodesCount = 0; + clipMap->info.leafbrushNodes = NULL; + clipMap->info.numLeafBrushes = 0; + clipMap->info.leafbrushes = NULL; + clipMap->info.numBrushVerts = 0; + clipMap->info.brushVerts = NULL; + clipMap->info.numBrushes = NULL; + clipMap->info.brushes = NULL; + clipMap->info.brushBounds = NULL; + clipMap->info.brushContents = NULL; + + clipMap->originalDynEntCount = DYN_ENT_COUNT; + clipMap->dynEntCount[0] = clipMap->originalDynEntCount + 256; + clipMap->dynEntCount[1] = 0; + clipMap->dynEntCount[2] = 0; + clipMap->dynEntCount[3] = 0; + + // assume that there are 0 dyn ents from here on + clipMap->dynEntClientList[0] = new DynEntityClient[clipMap->dynEntCount[0]]; + clipMap->dynEntClientList[1] = NULL; + memset(clipMap->dynEntClientList[0], 0, sizeof(DynEntityClient) * clipMap->dynEntCount[0]); + + clipMap->dynEntServerList[0] = NULL; + clipMap->dynEntServerList[1] = NULL; + + clipMap->dynEntCollList[0] = new DynEntityColl[clipMap->dynEntCount[0]]; + clipMap->dynEntCollList[1] = NULL; + clipMap->dynEntCollList[2] = NULL; + clipMap->dynEntCollList[3] = NULL; + memset(clipMap->dynEntCollList[0], 0, sizeof(DynEntityColl) * clipMap->dynEntCount[0]); + + clipMap->dynEntPoseList[0] = new DynEntityPose[clipMap->dynEntCount[0]]; + clipMap->dynEntPoseList[1] = NULL; + memset(clipMap->dynEntPoseList[0], 0, sizeof(DynEntityPose) * clipMap->dynEntCount[0]); + + clipMap->dynEntDefList[0] = new DynEntityDef[clipMap->dynEntCount[0]]; + clipMap->dynEntDefList[1] = NULL; + memset(clipMap->dynEntDefList[0], 0, sizeof(DynEntityDef) * clipMap->dynEntCount[0]); + for (int i = 0; i < clipMap->dynEntCount[0]; i++) + { + DynEntityDef* currDef = &clipMap->dynEntDefList[0][i]; + currDef->physConstraints[0] = 0x1FF; + currDef->physConstraints[1] = 0x1FF; + currDef->physConstraints[2] = 0x1FF; + currDef->physConstraints[3] = 0x1FF; + } + + clipMap->numSubModels = 1; + clipMap->cmodels = new cmodel_t[clipMap->numSubModels]; + clipMap->cmodels[0].leaf.firstCollAabbIndex = 0; + clipMap->cmodels[0].leaf.collAabbCount = 0; + clipMap->cmodels[0].leaf.brushContents = 0; + clipMap->cmodels[0].leaf.terrainContents = WORLD_TERRAIN_CONTENTS; + clipMap->cmodels[0].leaf.mins.x = 0.0f; + clipMap->cmodels[0].leaf.mins.y = 0.0f; + clipMap->cmodels[0].leaf.mins.z = 0.0f; + clipMap->cmodels[0].leaf.maxs.x = 0.0f; + clipMap->cmodels[0].leaf.maxs.y = 0.0f; + clipMap->cmodels[0].leaf.maxs.z = 0.0f; + clipMap->cmodels[0].leaf.leafBrushNode = 0; + clipMap->cmodels[0].leaf.cluster = 0; + clipMap->cmodels[0].info = NULL; + + auto gfxWorldAsset = m_context.LoadDependency(projInfo->bspName); + _ASSERT(gfxWorldAsset != NULL); + GfxWorld* gfxWorld = gfxWorldAsset->Asset(); + + 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.z = gfxWorld->models[0].bounds[0].z; + 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.z = gfxWorld->models[0].bounds[1].z; + clipMap->cmodels[0].radius = distBetweenPoints(clipMap->cmodels[0].mins, clipMap->cmodels[0].maxs) / 2; + + clipMap->numStaticModels = 0; + clipMap->staticModelList = NULL; + + clipMap->info.numMaterials = 1; + clipMap->info.materials = new ClipMaterial[clipMap->info.numMaterials]; + clipMap->info.materials[0].name = _strdup(defaultMaterialName.c_str()); + clipMap->info.materials[0].contentFlags = MATERIAL_CONTENT_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 + // might do weird stuff on walls + int walkableEdgeSize = (3 * clipMap->triCount + 31) / 32 * 4; + clipMap->triEdgeIsWalkable = new char[walkableEdgeSize]; + memset(clipMap->triEdgeIsWalkable, 0xFF, walkableEdgeSize * sizeof(char)); + + // BSP creation must go last as it depends on unids, tris and verts already being populated + // HACK: + // the BSP tree creation does not work when BO2's coordinate system is used for mins and maxs. + // Workaround is to convert every BO2 coordinate to OGL's before it is added into the BSP tree, + // and then convert them back when it is being parsed into the clipmap. Requires some hacky + // logic, check populateBSPTree_r and addAABBTreeFromLeaf + + vec3_t* firstVert = &clipMap->verts[0]; + vec3_t clipMins; + vec3_t clipMaxs; + clipMins.x = firstVert->x; + clipMins.y = firstVert->y; + clipMins.z = firstVert->z; + clipMaxs.x = firstVert->x; + clipMaxs.y = firstVert->y; + clipMaxs.z = firstVert->z; + clipMins = CMUtil::convertFromBO2Coords(clipMins); + clipMaxs = CMUtil::convertFromBO2Coords(clipMaxs); + for (int i = 1; i < clipMap->vertCount; i++) + { + vec3_t vertCoord = CMUtil::convertFromBO2Coords(clipMap->verts[i]); + CMUtil::calcNewBoundsWithPoint(&vertCoord, &clipMins, &clipMaxs); + } + + BSPTree* tree = new BSPTree(clipMins.x, clipMins.y, clipMins.z, clipMaxs.x, clipMaxs.y, clipMaxs.z, 0); + for (int i = 0; i < clipMap->partitionCount; i++) + { + auto currPartition = &clipMap->partitions[i]; + + uint16_t* firstTri = clipMap->triIndices[currPartition->firstTri]; + vec3_t* firstVert = &clipMap->verts[firstTri[0]]; + vec3_t mins; + vec3_t maxs; + mins.x = firstVert->x; + mins.y = firstVert->y; + mins.z = firstVert->z; + maxs.x = firstVert->x; + maxs.y = firstVert->y; + maxs.z = firstVert->z; + mins = CMUtil::convertFromBO2Coords(mins); + maxs = CMUtil::convertFromBO2Coords(maxs); + for (int k = 0; k < currPartition->triCount; k++) + { + uint16_t* tri = clipMap->triIndices[currPartition->firstTri + k]; + for (int l = 0; l < 3; l++) + { + uint16_t vertIndex = tri[l]; + vec3_t vertCoord = CMUtil::convertFromBO2Coords(clipMap->verts[vertIndex]); + CMUtil::calcNewBoundsWithPoint(&vertCoord, &mins, &maxs); + } + } + + Object* currObject = new Object(mins.x, mins.y, mins.z, maxs.x, maxs.y, maxs.z, i); + + tree->addObject(currObject); + } + + populateBSPTree(clipMap, tree); + + m_context.AddAsset(clipMap->name, clipMap); + } + + void createComWorld(customMapInfo* projInfo) + { + // all lights that aren't the sunlight or default light need their own GfxLightDef asset + ComWorld* comWorld = new ComWorld; + comWorld->name = _strdup(projInfo->bspName.c_str()); + comWorld->isInUse = 1; + comWorld->primaryLightCount = 2; + comWorld->primaryLights = new ComPrimaryLight[comWorld->primaryLightCount]; + + ComPrimaryLight* defaultLight = &comWorld->primaryLights[0]; + memset(defaultLight, 0, sizeof(ComPrimaryLight)); + + ComPrimaryLight* sunLight = &comWorld->primaryLights[1]; + memset(sunLight, 0, sizeof(ComPrimaryLight)); + sunLight->type = 1; + // Below are default values taken from mp_dig + sunLight->diffuseColor.r = 1.0f; + sunLight->diffuseColor.g = 1.0f; + sunLight->diffuseColor.b = 1.0f; + sunLight->diffuseColor.a = 0.0f; + sunLight->dir.x = -0.2410777360200882; + sunLight->dir.y = -0.8407384753227234; + sunLight->dir.z = 0.48480960726737976; + + m_context.AddAsset(comWorld->name, comWorld); + } + + void createMapEnts(customMapInfo* projInfo) + { + MapEnts* mapEnts = new MapEnts; + + mapEnts->name = _strdup(projInfo->bspName.c_str()); + + // don't need these + mapEnts->trigger.count = 0; + mapEnts->trigger.models = NULL; + mapEnts->trigger.hullCount = 0; + mapEnts->trigger.hulls = NULL; + mapEnts->trigger.slabCount = 0; + mapEnts->trigger.slabs = NULL; + + const auto file = m_search_path.Open("custom_map/entities.json"); + if (!file.IsOpen()) + { + printf("WARN: can't find entity json!\n"); + return; + } + + json entJs = json::parse(*file.m_stream); + + std::string entityString; + int entityCount = entJs["entityCount"]; + for (int i = 0; i < entityCount; i++) + { + auto currEntity = entJs["entities"][i]; + + if (i == 0) + { + std::string className = currEntity["classname"]; + if (className.compare("worldspawn") != 0) + { + printf("ERROR: first entity in the map entity string must be the worldspawn class!"); + hasLinkFailed = true; + return; + } + } + + entityString.append("{\n"); + + for (auto& element : currEntity.items()) + { + std::string key = element.key(); + std::string value = element.value(); + entityString.append(std::format("\"{}\" \"{}\"\n", key, value)); + } + + entityString.append("}\n"); + } + entityString.pop_back(); // remove newline character from the end of the string + + mapEnts->entityString = _strdup(entityString.c_str()); + mapEnts->numEntityChars = entityString.length() + 1; // numEntityChars includes the null character + + m_context.AddAsset(mapEnts->name, mapEnts); + } + + void createGameWorldMp(customMapInfo* projInfo) + { + GameWorldMp* gameWorldMp = new GameWorldMp; + + gameWorldMp->name = _strdup(projInfo->bspName.c_str()); + + gameWorldMp->path.nodeCount = 0; + gameWorldMp->path.originalNodeCount = 0; + gameWorldMp->path.visBytes = 0; + gameWorldMp->path.smoothBytes = 0; + gameWorldMp->path.nodeTreeCount = 0; + + int nodeCount = gameWorldMp->path.nodeCount + 128; + gameWorldMp->path.nodes = new pathnode_t[nodeCount]; + gameWorldMp->path.basenodes = new pathbasenode_t[nodeCount]; + memset(gameWorldMp->path.nodes, 0, nodeCount * sizeof(pathnode_t)); + memset(gameWorldMp->path.basenodes, 0, nodeCount * sizeof(pathbasenode_t)); + + gameWorldMp->path.pathVis = NULL; + gameWorldMp->path.smoothCache = NULL; + gameWorldMp->path.nodeTree = NULL; + + m_context.AddAsset(gameWorldMp->name, gameWorldMp); + } + + void createSkinnedVerts(customMapInfo* projInfo) + { + SkinnedVertsDef* skinnedVerts = new SkinnedVertsDef; + skinnedVerts->name = "skinnedverts"; + skinnedVerts->maxSkinnedVerts = projInfo->gfxInfo.vertexCount; + + m_context.AddAsset("skinnedverts", skinnedVerts); + } + + FootstepTableDef* addEmptyFootstepTableAsset(std::string assetName) + { + if (assetName.length() == 0) + return NULL; + + FootstepTableDef* footstepTable = new FootstepTableDef; + footstepTable->name = _strdup(assetName.c_str()); + memset(footstepTable->sndAliasTable, 0, sizeof(footstepTable->sndAliasTable)); + + m_context.AddAsset(assetName, footstepTable); + + return footstepTable; + } + + void checkAndAddDefaultRequiredAssets(customMapInfo* projectInfo) + { + if (m_context.LoadDependency("maps/mp/mp_dig.gsc") == NULL) + { + hasLinkFailed = true; + return; + } + if (m_context.LoadDependency("maps/mp/mp_dig_amb.gsc") == NULL) + { + hasLinkFailed = true; + return; + } + if (m_context.LoadDependency("maps/mp/mp_dig_fx.gsc") == NULL) + { + hasLinkFailed = true; + return; + } + if (m_context.LoadDependency("maps/mp/createfx/mp_dig_fx.gsc") == NULL) + { + hasLinkFailed = true; + return; + } + if (m_context.LoadDependency("clientscripts/mp/mp_dig.csc") == NULL) + { + hasLinkFailed = true; + return; + } + if (m_context.LoadDependency("clientscripts/mp/mp_dig_amb.csc") == NULL) + { + hasLinkFailed = true; + return; + } + if (m_context.LoadDependency("clientscripts/mp/mp_dig_fx.csc") == NULL) + { + hasLinkFailed = true; + return; + } + if (m_context.LoadDependency("clientscripts/mp/createfx/mp_dig_fx.csc") == NULL) + { + hasLinkFailed = true; + return; + } + + addEmptyFootstepTableAsset("default_1st_person"); + addEmptyFootstepTableAsset("default_3rd_person"); + addEmptyFootstepTableAsset("default_1st_person_quiet"); + addEmptyFootstepTableAsset("default_3rd_person_quiet"); + addEmptyFootstepTableAsset("default_3rd_person_loud"); + addEmptyFootstepTableAsset("default_ai"); + } }; diff --git a/src/ObjLoading/Game/T6/CustomMap/LoaderCustomMapT6.cpp b/src/ObjLoading/Game/T6/CustomMap/LoaderCustomMapT6.cpp index 284cb1b3..42c41951 100644 --- a/src/ObjLoading/Game/T6/CustomMap/LoaderCustomMapT6.cpp +++ b/src/ObjLoading/Game/T6/CustomMap/LoaderCustomMapT6.cpp @@ -10,7 +10,7 @@ using namespace T6; namespace { - class CustomMapLoader final : public AssetCreator + class CustomMapLoader final : public AssetCreator { public: CustomMapLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) @@ -32,7 +32,7 @@ namespace return AssetCreationResult::Failure(); // linker will add all the assets needed - CustomMapLinker* linker = new CustomMapLinker(m_memory, m_search_path, m_zone); + CustomMapLinker* linker = new CustomMapLinker(m_memory, m_search_path, m_zone, context); bool result = linker->linkCustomMap(mapInfo); if (result) @@ -50,7 +50,7 @@ namespace namespace T6 { - std::unique_ptr> CreateCustomMapLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) + std::unique_ptr> CreateCustomMapLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone) { return std::make_unique(memory, searchPath, zone); } diff --git a/src/ObjLoading/Game/T6/CustomMap/LoaderCustomMapT6.h b/src/ObjLoading/Game/T6/CustomMap/LoaderCustomMapT6.h index 13862ba6..78a388fc 100644 --- a/src/ObjLoading/Game/T6/CustomMap/LoaderCustomMapT6.h +++ b/src/ObjLoading/Game/T6/CustomMap/LoaderCustomMapT6.h @@ -9,5 +9,5 @@ namespace T6 { - std::unique_ptr> CreateCustomMapLoader(MemoryManager& memory, ISearchPath& searchPath); + std::unique_ptr> CreateCustomMapLoader(MemoryManager& memory, ISearchPath& searchPath, Zone& zone); } // namespace T6 diff --git a/src/ObjLoading/Game/T6/CustomMap/ProjectCreator.cpp b/src/ObjLoading/Game/T6/CustomMap/ProjectCreator.cpp index 77d5d53a..b08ca254 100644 --- a/src/ObjLoading/Game/T6/CustomMap/ProjectCreator.cpp +++ b/src/ObjLoading/Game/T6/CustomMap/ProjectCreator.cpp @@ -1,105 +1,105 @@ #include "ProjectCreator.h" -class CustomMapLoader +#include "OBJ_Loader.h" + +void parseMesh(objl::Mesh* OBJMesh, worldSurface* meshSurface, customMapInfo* projInfo) { - 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++) { - 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; - } + _ASSERT(OBJMesh->Indices[i] < UINT16_MAX); + surfIndices[i] = OBJMesh->Indices[i]; } - customMapInfo* createCustomMapInfo(std::string& projectName, ISearchPath& searchPath) + for (int i = 0; i < meshVertexCount; i++) { - std::string objFilePath = searchPath.GetPath() + "custom_map/world.obj"; + objl::Vertex* meshVertex = &OBJMesh->Vertices[i]; + customMapVertex* surfVertex = &surfVertices[i]; - objl::Loader OBJloader; - bool isLoaded = OBJloader.LoadFile(objFilePath); - if (!isLoaded) - { - printf("OBJLoader: unable to load obj file %s\n", objFilePath.c_str()); - return NULL; - } + surfVertex->pos.x = meshVertex->Position.X; + surfVertex->pos.y = meshVertex->Position.Y; + surfVertex->pos.z = meshVertex->Position.Z; - customMapInfo* projInfo = new customMapInfo; + 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; - projInfo->name = projectName; - projInfo->bspName = "maps/mp/" + projectName + ".d3dbsp"; + surfVertex->texCoord[0] = meshVertex->TextureCoordinate.X; + surfVertex->texCoord[1] = meshVertex->TextureCoordinate.Y; - projInfo->gfxInfo.surfaceCount = OBJloader.LoadedMeshes.size(); - projInfo->gfxInfo.surfaces = new worldSurface[projInfo->gfxInfo.surfaceCount]; + surfVertex->normal.x = meshVertex->Normal.X; + surfVertex->normal.y = meshVertex->Normal.Y; + surfVertex->normal.z = meshVertex->Normal.Z; - projInfo->gfxInfo.vertexCount = 0; - projInfo->gfxInfo.indexCount = 0; - projInfo->gfxInfo.vertices = (customMapVertex*)malloc(1); - projInfo->gfxInfo.indices = (uint16_t*)malloc(1); + // TODO: fix tangents, seems to work for now though + surfVertex->tangent.x = 1.0f; + surfVertex->tangent.y = 0.0f; + surfVertex->tangent.z = 0.0f; - for (int i = 0; i < projInfo->gfxInfo.surfaceCount; i++) - { - objl::Mesh* currMesh = &OBJloader.LoadedMeshes[i]; - worldSurface* currSurface = &projInfo->gfxInfo.surfaces[i]; + surfVertex->packedLmapCoord = 0; - currSurface->materialName = currMesh->MeshMaterial.name; - currSurface->reflectionProbeIndex = 0; - currSurface->primaryLightIndex = 0; - currSurface->lightmapIndex = 0; - currSurface->flags = 0; - - parseMesh(currMesh, currSurface, projInfo); - } - - return projInfo; + surfVertex->binormalSign = 0.0f; } -}; +} + +customMapInfo* 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; +} + diff --git a/src/ObjLoading/Game/T6/CustomMap/ProjectCreator.h b/src/ObjLoading/Game/T6/CustomMap/ProjectCreator.h index 8c6a5408..a75d041a 100644 --- a/src/ObjLoading/Game/T6/CustomMap/ProjectCreator.h +++ b/src/ObjLoading/Game/T6/CustomMap/ProjectCreator.h @@ -1,13 +1,12 @@ +#pragma once + #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); }; diff --git a/src/ObjLoading/Game/T6/CustomMap/Util.h b/src/ObjLoading/Game/T6/CustomMap/Util.h index 30e71cbf..216fa4bf 100644 --- a/src/ObjLoading/Game/T6/CustomMap/Util.h +++ b/src/ObjLoading/Game/T6/CustomMap/Util.h @@ -1,3 +1,6 @@ +#pragma once + + #include "Game/T6/T6.h" using namespace T6; diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp index 496dbd44..e1bcc7f1 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp @@ -389,7 +389,7 @@ namespace T6 collection.AddAssetCreator(std::make_unique>(zone)); collection.AddAssetCreator(std::make_unique>(zone)); collection.AddAssetCreator(std::make_unique>(zone)); - collection.AddAssetCreator(std::make_unique>(zone)); + //collection.AddAssetCreator(std::make_unique>(zone)); } void ConfigureLoaders(AssetCreatorCollection& collection, Zone& zone, ISearchPath& searchPath, IGdtQueryable& gdt) @@ -451,7 +451,7 @@ namespace T6 collection.AddAssetCreator(CreateRawZBarrierLoader(memory, searchPath, zone)); collection.AddAssetCreator(CreateGdtZBarrierLoader(memory, searchPath, gdt, zone)); - collection.AddAssetCreator(CreateCustomMapLoader(memory, searchPath)); + collection.AddAssetCreator(CreateCustomMapLoader(memory, searchPath, zone)); } } // namespace