#include "quakedef.h" #ifdef RGLQUAKE #include "glquake.h" #endif #define MAX_Q3MAP_INDICES 0x80000 #define MAX_Q3MAP_VERTEXES 0x80000 #define MAX_Q3MAP_BRUSHSIDES 0x30000 #define MAX_CM_BRUSHSIDES (MAX_Q3MAP_BRUSHSIDES << 1) #define MAX_CM_BRUSHES (MAX_Q2MAP_BRUSHES << 1) #define MAX_CM_PATCH_VERTS (4096) #define MAX_CM_FACES (MAX_Q2MAP_FACES) #define MAX_CM_PATCHES (0x10000) #define MAX_CM_LEAFFACES (MAX_Q2MAP_LEAFFACES) #define MAX_CM_AREAS MAX_Q2MAP_AREAS #define Q3SURF_NODRAW 0x80 // don't generate a drawsurface at all #define Q3SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes #define Q3SURF_NONSOLID 0x4000 // don't collide against curves with this set #if Q3SURF_NODRAW != SURF_NODRAW #error "nodraw isn't constant" #endif //these are in model.c (or gl_model.c) void GLMod_LoadVertexes (lump_t *l); void GLMod_LoadEdges (lump_t *l); void GLMod_LoadMarksurfaces (lump_t *l); void GLMod_LoadSurfedges (lump_t *l); void GLMod_LoadLighting (lump_t *l); void SWMod_LoadVertexes (lump_t *l); void SWMod_LoadEdges (lump_t *l); void SWMod_LoadMarksurfaces (lump_t *l); void SWMod_LoadSurfedges (lump_t *l); void SWMod_LoadLighting (lump_t *l); void Q2BSP_SetHullFuncs(hull_t *hull); qbyte areabits[MAX_Q2MAP_AREAS/8]; extern char loadname[32]; extern model_t *loadmodel; float RadiusFromBounds (vec3_t mins, vec3_t maxs) { int i; vec3_t corner; for (i=0 ; i<3 ; i++) { corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]); } return Length (corner); } void CalcSurfaceExtents (msurface_t *s) { float mins[2], maxs[2], val; int i,j, e; mvertex_t *v; mtexinfo_t *tex; int bmins[2], bmaxs[2]; mins[0] = mins[1] = 999999; maxs[0] = maxs[1] = -99999; tex = s->texinfo; for (i=0 ; inumedges ; 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. // 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; q3cface_t *map_faces; int numfaces; int *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<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 ; jnumleaffaces ; 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 ; imins[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 ; ic.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 ; iflags = 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 = Hunk_Alloc(sizeof(texture_t)); Con_Printf ("Couldn't load %s\n", name); memcpy(out->texture, &r_notexture_mip, sizeof(r_notexture_mip)); // 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 ; itexinfo[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 ; inumedges ; i++) { e = loadmodel->surfedges[s->firstedge+i]; if (e >= 0) v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; else v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; for (j=0 ; j<2 ; j++) { val = v->position[0] * tex->vecs[j][0] + v->position[1] * tex->vecs[j][1] + v->position[2] * tex->vecs[j][2] + tex->vecs[j][3]; if (val < mins[j]) mins[j] = val; if (val > maxs[j]) maxs[j] = val; } } for (i=0 ; i<2 ; i++) { bmins[i] = floor(mins[i]/16); bmaxs[i] = ceil(maxs[i]/16); s->texturemins[i] = bmins[i] * 16; s->extents[i] = (bmaxs[i] - bmins[i]) * 16; // if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512 )// 256 ) // Sys_Error ("Bad surface extents"); } }*/ /* ================= Mod_LoadFaces ================= */ #ifndef SERVERONLY void CMod_LoadFaces (lump_t *l) { dface_t *in; msurface_t *out; int i, count, surfnum; int planenum, side; int ti; extern qboolean r_usinglits; in = (void *)(cmod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = Hunk_AllocName ( (count+6)*sizeof(*out), loadname); //spare for skybox loadmodel->surfaces = out; loadmodel->numsurfaces = count; for ( surfnum=0 ; surfnumfirstedge = 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; } #endif CalcSurfaceExtents (out); // lighting info for (i=0 ; istyles[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 ; iminmaxs[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 ; ifirstbrushside = 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 ; iminmaxs[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 ; ifileofs); 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 ; inormal[j] = LittleFloat (in->normal[j]); if (out->normal[j] < 0) bits |= 1<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 ; ifileofs); 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 ; iplanenum); 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 ; inumareaportals = 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 ; iportalnum = 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 ; inumclusters ; 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= 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 ; imins[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 ; itexinfo[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; int i, count, j; vec2_t *lmout, *stout; 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) ); map_verts = out; map_vertstmexcoords = stout; map_vertlstmexcoords = lmout; numvertexes = count; for ( i=0 ; ipoint[j] ); } for ( j=0 ; j < 2 ; j++) { stout[i][j] = LittleFloat ( ((float *)in->texcoords)[j] ); lmout[i][j] = LittleFloat ( ((float *)in->texcoords)[j+2] ); } } } 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 ; ifileofs); 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 ; ifacetype = 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 //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];} vec_t VectorNormalize2 (vec3_t v, vec3_t out) { float length, ilength; length = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; if (length) { length = sqrt (length); // FIXME ilength = 1/length; out[0] = v[0]*ilength; out[1] = v[1]*ilength; out[2] = v[2]*ilength; } else { VectorClear (out); } return length; } float ColorNormalize (vec3_t in, vec3_t out) { float f = max (max (in[0], in[1]), in[2]); if ( f > 1.0 ) { f = 1.0 / f; out[0] = in[0] * f; out[1] = in[1] * f; out[2] = in[2] * f; } else { out[0] = in[0]; out[1] = in[1]; out[2] = in[2]; } return f; } int 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]; 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; int *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 ( mod->bmodel->normals_array[firstvert + i], normals[i] ); // Vector4Scale ( mod->bmodel->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 = (int *)Hunk_TempAllocMore ( numindexes * sizeof(int)); memcpy (mesh->indexes, tempIndexesArray, numindexes * sizeof(int) ); return mesh; } void CModQ3_LoadRFaces (lump_t *l) { int polysize = sizeof(glpoly_t) - VERTEXSIZE*sizeof(float); q3dface_t *in; msurface_t *out; mplane_t *pl; int count; int surfnum; int numverts, numindexes; int gv, v; int rv; int fv, fi; glpoly_t *p; mesh_t *mesh; 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(*p), 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; if (map_surfaces[in->shadernum].c.flags & (Q3SURF_NODRAW | Q3SURF_SKIP)) { out->mesh = NULL; out->polys = NULL; } else if (in->facetype == MST_PATCH) { mesh = GL_CreateMeshForPatch(loadmodel, in); out->polys = GL_MeshToGLPoly(mesh); out->mesh = NULL; } 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"); /* out->mesh = Hunk_Alloc(sizeof(mesh_t) + (sizeof(vec3_t) + sizeof(byte_vec4_t)) * numverts); out->mesh->normals_array= (vec3_t *)(out->mesh+1); out->mesh->colors_array = (byte_vec4_t *)(out->mesh->normals_array + numverts); 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; */ 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 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= 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 ; iminmaxs[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 ; ishadernum ); 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 ; iminmaxs[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 ; jnumleafbrushes ; 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 ; inormal[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 ; ifileofs); 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 ; iplanenum); 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>3] & (1<<(j&7)) ) { vcount++; } } } count = 0; scan = (qbyte *)map_q3pvs->data; dest = (unsigned *)((qbyte *)map_q3phs + 8); for (i=0 ; i= 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>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 ; inumsurfaces ; 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 ; inumsurfaces ; 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 ; inumsurfaces ; 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); if (header.version != Q2BSPVERSION && header.version != Q3BSPVERSION) Host_Error ("CMod_LoadBrushModel: %s has wrong version number (%i should be %i or %i)" , name, header.version, Q2BSPVERSION, Q3BSPVERSION); cmod_base = mod_base = (qbyte *)buf; switch(header.version) { default: Sys_Error("Bad internal renderer on q2 map load\n"); break; #if 1 case Q3BSPVERSION: mapisq3 = true; loadmodel->fromgame = fg_quake3; for (i=0 ; ifuncs.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 ; ifuncs.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 ; jhulls[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 ; jhulls[j].firstclipnode = bm->headnode; mod->hulls[j].lastclipnode = mod->numclipnodes-1; mod->hulls[j].available = false; Q2BSP_SetHullFuncs(&mod->hulls[j]); } VectorCopy (bm->maxs, mod->maxs); VectorCopy (bm->mins, mod->mins); #ifndef SERVERONLY mod->radius = RadiusFromBounds (mod->mins, mod->maxs); R_DefaultTrail(mod); #endif } } return &map_cmodels[0]; } /* ================== CM_InlineModel ================== */ q2cmodel_t *CM_InlineModel (char *name) { int num; if (!name) Host_Error("Bad model\n"); else if (name[0] != '*') Host_Error("Bad model\n"); num = atoi (name+1); if (num < 1 || num >= numcmodels) Host_Error ("CM_InlineModel: bad number"); return &map_cmodels[num]; } int CM_NumClusters (void) { return numclusters; } int CM_ClusterSize (void) { return map_q3pvs->rowsize ? map_q3pvs->rowsize : MAX_Q2MAP_LEAFS / 8; } int CM_NumInlineModels (void) { return numcmodels; } char *CM_EntityString (void) { return map_entitystring; } int CM_LeafContents (int leafnum) { if (leafnum < 0 || leafnum >= numleafs) Host_Error ("CM_LeafContents: bad number"); return map_leafs[leafnum].contents; } int CM_LeafCluster (int leafnum) { if (leafnum < 0 || leafnum >= numleafs) Host_Error ("CM_LeafCluster: bad number"); return map_leafs[leafnum].cluster; } int CM_LeafArea (int leafnum) { if (leafnum < 0 || leafnum >= numleafs) Host_Error ("CM_LeafArea: bad number"); return map_leafs[leafnum].area; } //======================================================================= mplane_t *box_planes; int box_headnode; q2cbrush_t *box_brush; mleaf_t *box_leaf; /* =================== 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_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; } /* ================== 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 ; inumsides ; 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 ; inumsides ; i++) { side = &map_brushsides[brush->firstbrushside+i]; plane = side->plane; if (!trace_ispoint) { // general box case // push the plane out apropriately for mins/maxs // FIXME: use signbits into 8 way lookup for each mins/maxs for (j=0 ; j<3 ; j++) { if (plane->normal[j] < 0) ofs[j] = maxs[j]; else ofs[j] = mins[j]; } dist = DotProduct (ofs, plane->normal); dist = plane->dist - dist; } else { // special point case dist = plane->dist; } d1 = DotProduct (p1, plane->normal) - dist; d2 = DotProduct (p2, plane->normal) - dist; if (d1 > 0) startout = true; // if completely in front of face, no intersection if (d1 > 0 && d2 >= d1) return; if (d1 <= 0 && d2 <= 0) continue; // crosses face if (d1 > d2) { // enter f = (d1-DIST_EPSILON) / (d1-d2); if (f > enterfrac) { enterfrac = f; clipplane = plane; leadside = side; } } else { // leave f = (d1 /*+ DIST_EPSILON*/) / (d1-d2); if (f < leavefrac) leavefrac = f; } } if (!startout) return; // original point is inside the patch if (enterfrac < leavefrac) { if (leadside && leadside->surface && enterfrac < trace->fraction) { if (enterfrac < 0) enterfrac = 0; trace->fraction = enterfrac; trace->plane.dist = clipplane->dist; VectorCopy(clipplane->normal, trace->plane.normal); trace->surface = &leadside->surface->c; trace->contents = brush->contents; } } } /* ================ CM_TestBoxInBrush ================ */ void CM_TestBoxInBrush (vec3_t mins, vec3_t maxs, vec3_t p1, trace_t *trace, q2cbrush_t *brush) { int i, j; mplane_t *plane; float dist; vec3_t ofs; float d1; q2cbrushside_t *side; if (!brush->numsides) return; for (i=0 ; inumsides ; 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 ; inumsides ; 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 ; knumleafbrushes ; 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 ; knumleafbrushes ; 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; int moved; 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; 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, 1024, headnode, &topnode); for (i=0 ; i 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 (-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; } // // 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]); } trace_trace.endpos[2] -= moved; 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); } 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 ; inumareaportals ; 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 ; ifloodvalid == 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>3; if (map_noareas.value) { // for debugging, send everything memset (buffer, 255, bytes); } else { memset (buffer, 0, bytes); if (mapisq3) { for (i=0 ; i>3] |= 1<<(i&7); } } else { floodnum = map_q2areas[area].floodnum; for (i=0 ; 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