mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-10 22:51:57 +00:00
ce3c561cfe
Particle system functions renamed a bit, a few other cleanups in that area. Console handling tweeked. Better rules for subconsoles and plugins. Commands are coloured if it'll be execed, which should help reduce occurences of chat being commands. tab compleation tweeked, partial compleation no longer changes the suggestion. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@895 fc73d0e0-1445-4013-8a0c-d673dee63da5
5107 lines
115 KiB
C
5107 lines
115 KiB
C
#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
|
|
|
|
#ifdef SWQUAKE
|
|
extern qboolean r_usinglits;
|
|
#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;
|
|
|
|
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_shadeq3;
|
|
|
|
int shaders = gl_shadeq3.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);
|
|
|
|
P_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 completely in front of face, no intersection
|
|
if (d1 > 0 && d2 >= d1)
|
|
return;
|
|
|
|
if (d1 > 0)
|
|
startout = true;
|
|
|
|
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
|