mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-29 23:22:01 +00:00
e7c1f9a490
make the 'identify' command assume that a single decimal number is not an address (allowing it to be handled as a user id). qcc: support default initialisers in function calls. tweak filesystem code to try to flush individual files instead of discarding the entire fs hash. fix 'snap' stuff. other tweaks... git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5058 fc73d0e0-1445-4013-8a0c-d673dee63da5
2315 lines
61 KiB
C
2315 lines
61 KiB
C
#include "quakedef.h"
|
|
#ifdef MAP_DOOM
|
|
#include "glquake.h"
|
|
#include "shader.h"
|
|
|
|
|
|
|
|
char *va2(char *buffer, size_t buffersize, const char *format, ...)
|
|
{
|
|
va_list argptr;
|
|
|
|
va_start (argptr, format);
|
|
buffer[--buffersize] = 0;
|
|
vsnprintf (buffer, buffersize, format, argptr);
|
|
va_end (argptr);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
int SignbitsForPlane (mplane_t *out);
|
|
int PlaneTypeForNormal ( vec3_t normal );
|
|
|
|
//coded from file specifications provided by:
|
|
//Matthew S Fell (msfell@aol.com)
|
|
//Unofficial Doom Specs
|
|
|
|
//(aol suck)
|
|
|
|
void Doom_SetModelFunc(model_t *mod);
|
|
int Doom_SectorNearPoint(vec3_t p);
|
|
|
|
//assumptions:
|
|
//1. That there is a node, and thus two ssectors.
|
|
//2. That the user doesn't want textures...
|
|
//3. That all segs ssectors for a single sector are all the same.
|
|
//4. That ALL sectors are fully enclosed, and not made of two areas.
|
|
//5. That no sectors are inside out.
|
|
|
|
/*FIXME:
|
|
we need to do a bsp2prt type thing (walk nodes and determine actual leaf/ssector shapes based upon those).
|
|
build sector geometry based upon this.
|
|
this is because flats in doom were implemented using a flood-fill algorithm and thus omits various unecessary inner edges, while 3d rendering apis all need tri-soup instead.
|
|
attempting to generate sane volumes from most doom maps is doomed to failure because quite often the sector values on linedefs is just buggy, resulting in some really whacky polygons that cannot be souped in any meaningful way.
|
|
this may still result in a mess of floor polygons outside the world, so be sure to draw those last, for early-z.
|
|
*/
|
|
|
|
enum {
|
|
THING_PLAYER = 1,
|
|
THING_PLAYER2 = 2,
|
|
THING_PLAYER3 = 3,
|
|
THING_PLAYER4 = 4,
|
|
THING_DMSPAWN = 11,
|
|
|
|
//we need to balance weapons according to ammo types.
|
|
THING_WCHAINSAW = 2005, //-> quad
|
|
THING_WSHOTGUN1 = 2001, //-> ng
|
|
THING_WSHOTGUN2 = 82, //-> sng
|
|
THING_WCHAINGUN = 2002, //-> ssg
|
|
THING_WROCKETL = 2003, //-> lightning
|
|
THING_WPLASMA = 2004, //-> grenade
|
|
THING_WBFG = 2006 //-> rocket
|
|
} THING_TYPES;
|
|
|
|
//thing flags
|
|
//skill/dm is appears in rather than quake's excuded in.
|
|
#define THING_EASY 1
|
|
#define THING_MEDIUM 2
|
|
#define THING_HARD 4
|
|
#define THING_DEAF 8
|
|
#define THING_DEATHMATCH 16
|
|
//other bits are ignored
|
|
|
|
|
|
|
|
typedef struct {
|
|
short xpos;
|
|
short ypos;
|
|
short angle;
|
|
unsigned short type;
|
|
unsigned short flags;
|
|
} dthing_t;
|
|
|
|
typedef struct {
|
|
short xpos;
|
|
short ypos;
|
|
} ddoomvertex_t;
|
|
|
|
typedef struct {
|
|
float xpos;
|
|
float ypos;
|
|
} mdoomvertex_t;
|
|
|
|
typedef struct {
|
|
unsigned short vert[2];
|
|
unsigned short flags;
|
|
short types;
|
|
short tag;
|
|
unsigned short sidedef[2]; //(0xffff is none for sidedef[1])
|
|
} dlinedef_t;
|
|
#define LINEDEF_IMPASSABLE 1
|
|
#define LINEDEF_BLOCKMONSTERS 2
|
|
#define LINEDEF_TWOSIDED 4
|
|
#define LINEDEF_UPPERUNPEGGED 8
|
|
#define LINEDEF_LOWERUNPEGGED 16
|
|
#define LINEDEF_SECRET 32 //seen as singlesided on automap, does nothing else.
|
|
#define LINEDEF_BLOCKSOUND 64
|
|
#define LINEDEF_NOTONMAP 128 //doesn't appear on automap.
|
|
#define LINEDEF_STARTONMAP 256
|
|
//others are ignored.
|
|
|
|
typedef struct {
|
|
short texx;
|
|
short texy;
|
|
char uppertex[8];
|
|
char lowertex[8];
|
|
char middletex[8];
|
|
unsigned short sector;
|
|
} dsidedef_t;
|
|
typedef struct {
|
|
float texx;
|
|
float texy;
|
|
int uppertex;
|
|
int lowertex;
|
|
int middletex;
|
|
unsigned short sector;
|
|
} msidedef_t;
|
|
|
|
typedef struct { //figure out which linedef to use and throw the rest away.
|
|
unsigned short vert[2];
|
|
short angle;
|
|
unsigned short linedef;
|
|
short direction;
|
|
short offset;
|
|
} dseg_t;
|
|
|
|
typedef struct {
|
|
unsigned short vert[2];
|
|
unsigned short linedef;
|
|
short direction;
|
|
unsigned short Partner; //the one on the other side of the owner's linedef
|
|
} dgl_seg1_t;
|
|
|
|
typedef struct {
|
|
unsigned int vert[2];
|
|
unsigned short linedef;
|
|
short direction;
|
|
unsigned int Partner; //the one on the other side of the owner's linedef
|
|
} dgl_seg3_t;
|
|
|
|
typedef struct {
|
|
unsigned short segcount;
|
|
unsigned short first;
|
|
} dssector_t;
|
|
|
|
typedef struct {
|
|
short x;
|
|
short y;
|
|
short dx;
|
|
short dy;
|
|
short y1upper;
|
|
short y1lower;
|
|
short x1lower;
|
|
short x1upper;
|
|
short y2upper;
|
|
short y2lower;
|
|
short x2lower;
|
|
short x2upper;
|
|
unsigned short node1;
|
|
unsigned short node2;
|
|
} ddoomnode_t;
|
|
#define NODE_IS_SSECTOR 0x8000
|
|
|
|
typedef struct {
|
|
short floorheight;
|
|
short ceilingheight;
|
|
char floortexture[8];
|
|
char ceilingtexture[8];
|
|
short lightlevel;
|
|
short specialtype;
|
|
short tag;
|
|
} dsector_t;
|
|
|
|
typedef struct {
|
|
int visframe;
|
|
int floortex;
|
|
int ceilingtex;
|
|
|
|
short floorheight;
|
|
short ceilingheight;
|
|
|
|
qbyte lightlev;
|
|
qbyte pad;
|
|
int numflattris;
|
|
short tag;
|
|
short specialtype;
|
|
|
|
unsigned short *flats;
|
|
} msector_t;
|
|
|
|
|
|
typedef struct {
|
|
short xorg;
|
|
short yorg;
|
|
short columns;
|
|
short rows;
|
|
} blockmapheader_t;
|
|
|
|
ddoomnode_t *nodel;
|
|
dssector_t *ssectorsl;
|
|
dthing_t *thingsl;
|
|
mdoomvertex_t *vertexesl;
|
|
dgl_seg3_t *segsl;
|
|
dlinedef_t *linedefsl;
|
|
msidedef_t *sidedefsm;
|
|
msector_t *sectorm;
|
|
plane_t *nodeplanes;
|
|
plane_t *lineplanes;
|
|
blockmapheader_t *blockmapl;
|
|
unsigned short *blockmapofs;
|
|
unsigned int nodec;
|
|
unsigned int sectorc;
|
|
unsigned int segsc;
|
|
unsigned int ssectorsc;
|
|
unsigned int thingsc;
|
|
unsigned int linedefsc;
|
|
unsigned int sidedefsc;
|
|
unsigned int vertexesc;
|
|
unsigned int vertexsglbase;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
char name[16];
|
|
shader_t *shader;
|
|
unsigned short width;
|
|
unsigned short height;
|
|
batch_t batch;
|
|
mesh_t *meshptr;
|
|
mesh_t mesh;
|
|
int maxverts;
|
|
int maxindicies;
|
|
} doomtexture_t;
|
|
doomtexture_t *doomtextures;
|
|
int numdoomtextures;
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////
|
|
//physics
|
|
|
|
/*walk the bsp tree*/
|
|
int Doom_SectorNearPoint(vec3_t p)
|
|
{
|
|
ddoomnode_t *node;
|
|
plane_t *plane;
|
|
int num;
|
|
int seg;
|
|
float d;
|
|
num = nodec-1;
|
|
while (1)
|
|
{
|
|
if (num & NODE_IS_SSECTOR)
|
|
{
|
|
num -= NODE_IS_SSECTOR;
|
|
for (seg = ssectorsl[num].first; seg < ssectorsl[num].first + ssectorsl[num].segcount; seg++)
|
|
if (segsl[seg].linedef != 0xffff)
|
|
break;
|
|
|
|
return sidedefsm[linedefsl[segsl[seg].linedef].sidedef[segsl[seg].direction]].sector;
|
|
}
|
|
|
|
node = nodel + num;
|
|
plane = nodeplanes + num;
|
|
|
|
// if (plane->type < 3)
|
|
// d = p[plane->type] - plane->dist;
|
|
// else
|
|
d = DotProduct (plane->normal, p) - plane->dist;
|
|
if (d < 0)
|
|
num = node->node2;
|
|
else
|
|
num = node->node1;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
int Doom_PointContents(model_t *model, vec3_t axis[3], vec3_t p)
|
|
{
|
|
int sec = Doom_SectorNearPoint(p);
|
|
if (p[2] < sectorm[sec].floorheight)
|
|
return FTECONTENTS_SOLID;
|
|
if (p[2] > sectorm[sec].ceilingheight)
|
|
return FTECONTENTS_SOLID;
|
|
return FTECONTENTS_EMPTY;
|
|
}
|
|
|
|
/*
|
|
fixme:
|
|
use q2-style bsp collision using the trisoup for flats collisions.
|
|
use blockmap for walls
|
|
*/
|
|
qboolean Doom_Trace(model_t *model, int hulloverride, framestate_t *framestate, vec3_t axis[3], vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, qboolean iscapsule, unsigned int contentstype, trace_t *trace)
|
|
{
|
|
#if 0
|
|
#define TRACESTEP 16
|
|
unsigned short *linedefs;
|
|
dlinedef_t *ld;
|
|
int bmi, obmi;
|
|
vec3_t delta;
|
|
int sec1 = Doom_SectorNearPoint(start);
|
|
vec3_t p1, pointonplane, ofs;
|
|
float d1, d2, c1, c2, planedist;
|
|
plane_t *lp;
|
|
mdoomvertex_t *v1, *v2;
|
|
int j;
|
|
float p2f;
|
|
|
|
float clipfrac;
|
|
#define DIST_EPSILON (0.03125)
|
|
|
|
// Con_Printf("%i\n", sec1);
|
|
|
|
if (start[2] < sectorm[sec1].floorheight-mins[2]) //whoops, started outside... ?
|
|
{
|
|
trace->fraction = 0;
|
|
trace->allsolid = trace->startsolid = true;
|
|
trace->endpos[0] = start[0];
|
|
trace->endpos[1] = start[1];
|
|
trace->endpos[2] = start[2]; //yeah, we do mean this - startsolid
|
|
// if (IS_NAN(trace->endpos[2]))
|
|
// Con_Printf("Nanny\n");
|
|
trace->plane.normal[0] = 0;
|
|
trace->plane.normal[1] = 0;
|
|
trace->plane.normal[2] = 1;
|
|
trace->plane.dist = sectorm[sec1].floorheight-mins[2];
|
|
|
|
return false;
|
|
}
|
|
if (start[2] > sectorm[sec1].ceilingheight-maxs[2]) //whoops, started outside... ?
|
|
{
|
|
trace->fraction = 0;
|
|
trace->allsolid = trace->startsolid = true;
|
|
trace->endpos[0] = start[0];
|
|
trace->endpos[1] = start[1];
|
|
trace->endpos[2] = start[2];
|
|
trace->plane.normal[0] = 0;
|
|
trace->plane.normal[1] = 0;
|
|
trace->plane.normal[2] = -1;
|
|
trace->plane.dist = -(sectorm[sec1].ceilingheight-maxs[2]);
|
|
return false;
|
|
}
|
|
|
|
obmi = -1;
|
|
VectorSubtract(end, start, delta);
|
|
p2f = Length(delta)+DIST_EPSILON;
|
|
if (IS_NAN(p2f) || p2f > 100000)
|
|
p2f = 100000;
|
|
VectorNormalize(delta);
|
|
|
|
trace->endpos[0] = end[0];
|
|
trace->endpos[1] = end[1];
|
|
trace->endpos[2] = end[2];
|
|
|
|
trace->fraction = 1;
|
|
while(1)
|
|
{
|
|
bmi = ((int)p1[0] - blockmapl->xorg)/128 + (((int)p1[1] - blockmapl->yorg)/128)*blockmapl->columns;
|
|
// Con_Printf("%i of %i ", bmi, blockmapl->rows*blockmapl->columns);
|
|
if (bmi >= 0 && bmi < blockmapl->rows*blockmapl->columns)
|
|
if (bmi != obmi)
|
|
{
|
|
#if 1
|
|
short dummy;
|
|
linedefs = &dummy;
|
|
for (dummy = 0; dummy < linedefsc; dummy++)
|
|
#else
|
|
for(linedefs = (short*)blockmapl + blockmapofs[bmi]+1; *linedefs != 0xffff; linedefs++)
|
|
#endif
|
|
{
|
|
ld = linedefsl + *linedefs;
|
|
if (ld->sidedef[1] != 0xffff)
|
|
{
|
|
if (sectorm[sidedefsm[ld->sidedef[0]].sector].floorheight == sectorm[sidedefsm[ld->sidedef[1]].sector].floorheight &&
|
|
sectorm[sidedefsm[ld->sidedef[0]].sector].ceilingheight == sectorm[sidedefsm[ld->sidedef[1]].sector].ceilingheight)
|
|
continue;
|
|
}
|
|
|
|
lp = lineplanes + *linedefs;
|
|
|
|
if (1)
|
|
{ //figure out how far to move the plane out by
|
|
for (j=0 ; j<2 ; j++)
|
|
{
|
|
if (lp->normal[j] < 0)
|
|
ofs[j] = maxs[j];
|
|
else
|
|
ofs[j] = mins[j];
|
|
}
|
|
ofs[2] = 0;
|
|
planedist = lp->dist - DotProduct (ofs, lp->normal);
|
|
}
|
|
else
|
|
planedist = lp->dist;
|
|
|
|
d1 = DotProduct(lp->normal, start) - (planedist);
|
|
d2 = DotProduct(lp->normal, end) - (planedist);
|
|
if (d1 > 0 && d2 > 0)
|
|
continue; //both points on the front side.
|
|
if (d1 < 0) //start on back side
|
|
{
|
|
if (ld->sidedef[1] != 0xffff) //two sided (optimisation)
|
|
{
|
|
planedist = -planedist+lp->dist;
|
|
if (/*d1 < planedist*-1 &&*/ d1 > planedist*2)
|
|
{ //right, we managed to end up just on the other side of a wall's plane.
|
|
v1 = &vertexesl[ld->vert[0]];
|
|
v2 = &vertexesl[ld->vert[1]];
|
|
if (!(d1 - d2))
|
|
continue;
|
|
if (d1<0) //back to front.
|
|
c1 = (d1+DIST_EPSILON) / (d1 - d2);
|
|
else
|
|
c1 = (d1-DIST_EPSILON) / (d1 - d2);
|
|
c2 = 1-c1;
|
|
pointonplane[0] = start[0]*c2 + p2[0]*c1;
|
|
/* if (pointonplane[0] > v1->xpos+DIST_EPSILON*2+hull->clip_maxs[0] && pointonplane[0] > v2->xpos+DIST_EPSILON*2+hull->clip_maxs[0])
|
|
continue;
|
|
if (pointonplane[0] < v1->xpos-DIST_EPSILON*2+hull->clip_mins[0] && pointonplane[0] < v2->xpos-DIST_EPSILON*2+hull->clip_mins[0])
|
|
continue;
|
|
*/ pointonplane[1] = start[1]*c2 + p2[1]*c1;
|
|
/* if (pointonplane[1] > v1->ypos+DIST_EPSILON*2+hull->clip_maxs[1] && pointonplane[1] > v2->ypos+DIST_EPSILON*2+hull->clip_maxs[1])
|
|
continue;
|
|
if (pointonplane[1] < v1->ypos-DIST_EPSILON*2+hull->clip_mins[1] && pointonplane[1] < v2->ypos-DIST_EPSILON*2+hull->clip_mins[1])
|
|
continue;
|
|
*/
|
|
pointonplane[2] = start[2]*c2 + p2[2]*c1;
|
|
|
|
Con_Printf("Started in wall\n");
|
|
j = sidedefsm[ld->sidedef[d1 < planedist]].sector;
|
|
//yup, we are in the thing
|
|
//prevent ourselves from entering the back-sector's floor/ceiling
|
|
if (pointonplane[2] < sectorm[j].floorheight-hull->clip_mins[2]) //whoops, started outside... ?
|
|
{
|
|
Con_Printf("Started in floor\n");
|
|
trace->allsolid = trace->startsolid = false;
|
|
trace->endpos[2] = sectorm[j].floorheight-hull->clip_mins[2];
|
|
trace->fraction = fabs(trace->endpos[2] - start[2]) / fabs(p2[2] - start[2]);
|
|
trace->endpos[0] = start[0]+delta[0]*trace->fraction*p2f;
|
|
trace->endpos[1] = start[1]+delta[1]*trace->fraction*p2f;
|
|
// if (IS_NAN(trace->endpos[2]))
|
|
// Con_Printf("Nanny\n");
|
|
trace->plane.normal[0] = 0;
|
|
trace->plane.normal[1] = 0;
|
|
trace->plane.normal[2] = 1;
|
|
trace->plane.dist = sectorm[j].floorheight-hull->clip_mins[2];
|
|
|
|
continue;
|
|
}
|
|
if (pointonplane[2] > sectorm[j].ceilingheight-hull->clip_maxs[2]) //whoops, started outside... ?
|
|
{
|
|
Con_Printf("Started in ceiling\n");
|
|
trace->allsolid = trace->startsolid = false;
|
|
trace->endpos[0] = pointonplane[0];
|
|
trace->endpos[1] = pointonplane[1];
|
|
trace->endpos[2] = sectorm[j].ceilingheight-hull->clip_maxs[2];
|
|
trace->fraction = fabs(trace->endpos[2] - start[2]) / fabs(p2[2] - start[2]);
|
|
trace->plane.normal[0] = 0;
|
|
trace->plane.normal[1] = 0;
|
|
trace->plane.normal[2] = -1;
|
|
trace->plane.dist = -(sectorm[j].ceilingheight-hull->clip_maxs[2]);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if (d2 < 0)
|
|
continue; //both points on the reverse side.
|
|
}
|
|
|
|
//line crosses plane.
|
|
|
|
v1 = &vertexesl[ld->vert[0]];
|
|
v2 = &vertexesl[ld->vert[1]];
|
|
|
|
if (d1<0) //back to front.
|
|
{
|
|
if (ld->sidedef[1] == 0xffff)
|
|
continue; //hack to allow them to pass
|
|
c1 = (d1+DIST_EPSILON) / (d1 - d2);
|
|
}
|
|
else
|
|
c1 = (d1-DIST_EPSILON) / (d1 - d2);
|
|
c2 = 1-c1;
|
|
pointonplane[0] = start[0]*c2 + p2[0]*c1;
|
|
if (pointonplane[0] > v1->xpos+DIST_EPSILON*2+hull->clip_maxs[0] && pointonplane[0] > v2->xpos+DIST_EPSILON*2+hull->clip_maxs[0])
|
|
continue;
|
|
if (pointonplane[0] < v1->xpos-DIST_EPSILON*2+hull->clip_mins[0] && pointonplane[0] < v2->xpos-DIST_EPSILON*2+hull->clip_mins[0])
|
|
continue;
|
|
pointonplane[1] = start[1]*c2 + p2[1]*c1;
|
|
if (pointonplane[1] > v1->ypos+DIST_EPSILON*2+hull->clip_maxs[1] && pointonplane[1] > v2->ypos+DIST_EPSILON*2+hull->clip_maxs[1])
|
|
continue;
|
|
if (pointonplane[1] < v1->ypos-DIST_EPSILON*2+hull->clip_mins[1] && pointonplane[1] < v2->ypos-DIST_EPSILON*2+hull->clip_mins[1])
|
|
continue;
|
|
pointonplane[2] = start[2]*c2 + p2[2]*c1;
|
|
|
|
if (ld->flags & LINEDEF_IMPASSABLE || ld->sidedef[1] == 0xffff) //unconditionally unpassable.
|
|
{ //unconditionally clipped.
|
|
}
|
|
else
|
|
{ //ensure that the side we are passing on to passes the clip (no ceiling/floor clips happened first)
|
|
msector_t *sec2;
|
|
|
|
if (d1<0)
|
|
sec2 = §orm[sidedefsm[ld->sidedef[1]].sector];
|
|
else
|
|
sec2 = §orm[sidedefsm[ld->sidedef[0]].sector];
|
|
|
|
if (pointonplane[2] < sec2->floorheight-hull->clip_mins[2])
|
|
{ //hit the floor first.
|
|
c1 = fabs(sectorm[sec1].floorheight-hull->clip_mins[2] - start[2]);
|
|
c2 = fabs(p2[2] - start[2]);
|
|
if (!c2)
|
|
c1 = 1;
|
|
else
|
|
c1 = (c1-DIST_EPSILON) / c2;
|
|
if (trace->fraction > c1)
|
|
{
|
|
// Con_Printf("Hit floor\n");
|
|
trace->fraction = c1;
|
|
trace->allsolid = trace->startsolid = true;
|
|
trace->endpos[0] = start[0] + trace->fraction*(p2[0]-start[0]);
|
|
trace->endpos[1] = start[1] + trace->fraction*(p2[1]-start[1]);
|
|
trace->endpos[2] = start[2] + trace->fraction*(p2[2]-start[2]);
|
|
trace->plane.normal[0] = 0;
|
|
trace->plane.normal[1] = 0;
|
|
trace->plane.normal[2] = 1;
|
|
trace->plane.dist = sectorm[sec1].floorheight-hull->clip_mins[2];
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (pointonplane[2] > sec2->ceilingheight-hull->clip_maxs[2])
|
|
{ //hit the floor first.
|
|
c1 = fabs((sectorm[sec1].ceilingheight-hull->clip_maxs[2]) - start[2]);
|
|
c2 = fabs(p2[2] - start[2]);
|
|
if (!c2)
|
|
c1 = 1;
|
|
else
|
|
c1 = (c1-DIST_EPSILON) / c2;
|
|
|
|
|
|
if (trace->fraction > c1)
|
|
{
|
|
// Con_Printf("Hit ceiling\n");
|
|
trace->fraction = c1;
|
|
trace->allsolid = trace->startsolid = true;
|
|
trace->endpos[0] = start[0] + trace->fraction*(p2[0]-start[0]);
|
|
trace->endpos[1] = start[1] + trace->fraction*(p2[1]-start[1]);
|
|
trace->endpos[2] = start[2] + trace->fraction*(p2[2]-start[2]);
|
|
trace->plane.normal[0] = 0;
|
|
trace->plane.normal[1] = 0;
|
|
trace->plane.normal[2] = -1;
|
|
trace->plane.dist = -(sectorm[sec1].ceilingheight-hull->clip_maxs[2]);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (d1<0)
|
|
sec2 = §orm[sidedefsm[ld->sidedef[0]].sector];
|
|
else
|
|
sec2 = §orm[sidedefsm[ld->sidedef[1]].sector];
|
|
|
|
if(sec2->ceilingheight == sec2->floorheight)
|
|
sec2->ceilingheight += 64;
|
|
|
|
if (pointonplane[2] > sec2->floorheight-hull->clip_mins[2] &&
|
|
pointonplane[2] < sec2->ceilingheight-hull->clip_maxs[2])
|
|
{
|
|
Con_Printf("Two sided passed\n");
|
|
continue;
|
|
}
|
|
|
|
// Con_Printf("blocked by two sided line\n");
|
|
// sec2->floorheight--;
|
|
}
|
|
|
|
if (d1<0) //back to front.
|
|
c1 = (d1+DIST_EPSILON) / (d1 - d2);
|
|
else
|
|
c1 = (d1-DIST_EPSILON) / (d1 - d2);
|
|
|
|
|
|
clipfrac = c1;
|
|
|
|
if (clipfrac < 0)
|
|
clipfrac = 0;
|
|
if (clipfrac > 1)
|
|
clipfrac = 1;
|
|
|
|
if (trace->fraction > clipfrac)
|
|
{
|
|
trace->fraction = clipfrac;
|
|
VectorMA(pointonplane, 0, lp->normal, trace->endpos);
|
|
VectorMA(trace->endpos, -0.1, delta, trace->endpos);
|
|
// if (IS_NAN(trace->endpos[2]))
|
|
// Con_Printf("Buggy clipping\n");
|
|
VectorCopy(lp->normal, trace->plane.normal);
|
|
trace->plane.dist = planedist;
|
|
// if (IS_NAN(trace->plane.normal[2]))
|
|
// Con_Printf("Buggy clipping\n");
|
|
|
|
if (clipfrac)
|
|
Con_Printf("Clip Wall %f\n", clipfrac);
|
|
}
|
|
}
|
|
|
|
obmi = bmi;
|
|
}
|
|
|
|
p1f += TRACESTEP;
|
|
if (p1f >= p2f)
|
|
break;
|
|
|
|
VectorMA(p1, TRACESTEP, delta, p1);
|
|
}
|
|
|
|
// VectorMA(start, p2f*trace->fraction, delta, p2);
|
|
|
|
if (p2[2] != start[2])
|
|
{
|
|
if (sec1 == Doom_SectorNearPoint(p2)) //special test.
|
|
{
|
|
if (p2[2] <= sectorm[sec1].floorheight-hull->clip_mins[2]) //whoops, started outside... ?
|
|
{
|
|
p1f = fabs(sectorm[sec1].floorheight-hull->clip_mins[2] - start[2]);
|
|
p2f = fabs(p2[2] - start[2]);
|
|
if (!p2f)
|
|
c1 = 1;
|
|
else
|
|
c1 = (p1f-DIST_EPSILON) / p2f;
|
|
if (trace->fraction > c1)
|
|
{
|
|
trace->fraction = c1;
|
|
trace->allsolid = trace->startsolid = false;
|
|
trace->endpos[0] = start[0] + trace->fraction*(p2[0]-start[0]);
|
|
trace->endpos[1] = start[1] + trace->fraction*(p2[1]-start[1]);
|
|
trace->endpos[2] = start[2] + trace->fraction*(p2[2]-start[2]);
|
|
trace->plane.normal[0] = 0;
|
|
trace->plane.normal[1] = 0;
|
|
trace->plane.normal[2] = 1;
|
|
trace->plane.dist = sectorm[sec1].floorheight-hull->clip_mins[2];
|
|
}
|
|
|
|
// if (IS_NAN(trace->endpos[2]))
|
|
// Con_Printf("Nanny\n");
|
|
}
|
|
if (p2[2] >= sectorm[sec1].ceilingheight-hull->clip_maxs[2]) //whoops, started outside... ?
|
|
{
|
|
p1f = fabs(sectorm[sec1].ceilingheight-hull->clip_maxs[2] - start[2]);
|
|
p2f = fabs(p2[2] - start[2]);
|
|
if (!p2f)
|
|
c1 = 1;
|
|
else
|
|
c1 = (p1f-DIST_EPSILON) / p2f;
|
|
if (trace->fraction > c1)
|
|
{
|
|
trace->fraction = c1;
|
|
trace->allsolid = trace->startsolid = false;
|
|
trace->endpos[0] = start[0] + trace->fraction*(p2[0]-start[0]);
|
|
trace->endpos[1] = start[1] + trace->fraction*(p2[1]-start[1]);
|
|
trace->endpos[2] = start[2] + trace->fraction*(p2[2]-start[2]);
|
|
trace->plane.normal[0] = 0;
|
|
trace->plane.normal[1] = 0;
|
|
trace->plane.normal[2] = -1;
|
|
trace->plane.dist = -(sectorm[sec1].ceilingheight-hull->clip_maxs[2]);
|
|
}
|
|
|
|
// if (IS_NAN(trace->endpos[2]))
|
|
// Con_Printf("Nanny\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
//we made it all the way through. yay.
|
|
|
|
trace->allsolid = trace->startsolid = false;
|
|
//Con_Printf("total = %f\n", trace->fraction);
|
|
#endif
|
|
return trace->fraction==1;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef SERVERONLY
|
|
qbyte doompalette[768];
|
|
static qboolean paletteloaded;
|
|
|
|
void Doom_LoadPalette(void)
|
|
{
|
|
char *file;
|
|
int greyscale;
|
|
if (!paletteloaded)
|
|
{
|
|
paletteloaded = true;
|
|
file = FS_LoadMallocFile("wad/playpal", NULL);
|
|
if (file)
|
|
{
|
|
memcpy(doompalette, file, 768);
|
|
Z_Free(file);
|
|
}
|
|
else
|
|
{
|
|
for (greyscale = 0; greyscale < 256; greyscale++)
|
|
{
|
|
doompalette[greyscale*3+0] = greyscale;
|
|
doompalette[greyscale*3+1] = greyscale;
|
|
doompalette[greyscale*3+2] = greyscale;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
int Doom_LoadFlat(model_t *mod, char *flatname)
|
|
{
|
|
#ifndef SERVERONLY
|
|
char texname[64];
|
|
int texnum;
|
|
|
|
sprintf(texname, "flats/%-.8s", flatname);
|
|
|
|
for (texnum = 0; texnum < numdoomtextures; texnum++)
|
|
{
|
|
if (!strcmp(doomtextures[texnum].name, texname))
|
|
return texnum;
|
|
}
|
|
|
|
doomtextures = BZ_Realloc(doomtextures, sizeof(*doomtextures)*((numdoomtextures+16)&~15));
|
|
memset(doomtextures + numdoomtextures, 0, sizeof(doomtextures[numdoomtextures]));
|
|
numdoomtextures++;
|
|
|
|
Q_strncpyz(doomtextures[texnum].name, texname, sizeof(doomtextures[texnum].name));
|
|
|
|
doomtextures[texnum].width = 64;
|
|
doomtextures[texnum].height = 64;
|
|
|
|
return texnum;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
#ifndef SERVERONLY
|
|
static void R_DrawWall(int texnum, int s, int t, float x1, float y1, float z1, float x2, float y2, float z2, qboolean unpegged, unsigned int colour4b)
|
|
{
|
|
doomtexture_t *tex = doomtextures+texnum;
|
|
mesh_t *mesh = &tex->mesh;
|
|
float len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
|
|
float s1, s2;
|
|
float t1, t2;
|
|
unsigned int col;
|
|
|
|
s1 = s/tex->width;
|
|
s2 = s1 + len/tex->width;
|
|
|
|
if (unpegged)
|
|
{
|
|
t2 = t/tex->height;
|
|
t1 = t2 - (z2-z1)/tex->height;
|
|
}
|
|
else
|
|
{
|
|
t1 = t/tex->height;
|
|
t2 = t1 + (z2-z1)/tex->height;
|
|
}
|
|
t1 = 0;
|
|
t2 = 1;
|
|
|
|
if (mesh->numvertexes+4 > tex->maxverts)
|
|
{
|
|
if (mesh->numvertexes+4 > MAX_INDICIES)
|
|
BE_DrawMesh_Single(tex->shader, mesh, NULL, 0);
|
|
else
|
|
{
|
|
tex->maxverts = mesh->numvertexes+4;
|
|
mesh->colors4b_array = BZ_Realloc(mesh->colors4b_array, sizeof(*mesh->colors4b_array) * tex->maxverts);
|
|
mesh->xyz_array = BZ_Realloc(mesh->xyz_array, sizeof(*mesh->xyz_array) * tex->maxverts);
|
|
mesh->st_array = BZ_Realloc(mesh->st_array, sizeof(*mesh->st_array) * tex->maxverts);
|
|
}
|
|
}
|
|
if (mesh->numindexes+6 > tex->maxindicies)
|
|
{
|
|
tex->maxindicies = mesh->numindexes+6;
|
|
mesh->indexes = BZ_Realloc(mesh->indexes, sizeof(*mesh->indexes) * tex->maxindicies);
|
|
}
|
|
|
|
col = colour4b * 0x01010101;
|
|
((unsigned char*)&col)[3] = 0xff;
|
|
*(unsigned int*)mesh->colors4b_array[mesh->numvertexes+0] = col;
|
|
*(unsigned int*)mesh->colors4b_array[mesh->numvertexes+1] = col;
|
|
*(unsigned int*)mesh->colors4b_array[mesh->numvertexes+2] = col;
|
|
*(unsigned int*)mesh->colors4b_array[mesh->numvertexes+3] = col;
|
|
VectorSet(mesh->xyz_array[mesh->numvertexes+0], x1, y1, z1);
|
|
VectorSet(mesh->xyz_array[mesh->numvertexes+1], x1, y1, z2);
|
|
VectorSet(mesh->xyz_array[mesh->numvertexes+2], x2, y2, z2);
|
|
VectorSet(mesh->xyz_array[mesh->numvertexes+3], x2, y2, z1);
|
|
Vector2Set(mesh->st_array[mesh->numvertexes+0], s1, t2);
|
|
Vector2Set(mesh->st_array[mesh->numvertexes+1], s1, t1);
|
|
Vector2Set(mesh->st_array[mesh->numvertexes+2], s2, t1);
|
|
Vector2Set(mesh->st_array[mesh->numvertexes+3], s2, t2);
|
|
|
|
mesh->indexes[mesh->numindexes+0] = mesh->numvertexes+0;
|
|
mesh->indexes[mesh->numindexes+1] = mesh->numvertexes+1;
|
|
mesh->indexes[mesh->numindexes+2] = mesh->numvertexes+2;
|
|
|
|
mesh->indexes[mesh->numindexes+3] = mesh->numvertexes+0;
|
|
mesh->indexes[mesh->numindexes+4] = mesh->numvertexes+2;
|
|
mesh->indexes[mesh->numindexes+5] = mesh->numvertexes+3;
|
|
|
|
mesh->numvertexes += 4;
|
|
mesh->numindexes += 6;
|
|
}
|
|
|
|
static void R_DrawFlats(int floortexnum, int floorheight, int ceiltexnum, int ceilheight, int numverts, unsigned short *verts, unsigned int colour4b)
|
|
{
|
|
mesh_t *mesh;
|
|
unsigned int col;
|
|
unsigned int v, i;
|
|
|
|
//floor
|
|
{
|
|
doomtexture_t *floortex = doomtextures + floortexnum;
|
|
mesh = &floortex->mesh;
|
|
if (mesh->numvertexes+numverts > floortex->maxverts)
|
|
{
|
|
if (mesh->numvertexes+numverts > MAX_INDICIES)
|
|
{
|
|
BE_DrawMesh_Single(floortex->shader, mesh, NULL, 0);
|
|
mesh->numvertexes = 0;
|
|
mesh->numindexes = 0;
|
|
}
|
|
else
|
|
{
|
|
floortex->maxverts = mesh->numvertexes+numverts;
|
|
mesh->colors4b_array = BZ_Realloc(mesh->colors4b_array, sizeof(*mesh->colors4b_array) * floortex->maxverts);
|
|
mesh->xyz_array = BZ_Realloc(mesh->xyz_array, sizeof(*mesh->xyz_array) * floortex->maxverts);
|
|
mesh->st_array = BZ_Realloc(mesh->st_array, sizeof(*mesh->st_array) * floortex->maxverts);
|
|
}
|
|
}
|
|
if (mesh->numindexes+numverts > floortex->maxindicies)
|
|
{
|
|
floortex->maxindicies = mesh->numindexes+numverts;
|
|
mesh->indexes = BZ_Realloc(mesh->indexes, sizeof(*mesh->indexes) * floortex->maxindicies);
|
|
}
|
|
|
|
col = colour4b * 0x01010101;
|
|
((unsigned char*)&col)[3] = 0xff;
|
|
|
|
for (i = 0; i < numverts; i++)
|
|
{
|
|
v = verts[i];
|
|
VectorSet(mesh->xyz_array[mesh->numvertexes+i], vertexesl[v].xpos, vertexesl[v].ypos, floorheight);
|
|
Vector2Set(mesh->st_array[mesh->numvertexes+i], vertexesl[v].xpos/64.0f, vertexesl[v].ypos/64.0f);
|
|
*(unsigned int*)mesh->colors4b_array[mesh->numvertexes+i] = col;
|
|
}
|
|
|
|
for (i = 0; i < numverts; i++)
|
|
{
|
|
mesh->indexes[mesh->numindexes+i] = mesh->numvertexes+i;
|
|
}
|
|
|
|
mesh->numvertexes += numverts;
|
|
mesh->numindexes += numverts;
|
|
|
|
// if (floortex->shader)
|
|
// BE_DrawMesh_Single(floortex->shader, mesh, NULL, 0);
|
|
}
|
|
|
|
//ceiling
|
|
{
|
|
doomtexture_t *ceiltex = doomtextures + ceiltexnum;
|
|
mesh = &ceiltex->mesh;
|
|
if (mesh->numvertexes+numverts > ceiltex->maxverts)
|
|
{
|
|
if (mesh->numvertexes+numverts > MAX_INDICIES)
|
|
{
|
|
BE_DrawMesh_Single(ceiltex->shader, mesh, NULL, 0);
|
|
mesh->numvertexes = 0;
|
|
mesh->numindexes = 0;
|
|
}
|
|
else
|
|
{
|
|
ceiltex->maxverts = mesh->numvertexes+numverts;
|
|
mesh->colors4b_array = BZ_Realloc(mesh->colors4b_array, sizeof(*mesh->colors4b_array) * ceiltex->maxverts);
|
|
mesh->xyz_array = BZ_Realloc(mesh->xyz_array, sizeof(*mesh->xyz_array) * ceiltex->maxverts);
|
|
mesh->st_array = BZ_Realloc(mesh->st_array, sizeof(*mesh->st_array) * ceiltex->maxverts);
|
|
}
|
|
}
|
|
if (mesh->numindexes+numverts > ceiltex->maxindicies)
|
|
{
|
|
ceiltex->maxindicies = mesh->numindexes+numverts;
|
|
mesh->indexes = BZ_Realloc(mesh->indexes, sizeof(*mesh->indexes) * ceiltex->maxindicies);
|
|
}
|
|
|
|
col = colour4b * 0x01010101;
|
|
((unsigned char*)&col)[3] = 0xff;
|
|
|
|
for (i = 0; i < numverts; i++)
|
|
{
|
|
v = verts[numverts-1-i];
|
|
VectorSet(mesh->xyz_array[mesh->numvertexes+i], vertexesl[v].xpos, vertexesl[v].ypos, ceilheight);
|
|
Vector2Set(mesh->st_array[mesh->numvertexes+i], vertexesl[v].xpos/64.0f, vertexesl[v].ypos/64.0f);
|
|
*(unsigned int*)mesh->colors4b_array[mesh->numvertexes+i] = col;
|
|
}
|
|
|
|
for (i = 0; i < numverts; i++)
|
|
{
|
|
mesh->indexes[mesh->numindexes+i] = mesh->numvertexes+i;
|
|
}
|
|
|
|
mesh->numvertexes += numverts;
|
|
mesh->numindexes += numverts;
|
|
|
|
// if (ceiltex->shader)
|
|
// BE_DrawMesh_Single(ceiltex->shader, mesh, NULL, 0);
|
|
}
|
|
}
|
|
|
|
static void R_DrawSSector(unsigned int ssec)
|
|
{
|
|
short v0, v1;
|
|
int sd;
|
|
dlinedef_t *ld;
|
|
int seg;
|
|
msector_t *sec, *sec2;
|
|
|
|
for (seg = ssectorsl[ssec].first + ssectorsl[ssec].segcount-1; seg >= ssectorsl[ssec].first; seg--)
|
|
if (segsl[seg].linedef != 0xffff)
|
|
break;
|
|
sec = sectorm + sidedefsm[linedefsl[segsl[seg].linedef].sidedef[segsl[seg].direction]].sector;
|
|
|
|
if (sec->visframe != r_visframecount)
|
|
{
|
|
R_DrawFlats(sec->floortex, sec->floorheight, sec->ceilingtex, sec->ceilingheight, sec->numflattris*3, sec->flats, sec->lightlev);
|
|
|
|
sec->visframe = r_visframecount;
|
|
}
|
|
for (seg = ssectorsl[ssec].first + ssectorsl[ssec].segcount-1; seg >= ssectorsl[ssec].first; seg--)
|
|
{
|
|
if (segsl[seg].linedef == 0xffff)
|
|
continue;
|
|
|
|
v0 = segsl[seg].vert[0];
|
|
v1 = segsl[seg].vert[1];
|
|
if (v0==v1)
|
|
continue;
|
|
ld = linedefsl + segsl[seg].linedef;
|
|
sd = ld->sidedef[segsl[seg].direction];
|
|
|
|
if (ld->sidedef[1] != 0xffff) //we can see through this linedef
|
|
{
|
|
sec2 = sectorm + sidedefsm[ld->sidedef[1-segsl[seg].direction]].sector;
|
|
|
|
if (sec->floorheight < sec2->floorheight)
|
|
{
|
|
R_DrawWall(sidedefsm[sd].lowertex,
|
|
sidedefsm[ld->sidedef[1-segsl[seg].direction]].texx,
|
|
sidedefsm[ld->sidedef[1-segsl[seg].direction]].texy,
|
|
vertexesl[v0].xpos, vertexesl[v0].ypos, sec->floorheight,
|
|
vertexesl[v1].xpos, vertexesl[v1].ypos, sec2->floorheight, ld->flags & LINEDEF_LOWERUNPEGGED, sec->lightlev);
|
|
}
|
|
|
|
if (sec->ceilingheight > sec2->ceilingheight)
|
|
{
|
|
R_DrawWall(sidedefsm[sd].uppertex,
|
|
sidedefsm[ld->sidedef[1-segsl[seg].direction]].texx,
|
|
sidedefsm[ld->sidedef[1-segsl[seg].direction]].texy,
|
|
vertexesl[v0].xpos, vertexesl[v0].ypos, sec2->ceilingheight,
|
|
vertexesl[v1].xpos, vertexesl[v1].ypos, sec->ceilingheight, ld->flags & LINEDEF_UPPERUNPEGGED, sec->lightlev);
|
|
}
|
|
|
|
if (sidedefsm[sd].middletex)
|
|
{
|
|
R_DrawWall(sidedefsm[sd].middletex,
|
|
sidedefsm[ld->sidedef[segsl[seg].direction]].texx,
|
|
sidedefsm[ld->sidedef[segsl[seg].direction]].texy,
|
|
vertexesl[v1].xpos, vertexesl[v1].ypos, (sec2->ceilingheight < sec->ceilingheight)?sec2->ceilingheight:sec->ceilingheight,
|
|
vertexesl[v0].xpos, vertexesl[v0].ypos, (sec2->floorheight > sec->floorheight)?sec2->floorheight:sec->floorheight, false, sec->lightlev);
|
|
}
|
|
}
|
|
else
|
|
{ //solid wall, draw full wall.
|
|
|
|
R_DrawWall(sidedefsm[sd].middletex,
|
|
sidedefsm[ld->sidedef[segsl[seg].direction]].texx,
|
|
sidedefsm[ld->sidedef[segsl[seg].direction]].texy,
|
|
vertexesl[v0].xpos, vertexesl[v0].ypos, sec->floorheight,
|
|
vertexesl[v1].xpos, vertexesl[v1].ypos, sec->ceilingheight, false, sec->lightlev);
|
|
}
|
|
}
|
|
}
|
|
|
|
mplane_t frustum2d[2];
|
|
static int Box2DOnPlaneSide (short emins[2], short emaxs[2], mplane_t *p)
|
|
{
|
|
float dist1, dist2;
|
|
int sides;
|
|
|
|
// general case
|
|
switch (p->signbits)
|
|
{
|
|
case 0:
|
|
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1];
|
|
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1];
|
|
break;
|
|
case 1:
|
|
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1];
|
|
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1];
|
|
break;
|
|
case 2:
|
|
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1];
|
|
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1];
|
|
break;
|
|
case 3:
|
|
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1];
|
|
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1];
|
|
break;
|
|
case 4:
|
|
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1];
|
|
dist2 = p->normal[0]*emins[0] + p->normal[1]*emins[1];
|
|
break;
|
|
case 5:
|
|
dist1 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1];
|
|
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1];
|
|
break;
|
|
case 6:
|
|
dist1 = p->normal[0]*emaxs[0] + p->normal[1]*emins[1];
|
|
dist2 = p->normal[0]*emins[0] + p->normal[1]*emaxs[1];
|
|
break;
|
|
case 7:
|
|
dist1 = p->normal[0]*emins[0] + p->normal[1]*emins[1];
|
|
dist2 = p->normal[0]*emaxs[0] + p->normal[1]*emaxs[1];
|
|
break;
|
|
default:
|
|
dist1 = dist2 = 0; // shut up compiler
|
|
// BOPS_Error ();
|
|
break;
|
|
}
|
|
|
|
sides = 0;
|
|
if (dist1 >= p->dist)
|
|
sides = 1;
|
|
if (dist2 < p->dist)
|
|
sides |= 2;
|
|
|
|
#ifdef PARANOID
|
|
if (sides == 0)
|
|
Sys_Error ("Box2DOnPlaneSide: sides==0");
|
|
#endif
|
|
|
|
return sides;
|
|
}
|
|
static qboolean R_Cull2DBox (short mins_x, short mins_y, short maxs_x, short maxs_y)
|
|
{
|
|
short mins[2], maxs[2];
|
|
int i;
|
|
//return false;
|
|
mins[0] = mins_x;
|
|
mins[1] = mins_y;
|
|
maxs[0] = maxs_x;
|
|
maxs[1] = maxs_y;
|
|
|
|
for (i=0 ; i<2 ; i++)
|
|
if (Box2DOnPlaneSide (mins, maxs, &frustum2d[i]) == 2)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void R_Set2DFrustum (void)
|
|
{
|
|
int i;
|
|
vec3_t vpn, vright, vup, viewang;
|
|
|
|
if ((int)r_novis.value & 4)
|
|
return;
|
|
|
|
viewang[0] = 0;
|
|
viewang[1] = r_refdef.viewangles[1];
|
|
viewang[2] = 0;
|
|
AngleVectors (viewang, vpn, vright, vup);
|
|
|
|
/* if (r_refdef.fov_x == 90)
|
|
{
|
|
// front side is visible
|
|
|
|
VectorAdd (vpn, vright, frustum2d[0].normal);
|
|
VectorSubtract (vpn, vright, frustum2d[1].normal);
|
|
}
|
|
else*/
|
|
{
|
|
|
|
// rotate VPN right by FOV_X/2 degrees
|
|
RotatePointAroundVector( frustum2d[0].normal, vup, vpn, -(90-r_refdef.fov_x / 2 ) );
|
|
// rotate VPN left by FOV_X/2 degrees
|
|
RotatePointAroundVector( frustum2d[1].normal, vup, vpn, 90-r_refdef.fov_x / 2 );
|
|
}
|
|
|
|
for (i=0 ; i<2 ; i++)
|
|
{
|
|
frustum2d[i].type = PLANE_ANYZ;
|
|
frustum2d[i].dist = DotProduct (r_origin, frustum2d[i].normal);
|
|
frustum2d[i].signbits = SignbitsForPlane (&frustum2d[i]);
|
|
}
|
|
}
|
|
|
|
|
|
static void R_RecursiveDoomNode(unsigned int node)
|
|
{
|
|
if (node & NODE_IS_SSECTOR)
|
|
{
|
|
R_DrawSSector(node & ~NODE_IS_SSECTOR);
|
|
|
|
return;
|
|
}
|
|
|
|
if (!R_Cull2DBox(nodel[node].x1lower, nodel[node].y1lower, nodel[node].x1upper, nodel[node].y1upper)||1)
|
|
R_RecursiveDoomNode(nodel[node].node1);
|
|
if (!R_Cull2DBox(nodel[node].x2lower, nodel[node].y2lower, nodel[node].x2upper, nodel[node].y2upper)||1)
|
|
R_RecursiveDoomNode(nodel[node].node2);
|
|
}
|
|
|
|
void R_DoomWorld(void)
|
|
{
|
|
int texnum;
|
|
doomtexture_t *t;
|
|
if (!nodel || !nodec)
|
|
return; //err... buggy
|
|
|
|
for (texnum = 0; texnum < numdoomtextures; texnum++) //a hash table might be a good plan.
|
|
{
|
|
t = &doomtextures[texnum];
|
|
t->mesh.numindexes = 0;
|
|
t->mesh.numvertexes = 0;
|
|
}
|
|
r_visframecount++;
|
|
R_RecursiveDoomNode(nodec-1);
|
|
|
|
memset(cl.worldmodel->batches, 0, sizeof(cl.worldmodel->batches));
|
|
for (texnum = 0; texnum < numdoomtextures; texnum++) //a hash table might be a good plan.
|
|
{
|
|
t = &doomtextures[texnum];
|
|
if (t->mesh.numindexes && t->shader)
|
|
{
|
|
t->batch.next = cl.worldmodel->batches[t->shader->sort];
|
|
cl.worldmodel->batches[t->shader->sort] = &t->batch;
|
|
|
|
BE_DrawMesh_Single(t->shader, &t->mesh, NULL, 0);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
//find the first ssector, go through it's list/
|
|
//grab the lines into multiple arrays.
|
|
//make sure all arrays are looped fully. If not, error out.
|
|
//if we have two arrays, we have a hole in the middle.
|
|
//with multiple arrays, from the second onwards
|
|
// grab two adjacent verts and find the nearest point in any other array, that is also on the positive side.
|
|
// One of the two should be an extreeme, and the external point should be in the direction that the angle points at.
|
|
// none found = error
|
|
// create a triangle from these points, fix array links.
|
|
// move on to next spare array.
|
|
//we now have a concave polygon with no holes.
|
|
//pick a point, follow along the walls making a triangle fan, until an angle of > 180, throw out fan, rebuild arrays.
|
|
//at new point, start a new fan. Be prepared to not be able to generate one.
|
|
|
|
#define MAX_REGIONS 256
|
|
#define MAX_POLYVERTS (MAX_FLATTRIS*3)
|
|
#define MAX_FLATTRIS 1024
|
|
|
|
//buffer to hold tris
|
|
static unsigned short indexes[MAX_POLYVERTS];
|
|
static unsigned int numindexes;
|
|
|
|
typedef struct {
|
|
int vertex[MAX_POLYVERTS];
|
|
unsigned int numverts;
|
|
float angle;
|
|
} conectedregion_t;
|
|
static conectedregion_t polyregions[MAX_REGIONS]; //we need to be able to join them as we go.
|
|
static unsigned int regions;
|
|
|
|
//throw out duplicates.
|
|
static void Triangulate_AddLine(int v1, int v2) //order makes a difference
|
|
{
|
|
int r, v;
|
|
int beginingof = -1;
|
|
int endof = -1;
|
|
int freer = -1;
|
|
|
|
for (r = 0; r < regions; r++)
|
|
{
|
|
if (!polyregions[r].numverts)
|
|
{
|
|
freer = r;
|
|
continue;
|
|
}
|
|
if (polyregions[r].vertex[0] == v2)
|
|
beginingof = r;
|
|
if (polyregions[r].vertex[polyregions[r].numverts-1] == v1)
|
|
endof = r;
|
|
|
|
for (v = polyregions[r].numverts-2; v >= 0; v--)
|
|
if (polyregions[r].vertex[v] == v1 && polyregions[r].vertex[v+1] == v2)
|
|
return; //whoops. Duplicate line.
|
|
}
|
|
if (beginingof >= 0 && endof >= 0)
|
|
{ //merge two regions. Copy one onto the end of the other.
|
|
if (beginingof == endof)
|
|
{ //close up
|
|
if (polyregions[endof].numverts+1 >= MAX_POLYVERTS)
|
|
{
|
|
Con_Printf(CON_WARNING "WARNING: Map region is too large.\n");
|
|
return;
|
|
}
|
|
polyregions[endof].vertex[polyregions[endof].numverts] = v2;
|
|
polyregions[endof].numverts++;
|
|
}
|
|
else
|
|
{
|
|
if (polyregions[endof].numverts+polyregions[beginingof].numverts >= MAX_POLYVERTS)
|
|
{
|
|
Con_Printf(CON_WARNING "WARNING: Map region is too large.\n");
|
|
return;
|
|
}
|
|
memcpy(polyregions[endof].vertex + polyregions[endof].numverts,
|
|
polyregions[beginingof].vertex,
|
|
sizeof(polyregions[beginingof].vertex[0])*polyregions[beginingof].numverts);
|
|
polyregions[endof].numverts += polyregions[beginingof].numverts;
|
|
polyregions[beginingof].numverts = 0;
|
|
}
|
|
}
|
|
else if (beginingof >= 0)
|
|
{ //insert into
|
|
if (polyregions[beginingof].numverts+1 >= MAX_POLYVERTS)
|
|
{
|
|
Con_Printf(CON_WARNING "WARNING: Map region is too large.\n");
|
|
return;
|
|
}
|
|
|
|
memmove(polyregions[beginingof].vertex + 1,
|
|
polyregions[beginingof].vertex,
|
|
sizeof(polyregions[beginingof].vertex[0])*polyregions[beginingof].numverts);
|
|
polyregions[beginingof].vertex[0] = v1;
|
|
polyregions[beginingof].numverts++;
|
|
}
|
|
else if (endof >= 0)
|
|
{ //stick outselves on the end
|
|
if (polyregions[endof].numverts+1 >= MAX_POLYVERTS)
|
|
{
|
|
Con_Printf(CON_WARNING "WARNING: Map region is too large.\n");
|
|
return;
|
|
}
|
|
polyregions[endof].vertex[polyregions[endof].numverts] = v2;
|
|
polyregions[endof].numverts++;
|
|
}
|
|
else
|
|
{ //new region.
|
|
if (freer < 0)
|
|
{
|
|
freer = regions++;
|
|
if (regions > MAX_REGIONS)
|
|
{
|
|
Con_Printf(CON_WARNING "WARNING: Too many regions. Sector is too chaotic/complicated.\n");
|
|
freer = 0;
|
|
regions = 1;
|
|
}
|
|
}
|
|
|
|
polyregions[freer].numverts = 2;
|
|
polyregions[freer].vertex[0] = v1;
|
|
polyregions[freer].vertex[1] = v2;
|
|
}
|
|
}
|
|
|
|
static unsigned short *Triangulate_Finish(int *numtris, unsigned short *old, int oldindexcount)
|
|
{
|
|
unsigned short *out;
|
|
unsigned int v1, v2, v3, v;
|
|
unsigned int r, v2s, f;
|
|
float a1;
|
|
float a2;
|
|
for (r = 0; r < regions; r++)
|
|
{
|
|
if (!polyregions[r].numverts)
|
|
continue;
|
|
|
|
if (polyregions[r].vertex[0] != polyregions[r].vertex[polyregions[r].numverts-1])
|
|
{
|
|
Con_Printf("Sector is not enclosed\n");
|
|
polyregions[r].vertex[polyregions[r].numverts] = polyregions[r].vertex[0];
|
|
polyregions[r].numverts++;
|
|
|
|
/*
|
|
*numtris = 0;
|
|
regions = 0;
|
|
|
|
|
|
return NULL;*/
|
|
}
|
|
|
|
polyregions[r].angle = 0;
|
|
polyregions[r].numverts--;//start == end
|
|
for (v = 0; v < polyregions[r].numverts; v++)
|
|
{
|
|
v1 = polyregions[r].vertex[v];
|
|
v2 = polyregions[r].vertex[(v+1)%(polyregions[r].numverts)];
|
|
v3 = polyregions[r].vertex[(v+2)%(polyregions[r].numverts)];
|
|
a1 = atan2(vertexesl[v3].ypos - vertexesl[v2].ypos, vertexesl[v3].xpos - vertexesl[v2].xpos);
|
|
a2 = atan2(vertexesl[v1].ypos - vertexesl[v2].ypos, vertexesl[v1].xpos - vertexesl[v2].xpos);
|
|
polyregions[r].angle += fabs(a1 - a2);
|
|
}
|
|
}
|
|
|
|
//FIXME: inner loops should find the nearest point in a forwards direction from one of the extreeme points.
|
|
|
|
//angle should be either (numverts-2)*PI //inner loop
|
|
//or PI*numverts+2*PI //outer loop
|
|
//unfortuantly it's rarly either of them...
|
|
|
|
for (r = 0; r < regions; r++)
|
|
{
|
|
if (polyregions[r].numverts<3)
|
|
continue;
|
|
v1 = polyregions[r].vertex[0];
|
|
v2 = polyregions[r].vertex[1];
|
|
v2s = 1;
|
|
f=0;
|
|
for (v = 2; polyregions[r].numverts>=3; )
|
|
{ //build a triangle fan.
|
|
if (numindexes+3 > MAX_POLYVERTS)
|
|
{
|
|
Con_Printf(CON_WARNING "WARNING: Sector is too big for triangulation\n");
|
|
break;
|
|
}
|
|
v3 = polyregions[r].vertex[v];
|
|
|
|
a1 = atan2(vertexesl[v3].ypos - vertexesl[v2].ypos, vertexesl[v3].xpos - vertexesl[v2].xpos);
|
|
a2 = atan2(vertexesl[v1].ypos - vertexesl[v2].ypos, vertexesl[v1].xpos - vertexesl[v2].xpos);
|
|
if (fabs(a1-a2) > M_PI+0.01) //this would be a reflex angle then.;.
|
|
{
|
|
/* indexes[numindexes++] = 0;
|
|
indexes[numindexes++] = v2;
|
|
indexes[numindexes++] = 1;
|
|
*/
|
|
v1 = v2;
|
|
v2 = v3;
|
|
v2s = v;
|
|
v=(v+1)%polyregions[r].numverts;
|
|
f++;
|
|
if (f >= 1000)
|
|
{ //infinate loop - shouldn't happen. must have got the angle stuff wrong.
|
|
Con_Printf(CON_WARNING "WARNING: Failed to triangulate polygon\n");
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//FIXME: make sure v1 -> v3 doesn't clip any same-region lines.
|
|
|
|
indexes[numindexes++] = v1;
|
|
indexes[numindexes++] = v2;
|
|
indexes[numindexes++] = v3;
|
|
memmove(polyregions[r].vertex+v2s, polyregions[r].vertex+v2s+1, (polyregions[r].numverts-v2s)*sizeof(polyregions[r].vertex[0]));
|
|
polyregions[r].numverts--;
|
|
v=(v)%polyregions[r].numverts;
|
|
v2 = v3;
|
|
v2s = v;
|
|
polyregions[r].vertex[polyregions[r].numverts] = 0;
|
|
}
|
|
}
|
|
|
|
if (!numindexes)
|
|
{
|
|
Con_Printf(CON_WARNING "Warning: Sector is empty\n");
|
|
|
|
*numtris = 0;
|
|
regions = 0;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
out = BZ_Realloc(old, sizeof(*out)*(numindexes+oldindexcount*3));
|
|
memcpy(out+oldindexcount*3, indexes, sizeof(*out)*numindexes);
|
|
|
|
*numtris = numindexes/3+oldindexcount;
|
|
regions = 0;
|
|
numindexes = 0;
|
|
|
|
return out;
|
|
}
|
|
|
|
static void Triangulate_Sectors(model_t *mod, dsector_t *sectorl, qboolean glbspinuse)
|
|
{
|
|
int seg, nsec;
|
|
int i, sec=-1;
|
|
|
|
sectorm = Z_Malloc(sectorc * sizeof(*sectorm));
|
|
|
|
if (glbspinuse)
|
|
{
|
|
for (i = 0; i < ssectorsc; i++)
|
|
{ //only do linedefs.
|
|
for (seg = ssectorsl[i].first; seg < ssectorsl[i].first + ssectorsl[i].segcount; seg++)
|
|
if (segsl[seg].linedef != 0xffff)
|
|
break;
|
|
|
|
if (seg == ssectorsl[i].first + ssectorsl[i].segcount) //throw a fit.
|
|
{
|
|
Con_Printf("SubSector %i has absolutly no walls\n", i);
|
|
continue;
|
|
}
|
|
|
|
nsec = sidedefsm[linedefsl[segsl[seg].linedef].sidedef[segsl[seg].direction]].sector;
|
|
if (sec != nsec)
|
|
{
|
|
if (sec>=0)
|
|
sectorm[sec].flats = Triangulate_Finish(§orm[sec].numflattris, sectorm[sec].flats, sectorm[sec].numflattris);
|
|
sec = nsec;
|
|
}
|
|
for (seg = ssectorsl[i].first; seg < ssectorsl[i].first + ssectorsl[i].segcount; seg++)
|
|
{ //ignore direction, it's do do with the intersection rather than the draw direction.
|
|
Triangulate_AddLine(segsl[seg].vert[0], segsl[seg].vert[1]);
|
|
}
|
|
}
|
|
if (sec>=0)
|
|
sectorm[sec].flats = Triangulate_Finish(§orm[sec].numflattris, sectorm[sec].flats, sectorm[sec].numflattris);
|
|
}
|
|
else
|
|
{
|
|
for (sec = 0; sec < sectorc; sec++)
|
|
{
|
|
for (i = 0; i < linedefsc; i++)
|
|
{
|
|
if (sidedefsm[linedefsl[i].sidedef[0]].sector == sec)
|
|
Triangulate_AddLine(linedefsl[i].vert[0], linedefsl[i].vert[1]);
|
|
if (linedefsl[i].sidedef[1] != 0xffff && sidedefsm[linedefsl[i].sidedef[1]].sector == sec)
|
|
Triangulate_AddLine(linedefsl[i].vert[1], linedefsl[i].vert[0]);
|
|
}
|
|
sectorm[sec].flats = Triangulate_Finish(§orm[sec].numflattris, sectorm[sec].flats, sectorm[sec].numflattris);
|
|
}
|
|
}
|
|
|
|
/*
|
|
for (i = 0; i < ssectorsc; i++)
|
|
{ //only do linedefs.
|
|
seg = ssectorsl[i].first;
|
|
nsec = sidedefsm[linedefsl[segsl[seg].linedef].sidedef[segsl[seg].direction]].sector;
|
|
if (sec != nsec)
|
|
{
|
|
if (sec>=0)
|
|
sectorm[sec].flats = Triangulate_Finish(§orm[sec].numflattris);
|
|
sec = nsec;
|
|
}
|
|
for (seg = ssectorsl[i].first; seg < ssectorsl[i].first + ssectorsl[i].segcount; seg++)
|
|
{ //ignore direction, it's do do with the intersection rather than the draw direction.
|
|
Triangulate_AddLine(segsl[seg].vert[0], segsl[seg].vert[1]);
|
|
}
|
|
}
|
|
if (sec>=0)
|
|
sectorm[sec].flats = Triangulate_Finish(§orm[sec].numflattris);
|
|
*/
|
|
|
|
for (i = 0; i < sectorc; i++)
|
|
{
|
|
sectorm[i].ceilingtex = Doom_LoadFlat(mod, sectorl[i].ceilingtexture);
|
|
sectorm[i].floortex = Doom_LoadFlat(mod, sectorl[i].floortexture);
|
|
sectorm[i].lightlev = sectorl[i].lightlevel;
|
|
sectorm[i].specialtype = sectorl[i].specialtype;
|
|
sectorm[i].tag = sectorl[i].tag;
|
|
sectorm[i].ceilingheight = sectorl[i].ceilingheight;
|
|
sectorm[i].floorheight = sectorl[i].floorheight;
|
|
}
|
|
}
|
|
|
|
#ifndef SERVERONLY
|
|
static void *textures1;
|
|
static void *textures2;
|
|
static char *pnames;
|
|
static void Doom_LoadTextureInfos(void)
|
|
{
|
|
textures1 = FS_LoadMallocFile("wad/texture1", NULL);
|
|
textures2 = FS_LoadMallocFile("wad/texture2", NULL);
|
|
pnames = FS_LoadMallocFile("wad/pnames", NULL);
|
|
}
|
|
|
|
typedef struct {
|
|
char name[8];
|
|
short always0_0;
|
|
short always0_1;
|
|
short width;
|
|
short height;
|
|
short always0_2;
|
|
short always0_3;
|
|
short componantcount;
|
|
} ddoomtexture_t;
|
|
typedef struct {
|
|
short xoffset;
|
|
short yoffset;
|
|
unsigned short patchnum;
|
|
unsigned short always_1;
|
|
unsigned short always_0;
|
|
} ddoomtexturecomponant_t;
|
|
|
|
typedef struct {
|
|
short width;
|
|
short height;
|
|
short xpos;
|
|
short ypos;
|
|
} doomimage_t;
|
|
|
|
static void Doom_ExtractPName(unsigned int *out, doomimage_t *di, size_t imgsize, int outwidth, int outheight, int x, int y)
|
|
{
|
|
unsigned int *colpointers;
|
|
int c, fr, rc, extra;
|
|
unsigned char *data, *coldata;
|
|
extern qbyte gammatable[256];
|
|
|
|
if (!di)
|
|
return;
|
|
|
|
data = (char *)di;
|
|
|
|
|
|
// out += x/*+di->xpos*/;
|
|
// out += (y/*+di->ypos*/)*outwidth;
|
|
|
|
colpointers = (unsigned int*)(data+sizeof(doomimage_t));
|
|
for (c = 0; c < di->width; c++)
|
|
{
|
|
if (c+x < 0)
|
|
continue;
|
|
if (c+x >= outwidth)
|
|
break;
|
|
|
|
if (colpointers[c] >= imgsize)
|
|
break;
|
|
coldata = data + colpointers[c];
|
|
while(1)
|
|
{
|
|
fr = *coldata++;
|
|
if (fr == 255)
|
|
break;
|
|
|
|
rc = *coldata++;
|
|
|
|
coldata++; //one not drawn, on each side
|
|
|
|
fr+=y;
|
|
|
|
if (fr<0)
|
|
{
|
|
coldata += -fr; //plus
|
|
rc -= -fr;
|
|
fr = 0;
|
|
}
|
|
|
|
if ((fr+rc) > outheight)
|
|
{
|
|
extra = rc - (outheight - fr) +1;
|
|
rc = outheight - fr;
|
|
if (rc < 0)
|
|
break;
|
|
}
|
|
else
|
|
extra = 1;
|
|
|
|
while(rc)
|
|
{
|
|
out[c+x + fr*outwidth] = (gammatable[doompalette[*coldata*3]]) + (gammatable[doompalette[*coldata*3+1]]<<8) + (gammatable[doompalette[*coldata*3+2]]<<16) + (255<<24);
|
|
coldata++;
|
|
fr++;
|
|
rc--;
|
|
}
|
|
|
|
coldata+=extra; //one not drawn, on each side
|
|
}
|
|
}
|
|
}
|
|
|
|
static texid_t Doom_LoadPatchFromTexWad(char *name, void *texlump, unsigned short *width, unsigned short *height, qboolean *hasalpha)
|
|
{
|
|
char patch[32] = "patches/";
|
|
unsigned int *tex;
|
|
ddoomtexture_t *tx;
|
|
ddoomtexturecomponant_t *tc;
|
|
texid_t result;
|
|
int i;
|
|
int count;
|
|
|
|
count = *(int *)texlump;
|
|
tex = (int *)texlump+1;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
tx = (ddoomtexture_t*)((unsigned char*)texlump + *tex);
|
|
if (!strncmp(tx->name, name, 8))
|
|
{
|
|
tex = BZ_Malloc(tx->width*tx->height*4);
|
|
memset(tex, 0, tx->width*tx->height*4);
|
|
*width = tx->width;
|
|
*height = tx->height;
|
|
tc = (ddoomtexturecomponant_t*)(tx+1);
|
|
for (i = 0; i < tx->componantcount; i++, tc++)
|
|
{
|
|
doomimage_t *img;
|
|
size_t imgsize;
|
|
strncpy(patch+8, pnames+4+8*tc->patchnum, 8);
|
|
Q_strlwr(patch+8);
|
|
patch[16] = '\0';
|
|
Q_strncatz(patch, ".pat", sizeof(patch));
|
|
|
|
img = (doomimage_t *)FS_LoadMallocFile(patch, &imgsize);
|
|
Doom_ExtractPName(tex, img, imgsize, tx->width, tx->height, tc->xoffset, tc->yoffset);
|
|
BZ_Free(img);
|
|
}
|
|
|
|
*hasalpha = false;
|
|
for (i = 0; i < tx->width * tx->height; i++)
|
|
{
|
|
if (!(tex[i] & 0xff000000))
|
|
{
|
|
*hasalpha = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
result = R_LoadTexture32(name, tx->width, tx->height, tex, 0);
|
|
BZ_Free(tex);
|
|
return result;
|
|
}
|
|
|
|
tex++;
|
|
}
|
|
|
|
return r_nulltex;
|
|
}
|
|
static int Doom_LoadPatch(model_t *mod, char *name)
|
|
{
|
|
qboolean hasalpha = false;
|
|
int texnum;
|
|
size_t nlen = strnlen(name, 8);
|
|
|
|
for (texnum = 0; texnum < numdoomtextures; texnum++) //a hash table might be a good plan.
|
|
{
|
|
if(!memcmp(name, doomtextures[texnum].name, nlen) && !doomtextures[texnum].name[nlen])
|
|
{
|
|
return texnum;
|
|
}
|
|
}
|
|
//couldn't find it.
|
|
// texnum = numdoomtextures;
|
|
|
|
doomtextures = BZ_Realloc(doomtextures, sizeof(*doomtextures)*((numdoomtextures+16)&~15));
|
|
memset(doomtextures + numdoomtextures, 0, sizeof(doomtextures[numdoomtextures]));
|
|
numdoomtextures++;
|
|
|
|
memcpy(doomtextures[texnum].name, name, nlen);
|
|
doomtextures[texnum].name[nlen] = 0;
|
|
|
|
return texnum;
|
|
}
|
|
|
|
static void Doom_LoadShaders(void *ctx, void *data, size_t a, size_t b)
|
|
{
|
|
model_t *mod = ctx;
|
|
texnums_t tn;
|
|
qboolean hasalpha = false;
|
|
qboolean isflat;
|
|
int texnum;
|
|
char tmp[MAX_QPATH];
|
|
|
|
for (texnum = 0; texnum < numdoomtextures; texnum++) //a hash table might be a good plan.
|
|
{
|
|
isflat = !strncmp(doomtextures[texnum].name, "flats/", 6);
|
|
memset(&tn, 0, sizeof(tn));
|
|
if (isflat)
|
|
{
|
|
void *file = FS_LoadMallocFile(va2(tmp, sizeof(tmp), "%s.raw", doomtextures[texnum].name), NULL);
|
|
if (file)
|
|
{
|
|
tn.base = Image_GetTexture(doomtextures[texnum].name, NULL, 0, file, doompalette, 64, 64, TF_8PAL24);
|
|
Z_Free(file);
|
|
}
|
|
doomtextures[texnum].width = 64;
|
|
doomtextures[texnum].height = 64;
|
|
}
|
|
else
|
|
{
|
|
if (textures1 && !TEXVALID(tn.base))
|
|
tn.base = Doom_LoadPatchFromTexWad(doomtextures[texnum].name, textures1, &doomtextures[texnum].width, &doomtextures[texnum].height, &hasalpha);
|
|
if (textures2 && !TEXVALID(tn.base))
|
|
tn.base = Doom_LoadPatchFromTexWad(doomtextures[texnum].name, textures2, &doomtextures[texnum].width, &doomtextures[texnum].height, &hasalpha);
|
|
}
|
|
if (!TEXVALID(tn.base))
|
|
{
|
|
doomtextures[texnum].width = 64;
|
|
doomtextures[texnum].height = 64;
|
|
hasalpha = false;
|
|
}
|
|
|
|
if (hasalpha)
|
|
doomtextures[texnum].shader = R_RegisterShader(doomtextures[texnum].name, SUF_NONE, "{\n{\nmap $diffuse\nrgbgen vertex\nalphagen vertex\nalphafunc ge128\n}\n}\n");
|
|
else
|
|
doomtextures[texnum].shader = R_RegisterShader(doomtextures[texnum].name, SUF_NONE, "{\n{\nmap $diffuse\nrgbgen vertex\nalphagen vertex\n}\n}\n");
|
|
|
|
R_BuildDefaultTexnums(&tn, doomtextures[texnum].shader);
|
|
}
|
|
};
|
|
|
|
static void Doom_Purge (struct model_s *mod)
|
|
{
|
|
int texnum;
|
|
for (texnum = 0; texnum < numdoomtextures; texnum++)
|
|
{
|
|
BZ_Free(doomtextures[texnum].mesh.colors4b_array);
|
|
BZ_Free(doomtextures[texnum].mesh.st_array);
|
|
BZ_Free(doomtextures[texnum].mesh.xyz_array);
|
|
BZ_Free(doomtextures[texnum].mesh.indexes);
|
|
}
|
|
BZ_Free(doomtextures);
|
|
doomtextures = NULL;
|
|
}
|
|
#endif
|
|
static void CleanWalls(model_t *mod, dsidedef_t *sidedefsl)
|
|
{
|
|
int i;
|
|
char texname[64];
|
|
char lastmiddle[9]="-";
|
|
char lastlower[9]="-";
|
|
char lastupper[9]="-";
|
|
int lastmidtex=0, lastuptex=0, lastlowtex=0;
|
|
sidedefsm = BZ_Malloc(sidedefsc * sizeof(*sidedefsm));
|
|
for (i = 0; i < sidedefsc; i++)
|
|
{
|
|
#if 1//def GLQUAKE
|
|
strncpy(texname, sidedefsl[i].middletex, 8);
|
|
texname[8] = '\0';
|
|
if (!strcmp(texname, "-"))
|
|
sidedefsm[i].middletex = 0;
|
|
else
|
|
{
|
|
if (!strncmp(texname, lastmiddle, 8))
|
|
sidedefsm[i].middletex = lastmidtex;
|
|
else
|
|
{
|
|
strncpy(lastmiddle, texname, 8);
|
|
sidedefsm[i].middletex = lastmidtex = Doom_LoadPatch(mod, texname);
|
|
}
|
|
}
|
|
|
|
strncpy(texname, sidedefsl[i].lowertex, 8);
|
|
texname[8] = '\0';
|
|
if (!strcmp(texname, "-"))
|
|
sidedefsm[i].lowertex = 0;
|
|
else
|
|
{
|
|
if (!strncmp(texname, lastlower, 8))
|
|
sidedefsm[i].lowertex = lastlowtex;
|
|
else
|
|
{
|
|
strncpy(lastlower, texname, 8);
|
|
sidedefsm[i].lowertex = lastlowtex = Doom_LoadPatch(mod, texname);
|
|
}
|
|
}
|
|
|
|
strncpy(texname, sidedefsl[i].uppertex, 8);
|
|
texname[8] = '\0';
|
|
if (!strcmp(texname, "-"))
|
|
sidedefsm[i].uppertex = 0;
|
|
else
|
|
{
|
|
if (!strncmp(texname, lastupper, 8))
|
|
sidedefsm[i].uppertex = lastuptex;
|
|
else
|
|
{
|
|
strncpy(lastupper, texname, 8);
|
|
sidedefsm[i].uppertex = lastuptex = Doom_LoadPatch(mod, texname);
|
|
}
|
|
}
|
|
#endif
|
|
sidedefsm[i].sector = sidedefsl[i].sector;
|
|
sidedefsm[i].texx = sidedefsl[i].texx;
|
|
sidedefsm[i].texy = sidedefsl[i].texy;
|
|
}
|
|
}
|
|
|
|
void QuakifyThings(model_t *mod, dthing_t *thingsl)
|
|
{
|
|
int sector;
|
|
int spawnflags;
|
|
char *name;
|
|
int i;
|
|
int zpos;
|
|
static char newlump[1024*1024]; //FIXME
|
|
char thingname[MAX_QPATH];
|
|
|
|
char *ptr = newlump;
|
|
vec3_t point;
|
|
|
|
sprintf(ptr, "{\n"
|
|
"\"classname\" \"worldspawn\"\n"
|
|
"}\n");
|
|
ptr += strlen(ptr);
|
|
|
|
for (i = 0; i < thingsc; i++)
|
|
{
|
|
switch(thingsl[i].type)
|
|
{
|
|
case THING_PLAYER: //fixme: spit out a coop spawn too.
|
|
name = "info_player_start";
|
|
break;
|
|
case THING_PLAYER2:
|
|
case THING_PLAYER3:
|
|
case THING_PLAYER4:
|
|
name = "info_player_coop";
|
|
break;
|
|
case THING_DMSPAWN:
|
|
name = "info_player_deathmatch";
|
|
break;
|
|
|
|
|
|
case THING_WCHAINSAW:
|
|
name = "item_artifact_super_damage";
|
|
break;
|
|
case THING_WSHOTGUN1:
|
|
name = "weapon_nailgun";
|
|
break;
|
|
case THING_WSHOTGUN2:
|
|
name = "weapon_supernailgun";
|
|
break;
|
|
case THING_WCHAINGUN:
|
|
name = "weapon_supershotgun";
|
|
break;
|
|
case THING_WROCKETL:
|
|
name = "weapon_rocketlauncher";
|
|
break;
|
|
case THING_WPLASMA:
|
|
name = "weapon_grenadelauncher";
|
|
break;
|
|
case THING_WBFG:
|
|
name = "weapon_lightning";
|
|
break;
|
|
|
|
default:
|
|
name = va2(thingname, sizeof(thingname), "thing_%i", thingsl[i].type);
|
|
break;
|
|
}
|
|
|
|
point[0] = thingsl[i].xpos;
|
|
point[1] = thingsl[i].ypos;
|
|
point[2] = 0;
|
|
sector = Doom_SectorNearPoint(point);
|
|
zpos = sectorm[sector].floorheight + 24; //things have no z coord, so find the sector they're in
|
|
|
|
spawnflags = SPAWNFLAG_NOT_EASY | SPAWNFLAG_NOT_MEDIUM | SPAWNFLAG_NOT_HARD | SPAWNFLAG_NOT_DEATHMATCH;
|
|
if (thingsl[i].flags & THING_EASY)
|
|
spawnflags -= SPAWNFLAG_NOT_EASY;
|
|
if (thingsl[i].flags & THING_MEDIUM)
|
|
spawnflags -= SPAWNFLAG_NOT_MEDIUM;
|
|
if (thingsl[i].flags & THING_HARD)
|
|
spawnflags -= SPAWNFLAG_NOT_HARD;
|
|
if (thingsl[i].flags & THING_DEATHMATCH)
|
|
spawnflags -= SPAWNFLAG_NOT_DEATHMATCH;
|
|
if (thingsl[i].flags & THING_DEAF)
|
|
spawnflags |= 1;
|
|
|
|
Q_snprintfz(ptr, newlump+sizeof(newlump)-ptr, "{\n"
|
|
"\"classname\" \"%s\"\n"
|
|
"\"origin\" \"%i %i %i\"\n"
|
|
"\"spawnflags\" \"%i\"\n"
|
|
"\"angle\" \"%i\"\n"
|
|
"}\n",
|
|
name,
|
|
thingsl[i].xpos, thingsl[i].ypos, zpos,
|
|
spawnflags,
|
|
thingsl[i].angle
|
|
);
|
|
ptr += strlen(ptr);
|
|
}
|
|
|
|
Mod_SetEntitiesStringLen(mod, newlump, ptr-newlump);
|
|
}
|
|
|
|
void Doom_GeneratePlanes(ddoomnode_t *nodel)
|
|
{
|
|
vec3_t point, up, line;
|
|
int n;
|
|
up[0] = 0;
|
|
up[1] = 0;
|
|
up[2] = 1;
|
|
line[2] = 0;
|
|
nodeplanes = BZ_Malloc(sizeof(*nodeplanes)*nodec);
|
|
lineplanes = BZ_Malloc(sizeof(*lineplanes)*linedefsc);
|
|
point[2] = 0;
|
|
for (n = 0; n < nodec; n++)
|
|
{
|
|
line[0] = nodel[n].dx;
|
|
line[1] = nodel[n].dy;
|
|
point[0] = nodel[n].x;
|
|
point[1] = nodel[n].y;
|
|
CrossProduct(line, up, nodeplanes[n].normal);
|
|
VectorNormalize(nodeplanes[n].normal);
|
|
nodeplanes[n].dist = DotProduct (point, nodeplanes[n].normal);
|
|
}
|
|
|
|
for (n = 0; n < linedefsc; n++)
|
|
{
|
|
point[0] = vertexesl[linedefsl[n].vert[0]].xpos;
|
|
point[1] = vertexesl[linedefsl[n].vert[0]].ypos;
|
|
line[0] = vertexesl[linedefsl[n].vert[1]].xpos-point[0];
|
|
line[1] = vertexesl[linedefsl[n].vert[1]].ypos-point[1];
|
|
CrossProduct(line, up, lineplanes[n].normal);
|
|
VectorNormalize(lineplanes[n].normal);
|
|
lineplanes[n].dist = DotProduct (point, lineplanes[n].normal);
|
|
}
|
|
}
|
|
|
|
/*
|
|
doom maps have no network limitations, but has +/-32767 map size limits (same as quake bsp)
|
|
fte defaults to a +/- 4096 world
|
|
a lot of maps are off-centered and can be moved to get them to fit fte's constraints, so if we can, do so
|
|
*/
|
|
static void MoveWorld(void)
|
|
{
|
|
int v;
|
|
short adj[2];
|
|
short min[2], max[2];
|
|
min[0] = 4096;
|
|
min[1] = 4096;
|
|
max[0] = -4096;
|
|
max[1] = -4096;
|
|
|
|
for (v = 0; v < vertexesc; v++)
|
|
{
|
|
if (min[0] > vertexesl[v].xpos)
|
|
min[0] = vertexesl[v].xpos;
|
|
if (min[1] > vertexesl[v].ypos)
|
|
min[1] = vertexesl[v].ypos;
|
|
|
|
if (max[0] < vertexesl[v].xpos)
|
|
max[0] = vertexesl[v].xpos;
|
|
if (max[1] < vertexesl[v].ypos)
|
|
max[1] = vertexesl[v].ypos;
|
|
}
|
|
|
|
if (min[0]>=-4096 && max[0]<=4096)
|
|
if (min[1]>=-4096 && max[1]<=4096)
|
|
return; //doesn't need adjusting, live with it.
|
|
|
|
if (max[0]-min[0]>=8192 || max[1]-min[1]>=8192)
|
|
{
|
|
Con_Printf(CON_WARNING "Warning: Map is too large for the network protocol\n");
|
|
return;
|
|
}
|
|
|
|
adj[0] = (max[0]-4096)&~63; //don't harm the tiling.
|
|
adj[1] = (max[1]-4096)&~63;
|
|
|
|
Con_Printf("Adjusting map (%i %i)\n", -adj[0], -adj[1]);
|
|
|
|
for (v = 0; v < vertexesc; v++)
|
|
{
|
|
vertexesl[v].xpos -= adj[0];
|
|
vertexesl[v].ypos -= adj[1];
|
|
}
|
|
|
|
for (v = 0; v < nodec; v++)
|
|
{
|
|
nodel[v].x -= adj[0];
|
|
nodel[v].y -= adj[1];
|
|
|
|
nodel[v].x1lower -= adj[0];
|
|
nodel[v].x1upper -= adj[1];
|
|
nodel[v].y1lower -= adj[0];
|
|
nodel[v].y1upper -= adj[1];
|
|
|
|
nodel[v].x2lower -= adj[0];
|
|
nodel[v].x2upper -= adj[1];
|
|
nodel[v].y2lower -= adj[0];
|
|
nodel[v].y2upper -= adj[1];
|
|
}
|
|
|
|
for (v = 0; v < thingsc; v++)
|
|
{
|
|
thingsl[v].xpos -= adj[0];
|
|
thingsl[v].ypos -= adj[1];
|
|
}
|
|
|
|
blockmapl->xorg -= adj[0];
|
|
blockmapl->yorg -= adj[1];
|
|
}
|
|
|
|
|
|
static void Doom_LoadVerticies(char *name)
|
|
{
|
|
ddoomvertex_t *std, *gl1;
|
|
int stdc, glc;
|
|
int *gl2;
|
|
int i;
|
|
size_t fsize;
|
|
char tmp[MAX_QPATH];
|
|
|
|
std = (void *)FS_LoadMallocFile (va2(tmp,sizeof(tmp),"%s.vertexes", name), &fsize);
|
|
stdc = fsize/sizeof(*std);
|
|
|
|
gl2 = (void *)FS_LoadMallocFile (va2(tmp,sizeof(tmp),"%s.gl_vert", name), &fsize);
|
|
if (!gl2)
|
|
{
|
|
glc = 0;
|
|
gl1 = NULL;
|
|
}
|
|
else if (gl2[0] == (('g'<<0)|('N'<<8)|('d'<<16)|('2'<<24)))
|
|
{
|
|
gl2++;
|
|
glc = (fsize-4)/sizeof(int)/2;
|
|
gl1 = NULL;
|
|
}
|
|
else
|
|
{
|
|
glc = fsize/sizeof(*gl1);
|
|
gl1 = (ddoomvertex_t*)gl2;
|
|
}
|
|
|
|
if (stdc)
|
|
{
|
|
vertexesc = stdc + glc;
|
|
vertexesl = BZ_Malloc(vertexesc*sizeof(*vertexesl));
|
|
|
|
vertexsglbase = stdc;
|
|
|
|
for (i = 0; i < stdc; i++)
|
|
{
|
|
vertexesl[i].xpos = std[i].xpos;
|
|
vertexesl[i].ypos = std[i].ypos;
|
|
}
|
|
if (gl1)
|
|
{
|
|
for (i = 0; i < glc; i++)
|
|
{
|
|
vertexesl[stdc+i].xpos = gl1[i].xpos;
|
|
vertexesl[stdc+i].ypos = gl1[i].ypos;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < glc; i++)
|
|
{
|
|
vertexesl[stdc+i].xpos = (float)gl2[i*2] / 0x10000;
|
|
vertexesl[stdc+i].ypos = (float)gl2[i*2+1] / 0x10000;
|
|
}
|
|
}
|
|
}
|
|
Z_Free(std);
|
|
Z_Free(gl2);
|
|
}
|
|
|
|
static void Doom_LoadSSectors(char *name)
|
|
{
|
|
size_t fsize;
|
|
char tmp[MAX_QPATH];
|
|
ssectorsl = (void *)FS_LoadMallocFile (va2(tmp, sizeof(tmp), "%s.gl_ssect", name), &fsize);
|
|
if (!ssectorsl)
|
|
ssectorsl = (void *)FS_LoadMallocFile (va2(tmp, sizeof(tmp), "%s.ssectors", name), &fsize);
|
|
//FIXME: "gNd3" means that it's glbsp version 3.
|
|
ssectorsc = fsize/sizeof(*ssectorsl);
|
|
}
|
|
|
|
static void Doom_LoadSSegs(char *name)
|
|
{ //these skirt the subsectors
|
|
|
|
void *file;
|
|
dgl_seg3_t *s3;
|
|
dgl_seg1_t *s1;
|
|
dseg_t *s0;
|
|
int i;
|
|
size_t fsize;
|
|
char tmp[MAX_QPATH];
|
|
|
|
file = (void *)FS_LoadMallocFile (va2(tmp, sizeof(tmp), "%s.gl_segs", name), &fsize);
|
|
if (!file)
|
|
{
|
|
s0 = (void *)FS_LoadMallocFile (va2(tmp, sizeof(tmp), "%s.segs", name), &fsize);
|
|
segsc = fsize/sizeof(*s0);
|
|
|
|
segsl = BZ_Malloc(segsc * sizeof(*segsl));
|
|
for (i = 0; i < segsc; i++)
|
|
{
|
|
segsl[i].vert[0] = s0[i].vert[0];
|
|
segsl[i].vert[1] = s0[i].vert[1];
|
|
segsl[i].linedef = s0[i].linedef;
|
|
segsl[i].direction = s0[i].direction;
|
|
segsl[i].Partner = 0xffff;
|
|
}
|
|
}
|
|
else if (*(int *)file == *(int *)"gNd3")
|
|
{
|
|
s3 = file;
|
|
segsc = fsize/sizeof(*s3);
|
|
|
|
segsl = s3;
|
|
}
|
|
else if (!file)
|
|
return;
|
|
else
|
|
{
|
|
s1 = file;
|
|
segsc = fsize/sizeof(*s1);
|
|
|
|
segsl = BZ_Malloc(segsc * sizeof(*segsl));
|
|
for (i = 0; i < segsc; i++)
|
|
{
|
|
if (s1[i].vert[0] & 0x8000)
|
|
segsl[i].vert[0] = (s1[i].vert[0]&0x7fff)+vertexsglbase;
|
|
else
|
|
segsl[i].vert[0] = s1[i].vert[0];
|
|
if (s1[i].vert[1] & 0x8000)
|
|
segsl[i].vert[1] = (s1[i].vert[1]&0x7fff)+vertexsglbase;
|
|
else
|
|
segsl[i].vert[1] = s1[i].vert[1];
|
|
segsl[i].linedef = s1[i].linedef;
|
|
segsl[i].direction = s1[i].direction;
|
|
if (s1[i].Partner == 0xffff)
|
|
segsl[i].Partner = 0xffffffff;
|
|
else
|
|
segsl[i].Partner = s1[i].Partner;
|
|
}
|
|
}
|
|
}
|
|
|
|
qboolean QDECL Mod_LoadDoomLevel(model_t *mod, void *buffer, size_t fsize)
|
|
{
|
|
int h;
|
|
dsector_t *sectorl;
|
|
dsidedef_t *sidedefsl;
|
|
char name[MAX_QPATH];
|
|
char tmp[MAX_QPATH];
|
|
|
|
int *gl_nodes;
|
|
|
|
if (fsize != 4)
|
|
{
|
|
Con_Printf("Wad map %s does actually exist... weird.\n", mod->name);
|
|
return false;
|
|
}
|
|
|
|
COM_StripExtension(mod->name, name, sizeof(name));
|
|
|
|
gl_nodes = (void *)FS_LoadMallocFile (va2(tmp,sizeof(tmp),"%s.gl_nodes", name), &fsize);
|
|
if (gl_nodes && fsize>0)
|
|
{
|
|
nodel = (void *)gl_nodes;
|
|
nodec = fsize/sizeof(*nodel);
|
|
}
|
|
else
|
|
{
|
|
gl_nodes=NULL;
|
|
nodel = (void *)FS_LoadMallocFile (va2(tmp,sizeof(tmp),"%s.nodes", name), &fsize);
|
|
nodec = fsize/sizeof(*nodel);
|
|
}
|
|
sectorl = (void *)FS_LoadMallocFile (va2(tmp,sizeof(tmp),"%s.sectors", name), &fsize);
|
|
sectorc = fsize/sizeof(*sectorl);
|
|
|
|
#ifndef SERVERONLY
|
|
numdoomtextures=0;
|
|
Doom_LoadPalette();
|
|
#endif
|
|
|
|
|
|
Doom_LoadVerticies(name);
|
|
|
|
Doom_LoadSSegs(name);
|
|
Doom_LoadSSectors(name);
|
|
|
|
thingsl = (void *)FS_LoadMallocFile (va2(tmp,sizeof(tmp),"%s.things", name), &fsize);
|
|
thingsc = fsize/sizeof(*thingsl);
|
|
linedefsl = (void *)FS_LoadMallocFile (va2(tmp,sizeof(tmp),"%s.linedefs", name), &fsize);
|
|
linedefsc = fsize/sizeof(*linedefsl);
|
|
sidedefsl = (void *)FS_LoadMallocFile (va2(tmp,sizeof(tmp),"%s.sidedefs", name), &fsize);
|
|
sidedefsc = fsize/sizeof(*sidedefsl);
|
|
blockmapl = (void *)FS_LoadMallocFile (va2(tmp,sizeof(tmp),"%s.blockmap", name), &fsize);
|
|
// blockmaps = fsize;
|
|
#ifndef SERVERONLY
|
|
Doom_LoadTextureInfos();
|
|
#endif
|
|
blockmapofs = (unsigned short*)(blockmapl+1);
|
|
|
|
if (!nodel || !sectorl || !segsl || !ssectorsl || !thingsl || !linedefsl || !sidedefsl || !vertexesl)
|
|
{
|
|
Sys_Error("Wad map doesn't contain enough lumps\n");
|
|
nodel = NULL;
|
|
return false;
|
|
}
|
|
|
|
MoveWorld();
|
|
|
|
Doom_GeneratePlanes(nodel);
|
|
|
|
mod->hulls[0].clip_mins[0] = 0;
|
|
mod->hulls[0].clip_mins[1] = 0;
|
|
mod->hulls[0].clip_mins[2] = 0;
|
|
mod->hulls[0].clip_maxs[0] = 0;
|
|
mod->hulls[0].clip_maxs[1] = 0;
|
|
mod->hulls[0].clip_maxs[2] = 0;
|
|
mod->hulls[0].available = true;
|
|
|
|
for (h = 1; h < MAX_MAP_HULLSM; h++)
|
|
mod->hulls[h].available = false;
|
|
|
|
Doom_SetModelFunc(mod);
|
|
|
|
mod->fromgame = fg_doom;
|
|
mod->type = mod_brush;
|
|
mod->nodes = (void*)0x1;
|
|
|
|
CleanWalls(mod, sidedefsl);
|
|
|
|
Triangulate_Sectors(mod, sectorl, !!gl_nodes);
|
|
|
|
QuakifyThings(mod, thingsl);
|
|
|
|
COM_AddWork(WG_MAIN, Doom_LoadShaders, mod, NULL, 0, 0);
|
|
return true;
|
|
}
|
|
|
|
void Doom_LightPointValues(model_t *model, vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir)
|
|
{
|
|
msector_t *sec;
|
|
sec = sectorm + Doom_SectorNearPoint(point);
|
|
|
|
res_dir[0] = 0;
|
|
res_dir[1] = 1;
|
|
res_dir[2] = 1;
|
|
res_diffuse[0] = sec->lightlev;
|
|
res_diffuse[1] = sec->lightlev;
|
|
res_diffuse[2] = sec->lightlev;
|
|
res_ambient[0] = sec->lightlev;
|
|
res_ambient[1] = sec->lightlev;
|
|
res_ambient[2] = sec->lightlev;
|
|
}
|
|
|
|
//return pvs bits for point
|
|
unsigned int Doom_FatPVS(struct model_s *model, vec3_t org, qbyte *pvsbuffer, unsigned int buffersize, qboolean merge)
|
|
{
|
|
//FIXME: use REJECT lump.
|
|
return 0;
|
|
}
|
|
|
|
//check if an ent is within the given pvs
|
|
qboolean Doom_EdictInFatPVS(struct model_s *model, struct pvscache_s *edict, qbyte *pvsbuffer)
|
|
{ //FIXME: use REJECT lump.
|
|
return true;
|
|
}
|
|
|
|
//generate useful info for correct functioning of Doom_EdictInFatPVS.
|
|
void Doom_FindTouchedLeafs(struct model_s *model, struct pvscache_s *ent, vec3_t cullmins, vec3_t cullmaxs)
|
|
{
|
|
//work out the sectors this ent is in for easy pvs.
|
|
}
|
|
|
|
//requires lightmaps - not supported.
|
|
void Doom_StainNode(struct mnode_s *node, float *parms)
|
|
{
|
|
}
|
|
|
|
//requires lightmaps - not supported.
|
|
void Doom_MarkLights(struct dlight_s *light, int bit, struct mnode_s *node)
|
|
{
|
|
}
|
|
|
|
void Doom_SetModelFunc(model_t *mod)
|
|
{
|
|
#ifndef SERVERONLY
|
|
mod->funcs.PurgeModel = Doom_Purge;
|
|
#endif
|
|
mod->funcs.FatPVS = Doom_FatPVS;
|
|
mod->funcs.EdictInFatPVS = Doom_EdictInFatPVS;
|
|
mod->funcs.FindTouchedLeafs = Doom_FindTouchedLeafs;
|
|
|
|
mod->funcs.LightPointValues = Doom_LightPointValues;
|
|
mod->funcs.StainNode = Doom_StainNode;
|
|
mod->funcs.MarkLights = Doom_MarkLights;
|
|
|
|
// mod->funcs.LeafPVS) (struct model_s *model, int num, qbyte *buffer, unsigned int buffersize);
|
|
|
|
mod->funcs.NativeTrace = Doom_Trace;
|
|
mod->funcs.PointContents = Doom_PointContents;
|
|
|
|
//Doom_SetCollisionFuncs(mod);
|
|
}
|
|
|
|
#endif
|