1
0
Fork 0
forked from fte/fteqw
fteqw/engine/common/gl_q2bsp.c

5105 lines
115 KiB
C
Raw Normal View History

#include "quakedef.h"
#ifdef RGLQUAKE
#include "glquake.h"
#include "shader.h"
#endif
#define MAX_Q3MAP_INDICES 0x80000
#define MAX_Q3MAP_VERTEXES 0x80000
#define MAX_Q3MAP_BRUSHSIDES 0x30000
#define MAX_CM_BRUSHSIDES (MAX_Q3MAP_BRUSHSIDES << 1)
#define MAX_CM_BRUSHES (MAX_Q2MAP_BRUSHES << 1)
#define MAX_CM_PATCH_VERTS (4096)
#define MAX_CM_FACES (MAX_Q2MAP_FACES)
#define MAX_CM_PATCHES (0x10000)
#define MAX_CM_LEAFFACES (MAX_Q2MAP_LEAFFACES)
#define MAX_CM_AREAS MAX_Q2MAP_AREAS
#define Q3SURF_NODRAW 0x80 // don't generate a drawsurface at all
#define Q3SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes
#define Q3SURF_NONSOLID 0x4000 // don't collide against curves with this set
#if Q3SURF_NODRAW != SURF_NODRAW
#error "nodraw isn't constant"
#endif
//these are in model.c (or gl_model.c)
void GLMod_LoadVertexes (lump_t *l);
void GLMod_LoadEdges (lump_t *l);
void GLMod_LoadMarksurfaces (lump_t *l);
void GLMod_LoadSurfedges (lump_t *l);
void GLMod_LoadLighting (lump_t *l);
void SWMod_LoadVertexes (lump_t *l);
void SWMod_LoadEdges (lump_t *l);
void SWMod_LoadMarksurfaces (lump_t *l);
void SWMod_LoadSurfedges (lump_t *l);
void SWMod_LoadLighting (lump_t *l);
void Q2BSP_SetHullFuncs(hull_t *hull);
qbyte areabits[MAX_Q2MAP_AREAS/8];
extern char loadname[32];
extern model_t *loadmodel;
float RadiusFromBounds (vec3_t mins, vec3_t maxs)
{
int i;
vec3_t corner;
for (i=0 ; i<3 ; i++)
{
corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);
}
return Length (corner);
}
void CalcSurfaceExtents (msurface_t *s)
{
float mins[2], maxs[2], val;
int i,j, e;
mvertex_t *v;
mtexinfo_t *tex;
int bmins[2], bmaxs[2];
mins[0] = mins[1] = 999999;
maxs[0] = maxs[1] = -99999;
tex = s->texinfo;
for (i=0 ; i<s->numedges ; i++)
{
e = loadmodel->surfedges[s->firstedge+i];
if (e >= 0)
v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
else
v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
for (j=0 ; j<2 ; j++)
{
val = v->position[0] * tex->vecs[j][0] +
v->position[1] * tex->vecs[j][1] +
v->position[2] * tex->vecs[j][2] +
tex->vecs[j][3];
if (val < mins[j])
mins[j] = val;
if (val > maxs[j])
maxs[j] = val;
}
}
for (i=0 ; i<2 ; i++)
{
bmins[i] = floor(mins[i]/16);
bmaxs[i] = ceil(maxs[i]/16);
s->texturemins[i] = bmins[i] * 16;
s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
// if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 ) //q2 uses 512. probably for skys.
// Sys_Error ("Bad surface extents");
}
}
void AddPointToBounds (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;
}
}
void ClearBounds (vec3_t mins, vec3_t maxs)
{
mins[0] = mins[1] = mins[2] = 99999;
maxs[0] = maxs[1] = maxs[2] = -99999;
}
#ifdef Q2BSPS
qbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out);
#ifdef SERVERONLY
#define Host_Error SV_Error
#endif
extern model_t *loadmodel;
extern qbyte *mod_base;
unsigned char d_q28to24table[1024];
/*
typedef struct q2csurface_s
{
char name[16];
int flags;
int value;
} q2csurface_t;
*/
typedef struct q2mapsurface_s // used internally due to name len probs //ZOID
{
q2csurface_t c;
char rname[32];
} q2mapsurface_t;
typedef struct cmodel_s
{
vec3_t mins, maxs;
vec3_t origin; // for sounds or lights
int headnode;
int numsurfaces;
int firstsurface;
int firstbrush; //q3 submodels are considered small enough that you will never need to walk any sort of tree.
int num_brushes;//the brushes are checked instead.
} q2cmodel_t;
typedef struct
{
mplane_t *plane;
q2mapsurface_t *surface;
} q2cbrushside_t;
typedef struct
{
int contents;
int numsides;
int firstbrushside;
int checkcount; // to avoid repeated testings
} q2cbrush_t;
typedef struct
{
int numareaportals;
int firstareaportal;
int floodnum; // if two areas have equal floodnums, they are connected
int floodvalid;
} q2carea_t;
typedef struct
{
int numareaportals[MAX_CM_AREAS];
} q3carea_t;
typedef struct
{
vec3_t absmins, absmaxs;
int numbrushes;
q2cbrush_t *brushes;
q2mapsurface_t *surface;
int checkcount; // to avoid repeated testings
} q3cpatch_t;
typedef struct
{
int facetype;
int numverts;
int firstvert;
int shadernum;
int patch_cp[2];
} q3cface_t;
int checkcount;
//FIXME: Unlimit these.
char map_name[MAX_QPATH];
int numbrushsides;
q2cbrushside_t map_brushsides[MAX_Q2MAP_BRUSHSIDES];
int numtexinfo;
q2mapsurface_t map_surfaces[MAX_Q2MAP_TEXINFO];
int numplanes;
mplane_t map_planes[MAX_Q2MAP_PLANES+6]; // extra for box hull
int numnodes;
mnode_t map_nodes[MAX_Q2MAP_NODES+6]; // extra for box hull
int numleafs = 1; // allow leaf funcs to be called without a map
mleaf_t map_leafs[MAX_Q2MAP_LEAFS];
int emptyleaf;
int numleafbrushes;
int map_leafbrushes[MAX_Q2MAP_LEAFBRUSHES];
int numcmodels;
q2cmodel_t map_cmodels[MAX_Q2MAP_MODELS];
int numbrushes;
q2cbrush_t map_brushes[MAX_Q2MAP_BRUSHES];
int numvisibility;
qbyte map_visibility[MAX_Q2MAP_VISIBILITY];
q2dvis_t *map_q2vis = (q2dvis_t *)map_visibility;
q3dvis_t *map_q3pvs = (q3dvis_t *)map_visibility;
qbyte map_hearability[MAX_Q2MAP_VISIBILITY];
q3dvis_t *map_q3phs = (q3dvis_t *)map_hearability;
int numentitychars;
char map_entitystring[MAX_Q2MAP_ENTSTRING];
int numareas = 1;
q2carea_t map_q2areas[MAX_Q2MAP_AREAS];
q3carea_t map_q3areas[MAX_CM_AREAS];
int numareaportals;
q2dareaportal_t map_areaportals[MAX_Q2MAP_AREAPORTALS];
q3cpatch_t map_patches[MAX_CM_PATCHES];
int numpatches;
int map_leafpatches[MAX_CM_LEAFFACES];
int numleafpatches;
int numclusters = 1;
q2mapsurface_t nullsurface;
int floodvalid;
qbyte portalopen[MAX_Q2MAP_AREAPORTALS]; //memset will work if it's a qbyte, really it should be a qboolean
static int mapisq3;
cvar_t map_noareas = {"map_noareas", "1"}; //1 for lack of mod support.
cvar_t map_noCurves = {"map_noCurves", "0", NULL, CVAR_CHEAT};
cvar_t map_autoopenportals = {"map_autoopenportals", "1"}; //1 for lack of mod support.
cvar_t r_subdivisions = {"r_subdivisions", "1"};
int CM_NumInlineModels (void);
q2cmodel_t *CM_InlineModel (char *name);
void CM_InitBoxHull (void);
void FloodAreaConnections (void);
int c_pointcontents;
int c_traces, c_brush_traces;
vec4_t *map_verts; //3points + pad (I'm not entirly sure of the need for the pad, maybe it's just faster)
int numvertexes;
vec2_t *map_vertstmexcoords;
vec2_t *map_vertlstmexcoords;
byte_vec4_t *map_colors_array;
vec3_t *map_normals_array;
#ifdef Q3SHADERS
typedef struct {
char shader[MAX_QPATH];
int brushNum;
int visibleSide; // the brush side that ray tests need to clip against (-1 == none)
} dfog_t;
mfog_t *map_fogs;
int map_numfogs;
#endif
q3cface_t *map_faces;
int numfaces;
index_t *map_surfindexes;
int map_numsurfindexes;
int *map_leaffaces;
int numleaffaces;
int PlaneTypeForNormal ( vec3_t normal )
{
vec_t ax, ay, az;
// NOTE: should these have an epsilon around 1.0?
if ( normal[0] >= 1.0)
return PLANE_X;
if ( normal[1] >= 1.0 )
return PLANE_Y;
if ( normal[2] >= 1.0 )
return PLANE_Z;
ax = fabs( normal[0] );
ay = fabs( normal[1] );
az = fabs( normal[2] );
if ( ax >= ay && ax >= az )
return PLANE_ANYX;
if ( ay >= ax && ay >= az )
return PLANE_ANYY;
return PLANE_ANYZ;
}
void CategorizePlane ( mplane_t *plane )
{
int i;
plane->signbits = 0;
plane->type = PLANE_ANYZ;
for (i = 0; i < 3; i++)
{
if (plane->normal[i] < 0)
plane->signbits |= 1<<i;
if (plane->normal[i] == 1.0f)
plane->type = i;
}
plane->type = PlaneTypeForNormal(plane->normal);
}
void PlaneFromPoints ( vec3_t verts[3], mplane_t *plane )
{
vec3_t v1, v2;
VectorSubtract( verts[1], verts[0], v1 );
VectorSubtract( verts[2], verts[0], v2 );
CrossProduct( v2, v1, plane->normal );
VectorNormalize( plane->normal );
plane->dist = DotProduct( verts[0], plane->normal );
}
qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_t maxs2)
{
return (mins1[0] <= maxs2[0] && mins1[1] <= maxs2[1] && mins1[2] <= maxs2[2] &&
maxs1[0] >= mins2[0] && maxs1[1] >= mins2[1] && maxs1[2] >= mins2[2]);
}
#define VectorAvg(a,b,c) ((c)[0]=((a)[0]+(b)[0])*0.5f,(c)[1]=((a)[1]+(b)[1])*0.5f, (c)[2]=((a)[2]+(b)[2])*0.5f)
#define Vector4Copy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3])
#define Vector4Scale(in,scale,out) ((out)[0]=(in)[0]*scale,(out)[1]=(in)[1]*scale,(out)[2]=(in)[2]*scale,(out)[3]=(in)[3]*scale)
#define Vector4Add(a,b,c) ((c)[0]=(((a[0])+(b[0]))),(c)[1]=(((a[1])+(b[1]))),(c)[2]=(((a[2])+(b[2]))),(c)[3]=(((a[3])+(b[3]))))
/*
===============
Patch_FlatnessTest
===============
*/
static int Patch_FlatnessTest ( float maxflat, vec4_t point0, vec4_t point1, vec4_t point2 )
{
vec3_t v1, v2, v3;
vec3_t t, n;
float dist, d, l;
int ft0, ft1;
VectorSubtract ( point2, point0, n );
l = VectorNormalize ( n );
if ( !l ) {
return 0;
}
VectorSubtract ( point1, point0, t );
d = -DotProduct ( t, n );
VectorMA ( t, d, n, t );
dist = VectorLength ( t );
if ( fabs(dist) <= maxflat ) {
return 0;
}
VectorAvg ( point1, point0, v1 );
VectorAvg ( point2, point1, v2 );
VectorAvg ( v1, v2, v3 );
ft0 = Patch_FlatnessTest ( maxflat, point0, v1, v3 );
ft1 = Patch_FlatnessTest ( maxflat, v3, v2, point2 );
return 1 + (int)floor( max ( ft0, ft1 ) + 0.5f );
}
/*
===============
Patch_GetFlatness
===============
*/
void Patch_GetFlatness ( float maxflat, vec4_t *points, int *patch_cp, int *flat )
{
int i, p, u, v;
flat[0] = flat[1] = 0;
for (v = 0; v < patch_cp[1] - 1; v += 2)
{
for (u = 0; u < patch_cp[0] - 1; u += 2)
{
p = v * patch_cp[0] + u;
i = Patch_FlatnessTest ( maxflat, points[p], points[p+1], points[p+2] );
flat[0] = max ( flat[0], i );
i = Patch_FlatnessTest ( maxflat, points[p+patch_cp[0]], points[p+patch_cp[0]+1], points[p+patch_cp[0]+2] );
flat[0] = max ( flat[0], i );
i = Patch_FlatnessTest ( maxflat, points[p+2*patch_cp[0]], points[p+2*patch_cp[0]+1], points[p+2*patch_cp[0]+2] );
flat[0] = max ( flat[0], i );
i = Patch_FlatnessTest ( maxflat, points[p], points[p+patch_cp[0]], points[p+2*patch_cp[0]] );
flat[1] = max ( flat[1], i );
i = Patch_FlatnessTest ( maxflat, points[p+1], points[p+patch_cp[0]+1], points[p+2*patch_cp[0]+1] );
flat[1] = max ( flat[1], i );
i = Patch_FlatnessTest ( maxflat, points[p+2], points[p+patch_cp[0]+2], points[p+2*patch_cp[0]+2] );
flat[1] = max ( flat[1], i );
}
}
}
/*
===============
Patch_Evaluate_QuadricBezier
===============
*/
static void Patch_Evaluate_QuadricBezier ( float t, vec4_t point0, vec4_t point1, vec3_t point2, vec4_t out )
{
float qt = t * t;
float dt = 2.0f * t, tt;
vec4_t tvec4;
tt = 1.0f - dt + qt;
Vector4Scale ( point0, tt, out );
tt = dt - 2.0f * qt;
Vector4Scale ( point1, tt, tvec4 );
Vector4Add ( out, tvec4, out );
Vector4Scale ( point2, qt, tvec4 );
Vector4Add ( out, tvec4, out );
}
/*
===============
Patch_Evaluate
===============
*/
void Patch_Evaluate ( vec4_t *p, int *numcp, int *tess, vec4_t *dest )
{
int num_patches[2], num_tess[2];
int index[3], dstpitch, i, u, v, x, y;
float s, t, step[2];
vec4_t *tvec, pv[3][3], v1, v2, v3;
num_patches[0] = numcp[0] / 2;
num_patches[1] = numcp[1] / 2;
dstpitch = num_patches[0] * tess[0] + 1;
step[0] = 1.0f / (float)tess[0];
step[1] = 1.0f / (float)tess[1];
for ( v = 0; v < num_patches[1]; v++ )
{
// last patch has one more row
if ( v < num_patches[1] - 1 ) {
num_tess[1] = tess[1];
} else {
num_tess[1] = tess[1] + 1;
}
for ( u = 0; u < num_patches[0]; u++ )
{
// last patch has one more column
if ( u < num_patches[0] - 1 ) {
num_tess[0] = tess[0];
} else {
num_tess[0] = tess[0] + 1;
}
index[0] = (v * numcp[0] + u) * 2;
index[1] = index[0] + numcp[0];
index[2] = index[1] + numcp[0];
// current 3x3 patch control points
for ( i = 0; i < 3; i++ )
{
Vector4Copy ( p[index[0]+i], pv[i][0] );
Vector4Copy ( p[index[1]+i], pv[i][1] );
Vector4Copy ( p[index[2]+i], pv[i][2] );
}
t = 0.0f;
tvec = dest + v * tess[1] * dstpitch + u * tess[0];
for ( y = 0; y < num_tess[1]; y++, t += step[1] )
{
Patch_Evaluate_QuadricBezier ( t, pv[0][0], pv[0][1], pv[0][2], v1 );
Patch_Evaluate_QuadricBezier ( t, pv[1][0], pv[1][1], pv[1][2], v2 );
Patch_Evaluate_QuadricBezier ( t, pv[2][0], pv[2][1], pv[2][2], v3 );
s = 0.0f;
for ( x = 0; x < num_tess[0]; x++, s += step[0] )
{
Patch_Evaluate_QuadricBezier ( s, v1, v2, v3, tvec[x] );
}
tvec += dstpitch;
}
}
}
}
/*
===============================================================================
PATCH LOADING
===============================================================================
*/
#define cm_subdivlevel 15
void CM_CreateBrush ( q2cbrush_t *brush, vec3_t *verts, q2mapsurface_t *surface )
{
int i, j, k, sign;
vec3_t v1, v2;
vec3_t absmins, absmaxs;
q2cbrushside_t *side;
mplane_t *plane;
static mplane_t mainplane, patchplanes[20];
qboolean skip[20];
int numpatchplanes = 0;
// calc absmins & absmaxs
ClearBounds ( absmins, absmaxs );
for (i = 0; i < 3; i++)
AddPointToBounds ( verts[i], absmins, absmaxs );
PlaneFromPoints ( verts, &mainplane );
// front plane
plane = &patchplanes[numpatchplanes++];
*plane = mainplane;
// back plane
plane = &patchplanes[numpatchplanes++];
VectorNegate (mainplane.normal, plane->normal);
plane->dist = -mainplane.dist;
// axial planes
for ( i = 0; i < 3; i++ ) {
for (sign = -1; sign <= 1; sign += 2) {
plane = &patchplanes[numpatchplanes++];
VectorClear ( plane->normal );
plane->normal[i] = sign;
plane->dist = sign > 0 ? absmaxs[i] : -absmins[i];
}
}
// edge planes
for ( i = 0; i < 3; i++ ) {
vec3_t normal;
VectorCopy (verts[i], v1);
VectorCopy (verts[(i + 1) % 3], v2);
for ( k = 0; k < 3; k++ ) {
normal[k] = 0;
normal[(k+1)%3] = v1[(k+2)%3] - v2[(k+2)%3];
normal[(k+2)%3] = -(v1[(k+1)%3] - v2[(k+1)%3]);
if (VectorCompare (normal, vec3_origin))
continue;
plane = &patchplanes[numpatchplanes++];
VectorNormalize ( normal );
VectorCopy ( normal, plane->normal );
plane->dist = DotProduct (plane->normal, v1);
if ( DotProduct(verts[(i + 2) % 3], normal) - plane->dist > 0 )
{ // invert
VectorInverse ( plane->normal );
plane->dist = -plane->dist;
}
}
}
// set plane->type and mark duplicate planes for removal
for (i = 0; i < numpatchplanes; i++)
{
CategorizePlane ( &patchplanes[i] );
skip[i] = false;
for (j = i + 1; j < numpatchplanes; j++)
if ( patchplanes[j].dist == patchplanes[i].dist
&& VectorCompare (patchplanes[j].normal, patchplanes[i].normal) )
{
skip[i] = true;
break;
}
}
brush->numsides = 0;
brush->firstbrushside = numbrushsides;
for (k = 0; k < 2; k++) {
for (i = 0; i < numpatchplanes; i++) {
if (skip[i])
continue;
// first, store all axially aligned planes
// then store everything else
// does it give a noticeable speedup?
if (!k && patchplanes[i].type >= 3)
continue;
skip[i] = true;
if (numplanes == MAX_Q2MAP_PLANES)
Host_Error ("CM_CreateBrush: numplanes == MAX_CM_PLANES");
plane = &map_planes[numplanes++];
*plane = patchplanes[i];
if (numbrushsides == MAX_CM_BRUSHSIDES)
Host_Error ("CM_CreateBrush: numbrushsides == MAX_CM_BRUSHSIDES");
side = &map_brushsides[numbrushsides++];
side->plane = plane;
if (DotProduct(plane->normal, mainplane.normal) >= 0)
side->surface = surface;
else
side->surface = NULL; // don't clip against this side
brush->numsides++;
}
}
}
void CM_CreatePatch ( q3cpatch_t *patch, int numverts, vec4_t *verts, int *patch_cp )
{
int step[2], size[2], flat[2], i, u, v;
vec4_t points[MAX_CM_PATCH_VERTS];
vec3_t tverts[4], tverts2[4];
q2cbrush_t *brush;
mplane_t mainplane;
// find the degree of subdivision in the u and v directions
Patch_GetFlatness ( cm_subdivlevel, verts, patch_cp, flat );
step[0] = (1 << flat[0]);
step[1] = (1 << flat[1]);
size[0] = (patch_cp[0] / 2) * step[0] + 1;
size[1] = (patch_cp[1] / 2) * step[1] + 1;
if ( size[0] * size[1] > MAX_CM_PATCH_VERTS ) {
Host_Error ( "CM_CreatePatch: patch has too many vertices" );
return;
}
// fill in
Patch_Evaluate ( verts, patch_cp, step, points );
patch->brushes = brush = map_brushes + numbrushes;
patch->numbrushes = 0;
ClearBounds (patch->absmins, patch->absmaxs);
// create a set of brushes
for (v = 0; v < size[1]-1; v++)
{
for (u = 0; u < size[0]-1; u++)
{
if (numbrushes >= MAX_CM_BRUSHES)
Host_Error ("CM_CreatePatch: too many patch brushes");
i = v * size[0] + u;
VectorCopy (points[i], tverts[0]);
VectorCopy (points[i + size[0]], tverts[1]);
VectorCopy (points[i + 1], tverts[2]);
VectorCopy (points[i + size[0] + 1], tverts[3]);
for (i = 0; i < 4; i++)
AddPointToBounds (tverts[i], patch->absmins, patch->absmaxs);
PlaneFromPoints (tverts, &mainplane);
// create two brushes
CM_CreateBrush (brush, tverts, patch->surface);
brush->contents = patch->surface->c.value;
brush++; numbrushes++; patch->numbrushes++;
VectorCopy (tverts[2], tverts2[0]);
VectorCopy (tverts[1], tverts2[1]);
VectorCopy (tverts[3], tverts2[2]);
CM_CreateBrush (brush, tverts2, patch->surface);
brush->contents = patch->surface->c.value;
brush++; numbrushes++; patch->numbrushes++;
}
}
}
//======================================================
/*
=================
CM_CreatePatchesForLeafs
=================
*/
void CM_CreatePatchesForLeafs (void)
{
int i, j, k;
mleaf_t *leaf;
q3cface_t *face;
q2mapsurface_t *surf;
q3cpatch_t *patch;
int checkout[MAX_CM_FACES];
memset (checkout, -1, sizeof(int)*MAX_CM_FACES);
for (i = 0, leaf = map_leafs; i < numleafs; i++, leaf++)
{
leaf->numleafpatches = 0;
leaf->firstleafpatch = numleafpatches;
if (leaf->cluster == -1)
continue;
for (j=0 ; j<leaf->numleaffaces ; j++)
{
k = leaf->firstleafface + j;
if (k >= numleaffaces) {
break;
}
k = map_leaffaces[k];
face = &map_faces[k];
if (face->facetype != MST_PATCH || face->numverts <= 0)
continue;
if (face->patch_cp[0] <= 0 || face->patch_cp[1] <= 0)
continue;
if (face->shadernum < 0 || face->shadernum >= numtexinfo)
continue;
surf = &map_surfaces[face->shadernum];
if ( !surf->c.value || (surf->c.flags & Q3SURF_NONSOLID) )
continue;
if ( numleafpatches >= MAX_CM_LEAFFACES )
Host_Error ("CM_CreatePatchesForLeafs: map has too many faces");
// the patch was already built
if (checkout[k] != -1)
{
map_leafpatches[numleafpatches] = checkout[k];
patch = &map_patches[checkout[k]];
}
else
{
if (numpatches >= MAX_CM_PATCHES)
Host_Error ("CM_CreatePatchesForLeafs: map has too many patches");
patch = &map_patches[numpatches];
patch->surface = surf;
map_leafpatches[numleafpatches] = numpatches;
checkout[k] = numpatches++;
CM_CreatePatch ( patch, face->numverts, map_verts + face->firstvert, face->patch_cp );
}
leaf->contents |= patch->surface->c.value;
leaf->numleafpatches++;
numleafpatches++;
}
}
}
/*
===============================================================================
MAP LOADING
===============================================================================
*/
qbyte *cmod_base;
/*
=================
CMod_LoadSubmodels
=================
*/
void CMod_LoadSubmodels (lump_t *l)
{
q2dmodel_t *in;
q2cmodel_t *out;
int i, j, count;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count < 1)
Host_Error ("Map with no models");
if (count > MAX_Q2MAP_MODELS)
Host_Error ("Map has too many models");
numcmodels = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
out = &map_cmodels[i];
for (j=0 ; j<3 ; j++)
{ // spread the mins / maxs by a pixel
out->mins[j] = LittleFloat (in->mins[j]) - 1;
out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
out->origin[j] = LittleFloat (in->origin[j]);
}
out->headnode = LittleLong (in->headnode);
out->firstsurface = LittleLong (in->firstface);
out->numsurfaces = LittleLong (in->numfaces);
}
}
/*
=================
CMod_LoadSurfaces
=================
*/
void CMod_LoadSurfaces (lump_t *l)
{
q2texinfo_t *in;
q2mapsurface_t *out;
int i, count;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count < 1)
Host_Error ("Map with no surfaces");
if (count > MAX_Q2MAP_TEXINFO)
Host_Error ("Map has too many surfaces");
numtexinfo = count;
out = map_surfaces;
for ( i=0 ; i<count ; i++, in++, out++)
{
Q_strncpyz (out->c.name, in->texture, sizeof(out->c.name));
Q_strncpyz (out->rname, in->texture, sizeof(out->rname));
out->c.flags = LittleLong (in->flags);
out->c.value = LittleLong (in->value);
}
}
#ifndef SERVERONLY
qbyte *ReadPCXFile(qbyte *buf, int length, int *width, int *height);
qbyte *ReadTargaFile(qbyte *buf, int length, int *width, int *height, int asgrey);
qbyte *ReadTargaFile(qbyte *buf, int length, int *width, int *height, int asgrey);
qbyte *ReadPCXFile(qbyte *buf, int length, int *width, int *height);
void *Mod_LoadWall(char *name)
{
qbyte *in, *oin;
texture_t *tex;
q2miptex_t *wal;
int width, height;
char ln[32];
COM_FileBase(name, ln);
wal = (void *)COM_LoadMallocFile (name);
if (!wal)
return NULL;
//FIXME: Is this needed?
oin = in = ReadPCXFile((qbyte *)wal, com_filesize, &width, &height);
if (!in)
oin = in = ReadTargaFile((qbyte *)wal, com_filesize, &width, &height, false);
if (in) //this is a pcx.
{
#ifdef RGLQUAKE
if (qrenderer == QR_OPENGL)
{
tex = Hunk_AllocName(sizeof(texture_t), ln);
tex->offsets[0] = sizeof(*tex);
tex->width = width;
tex->height = height;
texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR;
if (!(tex->gl_texturenum = Mod_LoadReplacementTexture(name, true, false, true)))
tex->gl_texturenum = GL_LoadTexture32 (name, width, height, (unsigned int *)in, true, false);
texture_mode = GL_LINEAR;
}
else
#endif
#ifdef SWQUAKE
if (qrenderer == QR_SOFTWARE)
{
int i, j;
qbyte *out;
tex = Hunk_AllocName(sizeof(texture_t) + width*r_pixbytes*height/64*85, ln);
tex->pixbytes = r_pixbytes;
tex->width = width;
tex->height = height;
tex->offsets[0] = sizeof(*tex);
tex->offsets[1] = tex->offsets[0] + width*height*r_pixbytes;
tex->offsets[2] = tex->offsets[1] + (width*height*r_pixbytes)/4;
tex->offsets[3] = tex->offsets[2] + (width*height*r_pixbytes)/(4*4);
out = (qbyte *)(tex+1);
if (tex->pixbytes == 4)
{
for (i = 0; i < width*height; i++)
{
*out++ = in[2];
*out++ = in[1];
*out++ = in[0];
*out++ = in[3];
in+=4;
}
}
else
{
for (i=0; i < tex->width*tex->height; i++) //downgrade colour
{
*out++ = GetPalette(in[0], in[1], in[2]);
in+=4;
}
in = (qbyte *)tex+tex->offsets[0]; //shrink mips.
for (j = 0; j < tex->height; j+=2) //we could convert mip[1], but shrinking is probably faster.
for (i = 0; i < tex->width; i+=2)
*out++ = in[i + tex->width*j];
for (j = 0; j < tex->height; j+=4)
for (i = 0; i < tex->width; i+=4)
*out++ = in[i + tex->width*j];
for (j = 0; j < tex->height; j+=8)
for (i = 0; i < tex->width; i+=8)
*out++ = in[i + tex->width*j];
}
}
else
#endif
{
Sys_Error("Mod_LoadWall with bad renderer\n");
tex = NULL;
}
BZ_Free(oin);
BZ_Free(wal);
return tex;
}
#if defined(RGLQUAKE)
if (qrenderer == QR_OPENGL)
{
int j;
tex = Hunk_AllocName(sizeof(texture_t), ln);
tex->offsets[0] = wal->offsets[0];
tex->width = wal->width;
tex->height = wal->height;
texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR;
if (!(tex->gl_texturenum = Mod_LoadReplacementTexture(wal->name, true, false, true)))
tex->gl_texturenum = GL_LoadTexture8Pal24 (wal->name, tex->width, tex->height, (qbyte *)wal+wal->offsets[0], d_q28to24table, true, false);
in = Hunk_TempAllocMore(wal->width*wal->height);
oin = (qbyte *)wal+wal->offsets[0];
for (j = 0; j < wal->width*wal->height; j++)
in[j] = (d_q28to24table[oin[j]*3+0] + d_q28to24table[oin[j]*3+1] + d_q28to24table[oin[j]*3+2])/3;
tex->gl_texturenumbumpmap = GL_LoadTexture8Bump (va("%s_bump", wal->name), tex->width, tex->height, in, true);
texture_mode = GL_LINEAR;
}
else
#endif
#if defined(SWQUAKE)
if (qrenderer == QR_SOFTWARE)
{
int i, j;
qbyte *out;
tex = Hunk_AllocName(sizeof(texture_t) + wal->width*r_pixbytes*wal->height/64*85, ln);
tex->width = wal->width;
tex->height = wal->height;
tex->pixbytes = r_pixbytes;
for (i = 0; i < MIPLEVELS; i++)
tex->offsets[i] = (wal->offsets[i] - sizeof(*wal))*tex->pixbytes + sizeof(*tex);
out = (qbyte *)(tex+1);
in = (qbyte *)wal+wal->offsets[0];
if (tex->pixbytes == 4)
{
for (i = 0; i < wal->width*wal->height/64*85; i++)
{
*out++ = d_q28to24table[*in*3+0];
*out++ = d_q28to24table[*in*3+1];
*out++ = d_q28to24table[*in*3+2];
*out++ = 255;
in++;
}
}
else
{
for (i=0; i < tex->width*tex->height; i++) //downgrade colour
{
*out++ = GetPalette(d_q28to24table[*in*3+0], d_q28to24table[*in*3+1], d_q28to24table[*in*3+2]);
in++;
}
in = (qbyte *)tex+tex->offsets[0]; //shrink mips.
for (j = 0; j < tex->height; j+=2) //we could convert mip[1], but shrinking is probably faster.
for (i = 0; i < tex->width; i+=2)
*out++ = in[i + tex->width*j];
for (j = 0; j < tex->height; j+=4)
for (i = 0; i < tex->width; i+=4)
*out++ = in[i + tex->width*j];
for (j = 0; j < tex->height; j+=8)
for (i = 0; i < tex->width; i+=8)
*out++ = in[i + tex->width*j];
}
}
else
#endif
{
Sys_Error("Mod_LoadWall with bad renderer\n");
tex = NULL;
}
BZ_Free(wal);
return tex;
}
void CMod_LoadTexInfo (lump_t *l) //yes I know these load from the same place
{
q2texinfo_t *in;
mtexinfo_t *out;
int i, j, count;
char name[MAX_QPATH];
float len1, len2;
int texcount;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
count = l->filelen / sizeof(*in);
out = Hunk_AllocName ( count*sizeof(*out), loadname);
loadmodel->textures = Hunk_AllocName(sizeof(texture_t *)*count, loadname);
texcount = 0;
loadmodel->texinfo = out;
loadmodel->numtexinfo = count;
#if !defined(SERVERONLY) && defined(RGLQUAKE)
skytexturenum = -1;
#endif
for ( i=0 ; i<count ; i++, in++, out++)
{
out->flags = LittleLong (in->flags);
for (j=0 ; j<8 ; j++)
out->vecs[0][j] = LittleFloat (in->vecs[0][j]);
len1 = Length (out->vecs[0]);
len2 = Length (out->vecs[1]);
len1 = (len1 + len2)/2;
if (len1 < 0.32)
out->mipadjust = 4;
else if (len1 < 0.49)
out->mipadjust = 3;
else if (len1 < 0.99)
out->mipadjust = 2;
else
out->mipadjust = 1;
//damn q2...
for (j=0; j < texcount; j++)
{
if (!strcmp(in->texture, loadmodel->textures[j]->name))
{
out->texture = loadmodel->textures[j];
break;
}
}
if (j == texcount) //load a new one
{
_snprintf (name, sizeof(name), "textures/%s.wal", in->texture);
out->texture = Mod_LoadWall (name);
if (!out->texture || !out->texture->width || !out->texture->height)
{
out->texture = Hunk_Alloc(sizeof(texture_t) + 16*16+8*8+4*4+2*2);
Con_Printf ("^2Couldn't load %s\n", name);
memcpy(out->texture, r_notexture_mip, sizeof(texture_t) + 16*16+8*8+4*4+2*2);
// out->texture = r_notexture_mip; // texture not found
// out->flags = 0;
}
Q_strncpyz(out->texture->name, in->texture, sizeof(out->texture->name));
#if !defined(SERVERONLY) && defined(RGLQUAKE)
if (out->flags & SURF_SKY)
skytexturenum = texcount;
#endif
loadmodel->textures[texcount++] = out->texture;
}
#if !defined(SERVERONLY) && defined(RGLQUAKE)
else if (out->flags & SURF_SKY)
out->texture = loadmodel->textures[skytexturenum];
#endif
}
loadmodel->numtextures = texcount;
// count animation frames
/*
for (i=0 ; i<count ; i++)
{
out = &loadmodel->texinfo[i];
// out->numframes = 1;
// for (step = out->next ; step && step != out ; step=step->next)
// out->numframes++;
}
*/
}
#endif
/*
void CalcSurfaceExtents (msurface_t *s)
{
float mins[2], maxs[2], val;
int i,j, e;
mvertex_t *v;
mtexinfo_t *tex;
int bmins[2], bmaxs[2];
mins[0] = mins[1] = 999999;
maxs[0] = maxs[1] = -99999;
tex = s->texinfo;
for (i=0 ; i<s->numedges ; i++)
{
e = loadmodel->surfedges[s->firstedge+i];
if (e >= 0)
v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
else
v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
for (j=0 ; j<2 ; j++)
{
val = v->position[0] * tex->vecs[j][0] +
v->position[1] * tex->vecs[j][1] +
v->position[2] * tex->vecs[j][2] +
tex->vecs[j][3];
if (val < mins[j])
mins[j] = val;
if (val > maxs[j])
maxs[j] = val;
}
}
for (i=0 ; i<2 ; i++)
{
bmins[i] = floor(mins[i]/16);
bmaxs[i] = ceil(maxs[i]/16);
s->texturemins[i] = bmins[i] * 16;
s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
// if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 )// 256 )
// Sys_Error ("Bad surface extents");
}
}*/
/*
=================
Mod_LoadFaces
=================
*/
#ifndef SERVERONLY
void CMod_LoadFaces (lump_t *l)
{
dface_t *in;
msurface_t *out;
int i, count, surfnum;
int planenum, side;
int ti;
extern qboolean r_usinglits;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
count = l->filelen / sizeof(*in);
out = Hunk_AllocName ( (count+6)*sizeof(*out), loadname); //spare for skybox
loadmodel->surfaces = out;
loadmodel->numsurfaces = count;
for ( surfnum=0 ; surfnum<count ; surfnum++, in++, out++)
{
out->firstedge = LittleLong(in->firstedge);
out->numedges = LittleShort(in->numedges);
out->flags = 0;
#if defined(RGLQUAKE)
out->polys = NULL;
#endif
planenum = LittleShort(in->planenum);
side = LittleShort(in->side);
if (side)
out->flags |= SURF_PLANEBACK;
out->plane = loadmodel->planes + planenum;
ti = LittleShort (in->texinfo);
if (ti < 0 || ti >= loadmodel->numtexinfo)
Host_Error ("MOD_LoadBmodel: bad texinfo number");
out->texinfo = loadmodel->texinfo + ti;
#ifndef SERVERONLY
if (out->texinfo->flags & SURF_SKY)
{
out->flags |= SURF_DRAWSKY;
}
if (out->texinfo->flags & SURF_WARP)
{
out->flags |= SURF_DRAWTURB|SURF_DRAWTILED;
}
#endif
CalcSurfaceExtents (out);
// lighting info
for (i=0 ; i<MAXLIGHTMAPS ; i++)
out->styles[i] = in->styles[i];
i = LittleLong(in->lightofs);
if (i == -1)
out->samples = NULL;
#ifdef RGLQUAKE
else if (qrenderer == QR_OPENGL)
out->samples = loadmodel->lightdata + i;
#endif
#ifdef SWQUAKE
else if (r_usinglits)
out->samples = loadmodel->lightdata + i;
#endif
else
out->samples = loadmodel->lightdata + i/3;
// set the drawing flags
/*
if (out->texinfo->flags & SURF_WARP)
{
out->flags |= SURF_DRAWTURB;
for (i=0 ; i<2 ; i++)
{
out->extents[i] = 16384;
out->texturemins[i] = -8192;
}
GL_SubdivideSurface (out); // cut up polygon for warps
}
*/
}
}
#endif
void CMod_SetParent (mnode_t *node, mnode_t *parent)
{
node->parent = parent;
if (node->contents != -1)
return;
CMod_SetParent (node->children[0], node);
CMod_SetParent (node->children[1], node);
}
/*
=================
CMod_LoadNodes
=================
*/
void CMod_LoadNodes (lump_t *l)
{
q2dnode_t *in;
int child;
mnode_t *out;
int i, j, count;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count < 1)
Host_Error ("Map has no nodes");
if (count > MAX_Q2MAP_NODES)
Host_Error ("Map has too many nodes");
out = map_nodes;
numnodes = count;
loadmodel->nodes = out;
loadmodel->numnodes = numnodes;
for (i=0 ; i<count ; i++, out++, in++)
{
memset(out, 0, sizeof(*out));
for (j=0 ; j<3 ; j++)
{
out->minmaxs[j] = LittleShort (in->mins[j]);
out->minmaxs[3+j] = LittleShort (in->maxs[j]);
}
out->plane = map_planes + LittleLong(in->planenum);
out->firstsurface = LittleShort (in->firstface);
out->numsurfaces = LittleShort (in->numfaces);
out->contents = -1; // differentiate from leafs
for (j=0 ; j<2 ; j++)
{
child = LittleLong (in->children[j]);
out->childnum[j] = child;
if (child < 0)
out->children[j] = (mnode_t *)(map_leafs + -1-child);
else
out->children[j] = map_nodes + child;
}
}
CMod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
}
/*
=================
CMod_LoadBrushes
=================
*/
void CMod_LoadBrushes (lump_t *l)
{
q2dbrush_t *in;
q2cbrush_t *out;
int i, count;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count > MAX_Q2MAP_BRUSHES)
Host_Error ("Map has too many brushes");
out = map_brushes;
numbrushes = count;
for (i=0 ; i<count ; i++, out++, in++)
{
out->firstbrushside = LittleLong(in->firstside);
out->numsides = LittleLong(in->numsides);
out->contents = LittleLong(in->contents);
}
}
/*
=================
CMod_LoadLeafs
=================
*/
void CMod_LoadLeafs (lump_t *l)
{
int i, j;
mleaf_t *out;
q2dleaf_t *in;
int count;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count < 1)
Host_Error ("Map with no leafs");
// need to save space for box planes
if (count > MAX_Q2MAP_PLANES)
Host_Error ("Map has too many planes");
out = map_leafs;
numleafs = count;
numclusters = 0;
loadmodel->leafs = out;
loadmodel->numleafs = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
memset(out, 0, sizeof(*out));
for (j=0 ; j<3 ; j++)
{
out->minmaxs[j] = LittleShort (in->mins[j]);
out->minmaxs[3+j] = LittleShort (in->maxs[j]);
}
out->contents = LittleLong (in->contents);
out->cluster = LittleShort (in->cluster);
out->area = LittleShort (in->area);
out->firstleafbrush = LittleShort (in->firstleafbrush);
out->numleafbrushes = LittleShort (in->numleafbrushes);
out->firstmarksurface = loadmodel->marksurfaces +
LittleShort(in->firstleafface);
out->nummarksurfaces = LittleShort(in->numleaffaces);
if (out->cluster >= numclusters)
numclusters = out->cluster + 1;
}
if (map_leafs[0].contents != Q2CONTENTS_SOLID)
Host_Error ("Map leaf 0 is not CONTENTS_SOLID");
emptyleaf = -1;
for (i=1 ; i<numleafs ; i++)
{
if (!map_leafs[i].contents)
{
emptyleaf = i;
break;
}
}
if (emptyleaf == -1)
Host_Error ("Map does not have an empty leaf");
}
/*
=================
CMod_LoadPlanes
=================
*/
void CMod_LoadPlanes (lump_t *l)
{
int i, j;
mplane_t *out;
dplane_t *in;
int count;
int bits;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count < 1)
Host_Error ("Map with no planes");
// need to save space for box planes
if (count >= MAX_Q2MAP_PLANES)
Host_Error ("Map has too many planes");
out = map_planes;
numplanes = count;
loadmodel->planes = out;
loadmodel->numplanes = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
bits = 0;
for (j=0 ; j<3 ; j++)
{
out->normal[j] = LittleFloat (in->normal[j]);
if (out->normal[j] < 0)
bits |= 1<<j;
}
out->dist = LittleFloat (in->dist);
out->type = LittleLong (in->type);
out->signbits = bits;
}
}
/*
=================
CMod_LoadLeafBrushes
=================
*/
void CMod_LoadLeafBrushes (lump_t *l)
{
int i;
int *out;
unsigned short *in;
int count;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count < 1)
Host_Error ("Map with no planes");
// need to save space for box planes
if (count > MAX_Q2MAP_LEAFBRUSHES)
Host_Error ("Map has too many leafbrushes");
out = map_leafbrushes;
numleafbrushes = count;
for ( i=0 ; i<count ; i++, in++, out++)
*out = LittleShort (*in);
}
/*
=================
CMod_LoadBrushSides
=================
*/
void CMod_LoadBrushSides (lump_t *l)
{
int i, j;
q2cbrushside_t *out;
q2dbrushside_t *in;
int count;
int num;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
// need to save space for box planes
if (count > MAX_Q2MAP_BRUSHSIDES)
Host_Error ("Map has too many planes");
out = map_brushsides;
numbrushsides = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
num = LittleShort (in->planenum);
out->plane = &map_planes[num];
j = LittleShort (in->texinfo);
if (j >= numtexinfo)
Host_Error ("Bad brushside texinfo");
out->surface = &map_surfaces[j];
}
}
/*
=================
CMod_LoadAreas
=================
*/
void CMod_LoadAreas (lump_t *l)
{
int i;
q2carea_t *out;
q2darea_t *in;
int count;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count > MAX_Q2MAP_AREAS)
Host_Error ("Map has too many areas");
out = map_q2areas;
numareas = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
out->numareaportals = LittleLong (in->numareaportals);
out->firstareaportal = LittleLong (in->firstareaportal);
out->floodvalid = 0;
out->floodnum = 0;
}
}
/*
=================
CMod_LoadAreaPortals
=================
*/
void CMod_LoadAreaPortals (lump_t *l)
{
int i;
q2dareaportal_t *out;
q2dareaportal_t *in;
int count;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count > MAX_Q2MAP_AREAS)
Host_Error ("Map has too many areas");
out = map_areaportals;
numareaportals = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
out->portalnum = LittleLong (in->portalnum);
out->otherarea = LittleLong (in->otherarea);
}
}
/*
=================
CMod_LoadVisibility
=================
*/
void CMod_LoadVisibility (lump_t *l)
{
int i;
numvisibility = l->filelen;
if (l->filelen > MAX_Q2MAP_VISIBILITY)
Host_Error ("Map has too large visibility lump");
memcpy (map_visibility, cmod_base + l->fileofs, l->filelen);
loadmodel->vis = map_q2vis;
map_q2vis->numclusters = LittleLong (map_q2vis->numclusters);
for (i=0 ; i<map_q2vis->numclusters ; i++)
{
map_q2vis->bitofs[i][0] = LittleLong (map_q2vis->bitofs[i][0]);
map_q2vis->bitofs[i][1] = LittleLong (map_q2vis->bitofs[i][1]);
}
}
/*
=================
CMod_LoadEntityString
=================
*/
void CMod_LoadEntityString (lump_t *l)
{
numentitychars = l->filelen;
if (l->filelen > MAX_Q2MAP_ENTSTRING)
Host_Error ("Map has too large entity lump");
memcpy (map_entitystring, cmod_base + l->fileofs, l->filelen);
loadmodel->entities = map_entitystring;
}
void CModQ3_LoadMarksurfaces (lump_t *l)
{
int i, j, count;
int *in;
msurface_t **out;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
count = l->filelen / sizeof(*in);
out = Hunk_AllocName ( count*sizeof(*out), loadname);
loadmodel->marksurfaces = out;
loadmodel->nummarksurfaces = count;
for ( i=0 ; i<count ; i++)
{
j = LittleLong(in[i]);
if (j < 0 || j >= loadmodel->numsurfaces)
Sys_Error ("Mod_ParseMarksurfaces: bad surface number");
out[i] = loadmodel->surfaces + j;
}
}
void CModQ3_LoadSubmodels (lump_t *l)
{
q3dmodel_t *in;
q2cmodel_t *out;
int i, j, count;
int *leafbrush;
mleaf_t *bleaf;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count < 1)
Host_Error ("Map with no models");
if (count > MAX_Q2MAP_MODELS)
Host_Error ("Map has too many models");
numcmodels = count;
mapisq3 = true;
for ( i=0 ; i<count ; i++, in++, out++)
{
out = &map_cmodels[i];
for (j=0 ; j<3 ; j++)
{ // spread the mins / maxs by a pixel
out->mins[j] = LittleFloat (in->mins[j]) - 1;
out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
out->origin[j] = (out->maxs[j] + out->mins[j])/2;
}
if (!i)
out->headnode = 0;
else
{
//create a new leaf to hold the bruses and be directly clipped
out->headnode = -1 - numleafs;
out->firstsurface = LittleLong (in->firstsurface);
out->numsurfaces = LittleLong (in->num_surfaces);
// out->firstbrush = LittleLong(in->firstbrush);
// out->num_brushes = LittleLong(in->num_brushes);
bleaf = &map_leafs[numleafs++];
bleaf->numleafbrushes = LittleLong ( in->num_brushes );
bleaf->firstleafbrush = numleafbrushes;
bleaf->contents = 0;
leafbrush = &map_leafbrushes[numleafbrushes];
for ( j = 0; j < bleaf->numleafbrushes; j++, leafbrush++ ) {
*leafbrush = LittleLong ( in->firstbrush ) + j;
bleaf->contents |= map_brushes[*leafbrush].contents;
}
numleafbrushes += bleaf->numleafbrushes;
}
//submodels
}
}
void CModQ3_LoadShaders (lump_t *l)
{
dq3shader_t *in;
q2mapsurface_t *out;
int i, count;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count < 1)
Host_Error ("Map with no shaders");
else if (count > MAX_Q2MAP_TEXINFO)
Host_Error ("Map has too many shaders");
numtexinfo = count;
out = map_surfaces;
#if !defined(SERVERONLY) && defined(RGLQUAKE)
skytexturenum = -1;
#endif
loadmodel->texinfo = Hunk_Alloc(sizeof(mtexinfo_t)*count);
loadmodel->numtextures = count;
loadmodel->textures = Hunk_Alloc(sizeof(texture_t*)*count);
for ( i=0 ; i<count ; i++, in++, out++ )
{
loadmodel->texinfo[i].texture = Hunk_Alloc(sizeof(texture_t));
Q_strncpyz(loadmodel->texinfo[i].texture->name, in->shadername, sizeof(loadmodel->texinfo[i].texture->name));
#ifdef RGLQUAKE
if (qrenderer == QR_OPENGL)
{
loadmodel->texinfo[i].texture->gl_texturenum = Mod_LoadHiResTexture(in->shadername, true, false, true);
loadmodel->texinfo[i].texture->gl_texturenumfb = 0;
loadmodel->texinfo[i].texture->gl_texturenumbumpmap = 0;
}
#endif
loadmodel->textures[i] = loadmodel->texinfo[i].texture;
out->c.flags = LittleLong ( in->surfflags );
out->c.value = LittleLong ( in->contents );
#if !defined(SERVERONLY) && defined(RGLQUAKE)
if (!strncmp(in->shadername, "textures/skies/", 15))
{
loadmodel->texinfo[i].flags |= SURF_SKY;
skytexturenum = i;
}
#endif
}
}
void CModQ3_LoadVertexes (lump_t *l)
{
q3dvertex_t *in;
vec4_t *out;
vec3_t *nout;
int i, count, j;
vec2_t *lmout, *stout;
byte_vec4_t *cout;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("CMOD_LoadVertexes: funny lump size");
count = l->filelen / sizeof(*in);
if (count > MAX_Q3MAP_VERTEXES)
Host_Error ("Map has too many vertexes");
out = Hunk_Alloc ( count*sizeof(*out) );
stout = Hunk_Alloc ( count*sizeof(*stout) );
lmout = Hunk_Alloc ( count*sizeof(*lmout) );
cout = Hunk_Alloc ( count*sizeof(*cout) );
nout = Hunk_Alloc ( count*sizeof(*nout) );
map_verts = out;
map_vertstmexcoords = stout;
map_vertlstmexcoords = lmout;
map_colors_array = cout;
map_normals_array = nout;
numvertexes = count;
for ( i=0 ; i<count ; i++, in++)
{
for ( j=0 ; j < 3 ; j++)
{
out[i][j] = LittleFloat ( in->point[j] );
nout[i][j] = LittleFloat (in->normal[j]);
}
for ( j=0 ; j < 2 ; j++)
{
stout[i][j] = LittleFloat ( ((float *)in->texcoords)[j] );
lmout[i][j] = LittleFloat ( ((float *)in->texcoords)[j+2] );
}
for ( j=0 ; j < 4 ; j++)
{
cout[i][j] = in->color[j];
}
}
}
void CModQ3_LoadIndexes (lump_t *l)
{
int i, count;
int *in, *out;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
count = l->filelen / sizeof(*in);
if (count < 1 || count >= MAX_Q3MAP_INDICES)
Host_Error ("MOD_LoadBmodel: bad surfedges count in %s: %i",
loadmodel->name, count);
out = Hunk_AllocName ( count*sizeof(*out), loadmodel->name );
map_surfindexes = out;
map_numsurfindexes = count;
for ( i=0 ; i<count ; i++)
out[i] = LittleLong (in[i]);
}
/*
=================
CMod_LoadFaces
=================
*/
void CModQ3_LoadFaces (lump_t *l)
{
q3dface_t *in;
q3cface_t *out;
int i, count;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count > MAX_MAP_FACES)
Host_Error ("Map has too many faces");
out = BZ_Malloc ( count*sizeof(*out) );
map_faces = out;
numfaces = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
out->facetype = LittleLong ( in->facetype );
out->shadernum = LittleLong ( in->shadernum );
out->numverts = LittleLong ( in->num_vertices );
out->firstvert = LittleLong ( in->firstvertex );
out->patch_cp[0] = LittleLong ( in->patchwidth );
out->patch_cp[1] = LittleLong ( in->patchheight );
}
loadmodel->numsurfaces = i;
}
#ifdef RGLQUAKE
/*
=================
Mod_LoadFogs
=================
*/
#ifdef Q3SHADERS
void CModQ3_LoadFogs (lump_t *l)
{
dfog_t *in;
mfog_t *out;
q2cbrush_t *brush;
q2cbrushside_t *visibleside, *brushsides;
int i, j, count;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
count = l->filelen / sizeof(*in);
out = Hunk_Alloc ( count*sizeof(*out) );
map_fogs = out;
map_numfogs = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
if ( LittleLong ( in->visibleSide ) == -1 )
{
continue;
}
brush = map_brushes + LittleLong ( in->brushNum );
brushsides = map_brushsides + brush->firstbrushside;
visibleside = brushsides + LittleLong ( in->visibleSide );
out->visibleplane = visibleside->plane;
out->shader = R_RegisterShader ( in->shader );
out->numplanes = brush->numsides;
out->planes = Hunk_Alloc ( out->numplanes*sizeof(cplane_t *) );
for ( j = 0; j < out->numplanes; j++ )
{
out->planes[j] = brushsides[j].plane;
}
}
if (count)
GL_InitFogTexture();
}
#endif
//Convert a patch in to a list of glpolys
#define MAX_ARRAY_VERTS 2048
glpoly_t *GL_MeshToGLPoly(mesh_t *mesh)
{
int polysize = sizeof(glpoly_t) - (VERTEXSIZE)*sizeof(float);
int gv;
int v;
int rv;
glpoly_t *p, *ret;
int numindx;
if (!mesh)
return NULL;
numindx = mesh->numindexes;
ret = NULL;
p = Hunk_Alloc(polysize * numindx/3);
for (gv = 0; gv < numindx; )
{
for (v = gv; v < gv+3; v++)
{
rv = mesh->indexes[v];
p->verts[v%3][0] = mesh->xyz_array[rv][0];
p->verts[v%3][1] = mesh->xyz_array[rv][1];
p->verts[v%3][2] = mesh->xyz_array[rv][2];
p->verts[v%3][3] = mesh->st_array[rv][0];
p->verts[v%3][4] = mesh->st_array[rv][1];
p->verts[v%3][5] = mesh->lmst_array[rv][0];
p->verts[v%3][6] = mesh->lmst_array[rv][1];
}
gv+=3;
p->next = ret;
p->numverts = 3;
ret = p;
p = (glpoly_t *)((char *)p + polysize);
}
return ret;
}
#define Vector2Copy(a,b) {(b)[0]=(a)[0];(b)[1]=(a)[1];}
index_t tempIndexesArray[MAX_ARRAY_VERTS*3];
vec4_t tempxyz_array[MAX_ARRAY_VERTS]; //structure is used only at load.
vec3_t tempnormals_array[MAX_ARRAY_VERTS]; //so what harm is there in doing this?
vec2_t tempst_array[MAX_ARRAY_VERTS];
vec2_t templmst_array[MAX_ARRAY_VERTS];
byte_vec4_t tempcolors_array[MAX_ARRAY_VERTS];
#ifdef Q3SHADERS
#define Hunk_TempAllocMore Hunk_Alloc
#endif
mesh_t *GL_CreateMeshForPatch ( model_t *mod, q3dface_t *surf )
{
int numverts, numindexes, firstvert, patch_cp[2], step[2], size[2], flat[2], i, u, v, p;
vec4_t colors[MAX_ARRAY_VERTS], points[MAX_ARRAY_VERTS], normals[MAX_ARRAY_VERTS],
lm_st[MAX_ARRAY_VERTS], tex_st[MAX_ARRAY_VERTS];
vec4_t c, colors2[MAX_ARRAY_VERTS], normals2[MAX_ARRAY_VERTS], lm_st2[MAX_ARRAY_VERTS], tex_st2[MAX_ARRAY_VERTS];
mesh_t *mesh;
index_t *indexes;
float subdivlevel;
patch_cp[0] = LittleLong ( surf->patchwidth );
patch_cp[1] = LittleLong ( surf->patchheight );
if ( !patch_cp[0] || !patch_cp[1] ) {
return NULL;
}
subdivlevel = r_subdivisions.value;
if ( subdivlevel < 1 )
subdivlevel = 1;
numverts = LittleLong ( surf->num_vertices );
firstvert = LittleLong ( surf->firstvertex );
for ( i = 0; i < numverts; i++ ) {
VectorCopy ( map_verts[firstvert + i], points[i] );
VectorCopy ( map_normals_array[firstvert + i], normals[i] );
Vector4Scale ( map_colors_array[firstvert + i], (1.0 / 255.0), colors[i] );
Vector2Copy ( map_vertstmexcoords[firstvert + i], tex_st[i] );
Vector2Copy ( map_vertlstmexcoords[firstvert + i], lm_st[i] );
}
// find the degree of subdivision in the u and v directions
Patch_GetFlatness ( subdivlevel, points, patch_cp, flat );
// allocate space for mesh
step[0] = (1 << flat[0]);
step[1] = (1 << flat[1]);
size[0] = (patch_cp[0] / 2) * step[0] + 1;
size[1] = (patch_cp[1] / 2) * step[1] + 1;
numverts = size[0] * size[1];
if ( numverts > MAX_ARRAY_VERTS ) {
return NULL;
}
mesh = (mesh_t *)Hunk_TempAllocMore ( sizeof(mesh_t));
mesh->numvertexes = numverts;
mesh->xyz_array = Hunk_TempAllocMore ( numverts * sizeof(vec4_t));
mesh->normals_array = Hunk_TempAllocMore ( numverts * sizeof(vec3_t));
mesh->st_array = Hunk_TempAllocMore ( numverts * sizeof(vec2_t));
mesh->lmst_array = Hunk_TempAllocMore ( numverts * sizeof(vec2_t));
mesh->colors_array = Hunk_TempAllocMore ( numverts * sizeof(byte_vec4_t));
mesh->patchWidth = size[0];
mesh->patchHeight = size[1];
// fill in
Patch_Evaluate ( points, patch_cp, step, mesh->xyz_array );
Patch_Evaluate ( colors, patch_cp, step, colors2 );
Patch_Evaluate ( normals, patch_cp, step, normals2 );
Patch_Evaluate ( lm_st, patch_cp, step, lm_st2 );
Patch_Evaluate ( tex_st, patch_cp, step, tex_st2 );
for (i = 0; i < numverts; i++)
{
VectorNormalize2 ( normals2[i], mesh->normals_array[i] );
ColorNormalize ( colors2[i], c );
Vector4Scale ( c, 255.0, mesh->colors_array[i] );
Vector2Copy ( tex_st2[i], mesh->st_array[i] );
Vector2Copy ( lm_st2[i], mesh->lmst_array[i] );
}
// compute new indexes avoiding adding invalid triangles
numindexes = 0;
indexes = tempIndexesArray;
for (v = 0, i = 0; v < size[1]-1; v++)
{
for (u = 0; u < size[0]-1; u++, i += 6)
{
indexes[0] = p = v * size[0] + u;
indexes[1] = p + size[0];
indexes[2] = p + 1;
if ( !VectorCompare(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) &&
!VectorCompare(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) &&
!VectorCompare(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) ) {
indexes += 3;
numindexes += 3;
}
indexes[0] = p + 1;
indexes[1] = p + size[0];
indexes[2] = p + size[0] + 1;
if ( !VectorCompare(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[1]]) &&
!VectorCompare(mesh->xyz_array[indexes[0]], mesh->xyz_array[indexes[2]]) &&
!VectorCompare(mesh->xyz_array[indexes[1]], mesh->xyz_array[indexes[2]]) ) {
indexes += 3;
numindexes += 3;
}
}
}
// allocate and fill index table
mesh->numindexes = numindexes;
mesh->indexes = (index_t *)Hunk_TempAllocMore ( numindexes * sizeof(index_t));
memcpy (mesh->indexes, tempIndexesArray, numindexes * sizeof(index_t) );
return mesh;
}
#ifdef Q3SHADERS
#undef Hunk_TempAllocMore
#endif
void CModQ3_LoadRFaces (lump_t *l)
{
#ifndef Q3SHADERS
int polysize = sizeof(glpoly_t) - VERTEXSIZE*sizeof(float);
glpoly_t *p;
int rv, fi;
int gv, v;
#endif
q3dface_t *in;
msurface_t *out;
mplane_t *pl;
int count;
int surfnum;
int numverts, numindexes;
int fv;
mesh_t *mesh;
extern cvar_t gl_shaders;
int shaders = 1;//0;//gl_shaders.value;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
count = l->filelen / sizeof(*in);
out = Hunk_AllocName ( count*sizeof(*out), loadmodel->name );
pl = Hunk_AllocName (count*sizeof(*pl), loadmodel->name);//create a new array of planes for speed.
loadmodel->surfaces = out;
loadmodel->numsurfaces = count;
for (surfnum = 0; surfnum < count; surfnum++, out++, in++, pl++)
{
out->plane = pl;
out->texinfo = loadmodel->texinfo + LittleLong(in->shadernum);
in->facetype = LittleLong(in->facetype);
out->lightmaptexturenum = in->lightmapnum;
out->light_s = in->lightmap_x;
out->light_t = in->lightmap_y;
out->extents[0] = (in->lightmap_width-1)<<4;
out->extents[1] = (in->lightmap_height-1)<<4;
out->samples = loadmodel->lightdata + 3*(out->light_s + out->light_t*128 + out->lightmaptexturenum*128*128);
if (out->lightmaptexturenum<0)
out->samples=NULL;
fv = LittleLong(in->firstvertex);
{
vec3_t v[3];
VectorCopy(map_verts[fv+0], v[0]);
VectorCopy(map_verts[fv+1], v[1]);
VectorCopy(map_verts[fv+2], v[2]);
PlaneFromPoints(v, pl);
CategorizePlane(pl);
}
/*
if (in->fognum!=-1)
continue;
*/
if (map_surfaces[in->shadernum].c.value == 0 || map_surfaces[in->shadernum].c.value & Q3CONTENTS_TRANSLUCENT)
//q3dm10's thingie is 0
out->flags |= SURF_DRAWALPHA;
if (loadmodel->texinfo[in->shadernum].flags & SURF_SKY)
out->flags |= SURF_DRAWSKY;
#ifdef Q3SHADERS
if (!out->texinfo->texture->shader && shaders)
{
extern cvar_t r_vertexlight;
if (in->facetype == MST_FLARE)
out->texinfo->texture->shader = R_RegisterShader_Flare (out->texinfo->texture->name);
else if (in->facetype == MST_TRIANGLE_SOUP || r_vertexlight.value)
out->texinfo->texture->shader = R_RegisterShader_Vertex (out->texinfo->texture->name);
else
out->texinfo->texture->shader = R_RegisterShader(out->texinfo->texture->name);
}
if (in->fognum == -1 || !map_numfogs)
out->fog = NULL;
else
out->fog = map_fogs + in->fognum;
#endif
if (map_surfaces[in->shadernum].c.flags & (Q3SURF_NODRAW | Q3SURF_SKIP))
{
if (map_surfaces[in->shadernum].c.flags & Q3SURF_SKIP)
Con_Printf("Surface skip\n");
out->mesh = NULL;
out->polys = NULL;
}
else if (in->facetype == MST_PATCH)
{
out->mesh = GL_CreateMeshForPatch(loadmodel, in);
#ifdef Q3SHADERS
if (!out->texinfo->texture->shader)
{
out->polys = GL_MeshToGLPoly(out->mesh);
out->mesh = NULL;
}
#endif
}
else if (in->facetype == MST_PLANAR || in->facetype == MST_TRIANGLE_SOUP)
{
numindexes = LittleLong(in->num_indexes);
numverts = LittleLong(in->num_vertices);
if (numindexes%3)
Host_Error("mesh indexes should be multiples of 3");
#ifdef Q3SHADERS
out->mesh = Hunk_Alloc(sizeof(mesh_t) + (sizeof(vec3_t)) * numverts);
out->mesh->normals_array= map_normals_array + LittleLong(in->firstvertex);
out->mesh->colors_array = map_colors_array + LittleLong(in->firstvertex);
out->mesh->indexes = map_surfindexes + LittleLong(in->firstindex);
out->mesh->xyz_array = map_verts + LittleLong(in->firstvertex);
out->mesh->st_array = map_vertstmexcoords + LittleLong(in->firstvertex);
out->mesh->lmst_array = map_vertlstmexcoords + LittleLong(in->firstvertex);
out->mesh->numindexes = numindexes;
out->mesh->numvertexes = numverts;
#else
p = Hunk_AllocName (polysize*numindexes/3, "SDList");
fv = LittleLong(in->firstvertex);
fi = LittleLong(in->firstindex);
for (gv = 0; gv < numindexes; )
{
for (v = gv; v < gv+3; v++)
{
rv = fv+map_surfindexes[fi+v];
p->verts[v%3][0] = map_verts[rv][0];
p->verts[v%3][1] = map_verts[rv][1];
p->verts[v%3][2] = map_verts[rv][2];
p->verts[v%3][3] = map_vertstmexcoords[rv][0];
p->verts[v%3][4] = map_vertstmexcoords[rv][1];
p->verts[v%3][5] = map_vertlstmexcoords[rv][0];
p->verts[v%3][6] = map_vertlstmexcoords[rv][1];
}
gv+=3;
p->next = out->polys;
p->numverts = 3;
out->polys = p;
p = (glpoly_t *)((char *)p + polysize);
}
#endif
#ifdef Q3SHADERS
if (!out->texinfo->texture->shader)
{
out->polys = GL_MeshToGLPoly(out->mesh);
out->mesh = NULL;
}
#endif
}
else
{
// int r, g, b;
extern index_t r_quad_indexes[6];
mesh = out->mesh = (mesh_t *)Hunk_Alloc ( sizeof(mesh_t));
mesh->xyz_array = (vec4_t *)Hunk_Alloc ( sizeof(vec4_t));
mesh->numvertexes = 1;
mesh->indexes = r_quad_indexes;
mesh->numindexes = 6;
// VectorCopy ( out->origin, mesh->xyz_array[0] );
/* r = LittleFloat ( in->lightmapVecs[0][0] ) * 255.0f;
r = bound ( 0, r, 255 );
g = LittleFloat ( in->lightmapVecs[0][1] ) * 255.0f;
g = bound ( 0, g, 255 );
b = LittleFloat ( in->lightmapVecs[0][2] ) * 255.0f;
b = bound ( 0, b, 255 );
out->dlightbits = (unsigned int)COLOR_RGB ( r, g, b );
*/ }
}
}
#endif
void CModQ3_LoadLeafFaces (lump_t *l)
{
int i, j, count;
int *in;
int *out;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count > MAX_Q2MAP_LEAFFACES)
Host_Error ("Map has too many leaffaces");
out = BZ_Malloc ( count*sizeof(*out) );
map_leaffaces = out;
numleaffaces = count;
for ( i=0 ; i<count ; i++)
{
j = LittleLong ( in[i] );
if (j < 0 || j >= numfaces)
Host_Error ("CMod_LoadLeafFaces: bad surface number");
out[i] = j;
}
}
void CModQ3_LoadNodes (lump_t *l)
{
int i, j, count, p;
q3dnode_t *in;
mnode_t *out;
//dnode_t
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
count = l->filelen / sizeof(*in);
out = map_nodes;//Hunk_AllocName ( count*sizeof(*out), loadname);
if (count > MAX_MAP_NODES)
Host_Error("Too many nodes on map");
numnodes = count;
loadmodel->nodes = out;
loadmodel->numnodes = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
for (j=0 ; j<3 ; j++)
{
out->minmaxs[j] = LittleLong (in->mins[j]);
out->minmaxs[3+j] = LittleLong (in->maxs[j]);
}
p = LittleLong(in->plane);
out->plane = loadmodel->planes + p;
out->firstsurface = 0;//LittleShort (in->firstface);
out->numsurfaces = 0;//LittleShort (in->numfaces);
out->contents = -1;
for (j=0 ; j<2 ; j++)
{
p = LittleLong (in->children[j]);
out->childnum[j] = p;
if (p >= 0)
{
out->children[j] = loadmodel->nodes + p;
}
else
out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
}
}
CMod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
}
void CModQ3_LoadBrushes (lump_t *l)
{
q3dbrush_t *in;
q2cbrush_t *out;
int i, count;
int shaderref;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count > MAX_Q2MAP_BRUSHES)
Host_Error ("Map has too many brushes");
out = map_brushes;
numbrushes = count;
for (i=0 ; i<count ; i++, out++, in++)
{
shaderref = LittleLong ( in->shadernum );
out->contents = map_surfaces[shaderref].c.value;
out->firstbrushside = LittleLong ( in->firstside );
out->numsides = LittleLong ( in->num_sides );
}
}
void CModQ3_LoadLeafs (lump_t *l)
{
int i, j;
mleaf_t *out;
q3dleaf_t *in;
int count;
q2cbrush_t *brush;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count < 1)
Host_Error ("Map with no leafs");
// need to save space for box planes
if (count > MAX_Q2MAP_PLANES)
Host_Error ("Map has too many planes");
if (count > MAX_MAP_LEAFS)
Host_Error("Too many nodes on map");
out = map_leafs;
numleafs = count;
numclusters = 0;
loadmodel->leafs = out;
loadmodel->numleafs = count;
emptyleaf = -1;
for ( i=0 ; i<count ; i++, in++, out++)
{
for (j = 0; j < 3; j++)
{
out->minmaxs[0+j] = LittleFloat(in->mins[j]);
out->minmaxs[3+j] = LittleFloat(in->maxs[j]);
}
out->cluster = LittleLong ( in->cluster );
out->area = LittleLong ( in->area ) + 1;
out->firstleafface = LittleLong ( in->firstleafsurface );
out->numleaffaces = LittleLong ( in->num_leafsurfaces );
out->contents = 0;
out->firstleafbrush = LittleLong ( in->firstleafbrush );
out->numleafbrushes = LittleLong ( in->num_leafbrushes );
out->firstmarksurface = loadmodel->marksurfaces +
LittleLong(in->firstleafsurface);
out->nummarksurfaces = LittleLong(in->num_leafsurfaces);
if (out->minmaxs[0] > out->minmaxs[3+0] || out->minmaxs[1] > out->minmaxs[3+1] ||
out->minmaxs[2] > out->minmaxs[3+2] || VectorCompare (out->minmaxs, out->minmaxs+3))
{
out->nummarksurfaces = 0;
}
for ( j=0 ; j<out->numleafbrushes ; j++)
{
brush = &map_brushes[map_leafbrushes[out->firstleafbrush + j]];
out->contents |= brush->contents;
}
if ( out->area >= numareas ) {
numareas = out->area + 1;
}
if ( !out->contents ) {
emptyleaf = i;
}
}
// if map doesn't have an empty leaf - force one
if ( emptyleaf == -1 ) {
if (numleafs >= MAX_MAP_LEAFS-1)
Host_Error ("Map does not have an empty leaf");
out->cluster = -1;
out->area = -1;
out->numleafbrushes = 0;
out->contents = 0;
out->firstleafbrush = 0;
Con_DPrintf ( "Forcing an empty leaf: %i\n", numleafs );
emptyleaf = numleafs++;
}
}
void CModQ3_LoadPlanes (lump_t *l)
{
int i, j;
mplane_t *out;
Q3PLANE_t *in;
int count;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
count = l->filelen / sizeof(*in);
out = map_planes;//Hunk_AllocName ( count*2*sizeof(*out), loadname);
numplanes = count;
loadmodel->planes = out;
loadmodel->numplanes = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
for (j=0 ; j<3 ; j++)
{
out->normal[j] = LittleFloat (in->n[j]);
}
out->dist = LittleFloat (in->d);
CategorizePlane(out);
}
}
void CModQ3_LoadLeafBrushes (lump_t *l)
{
int i;
int *out;
int *in;
int count;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
if (count < 1)
Host_Error ("Map with no planes");
// need to save space for box planes
if (count > MAX_Q2MAP_LEAFBRUSHES)
Host_Error ("Map has too many leafbrushes");
out = map_leafbrushes;
numleafbrushes = count;
for ( i=0 ; i<count ; i++, in++, out++)
*out = LittleLong (*in);
}
void CModQ3_LoadBrushSides (lump_t *l)
{
int i, j;
q2cbrushside_t *out;
q3dbrushside_t *in;
int count;
int num;
in = (void *)(cmod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size");
count = l->filelen / sizeof(*in);
// need to save space for box planes
if (count > MAX_Q2MAP_BRUSHSIDES)
Host_Error ("Map has too many planes");
out = map_brushsides;
numbrushsides = count;
for ( i=0 ; i<count ; i++, in++, out++)
{
num = LittleLong (in->planenum);
out->plane = &map_planes[num];
j = LittleLong (in->texinfo);
if (j >= numtexinfo)
Host_Error ("Bad brushside texinfo");
out->surface = &map_surfaces[j];
}
}
void CModQ3_LoadVisibility (lump_t *l)
{
numvisibility = l->filelen;
if (l->filelen > MAX_Q2MAP_VISIBILITY+sizeof(int)*2)
Host_Error ("Map has too large visibility lump");
loadmodel->vis = (q2dvis_t *)map_q3pvs;
memcpy (map_visibility, cmod_base + l->fileofs, l->filelen);
numclusters = map_q3pvs->numclusters = LittleLong (map_q3pvs->numclusters);
map_q3pvs->rowsize = LittleLong (map_q3pvs->rowsize);
}
#ifndef SERVERONLY
void CModQ3_LoadLightgrid (lump_t *l)
{
dq3gridlight_t *in;
dq3gridlight_t *out;
q3lightgridinfo_t *grid;
int count;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
count = l->filelen / sizeof(*in);
grid = Hunk_AllocName (sizeof(q3lightgridinfo_t) + (count-1)*sizeof(*out), loadmodel->name );
out = grid->lightgrid;
loadmodel->lightgrid = grid;
grid->numlightgridelems = count;
// lightgrid is all 8 bit
memcpy ( out, in, count*sizeof(*out) );
}
#endif
#ifndef SERVERONLY
qbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out);
int CM_GetQ2Palette (void)
{
char *f = (void *)COM_LoadMallocFile("pics/colormap.pcx");
if (!f)
{
Con_Printf ("Couldn't find pics/colormap.pcx");
return -1;
}
if (!ReadPCXPalette(f, com_filesize, d_q28to24table))
{
Con_Printf ("Couldn't read pics/colormap.pcx");
BZ_Free(f);
return -1;
}
BZ_Free(f);
#ifdef RGLQUAKE
{
extern float vid_gamma;
float f, inf;
qbyte palette[768];
qbyte *pal;
int i;
pal = d_q28to24table;
for (i=0 ; i<768 ; i++)
{
f = pow ( (pal[i]+1)/256.0 , vid_gamma );
inf = f*255 + 0.5;
if (inf < 0)
inf = 0;
if (inf > 255)
inf = 255;
palette[i] = inf;
}
memcpy (d_q28to24table, palette, sizeof(palette));
}
#endif
return 0;
}
#endif
void CM_OpenAllPortals(char *ents) //this is a compleate hack. About as compleate as possible.
{ //q2 levels contain a thingie called area portals. Basically, doors can seperate two areas and
//the engine knows when this portal is open, and weather to send ents from both sides of the door
//or not. It's not just ents, but also walls. We want to just open them by default and hope the
//progs knows how to close them.
char style[8];
char name[64];
if (!map_autoopenportals.value)
return;
while(*ents)
{
if (*ents == '{') //an entity
{
ents++;
*style = '\0';
*name = '\0';
while (*ents)
{
ents = COM_Parse(ents);
if (!strcmp(com_token, "classname"))
{
ents = COM_ParseOut(ents, name, sizeof(name));
}
else if (!strcmp(com_token, "style"))
{
ents = COM_ParseOut(ents, style, sizeof(style));
}
else if (*com_token == '}')
break;
else
ents = COM_Parse(ents); //other field
ents++;
}
if (!strcmp(name, "func_areaportal"))
{
CMQ2_SetAreaPortalState(atoi(style), true);
}
}
ents++;
}
}
void CMQ3_CalcPHS (void)
{
int rowbytes, rowwords;
int i, j, k, l, index;
int bitbyte;
unsigned *dest, *src;
qbyte *scan;
int count, vcount;
int numclusters;
Con_DPrintf ("Building PHS...\n");
rowwords = map_q3pvs->rowsize / sizeof(long);
rowbytes = map_q3pvs->rowsize;
memset ( map_q3phs, 0, MAX_Q2MAP_VISIBILITY );
map_q3phs->rowsize = map_q3pvs->rowsize;
map_q3phs->numclusters = numclusters = map_q3pvs->numclusters;
vcount = 0;
for (i=0 ; i<numclusters ; i++)
{
scan = CM_ClusterPVS ( i, NULL );
for (j=0 ; j<numclusters ; j++)
{
if ( scan[j>>3] & (1<<(j&7)) )
{
vcount++;
}
}
}
count = 0;
scan = (qbyte *)map_q3pvs->data;
dest = (unsigned *)((qbyte *)map_q3phs + 8);
for (i=0 ; i<numclusters ; i++, dest += rowwords, scan += rowbytes)
{
memcpy (dest, scan, rowbytes);
for (j=0 ; j<rowbytes ; j++)
{
bitbyte = scan[j];
if (!bitbyte)
continue;
for (k=0 ; k<8 ; k++)
{
if (! (bitbyte & (1<<k)) )
continue;
// OR this pvs row into the phs
index = (j<<3) + k;
if (index >= numclusters)
Host_Error ("CM_CalcPHS: Bad bit in PVS"); // pad bits should be 0
src = (unsigned *)((qbyte*)map_q3pvs->data) + index*rowwords;
for (l=0 ; l<rowwords ; l++)
dest[l] |= src[l];
}
}
for (j=0 ; j<numclusters ; j++)
if ( ((qbyte *)dest)[j>>3] & (1<<(j&7)) )
count++;
}
Con_Printf ("Average clusters visible / hearable / total: %i / %i / %i\n"
, vcount/numclusters, count/numclusters, numclusters);
}
qbyte *CM_LeafnumPVS (int leafnum, model_t *model, qbyte *buffer)
{
return CM_ClusterPVS(CM_LeafCluster(leafnum), buffer);
}
int CM_ModelPointLeafnum (vec3_t p, model_t *mdl)
{
return CM_PointLeafnum(p);
}
#ifndef SERVERONLY
#define GLQ2BSP_LightPointValues GLQ1BSP_LightPointValues
#define SWQ2BSP_LightPointValues SWQ1BSP_LightPointValues
extern int r_dlightframecount;
void Q2BSP_MarkLights (dlight_t *light, int bit, mnode_t *node)
{
mplane_t *splitplane;
float dist;
msurface_t *surf;
int i;
if (node->contents != -1)
return;
splitplane = node->plane;
dist = DotProduct (light->origin, splitplane->normal) - splitplane->dist;
if (dist > light->radius)
{
Q2BSP_MarkLights (light, bit, node->children[0]);
return;
}
if (dist < -light->radius)
{
Q2BSP_MarkLights (light, bit, node->children[1]);
return;
}
// mark the polygons
surf = cl.worldmodel->surfaces + node->firstsurface;
for (i=0 ; i<node->numsurfaces ; i++, surf++)
{
if (surf->dlightframe != r_dlightframecount)
{
surf->dlightbits = 0;
surf->dlightframe = r_dlightframecount;
}
surf->dlightbits |= bit;
}
Q2BSP_MarkLights (light, bit, node->children[0]);
Q2BSP_MarkLights (light, bit, node->children[1]);
}
#ifndef SERVERONLY
#ifdef RGLQUAKE
void GLR_StainSurf (msurface_t *surf, float *parms);
void GLR_Q2BSP_StainNode (mnode_t *node, float *parms)
{
mplane_t *splitplane;
float dist;
msurface_t *surf;
int i;
if (node->contents != -1)
return;
splitplane = node->plane;
dist = DotProduct ((parms+1), splitplane->normal) - splitplane->dist;
if (dist > (*parms))
{
GLR_Q2BSP_StainNode (node->children[0], parms);
return;
}
if (dist < (-*parms))
{
GLR_Q2BSP_StainNode (node->children[1], parms);
return;
}
// mark the polygons
surf = cl.worldmodel->surfaces + node->firstsurface;
for (i=0 ; i<node->numsurfaces ; i++, surf++)
{
if (surf->flags&~(SURF_DONTWARP|SURF_PLANEBACK))
continue;
GLR_StainSurf(surf, parms);
}
GLR_Q2BSP_StainNode (node->children[0], parms);
GLR_Q2BSP_StainNode (node->children[1], parms);
}
#endif
#ifdef SWQUAKE
void SWR_StainSurf (msurface_t *surf, float *parms);
void SWR_Q2BSP_StainNode (mnode_t *node, float *parms)
{
mplane_t *splitplane;
float dist;
msurface_t *surf;
int i;
if (node->contents != -1)
return;
splitplane = node->plane;
dist = DotProduct ((parms+1), splitplane->normal) - splitplane->dist;
if (dist > (*parms))
{
SWR_Q2BSP_StainNode (node->children[0], parms);
return;
}
if (dist < (-*parms))
{
SWR_Q2BSP_StainNode (node->children[1], parms);
return;
}
// mark the polygons
surf = cl.worldmodel->surfaces + node->firstsurface;
for (i=0 ; i<node->numsurfaces ; i++, surf++)
{
if (surf->flags&~(SURF_DONTWARP|SURF_PLANEBACK))
continue;
SWR_StainSurf(surf, parms);
}
SWR_Q2BSP_StainNode (node->children[0], parms);
SWR_Q2BSP_StainNode (node->children[1], parms);
}
#endif
#endif
#endif
void Q2BSP_FatPVS (vec3_t org, qboolean add);
qboolean Q2BSP_EdictInFatPVS(edict_t *ent);
void Q2BSP_FindTouchedLeafs(edict_t *ent);
void GLQ2BSP_LightPointValues(vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir);
void SWQ2BSP_LightPointValues(vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir);
/*
==================
CM_LoadMap
Loads in the map and all submodels
==================
*/
q2cmodel_t *CM_LoadMap (char *name, char *filein, qboolean clientload, unsigned *checksum)
{
unsigned *buf;
int i,j;
q2dheader_t header;
int length;
static unsigned last_checksum;
// free old stuff
numplanes = 0;
numnodes = 0;
numleafs = 0;
numcmodels = 0;
numvisibility = 0;
numentitychars = 0;
map_entitystring[0] = 0;
map_name[0] = 0;
loadmodel->type = mod_brush;
if (!name || !name[0])
{
numleafs = 1;
numclusters = 1;
numareas = 1;
*checksum = 0;
return &map_cmodels[0]; // cinematic servers won't have anything at all
}
//
// load the file
//
buf = (unsigned *)filein;
length = com_filesize;
if (!buf)
Host_Error ("Couldn't load %s", name);
last_checksum = LittleLong (Com_BlockChecksum (buf, length));
*checksum = last_checksum;
header = *(q2dheader_t *)(buf);
header.ident = LittleLong(header.ident);
header.version = LittleLong(header.version);
cmod_base = mod_base = (qbyte *)buf;
switch(header.version)
{
default:
if (header.version != Q2BSPVERSION && header.version != Q3BSPVERSION)
Host_Error ("Quake 2 or Quake 3 based BSP with unknown header (%i should be %i or %i)"
, name, header.version, Q2BSPVERSION, Q3BSPVERSION);
break;
#if 1
case Q3BSPVERSION+1:
case Q3BSPVERSION:
mapisq3 = true;
loadmodel->fromgame = fg_quake3;
for (i=0 ; i<Q3LUMPS_TOTAL ; i++)
{
header.lumps[i].filelen = LittleLong (header.lumps[i].filelen);
header.lumps[i].fileofs = LittleLong (header.lumps[i].fileofs);
}
/*
#ifndef SERVERONLY
GLMod_LoadVertexes (&header.lumps[Q3LUMP_DRAWVERTS]);
// GLMod_LoadEdges (&header.lumps[Q3LUMP_EDGES]);
// GLMod_LoadSurfedges (&header.lumps[Q3LUMP_SURFEDGES]);
GLMod_LoadLighting (&header.lumps[Q3LUMP_LIGHTMAPS]);
#endif
CModQ3_LoadShaders (&header.lumps[Q3LUMP_SHADERS]);
CModQ3_LoadPlanes (&header.lumps[Q3LUMP_PLANES]);
CModQ3_LoadLeafBrushes (&header.lumps[Q3LUMP_LEAFBRUSHES]);
CModQ3_LoadBrushes (&header.lumps[Q3LUMP_BRUSHES]);
CModQ3_LoadBrushSides (&header.lumps[Q3LUMP_BRUSHSIDES]);
#ifndef SERVERONLY
CMod_LoadTexInfo (&header.lumps[Q3LUMP_SHADERS]);
CMod_LoadFaces (&header.lumps[Q3LUMP_SURFACES]);
// GLMod_LoadMarksurfaces (&header.lumps[Q3LUMP_LEAFFACES]);
#endif
CMod_LoadVisibility (&header.lumps[Q3LUMP_VISIBILITY]);
CModQ3_LoadSubmodels (&header.lumps[Q3LUMP_MODELS]);
CModQ3_LoadLeafs (&header.lumps[Q3LUMP_LEAFS]);
CModQ3_LoadNodes (&header.lumps[Q3LUMP_NODES]);
// CMod_LoadAreas (&header.lumps[Q3LUMP_AREAS]);
// CMod_LoadAreaPortals (&header.lumps[Q3LUMP_AREAPORTALS]);
CMod_LoadEntityString (&header.lumps[Q3LUMP_ENTITIES]);
*/
switch(qrenderer)
{
#if defined(RGLQUAKE)
case QR_OPENGL:
#endif
case QR_NONE: //dedicated only
mapisq3 = true;
CModQ3_LoadShaders (&header.lumps[Q3LUMP_SHADERS]);
CModQ3_LoadPlanes (&header.lumps[Q3LUMP_PLANES]);
CModQ3_LoadLeafBrushes (&header.lumps[Q3LUMP_LEAFBRUSHES]);
CModQ3_LoadBrushes (&header.lumps[Q3LUMP_BRUSHES]);
CModQ3_LoadBrushSides (&header.lumps[Q3LUMP_BRUSHSIDES]);
CModQ3_LoadVertexes (&header.lumps[Q3LUMP_DRAWVERTS]);
CModQ3_LoadFaces (&header.lumps[Q3LUMP_SURFACES]);
#if defined(RGLQUAKE)
if (qrenderer == QR_OPENGL)
{
GLMod_LoadLighting (&header.lumps[Q3LUMP_LIGHTMAPS]); //fixme: duplicated loading.
CModQ3_LoadLightgrid (&header.lumps[Q3LUMP_LIGHTGRID]);
CModQ3_LoadIndexes (&header.lumps[Q3LUMP_DRAWINDEXES]);
#ifdef Q3SHADERS
if (header.version != Q3BSPVERSION+1)
CModQ3_LoadFogs (&header.lumps[Q3LUMP_FOGS]);
else
map_numfogs = 0;
#endif
CModQ3_LoadRFaces (&header.lumps[Q3LUMP_SURFACES]);
CModQ3_LoadMarksurfaces (&header.lumps[Q3LUMP_LEAFSURFACES]); //fixme: duplicated loading.
}
#endif
CModQ3_LoadLeafFaces (&header.lumps[Q3LUMP_LEAFSURFACES]);
CModQ3_LoadLeafs (&header.lumps[Q3LUMP_LEAFS]);
CModQ3_LoadNodes (&header.lumps[Q3LUMP_NODES]);
CModQ3_LoadSubmodels (&header.lumps[Q3LUMP_MODELS]);
CModQ3_LoadVisibility (&header.lumps[Q3LUMP_VISIBILITY]);
CMod_LoadEntityString (&header.lumps[Q3LUMP_ENTITIES]);
loadmodel->funcs.FatPVS = Q2BSP_FatPVS;
loadmodel->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS;
loadmodel->funcs.FindTouchedLeafs_Q1 = Q2BSP_FindTouchedLeafs;
loadmodel->funcs.LeafPVS = CM_LeafnumPVS;
loadmodel->funcs.LeafForPoint = CM_ModelPointLeafnum;
#if defined(RGLQUAKE)
loadmodel->funcs.LightPointValues = GLQ3_LightGrid;
loadmodel->funcs.StainNode = GLR_Q2BSP_StainNode;
loadmodel->funcs.MarkLights = Q2BSP_MarkLights;
#endif
#ifndef SERVERONLY
//light grid info
if (loadmodel->lightgrid)
{
float maxs;
q3lightgridinfo_t *lg = loadmodel->lightgrid;
if ( lg->gridSize[0] < 1 || lg->gridSize[1] < 1 || lg->gridSize[2] < 1 )
{
lg->gridSize[0] = 64;
lg->gridSize[1] = 64;
lg->gridSize[2] = 128;
}
for ( i = 0; i < 3; i++ )
{
lg->gridMins[i] = lg->gridSize[i] * ceil( (map_cmodels->mins[i] + 1) / lg->gridSize[i] );
maxs = lg->gridSize[i] * floor( (map_cmodels->maxs[i] - 1) / lg->gridSize[i] );
lg->gridBounds[i] = (maxs - lg->gridMins[i])/lg->gridSize[i] + 1;
}
lg->gridBounds[3] = lg->gridBounds[1] * lg->gridBounds[0];
}
#endif
CM_CreatePatchesForLeafs (); //for clipping
CMQ3_CalcPHS();
// BZ_Free(map_verts);
BZ_Free(map_faces);
BZ_Free(map_leaffaces);
break;
default:
Sys_Error("Quake 3 maps are supported with dedicated servers and opengl only\n");
}
break;
#endif
case Q2BSPVERSION:
mapisq3 = false;
for (i=0 ; i<Q2HEADER_LUMPS ; i++)
{
header.lumps[i].filelen = LittleLong (header.lumps[i].filelen);
header.lumps[i].fileofs = LittleLong (header.lumps[i].fileofs);
}
#ifndef SERVERONLY
if (CM_GetQ2Palette())
memcpy(d_q28to24table, host_basepal, 768);
#endif
switch(qrenderer)
{
case QR_NONE: //dedicated only
CMod_LoadSurfaces (&header.lumps[Q2LUMP_TEXINFO]);
CMod_LoadLeafBrushes (&header.lumps[Q2LUMP_LEAFBRUSHES]);
CMod_LoadPlanes (&header.lumps[Q2LUMP_PLANES]);
CMod_LoadVisibility (&header.lumps[Q2LUMP_VISIBILITY]);
CMod_LoadBrushes (&header.lumps[Q2LUMP_BRUSHES]);
CMod_LoadBrushSides (&header.lumps[Q2LUMP_BRUSHSIDES]);
CMod_LoadSubmodels (&header.lumps[Q2LUMP_MODELS]);
CMod_LoadLeafs (&header.lumps[Q2LUMP_LEAFS]);
CMod_LoadNodes (&header.lumps[Q2LUMP_NODES]);
CMod_LoadAreas (&header.lumps[Q2LUMP_AREAS]);
CMod_LoadAreaPortals (&header.lumps[Q2LUMP_AREAPORTALS]);
CMod_LoadEntityString (&header.lumps[Q2LUMP_ENTITIES]);
loadmodel->funcs.FatPVS = Q2BSP_FatPVS;
loadmodel->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS;
loadmodel->funcs.FindTouchedLeafs_Q1 = Q2BSP_FindTouchedLeafs;
loadmodel->funcs.LightPointValues = NULL;
loadmodel->funcs.StainNode = NULL;
loadmodel->funcs.MarkLights = NULL;
loadmodel->funcs.LeafPVS = CM_LeafnumPVS;
loadmodel->funcs.LeafForPoint = CM_ModelPointLeafnum;
break;
#if defined(RGLQUAKE)
case QR_OPENGL:
// load into heap
#ifndef SERVERONLY
GLMod_LoadVertexes (&header.lumps[Q2LUMP_VERTEXES]);
GLMod_LoadEdges (&header.lumps[Q2LUMP_EDGES]);
GLMod_LoadSurfedges (&header.lumps[Q2LUMP_SURFEDGES]);
GLMod_LoadLighting (&header.lumps[Q2LUMP_LIGHTING]);
#endif
CMod_LoadSurfaces (&header.lumps[Q2LUMP_TEXINFO]);
CMod_LoadLeafBrushes (&header.lumps[Q2LUMP_LEAFBRUSHES]);
CMod_LoadPlanes (&header.lumps[Q2LUMP_PLANES]);
#ifndef SERVERONLY
CMod_LoadTexInfo (&header.lumps[Q2LUMP_TEXINFO]);
CMod_LoadFaces (&header.lumps[Q2LUMP_FACES]);
GLMod_LoadMarksurfaces (&header.lumps[Q2LUMP_LEAFFACES]);
#endif
CMod_LoadVisibility (&header.lumps[Q2LUMP_VISIBILITY]);
CMod_LoadBrushes (&header.lumps[Q2LUMP_BRUSHES]);
CMod_LoadBrushSides (&header.lumps[Q2LUMP_BRUSHSIDES]);
CMod_LoadSubmodels (&header.lumps[Q2LUMP_MODELS]);
CMod_LoadLeafs (&header.lumps[Q2LUMP_LEAFS]);
CMod_LoadNodes (&header.lumps[Q2LUMP_NODES]);
CMod_LoadAreas (&header.lumps[Q2LUMP_AREAS]);
CMod_LoadAreaPortals (&header.lumps[Q2LUMP_AREAPORTALS]);
CMod_LoadEntityString (&header.lumps[Q2LUMP_ENTITIES]);
loadmodel->funcs.FatPVS = Q2BSP_FatPVS;
loadmodel->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS;
loadmodel->funcs.FindTouchedLeafs_Q1 = Q2BSP_FindTouchedLeafs;
loadmodel->funcs.LightPointValues = GLQ2BSP_LightPointValues;
loadmodel->funcs.StainNode = GLR_Q2BSP_StainNode;
loadmodel->funcs.MarkLights = Q2BSP_MarkLights;
loadmodel->funcs.LeafPVS = CM_LeafnumPVS;
loadmodel->funcs.LeafForPoint = CM_ModelPointLeafnum;
break;
#endif
#if defined(SWQUAKE)
case QR_SOFTWARE:
// load into heap
#ifndef SERVERONLY
SWMod_LoadVertexes (&header.lumps[Q2LUMP_VERTEXES]);
SWMod_LoadEdges (&header.lumps[Q2LUMP_EDGES]);
SWMod_LoadSurfedges (&header.lumps[Q2LUMP_SURFEDGES]);
SWMod_LoadLighting (&header.lumps[Q2LUMP_LIGHTING]);
#endif
CMod_LoadSurfaces (&header.lumps[Q2LUMP_TEXINFO]);
CMod_LoadLeafBrushes (&header.lumps[Q2LUMP_LEAFBRUSHES]);
CMod_LoadPlanes (&header.lumps[Q2LUMP_PLANES]);
#ifndef SERVERONLY
CMod_LoadTexInfo (&header.lumps[Q2LUMP_TEXINFO]);
CMod_LoadFaces (&header.lumps[Q2LUMP_FACES]);
SWMod_LoadMarksurfaces (&header.lumps[Q2LUMP_LEAFFACES]);
#endif
CMod_LoadVisibility (&header.lumps[Q2LUMP_VISIBILITY]);
CMod_LoadBrushes (&header.lumps[Q2LUMP_BRUSHES]);
CMod_LoadBrushSides (&header.lumps[Q2LUMP_BRUSHSIDES]);
CMod_LoadSubmodels (&header.lumps[Q2LUMP_MODELS]);
CMod_LoadLeafs (&header.lumps[Q2LUMP_LEAFS]);
CMod_LoadNodes (&header.lumps[Q2LUMP_NODES]);
CMod_LoadAreas (&header.lumps[Q2LUMP_AREAS]);
CMod_LoadAreaPortals (&header.lumps[Q2LUMP_AREAPORTALS]);
CMod_LoadEntityString (&header.lumps[Q2LUMP_ENTITIES]);
loadmodel->funcs.FatPVS = Q2BSP_FatPVS;
loadmodel->funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS;
loadmodel->funcs.FindTouchedLeafs_Q1 = Q2BSP_FindTouchedLeafs;
loadmodel->funcs.LightPointValues = SWQ2BSP_LightPointValues;
loadmodel->funcs.StainNode = SWR_Q2BSP_StainNode;
loadmodel->funcs.MarkLights = Q2BSP_MarkLights;
loadmodel->funcs.LeafPVS = CM_LeafnumPVS;
loadmodel->funcs.LeafForPoint = CM_ModelPointLeafnum;
break;
#endif
default:
Sys_Error("Bad internal renderer on q2 map load\n");
}
}
#ifndef SERVERONLY
Mod_ParseInfoFromEntityLump(loadmodel->entities); //only done for client's world model (or server if the server is loading it for client)
#endif
CM_InitBoxHull ();
if (map_autoopenportals.value)
memset (portalopen, 1, sizeof(portalopen)); //open them all. Used for progs that havn't got a clue.
else
memset (portalopen, 0, sizeof(portalopen)); //make them start closed.
FloodAreaConnections ();
strcpy (map_name, name);
loadmodel->numsubmodels = CM_NumInlineModels();
{
model_t *mod = loadmodel;
mod->hulls[0].firstclipnode = map_cmodels[0].headnode;
mod->hulls[0].available = true;
Q2BSP_SetHullFuncs(&mod->hulls[0]);
for (j=1 ; j<MAX_MAP_HULLSM ; j++)
{
mod->hulls[j].firstclipnode = map_cmodels[0].headnode;
mod->hulls[j].available = false;
Q2BSP_SetHullFuncs(&mod->hulls[j]);
}
for (i=1 ; i< loadmodel->numsubmodels ; i++)
{
q2cmodel_t *bm;
char name[10];
sprintf (name, "*%i", i);
loadmodel = Mod_FindName (name);
*loadmodel = *mod;
strcpy (loadmodel->name, name);
mod = loadmodel;
bm = CM_InlineModel (name);
mod->hulls[0].firstclipnode = bm->headnode;
mod->hulls[j].available = true;
mod->nummodelsurfaces = bm->numsurfaces;
mod->firstmodelsurface = bm->firstsurface;
Q2BSP_SetHullFuncs(&mod->hulls[0]);
for (j=1 ; j<MAX_MAP_HULLSM ; j++)
{
mod->hulls[j].firstclipnode = bm->headnode;
mod->hulls[j].lastclipnode = mod->numclipnodes-1;
mod->hulls[j].available = false;
Q2BSP_SetHullFuncs(&mod->hulls[j]);
}
VectorCopy (bm->maxs, mod->maxs);
VectorCopy (bm->mins, mod->mins);
#ifndef SERVERONLY
mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
R_DefaultTrail(mod);
#endif
}
}
return &map_cmodels[0];
}
/*
==================
CM_InlineModel
==================
*/
q2cmodel_t *CM_InlineModel (char *name)
{
int num;
if (!name)
Host_Error("Bad model\n");
else if (name[0] != '*')
Host_Error("Bad model\n");
num = atoi (name+1);
if (num < 1 || num >= numcmodels)
Host_Error ("CM_InlineModel: bad number");
return &map_cmodels[num];
}
int CM_NumClusters (void)
{
return numclusters;
}
int CM_ClusterSize (void)
{
return map_q3pvs->rowsize ? map_q3pvs->rowsize : MAX_Q2MAP_LEAFS / 8;
}
int CM_NumInlineModels (void)
{
return numcmodels;
}
char *CM_EntityString (void)
{
return map_entitystring;
}
int CM_LeafContents (int leafnum)
{
if (leafnum < 0 || leafnum >= numleafs)
Host_Error ("CM_LeafContents: bad number");
return map_leafs[leafnum].contents;
}
int CM_LeafCluster (int leafnum)
{
if (leafnum < 0 || leafnum >= numleafs)
Host_Error ("CM_LeafCluster: bad number");
return map_leafs[leafnum].cluster;
}
int CM_LeafArea (int leafnum)
{
if (leafnum < 0 || leafnum >= numleafs)
Host_Error ("CM_LeafArea: bad number");
return map_leafs[leafnum].area;
}
//=======================================================================
mplane_t *box_planes;
int box_headnode;
q2cbrush_t *box_brush;
mleaf_t *box_leaf;
model_t box_model;
/*
===================
CM_InitBoxHull
Set up the planes and nodes so that the six floats of a bounding box
can just be stored out and get a proper clipping hull structure.
===================
*/
void CM_InitBoxHull (void)
{
int i;
int side;
mnode_t *c;
mplane_t *p;
q2cbrushside_t *s;
box_model.funcs.FatPVS = Q2BSP_FatPVS;
box_model.funcs.EdictInFatPVS = Q2BSP_EdictInFatPVS;
box_model.funcs.FindTouchedLeafs_Q1 = Q2BSP_FindTouchedLeafs;
#ifndef SERVERONLY
box_model.funcs.MarkLights = Q2BSP_MarkLights;
#endif
box_model.funcs.LeafPVS = CM_LeafnumPVS;
box_model.funcs.LeafForPoint = CM_ModelPointLeafnum;
box_model.hulls[0].available = true;
Q2BSP_SetHullFuncs(&box_model.hulls[0]);
box_headnode = numnodes;
box_planes = &map_planes[numplanes];
if (numnodes+6 > MAX_Q2MAP_NODES
|| numbrushes+1 > MAX_Q2MAP_BRUSHES
|| numleafbrushes+1 > MAX_Q2MAP_LEAFBRUSHES
|| numbrushsides+6 > MAX_Q2MAP_BRUSHSIDES
|| numplanes+12 > MAX_Q2MAP_PLANES)
Host_Error ("Not enough room for box tree");
box_brush = &map_brushes[numbrushes];
box_brush->numsides = 6;
box_brush->firstbrushside = numbrushsides;
box_brush->contents = Q2CONTENTS_MONSTER;
box_leaf = &map_leafs[numleafs];
box_leaf->contents = Q2CONTENTS_MONSTER;
box_leaf->firstleafbrush = numleafbrushes;
box_leaf->numleafbrushes = 1;
map_leafbrushes[numleafbrushes] = numbrushes;
for (i=0 ; i<6 ; i++)
{
side = i&1;
// brush sides
s = &map_brushsides[numbrushsides+i];
s->plane = map_planes + (numplanes+i*2+side);
s->surface = &nullsurface;
// nodes
c = &map_nodes[box_headnode+i];
c->plane = map_planes + (numplanes+i*2);
c->childnum[side] = -1 - emptyleaf;
if (i != 5)
c->childnum[side^1] = box_headnode+i + 1;
else
c->childnum[side^1] = -1 - numleafs;
// planes
p = &box_planes[i*2];
p->type = i>>1;
p->signbits = 0;
VectorClear (p->normal);
p->normal[i>>1] = 1;
p = &box_planes[i*2+1];
p->type = 3 + (i>>1);
p->signbits = 0;
VectorClear (p->normal);
p->normal[i>>1] = -1;
}
}
/*
===================
CM_HeadnodeForBox
To keep everything totally uniform, bounding boxes are turned into small
BSP trees instead of being compared directly.
===================
*/
int CM_HeadnodeForBox (vec3_t mins, vec3_t maxs)
{
box_planes[0].dist = maxs[0];
box_planes[1].dist = -maxs[0];
box_planes[2].dist = mins[0];
box_planes[3].dist = -mins[0];
box_planes[4].dist = maxs[1];
box_planes[5].dist = -maxs[1];
box_planes[6].dist = mins[1];
box_planes[7].dist = -mins[1];
box_planes[8].dist = maxs[2];
box_planes[9].dist = -maxs[2];
box_planes[10].dist = mins[2];
box_planes[11].dist = -mins[2];
return box_headnode;
}
model_t *CM_TempBoxModel(vec3_t mins, vec3_t maxs)
{
box_model.hulls[0].firstclipnode = CM_HeadnodeForBox(mins, maxs);
return &box_model;
}
/*
==================
CM_PointLeafnum_r
==================
*/
int CM_PointLeafnum_r (vec3_t p, int num)
{
float d;
mnode_t *node;
mplane_t *plane;
while (num >= 0)
{
node = map_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];
}
c_pointcontents++; // optimize counter
return -1 - num;
}
int CM_PointLeafnum (vec3_t p)
{
if (!numplanes)
return 0; // sound may call this without map loaded
return CM_PointLeafnum_r (p, 0);
}
/*
=============
CM_BoxLeafnums
Fills in a list of all the leafs touched
=============
*/
int leaf_count, leaf_maxcount;
int *leaf_list;
float *leaf_mins, *leaf_maxs;
int leaf_topnode;
void CM_BoxLeafnums_r (int nodenum)
{
mplane_t *plane;
mnode_t *node;
int s;
while (1)
{
if (nodenum < 0)
{
if (leaf_count >= leaf_maxcount)
{
// Com_Printf ("CM_BoxLeafnums_r: overflow\n");
return;
}
leaf_list[leaf_count++] = -1 - nodenum;
return;
}
node = &map_nodes[nodenum];
plane = node->plane;
// s = BoxOnPlaneSide (leaf_mins, leaf_maxs, 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;
CM_BoxLeafnums_r (node->childnum[0]);
nodenum = node->childnum[1];
}
}
}
int CM_BoxLeafnums_headnode (vec3_t mins, vec3_t maxs, int *list, int listsize, int headnode, int *topnode)
{
leaf_list = list;
leaf_count = 0;
leaf_maxcount = listsize;
leaf_mins = mins;
leaf_maxs = maxs;
leaf_topnode = -1;
CM_BoxLeafnums_r (headnode);
if (topnode)
*topnode = leaf_topnode;
return leaf_count;
}
int CM_BoxLeafnums (vec3_t mins, vec3_t maxs, int *list, int listsize, int *topnode)
{
return CM_BoxLeafnums_headnode (mins, maxs, list,
listsize, map_cmodels[0].headnode, topnode);
}
/*
==================
CM_PointContents
==================
*/
#define PlaneDiff(point,plane) (((plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal)) - (plane)->dist)
int CM_PointContents (vec3_t p, int headnode)
{
int i, j, contents;
mleaf_t *leaf;
q2cbrush_t *brush;
q2cbrushside_t *brushside;
if (!numnodes) // map not loaded
return 0;
i = CM_PointLeafnum_r (p, headnode);
if (!mapisq3)
return map_leafs[i].contents; //q2 is simple.
leaf = &map_leafs[i];
// if ( leaf->contents & CONTENTS_NODROP ) {
// contents = CONTENTS_NODROP;
// } else {
contents = 0;
// }
for (i = 0; i < leaf->numleafbrushes; i++)
{
brush = &map_brushes[map_leafbrushes[leaf->firstleafbrush + i]];
// check if brush actually adds something to contents
if ( (contents & brush->contents) == brush->contents ) {
continue;
}
brushside = &map_brushsides[brush->firstbrushside];
for ( j = 0; j < brush->numsides; j++, brushside++ )
{
if ( PlaneDiff (p, brushside->plane) > 0 )
break;
}
if (j == brush->numsides)
contents |= brush->contents;
}
return contents;
}
/*
==================
CM_TransformedPointContents
Handles offseting and rotation of the end points for moving and
rotating entities
==================
*/
int CM_TransformedPointContents (vec3_t p, int headnode, vec3_t origin, vec3_t angles)
{
vec3_t p_l;
vec3_t temp;
vec3_t forward, right, up;
// subtract origin offset
VectorSubtract (p, origin, p_l);
// rotate start and end into the models frame of reference
if (headnode != box_headnode &&
(angles[0] || angles[1] || angles[2]) )
{
AngleVectors (angles, forward, right, up);
VectorCopy (p_l, temp);
p_l[0] = DotProduct (temp, forward);
p_l[1] = -DotProduct (temp, right);
p_l[2] = DotProduct (temp, up);
}
return CM_PointContents(p, headnode);
}
/*
===============================================================================
BOX TRACING
===============================================================================
*/
// 1/32 epsilon to keep floating point happy
#define DIST_EPSILON (0.03125)
vec3_t trace_start, trace_end;
vec3_t trace_mins, trace_maxs;
vec3_t trace_extents;
vec3_t trace_absmins, trace_absmaxs;
trace_t trace_trace;
int trace_contents;
qboolean trace_ispoint; // optimized case
/*
================
CM_ClipBoxToBrush
================
*/
void CM_ClipBoxToBrush (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2,
trace_t *trace, q2cbrush_t *brush)
{
int i, j;
mplane_t *plane, *clipplane;
float dist;
float enterfrac, leavefrac;
vec3_t ofs;
float d1, d2;
qboolean getout, startout;
float f;
q2cbrushside_t *side, *leadside;
enterfrac = -1;
leavefrac = 1;
clipplane = NULL;
if (!brush->numsides)
return;
c_brush_traces++;
getout = false;
startout = false;
leadside = NULL;
for (i=0 ; i<brush->numsides ; i++)
{
side = &map_brushsides[brush->firstbrushside+i];
plane = side->plane;
// FIXME: special case for axial
if (!trace_ispoint)
{ // general box case
// push the plane out apropriately for mins/maxs
// FIXME: use signbits into 8 way lookup for each mins/maxs
for (j=0 ; j<3 ; j++)
{
if (plane->normal[j] < 0)
ofs[j] = maxs[j];
else
ofs[j] = mins[j];
}
dist = DotProduct (ofs, plane->normal);
dist = plane->dist - dist;
}
else
{ // special point case
dist = plane->dist;
}
d1 = DotProduct (p1, plane->normal) - dist;
d2 = DotProduct (p2, plane->normal) - dist;
if (d2 > 0)
getout = true; // endpoint is not in solid
if (d1 > 0)
startout = true;
// if completely in front of face, no intersection
if (d1 > 0 && d2 >= d1)
return;
if (d1 <= 0 && d2 <= 0)
continue;
// crosses face
if (d1 > d2)
{ // enter
f = (d1-DIST_EPSILON) / (d1-d2);
if (f > enterfrac)
{
enterfrac = f;
clipplane = plane;
leadside = side;
}
}
else
{ // leave
f = (d1+DIST_EPSILON) / (d1-d2);
if (f < leavefrac)
leavefrac = f;
}
}
if (!startout)
{ // original point was inside brush
trace->startsolid = true;
if (!getout)
trace->allsolid = true;
return;
}
if (enterfrac < leavefrac)
{
if (enterfrac > -1 && enterfrac < trace->fraction)
{
if (enterfrac < 0)
enterfrac = 0;
trace->fraction = enterfrac;
trace->plane.dist = clipplane->dist;
VectorCopy(clipplane->normal, trace->plane.normal);
trace->surface = &(leadside->surface->c);
trace->contents = brush->contents;
}
}
}
void CM_ClipBoxToPatch (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2,
trace_t *trace, q2cbrush_t *brush)
{
int i, j;
mplane_t *plane, *clipplane;
float enterfrac, leavefrac;
vec3_t ofs;
float d1, d2;
float dist;
qboolean startout;
float f;
q2cbrushside_t *side, *leadside;
if (!brush->numsides)
return;
c_brush_traces++;
enterfrac = -1;
leavefrac = 1;
clipplane = NULL;
startout = false;
leadside = NULL;
for (i=0 ; i<brush->numsides ; i++)
{
side = &map_brushsides[brush->firstbrushside+i];
plane = side->plane;
if (!trace_ispoint)
{ // general box case
// push the plane out apropriately for mins/maxs
// FIXME: use signbits into 8 way lookup for each mins/maxs
for (j=0 ; j<3 ; j++)
{
if (plane->normal[j] < 0)
ofs[j] = maxs[j];
else
ofs[j] = mins[j];
}
dist = DotProduct (ofs, plane->normal);
dist = plane->dist - dist;
}
else
{ // special point case
dist = plane->dist;
}
d1 = DotProduct (p1, plane->normal) - dist;
d2 = DotProduct (p2, plane->normal) - dist;
if (d1 > 0)
startout = true;
// if completely in front of face, no intersection
if (d1 > 0 && d2 >= d1)
return;
if (d1 <= 0 && d2 <= 0)
continue;
// crosses face
if (d1 > d2)
{ // enter
f = (d1-DIST_EPSILON) / (d1-d2);
if (f > enterfrac)
{
enterfrac = f;
clipplane = plane;
leadside = side;
}
}
else
{ // leave
f = (d1 + DIST_EPSILON) / (d1-d2);
if (f < leavefrac)
leavefrac = f;
}
}
if (!startout)
return; // original point is inside the patch
if (enterfrac < leavefrac)
{
if (leadside && leadside->surface
&& enterfrac < trace->fraction)
{
if (enterfrac < 0)
enterfrac = 0;
trace->fraction = enterfrac;
trace->plane.dist = clipplane->dist;
VectorCopy(clipplane->normal, trace->plane.normal);
trace->surface = &leadside->surface->c;
trace->contents = brush->contents;
}
}
}
/*
================
CM_TestBoxInBrush
================
*/
void CM_TestBoxInBrush (vec3_t mins, vec3_t maxs, vec3_t p1,
trace_t *trace, q2cbrush_t *brush)
{
int i, j;
mplane_t *plane;
float dist;
vec3_t ofs;
float d1;
q2cbrushside_t *side;
if (!brush->numsides)
return;
for (i=0 ; i<brush->numsides ; i++)
{
side = &map_brushsides[brush->firstbrushside+i];
plane = side->plane;
// FIXME: special case for axial
// general box case
// push the plane out apropriately for mins/maxs
// FIXME: use signbits into 8 way lookup for each mins/maxs
for (j=0 ; j<3 ; j++)
{
if (plane->normal[j] < 0)
ofs[j] = maxs[j];
else
ofs[j] = mins[j];
}
dist = DotProduct (ofs, plane->normal);
dist = plane->dist - dist;
d1 = DotProduct (p1, plane->normal) - dist;
// if completely in front of face, no intersection
if (d1 > 0)
return;
}
// inside this brush
trace->startsolid = trace->allsolid = true;
trace->fraction = 0;
trace->contents = brush->contents;
}
void CM_TestBoxInPatch (vec3_t mins, vec3_t maxs, vec3_t p1,
trace_t *trace, q2cbrush_t *brush)
{
int i, j;
mplane_t *plane;
vec3_t ofs;
float dist;
float d1, maxdist;
q2cbrushside_t *side;
if (!brush->numsides)
return;
maxdist = -9999;
for (i=0 ; i<brush->numsides ; i++)
{
side = &map_brushsides[brush->firstbrushside+i];
plane = side->plane;
// general box case
// push the plane out apropriately for mins/maxs
// FIXME: use signbits into 8 way lookup for each mins/maxs
for (j=0 ; j<3 ; j++)
{
if (plane->normal[j] < 0)
ofs[j] = maxs[j];
else
ofs[j] = mins[j];
}
dist = DotProduct (ofs, plane->normal);
dist = plane->dist - dist;
d1 = DotProduct (p1, plane->normal) - dist;
// if completely in front of face, no intersection
if (d1 > 0)
return;
if (side->surface && d1 > maxdist)
maxdist = d1;
}
// FIXME
if (maxdist < -0.25)
return; // deep inside the patch
// inside this patch
trace->startsolid = trace->allsolid = true;
trace->fraction = 0;
trace->contents = brush->contents;
}
/*
================
CM_TraceToLeaf
================
*/
void CM_TraceToLeaf (int leafnum)
{
int k, j;
int brushnum;
mleaf_t *leaf;
q2cbrush_t *b;
int patchnum;
q3cpatch_t *patch;
leaf = &map_leafs[leafnum];
if ( !(leaf->contents & trace_contents))
return;
// trace line against all brushes in the leaf
for (k=0 ; k<leaf->numleafbrushes ; k++)
{
brushnum = map_leafbrushes[leaf->firstleafbrush+k];
b = &map_brushes[brushnum];
if (b->checkcount == checkcount)
continue; // already checked this brush in another leaf
b->checkcount = checkcount;
if ( !(b->contents & trace_contents))
continue;
CM_ClipBoxToBrush (trace_mins, trace_maxs, trace_start, trace_end, &trace_trace, b);
if (!trace_trace.fraction)
return;
}
if (!mapisq3 || map_noCurves.value)
return;
// trace line against all patches in the leaf
for (k = 0; k < leaf->numleafpatches; k++)
{
patchnum = map_leafpatches[leaf->firstleafpatch+k];
patch = &map_patches[patchnum];
if (patch->checkcount == checkcount)
continue; // already checked this patch in another leaf
patch->checkcount = checkcount;
if ( !(patch->surface->c.value & trace_contents) )
continue;
if ( !BoundsIntersect(patch->absmins, patch->absmaxs, trace_absmins, trace_absmaxs) )
continue;
for (j = 0; j < patch->numbrushes; j++)
{
CM_ClipBoxToPatch (trace_mins, trace_maxs, trace_start, trace_end, &trace_trace, &patch->brushes[j]);
if (!trace_trace.fraction)
return;
}
}
}
/*
================
CM_TestInLeaf
================
*/
void CM_TestInLeaf (int leafnum)
{
int k, j;
int brushnum;
int patchnum;
mleaf_t *leaf;
q2cbrush_t *b;
q3cpatch_t *patch;
leaf = &map_leafs[leafnum];
if ( !(leaf->contents & trace_contents))
return;
// trace line against all brushes in the leaf
for (k=0 ; k<leaf->numleafbrushes ; k++)
{
brushnum = map_leafbrushes[leaf->firstleafbrush+k];
b = &map_brushes[brushnum];
if (b->checkcount == checkcount)
continue; // already checked this brush in another leaf
b->checkcount = checkcount;
if ( !(b->contents & trace_contents))
continue;
CM_TestBoxInBrush (trace_mins, trace_maxs, trace_start, &trace_trace, b);
if (!trace_trace.fraction)
return;
}
if (!mapisq3 || map_noCurves.value)
return;
// trace line against all patches in the leaf
for (k = 0; k < leaf->numleafpatches; k++)
{
patchnum = map_leafpatches[leaf->firstleafpatch+k];
patch = &map_patches[patchnum];
if (patch->checkcount == checkcount)
continue; // already checked this patch in another leaf
patch->checkcount = checkcount;
if ( !(patch->surface->c.value & trace_contents) )
continue;
if ( !BoundsIntersect(patch->absmins, patch->absmaxs, trace_absmins, trace_absmaxs) )
continue;
for (j = 0; j < patch->numbrushes; j++)
{
CM_TestBoxInPatch (trace_mins, trace_maxs, trace_start, &trace_trace, &patch->brushes[j]);
if (!trace_trace.fraction)
return;
}
}
}
/*
==================
CM_RecursiveHullCheck
==================
*/
void CM_RecursiveHullCheck (int num, float p1f, float p2f, vec3_t p1, vec3_t p2)
{
mnode_t *node;
mplane_t *plane;
float t1, t2, offset;
float frac, frac2;
float idist;
int i;
vec3_t mid;
int side;
float midf;
if (trace_trace.fraction <= p1f)
return; // already hit something nearer
// if < 0, we are in a leaf node
if (num < 0)
{
CM_TraceToLeaf (-1-num);
return;
}
//
// find the point distances to the seperating plane
// and the offset for the size of the box
//
node = map_nodes + num;
plane = node->plane;
if (plane->type < 3)
{
t1 = p1[plane->type] - plane->dist;
t2 = p2[plane->type] - plane->dist;
offset = trace_extents[plane->type];
}
else
{
t1 = DotProduct (plane->normal, p1) - plane->dist;
t2 = DotProduct (plane->normal, p2) - plane->dist;
if (trace_ispoint)
offset = 0;
else
offset = fabs(trace_extents[0]*plane->normal[0]) +
fabs(trace_extents[1]*plane->normal[1]) +
fabs(trace_extents[2]*plane->normal[2]);
}
#if 0
CM_RecursiveHullCheck (node->childnum[0], p1f, p2f, p1, p2);
CM_RecursiveHullCheck (node->childnum[1], p1f, p2f, p1, p2);
return;
#endif
// see which sides we need to consider
if (t1 >= offset && t2 >= offset)
{
CM_RecursiveHullCheck (node->childnum[0], p1f, p2f, p1, p2);
return;
}
if (t1 < -offset && t2 < -offset)
{
CM_RecursiveHullCheck (node->childnum[1], p1f, p2f, p1, p2);
return;
}
// put the crosspoint DIST_EPSILON pixels on the near side
if (t1 < t2)
{
idist = 1.0/(t1-t2);
side = 1;
frac2 = (t1 + offset + DIST_EPSILON)*idist;
frac = (t1 - offset + DIST_EPSILON)*idist;
}
else if (t1 > t2)
{
idist = 1.0/(t1-t2);
side = 0;
frac2 = (t1 - offset - DIST_EPSILON)*idist;
frac = (t1 + offset + DIST_EPSILON)*idist;
}
else
{
side = 0;
frac = 1;
frac2 = 0;
}
// move up to the node
if (frac < 0)
frac = 0;
if (frac > 1)
frac = 1;
midf = p1f + (p2f - p1f)*frac;
for (i=0 ; i<3 ; i++)
mid[i] = p1[i] + frac*(p2[i] - p1[i]);
CM_RecursiveHullCheck (node->childnum[side], p1f, midf, p1, mid);
// go past the node
if (frac2 < 0)
frac2 = 0;
if (frac2 > 1)
frac2 = 1;
midf = p1f + (p2f - p1f)*frac2;
for (i=0 ; i<3 ; i++)
mid[i] = p1[i] + frac2*(p2[i] - p1[i]);
CM_RecursiveHullCheck (node->childnum[side^1], midf, p2f, mid, p2);
}
//======================================================================
/*
==================
CM_BoxTrace
==================
*/
trace_t CM_BoxTrace (vec3_t start, vec3_t end,
vec3_t mins, vec3_t maxs,
int headnode, int brushmask)
{
int i;
#if ADJ
int moved;
#endif
vec3_t point;
checkcount++; // for multi-check avoidance
c_traces++; // for statistics, may be zeroed
// fill in a default trace
memset (&trace_trace, 0, sizeof(trace_trace));
trace_trace.fraction = 1;
trace_trace.surface = &(nullsurface.c);
if (!numnodes) // map not loaded
return trace_trace;
trace_contents = brushmask;
VectorCopy (start, trace_start);
VectorCopy (end, trace_end);
VectorCopy (mins, trace_mins);
VectorCopy (maxs, trace_maxs);
// build a bounding box of the entire move (for patches)
ClearBounds (trace_absmins, trace_absmaxs);
VectorAdd (start, trace_mins, point);
AddPointToBounds (point, trace_absmins, trace_absmaxs);
VectorAdd (start, trace_maxs, point);
AddPointToBounds (point, trace_absmins, trace_absmaxs);
VectorAdd (end, trace_mins, point);
AddPointToBounds (point, trace_absmins, trace_absmaxs);
VectorAdd (end, trace_maxs, point);
AddPointToBounds (point, trace_absmins, trace_absmaxs);
//
// check for position test special case
//
if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2])
{
int leafs[1024];
int i, numleafs;
vec3_t c1, c2;
int topnode;
#if ADJ
if (-mins[2] != maxs[2]) //be prepared to move the thing up to counter the different min/max
{
moved = (trace_maxs[2] - trace_mins[2])/2;
trace_mins[2] = -moved;
trace_maxs[2] = moved;
trace_extents[2] = -trace_mins[2] > trace_maxs[2] ? -trace_mins[2] : trace_maxs[2];
moved = (maxs[2] - trace_maxs[2]);
}
trace_start[2]+=moved;
trace_end[2]+=moved;
#endif
VectorAdd (start, mins, c1);
VectorAdd (start, maxs, c2);
for (i=0 ; i<3 ; i++)
{
c1[i] -= 1;
c2[i] += 1;
}
numleafs = CM_BoxLeafnums_headnode (c1, c2, leafs, sizeof(leafs)/sizeof(leafs[0]), headnode, &topnode);
for (i=0 ; i<numleafs ; i++)
{
CM_TestInLeaf (leafs[i]);
if (trace_trace.allsolid)
break;
}
VectorCopy (start, trace_trace.endpos);
#if ADJ
trace_trace.endpos[2] -= moved;
#endif
return trace_trace;
}
#if ADJ
moved = 0;
#endif
//
// check for point special case
//
if (trace_mins[0] == 0 && trace_mins[1] == 0 && trace_mins[2] == 0
&& trace_maxs[0] == 0 && trace_maxs[1] == 0 && trace_maxs[2] == 0)
{
trace_ispoint = true;
VectorClear (trace_extents);
}
else
{
trace_ispoint = false;
trace_extents[0] = -trace_mins[0] > trace_maxs[0] ? -trace_mins[0] : trace_maxs[0];
trace_extents[1] = -trace_mins[1] > trace_maxs[1] ? -trace_mins[1] : trace_maxs[1];
trace_extents[2] = -trace_mins[2] > trace_maxs[2] ? -trace_mins[2] : trace_maxs[2];
#if ADJ
if (-mins[2] != maxs[2]) //be prepared to move the thing up to counter the different min/max
{
moved = (trace_maxs[2] - trace_mins[2])/2;
trace_mins[2] = -moved;
trace_maxs[2] = moved;
trace_extents[2] = -trace_mins[2] > trace_maxs[2] ? -trace_mins[2] : trace_maxs[2];
moved = (maxs[2] - trace_maxs[2]);
}
trace_start[2]+=moved;
trace_end[2]+=moved;
#endif
}
//
// general sweeping through world
//
CM_RecursiveHullCheck (headnode, 0, 1, trace_start, trace_end);
if (trace_trace.fraction == 1)
{
VectorCopy (trace_end, trace_trace.endpos);
}
else
{
for (i=0 ; i<3 ; i++)
trace_trace.endpos[i] = trace_start[i] + trace_trace.fraction * (trace_end[i] - trace_start[i]);
}
#if ADJ
trace_trace.endpos[2] -= moved;
#endif
return trace_trace;
}
/*
==================
CM_TransformedBoxTrace
Handles offseting and rotation of the end points for moving and
rotating entities
==================
*/
#ifdef _MSC_VER
#pragma optimize( "", off )
#endif
trace_t CM_TransformedBoxTrace (vec3_t start, vec3_t end,
vec3_t mins, vec3_t maxs,
int headnode, int brushmask,
vec3_t origin, vec3_t angles)
{
trace_t trace;
vec3_t start_l, end_l;
vec3_t a;
vec3_t forward, right, up;
vec3_t temp;
qboolean rotated;
// subtract origin offset
VectorSubtract (start, origin, start_l);
VectorSubtract (end, origin, end_l);
// rotate start and end into the models frame of reference
if (headnode != box_headnode &&
(angles[0] || angles[1] || angles[2]) )
rotated = true;
else
rotated = false;
if (rotated)
{
AngleVectors (angles, forward, right, up);
VectorCopy (start_l, temp);
start_l[0] = DotProduct (temp, forward);
start_l[1] = -DotProduct (temp, right);
start_l[2] = DotProduct (temp, up);
VectorCopy (end_l, temp);
end_l[0] = DotProduct (temp, forward);
end_l[1] = -DotProduct (temp, right);
end_l[2] = DotProduct (temp, up);
}
// sweep the box through the model
trace = CM_BoxTrace (start_l, end_l, mins, maxs, headnode, brushmask);
if (rotated && trace.fraction != 1.0)
{
// FIXME: figure out how to do this with existing angles
VectorNegate (angles, a);
AngleVectors (a, forward, right, up);
VectorCopy (trace.plane.normal, temp);
trace.plane.normal[0] = DotProduct (temp, forward);
trace.plane.normal[1] = -DotProduct (temp, right);
trace.plane.normal[2] = DotProduct (temp, up);
}
if (trace.fraction == 1)
{
VectorCopy(end, trace.endpos);
}
else
{
trace.endpos[0] = start[0] + trace.fraction * (end[0] - start[0]);
trace.endpos[1] = start[1] + trace.fraction * (end[1] - start[1]);
trace.endpos[2] = start[2] + trace.fraction * (end[2] - start[2]);
}
return trace;
}
#ifdef _MSC_VER
#pragma optimize( "", on )
#endif
/*
===============================================================================
PVS / PHS
===============================================================================
*/
/*
===================
CM_DecompressVis
===================
*/
/*
qbyte *Mod_Q2DecompressVis (qbyte *in, model_t *model)
{
static qbyte decompressed[MAX_MAP_LEAFS/8];
int c;
qbyte *out;
int row;
row = (model->vis->numclusters+7)>>3;
out = decompressed;
if (!in)
{ // no vis info, so make all visible
while (row)
{
*out++ = 0xff;
row--;
}
return decompressed;
}
do
{
if (*in)
{
*out++ = *in++;
continue;
}
c = in[1];
in += 2;
while (c)
{
*out++ = 0;
c--;
}
} while (out - decompressed < row);
return decompressed;
}
#define DVIS_PVS 0
#define DVIS_PHS 1
qbyte *Mod_ClusterPVS (int cluster, model_t *model)
{
if (cluster == -1 || !model->vis)
return mod_novis;
return Mod_Q2DecompressVis ( (qbyte *)model->vis + model->vis->bitofs[cluster][DVIS_PVS],
model);
}
*/
void CM_DecompressVis (qbyte *in, qbyte *out)
{
int c;
qbyte *out_p;
int row;
row = (numclusters+7)>>3;
out_p = out;
if (!in || !numvisibility)
{ // no vis info, so make all visible
while (row)
{
*out_p++ = 0xff;
row--;
}
return;
}
do
{
if (*in)
{
*out_p++ = *in++;
continue;
}
c = in[1];
in += 2;
if ((out_p - out) + c > row)
{
c = row - (out_p - out);
Con_DPrintf ("warning: Vis decompression overrun\n");
}
while (c)
{
*out_p++ = 0;
c--;
}
} while (out_p - out < row);
}
qbyte pvsrow[MAX_Q2MAP_LEAFS/8];
qbyte phsrow[MAX_Q2MAP_LEAFS/8];
qbyte *CM_ClusterPVS (int cluster, qbyte *buffer)
{
if (!buffer)
buffer = pvsrow;
if (mapisq3)
{
if (cluster != -1 && map_q3pvs->numclusters)
{
return (qbyte *)map_q3pvs->data + cluster * map_q3pvs->rowsize;
}
else
{
memset (buffer, 0, (numclusters+7)>>3);
return buffer;
}
}
if (cluster == -1)
memset (buffer, 0, (numclusters+7)>>3);
else
CM_DecompressVis (map_visibility + map_q2vis->bitofs[cluster][DVIS_PVS], buffer);
return buffer;
}
qbyte *CM_ClusterPHS (int cluster)
{
if (mapisq3) //phs not working yet.
{
if (cluster != -1 && map_q3phs->numclusters)
{
return (qbyte *)map_q3phs->data + cluster * map_q3phs->rowsize;
}
else
{
memset (phsrow, 0, (numclusters+7)>>3);
return phsrow;
}
}
if (cluster == -1)
memset (phsrow, 0, (numclusters+7)>>3);
else
CM_DecompressVis (map_visibility + map_q2vis->bitofs[cluster][DVIS_PHS], phsrow);
return phsrow;
}
/*
===============================================================================
AREAPORTALS
===============================================================================
*/
void FloodArea_r (q2carea_t *area, int floodnum)
{
int i;
q2dareaportal_t *p;
if (area->floodvalid == floodvalid)
{
if (area->floodnum == floodnum)
return;
Host_Error ("FloodArea_r: reflooded");
}
area->floodnum = floodnum;
area->floodvalid = floodvalid;
p = &map_areaportals[area->firstareaportal];
for (i=0 ; i<area->numareaportals ; i++, p++)
{
if (portalopen[p->portalnum])
FloodArea_r (&map_q2areas[p->otherarea], floodnum);
}
}
/*
====================
FloodAreaConnections
====================
*/
void FloodAreaConnections (void)
{
int i, j;
q2carea_t *area;
int floodnum;
if (mapisq3)
{
// area 0 is not used
for (i=1 ; i<numareas ; i++)
{
for ( j = 1; j < numareas; j++ ) {
map_q3areas[i].numareaportals[j] = ( j == i );
}
}
return;
}
// all current floods are now invalid
floodvalid++;
floodnum = 0;
// area 0 is not used
for (i=1 ; i<numareas ; i++)
{
area = &map_q2areas[i];
if (area->floodvalid == floodvalid)
continue; // already flooded into
floodnum++;
FloodArea_r (area, floodnum);
}
}
void CMQ2_SetAreaPortalState (int portalnum, qboolean open)
{
if (mapisq3)
Host_Error ("CMQ2_SetAreaPortalState on q3 map");
if (portalnum > numareaportals)
Host_Error ("areaportal > numareaportals");
if (portalopen[portalnum] == open)
return;
portalopen[portalnum] = open;
FloodAreaConnections ();
return;
}
void CMQ3_SetAreaPortalState (int area1, int area2, qboolean open)
{
if (!mapisq3)
Host_Error ("CMQ3_SetAreaPortalState on non-q3 map");
if (area1 > numareas || area2 > numareas)
Host_Error ("CMQ3_SetAreaPortalState: area > numareas");
if (open)
{
map_q3areas[area1].numareaportals[area2]++;
map_q3areas[area2].numareaportals[area1]++;
}
else
{
map_q3areas[area1].numareaportals[area2]--;
map_q3areas[area2].numareaportals[area1]--;
}
}
qboolean CM_AreasConnected (int area1, int area2)
{
if (map_noareas.value)
return true;
if (area1 > numareas || area2 > numareas)
Host_Error ("area > numareas");
if (mapisq3)
{
int i;
for (i=1 ; i<numareas ; i++)
{
if ( map_q3areas[i].numareaportals[area1] &&
map_q3areas[i].numareaportals[area2] )
return true;
}
}
else
{
if (map_q2areas[area1].floodnum == map_q2areas[area2].floodnum)
return true;
}
return false;
}
/*
=================
CM_WriteAreaBits
Writes a length qbyte followed by a bit vector of all the areas
that area in the same flood as the area parameter
This is used by the client refreshes to cull visibility
=================
*/
int CM_WriteAreaBits (qbyte *buffer, int area)
{
int i;
int floodnum;
int bytes;
bytes = (numareas+7)>>3;
if (map_noareas.value)
{ // for debugging, send everything
memset (buffer, 255, bytes);
}
else
{
memset (buffer, 0, bytes);
if (mapisq3)
{
for (i=0 ; i<numareas ; i++)
{
if (!area || CM_AreasConnected ( i, area ) || i == area)
buffer[i>>3] |= 1<<(i&7);
}
}
else
{
floodnum = map_q2areas[area].floodnum;
for (i=0 ; i<numareas ; i++)
{
if (map_q2areas[i].floodnum == floodnum || !area)
buffer[i>>3] |= 1<<(i&7);
}
}
}
return bytes;
}
/*
===================
CM_WritePortalState
Writes the portal state to a savegame file
===================
*/
void CM_WritePortalState (FILE *f)
{
fwrite (portalopen, sizeof(portalopen), 1, f);
}
/*
===================
CM_ReadPortalState
Reads the portal state from a savegame file
and recalculates the area connections
===================
*/
void CM_ReadPortalState (FILE *f)
{
fread (portalopen, 1, sizeof(portalopen), f);
FloodAreaConnections ();
}
/*
=============
CM_HeadnodeVisible
Returns true if any leaf under headnode has a cluster that
is potentially visible
=============
*/
qboolean CM_HeadnodeVisible (int nodenum, qbyte *visbits)
{
int leafnum;
int cluster;
mnode_t *node;
if (nodenum < 0)
{
leafnum = -1-nodenum;
cluster = map_leafs[leafnum].cluster;
if (cluster == -1)
return false;
if (visbits[cluster>>3] & (1<<(cluster&7)))
return true;
return false;
}
node = &map_nodes[nodenum];
if (CM_HeadnodeVisible(node->childnum[0], visbits))
return true;
return CM_HeadnodeVisible(node->childnum[1], visbits);
}
qboolean Q2BSP_RecursiveHullCheck (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
{
trace_t ret = CM_BoxTrace(p1, p2, hull->clip_mins, hull->clip_maxs, hull->firstclipnode, MASK_SOLID);
memcpy(trace, &ret, sizeof(trace_t));
if (ret.fraction==1)
return true;
return false;
}
int Q2BSP_HullPointContents(hull_t *hull, vec3_t p)
{
int pc, ret = FTECONTENTS_EMPTY;
pc = CM_PointContents (p, hull->firstclipnode);
if (pc & (Q2CONTENTS_SOLID|Q2CONTENTS_WINDOW))
ret |= FTECONTENTS_SOLID;
if (pc & Q2CONTENTS_LAVA)
ret |= FTECONTENTS_LAVA;
if (pc & Q2CONTENTS_SLIME)
ret |= FTECONTENTS_SLIME;
if (pc & Q2CONTENTS_WATER)
ret |= FTECONTENTS_WATER;
if (pc & Q2CONTENTS_LADDER)
ret |= FTECONTENTS_LADDER;
return ret;
}
void Q2BSP_SetHullFuncs(hull_t *hull)
{
hull->funcs.RecursiveHullCheck = Q2BSP_RecursiveHullCheck;
hull->funcs.HullPointContents = Q2BSP_HullPointContents;
}
int map_checksum;
void Mod_LoadQ2BrushModel (model_t *mod, void *buffer)
{
mod->fromgame = fg_quake2;
CM_LoadMap(mod->name, buffer, true, &map_checksum);
}
void CM_Init(void) //register cvars.
{
#define MAPOPTIONS "Map Cvar Options"
Cvar_Register(&map_noareas, MAPOPTIONS);
Cvar_Register(&map_noCurves, MAPOPTIONS);
Cvar_Register(&map_autoopenportals, MAPOPTIONS);
}
#endif