960 lines
24 KiB
C
960 lines
24 KiB
C
|
/*
|
||
|
gl_alias.c - alias model renderer
|
||
|
Copyright (C) 2017 Uncle Mike
|
||
|
|
||
|
This program is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
*/
|
||
|
|
||
|
#include "common.h"
|
||
|
#include "client.h"
|
||
|
#include "mathlib.h"
|
||
|
#include "const.h"
|
||
|
#include "r_studioint.h"
|
||
|
#include "triangleapi.h"
|
||
|
#include "alias.h"
|
||
|
#include "pm_local.h"
|
||
|
#include "gl_local.h"
|
||
|
#include "cl_tent.h"
|
||
|
|
||
|
/*
|
||
|
=================================================================
|
||
|
|
||
|
ALIAS MODEL DISPLAY LIST GENERATION
|
||
|
|
||
|
=================================================================
|
||
|
*/
|
||
|
|
||
|
model_t *aliasmodel;
|
||
|
aliashdr_t *paliashdr;
|
||
|
|
||
|
qboolean used[8192];
|
||
|
|
||
|
// the command list holds counts and s/t values that are valid for
|
||
|
// every frame
|
||
|
int commands[8192];
|
||
|
int numcommands;
|
||
|
|
||
|
// all frames will have their vertexes rearranged and expanded
|
||
|
// so they are in the order expected by the command list
|
||
|
int vertexorder[8192];
|
||
|
int numorder;
|
||
|
|
||
|
int allverts, alltris;
|
||
|
|
||
|
int stripverts[128];
|
||
|
int striptris[128];
|
||
|
int stripcount;
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
StripLength
|
||
|
================
|
||
|
*/
|
||
|
int StripLength (int starttri, int startv)
|
||
|
{
|
||
|
int m1, m2;
|
||
|
int j;
|
||
|
mtriangle_t *last, *check;
|
||
|
int k;
|
||
|
|
||
|
used[starttri] = 2;
|
||
|
|
||
|
last = &triangles[starttri];
|
||
|
|
||
|
stripverts[0] = last->vertindex[(startv)%3];
|
||
|
stripverts[1] = last->vertindex[(startv+1)%3];
|
||
|
stripverts[2] = last->vertindex[(startv+2)%3];
|
||
|
|
||
|
striptris[0] = starttri;
|
||
|
stripcount = 1;
|
||
|
|
||
|
m1 = last->vertindex[(startv+2)%3];
|
||
|
m2 = last->vertindex[(startv+1)%3];
|
||
|
|
||
|
// look for a matching triangle
|
||
|
nexttri:
|
||
|
for (j=starttri+1, check=&triangles[starttri+1] ; j<pheader->numtris ; j++, check++)
|
||
|
{
|
||
|
if (check->facesfront != last->facesfront)
|
||
|
continue;
|
||
|
for (k=0 ; k<3 ; k++)
|
||
|
{
|
||
|
if (check->vertindex[k] != m1)
|
||
|
continue;
|
||
|
if (check->vertindex[ (k+1)%3 ] != m2)
|
||
|
continue;
|
||
|
|
||
|
// this is the next part of the fan
|
||
|
|
||
|
// if we can't use this triangle, this tristrip is done
|
||
|
if (used[j])
|
||
|
goto done;
|
||
|
|
||
|
// the new edge
|
||
|
if (stripcount & 1)
|
||
|
m2 = check->vertindex[ (k+2)%3 ];
|
||
|
else
|
||
|
m1 = check->vertindex[ (k+2)%3 ];
|
||
|
|
||
|
stripverts[stripcount+2] = check->vertindex[ (k+2)%3 ];
|
||
|
striptris[stripcount] = j;
|
||
|
stripcount++;
|
||
|
|
||
|
used[j] = 2;
|
||
|
goto nexttri;
|
||
|
}
|
||
|
}
|
||
|
done:
|
||
|
|
||
|
// clear the temp used flags
|
||
|
for (j=starttri+1 ; j<pheader->numtris ; j++)
|
||
|
if (used[j] == 2)
|
||
|
used[j] = 0;
|
||
|
|
||
|
return stripcount;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===========
|
||
|
FanLength
|
||
|
===========
|
||
|
*/
|
||
|
int FanLength (int starttri, int startv)
|
||
|
{
|
||
|
int m1, m2;
|
||
|
int j;
|
||
|
mtriangle_t *last, *check;
|
||
|
int k;
|
||
|
|
||
|
used[starttri] = 2;
|
||
|
|
||
|
last = &triangles[starttri];
|
||
|
|
||
|
stripverts[0] = last->vertindex[(startv)%3];
|
||
|
stripverts[1] = last->vertindex[(startv+1)%3];
|
||
|
stripverts[2] = last->vertindex[(startv+2)%3];
|
||
|
|
||
|
striptris[0] = starttri;
|
||
|
stripcount = 1;
|
||
|
|
||
|
m1 = last->vertindex[(startv+0)%3];
|
||
|
m2 = last->vertindex[(startv+2)%3];
|
||
|
|
||
|
|
||
|
// look for a matching triangle
|
||
|
nexttri:
|
||
|
for (j=starttri+1, check=&triangles[starttri+1] ; j<pheader->numtris ; j++, check++)
|
||
|
{
|
||
|
if (check->facesfront != last->facesfront)
|
||
|
continue;
|
||
|
for (k=0 ; k<3 ; k++)
|
||
|
{
|
||
|
if (check->vertindex[k] != m1)
|
||
|
continue;
|
||
|
if (check->vertindex[ (k+1)%3 ] != m2)
|
||
|
continue;
|
||
|
|
||
|
// this is the next part of the fan
|
||
|
|
||
|
// if we can't use this triangle, this tristrip is done
|
||
|
if (used[j])
|
||
|
goto done;
|
||
|
|
||
|
// the new edge
|
||
|
m2 = check->vertindex[ (k+2)%3 ];
|
||
|
|
||
|
stripverts[stripcount+2] = m2;
|
||
|
striptris[stripcount] = j;
|
||
|
stripcount++;
|
||
|
|
||
|
used[j] = 2;
|
||
|
goto nexttri;
|
||
|
}
|
||
|
}
|
||
|
done:
|
||
|
|
||
|
// clear the temp used flags
|
||
|
for (j=starttri+1 ; j<pheader->numtris ; j++)
|
||
|
if (used[j] == 2)
|
||
|
used[j] = 0;
|
||
|
|
||
|
return stripcount;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
BuildTris
|
||
|
|
||
|
Generate a list of trifans or strips
|
||
|
for the model, which holds for all frames
|
||
|
================
|
||
|
*/
|
||
|
void BuildTris (void)
|
||
|
{
|
||
|
int i, j, k;
|
||
|
int startv;
|
||
|
mtriangle_t *last, *check;
|
||
|
int m1, m2;
|
||
|
int striplength;
|
||
|
trivertx_t *v;
|
||
|
mtriangle_t *tv;
|
||
|
float s, t;
|
||
|
int index;
|
||
|
int len, bestlen, besttype;
|
||
|
int bestverts[1024];
|
||
|
int besttris[1024];
|
||
|
int type;
|
||
|
|
||
|
//
|
||
|
// build tristrips
|
||
|
//
|
||
|
numorder = 0;
|
||
|
numcommands = 0;
|
||
|
memset (used, 0, sizeof(used));
|
||
|
for (i=0 ; i<pheader->numtris ; i++)
|
||
|
{
|
||
|
// pick an unused triangle and start the trifan
|
||
|
if (used[i])
|
||
|
continue;
|
||
|
|
||
|
bestlen = 0;
|
||
|
for (type = 0 ; type < 2 ; type++)
|
||
|
// type = 1;
|
||
|
{
|
||
|
for (startv =0 ; startv < 3 ; startv++)
|
||
|
{
|
||
|
if (type == 1)
|
||
|
len = StripLength (i, startv);
|
||
|
else
|
||
|
len = FanLength (i, startv);
|
||
|
if (len > bestlen)
|
||
|
{
|
||
|
besttype = type;
|
||
|
bestlen = len;
|
||
|
for (j=0 ; j<bestlen+2 ; j++)
|
||
|
bestverts[j] = stripverts[j];
|
||
|
for (j=0 ; j<bestlen ; j++)
|
||
|
besttris[j] = striptris[j];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// mark the tris on the best strip as used
|
||
|
for (j=0 ; j<bestlen ; j++)
|
||
|
used[besttris[j]] = 1;
|
||
|
|
||
|
if (besttype == 1)
|
||
|
commands[numcommands++] = (bestlen+2);
|
||
|
else
|
||
|
commands[numcommands++] = -(bestlen+2);
|
||
|
|
||
|
for (j=0 ; j<bestlen+2 ; j++)
|
||
|
{
|
||
|
// emit a vertex into the reorder buffer
|
||
|
k = bestverts[j];
|
||
|
vertexorder[numorder++] = k;
|
||
|
|
||
|
// emit s/t coords into the commands stream
|
||
|
s = stverts[k].s;
|
||
|
t = stverts[k].t;
|
||
|
if (!triangles[besttris[0]].facesfront && stverts[k].onseam)
|
||
|
s += pheader->skinwidth / 2; // on back side
|
||
|
s = (s + 0.5) / pheader->skinwidth;
|
||
|
t = (t + 0.5) / pheader->skinheight;
|
||
|
|
||
|
*(float *)&commands[numcommands++] = s;
|
||
|
*(float *)&commands[numcommands++] = t;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
commands[numcommands++] = 0; // end of list marker
|
||
|
|
||
|
Con_DPrintf ("%3i tri %3i vert %3i cmd\n", pheader->numtris, numorder, numcommands);
|
||
|
|
||
|
allverts += numorder;
|
||
|
alltris += pheader->numtris;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
GL_MakeAliasModelDisplayLists
|
||
|
================
|
||
|
*/
|
||
|
void GL_MakeAliasModelDisplayLists (model_t *m, aliashdr_t *hdr)
|
||
|
{
|
||
|
int i, j;
|
||
|
maliasgroup_t *paliasgroup;
|
||
|
int *cmds;
|
||
|
trivertx_t *verts;
|
||
|
char cache[MAX_QPATH], fullpath[MAX_OSPATH], *c;
|
||
|
FILE *f;
|
||
|
int len;
|
||
|
byte *data;
|
||
|
float hscale, vscale; //johnfitz -- padded skins
|
||
|
int count; //johnfitz -- precompute texcoords for padded skins
|
||
|
int *loadcmds; //johnfitz
|
||
|
|
||
|
//johnfitz -- padded skins
|
||
|
hscale = (float)hdr->skinwidth/(float)TexMgr_PadConditional(hdr->skinwidth);
|
||
|
vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight);
|
||
|
//johnfitz
|
||
|
|
||
|
aliasmodel = m;
|
||
|
paliashdr = hdr; // (aliashdr_t *)Mod_Extradata (m);
|
||
|
|
||
|
//johnfitz -- generate meshes
|
||
|
|
||
|
#if 1 //always regenerate meshes
|
||
|
|
||
|
Con_DPrintf ("meshing %s...\n",m->name);
|
||
|
BuildTris ();
|
||
|
|
||
|
#else //conditional regeneration
|
||
|
|
||
|
if (gl_alwaysmesh.value) // build it from scratch, and don't bother saving it to disk
|
||
|
{
|
||
|
Con_DPrintf ("meshing %s...\n",m->name);
|
||
|
BuildTris ();
|
||
|
}
|
||
|
else // check disk cache, and rebuild it and save to disk if necessary
|
||
|
{
|
||
|
|
||
|
//create directories
|
||
|
sprintf (gldir, "%s/glquake", com_gamedir);
|
||
|
Sys_mkdir (com_gamedir);
|
||
|
Sys_mkdir (gldir);
|
||
|
|
||
|
//
|
||
|
// look for a cached version
|
||
|
//
|
||
|
strcpy (cache, "glquake/");
|
||
|
COM_StripExtension (m->name+strlen("progs/"), cache+strlen("glquake/"));
|
||
|
strcat (cache, ".ms2");
|
||
|
|
||
|
COM_FOpenFile (cache, &f);
|
||
|
if (f)
|
||
|
{
|
||
|
fread (&numcommands, 4, 1, f);
|
||
|
fread (&numorder, 4, 1, f);
|
||
|
fread (&commands, numcommands * sizeof(commands[0]), 1, f);
|
||
|
fread (&vertexorder, numorder * sizeof(vertexorder[0]), 1, f);
|
||
|
fclose (f);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// build it from scratch
|
||
|
//
|
||
|
Con_Printf ("meshing %s...\n",m->name);
|
||
|
BuildTris ();
|
||
|
|
||
|
//
|
||
|
// save out the cached version
|
||
|
//
|
||
|
sprintf (fullpath, "%s/%s", com_gamedir, cache);
|
||
|
f = fopen (fullpath, "wb");
|
||
|
if (f)
|
||
|
{
|
||
|
fwrite (&numcommands, 4, 1, f);
|
||
|
fwrite (&numorder, 4, 1, f);
|
||
|
fwrite (&commands, numcommands * sizeof(commands[0]), 1, f);
|
||
|
fwrite (&vertexorder, numorder * sizeof(vertexorder[0]), 1, f);
|
||
|
fclose (f);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
//johnfitz
|
||
|
|
||
|
|
||
|
// save the data out
|
||
|
|
||
|
paliashdr->poseverts = numorder;
|
||
|
|
||
|
cmds = Hunk_Alloc (numcommands * 4);
|
||
|
paliashdr->commands = (byte *)cmds - (byte *)paliashdr;
|
||
|
|
||
|
//johnfitz -- precompute texcoords for padded skins
|
||
|
loadcmds = commands;
|
||
|
while(1)
|
||
|
{
|
||
|
*cmds++ = count = *loadcmds++;
|
||
|
|
||
|
if (!count)
|
||
|
break;
|
||
|
|
||
|
if (count < 0)
|
||
|
count = -count;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
*(float *)cmds++ = hscale * (*(float *)loadcmds++);
|
||
|
*(float *)cmds++ = vscale * (*(float *)loadcmds++);
|
||
|
} while (--count);
|
||
|
}
|
||
|
//johnfitz
|
||
|
|
||
|
verts = Hunk_Alloc (paliashdr->numposes * paliashdr->poseverts * sizeof(trivertx_t));
|
||
|
paliashdr->posedata = (byte *)verts - (byte *)paliashdr;
|
||
|
for (i=0 ; i<paliashdr->numposes ; i++)
|
||
|
for (j=0 ; j<numorder ; j++)
|
||
|
*verts++ = poseverts[i][vertexorder[j]];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
ALIAS MODELS
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
aliashdr_t *pheader;
|
||
|
|
||
|
stvert_t stverts[MAXALIASVERTS];
|
||
|
mtriangle_t triangles[MAXALIASTRIS];
|
||
|
|
||
|
// a pose is a single set of vertexes. a frame may be
|
||
|
// an animating sequence of poses
|
||
|
trivertx_t *poseverts[MAXALIASFRAMES];
|
||
|
int posenum;
|
||
|
|
||
|
byte **player_8bit_texels_tbl;
|
||
|
byte *player_8bit_texels;
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
Mod_LoadAliasFrame
|
||
|
=================
|
||
|
*/
|
||
|
void * Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame)
|
||
|
{
|
||
|
trivertx_t *pframe, *pinframe;
|
||
|
int i, j;
|
||
|
daliasframe_t *pdaliasframe;
|
||
|
|
||
|
pdaliasframe = (daliasframe_t *)pin;
|
||
|
|
||
|
strcpy (frame->name, pdaliasframe->name);
|
||
|
frame->firstpose = posenum;
|
||
|
frame->numposes = 1;
|
||
|
|
||
|
for (i=0 ; i<3 ; i++)
|
||
|
{
|
||
|
// these are byte values, so we don't have to worry about
|
||
|
// endianness
|
||
|
frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i];
|
||
|
frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i];
|
||
|
}
|
||
|
|
||
|
|
||
|
pinframe = (trivertx_t *)(pdaliasframe + 1);
|
||
|
|
||
|
poseverts[posenum] = pinframe;
|
||
|
posenum++;
|
||
|
|
||
|
pinframe += pheader->numverts;
|
||
|
|
||
|
return (void *)pinframe;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
Mod_LoadAliasGroup
|
||
|
=================
|
||
|
*/
|
||
|
void *Mod_LoadAliasGroup (void * pin, maliasframedesc_t *frame)
|
||
|
{
|
||
|
daliasgroup_t *pingroup;
|
||
|
int i, numframes;
|
||
|
daliasinterval_t *pin_intervals;
|
||
|
void *ptemp;
|
||
|
|
||
|
pingroup = (daliasgroup_t *)pin;
|
||
|
|
||
|
numframes = LittleLong (pingroup->numframes);
|
||
|
|
||
|
frame->firstpose = posenum;
|
||
|
frame->numposes = numframes;
|
||
|
|
||
|
for (i=0 ; i<3 ; i++)
|
||
|
{
|
||
|
// these are byte values, so we don't have to worry about endianness
|
||
|
frame->bboxmin.v[i] = pingroup->bboxmin.v[i];
|
||
|
frame->bboxmax.v[i] = pingroup->bboxmax.v[i];
|
||
|
}
|
||
|
|
||
|
|
||
|
pin_intervals = (daliasinterval_t *)(pingroup + 1);
|
||
|
|
||
|
frame->interval = LittleFloat (pin_intervals->interval);
|
||
|
|
||
|
pin_intervals += numframes;
|
||
|
|
||
|
ptemp = (void *)pin_intervals;
|
||
|
|
||
|
for (i=0 ; i<numframes ; i++)
|
||
|
{
|
||
|
poseverts[posenum] = (trivertx_t *)((daliasframe_t *)ptemp + 1);
|
||
|
posenum++;
|
||
|
|
||
|
ptemp = (trivertx_t *)((daliasframe_t *)ptemp + 1) + pheader->numverts;
|
||
|
}
|
||
|
|
||
|
return ptemp;
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
Mod_FloodFillSkin
|
||
|
|
||
|
Fill background pixels so mipmapping doesn't have haloes - Ed
|
||
|
=================
|
||
|
*/
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
short x, y;
|
||
|
} floodfill_t;
|
||
|
|
||
|
extern unsigned d_8to24table[];
|
||
|
|
||
|
// must be a power of 2
|
||
|
#define FLOODFILL_FIFO_SIZE 0x1000
|
||
|
#define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1)
|
||
|
|
||
|
#define FLOODFILL_STEP( off, dx, dy ) \
|
||
|
{ \
|
||
|
if (pos[off] == fillcolor) \
|
||
|
{ \
|
||
|
pos[off] = 255; \
|
||
|
fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \
|
||
|
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \
|
||
|
} \
|
||
|
else if (pos[off] != 255) fdc = pos[off]; \
|
||
|
}
|
||
|
|
||
|
void Mod_FloodFillSkin( byte *skin, int skinwidth, int skinheight )
|
||
|
{
|
||
|
byte fillcolor = *skin; // assume this is the pixel to fill
|
||
|
floodfill_t fifo[FLOODFILL_FIFO_SIZE];
|
||
|
int inpt = 0, outpt = 0;
|
||
|
int filledcolor = -1;
|
||
|
int i;
|
||
|
|
||
|
if (filledcolor == -1)
|
||
|
{
|
||
|
filledcolor = 0;
|
||
|
// attempt to find opaque black
|
||
|
for (i = 0; i < 256; ++i)
|
||
|
if (d_8to24table[i] == (255 << 0)) // alpha 1.0
|
||
|
{
|
||
|
filledcolor = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// can't fill to filled color or to transparent color (used as visited marker)
|
||
|
if ((fillcolor == filledcolor) || (fillcolor == 255))
|
||
|
{
|
||
|
//printf( "not filling skin from %d to %d\n", fillcolor, filledcolor );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
fifo[inpt].x = 0, fifo[inpt].y = 0;
|
||
|
inpt = (inpt + 1) & FLOODFILL_FIFO_MASK;
|
||
|
|
||
|
while (outpt != inpt)
|
||
|
{
|
||
|
int x = fifo[outpt].x, y = fifo[outpt].y;
|
||
|
int fdc = filledcolor;
|
||
|
byte *pos = &skin[x + skinwidth * y];
|
||
|
|
||
|
outpt = (outpt + 1) & FLOODFILL_FIFO_MASK;
|
||
|
|
||
|
if (x > 0) FLOODFILL_STEP( -1, -1, 0 );
|
||
|
if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 );
|
||
|
if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 );
|
||
|
if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 );
|
||
|
skin[x + skinwidth * y] = fdc;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
Mod_LoadAllSkins
|
||
|
===============
|
||
|
*/
|
||
|
void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype)
|
||
|
{
|
||
|
int i, j, k, size, groupskins;
|
||
|
char name[32];
|
||
|
byte *copy, *skin, *texels;
|
||
|
daliasskingroup_t *pinskingroup;
|
||
|
daliasskininterval_t *pinskinintervals;
|
||
|
int padx, pady, ii, jj; //johnfitz -- padded player skin
|
||
|
char fbr_mask_name[64]; //johnfitz -- added for fullbright support
|
||
|
unsigned offset; //johnfitz
|
||
|
|
||
|
skin = (byte *)(pskintype + 1);
|
||
|
|
||
|
if (numskins < 1 || numskins > MAX_SKINS)
|
||
|
Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins);
|
||
|
|
||
|
size = pheader->skinwidth * pheader->skinheight;
|
||
|
|
||
|
for (i=0 ; i<numskins ; i++)
|
||
|
{
|
||
|
if (pskintype->type == ALIAS_SKIN_SINGLE)
|
||
|
{
|
||
|
Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight );
|
||
|
|
||
|
// save 8 bit texels for the player model to remap
|
||
|
texels = Hunk_AllocName(size, loadname);
|
||
|
pheader->texels[i] = texels - (byte *)pheader;
|
||
|
memcpy (texels, (byte *)(pskintype + 1), size);
|
||
|
|
||
|
//johnfitz -- rewritten
|
||
|
sprintf (name, "%s:frame%i", loadmodel->name, i);
|
||
|
offset = (unsigned)(pskintype+1) - (unsigned)mod_base;
|
||
|
if (Mod_CheckFullbrights ((byte *)(pskintype+1), size))
|
||
|
{
|
||
|
pheader->gltextures[i][0] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight,
|
||
|
SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_NOBRIGHT);
|
||
|
sprintf (fbr_mask_name, "%s:frame%i_glow", loadmodel->name, i);
|
||
|
pheader->fbtextures[i][0] = TexMgr_LoadImage (loadmodel, fbr_mask_name, pheader->skinwidth, pheader->skinheight,
|
||
|
SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_FULLBRIGHT);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pheader->gltextures[i][0] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight,
|
||
|
SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, TEXPREF_PAD);
|
||
|
pheader->fbtextures[i][0] = NULL;
|
||
|
}
|
||
|
|
||
|
pheader->gltextures[i][3] = pheader->gltextures[i][2] = pheader->gltextures[i][1] = pheader->gltextures[i][0];
|
||
|
pheader->fbtextures[i][3] = pheader->fbtextures[i][2] = pheader->fbtextures[i][1] = pheader->fbtextures[i][0];
|
||
|
//johnfitz
|
||
|
|
||
|
pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + size);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// animating skin group. yuck.
|
||
|
pskintype++;
|
||
|
pinskingroup = (daliasskingroup_t *)pskintype;
|
||
|
groupskins = LittleLong (pinskingroup->numskins);
|
||
|
pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1);
|
||
|
|
||
|
pskintype = (void *)(pinskinintervals + groupskins);
|
||
|
|
||
|
for (j=0 ; j<groupskins ; j++)
|
||
|
{
|
||
|
Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight );
|
||
|
if (j == 0) {
|
||
|
texels = Hunk_AllocName(size, loadname);
|
||
|
pheader->texels[i] = texels - (byte *)pheader;
|
||
|
memcpy (texels, (byte *)(pskintype), size);
|
||
|
}
|
||
|
|
||
|
//johnfitz -- rewritten
|
||
|
sprintf (name, "%s:frame%i_%i", loadmodel->name, i,j);
|
||
|
offset = (unsigned)(pskintype) - (unsigned)mod_base; //johnfitz
|
||
|
if (Mod_CheckFullbrights ((byte *)(pskintype), size))
|
||
|
{
|
||
|
pheader->gltextures[i][j&3] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight,
|
||
|
SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_NOBRIGHT);
|
||
|
sprintf (fbr_mask_name, "%s:frame%i_%i_glow", loadmodel->name, i,j);
|
||
|
pheader->fbtextures[i][j&3] = TexMgr_LoadImage (loadmodel, fbr_mask_name, pheader->skinwidth, pheader->skinheight,
|
||
|
SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_FULLBRIGHT);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pheader->gltextures[i][j&3] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight,
|
||
|
SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, TEXPREF_PAD);
|
||
|
pheader->fbtextures[i][j&3] = NULL;
|
||
|
}
|
||
|
//johnfitz
|
||
|
|
||
|
pskintype = (daliasskintype_t *)((byte *)(pskintype) + size);
|
||
|
}
|
||
|
k = j;
|
||
|
for (/* */; j < 4; j++)
|
||
|
pheader->gltextures[i][j&3] =
|
||
|
pheader->gltextures[i][j - k];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (void *)pskintype;
|
||
|
}
|
||
|
|
||
|
//=========================================================================
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
Mod_CalcAliasBounds -- johnfitz -- calculate bounds of alias model for nonrotated, yawrotated, and fullrotated cases
|
||
|
=================
|
||
|
*/
|
||
|
void Mod_CalcAliasBounds (aliashdr_t *a)
|
||
|
{
|
||
|
int i,j,k;
|
||
|
float dist, yawradius, radius;
|
||
|
vec3_t v;
|
||
|
|
||
|
//clear out all data
|
||
|
for (i=0; i<3;i++)
|
||
|
{
|
||
|
loadmodel->mins[i] = loadmodel->ymins[i] = loadmodel->rmins[i] = 999999;
|
||
|
loadmodel->maxs[i] = loadmodel->ymaxs[i] = loadmodel->rmaxs[i] = -999999;
|
||
|
radius = yawradius = 0;
|
||
|
}
|
||
|
|
||
|
//process verts
|
||
|
for (i=0 ; i<a->numposes; i++)
|
||
|
for (j=0; j<a->numverts; j++)
|
||
|
{
|
||
|
for (k=0; k<3;k++)
|
||
|
v[k] = poseverts[i][j].v[k] * pheader->scale[k] + pheader->scale_origin[k];
|
||
|
|
||
|
for (k=0; k<3;k++)
|
||
|
{
|
||
|
loadmodel->mins[k] = min (loadmodel->mins[k], v[k]);
|
||
|
loadmodel->maxs[k] = max (loadmodel->maxs[k], v[k]);
|
||
|
}
|
||
|
dist = v[0] * v[0] + v[1] * v[1];
|
||
|
if (yawradius < dist)
|
||
|
yawradius = dist;
|
||
|
dist += v[2] * v[2];
|
||
|
if (radius < dist)
|
||
|
radius = dist;
|
||
|
}
|
||
|
|
||
|
//rbounds will be used when entity has nonzero pitch or roll
|
||
|
radius = sqrt(radius);
|
||
|
loadmodel->rmins[0] = loadmodel->rmins[1] = loadmodel->rmins[2] = -radius;
|
||
|
loadmodel->rmaxs[0] = loadmodel->rmaxs[1] = loadmodel->rmaxs[2] = radius;
|
||
|
|
||
|
//ybounds will be used when entity has nonzero yaw
|
||
|
yawradius = sqrt(yawradius);
|
||
|
loadmodel->ymins[0] = loadmodel->ymins[1] = -yawradius;
|
||
|
loadmodel->ymaxs[0] = loadmodel->ymaxs[1] = yawradius;
|
||
|
loadmodel->ymins[2] = loadmodel->mins[2];
|
||
|
loadmodel->ymaxs[2] = loadmodel->maxs[2];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
Mod_SetExtraFlags -- johnfitz -- set up extra flags that aren't in the mdl
|
||
|
=================
|
||
|
*/
|
||
|
void Mod_SetExtraFlags (model_t *mod)
|
||
|
{
|
||
|
extern cvar_t r_nolerp_list;
|
||
|
char *s;
|
||
|
int i;
|
||
|
|
||
|
if (!mod || !mod->name || mod->type != mod_alias)
|
||
|
return;
|
||
|
|
||
|
mod->flags &= 0xFF; //only preserve first byte
|
||
|
|
||
|
// nolerp flag
|
||
|
for (s=r_nolerp_list.string; *s; s += i+1, i=0)
|
||
|
{
|
||
|
//search forwards to the next comma or end of string
|
||
|
for (i=0; s[i] != ',' && s[i] != 0; i++) ;
|
||
|
|
||
|
//compare it to the model name
|
||
|
if (!strncmp(mod->name, s, i))
|
||
|
{
|
||
|
mod->flags |= MOD_NOLERP;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// noshadow flag (TODO: make this a cvar list)
|
||
|
if (!strcmp (mod->name, "progs/flame2.mdl") ||
|
||
|
!strcmp (mod->name, "progs/flame.mdl") ||
|
||
|
!strcmp (mod->name, "progs/bolt1.mdl") ||
|
||
|
!strcmp (mod->name, "progs/bolt2.mdl") ||
|
||
|
!strcmp (mod->name, "progs/bolt3.mdl") ||
|
||
|
!strcmp (mod->name, "progs/laser.mdl"))
|
||
|
mod->flags |= MOD_NOSHADOW;
|
||
|
|
||
|
// fullbright hack (TODO: make this a cvar list)
|
||
|
if (!strcmp (mod->name, "progs/flame2.mdl") ||
|
||
|
!strcmp (mod->name, "progs/flame.mdl") ||
|
||
|
!strcmp (mod->name, "progs/boss.mdl"))
|
||
|
mod->flags |= MOD_FBRIGHTHACK;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
Mod_LoadAliasModel
|
||
|
=================
|
||
|
*/
|
||
|
void Mod_LoadAliasModel (model_t *mod, void *buffer)
|
||
|
{
|
||
|
int i, j;
|
||
|
mdl_t *pinmodel;
|
||
|
stvert_t *pinstverts;
|
||
|
dtriangle_t *pintriangles;
|
||
|
int version, numframes, numskins;
|
||
|
int size;
|
||
|
daliasframetype_t *pframetype;
|
||
|
daliasskintype_t *pskintype;
|
||
|
int start, end, total;
|
||
|
|
||
|
start = Hunk_LowMark ();
|
||
|
|
||
|
pinmodel = (mdl_t *)buffer;
|
||
|
mod_base = (byte *)buffer; //johnfitz
|
||
|
|
||
|
version = LittleLong (pinmodel->version);
|
||
|
if (version != ALIAS_VERSION)
|
||
|
Sys_Error ("%s has wrong version number (%i should be %i)",
|
||
|
mod->name, version, ALIAS_VERSION);
|
||
|
|
||
|
//
|
||
|
// allocate space for a working header, plus all the data except the frames,
|
||
|
// skin and group info
|
||
|
//
|
||
|
size = sizeof (aliashdr_t)
|
||
|
+ (LittleLong (pinmodel->numframes) - 1) *
|
||
|
sizeof (pheader->frames[0]);
|
||
|
pheader = Hunk_AllocName (size, loadname);
|
||
|
|
||
|
mod->flags = LittleLong (pinmodel->flags);
|
||
|
|
||
|
//
|
||
|
// endian-adjust and copy the data, starting with the alias model header
|
||
|
//
|
||
|
pheader->boundingradius = LittleFloat (pinmodel->boundingradius);
|
||
|
pheader->numskins = LittleLong (pinmodel->numskins);
|
||
|
pheader->skinwidth = LittleLong (pinmodel->skinwidth);
|
||
|
pheader->skinheight = LittleLong (pinmodel->skinheight);
|
||
|
|
||
|
if (pheader->skinheight > MAX_LBM_HEIGHT)
|
||
|
Sys_Error ("model %s has a skin taller than %d", mod->name,
|
||
|
MAX_LBM_HEIGHT);
|
||
|
|
||
|
pheader->numverts = LittleLong (pinmodel->numverts);
|
||
|
|
||
|
if (pheader->numverts <= 0)
|
||
|
Sys_Error ("model %s has no vertices", mod->name);
|
||
|
|
||
|
if (pheader->numverts > MAXALIASVERTS)
|
||
|
Sys_Error ("model %s has too many vertices", mod->name);
|
||
|
|
||
|
pheader->numtris = LittleLong (pinmodel->numtris);
|
||
|
|
||
|
if (pheader->numtris <= 0)
|
||
|
Sys_Error ("model %s has no triangles", mod->name);
|
||
|
|
||
|
pheader->numframes = LittleLong (pinmodel->numframes);
|
||
|
numframes = pheader->numframes;
|
||
|
if (numframes < 1)
|
||
|
Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes);
|
||
|
|
||
|
pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO;
|
||
|
mod->synctype = LittleLong (pinmodel->synctype);
|
||
|
mod->numframes = pheader->numframes;
|
||
|
|
||
|
for (i=0 ; i<3 ; i++)
|
||
|
{
|
||
|
pheader->scale[i] = LittleFloat (pinmodel->scale[i]);
|
||
|
pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]);
|
||
|
pheader->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]);
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// load the skins
|
||
|
//
|
||
|
pskintype = (daliasskintype_t *)&pinmodel[1];
|
||
|
pskintype = Mod_LoadAllSkins (pheader->numskins, pskintype);
|
||
|
|
||
|
//
|
||
|
// load base s and t vertices
|
||
|
//
|
||
|
pinstverts = (stvert_t *)pskintype;
|
||
|
|
||
|
for (i=0 ; i<pheader->numverts ; i++)
|
||
|
{
|
||
|
stverts[i].onseam = LittleLong (pinstverts[i].onseam);
|
||
|
stverts[i].s = LittleLong (pinstverts[i].s);
|
||
|
stverts[i].t = LittleLong (pinstverts[i].t);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// load triangle lists
|
||
|
//
|
||
|
pintriangles = (dtriangle_t *)&pinstverts[pheader->numverts];
|
||
|
|
||
|
for (i=0 ; i<pheader->numtris ; i++)
|
||
|
{
|
||
|
triangles[i].facesfront = LittleLong (pintriangles[i].facesfront);
|
||
|
|
||
|
for (j=0 ; j<3 ; j++)
|
||
|
{
|
||
|
triangles[i].vertindex[j] =
|
||
|
LittleLong (pintriangles[i].vertindex[j]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// load the frames
|
||
|
//
|
||
|
posenum = 0;
|
||
|
pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris];
|
||
|
|
||
|
for (i=0 ; i<numframes ; i++)
|
||
|
{
|
||
|
aliasframetype_t frametype;
|
||
|
frametype = LittleLong (pframetype->type);
|
||
|
if (frametype == ALIAS_SINGLE)
|
||
|
pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]);
|
||
|
else
|
||
|
pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]);
|
||
|
}
|
||
|
|
||
|
pheader->numposes = posenum;
|
||
|
|
||
|
mod->type = mod_alias;
|
||
|
|
||
|
Mod_SetExtraFlags (mod); //johnfitz
|
||
|
|
||
|
Mod_CalcAliasBounds (pheader); //johnfitz
|
||
|
|
||
|
//
|
||
|
// build the draw lists
|
||
|
//
|
||
|
GL_MakeAliasModelDisplayLists (mod, pheader);
|
||
|
|
||
|
//
|
||
|
// move the complete, relocatable alias model to the cache
|
||
|
//
|
||
|
end = Hunk_LowMark ();
|
||
|
total = end - start;
|
||
|
|
||
|
Cache_Alloc (&mod->cache, total, loadname);
|
||
|
if (!mod->cache.data)
|
||
|
return;
|
||
|
memcpy (mod->cache.data, pheader, total);
|
||
|
|
||
|
Hunk_FreeToLowMark (start);
|
||
|
}
|