1863 lines
No EOL
58 KiB
C
1863 lines
No EOL
58 KiB
C
// 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 ( ; i<count ; i++)
|
|
{
|
|
for (j=0 ; j<i ; j++)
|
|
if (leafs[i] == leafs[j])
|
|
break;
|
|
if (j != i)
|
|
continue; // already have the cluster we want
|
|
mod->funcs.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 ; i<num_leafs ; i++)
|
|
{
|
|
clusters[i] = leafs[i]->cluster; //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 ; i<num_leafs ; i++)
|
|
{
|
|
if (clusters[i] == -1)
|
|
continue; // not a visible leaf
|
|
for (j=0 ; j<i ; j++)
|
|
if (clusters[j] == clusters[i])
|
|
break;
|
|
if (j == i)
|
|
{
|
|
if (ent->num_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 ; i<count ; i++, in++, out++ )
|
|
{
|
|
out->c.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<mn)
|
|
mx=mn=0; //erk?
|
|
mesh[i].numvertexes = mx-mn;
|
|
if (mesh[i].numvertexes > 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<<j;
|
|
else if (out->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;
|
|
} |