// https://wiki.zeroy.com/index.php?title=Call_of_Duty_1:_d3dbsp // https://wiki.zeroy.com/index.php?title=Call_of_Duty_2:_d3dbsp //yes, shockingly badly documented. that's half the challenge though, right? //vaguely derived from quake3. #include "../plugin.h" #include "../engine/common/com_mesh.h" #include "../engine/common/com_bih.h" static plugfsfuncs_t *filefuncs; static plugmodfuncs_t *modfuncs; static plugthreadfuncs_t *threadfuncs; typedef struct { //materials are used for rendering and collisions q2mapsurface_t *surfaces; //for collision properties, texturing info, not actual surfaces. //generally useful stuff unsigned int codbspver; mplane_t *planes; size_t num_planes; struct codnode_s { mplane_t *plane; int childnum[2]; ivec3_t mins, maxs; } *nodes; size_t numnodes; struct codleaf_s { int cluster; int area; //we don't care about what we don't understand. //unsigned int firstleafbrush; //unsigned int numleafbrushes; //unsigned int firstleafsurface; //unsigned int numleafsurfaces; //int cell; unsigned int firstlightindex; unsigned int numlightindexes; } *leaf; size_t numleafs; qbyte *pvsdata; //rendering stuff. this is pretty simple as its all soup. mesh_t soupverts; //don't trust the indexes! struct codsoup_s { unsigned int vertex_offset; unsigned int index_offset; unsigned int index_fixup; } *soups; //aka surfs //brush collision... this stuff all goes away once we've built our BIH. const struct codbsp_brushside_s { //unswapped... union{ unsigned int plane; float dist; }; unsigned int material_idx; } *brushsides; size_t num_brushsides; q2cbrush_t *brushes; size_t num_brushes; //patch/soup collision nightmare. const struct codpatch_s { //unswapped... short mat; //material? short mode; //mode union { struct { //patch short w; //width short h; //height int unknown; //unknown. flatness? int firstvert; //first CODLUMP_COLLISIONVERTS } mode0; struct { //soup short numverts; short numidx; int firstvert; //first CODLUMP_COLLISIONVERTS int firstidx; //first CODLUMP_COLLISIONINDEXES } mode1; }; } *patches; size_t numpatches; const unsigned int *leafpatches; //loadtime only, unswapped... size_t numleafpatches; vecV_t *patchvertexes; size_t numpatchvertexes; index_t *patchindexes; size_t numpatchindexes; unsigned short *lightindexes; size_t numlightindexes; struct codlight_s { int type; vec3_t xyz; vec3_t rgb; vec3_t dir; float scale; float fov; } *lights; size_t numlights; } codbspinfo_t; #define COD1BSP_VERSION 0x0000003b #define COD2BSP_VERSION 0x00000004 enum { COD1LUMP_MATERIALS=0, //names, surfaceflags, and contentbits for said materials (so dedicated servers don't need to parse anything extra). COD1LUMP_LIGHTMAPS=1, //just 2d images. 512*512 RGB ones. COD1LUMP_PLANES=2, //used for nodes and brushsides COD1LUMP_BRUSHSIDES=3, //which planes+materials to use for each side of the brushes... COD1LUMP_BRUSHES=4, //defines which sets of said sides to group, and their material(contents). // COD1LUMP_UNKNOWN=5, COD1LUMP_SOUPS=6, //q3 would call em surfaces. except they're ALL trisoup, none are planar/patches, they're prebatched (within their 'cells'). COD1LUMP_SOUPVERTS=7, //the vertex attributes for the soup COD1LUMP_SOUPINDEXES=8, //the indexes, woo. how fancy. it ain't soup without this! COD1LUMP_CULLGROUPS=9, COD1LUMP_CULLGROUPINDEXES=10, COD1LUMP_PORTALVERTS=11, COD1LUMP_OCCLUDERS=12, COD1LUMP_OCCLUDERPLANES=13, COD1LUMP_OCCLUDEREDGES=14, COD1LUMP_OCCLUDERINDEXES=15, COD1LUMP_AABBTREES=16, //some sort of tree... COD1LUMP_CELLS=17, //part of the portal rendering system (reduced number vs leafs so there's less to compute) COD1LUMP_PORTALS=18, //for walking between cells? COD1LUMP_LIGHTINDEXES=19, //indexes into LIGHTVALUES COD1LUMP_NODES=20, //tree leading to leafs. COD1LUMP_LEAFS=21, //describes a small convex area COD1LUMP_LEAFBRUSHES=22, //list of brushes so leafs can share them, for collision+pointcontents. COD1LUMP_LEAFPATCHES=23, //list of patches and embedded meshes per leaf, for collision COD1LUMP_PATCHCOLLISION=24, //defines which verts/topology etc to use for each mesh/patch COD1LUMP_COLLISIONVERTS=25, //simple verts used for patch/embedded-mesh collision COD1LUMP_COLLISIONINDEXES=26,//indexes for the collision verts, only used for embedded meshes COD1LUMP_MODELS=27, //sub (aka inline) models - the first entry is the worldmodel, the second is your "*1" model and upwards. COD1LUMP_VISIBILITY=28, //numclusters, numrowbytes, then just packed rows per cluster (no compression like q3 unlike q1). COD1LUMP_ENTITIES=29, COD1LUMP_LIGHTS=30, // COD1LUMP_UNKNOWN=31, // COD1LUMP_UNKNOWN=32, COD1LUMP_COUNT=33 }; enum { COD2LUMP_MATERIALS=0, COD2LUMP_LIGHTMAPS=1, COD2LUMP_LIGHTGRIDHASH=2, COD2LUMP_LIGHTGRIDVALUES=3, COD2LUMP_PLANES=4, COD2LUMP_BRUSHSIDES=5, COD2LUMP_BRUSHES=6, COD2LUMP_SOUPS=7, COD2LUMP_SOUPVERTS=8, COD2LUMP_SOUPINDEXES=9, COD2LUMP_CULLGROUPS=10, COD2LUMP_CULLGROUPINDEXES=11, // COD2LUMP_UNKNOWN=12, // COD2LUMP_UNKNOWN=13, // COD2LUMP_UNKNOWN=14, // COD2LUMP_UNKNOWN=15, // COD2LUMP_UNKNOWN=16, COD2LUMP_PORTALVERTS=17, COD2LUMP_OCCLUDERS=18, COD2LUMP_OCCLUDERPLANES=19, COD2LUMP_OCCLUDEREDGES=20, COD2LUMP_OCCLUDERINDEXES=21, COD2LUMP_AABBTREES=22, COD2LUMP_CELLS=23, COD2LUMP_PORTALS=24, COD2LUMP_NODES=25, COD2LUMP_LEAFS=26, COD2LUMP_LEAFBRUSHES=27, // COD2LUMP_LEAFPATCHES=28, //o.O COD2LUMP_COLLISIONVERTS=29, COD2LUMP_COLLISIONEDGES=30, COD2LUMP_COLLISIONINDEXES=31, COD2LUMP_COLLISIONBORDERS=32, COD2LUMP_COLLISIONPARTS=33, COD2LUMP_COLLISIONAABBS=34, COD2LUMP_MODELS=35, COD2LUMP_VISIBILITY=36, COD2LUMP_ENTITIES=37, COD2LUMP_PATHS=38, COD2LUMP_COUNT=39 }; static struct codleaf_s *CODBSP_LeafForPoint (model_t *mod, const vec3_t p, int num) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; float d; struct codnode_s *node; mplane_t *plane; while (num >= 0) { node = prv->nodes + num; plane = node->plane; if (plane->type < 3) d = p[plane->type] - plane->dist; else d = DotProduct (plane->normal, p) - plane->dist; if (d < 0) num = node->childnum[1]; else num = node->childnum[0]; } return &prv->leaf[-1 - num]; } static int CODBSP_ClusterForPoint (struct model_s *model, const vec3_t point, int *areaout) { struct codleaf_s *leaf = CODBSP_LeafForPoint(model, point, 0); if (areaout) *areaout = leaf->area; return leaf->cluster; } static qbyte *CODBSP_ClusterPVS (struct model_s *model, int cluster, pvsbuffer_t *pvsbuffer, pvsmerge_t merge) { codbspinfo_t *prv = (codbspinfo_t*)model->meshinfo; size_t i; if (cluster >= 0 && cluster < model->numclusters) { qbyte *pvs = prv->pvsdata + cluster*model->pvsbytes; //packed, without compresion. if (merge == PVM_FAST) return pvs; else { if (pvsbuffer->buffersize < model->pvsbytes) pvsbuffer->buffer = plugfuncs->Realloc(pvsbuffer->buffer, pvsbuffer->buffersize = model->pvsbytes); if (merge==PVM_REPLACE) memcpy(pvsbuffer->buffer, pvs, model->pvsbytes); else for (i = 0; i < model->pvsbytes; i++) pvsbuffer->buffer[i] |= pvs[i]; //slooooow return pvsbuffer->buffer; } } if (pvsbuffer) { if (pvsbuffer->buffersize < model->pvsbytes) pvsbuffer->buffer = plugfuncs->Realloc(pvsbuffer->buffer, pvsbuffer->buffersize = model->pvsbytes); if (merge!=PVM_MERGE) memset(pvsbuffer->buffer, 0, model->pvsbytes); return pvsbuffer->buffer; } return NULL; } //static qbyte *CODBSP_ClusterPHS (struct model_s *model, int cluster, pvsbuffer_t *pvsbuffer){return "\xff";} //static void CODBSP_PurgeModel(struct model_s *mod){} //static qbyte *CODBSP_ClustersInSphere(struct model_s *model, const vec3_t point, float radius, pvsbuffer_t *pvsbuffer, const qbyte *fte_restrict unionwith){} //static size_t CODBSP_WriteAreaBits(struct model_s *model, qbyte *buffer, size_t maxbytes, int area, qboolean merge){} //static qboolean CODBSP_AreasConnected(struct model_s *model, unsigned int area1, unsigned int area2){} //static void CODBSP_SetAreaPortalState(struct model_s *model, unsigned int portal, unsigned int area1, unsigned int area2, qboolean open){} //static size_t CODBSP_SaveAreaPortalBlob(struct model_s *model, void **ptr){} //static size_t CODBSP_LoadAreaPortalBlob(struct model_s *model, void *ptr, size_t size){} #ifdef HAVE_SERVER static int leaf_count, leaf_maxcount; static struct codleaf_s **leaf_list; static const float *leaf_mins, *leaf_maxs; static int leaf_topnode; #define BoxOnPlaneSide CodBoxOnPlaneSide static int BoxOnPlaneSide (const vec3_t emins, const vec3_t emaxs, const mplane_t *p) { float dist1, dist2; int sides; // general case switch (p->signbits) { default: case 0: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; break; case 1: dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; break; case 2: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; break; case 3: dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; break; case 4: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; break; case 5: dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emaxs[2]; break; case 6: dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; break; case 7: dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1] + p->normal[2]*emins[2]; dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1] + p->normal[2]*emaxs[2]; break; } sides = 0; if (dist1 >= p->dist) sides = 1; if (dist2 < p->dist) sides |= 2; return sides; } static void CODBSP_BoxLeafs_r (codbspinfo_t *prv, int nodenum) { mplane_t *plane; struct codnode_s *node; int s; while (1) { if (nodenum < 0) { if (leaf_count >= leaf_maxcount) return; leaf_list[leaf_count++] = &prv->leaf[-1 - nodenum]; return; } node = &prv->nodes[nodenum]; plane = node->plane; s = BOX_ON_PLANE_SIDE(leaf_mins, leaf_maxs, plane); if (s == 1) nodenum = node->childnum[0]; else if (s == 2) nodenum = node->childnum[1]; else { // go down both if (leaf_topnode == -1) leaf_topnode = nodenum; CODBSP_BoxLeafs_r (prv, node->childnum[0]); nodenum = node->childnum[1]; } } } static int CODBSP_BoxLeafs (model_t *mod, const vec3_t mins, const vec3_t maxs, struct codleaf_s **list, int listsize, int *topnode) { leaf_list = list; leaf_count = 0; leaf_maxcount = listsize; leaf_mins = mins; leaf_maxs = maxs; leaf_topnode = -1; CODBSP_BoxLeafs_r (mod->meshinfo, 0); if (topnode) *topnode = leaf_topnode; return leaf_count; } static unsigned int CODBSP_FatPVS (struct model_s *mod, const vec3_t org, pvsbuffer_t *result, qboolean merge) { struct codleaf_s *leafs[64]; int i, j, count; vec3_t mins, maxs; for (i=0 ; i<3 ; i++) { mins[i] = org[i] - 8; maxs[i] = org[i] + 8; } count = CODBSP_BoxLeafs (mod, mins, maxs, leafs, countof(leafs), NULL); if (count < 1) Sys_Errorf ("CODBSP_FatPVS: count < 1"); //grow the buffer if needed if (result->buffersize < mod->pvsbytes) result->buffer = plugfuncs->Realloc(result->buffer, result->buffersize=mod->pvsbytes); if (count == 1 && leafs[0]->cluster == -1) { //if the only leaf is the outside then broadcast it. memset(result->buffer, 0xff, mod->pvsbytes); i = count; } else { i = 0; if (!merge) mod->funcs.ClusterPVS(mod, leafs[i++]->cluster, result, PVM_REPLACE); // or in all the other leaf bits for ( ; ifuncs.ClusterPVS(mod, leafs[i]->cluster, result, PVM_MERGE); } } return mod->pvsbytes; } static qboolean CODBSP_HeadnodeVisible (codbspinfo_t *prv, int nodenum, const qbyte *visbits) { int leafnum; int cluster; struct codnode_s *node; if (nodenum < 0) { leafnum = -1-nodenum; cluster = prv->leaf[leafnum].cluster; if (cluster == -1) return false; if (visbits[cluster>>3] & (1<<(cluster&7))) return true; return false; } node = &prv->nodes[nodenum]; if (CODBSP_HeadnodeVisible(prv, node->childnum[0], visbits)) return true; return CODBSP_HeadnodeVisible(prv, node->childnum[1], visbits); } static qboolean CODBSP_EdictInFatPVS (struct model_s *mod, const struct pvscache_s *ent, const qbyte *pvs, const int *areas) { int i,l; /*int nullarea = -1; if (areas) { //areas[0] is the count of areas the camera is in, if valid. requires us to track portal states... for (i = 1; ; i++) { if (i > areas[0]) return false; //none of the camera's areas could see the entity if (areas[i] == ent->areanum) { if (areas[i] != nullarea) break; //else entity is fully outside the world, invisible to all... } else if (CODBSP_AreasConnected (mod, areas[i], ent->areanum)) break; // doors can legally straddle two areas, so // we may need to check another one else if (ent->areanum2 != nullarea && CODBSP_AreasConnected (mod, areas[i], ent->areanum2)) break; } }*/ if (ent->num_leafs == -1) { // too many leafs for individual check, go by headnode if (!CODBSP_HeadnodeVisible (mod->meshinfo, ent->headnode, pvs)) return false; } else { // check individual leafs for (i=0 ; i < ent->num_leafs ; i++) { l = ent->leafnums[i]; if (pvs[l >> 3] & (1 << (l&7) )) break; } if (i == ent->num_leafs) return false; // not visible } return true; } static void CODBSP_FindTouchedLeafs (struct model_s *model, struct pvscache_s *ent, const vec3_t mins, const vec3_t maxs) { #define MAX_TOTAL_ENT_LEAFS MAX_ENT_LEAFS+1 struct codleaf_s *leafs[MAX_TOTAL_ENT_LEAFS]; int clusters[MAX_TOTAL_ENT_LEAFS]; int num_leafs; int topnode; int i, j; int area; int nullarea = -1; //ent->num_leafs == q2's ent->num_clusters ent->num_leafs = 0; ent->areanum = nullarea; ent->areanum2 = nullarea; if (!mins || !maxs) return; //get all leafs, including solids num_leafs = CODBSP_BoxLeafs (model, mins, maxs, leafs, MAX_TOTAL_ENT_LEAFS, &topnode); // set areas for (i=0 ; icluster; //could dedupe these. area = leafs[i]->area; if (area != nullarea) { // doors may legally straggle two areas, // but nothing should ever need more than that if (ent->areanum != nullarea && ent->areanum != area) ent->areanum2 = area; else ent->areanum = area; } } if (num_leafs >= MAX_TOTAL_ENT_LEAFS) { // assume we missed some leafs, and mark by headnode ent->num_leafs = -1; ent->headnode = topnode; } else { ent->num_leafs = 0; for (i=0 ; inum_leafs == MAX_ENT_LEAFS) { // assume we missed some leafs, and mark by headnode ent->num_leafs = -1; ent->headnode = topnode; break; } ent->leafnums[ent->num_leafs++] = clusters[i]; } } } } #endif #ifdef HAVE_CLIENT static void CODBSP_LightPointValues (struct model_s *mod, const vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct codleaf_s *leaf = CODBSP_LeafForPoint(mod, point, 0); unsigned short *lightindexes = prv->lightindexes + leaf->firstlightindex; size_t i; struct codlight_s *light; // vec3_t move; // float d; float scale; if (!leaf->numlightindexes) { VectorSet(res_diffuse, 128,128,128); VectorSet(res_ambient, 128,128,128); VectorSet(res_dir, 1,0,0); return; } VectorSet(res_diffuse, 0,0,0); VectorSet(res_ambient, 0,0,0); VectorSet(res_dir, 0,0,0); for (i = 0; i < leaf->numlightindexes; i++, lightindexes++) { if (*lightindexes >= prv->numlights) { // :( don't know what this is meant to signify, happens with the first index more often than not. if (!prv->numlights) continue; light = prv->lights; } else light = prv->lights + *lightindexes; switch (light->type) { case 1: //sun... scale = 256; break; /* case 4: VectorSubtract(point, light->xyz, move); d = DotProduct(move,move); if (d > light->scale) continue; scale = (light->scale-d)/d; scale *= 256; break;*/ /* case 5: break;*/ /* case 7: break;*/ default: continue; } VectorMA(res_diffuse, scale, light->rgb, res_diffuse); VectorMA(res_ambient, scale, light->rgb, res_ambient); VectorMA(res_dir, scale, light->dir, res_dir); break; //:( } scale = DotProduct(res_dir,res_dir); if (scale <= 0) VectorSet(res_dir, 1,0,0); else VectorScale(res_dir, 1/scale, res_dir); } //static void CODBSP_GenerateShadowMesh (struct model_s *model, dlight_t *dl, const qbyte *lvis, qbyte *truevis, void(*callback)(struct msurface_s*)){} //static void CODBSP_StainNode (struct model_s *model, float *parms){} //static void CODBSP_MarkLights (struct dlight_s *light, dlightbitmask_t bit, struct mnode_s *node){} static void CODBSP_BuildSurfMesh(model_t *mod, msurface_t *surf, builddata_t *bd) { //just builds the actual mesh data, now that it has per-batch storage allocated. codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; mesh_t *mesh = surf->mesh; struct codsoup_s *soup = &prv->soups[surf-mod->surfaces]; int i; mesh->istrifan = false; memcpy(mesh->xyz_array, prv->soupverts.xyz_array+soup->vertex_offset, sizeof(*mesh->xyz_array)*mesh->numvertexes); memcpy(mesh->normals_array, prv->soupverts.normals_array+soup->vertex_offset, sizeof(*mesh->normals_array)*mesh->numvertexes); for (i = 0; i < mesh->numvertexes; i++) Vector4Scale(prv->soupverts.colors4b_array[soup->vertex_offset+i], 1.f/255, mesh->colors4f_array[0][i]); //memcpy(mesh->colors4b_array, prv->soupverts.colors4b_array+soup->vertex_offset, sizeof(*mesh->colors4b_array)*mesh->numvertexes); memcpy(mesh->st_array, prv->soupverts.st_array+soup->vertex_offset, sizeof(*mesh->st_array)*mesh->numvertexes); memcpy(mesh->lmst_array[0], prv->soupverts.lmst_array[0]+soup->vertex_offset, sizeof(*mesh->lmst_array[0])*mesh->numvertexes); if (soup->index_fixup) { for (i = 0; i < mesh->numindexes; i++) mesh->indexes[i] = prv->soupverts.indexes[soup->index_offset+i] - soup->index_fixup; } else memcpy(mesh->indexes, prv->soupverts.indexes+soup->index_offset, sizeof(*mesh->indexes)*mesh->numindexes); if (prv->soupverts.snormals_array) { //cod2 made them explicit. yay. memcpy(mesh->snormals_array, prv->soupverts.snormals_array+soup->vertex_offset, sizeof(*mesh->snormals_array)*mesh->numvertexes); memcpy(mesh->tnormals_array, prv->soupverts.tnormals_array+soup->vertex_offset, sizeof(*mesh->tnormals_array)*mesh->numvertexes); } else { //compute the tangents for rtlights. modfuncs->AccumulateTextureVectors(mesh->xyz_array, mesh->st_array, mesh->normals_array, mesh->snormals_array, mesh->tnormals_array, mesh->indexes, mesh->numindexes, false); modfuncs->NormaliseTextureVectors(mesh->normals_array, mesh->snormals_array, mesh->tnormals_array, mesh->numvertexes, false); } } static void CODBSP_GenerateMaterials(void *ctx, void *data, size_t a, size_t b) { model_t *mod = ctx; const char *script; if (!a) { //submodels share textures, so only do this if 'a' is 0 (inline index, 0 = world). for(a = 0; a < mod->numtextures; a++) { script = NULL; if (!strncmp(mod->textures[a]->name, "sky/", 4)) script = "{\n" "sort sky\n" "surfaceparm nodlight\n" "skyparms - - -\n" "}\n"; mod->textures[a]->shader = modfuncs->RegisterBasicShader(mod, mod->textures[a]->name, SUF_LIGHTMAP, script, PTI_INVALID, 0, 0, NULL, NULL); } } modfuncs->Batches_Build(mod, data); if (data) plugfuncs->Free(data); } static void CODBSP_PrepareFrame(struct model_s *mod, refdef_t *refdef, int area, int clusters[2], pvsbuffer_t *vis, qbyte **entvis_out, qbyte **surfvis_out) { *entvis_out = *surfvis_out = CODBSP_ClusterPVS(mod, clusters[0], vis, false); if (clusters[1] != -1) CODBSP_ClusterPVS(mod, clusters[1], vis, true); /*if (!refdef->areabitsknown) { //generate the info each frame, as the gamecode didn't tell us what to use. int leafnum = CODBSP_PointLeafnum (mod, refdef->vieworg); int clientarea = CODBSP_LeafArea (mod, leafnum); CODBSP_WriteAreaBits(mod, refdef->areabits, clientarea, false); refdef->areabitsknown = true; }*/ if (0) { size_t i; msurface_t *surf; for (i = mod->firstmodelsurface+mod->nummodelsurfaces; i --> mod->firstmodelsurface; ) { surf = &mod->surfaces[i]; surf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh; } } else { size_t i; msurface_t *surf; for (i = mod->firstmodelsurface; i < mod->nummodelsurfaces; i++) { surf = &mod->surfaces[i]; surf->sbatch->mesh[surf->sbatch->meshes++] = surf->mesh; } } //for static props... //ent = modfuncs->NewSceneEntity(); } static void CODBSP_InfoForPoint(struct model_s *mod, vec3_t pos, int *area, int *cluster, unsigned int *contentbits) { struct codleaf_s *leaf = CODBSP_LeafForPoint(mod, pos, 0); *area = leaf->area; *cluster = leaf->cluster; *contentbits = mod->funcs.PointContents(mod, NULL, pos); //needs a proper pointcontents. } #endif static qboolean CODBSP_LoadShaders (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct { char shadername[64]; unsigned int surfflags; unsigned int contents; } *in = (void *)(mod_base + l->fileofs); q2mapsurface_t *out; int i, count; texture_t *tex; if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadShaders: funny lump size\n"); return false; } count = l->filelen / sizeof(*in); if (count < 1) { Con_Printf (CON_ERROR "CODBSP_LoadShaders: Map with no shaders\n"); return false; } mod->numtexinfo = count; out = prv->surfaces = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out)); mod->textures = plugfuncs->GMalloc(&mod->memgroup, (sizeof(texture_t*)+sizeof(mtexinfo_t)+sizeof(texture_t))*count); //+1 is 'noshader' for flares. mod->texinfo = (mtexinfo_t*)(mod->textures+count); tex = (texture_t*)(mod->texinfo+count); mod->numtextures = count; for ( i=0 ; ic.flags = LittleLong ( in->surfflags ); out->c.value = LittleLong ( in->contents ); Q_strlcpy(out->rname, in->shadername, sizeof(out->rname)); mod->texinfo[i].texture = tex+i; mod->texinfo[i].flags = prv->surfaces[i].c.flags; Q_strlcpy(mod->texinfo[i].texture->name, in->shadername, sizeof(mod->texinfo[i].texture->name)); mod->textures[i] = mod->texinfo[i].texture; } return true; } static qboolean COD1BSP_LoadLightmap(model_t *mod, qbyte *mod_base, lump_t *l) { // codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; int overbright; int bytes; size_t i, lev; qbyte *in = mod_base+l->fileofs; mod->lightmaps.width = 512; mod->lightmaps.height = 512; mod->lightmaps.prebaked = PTI_RGB8; mod->lightmaps.fmt = LM_RGB8; bytes = 3; mod->lightmaps.surfstyles = 1; //always style 0... mod->lightmaps.maxstyle = 0; mod->lightmaps.deluxemapping = false; mod->lightmaps.deluxemapping_modelspace = false; mod->lightmaps.first = 0; mod->lightmaps.count = l->filelen / (mod->lightmaps.width*mod->lightmaps.height*bytes); if (l->filelen != mod->lightmaps.count * (mod->lightmaps.width*mod->lightmaps.height*bytes)) { Con_Printf (CON_ERROR "CODBSP_LoadLighting: funny lump size\n"); return false; //err... rounded badly. } mod->lightdata = plugfuncs->GMalloc(&mod->memgroup, l->filelen); mod->lightdatasize = l->filelen; overbright = cvarfuncs->GetFloat("gl_overbright"); mod->engineflags = MDLF_NEEDOVERBRIGHT; if (overbright == 2) memcpy(mod->lightdata, in, l->filelen); else { qbyte *out = mod->lightdata; overbright = (1<<(2-overbright)); for (i = 0; i < l->filelen; i++, in++) { lev = *in * overbright; *out++ = min(255, lev); } } return true; } static qboolean COD2BSP_LoadLightmap(model_t *mod, qbyte *mod_base, lump_t *l) { //seems to be sets of 4 images (3 normalmaps and some extra discoloured one). more deluxemap than lightmap. this is not useful to us. //cod2 bundles some hlsl code, which may reveal clues. // codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; mod->lightmaps.width = 512; mod->lightmaps.height = 512; mod->lightmaps.prebaked = PTI_RGBA8; //needs glsl to use properly. mod->lightmaps.surfstyles = 1; //always style 0... mod->lightmaps.maxstyle = 0; mod->lightmaps.deluxemapping = false; //fixme: uses 4 lightmap textures at a time. mod->lightmaps.deluxemapping_modelspace = false; mod->lightmaps.first = 0; mod->lightmaps.count = 0; Con_Printf(CON_WARNING"COD2 lightmaps are not supported\n"); return true; } static qboolean COD1BSP_LoadSoupVertices (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct { vec3_t position; vec2_t tc; vec2_t lmtc; vec3_t normal; byte_vec4_t rgba; } *in = (void*)((qbyte*)mod_base + l->fileofs); size_t i, count = l->filelen / sizeof(*in); mesh_t *mesh = &prv->soupverts; if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadSoupVertices: funny lump size\n"); return false; } //allocate lots of space. stoopid separate arrays. prv->soupverts.istrifan = false; prv->soupverts.xyz_array = plugfuncs->GMalloc(&mod->memgroup, count*( sizeof(*mesh->xyz_array)+ sizeof(*mesh->normals_array)+ sizeof(*mesh->colors4b_array)+ sizeof(*mesh->st_array)+ sizeof(*mesh->lmst_array[0]))); mesh->normals_array = (void*)(mesh->xyz_array + count); mesh->colors4b_array = (void*)(mesh->normals_array + count); mesh->st_array = (void*)(mesh->colors4b_array + count); mesh->lmst_array[0] = (void*)(mesh->st_array + count); //copy it all over. for (i = 0; i < count; i++, in++) { mesh->xyz_array[i][0] = LittleFloat(in->position[0]); mesh->xyz_array[i][1] = LittleFloat(in->position[1]); mesh->xyz_array[i][2] = LittleFloat(in->position[2]); mesh->st_array[i][0] = LittleFloat(in->tc[0]); mesh->st_array[i][1] = LittleFloat(in->tc[1]); mesh->lmst_array[0][i][0] = LittleFloat(in->lmtc[0]); mesh->lmst_array[0][i][1] = LittleFloat(in->lmtc[1]); mesh->normals_array[i][0] = LittleFloat(in->normal[0]); mesh->normals_array[i][1] = LittleFloat(in->normal[1]); mesh->normals_array[i][2] = LittleFloat(in->normal[2]); mesh->colors4b_array[i][0] = in->rgba[0]; mesh->colors4b_array[i][1] = in->rgba[1]; mesh->colors4b_array[i][2] = in->rgba[2]; mesh->colors4b_array[i][3] = in->rgba[3]; } return true; } static qboolean COD2BSP_LoadSoupVertices (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct { //slightly rearranged for some reason (plus addition of tangents at the end) vec3_t position; vec3_t normal; byte_vec4_t rgba; vec2_t tc; vec2_t lmtc; vec3_t sdir; vec3_t tdir; } *in = (void*)((qbyte*)mod_base + l->fileofs); size_t i, count = l->filelen / sizeof(*in); mesh_t *mesh = &prv->soupverts; if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadSoupVertices: funny lump size\n"); return false; } //allocate lots of space. stoopid separate arrays. prv->soupverts.istrifan = false; prv->soupverts.xyz_array = plugfuncs->GMalloc(&mod->memgroup, count*( sizeof(*mesh->xyz_array)+ sizeof(*mesh->normals_array)+ sizeof(*mesh->snormals_array)+ sizeof(*mesh->tnormals_array)+ sizeof(*mesh->colors4b_array)+ sizeof(*mesh->st_array)+ sizeof(*mesh->lmst_array[0]))); mesh->normals_array = (void*)(mesh->xyz_array + count); mesh->snormals_array = (void*)(mesh->normals_array + count); mesh->tnormals_array = (void*)(mesh->snormals_array + count); mesh->colors4b_array = (void*)(mesh->tnormals_array + count); mesh->st_array = (void*)(mesh->colors4b_array + count); mesh->lmst_array[0] = (void*)(mesh->st_array + count); //copy it all over. for (i = 0; i < count; i++, in++) { mesh->xyz_array[i][0] = LittleFloat(in->position[0]); mesh->xyz_array[i][1] = LittleFloat(in->position[1]); mesh->xyz_array[i][2] = LittleFloat(in->position[2]); mesh->st_array[i][0] = LittleFloat(in->tc[0]); mesh->st_array[i][1] = LittleFloat(in->tc[1]); mesh->lmst_array[0][i][0] = LittleFloat(in->lmtc[0]); mesh->lmst_array[0][i][1] = LittleFloat(in->lmtc[1]); mesh->normals_array[i][0] = LittleFloat(in->normal[0]); mesh->normals_array[i][1] = LittleFloat(in->normal[1]); mesh->normals_array[i][2] = LittleFloat(in->normal[2]); mesh->snormals_array[i][0] = LittleFloat(in->sdir[0]); mesh->snormals_array[i][1] = LittleFloat(in->sdir[1]); mesh->snormals_array[i][2] = LittleFloat(in->sdir[2]); mesh->tnormals_array[i][0] = LittleFloat(in->tdir[0]); mesh->tnormals_array[i][1] = LittleFloat(in->tdir[1]); mesh->tnormals_array[i][2] = LittleFloat(in->tdir[2]); mesh->colors4b_array[i][0] = in->rgba[0]; mesh->colors4b_array[i][1] = in->rgba[1]; mesh->colors4b_array[i][2] = in->rgba[2]; mesh->colors4b_array[i][3] = in->rgba[2]; } return true; } static qboolean CODBSP_LoadSoupIndexes (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; unsigned short *in = (void*)((qbyte*)mod_base + l->fileofs); size_t i, count = l->filelen / sizeof(*in); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadSoupIndexes: funny lump size\n"); return false; } prv->soupverts.indexes = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*prv->soupverts.indexes)); for (i = 0; i < count; i++, in++) prv->soupverts.indexes[i] = LittleShort(*in); return true; } static qboolean CODBSP_LoadSoups (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct { unsigned short material_idx; unsigned short lightmap_idx; unsigned int vertex_offset; unsigned short vertex_count; unsigned short index_count; unsigned int index_offset; } *in = (void*)((qbyte*)mod_base + l->fileofs); struct codsoup_s *out; mesh_t *mesh; size_t j, i, count = l->filelen / sizeof(*in); unsigned int mn,mx, idx; if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadSoups: funny lump size\n"); return false; } prv->soups = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out)); mod->numsurfaces = count; mod->nummodelsurfaces = count; mod->surfaces = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*mod->surfaces) + count*sizeof(*mesh)); mesh = (void*)(mod->surfaces+count); for (i = 0; i < count; i++, in++, out++) { unsigned short tex = LittleShort(in->material_idx); unsigned short lmap = LittleShort(in->lightmap_idx); out->vertex_offset = LittleLong (in->vertex_offset); out->index_offset = LittleLong (in->index_offset); if (tex >= mod->numtexinfo) return false; for (j = 0; j < MAXCPULIGHTMAPS; j++) mod->surfaces[i].styles[j] = INVALID_LIGHTSTYLE; for (j = 0; j < MAXRLIGHTMAPS; j++) { mod->surfaces[i].vlstyles[j] = INVALID_VLIGHTSTYLE; mod->surfaces[i].lightmaptexturenums[j] = -1; } mod->surfaces[i].styles[0] = 0; mod->surfaces[i].lightmaptexturenums[0] = lmap==(unsigned short)~0u?INVALID_LIGHTSTYLE:lmap; mod->surfaces[i].texinfo = &mod->texinfo[tex]; mod->surfaces[i].mesh = &mesh[i]; mesh[i].numindexes = LittleShort(in->index_count); mesh[i].numvertexes = LittleShort(in->vertex_count); //cod2 sucks and is way out and results in horrible memory use. calculate what it should have been. mn = ~0i; mx = 0; for (j = 0; j < mesh[i].numindexes; j++) { idx = LittleLong(prv->soupverts.indexes[out->index_offset+j]); if (mx<= idx) mx = idx+1; if (mn > idx) mn = idx; } if (mx 65535) return false; out->index_fixup = mn; out->vertex_offset += mn; } return true; } static qboolean CODBSP_LoadLights (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct { //18... int type; //1=sunlight? //4=omni? //5=oriented non-spot? //7=oriented spot light? vec3_t rgb; //these values are completely fucked in most modes. :( vec3_t xyz; //actually matches a lightsource vec3_t dir; float pointnineish; //some sort of exponent? falloff? float scale; //?big float. radius? sometimes denormalised? float fov; //very fovy int naught0; //no info. could be floats. int naught1; //no info. could be floats. int naught2; //no info. could be floats. int naught3; //no info. could be floats. int naught4; //no info. could be floats. } *in = (void*)((qbyte*)mod_base + l->fileofs); struct codlight_s *out; size_t i, count = l->filelen / sizeof(*in); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadLightValues: funny lump size\n"); return false; } prv->lights = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out)); prv->numlights = count; for (i = 0; i < count; i++, in++, out++) { out->type = LittleLong(in->type); out->xyz[0] = LittleFloat(in->xyz[0]); out->xyz[1] = LittleFloat(in->xyz[1]); out->xyz[2] = LittleFloat(in->xyz[2]); out->rgb[0] = LittleFloat(in->rgb[0]); out->rgb[1] = LittleFloat(in->rgb[1]); out->rgb[2] = LittleFloat(in->rgb[2]); out->dir[0] = LittleFloat(in->dir[0]); out->dir[1] = LittleFloat(in->dir[1]); out->dir[2] = LittleFloat(in->dir[2]); out->scale = LittleFloat(in->scale); out->fov = LittleFloat(in->fov); } return true; } static qboolean CODBSP_LoadLightIndexes (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; unsigned short *in = (void*)((qbyte*)mod_base + l->fileofs), *out, v; size_t i, count = l->filelen / sizeof(*in); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadLightIndexes: funny lump size\n"); return false; } prv->lightindexes = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out)); prv->numlightindexes = count; for (i = 0; i < count; i++) { v = LittleShort(*in++); if (v == 0xffff) ; //o.O else if (v >= prv->numlights) { Con_Printf (CON_ERROR "CODBSP_LoadLightIndexes: invalid index %i\n", v); return false; } *out++ = v; } return true; } static qboolean CODBSP_LoadEntities (model_t *mod, qbyte *mod_base, lump_t *l) { //just quake-style { "field" "value" "field2" "value2" } blocks. return modfuncs->LoadEntities(mod, mod_base+l->fileofs, l->filelen); } static qboolean CODBSP_LoadPlanes (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; vec4_t *in = (void*)((qbyte*)mod_base + l->fileofs); mplane_t *out; size_t j, i, count = l->filelen / sizeof(*in); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadPlanes: funny lump size\n"); return false; } prv->planes = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out)); prv->num_planes = count; for (i = 0; i < count; i++, out++) { out->normal[0] = LittleFloat(in[i][0]); out->normal[1] = LittleFloat(in[i][1]); out->normal[2] = LittleFloat(in[i][2]); out->dist = LittleFloat(in[i][3]); out->type = PLANE_ANYZ; out->signbits = 0; for (j=0 ; j<3 ; j++) { if (out->normal[j] < 0) out->signbits |= 1<normal[j] == 1) out->type = j; } } return true; } static qboolean CODBSP_LoadBrushSides (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct codbsp_brushside_s *in = (void*)((qbyte*)mod_base + l->fileofs); size_t count = l->filelen / sizeof(*in); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadBrushSides: funny lump size\n"); return false; } prv->brushsides = in; //used elsewhere in the loader prv->num_brushsides = count; return true; } static qboolean CODBSP_LoadBrushes (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; const struct { unsigned short sides; unsigned short material; } *in = (void*)((qbyte*)mod_base + l->fileofs); const struct codbsp_brushside_s *inside = prv->brushsides; q2cbrush_t *out; q2cbrushside_t *outside; mplane_t *aplane; size_t j, i, count = l->filelen / sizeof(*in); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadBrushes: funny lump size\n"); return false; } prv->brushes = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out)); aplane = plugfuncs->GMalloc(&mod->memgroup, count*6*sizeof(*aplane)); //read the data for (i = 0, j = 0; i < count; i++, in++) { unsigned int mat = LittleShort(in->material); out[i].numsides = (unsigned short)LittleShort(in->sides); out[i].contents = prv->surfaces[mat].c.value; //is this right? seems to kinda work? feels wrong though. j += out[i].numsides; } //fix up the planes... outside = plugfuncs->GMalloc(&mod->memgroup, j*sizeof(*outside)); prv->num_brushes = count; for (i = 0, j = 0; i < count; i++, out++) { out->brushside = outside; for (j = 0; j < out->numsides; j++, inside++, outside++) { unsigned int mat = LittleLong(inside->material_idx); if (j < 6) { aplane->dist = LittleFloat(inside->dist); if (j&1) { //stored nx px ny py nz pz aplane->normal[j>>1] = 1; out->absmaxs[j>>1] = aplane->dist; } else { aplane->normal[j>>1] = -1; out->absmins[j>>1] = aplane->dist; aplane->dist *= -1; } outside->plane = aplane++; } else outside->plane = prv->planes + LittleLong(inside->plane); outside->surface = &prv->surfaces[mat]; } } return true; } /*static qboolean CODBSP_LoadLeafBrushes (model_t *mod, qbyte *mod_base, lump_t *l) { //we don't really care about this, as we're using our BIH stuff for collisions instead. // codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct { int a; } *in = (void*)((qbyte*)mod_base + l->fileofs); size_t i, count = l->filelen / sizeof(*in); int highest=0; int lowest=0; if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadLeafBrushes: funny lump size\n"); return false; } for (i = 0; i < count; i++, in++) { if (lowest > in->a) lowest = in->a; if (highest < in->a) highest = in->a; // Con_Printf("%i: %i\n", (int)i, in->a); } Con_Printf("leaf brushes: %i - %i\n", lowest, highest); return true; }*/ static qboolean CODBSP_LoadPatchVertexes (model_t *mod, qbyte *mod_base, lump_t *l) { //for collision codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; const vec3_t *in = (void*)((qbyte*)mod_base + l->fileofs); vecV_t *out; size_t i, count = l->filelen / sizeof(*in); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadPatchVertexes: funny lump size\n"); return false; } prv->patchvertexes = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out)); prv->numpatchvertexes = count; for (i = 0; i < count; i++) { out[i][0] = LittleFloat(in[i][0]); out[i][1] = LittleFloat(in[i][1]); out[i][2] = LittleFloat(in[i][2]); } return true; } static qboolean CODBSP_LoadPatchIndexes (model_t *mod, qbyte *mod_base, lump_t *l) { //for collision codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; unsigned short *in = (void*)((qbyte*)mod_base + l->fileofs); index_t *out; size_t i, count = l->filelen / sizeof(*in); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadPatchIndexes: funny lump size\n"); return false; } prv->patchindexes = out = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*out)); prv->numpatchindexes = count; for (i = 0; i < count; i++) { *out++ = (unsigned short)LittleShort(*in++); } return true; } static qboolean CODBSP_LoadPatchCollision (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct codpatch_s *in = (void*)((qbyte*)mod_base + l->fileofs); size_t i, count = l->filelen / sizeof(*in); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadPatchesCollision: funny lump size\n"); return false; } prv->patches = in; prv->numpatches = count; for (i = 0; i < count; i++, in++) { if (in->mode==0) ;//Con_Printf("p%i: %s ?+%i*%-4i v%i+%i %i\n", (int)i, mod->textures[in->mat]->name, in->mode0.w, in->mode0.h, in->mode0.firstvert,in->mode0.w*in->mode0.h, in->mode0.unknown); else if (in->mode==1) ;//Con_Printf("s%i: %s %4i+%-4i v%i+%i\n", (int)i, mod->textures[in->mat]->name, in->mode1.firstidx,in->mode1.numidx, in->mode1.firstvert,in->mode1.numverts); else { Con_Printf("?%i: %s %i ?!?!?!?!?\n", (int)i, mod->textures[in->mat]->name, in->mode); return false; //nope. } } return true; } static qboolean CODBSP_LoadLeafPatches (model_t *mod, qbyte *mod_base, lump_t *l) { //for collision codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; unsigned int *in = (void*)((qbyte*)mod_base + l->fileofs); size_t count = l->filelen / sizeof(*in); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadLeafPatches: funny lump size\n"); return false; } prv->leafpatches = in; prv->numleafpatches = count; return true; } /* static qboolean CODBSP_LoadAABBs (model_t *mod, qbyte *mod_base, lump_t *l) { // codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct { int a; //surfaceindex (submodel0 only, so stops short of the full range implied by the lump's count) int b; //numsurfaces int c; //some sort of offset? } *in = (void*)((qbyte*)mod_base + l->fileofs); size_t i, count = l->filelen / sizeof(*in); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadAABBs: funny lump size\n"); return false; } for (i = 0; i < count; i++, in++) { // Con_Printf("%i: %i+%i %i\n", (int)i, in->a, in->b, in->c); } return true; } static qboolean CODBSP_LoadCells (model_t *mod, qbyte *mod_base, lump_t *l) { // codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct { vec3_t mins; vec3_t maxs; int aabtree; //lump 16ish? CODLUMP_AABBTREES int firstportal; //lump 18? CODLUMP_PORTALS int numportals; int firstcullgroupindex; //lump 10? CODLUMP_CULLGROUPINDEXES int numcullgroupindexes; int firstoccluderindex; int numoccluders; } *in = (void*)((qbyte*)mod_base + l->fileofs); size_t i, count = l->filelen / sizeof(*in); if (l->filelen % sizeof(*in)) { Con_Printf (CON_ERROR "CODBSP_LoadCells: funny lump size\n"); return false; } for (i = 0; i < count; i++, in++) { Con_Printf("%i: [%f %f %f] [%f %f %f] %i %i+%i %i+%i %i+%i\n", (int)i, in->mins[0], in->mins[1], in->mins[2], in->maxs[0], in->maxs[1], in->maxs[2], in->aabtree, in->firstportal, in->numportals, in->firstcullgroupindex, in->numcullgroupindexes, in->firstoccluderindex, in->numoccluders); } return true; }*/ static qboolean CODBSP_LoadLeafs (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct codinlinemodel_s{ int cluster; //-1 for invalid int area; //-1 for invalid unsigned int firstleafsurfs; unsigned int numsurfaces; unsigned int firstleafbrushes; unsigned int numbrushes; int cell; //-1 for invalid unsigned int firstlightindex; unsigned int numlightindexes; } *in = (void*)((qbyte*)mod_base + l->fileofs); size_t count = l->filelen / sizeof(*in); size_t i; if (l->filelen % sizeof(*in) || count < 1) { Con_Printf (CON_ERROR "CODBSP_LoadLeafs: funny lump size\n"); return false; } prv->leaf = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*prv->leaf)); prv->numleafs = count; for (i = 0; i < count; i++, in++) { prv->leaf[i].cluster = LittleLong(in->cluster); prv->leaf[i].area = LittleLong(in->area); prv->leaf[i].firstlightindex = LittleLong(in->firstlightindex); prv->leaf[i].numlightindexes = LittleLong(in->numlightindexes); } return true; } static qboolean CODBSP_LoadNodes (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; struct codinlinemodel_s{ unsigned int plane; int child[2]; //negative for leaf ivec3_t mins; ivec3_t maxs; } *in = (void*)((qbyte*)mod_base + l->fileofs); size_t count = l->filelen / sizeof(*in); size_t i; if (l->filelen % sizeof(*in) || count < 1) { Con_Printf (CON_ERROR "CODBSP_LoadNodes: funny lump size\n"); return false; } prv->nodes = plugfuncs->GMalloc(&mod->memgroup, count*sizeof(*prv->nodes)); prv->numnodes = count; for (i = 0; i < count; i++, in++) { prv->nodes[i].plane = &prv->planes[LittleLong(in->plane)]; prv->nodes[i].childnum[0] = LittleLong(in->child[0]); prv->nodes[i].childnum[1] = LittleLong(in->child[1]); prv->nodes[i].mins[0] = LittleLong(in->mins[0]); prv->nodes[i].mins[1] = LittleLong(in->mins[1]); prv->nodes[i].mins[2] = LittleLong(in->mins[2]); prv->nodes[i].maxs[0] = LittleLong(in->maxs[0]); prv->nodes[i].maxs[1] = LittleLong(in->maxs[1]); prv->nodes[i].maxs[2] = LittleLong(in->maxs[2]); } return true; } static qboolean CODBSP_LoadVisibility (model_t *mod, qbyte *mod_base, lump_t *l) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; qbyte *in = (void*)((qbyte*)mod_base + l->fileofs); if (!l->filelen) { //unvised. mod->numclusters = 0; mod->pvsbytes = 0; prv->pvsdata = NULL; return true; } if (l->filelen < 8) { Con_Printf (CON_ERROR "CODBSP_LoadVisibility: funny lump size\n"); return false; } mod->numclusters = LittleLong(((int*)in)[0]); mod->pvsbytes = LittleLong(((int*)in)[1]); if (l->filelen != 8 + mod->numclusters*mod->pvsbytes) { Con_Printf (CON_ERROR "CODBSP_LoadVisibility: funny lump size\n"); return false; } prv->pvsdata = plugfuncs->GMalloc(&mod->memgroup, l->filelen-8); memcpy(prv->pvsdata, in+8, mod->numclusters*mod->pvsbytes); return true; } void AddPointToBounds (const vec3_t v, vec3_t mins, vec3_t maxs) { int i; vec_t val; for (i=0 ; i<3 ; i++) { val = v[i]; if (val < mins[i]) mins[i] = val; if (val > maxs[i]) maxs[i] = val; } } static void CODBSP_BuildBIH (model_t *mod, size_t firstbrush, size_t numbrushes, size_t firstleafpatch, size_t numleafpatches) { codbspinfo_t *prv = (codbspinfo_t*)mod->meshinfo; size_t numtriangles = 0; size_t numquads = 0; index_t *silly; struct bihleaf_s *bihleaf, *l; size_t i, j, lp; qbyte *patches = memset(alloca((prv->numpatches+7)>>3), 0, (prv->numpatches+7)>>3); for (i = firstleafpatch; i < numleafpatches; i++) { //de-dupe them... *sigh* lp = LittleLong(prv->leafpatches[i]); if (lp < prv->numpatches) patches[lp>>3] |= 1u<<(lp&7); } for (i = 0; i < prv->numpatches; i++) { if (patches[i>>3] & (1u<<(i&7))) { if (prv->patches[i].mode) numtriangles+=(unsigned short)LittleShort(prv->patches[i].mode1.numidx)/3; else numquads+=(unsigned int)((unsigned short)LittleShort(prv->patches[i].mode0.w)-1) * ((unsigned short)LittleShort(prv->patches[i].mode0.h)-1); } } bihleaf = l = plugfuncs->Malloc(sizeof(*bihleaf)*(numbrushes+numtriangles+numquads*2)); silly = plugfuncs->GMalloc(&mod->memgroup, sizeof(*silly)*6*numquads); //now we have enough storage, spit them out providing bounds info. for (i = 0; i < prv->numpatches; i++) { if (patches[i>>3] & (1u<<(i&7))) { if (prv->patches[i].mode) { unsigned int numidx = (unsigned short)LittleShort(prv->patches[i].mode1.numidx); for (j = 0; j < numidx; j+=3) { vec_t *v1,*v2,*v3; l->type = BIH_TRIANGLE; l->data.contents = prv->surfaces[LittleLong(prv->patches[i].mat)].c.value; l->data.tri.xyz = prv->patchvertexes + (unsigned int)LittleLong(prv->patches[i].mode1.firstvert); l->data.tri.indexes = prv->patchindexes + (unsigned int)LittleLong(prv->patches[i].mode1.firstidx) + j; v1 = l->data.tri.xyz[l->data.tri.indexes[0]]; v2 = l->data.tri.xyz[l->data.tri.indexes[1]]; v3 = l->data.tri.xyz[l->data.tri.indexes[2]]; VectorCopy(v1, l->mins); VectorCopy(v1, l->maxs); AddPointToBounds(v2, l->mins, l->maxs); AddPointToBounds(v3, l->mins, l->maxs); l++; } } else { unsigned int w = (unsigned short)LittleShort(prv->patches[i].mode0.w); unsigned int h = (unsigned short)LittleShort(prv->patches[i].mode0.h); unsigned int x, y; for (y = 0; y < h-1; y++) for (x = 0; x < w-1; x++) { const vec_t *v1,*v2,*v3; silly[0] = x+y*w; silly[1] = silly[0]+1; silly[2] = silly[0]+w; silly[3] = silly[1]; silly[4] = silly[1]+w; silly[5] = silly[2]; l->type = BIH_TRIANGLE; l->data.contents = FTECONTENTS_SOLID; //prv->surfaces[LittleLong(prv->patches[i].mat)].c.value; l->data.tri.xyz = prv->patchvertexes + (unsigned int)LittleLong(prv->patches[i].mode0.firstvert); l->data.tri.indexes = silly; v1 = l->data.tri.xyz[l->data.tri.indexes[0]]; v2 = l->data.tri.xyz[l->data.tri.indexes[1]]; v3 = l->data.tri.xyz[l->data.tri.indexes[2]]; VectorCopy(v1, l->mins); VectorCopy(v1, l->maxs); AddPointToBounds(v2, l->mins, l->maxs); AddPointToBounds(v3, l->mins, l->maxs); l++; silly+=3; l->type = BIH_TRIANGLE; l->data.contents = FTECONTENTS_SOLID; //prv->surfaces[LittleLong(prv->patches[i].mat)].c.value; l->data.tri.xyz = prv->patchvertexes + (unsigned int)LittleLong(prv->patches[i].mode0.firstvert); l->data.tri.indexes = silly; v1 = l->data.tri.xyz[l->data.tri.indexes[0]]; v2 = l->data.tri.xyz[l->data.tri.indexes[1]]; v3 = l->data.tri.xyz[l->data.tri.indexes[2]]; VectorCopy(v1, l->mins); VectorCopy(v1, l->maxs); AddPointToBounds(v2, l->mins, l->maxs); AddPointToBounds(v3, l->mins, l->maxs); l++; silly+=3; } } } } //now we have enough storage, spit them out providing bounds info. for (i = 0; i < numbrushes; i++) { q2cbrush_t *b = &prv->brushes[firstbrush+i]; l->type = BIH_BRUSH; l->data.brush = b; l->data.contents = b->contents; VectorCopy(b->absmins, l->mins); VectorCopy(b->absmaxs, l->maxs); l++; } modfuncs->BIH_Build(mod, bihleaf, l-bihleaf); plugfuncs->Free(bihleaf); } static qboolean CODBSP_LoadInlineModels (model_t *wmod, qbyte *mod_base, lump_t *l) { // codbspinfo_t *prv = (codbspinfo_t*)wmod->meshinfo; struct codinlinemodel_s{ vec3_t mins; vec3_t maxs; unsigned int firstsurf; unsigned int numsurfs; unsigned int firstleafpatch; //seems to match lump 23, unsigned int numleafpatches; unsigned int firstbrush; unsigned int numbrushes; } *in = (void*)((qbyte*)mod_base + l->fileofs); size_t count = l->filelen / sizeof(*in); size_t i, j; if (l->filelen % sizeof(*in) || count < 1) { Con_Printf (CON_ERROR "CODBSP_LoadInlineModels: funny lump size\n"); return false; } for (i = 0; i < count; i++, in++) { char name[MAX_QPATH]; model_t *mod; if (i) { //submodels Q_snprintfz (name, sizeof(name), "*%u:%s", (unsigned int)i, wmod->publicname); mod = modfuncs->BeginSubmodelLoad(name); *mod = *wmod; mod->archive = NULL; mod->entities_raw = NULL; mod->submodelof = wmod; Q_strlcpy(mod->publicname, name, sizeof(mod->publicname)); Q_snprintfz (mod->name, sizeof(mod->name), "*%u:%s", (unsigned int)i, wmod->name); memset(&mod->memgroup, 0, sizeof(mod->memgroup)); } else //handle the world model here too mod = wmod; mod->hulls[0].firstclipnode = i?-1:0; for (j = 1; j < countof(mod->hulls); j++) mod->hulls[j].firstclipnode = -1; //no nodes, mod->nodes = mod->rootnode = NULL; mod->leafs = NULL; mod->nummodelsurfaces = LittleLong(in->numsurfs); mod->firstmodelsurface = LittleLong(in->firstsurf); VectorCopy(in->mins, mod->mins); VectorCopy(in->maxs, mod->maxs); CODBSP_BuildBIH(mod, LittleLong(in->firstbrush), LittleLong(in->numbrushes), LittleLong(in->firstleafpatch), LittleLong(in->numleafpatches)); memset(&mod->batches, 0, sizeof(mod->batches)); mod->vbos = NULL; #ifdef HAVE_CLIENT // mod->radius = RadiusFromBounds (mod->mins, mod->maxs); // if (qrenderer != QR_NONE) { builddata_t *bd = plugfuncs->Malloc(sizeof(*bd)); bd->buildfunc = CODBSP_BuildSurfMesh; bd->paintlightmaps = false; //q3like with prebaked lightmaps. threadfuncs->AddWork(WG_MAIN, CODBSP_GenerateMaterials, mod, bd, 0, 0); } #endif if (mod != wmod) modfuncs->EndSubmodelLoad(mod, MLS_LOADED); } return true; } static qboolean QDECL Mod_LoadCodBSP(struct model_s *mod, void *buffer, size_t fsize) { codbspinfo_t *prv; qboolean okay = true; int i; int ver = LittleLong(((int*)buffer)[1]); lump_t lumps[max((int)COD1LUMP_COUNT, (int)COD2LUMP_COUNT)]; mod->fromgame = fg_new; #ifdef HAVE_SERVER mod->funcs.FatPVS = CODBSP_FatPVS; mod->funcs.EdictInFatPVS = CODBSP_EdictInFatPVS; mod->funcs.FindTouchedLeafs = CODBSP_FindTouchedLeafs; #endif #ifdef HAVE_CLIENT mod->funcs.LightPointValues = CODBSP_LightPointValues; // mod->funcs.StainNode = CODBSP_StainNode; // mod->funcs.MarkLights = CODBSP_MarkLights; // mod->funcs.GenerateShadowMesh = CODBSP_GenerateShadowMesh; #endif mod->funcs.ClusterPVS = CODBSP_ClusterPVS; // mod->funcs.ClusterPHS = CODBSP_ClusterPHS; mod->funcs.ClusterForPoint = CODBSP_ClusterForPoint; // mod->funcs.SetAreaPortalState = CODBSP_SetAreaPortalState; // mod->funcs.AreasConnected = CODBSP_AreasConnected; // mod->funcs.LoadAreaPortalBlob = CODBSP_LoadAreaPortalBlob; // mod->funcs.SaveAreaPortalBlob = CODBSP_SaveAreaPortalBlob; mod->funcs.PrepareFrame = CODBSP_PrepareFrame; mod->funcs.InfoForPoint = CODBSP_InfoForPoint; if (ver == COD1BSP_VERSION) { memcpy(lumps, (char*)buffer+8, sizeof(*lumps)*COD1LUMP_COUNT); for (i = 0; i < COD1LUMP_COUNT; i++) { int ffs = lumps[i].filelen; //ffs lumps[i].filelen = LittleLong(lumps[i].fileofs); lumps[i].fileofs = LittleLong(ffs); if (lumps[i].filelen && lumps[i].fileofs+(size_t)lumps[i].filelen > fsize) { Con_Printf(CON_ERROR"Truncated BSP file\n"); return false; } } mod->meshinfo = prv = plugfuncs->GMalloc(&mod->memgroup, sizeof(codbspinfo_t)); prv->codbspver = ver; //basic trisoup info okay = okay && CODBSP_LoadShaders(mod, buffer, &lumps[COD1LUMP_MATERIALS]); okay = okay && COD1BSP_LoadLightmap(mod, buffer, &lumps[COD1LUMP_LIGHTMAPS]); okay = okay && COD1BSP_LoadSoupVertices(mod, buffer, &lumps[COD1LUMP_SOUPVERTS]); okay = okay && CODBSP_LoadSoupIndexes(mod, buffer, &lumps[COD1LUMP_SOUPINDEXES]); okay = okay && CODBSP_LoadSoups(mod, buffer, &lumps[COD1LUMP_SOUPS]); //gamecode needs to know what's around okay = okay && CODBSP_LoadLights(mod, buffer, &lumps[COD1LUMP_LIGHTS]); okay = okay && CODBSP_LoadLightIndexes(mod, buffer, &lumps[COD1LUMP_LIGHTINDEXES]); okay = okay && CODBSP_LoadEntities(mod, buffer, &lumps[COD1LUMP_ENTITIES]); //basic collision okay = okay && CODBSP_LoadPlanes(mod, buffer, &lumps[COD1LUMP_PLANES]); okay = okay && CODBSP_LoadBrushSides(mod, buffer, &lumps[COD1LUMP_BRUSHSIDES]); okay = okay && CODBSP_LoadBrushes(mod, buffer, &lumps[COD1LUMP_BRUSHES]); //okay = okay && CODBSP_LoadLeafBrushes(mod, buffer, &lumps[COD1LUMP_LEAFBRUSHES]); //patch collision... okay = okay && CODBSP_LoadPatchVertexes(mod, buffer, &lumps[COD1LUMP_COLLISIONVERTS]); okay = okay && CODBSP_LoadPatchIndexes(mod, buffer, &lumps[COD1LUMP_COLLISIONINDEXES]); okay = okay && CODBSP_LoadPatchCollision(mod, buffer, &lumps[COD1LUMP_PATCHCOLLISION]); okay = okay && CODBSP_LoadLeafPatches(mod, buffer, &lumps[COD1LUMP_LEAFPATCHES]); //seems like cod is a portal engine? //okay = okay && CODBSP_LoadAABBs(mod, buffer, &lumps[COD1LUMP_AABBTREES]); //okay = okay && CODBSP_LoadCells(mod, buffer, &lumps[COD1LUMP_CELLS]); //but we still have pvs with its clusters+areas that are node based (also required to determine which 'cell' we're inside). okay = okay && CODBSP_LoadVisibility(mod, buffer, &lumps[COD1LUMP_VISIBILITY]); okay = okay && CODBSP_LoadLeafs(mod, buffer, &lumps[COD1LUMP_LEAFS]); okay = okay && CODBSP_LoadNodes(mod, buffer, &lumps[COD1LUMP_NODES]); okay = okay && CODBSP_LoadInlineModels(mod, buffer, &lumps[COD1LUMP_MODELS]); } else if (ver == COD2BSP_VERSION) { memcpy(lumps, (char*)buffer+8, sizeof(*lumps)*COD2LUMP_COUNT); for (i = 0; i < COD2LUMP_COUNT; i++) { int ffs = lumps[i].filelen; //ffs lumps[i].filelen = LittleLong(lumps[i].fileofs); lumps[i].fileofs = LittleLong(ffs); if (lumps[i].filelen && lumps[i].fileofs+(size_t)lumps[i].filelen > fsize) { Con_Printf(CON_ERROR"Truncated BSP file\n"); return false; } } mod->meshinfo = prv = plugfuncs->GMalloc(&mod->memgroup, sizeof(codbspinfo_t)); prv->codbspver = ver; //basic trisoup info okay = okay && CODBSP_LoadShaders(mod, buffer, &lumps[COD2LUMP_MATERIALS]); okay = okay && COD2BSP_LoadLightmap(mod, buffer, &lumps[COD2LUMP_LIGHTMAPS]); okay = okay && COD2BSP_LoadSoupVertices(mod, buffer, &lumps[COD2LUMP_SOUPVERTS]); okay = okay && CODBSP_LoadSoupIndexes(mod, buffer, &lumps[COD2LUMP_SOUPINDEXES]); okay = okay && CODBSP_LoadSoups(mod, buffer, &lumps[COD2LUMP_SOUPS]); //gamecode needs to know what's around okay = okay && CODBSP_LoadEntities(mod, buffer, &lumps[COD2LUMP_ENTITIES]); //basic collision okay = okay && CODBSP_LoadPlanes(mod, buffer, &lumps[COD2LUMP_PLANES]); okay = okay && CODBSP_LoadBrushSides(mod, buffer, &lumps[COD2LUMP_BRUSHSIDES]); okay = okay && CODBSP_LoadBrushes(mod, buffer, &lumps[COD2LUMP_BRUSHES]); //okay = okay && CODBSP_LoadLeafBrushes(mod, buffer, &lumps[COD2LUMP_LEAFBRUSHES]); //patch collision... // okay = okay && CODBSP_LoadPatchVertexes(mod, buffer, &lumps[COD1LUMP_COLLISIONVERTS]); // okay = okay && CODBSP_LoadPatchIndexes(mod, buffer, &lumps[COD1LUMP_COLLISIONINDEXES]); // okay = okay && CODBSP_LoadPatchCollision(mod, buffer, &lumps[COD2LUMP_PATCHCOLLISION]); // okay = okay && CODBSP_LoadLeafPatches(mod, buffer, &lumps[COD2LUMP_LEAFPATCHES]); //seems like cod is a portal engine? //okay = okay && CODBSP_LoadAABBs(mod, buffer, &lumps[COD2LUMP_AABBTREES]); //okay = okay && CODBSP_LoadCells(mod, buffer, &lumps[COD2LUMP_CELLS]); //but we still have pvs with its clusters+areas that are node based (also required to determine which 'cell' we're inside). okay = okay && CODBSP_LoadVisibility(mod, buffer, &lumps[COD2LUMP_VISIBILITY]); okay = okay && CODBSP_LoadLeafs(mod, buffer, &lumps[COD2LUMP_LEAFS]); okay = okay && CODBSP_LoadNodes(mod, buffer, &lumps[COD2LUMP_NODES]); okay = okay && CODBSP_LoadInlineModels(mod, buffer, &lumps[COD2LUMP_MODELS]); } else { Con_Printf(CON_ERROR"Bad COD Version...\n"); //should have already been checked, so this ain't possible. okay = false; } return okay; } qboolean CODBSP_Init(void) { filefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs)); modfuncs = plugfuncs->GetEngineInterface(plugmodfuncs_name, sizeof(*modfuncs)); threadfuncs = plugfuncs->GetEngineInterface(plugthreadfuncs_name, sizeof(*threadfuncs)); if (modfuncs && modfuncs->version != MODPLUGFUNCS_VERSION) modfuncs = NULL; if (modfuncs && filefuncs && threadfuncs) { modfuncs->RegisterModelFormatMagic("CoD(1) Maps", "IBSP\x3b\0\0\0",8, Mod_LoadCodBSP); modfuncs->RegisterModelFormatMagic("CoD2 Maps", "IBSP\x04\0\0\0",8, Mod_LoadCodBSP); return true; } return false; }