9ae7e2621d
Lots of changes. CSQC should be functional, but is still tied to debug builds. It WILL have some bugs still, hopefully I'll be able to clean them up better if people test it a bit. Precompiled headers are working properly now. Compile times are now much quicker in msvc. This takes most of the files this commit. Restructured how client commands work. They're buffered outside the network message, some multithreaded code is in. It needs a bit of testing before it's active. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@885 fc73d0e0-1445-4013-8a0c-d673dee63da5
3270 lines
83 KiB
C
3270 lines
83 KiB
C
#include "quakedef.h"
|
|
#ifdef RGLQUAKE
|
|
#include "glquake.h"
|
|
#include "shader.h"
|
|
#include "hash.h"
|
|
|
|
#ifdef ZYMOTICMODELS
|
|
#define SKELETALMODELS
|
|
#endif
|
|
|
|
//FIXME
|
|
typedef struct
|
|
{
|
|
float scale[3]; // multiply qbyte verts by this
|
|
float translate[3]; // then add this
|
|
char name[16]; // frame name from grabbing
|
|
dtrivertx_t verts[1]; // variable sized
|
|
} dmd2aliasframe_t;
|
|
|
|
|
|
|
|
// entity_state_t->renderfx flags
|
|
#define Q2RF_MINLIGHT 1 // allways have some light (viewmodel)
|
|
#define Q2RF_VIEWERMODEL 2 // don't draw through eyes, only mirrors
|
|
#define Q2RF_WEAPONMODEL 4 // only draw through eyes
|
|
#define Q2RF_FULLBRIGHT 8 // allways draw full intensity
|
|
#define Q2RF_DEPTHHACK 16 // for view weapon Z crunching
|
|
#define Q2RF_TRANSLUCENT 32
|
|
#define Q2RF_FRAMELERP 64
|
|
#define Q2RF_BEAM 128
|
|
#define Q2RF_CUSTOMSKIN 256 // skin is an index in image_precache
|
|
#define Q2RF_GLOW 512 // pulse lighting for bonus items
|
|
#define Q2RF_SHELL_RED 1024
|
|
#define Q2RF_SHELL_GREEN 2048
|
|
#define Q2RF_SHELL_BLUE 4096
|
|
|
|
//ROGUE
|
|
#define Q2RF_IR_VISIBLE 0x00008000 // 32768
|
|
#define Q2RF_SHELL_DOUBLE 0x00010000 // 65536
|
|
#define Q2RF_SHELL_HALF_DAM 0x00020000
|
|
#define Q2RF_USE_DISGUISE 0x00040000
|
|
//ROGUE
|
|
|
|
|
|
|
|
|
|
extern cvar_t gl_part_flame, gl_part_torch, r_fullbrightSkins, r_fb_models;
|
|
extern cvar_t r_noaliasshadows;
|
|
void R_TorchEffect (vec3_t pos, int type);
|
|
void GLMod_FloodFillSkin( qbyte *skin, int skinwidth, int skinheight );
|
|
|
|
|
|
|
|
extern char loadname[32]; // for hunk tags
|
|
|
|
|
|
int numTempColours;
|
|
byte_vec4_t *tempColours;
|
|
|
|
int numTempVertexCoords;
|
|
vec4_t *tempVertexCoords;
|
|
|
|
int numTempNormals;
|
|
vec3_t *tempNormals;
|
|
|
|
extern cvar_t gl_ati_truform;
|
|
extern cvar_t r_vertexdlights;
|
|
|
|
typedef struct {
|
|
int ofs_indexes;
|
|
int numindexes;
|
|
|
|
int ofs_trineighbours;
|
|
|
|
int numskins;
|
|
int ofsskins;
|
|
|
|
qboolean sharesverts; //used with models with two shaders using the same vertex.
|
|
|
|
|
|
int numverts;
|
|
|
|
int ofs_st_array;
|
|
|
|
int groups;
|
|
int groupofs;
|
|
|
|
int nextsurf;
|
|
|
|
|
|
int numbones;
|
|
int ofsbones;
|
|
int numtransforms;
|
|
int ofstransforms;
|
|
|
|
//these exist only in the root mesh.
|
|
int numtagframes;
|
|
int numtags;
|
|
int ofstags;
|
|
} galiasinfo_t;
|
|
|
|
//frame is an index into this
|
|
typedef struct {
|
|
#ifdef SKELETALMODELS
|
|
qboolean isskeletal;
|
|
#endif
|
|
int numposes;
|
|
float rate;
|
|
int poseofs;
|
|
} galiasgroup_t;
|
|
|
|
typedef struct {
|
|
int ofsverts;
|
|
int ofsnormals;
|
|
|
|
vec3_t scale;
|
|
vec3_t scale_origin;
|
|
} galiaspose_t;
|
|
|
|
#ifdef SKELETALMODELS
|
|
typedef struct {
|
|
int parent;
|
|
} galiasbone_t;
|
|
|
|
typedef struct {
|
|
//skeletal poses refer to this.
|
|
int vertexindex;
|
|
int boneindex;
|
|
vec4_t org;
|
|
} galisskeletaltransforms_t;
|
|
#endif
|
|
|
|
//we can't be bothered with animating skins.
|
|
//We'll load up to four of them but after that you're on your own
|
|
typedef struct {
|
|
int skinwidth;
|
|
int skinheight;
|
|
int ofstexels; //this is 8bit for frame 0 only. only valid in q1 models without replacement textures, used for colourising player skins.
|
|
float skinspeed;
|
|
int texnums;
|
|
int ofstexnums;
|
|
} galiasskin_t;
|
|
|
|
typedef struct {
|
|
int base;
|
|
int bump;
|
|
int fullbright;
|
|
|
|
shader_t *shader;
|
|
} galiastexnum_t;
|
|
|
|
typedef struct {
|
|
char name[MAX_QPATH];
|
|
galiastexnum_t texnum;
|
|
int colour;
|
|
int skinnum;
|
|
bucket_t bucket;
|
|
} galiascolourmapped_t;
|
|
|
|
static hashtable_t skincolourmapped;
|
|
|
|
static vec3_t shadevector;
|
|
static vec3_t shadelight, ambientlight;
|
|
static void R_LerpFrames(mesh_t *mesh, galiaspose_t *p1, galiaspose_t *p2, float lerp, qbyte alpha, float expand)
|
|
{
|
|
extern cvar_t r_nolerp, r_nolightdir;
|
|
float blerp = 1-lerp;
|
|
int i;
|
|
float l;
|
|
int temp;
|
|
vec3_t *p1v, *p2v;
|
|
vec3_t *p1n, *p2n;
|
|
p1v = (vec3_t *)((char *)p1 + p1->ofsverts);
|
|
p2v = (vec3_t *)((char *)p2 + p2->ofsverts);
|
|
|
|
p1n = (vec3_t *)((char *)p1 + p1->ofsnormals);
|
|
p2n = (vec3_t *)((char *)p2 + p2->ofsnormals);
|
|
|
|
if (p1v == p2v || r_nolerp.value)
|
|
{
|
|
mesh->normals_array = (vec3_t*)((char *)p1 + p1->ofsnormals);
|
|
if (r_nolightdir.value)
|
|
{
|
|
for (i = 0; i < mesh->numvertexes; i++)
|
|
{
|
|
mesh->xyz_array[i][0] = p1v[i][0];
|
|
mesh->xyz_array[i][1] = p1v[i][1];
|
|
mesh->xyz_array[i][2] = p1v[i][2];
|
|
|
|
mesh->colors_array[i][0] = /*ambientlight[0]/2*/+shadelight[0];
|
|
mesh->colors_array[i][1] = /*ambientlight[1]/2*/+shadelight[1];
|
|
mesh->colors_array[i][2] = /*ambientlight[2]/2*/+shadelight[2];
|
|
mesh->colors_array[i][3] = alpha;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < mesh->numvertexes; i++)
|
|
{
|
|
mesh->xyz_array[i][0] = p1v[i][0];
|
|
mesh->xyz_array[i][1] = p1v[i][1];
|
|
mesh->xyz_array[i][2] = p1v[i][2];
|
|
|
|
l = DotProduct(mesh->normals_array[i], shadevector);
|
|
|
|
temp = l*ambientlight[0]+shadelight[0];
|
|
if (temp < 0) temp = 0;
|
|
else if (temp > 255) temp = 255;
|
|
mesh->colors_array[i][0] = temp;
|
|
|
|
temp = l*ambientlight[1]+shadelight[1];
|
|
if (temp < 0) temp = 0;
|
|
else if (temp > 255) temp = 255;
|
|
mesh->colors_array[i][1] = temp;
|
|
|
|
temp = l*ambientlight[2]+shadelight[2];
|
|
if (temp < 0) temp = 0;
|
|
else if (temp > 255) temp = 255;
|
|
mesh->colors_array[i][2] = temp;
|
|
|
|
mesh->colors_array[i][3] = alpha;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (r_nolightdir.value)
|
|
{
|
|
for (i = 0; i < mesh->numvertexes; i++)
|
|
{
|
|
mesh->normals_array[i][0] = p1n[i][0]*lerp + p2n[i][0]*blerp;
|
|
mesh->normals_array[i][1] = p1n[i][1]*lerp + p2n[i][1]*blerp;
|
|
mesh->normals_array[i][2] = p1n[i][2]*lerp + p2n[i][2]*blerp;
|
|
|
|
mesh->xyz_array[i][0] = p1v[i][0]*lerp + p2v[i][0]*blerp;
|
|
mesh->xyz_array[i][1] = p1v[i][1]*lerp + p2v[i][1]*blerp;
|
|
mesh->xyz_array[i][2] = p1v[i][2]*lerp + p2v[i][2]*blerp;
|
|
|
|
mesh->colors_array[i][0] = ambientlight[0];
|
|
mesh->colors_array[i][1] = ambientlight[1];
|
|
mesh->colors_array[i][2] = ambientlight[2];
|
|
mesh->colors_array[i][3] = alpha;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < mesh->numvertexes; i++)
|
|
{
|
|
mesh->normals_array[i][0] = p1n[i][0]*lerp + p2n[i][0]*blerp;
|
|
mesh->normals_array[i][1] = p1n[i][1]*lerp + p2n[i][1]*blerp;
|
|
mesh->normals_array[i][2] = p1n[i][2]*lerp + p2n[i][2]*blerp;
|
|
|
|
mesh->xyz_array[i][0] = p1v[i][0]*lerp + p2v[i][0]*blerp;
|
|
mesh->xyz_array[i][1] = p1v[i][1]*lerp + p2v[i][1]*blerp;
|
|
mesh->xyz_array[i][2] = p1v[i][2]*lerp + p2v[i][2]*blerp;
|
|
|
|
l = DotProduct(mesh->normals_array[i], shadevector);
|
|
temp = l*ambientlight[0]+shadelight[0];
|
|
if (temp < 0) temp = 0;
|
|
else if (temp > 255) temp = 255;
|
|
mesh->colors_array[i][0] = temp;
|
|
|
|
temp = l*ambientlight[1]+shadelight[1];
|
|
if (temp < 0) temp = 0;
|
|
else if (temp > 255) temp = 255;
|
|
mesh->colors_array[i][1] = temp;
|
|
|
|
temp = l*ambientlight[2]+shadelight[2];
|
|
if (temp < 0) temp = 0;
|
|
else if (temp > 255) temp = 255;
|
|
mesh->colors_array[i][2] = temp;
|
|
|
|
mesh->colors_array[i][3] = alpha;
|
|
}
|
|
}
|
|
}
|
|
if (expand)
|
|
{
|
|
for (i = 0; i < mesh->numvertexes; i++)
|
|
{
|
|
mesh->xyz_array[i][0] += mesh->normals_array[i][0]*expand;
|
|
mesh->xyz_array[i][1] += mesh->normals_array[i][1]*expand;
|
|
mesh->xyz_array[i][2] += mesh->normals_array[i][2]*expand;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef SKELETALMODELS
|
|
static void R_BuildSkeletalMesh(mesh_t *mesh, float *plerp, float **pose, int poses, galiasbone_t *bones, int bonecount, galisskeletaltransforms_t *weights, int numweights)
|
|
{
|
|
float bonepose[256][12];
|
|
float *outhead;
|
|
galisskeletaltransforms_t *v;
|
|
|
|
int i, k, b;
|
|
float *out, *matrix, m[12];
|
|
|
|
|
|
// vertex weighted skeletal
|
|
// interpolate matrices and concatenate them to their parents
|
|
for (i = 0;i < bonecount;i++)
|
|
{
|
|
for (k = 0;k < 12;k++)
|
|
m[k] = 0;
|
|
for (b = 0;b < poses;b++)
|
|
{
|
|
matrix = pose[b] + i*12;
|
|
|
|
for (k = 0;k < 12;k++)
|
|
m[k] += matrix[k] * plerp[b];
|
|
}
|
|
if (bones[i].parent >= 0)
|
|
R_ConcatTransforms((void*)bonepose[bones[i].parent], (void*)m, (void*)bonepose[i]);
|
|
else
|
|
for (k = 0;k < 12;k++) //parentless
|
|
bonepose[i][k] = m[k];
|
|
}
|
|
|
|
outhead = (float*)mesh->xyz_array;
|
|
// blend the vertex bone weights
|
|
memset(outhead, 0, mesh->numvertexes * sizeof(mesh->xyz_array[0]));
|
|
|
|
for (i = 0; i < mesh->numvertexes; i++)
|
|
{
|
|
mesh->normals_array[i][0] = 0;
|
|
mesh->normals_array[i][1] = 0;
|
|
mesh->normals_array[i][2] = 1;
|
|
|
|
mesh->colors_array[i][0] = ambientlight[0];
|
|
mesh->colors_array[i][1] = ambientlight[1];
|
|
mesh->colors_array[i][2] = ambientlight[2];
|
|
mesh->colors_array[i][3] = 255;//alpha;
|
|
/*
|
|
mesh->xyz_array[i][0] = 0;
|
|
mesh->xyz_array[i][1] = 0;
|
|
mesh->xyz_array[i][2] = 0;
|
|
mesh->xyz_array[i][3] = 1;
|
|
*/
|
|
}
|
|
|
|
v = weights;
|
|
for (i = 0;i < numweights;i++, v++)
|
|
{
|
|
out = outhead + v->vertexindex * 4;
|
|
matrix = bonepose[v->boneindex];
|
|
// FIXME: this can very easily be optimized with SSE or 3DNow
|
|
out[0] += v->org[0] * matrix[0] + v->org[1] * matrix[1] + v->org[2] * matrix[ 2] + v->org[3] * matrix[ 3];
|
|
out[1] += v->org[0] * matrix[4] + v->org[1] * matrix[5] + v->org[2] * matrix[ 6] + v->org[3] * matrix[ 7];
|
|
out[2] += v->org[0] * matrix[8] + v->org[1] * matrix[9] + v->org[2] * matrix[10] + v->org[3] * matrix[11];
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void R_GAliasAddDlights(mesh_t *mesh, vec3_t org, vec3_t angles)
|
|
{
|
|
int l, v;
|
|
vec3_t rel;
|
|
vec3_t dir;
|
|
float dot, d, a, f;
|
|
for (l=0 ; l<MAX_DLIGHTS ; l++)
|
|
{
|
|
if (cl_dlights[l].radius)
|
|
{
|
|
VectorSubtract (cl_dlights[l].origin,
|
|
org,
|
|
dir);
|
|
if (Length(dir)>cl_dlights[l].radius+mesh->radius) //far out man!
|
|
continue;
|
|
|
|
rel[0] = -DotProduct(dir, currententity->axis[0]);
|
|
rel[1] = -DotProduct(dir, currententity->axis[1]); //quake's crazy.
|
|
rel[2] = -DotProduct(dir, currententity->axis[2]);
|
|
/*
|
|
glBegin(GL_LINES);
|
|
glVertex3f(0,0,0);
|
|
glVertex3f(rel[0],rel[1],rel[2]);
|
|
glEnd();
|
|
*/
|
|
for (v = 0; v < mesh->numvertexes; v++)
|
|
{
|
|
VectorSubtract(mesh->xyz_array[v], rel, dir);
|
|
dot = DotProduct(dir, mesh->normals_array[v]);
|
|
if (dot>0)
|
|
{
|
|
d = DotProduct(dir, dir);
|
|
a = 1/d;
|
|
if (a>0)
|
|
{
|
|
a *= 10000000*dot/sqrt(d);
|
|
f = mesh->colors_array[v][0] + a*cl_dlights[l].color[0];
|
|
if (f > 255)
|
|
f = 255;
|
|
else if (f < 0)
|
|
f = 0;
|
|
mesh->colors_array[v][0] = f;
|
|
|
|
f = mesh->colors_array[v][1] + a*cl_dlights[l].color[1];
|
|
if (f > 255)
|
|
f = 255;
|
|
else if (f < 0)
|
|
f = 0;
|
|
mesh->colors_array[v][1] = f;
|
|
|
|
f = mesh->colors_array[v][2] + a*cl_dlights[l].color[2];
|
|
if (f > 255)
|
|
f = 255;
|
|
else if (f < 0)
|
|
f = 0;
|
|
mesh->colors_array[v][2] = f;
|
|
}
|
|
// else
|
|
// mesh->colors_array[v][1] =255;
|
|
}
|
|
// else
|
|
// mesh->colors_array[v][2] =255;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static qboolean R_GAliasBuildMesh(mesh_t *mesh, galiasinfo_t *inf, int frame1, int frame2, float lerp, float alpha)
|
|
{
|
|
galiasgroup_t *g1, *g2;
|
|
if (!inf->groups)
|
|
{
|
|
Con_DPrintf("Model with no frames (%s)\n", currententity->model->name);
|
|
return false;
|
|
}
|
|
if (frame1 < 0)
|
|
{
|
|
Con_DPrintf("Negative frame (%s)\n", currententity->model->name);
|
|
frame1 = 0;
|
|
}
|
|
if (frame2 < 0)
|
|
{
|
|
Con_DPrintf("Negative frame (%s)\n", currententity->model->name);
|
|
frame2 = frame1;
|
|
}
|
|
if (frame1 >= inf->groups)
|
|
{
|
|
Con_DPrintf("Too high frame %i (%s)\n", frame1, currententity->model->name);
|
|
frame1 = 0;
|
|
}
|
|
if (frame2 >= inf->groups)
|
|
{
|
|
Con_DPrintf("Too high frame %i (%s)\n", frame2, currententity->model->name);
|
|
frame2 = frame1;
|
|
}
|
|
|
|
if (lerp <= 0)
|
|
frame2 = frame1;
|
|
else if (lerp >= 1)
|
|
frame1 = frame2;
|
|
|
|
if (numTempColours < inf->numverts)
|
|
{
|
|
if (tempColours)
|
|
BZ_Free(tempColours);
|
|
tempColours = BZ_Malloc(sizeof(*tempColours)*inf->numverts);
|
|
numTempColours = inf->numverts;
|
|
}
|
|
if (numTempNormals < inf->numverts)
|
|
{
|
|
if (tempNormals)
|
|
BZ_Free(tempNormals);
|
|
tempNormals = BZ_Malloc(sizeof(*tempNormals)*inf->numverts);
|
|
numTempNormals = inf->numverts;
|
|
}
|
|
if (numTempVertexCoords < inf->numverts)
|
|
{
|
|
if (tempVertexCoords)
|
|
BZ_Free(tempVertexCoords);
|
|
tempVertexCoords = BZ_Malloc(sizeof(*tempVertexCoords)*inf->numverts);
|
|
numTempVertexCoords = inf->numverts;
|
|
}
|
|
|
|
mesh->indexes = (index_t*)((char *)inf + inf->ofs_indexes);
|
|
mesh->numindexes = inf->numindexes;
|
|
mesh->st_array = (vec2_t*)((char *)inf + inf->ofs_st_array);
|
|
mesh->lmst_array = NULL;
|
|
mesh->colors_array = tempColours;
|
|
mesh->xyz_array = tempVertexCoords;
|
|
mesh->numvertexes = inf->numverts;
|
|
mesh->trneighbors = (int *)((char *)inf + inf->ofs_trineighbours);
|
|
|
|
if (inf->sharesverts)
|
|
return false; //don't generate the new vertex positions. We still have them all.
|
|
|
|
mesh->normals_array = tempNormals;
|
|
|
|
g1 = (galiasgroup_t*)((char *)inf + inf->groupofs + sizeof(galiasgroup_t)*frame1);
|
|
g2 = (galiasgroup_t*)((char *)inf + inf->groupofs + sizeof(galiasgroup_t)*frame2);
|
|
|
|
//we don't support meshes with one pose skeletal and annother not.
|
|
//we don't support meshes with one group skeletal and annother not.
|
|
|
|
#ifdef SKELETALMODELS
|
|
if (g1->isskeletal)
|
|
{
|
|
int l=0;
|
|
float plerp[4];
|
|
float *pose[4];
|
|
float mlerp; //minor lerp, poses within a group.
|
|
|
|
mlerp = cl.time*g1->rate;
|
|
frame1=mlerp;
|
|
frame2=frame1+1;
|
|
mlerp-=frame1;
|
|
frame1=frame1%g1->numposes;
|
|
frame2=frame2%g1->numposes;
|
|
|
|
plerp[l] = (1-mlerp)*(1-lerp);
|
|
if (plerp[l]>0)
|
|
pose[l++] = (float *)((char *)g1 + g1->poseofs + sizeof(float)*inf->numbones*12*frame1);
|
|
plerp[l] = (mlerp)*(1-lerp);
|
|
if (plerp[l]>0)
|
|
pose[l++] = (float *)((char *)g1 + g1->poseofs + sizeof(float)*inf->numbones*12*frame2);
|
|
|
|
mlerp = cl.time*g2->rate;
|
|
frame1=mlerp;
|
|
frame2=frame1+1;
|
|
mlerp-=frame1;
|
|
frame1=frame1%g2->numposes;
|
|
frame2=frame2%g2->numposes;
|
|
|
|
plerp[l] = (1-mlerp)*(lerp);
|
|
if (plerp[l]>0)
|
|
pose[l++] = (float *)((char *)g2 + g2->poseofs + sizeof(float)*inf->numbones*12*frame1);
|
|
plerp[l] = (mlerp)*(lerp);
|
|
if (plerp[l]>0)
|
|
pose[l++] = (float *)((char *)g2 + g2->poseofs + sizeof(float)*inf->numbones*12*frame2);
|
|
/*
|
|
pose[0] = (float *)((char *)g1 + g1->poseofs);
|
|
plerp[0] = 1;
|
|
plerp[1] = 0;
|
|
plerp[3] = 0;
|
|
plerp[4] = 0;
|
|
l = 1;
|
|
*/
|
|
R_BuildSkeletalMesh(mesh, plerp, pose, l, (galiasbone_t *)((char*)inf+inf->ofsbones), inf->numbones, (galisskeletaltransforms_t *)((char*)inf+inf->ofstransforms), inf->numtransforms);
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
if (g1 == g2) //lerping within group is only done if not changing group
|
|
{
|
|
lerp = cl.time*g1->rate;
|
|
frame1=lerp;
|
|
frame2=frame1+1;
|
|
lerp-=frame1;
|
|
frame1=frame1%g1->numposes;
|
|
frame2=frame2%g1->numposes;
|
|
|
|
}
|
|
else //don't bother with a four way lerp. Yeah, this will produce jerkyness with models with just framegroups.
|
|
{
|
|
frame1=0;
|
|
frame2=0;
|
|
}
|
|
|
|
R_LerpFrames(mesh, (galiaspose_t *)((char *)g1 + g1->poseofs + sizeof(galiaspose_t)*frame1),
|
|
(galiaspose_t *)((char *)g2 + g2->poseofs + sizeof(galiaspose_t)*frame2),
|
|
1-lerp, (qbyte)(alpha*255), currententity->fatness);//20*sin(cl.time));
|
|
|
|
return true; //to allow the mesh to be dlighted.
|
|
}
|
|
|
|
void GL_GAliasFlushSkinCache(void)
|
|
{
|
|
int i;
|
|
bucket_t *b;
|
|
for (i = 0; i < skincolourmapped.numbuckets; i++)
|
|
{
|
|
while((b = skincolourmapped.bucket[i]))
|
|
{
|
|
skincolourmapped.bucket[i] = b->next;
|
|
BZ_Free(b->data);
|
|
}
|
|
}
|
|
if (skincolourmapped.bucket)
|
|
BZ_Free(skincolourmapped.bucket);
|
|
skincolourmapped.bucket = NULL;
|
|
skincolourmapped.numbuckets = 0;
|
|
}
|
|
|
|
static galiastexnum_t *GL_ChooseSkin(galiasinfo_t *inf, char *modelname, entity_t *e)
|
|
{
|
|
galiasskin_t *skins;
|
|
galiastexnum_t *texnums;
|
|
int frame;
|
|
|
|
int tc, bc;
|
|
|
|
if (!gl_nocolors.value)
|
|
{
|
|
if (e->scoreboard)
|
|
{
|
|
if (!e->scoreboard->skin)
|
|
Skin_Find(e->scoreboard);
|
|
tc = e->scoreboard->topcolor;
|
|
bc = e->scoreboard->bottomcolor;
|
|
|
|
//colour forcing
|
|
if (cl.splitclients<2 && !(cl.fpd & FPD_NO_FORCE_COLOR)) //no colour/skin forcing in splitscreen.
|
|
{
|
|
if (cl.teamplay && !strcmp(e->scoreboard->team, cl.players[cl.playernum[0]].team))
|
|
{
|
|
if (cl_teamtopcolor>=0)
|
|
tc = cl_teamtopcolor;
|
|
if (cl_teambottomcolor>=0)
|
|
bc = cl_teambottomcolor;
|
|
}
|
|
else
|
|
{
|
|
if (cl_enemytopcolor>=0)
|
|
tc = cl_enemytopcolor;
|
|
if (cl_enemybottomcolor>=0)
|
|
bc = cl_enemybottomcolor;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tc = 1;
|
|
bc = 1;
|
|
}
|
|
|
|
if (tc != 1 || bc != 1 || (e->scoreboard && e->scoreboard->skin))
|
|
{
|
|
int inwidth, inheight;
|
|
int tinwidth, tinheight;
|
|
char *skinname;
|
|
qbyte *original;
|
|
int cc;
|
|
galiascolourmapped_t *cm;
|
|
cc = (tc<<4)|bc;
|
|
|
|
if (!strstr(modelname, "progs/player.mdl"))
|
|
skinname = modelname;
|
|
else
|
|
{
|
|
if (e->scoreboard && e->scoreboard->skin && !gl_nocolors.value)
|
|
skinname = e->scoreboard->skin->name;
|
|
else
|
|
skinname = modelname;
|
|
}
|
|
|
|
if (!skincolourmapped.numbuckets)
|
|
Hash_InitTable(&skincolourmapped, 256, BZ_Malloc(Hash_BytesForBuckets(256)));
|
|
|
|
for (cm = Hash_Get(&skincolourmapped, skinname); cm; cm = Hash_GetNext(&skincolourmapped, skinname, cm))
|
|
{
|
|
if (cm->colour == cc && cm->skinnum == e->skinnum)
|
|
{
|
|
return &cm->texnum;
|
|
}
|
|
}
|
|
|
|
skins = (galiasskin_t*)((char *)inf + inf->ofsskins);
|
|
if (!skins->texnums)
|
|
return NULL;
|
|
if (e->skinnum >= 0 && e->skinnum < inf->numskins)
|
|
skins += e->skinnum;
|
|
texnums = (galiastexnum_t*)((char *)skins + skins->ofstexnums);
|
|
|
|
|
|
//colourmap isn't present yet.
|
|
cm = BZ_Malloc(sizeof(*cm));
|
|
Q_strncpyz(cm->name, skinname, sizeof(cm->name));
|
|
Hash_Add(&skincolourmapped, cm->name, cm, &cm->bucket);
|
|
cm->colour = cc;
|
|
cm->skinnum = e->skinnum;
|
|
cm->texnum.fullbright = 0;
|
|
cm->texnum.base = 0;
|
|
cm->texnum.bump = texnums[cm->skinnum].bump; //can't colour bumpmapping
|
|
if (skinname!=modelname && e->scoreboard && e->scoreboard->skin)
|
|
{
|
|
original = Skin_Cache8(e->scoreboard->skin);
|
|
inwidth = e->scoreboard->skin->width;
|
|
inheight = e->scoreboard->skin->height;
|
|
}
|
|
else
|
|
{
|
|
original = NULL;
|
|
inwidth = 0;
|
|
}
|
|
if (!original)
|
|
{
|
|
if (skins->ofstexels)
|
|
{
|
|
original = (qbyte *)skins + skins->ofstexels;
|
|
inwidth = skins->skinwidth;
|
|
inheight = skins->skinheight;
|
|
}
|
|
else
|
|
{
|
|
original = NULL;
|
|
inwidth = 0;
|
|
inheight = 0;
|
|
}
|
|
}
|
|
tinwidth = skins->skinwidth;
|
|
tinheight = skins->skinheight;
|
|
if (original)
|
|
{
|
|
int i, j;
|
|
qbyte translate[256];
|
|
unsigned translate32[256];
|
|
static unsigned pixels[512*512];
|
|
unsigned *out;
|
|
unsigned frac, fracstep;
|
|
|
|
unsigned scaled_width, scaled_height;
|
|
qbyte *inrow;
|
|
|
|
texnums = &cm->texnum;
|
|
|
|
texnums->base = 0;
|
|
texnums->fullbright = 0;
|
|
|
|
scaled_width = gl_max_size.value < 512 ? gl_max_size.value : 512;
|
|
scaled_height = gl_max_size.value < 512 ? gl_max_size.value : 512;
|
|
|
|
for (i=0 ; i<256 ; i++)
|
|
translate[i] = i;
|
|
|
|
tc<<=4;
|
|
bc<<=4;
|
|
|
|
for (i=0 ; i<16 ; i++)
|
|
{
|
|
if (tc < 128) // the artists made some backwards ranges. sigh.
|
|
translate[TOP_RANGE+i] = tc+i;
|
|
else
|
|
translate[TOP_RANGE+i] = tc+15-i;
|
|
|
|
if (bc < 128)
|
|
translate[BOTTOM_RANGE+i] = bc+i;
|
|
else
|
|
translate[BOTTOM_RANGE+i] = bc+15-i;
|
|
}
|
|
|
|
|
|
for (i=0 ; i<256 ; i++)
|
|
translate32[i] = d_8to24rgbtable[translate[i]];
|
|
|
|
out = pixels;
|
|
fracstep = tinwidth*0x10000/scaled_width;
|
|
for (i=0 ; i<scaled_height ; i++, out += scaled_width)
|
|
{
|
|
inrow = original + inwidth*(i*tinheight/scaled_height);
|
|
frac = fracstep >> 1;
|
|
for (j=0 ; j<scaled_width ; j+=4)
|
|
{
|
|
out[j] = translate32[inrow[frac>>16]];
|
|
frac += fracstep;
|
|
out[j+1] = translate32[inrow[frac>>16]];
|
|
frac += fracstep;
|
|
out[j+2] = translate32[inrow[frac>>16]];
|
|
frac += fracstep;
|
|
out[j+3] = translate32[inrow[frac>>16]];
|
|
frac += fracstep;
|
|
}
|
|
}
|
|
texnums->base = texture_extension_number++;
|
|
GL_Bind(texnums->base);
|
|
qglTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
|
|
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
|
|
//now do the fullbrights.
|
|
out = pixels;
|
|
fracstep = tinwidth*0x10000/scaled_width;
|
|
for (i=0 ; i<scaled_height ; i++, out += scaled_width)
|
|
{
|
|
inrow = original + inwidth*(i*tinheight/scaled_height);
|
|
frac = fracstep >> 1;
|
|
for (j=0 ; j<scaled_width ; j+=1)
|
|
{
|
|
if (inrow[frac>>16] < 255-vid.fullbright)
|
|
((char *) (&out[j]))[3] = 0; //alpha 0
|
|
frac += fracstep;
|
|
}
|
|
}
|
|
texnums->fullbright = texture_extension_number++;
|
|
GL_Bind(texnums->fullbright);
|
|
qglTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
|
|
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
}
|
|
else
|
|
{
|
|
skins = (galiasskin_t*)((char *)inf + inf->ofsskins);
|
|
if (e->skinnum >= 0 && e->skinnum < inf->numskins)
|
|
skins += e->skinnum;
|
|
|
|
if (!skins->texnums)
|
|
return NULL;
|
|
|
|
frame = cl.time*skins->skinspeed;
|
|
frame = frame%skins->texnums;
|
|
texnums = (galiastexnum_t*)((char *)skins + skins->ofstexnums + frame*sizeof(galiastexnum_t));
|
|
memcpy(&cm->texnum, texnums, sizeof(cm->texnum));
|
|
}
|
|
return &cm->texnum;
|
|
}
|
|
}
|
|
|
|
skins = (galiasskin_t*)((char *)inf + inf->ofsskins);
|
|
if (e->skinnum >= 0 && e->skinnum < inf->numskins)
|
|
skins += e->skinnum;
|
|
|
|
if (!skins->texnums)
|
|
return NULL;
|
|
|
|
frame = cl.time*skins->skinspeed;
|
|
frame = frame%skins->texnums;
|
|
texnums = (galiastexnum_t*)((char *)skins + skins->ofstexnums + frame*sizeof(galiastexnum_t));
|
|
|
|
return texnums;
|
|
}
|
|
|
|
|
|
static int numFacing;
|
|
static qbyte *triangleFacing;
|
|
static void R_CalcFacing(mesh_t *mesh, vec3_t lightpos)
|
|
{
|
|
float *v1, *v2, *v3;
|
|
vec3_t d1, d2, norm;
|
|
|
|
int i;
|
|
|
|
index_t *indexes = mesh->indexes;
|
|
int numtris = mesh->numindexes/3;
|
|
|
|
|
|
if (numFacing < numtris)
|
|
{
|
|
if (triangleFacing)
|
|
BZ_Free(triangleFacing);
|
|
triangleFacing = BZ_Malloc(sizeof(*triangleFacing)*numtris);
|
|
numFacing = numtris;
|
|
}
|
|
|
|
for (i = 0; i < numtris; i++, indexes+=3)
|
|
{
|
|
v1 = (float *)(mesh->xyz_array + indexes[0]);
|
|
v2 = (float *)(mesh->xyz_array + indexes[1]);
|
|
v3 = (float *)(mesh->xyz_array + indexes[2]);
|
|
|
|
VectorSubtract(v1, v2, d1);
|
|
VectorSubtract(v3, v2, d2);
|
|
CrossProduct(d1, d2, norm);
|
|
|
|
triangleFacing[i] = (( lightpos[0] - v1[0] ) * norm[0] + ( lightpos[1] - v1[1] ) * norm[1] + ( lightpos[2] - v1[2] ) * norm[2]) > 0;
|
|
}
|
|
}
|
|
|
|
#define PROJECTION_DISTANCE 30000
|
|
static int numProjectedShadowVerts;
|
|
static vec3_t *ProjectedShadowVerts;
|
|
static void R_ProjectShadowVolume(mesh_t *mesh, vec3_t lightpos)
|
|
{
|
|
int numverts = mesh->numvertexes;
|
|
int i;
|
|
vec4_t *input = mesh->xyz_array;
|
|
vec3_t *projected;
|
|
if (numProjectedShadowVerts < numverts)
|
|
{
|
|
if (ProjectedShadowVerts)
|
|
BZ_Free(ProjectedShadowVerts);
|
|
ProjectedShadowVerts = BZ_Malloc(sizeof(*ProjectedShadowVerts)*numverts);
|
|
numProjectedShadowVerts = numverts;
|
|
}
|
|
projected = ProjectedShadowVerts;
|
|
for (i = 0; i < numverts; i++)
|
|
{
|
|
projected[i][0] = input[i][0] + (input[i][0]-lightpos[0])*PROJECTION_DISTANCE;
|
|
projected[i][1] = input[i][1] + (input[i][1]-lightpos[1])*PROJECTION_DISTANCE;
|
|
projected[i][2] = input[i][2] + (input[i][2]-lightpos[2])*PROJECTION_DISTANCE;
|
|
}
|
|
}
|
|
|
|
static void R_DrawShadowVolume(mesh_t *mesh)
|
|
{
|
|
int t;
|
|
vec3_t *proj = ProjectedShadowVerts;
|
|
vec4_t *verts = mesh->xyz_array;
|
|
index_t *indexes = mesh->indexes;
|
|
int *neighbours = mesh->trneighbors;
|
|
int numtris = mesh->numindexes/3;
|
|
|
|
qglBegin(GL_TRIANGLES);
|
|
for (t = 0; t < numtris; t++)
|
|
{
|
|
if (triangleFacing[t])
|
|
{
|
|
//draw front
|
|
qglVertex3fv(verts[indexes[t*3+0]]);
|
|
qglVertex3fv(verts[indexes[t*3+1]]);
|
|
qglVertex3fv(verts[indexes[t*3+2]]);
|
|
|
|
//draw back
|
|
qglVertex3fv(proj[indexes[t*3+1]]);
|
|
qglVertex3fv(proj[indexes[t*3+0]]);
|
|
qglVertex3fv(proj[indexes[t*3+2]]);
|
|
|
|
//draw side caps
|
|
if (neighbours[t*3+0] < 0 || !triangleFacing[neighbours[t*3+0]])
|
|
{
|
|
qglVertex3fv(verts[indexes[t*3+1]]);
|
|
qglVertex3fv(verts[indexes[t*3+0]]);
|
|
qglVertex3fv(proj [indexes[t*3+0]]);
|
|
qglVertex3fv(verts[indexes[t*3+1]]);
|
|
qglVertex3fv(proj [indexes[t*3+0]]);
|
|
qglVertex3fv(proj [indexes[t*3+1]]);
|
|
}
|
|
|
|
if (neighbours[t*3+1] < 0 || !triangleFacing[neighbours[t*3+1]])
|
|
{
|
|
qglVertex3fv(verts[indexes[t*3+2]]);
|
|
qglVertex3fv(verts[indexes[t*3+1]]);
|
|
qglVertex3fv(proj [indexes[t*3+1]]);
|
|
qglVertex3fv(verts[indexes[t*3+2]]);
|
|
qglVertex3fv(proj [indexes[t*3+1]]);
|
|
qglVertex3fv(proj [indexes[t*3+2]]);
|
|
}
|
|
|
|
if (neighbours[t*3+2] < 0 || !triangleFacing[neighbours[t*3+2]])
|
|
{
|
|
qglVertex3fv(verts[indexes[t*3+0]]);
|
|
qglVertex3fv(verts[indexes[t*3+2]]);
|
|
qglVertex3fv(proj [indexes[t*3+2]]);
|
|
qglVertex3fv(verts[indexes[t*3+0]]);
|
|
qglVertex3fv(proj [indexes[t*3+2]]);
|
|
qglVertex3fv(proj [indexes[t*3+0]]);
|
|
}
|
|
}
|
|
}
|
|
qglEnd();
|
|
}
|
|
|
|
void GL_DrawAliasMesh_Sketch (mesh_t *mesh)
|
|
{
|
|
int i;
|
|
extern int gldepthfunc;
|
|
#ifdef Q3SHADERS
|
|
R_UnlockArrays();
|
|
#endif
|
|
|
|
qglDepthFunc(gldepthfunc);
|
|
qglDepthMask(1);
|
|
|
|
if (gldepthmin == 0.5)
|
|
qglCullFace ( GL_BACK );
|
|
else
|
|
qglCullFace ( GL_FRONT );
|
|
|
|
GL_TexEnv(GL_MODULATE);
|
|
|
|
qglDisable(GL_TEXTURE_2D);
|
|
|
|
qglVertexPointer(3, GL_FLOAT, 16, mesh->xyz_array);
|
|
qglEnableClientState( GL_VERTEX_ARRAY );
|
|
|
|
if (mesh->normals_array && qglNormalPointer) //d3d wrapper doesn't support normals, and this is only really needed for truform
|
|
{
|
|
qglNormalPointer(GL_FLOAT, 0, mesh->normals_array);
|
|
qglEnableClientState( GL_NORMAL_ARRAY );
|
|
}
|
|
|
|
if (mesh->colors_array)
|
|
{
|
|
qglColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh->colors_array);
|
|
qglEnableClientState( GL_COLOR_ARRAY );
|
|
}
|
|
else
|
|
qglDisableClientState( GL_COLOR_ARRAY );
|
|
|
|
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
qglTexCoordPointer(2, GL_FLOAT, 0, mesh->st_array);
|
|
|
|
qglDrawElements(GL_TRIANGLES, mesh->numindexes, GL_UNSIGNED_INT, mesh->indexes);
|
|
|
|
qglDisableClientState( GL_VERTEX_ARRAY );
|
|
qglDisableClientState( GL_COLOR_ARRAY );
|
|
qglDisableClientState( GL_NORMAL_ARRAY );
|
|
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
|
|
qglColor3f(0, 0, 0);
|
|
qglBegin(GL_LINES);
|
|
for (i = 0; i < mesh->numindexes; i+=3)
|
|
{
|
|
float *v1, *v2, *v3;
|
|
int n;
|
|
v1 = mesh->xyz_array[mesh->indexes[i+0]];
|
|
v2 = mesh->xyz_array[mesh->indexes[i+1]];
|
|
v3 = mesh->xyz_array[mesh->indexes[i+2]];
|
|
for (n = 0; n < 3; n++) //rember we do this triangle AND the neighbours
|
|
{
|
|
qglVertex3f(v1[0]+0.5*(rand()/(float)RAND_MAX-0.5),
|
|
v1[1]+0.5*(rand()/(float)RAND_MAX-0.5),
|
|
v1[2]+0.5*(rand()/(float)RAND_MAX-0.5));
|
|
qglVertex3f(v2[0]+0.5*(rand()/(float)RAND_MAX-0.5),
|
|
v2[1]+0.5*(rand()/(float)RAND_MAX-0.5),
|
|
v2[2]+0.5*(rand()/(float)RAND_MAX-0.5));
|
|
|
|
qglVertex3f(v2[0]+0.5*(rand()/(float)RAND_MAX-0.5),
|
|
v2[1]+0.5*(rand()/(float)RAND_MAX-0.5),
|
|
v2[2]+0.5*(rand()/(float)RAND_MAX-0.5));
|
|
qglVertex3f(v3[0]+0.5*(rand()/(float)RAND_MAX-0.5),
|
|
v3[1]+0.5*(rand()/(float)RAND_MAX-0.5),
|
|
v3[2]+0.5*(rand()/(float)RAND_MAX-0.5));
|
|
|
|
qglVertex3f(v3[0]+0.5*(rand()/(float)RAND_MAX-0.5),
|
|
v3[1]+0.5*(rand()/(float)RAND_MAX-0.5),
|
|
v3[2]+0.5*(rand()/(float)RAND_MAX-0.5));
|
|
qglVertex3f(v1[0]+0.5*(rand()/(float)RAND_MAX-0.5),
|
|
v1[1]+0.5*(rand()/(float)RAND_MAX-0.5),
|
|
v1[2]+0.5*(rand()/(float)RAND_MAX-0.5));
|
|
}
|
|
}
|
|
qglEnd();
|
|
|
|
#ifdef Q3SHADERS
|
|
R_IBrokeTheArrays();
|
|
#endif
|
|
}
|
|
|
|
void GL_DrawAliasMesh (mesh_t *mesh, int texnum)
|
|
{
|
|
extern int gldepthfunc;
|
|
#ifdef Q3SHADERS
|
|
R_UnlockArrays();
|
|
#endif
|
|
|
|
qglDepthFunc(gldepthfunc);
|
|
qglDepthMask(1);
|
|
|
|
GL_Bind(texnum);
|
|
if (gldepthmin == 0.5)
|
|
qglCullFace ( GL_BACK );
|
|
else
|
|
qglCullFace ( GL_FRONT );
|
|
|
|
GL_TexEnv(GL_MODULATE);
|
|
|
|
qglVertexPointer(3, GL_FLOAT, 16, mesh->xyz_array);
|
|
qglEnableClientState( GL_VERTEX_ARRAY );
|
|
|
|
if (mesh->normals_array && qglNormalPointer) //d3d wrapper doesn't support normals, and this is only really needed for truform
|
|
{
|
|
qglNormalPointer(GL_FLOAT, 0, mesh->normals_array);
|
|
qglEnableClientState( GL_NORMAL_ARRAY );
|
|
}
|
|
|
|
if (mesh->colors_array)
|
|
{
|
|
qglColorPointer(4, GL_UNSIGNED_BYTE, 0, mesh->colors_array);
|
|
qglEnableClientState( GL_COLOR_ARRAY );
|
|
}
|
|
else
|
|
qglDisableClientState( GL_COLOR_ARRAY );
|
|
|
|
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
qglTexCoordPointer(2, GL_FLOAT, 0, mesh->st_array);
|
|
|
|
qglDrawElements(GL_TRIANGLES, mesh->numindexes, GL_UNSIGNED_INT, mesh->indexes);
|
|
|
|
qglDisableClientState( GL_VERTEX_ARRAY );
|
|
qglDisableClientState( GL_COLOR_ARRAY );
|
|
qglDisableClientState( GL_NORMAL_ARRAY );
|
|
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
|
|
#ifdef Q3SHADERS
|
|
R_IBrokeTheArrays();
|
|
#endif
|
|
}
|
|
|
|
void R_DrawGAliasModel (entity_t *e)
|
|
{
|
|
extern cvar_t r_drawflat;
|
|
model_t *clmodel;
|
|
vec3_t mins, maxs;
|
|
vec3_t dist;
|
|
vec_t add;
|
|
int i;
|
|
galiasinfo_t *inf;
|
|
mesh_t mesh;
|
|
galiastexnum_t *skin;
|
|
float entScale;
|
|
vec3_t lightdir;
|
|
|
|
float tmatrix[3][4];
|
|
|
|
currententity = e;
|
|
|
|
// if (e->flags & Q2RF_VIEWERMODEL && e->keynum == cl.playernum[r_refdef.currentplayernum]+1)
|
|
// return;
|
|
|
|
{
|
|
extern int cl_playerindex;
|
|
if (e->scoreboard && e->model == cl.model_precache[cl_playerindex])
|
|
{
|
|
clmodel = e->scoreboard->model;
|
|
if (!clmodel || clmodel->type != mod_alias)
|
|
clmodel = e->model;
|
|
}
|
|
else
|
|
clmodel = e->model;
|
|
}
|
|
|
|
VectorAdd (e->origin, clmodel->mins, mins);
|
|
VectorAdd (e->origin, clmodel->maxs, maxs);
|
|
|
|
// if (!(e->flags & Q2RF_WEAPONMODEL))
|
|
// if (R_CullBox (mins, maxs))
|
|
// return;
|
|
|
|
if (!(r_refdef.flags & 1)) //RDF_NOWORLDMODEL
|
|
{
|
|
cl.worldmodel->funcs.LightPointValues(e->origin, shadelight, ambientlight, lightdir);
|
|
}
|
|
else
|
|
{
|
|
ambientlight[0] = ambientlight[1] = ambientlight[2] = shadelight[0] = shadelight[1] = shadelight[2] = 255;
|
|
lightdir[0] = 0;
|
|
lightdir[1] = 1;
|
|
lightdir[2] = 1;
|
|
}
|
|
|
|
if (!r_vertexdlights.value)
|
|
{
|
|
for (i=0 ; i<MAX_DLIGHTS ; i++)
|
|
{
|
|
if (cl_dlights[i].radius)
|
|
{
|
|
VectorSubtract (e->origin,
|
|
cl_dlights[i].origin,
|
|
dist);
|
|
add = cl_dlights[i].radius - Length(dist);
|
|
|
|
if (add > 0) {
|
|
add*=5;
|
|
ambientlight[0] += add * cl_dlights[i].color[0];
|
|
ambientlight[1] += add * cl_dlights[i].color[1];
|
|
ambientlight[2] += add * cl_dlights[i].color[2];
|
|
//ZOID models should be affected by dlights as well
|
|
shadelight[0] += add * cl_dlights[i].color[0];
|
|
shadelight[1] += add * cl_dlights[i].color[1];
|
|
shadelight[2] += add * cl_dlights[i].color[2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) //clamp light so it doesn't get vulgar.
|
|
{
|
|
if (ambientlight[i] > 128)
|
|
ambientlight[i] = 128;
|
|
if (ambientlight[i] + shadelight[i] > 192)
|
|
shadelight[i] = 192 - ambientlight[i];
|
|
}
|
|
|
|
if (e->flags & Q2RF_WEAPONMODEL)
|
|
{
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
if (ambientlight[i] < 24)
|
|
ambientlight[i] = shadelight[i] = 24;
|
|
}
|
|
}
|
|
|
|
//MORE HUGE HACKS! WHEN WILL THEY CEASE!
|
|
// clamp lighting so it doesn't overbright as much
|
|
// ZOID: never allow players to go totally black
|
|
if (!strcmp(clmodel->name, "progs/player.mdl"))
|
|
{
|
|
float fb = r_fullbrightSkins.value;
|
|
if (fb > cls.allow_fbskins)
|
|
fb = cls.allow_fbskins;
|
|
if (fb < 0)
|
|
fb = 0;
|
|
if (fb)
|
|
{
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
ambientlight[i] = max(ambientlight[i], 8 + fb * 120);
|
|
shadelight[i] = max(shadelight[i], 8 + fb * 120);
|
|
}
|
|
}
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
if (ambientlight[i] < 8)
|
|
ambientlight[i] = shadelight[i] = 8;
|
|
}
|
|
|
|
}
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
if (ambientlight[i] > 128)
|
|
ambientlight[i] = 128;
|
|
|
|
shadelight[i] /= 200.0/255;
|
|
ambientlight[i] /= 200.0/255;
|
|
}
|
|
|
|
if ((e->drawflags & MLS_MASKIN) == MLS_ABSLIGHT)
|
|
{
|
|
shadelight[0] = shadelight[1] = shadelight[2] = e->abslight;
|
|
ambientlight[0] = ambientlight[1] = ambientlight[2] = 0;
|
|
}
|
|
|
|
//#define SHOWLIGHTDIR
|
|
{ //lightdir is absolute, shadevector is relative
|
|
shadevector[0] = DotProduct(lightdir, e->axis[0]);
|
|
shadevector[1] = DotProduct(lightdir, e->axis[1]);
|
|
shadevector[2] = DotProduct(lightdir, e->axis[2]);
|
|
VectorNormalize(shadevector);
|
|
|
|
VectorCopy(shadevector, mesh.lightaxis[2]);
|
|
VectorVectors(mesh.lightaxis[2], mesh.lightaxis[1], mesh.lightaxis[0]);
|
|
VectorInverse(mesh.lightaxis[1]);
|
|
}
|
|
/*
|
|
an = e->angles[1]/180*M_PI;
|
|
shadevector[0] = cos(-an);
|
|
shadevector[1] = sin(-an);
|
|
shadevector[2] = 1;
|
|
VectorNormalize (shadevector);
|
|
*/
|
|
|
|
GL_DisableMultitexture();
|
|
GL_TexEnv(GL_MODULATE);
|
|
if (gl_smoothmodels.value)
|
|
qglShadeModel (GL_SMOOTH);
|
|
if (gl_affinemodels.value)
|
|
qglHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
|
|
|
|
qglDisable (GL_ALPHA_TEST);
|
|
|
|
if (e->flags & Q2RF_DEPTHHACK)
|
|
qglDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin));
|
|
|
|
// glColor3f( 1,1,1);
|
|
if ((e->model->flags & EF_SPECIAL_TRANS)) //hexen2 flags.
|
|
{
|
|
qglEnable (GL_BLEND);
|
|
qglBlendFunc (GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
|
|
// glColor3f( 1,1,1);
|
|
qglDisable( GL_CULL_FACE );
|
|
}
|
|
else if (e->drawflags & DRF_TRANSLUCENT)
|
|
{
|
|
qglEnable (GL_BLEND);
|
|
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
e->alpha = r_wateralpha.value;
|
|
// qglColor4f( 1,1,1,r_wateralpha.value);
|
|
}
|
|
else if ((e->model->flags & EF_TRANSPARENT))
|
|
{
|
|
qglEnable (GL_BLEND);
|
|
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
// qglColor3f( 1,1,1);
|
|
}
|
|
else if ((e->model->flags & EF_HOLEY))
|
|
{
|
|
qglEnable (GL_ALPHA_TEST);
|
|
// qglEnable (GL_BLEND);
|
|
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
// qglColor3f( 1,1,1);
|
|
}
|
|
else if (e->alpha < 1)
|
|
{
|
|
qglEnable(GL_BLEND);
|
|
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
else
|
|
{
|
|
qglDisable(GL_BLEND);
|
|
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
// qglEnable (GL_ALPHA_TEST);
|
|
|
|
qglPushMatrix();
|
|
R_RotateForEntity(e);
|
|
|
|
if (e->scale != 1 && e->scale != 0) //hexen 2 stuff
|
|
{
|
|
vec3_t scale;
|
|
vec3_t scale_origin;
|
|
float xyfact, zfact;
|
|
scale[0] = (clmodel->maxs[0]-clmodel->mins[0])/255;
|
|
scale[1] = (clmodel->maxs[1]-clmodel->mins[1])/255;
|
|
scale[2] = (clmodel->maxs[2]-clmodel->mins[2])/255;
|
|
scale_origin[0] = clmodel->mins[0];
|
|
scale_origin[1] = clmodel->mins[1];
|
|
scale_origin[2] = clmodel->mins[2];
|
|
|
|
/* qglScalef( 1/scale[0],
|
|
1/scale[1],
|
|
1/scale[2]);
|
|
qglTranslatef ( -scale_origin[0],
|
|
-scale_origin[1],
|
|
-scale_origin[2]);
|
|
*/
|
|
|
|
if(e->scale != 0 && e->scale != 1)
|
|
{
|
|
entScale = (float)e->scale;
|
|
switch(e->drawflags&SCALE_TYPE_MASKIN)
|
|
{
|
|
default:
|
|
case SCALE_TYPE_UNIFORM:
|
|
tmatrix[0][0] = scale[0]*entScale;
|
|
tmatrix[1][1] = scale[1]*entScale;
|
|
tmatrix[2][2] = scale[2]*entScale;
|
|
xyfact = zfact = (entScale-1.0)*127.95;
|
|
break;
|
|
case SCALE_TYPE_XYONLY:
|
|
tmatrix[0][0] = scale[0]*entScale;
|
|
tmatrix[1][1] = scale[1]*entScale;
|
|
tmatrix[2][2] = scale[2];
|
|
xyfact = (entScale-1.0)*127.95;
|
|
zfact = 1.0;
|
|
break;
|
|
case SCALE_TYPE_ZONLY:
|
|
tmatrix[0][0] = scale[0];
|
|
tmatrix[1][1] = scale[1];
|
|
tmatrix[2][2] = scale[2]*entScale;
|
|
xyfact = 1.0;
|
|
zfact = (entScale-1.0)*127.95;
|
|
break;
|
|
}
|
|
switch(currententity->drawflags&SCALE_ORIGIN_MASKIN)
|
|
{
|
|
default:
|
|
case SCALE_ORIGIN_CENTER:
|
|
tmatrix[0][3] = scale_origin[0]-scale[0]*xyfact;
|
|
tmatrix[1][3] = scale_origin[1]-scale[1]*xyfact;
|
|
tmatrix[2][3] = scale_origin[2]-scale[2]*zfact;
|
|
break;
|
|
case SCALE_ORIGIN_BOTTOM:
|
|
tmatrix[0][3] = scale_origin[0]-scale[0]*xyfact;
|
|
tmatrix[1][3] = scale_origin[1]-scale[1]*xyfact;
|
|
tmatrix[2][3] = scale_origin[2];
|
|
break;
|
|
case SCALE_ORIGIN_TOP:
|
|
tmatrix[0][3] = scale_origin[0]-scale[0]*xyfact;
|
|
tmatrix[1][3] = scale_origin[1]-scale[1]*xyfact;
|
|
tmatrix[2][3] = scale_origin[2]-scale[2]*zfact*2.0;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tmatrix[0][0] = scale[0];
|
|
tmatrix[1][1] = scale[1];
|
|
tmatrix[2][2] = scale[2];
|
|
tmatrix[0][3] = scale_origin[0];
|
|
tmatrix[1][3] = scale_origin[1];
|
|
tmatrix[2][3] = scale_origin[2];
|
|
}
|
|
|
|
/* if(clmodel->flags&EF_ROTATE)
|
|
{ // Floating motion
|
|
tmatrix[2][3] += sin(currententity->origin[0]
|
|
+currententity->origin[1]+(cl.time*3))*5.5;
|
|
}*/
|
|
|
|
qglTranslatef (tmatrix[0][3],tmatrix[1][3],tmatrix[2][3]);
|
|
qglScalef (tmatrix[0][0],tmatrix[1][1],tmatrix[2][2]);
|
|
|
|
qglScalef( 1/scale[0],
|
|
1/scale[1],
|
|
1/scale[2]);
|
|
qglTranslatef ( -scale_origin[0],
|
|
-scale_origin[1],
|
|
-scale_origin[2]);
|
|
}
|
|
|
|
inf = GLMod_Extradata (clmodel);
|
|
if (qglPNTrianglesfATI && gl_ati_truform.value)
|
|
qglEnable(GL_PN_TRIANGLES_ATI);
|
|
|
|
memset(&mesh, 0, sizeof(mesh));
|
|
for(; inf; ((inf->nextsurf)?(inf = (galiasinfo_t*)((char *)inf + inf->nextsurf)):(inf=NULL)))
|
|
{
|
|
if (R_GAliasBuildMesh(&mesh, inf, e->frame, e->oldframe, e->lerptime, e->alpha) && r_vertexdlights.value)
|
|
R_GAliasAddDlights(&mesh, e->origin, e->angles);
|
|
|
|
c_alias_polys += mesh.numindexes/3;
|
|
|
|
if (r_drawflat.value == 2)
|
|
{
|
|
GL_DrawAliasMesh_Sketch(&mesh);
|
|
continue;
|
|
}
|
|
#ifdef Q3SHADERS
|
|
else if (currententity->forcedshader)
|
|
{
|
|
meshbuffer_t mb;
|
|
|
|
R_IBrokeTheArrays();
|
|
|
|
mb.entity = &r_worldentity;
|
|
mb.shader = currententity->forcedshader;
|
|
mb.fog = NULL;
|
|
mb.mesh = &mesh;
|
|
mb.infokey = currententity->keynum;
|
|
mb.dlightbits = 0;
|
|
|
|
R_PushMesh(&mesh, mb.shader->features | MF_NONBATCHED | MF_COLORS);
|
|
|
|
R_RenderMeshBuffer ( &mb, false );
|
|
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
skin = GL_ChooseSkin(inf, clmodel->name, e);
|
|
|
|
if (!skin)
|
|
{
|
|
qglEnable(GL_TEXTURE_2D);
|
|
GL_DrawAliasMesh_Sketch(&mesh);
|
|
}
|
|
#ifdef Q3SHADERS
|
|
else if (skin->shader)
|
|
{
|
|
meshbuffer_t mb;
|
|
|
|
mb.entity = &r_worldentity;
|
|
mb.shader = skin->shader;
|
|
mb.fog = NULL;
|
|
mb.mesh = &mesh;
|
|
mb.infokey = currententity->keynum;
|
|
mb.dlightbits = 0;
|
|
|
|
R_IBrokeTheArrays();
|
|
|
|
R_PushMesh(&mesh, skin->shader->features | MF_NONBATCHED | MF_COLORS);
|
|
|
|
R_RenderMeshBuffer ( &mb, false );
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
qglEnable(GL_TEXTURE_2D);
|
|
// if (skin->bump)
|
|
// GL_DrawMeshBump(&mesh, skin->base, 0, skin->bump, 0);
|
|
// else
|
|
GL_DrawAliasMesh(&mesh, skin->base);
|
|
|
|
if (skin->fullbright && r_fb_models.value && cls.allow_luma)
|
|
{
|
|
mesh.colors_array = NULL;
|
|
qglEnable(GL_BLEND);
|
|
qglColor4f(1, 1, 1, e->alpha*r_fb_models.value);
|
|
c_alias_polys += mesh.numindexes/3;
|
|
GL_DrawAliasMesh(&mesh, skin->fullbright);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (qglPNTrianglesfATI && gl_ati_truform.value)
|
|
qglDisable(GL_PN_TRIANGLES_ATI);
|
|
|
|
#ifdef SHOWLIGHTDIR //testing
|
|
qglDisable(GL_TEXTURE_2D);
|
|
qglBegin(GL_LINES);
|
|
qglColor3f(1,0,0);
|
|
qglVertex3f( 0,
|
|
0,
|
|
0);
|
|
qglVertex3f( 100*mesh.lightaxis[0][0],
|
|
100*mesh.lightaxis[0][1],
|
|
100*mesh.lightaxis[0][2]);
|
|
|
|
qglColor3f(0,1,0);
|
|
qglVertex3f( 0,
|
|
0,
|
|
0);
|
|
qglVertex3f( 100*mesh.lightaxis[1][0],
|
|
100*mesh.lightaxis[1][1],
|
|
100*mesh.lightaxis[1][2]);
|
|
|
|
qglColor3f(0,0,1);
|
|
qglVertex3f( 0,
|
|
0,
|
|
0);
|
|
qglVertex3f( 100*mesh.lightaxis[2][0],
|
|
100*mesh.lightaxis[2][1],
|
|
100*mesh.lightaxis[2][2]);
|
|
qglEnd();
|
|
qglEnable(GL_TEXTURE_2D);
|
|
#endif
|
|
|
|
qglPopMatrix();
|
|
|
|
qglDisable(GL_BLEND);
|
|
|
|
qglBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
GL_TexEnv(GL_REPLACE);
|
|
|
|
qglEnable(GL_TEXTURE_2D);
|
|
|
|
qglShadeModel (GL_FLAT);
|
|
if (gl_affinemodels.value)
|
|
qglHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
|
|
|
if (e->flags & Q2RF_DEPTHHACK)
|
|
qglDepthRange (gldepthmin, gldepthmax);
|
|
|
|
if ((currententity->model->flags & EF_SPECIAL_TRANS) && gl_cull.value)
|
|
qglEnable( GL_CULL_FACE );
|
|
if ((currententity->model->flags & EF_HOLEY))
|
|
qglDisable( GL_ALPHA_TEST );
|
|
|
|
#ifdef SHOWLIGHTDIR //testing
|
|
qglDisable(GL_TEXTURE_2D);
|
|
qglColor3f(1,1,1);
|
|
qglBegin(GL_LINES);
|
|
qglVertex3f( currententity->origin[0],
|
|
currententity->origin[1],
|
|
currententity->origin[2]);
|
|
qglVertex3f( currententity->origin[0]+100*lightdir[0],
|
|
currententity->origin[1]+100*lightdir[1],
|
|
currententity->origin[2]+100*lightdir[2]);
|
|
qglEnd();
|
|
qglEnable(GL_TEXTURE_2D);
|
|
#endif
|
|
}
|
|
|
|
//returns result in the form of the result vector
|
|
void RotateLightVector(vec3_t angles, vec3_t origin, vec3_t lightpoint, vec3_t result)
|
|
{
|
|
vec3_t f, r, u, offs;
|
|
|
|
angles[0]*=-1;
|
|
AngleVectors(angles, f, r, u);
|
|
angles[0]*=-1;
|
|
|
|
offs[0] = lightpoint[0] - origin[0];
|
|
offs[1] = lightpoint[1] - origin[1];
|
|
offs[2] = lightpoint[2] - origin[2];
|
|
|
|
result[0] = DotProduct (offs, f);
|
|
result[1] = -DotProduct (offs, r);
|
|
result[2] = DotProduct (offs, u);
|
|
}
|
|
|
|
void GL_LightMesh (mesh_t *mesh, vec3_t lightpos, vec3_t colours, float radius)
|
|
{
|
|
vec3_t dir;
|
|
int i;
|
|
float dot;
|
|
vec4_t *xyz = mesh->xyz_array;
|
|
vec3_t *normals = mesh->normals_array;
|
|
byte_vec4_t *out = mesh->colors_array;
|
|
|
|
if (normals)
|
|
{
|
|
for (i = 0; i < mesh->numvertexes; i++)
|
|
{
|
|
VectorSubtract(lightpos, xyz[i], dir);
|
|
dot = DotProduct(dir, normals[i]);
|
|
out[i][0] = colours[0]*dot;
|
|
out[i][1] = colours[1]*dot;
|
|
out[i][2] = colours[2]*dot;
|
|
out[i][3] = 255;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < mesh->numvertexes; i++)
|
|
{
|
|
VectorSubtract(lightpos, xyz[i], dir);
|
|
out[i][0] = colours[0];
|
|
out[i][1] = colours[1];
|
|
out[i][2] = colours[2];
|
|
out[i][3] = 255;
|
|
}
|
|
}
|
|
}
|
|
|
|
void R_DrawGAliasModelLighting (entity_t *e, vec3_t lightpos, vec3_t colours, float radius)
|
|
{
|
|
return; //not ready yet
|
|
#if 0
|
|
|
|
model_t *clmodel = e->model;
|
|
vec3_t mins, maxs;
|
|
vec3_t lightdir;
|
|
galiasinfo_t *inf;
|
|
mesh_t mesh;
|
|
|
|
if (e->flags & Q2RF_VIEWERMODEL)
|
|
return;
|
|
|
|
//Total insanity with r_shadows 2...
|
|
// if (!strcmp (clmodel->name, "progs/flame2.mdl"))
|
|
// CL_NewDlight (e, e->origin[0]-1, e->origin[1]+1, e->origin[2]+24, 200 + (rand()&31), host_frametime*2, 3);
|
|
|
|
// if (!strcmp (clmodel->name, "progs/armor.mdl"))
|
|
// CL_NewDlight (e->keynum, e->origin[0]-1, e->origin[1]+1, e->origin[2]+25, 200 + (rand()&31), host_frametime*2, 3);
|
|
|
|
VectorAdd (e->origin, clmodel->mins, mins);
|
|
VectorAdd (e->origin, clmodel->maxs, maxs);
|
|
|
|
if (!(e->flags & Q2RF_WEAPONMODEL))
|
|
if (R_CullBox (mins, maxs))
|
|
return;
|
|
|
|
RotateLightVector(e->angles, e->origin, lightpos, lightdir);
|
|
|
|
|
|
GL_DisableMultitexture();
|
|
GL_TexEnv(GL_MODULATE);
|
|
if (gl_smoothmodels.value)
|
|
glShadeModel (GL_SMOOTH);
|
|
if (gl_affinemodels.value)
|
|
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
|
|
|
|
|
|
if (e->flags & Q2RF_DEPTHHACK)
|
|
glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin));
|
|
|
|
glColor3f(colours[0], colours[1], colours[2]);
|
|
|
|
glPushMatrix();
|
|
R_RotateForEntity(e);
|
|
inf = GLMod_Extradata (clmodel);
|
|
if (gl_ati_truform.value)
|
|
glEnable(GL_PN_TRIANGLES_ATI);
|
|
while(inf)
|
|
{
|
|
R_GAliasBuildMesh(&mesh, inf, e->frame, e->oldframe, e->lerptime, e->alpha);
|
|
|
|
GL_LightMesh(&mesh, lightdir, colours, radius);
|
|
|
|
#ifdef Q3SHADERS
|
|
GL_DrawAliasMesh(&mesh, 0);
|
|
#else
|
|
GL_DrawMesh(&mesh, NULL, 0, 0);
|
|
#endif
|
|
|
|
if (inf->nextsurf)
|
|
inf = (galiasinfo_t*)((char *)inf + inf->nextsurf);
|
|
else
|
|
inf = NULL;
|
|
}
|
|
glPopMatrix();
|
|
if (gl_ati_truform.value)
|
|
glDisable(GL_PN_TRIANGLES_ATI);
|
|
|
|
GL_TexEnv(GL_REPLACE);
|
|
|
|
glShadeModel (GL_FLAT);
|
|
if (gl_affinemodels.value)
|
|
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
|
|
|
if (e->flags & Q2RF_DEPTHHACK)
|
|
glDepthRange (gldepthmin, gldepthmax);
|
|
#endif
|
|
}
|
|
|
|
//FIXME: Be less agressive.
|
|
//This function will have to be called twice (for geforce cards), with the same data, so do the building once and rendering twice.
|
|
void R_DrawGAliasShadowVolume(entity_t *e, vec3_t lightpos, float radius)
|
|
{
|
|
model_t *clmodel = e->model;
|
|
galiasinfo_t *inf;
|
|
mesh_t mesh;
|
|
vec3_t lightorg;
|
|
|
|
if (!strcmp (clmodel->name, "progs/flame2.mdl"))
|
|
return;
|
|
if (!strncmp (clmodel->name, "progs/bolt", 10))
|
|
return;
|
|
if (r_noaliasshadows.value)
|
|
return;
|
|
|
|
RotateLightVector(e->angles, e->origin, lightpos, lightorg);
|
|
|
|
if (Length(lightorg) > radius + clmodel->radius)
|
|
return;
|
|
|
|
qglPushMatrix();
|
|
R_RotateForEntity(e);
|
|
|
|
|
|
inf = GLMod_Extradata (clmodel);
|
|
while(inf)
|
|
{
|
|
if (inf->ofs_trineighbours)
|
|
{
|
|
R_GAliasBuildMesh(&mesh, inf, e->frame, e->oldframe, e->lerptime, e->alpha);
|
|
R_CalcFacing(&mesh, lightorg);
|
|
R_ProjectShadowVolume(&mesh, lightorg);
|
|
R_DrawShadowVolume(&mesh);
|
|
}
|
|
|
|
if (inf->nextsurf)
|
|
inf = (galiasinfo_t*)((char *)inf + inf->nextsurf);
|
|
else
|
|
inf = NULL;
|
|
}
|
|
|
|
qglPopMatrix();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int R_FindTriangleWithEdge ( int *indexes, int numtris, int start, int end, int ignore)
|
|
{
|
|
int i;
|
|
int match, count;
|
|
|
|
count = 0;
|
|
match = -1;
|
|
|
|
for (i = 0; i < numtris; i++, indexes += 3)
|
|
{
|
|
if ( (indexes[0] == start && indexes[1] == end)
|
|
|| (indexes[1] == start && indexes[2] == end)
|
|
|| (indexes[2] == start && indexes[0] == end) ) {
|
|
if (i != ignore)
|
|
match = i;
|
|
count++;
|
|
} else if ( (indexes[1] == start && indexes[0] == end)
|
|
|| (indexes[2] == start && indexes[1] == end)
|
|
|| (indexes[0] == start && indexes[2] == end) ) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
// detect edges shared by three triangles and make them seams
|
|
if (count > 2)
|
|
match = -1;
|
|
|
|
return match;
|
|
}
|
|
|
|
static void R_BuildTriangleNeighbours ( int *neighbours, int *indexes, int numtris )
|
|
{
|
|
int i, *n;
|
|
int *index;
|
|
|
|
for (i = 0, index = indexes, n = neighbours; i < numtris; i++, index += 3, n += 3)
|
|
{
|
|
n[0] = R_FindTriangleWithEdge (indexes, numtris, index[1], index[0], i);
|
|
n[1] = R_FindTriangleWithEdge (indexes, numtris, index[2], index[1], i);
|
|
n[2] = R_FindTriangleWithEdge (indexes, numtris, index[0], index[2], i);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void GL_GenerateNormals(float *orgs, float *normals, int *indicies, int numtris, int numverts)
|
|
{
|
|
vec3_t d1, d2;
|
|
vec3_t norm;
|
|
int t, i, v1, v2, v3;
|
|
int tricounts[MD2MAX_VERTS];
|
|
vec3_t combined[MD2MAX_VERTS];
|
|
int triremap[MD2MAX_VERTS];
|
|
if (numverts > MD2MAX_VERTS)
|
|
return; //not an issue, you just loose the normals.
|
|
|
|
memset(triremap, 0, numverts*sizeof(triremap[0]));
|
|
|
|
v2=0;
|
|
for (i = 0; i < numverts; i++) //weld points
|
|
{
|
|
for (v1 = 0; v1 < v2; v1++)
|
|
{
|
|
if (orgs[i*3+0] == combined[v1][0] &&
|
|
orgs[i*3+1] == combined[v1][1] &&
|
|
orgs[i*3+2] == combined[v1][2])
|
|
{
|
|
triremap[i] = v1;
|
|
break;
|
|
}
|
|
}
|
|
if (v1 == v2)
|
|
{
|
|
combined[v1][0] = orgs[i*3+0];
|
|
combined[v1][1] = orgs[i*3+1];
|
|
combined[v1][2] = orgs[i*3+2];
|
|
v2++;
|
|
|
|
triremap[i] = v1;
|
|
}
|
|
}
|
|
memset(tricounts, 0, v2*sizeof(tricounts[0]));
|
|
memset(combined, 0, v2*sizeof(*combined));
|
|
|
|
for (t = 0; t < numtris; t++)
|
|
{
|
|
v1 = triremap[indicies[t*3]];
|
|
v2 = triremap[indicies[t*3+1]];
|
|
v3 = triremap[indicies[t*3+2]];
|
|
|
|
VectorSubtract((orgs+v2*3), (orgs+v1*3), d1);
|
|
VectorSubtract((orgs+v3*3), (orgs+v1*3), d2);
|
|
CrossProduct(d1, d2, norm);
|
|
VectorNormalize(norm);
|
|
|
|
VectorAdd(norm, combined[v1], combined[v1]);
|
|
VectorAdd(norm, combined[v2], combined[v2]);
|
|
VectorAdd(norm, combined[v3], combined[v3]);
|
|
|
|
tricounts[v1]++;
|
|
tricounts[v2]++;
|
|
tricounts[v3]++;
|
|
}
|
|
|
|
for (i = 0; i < numverts; i++)
|
|
{
|
|
if (tricounts[triremap[i]])
|
|
{
|
|
VectorScale(combined[triremap[i]], 1.0f/tricounts[triremap[i]], normals+i*3);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//Q1 model loading
|
|
#if 1
|
|
static galiasinfo_t *galias;
|
|
static model_t *loadmodel;
|
|
static dmdl_t *pq1inmodel;
|
|
#define NUMVERTEXNORMALS 162
|
|
extern float r_avertexnormals[NUMVERTEXNORMALS][3];
|
|
static void *Q1_LoadFrameGroup (daliasframetype_t *pframetype, int *seamremaps)
|
|
{
|
|
galiaspose_t *pose;
|
|
galiasgroup_t *frame;
|
|
dtrivertx_t *pinframe;
|
|
int i, j, k;
|
|
daliasgroup_t *ingroup;
|
|
daliasinterval_t *intervals;
|
|
|
|
vec3_t *normals;
|
|
vec3_t *verts;
|
|
|
|
frame = (galiasgroup_t*)((char *)galias + galias->groupofs);
|
|
|
|
for (i = 0; i < pq1inmodel->numframes; i++)
|
|
{
|
|
switch(LittleLong(pframetype->type))
|
|
{
|
|
case ALIAS_SINGLE:
|
|
pinframe = (dtrivertx_t*)((char *)(pframetype+1)+sizeof(daliasframe_t));
|
|
pose = (galiaspose_t *)Hunk_Alloc(sizeof(galiaspose_t) + sizeof(vec3_t)*2*galias->numverts);
|
|
frame->poseofs = (char *)pose - (char *)frame;
|
|
frame->numposes = 1;
|
|
galias->groups++;
|
|
|
|
verts = (vec3_t *)(pose+1);
|
|
normals = &verts[galias->numverts];
|
|
pose->ofsverts = (char *)verts - (char *)pose;
|
|
pose->ofsnormals = (char *)normals - (char *)pose;
|
|
|
|
for (j = 0; j < pq1inmodel->numverts; j++)
|
|
{
|
|
verts[j][0] = pinframe[j].v[0]*pq1inmodel->scale[0]+pq1inmodel->scale_origin[0];
|
|
verts[j][1] = pinframe[j].v[1]*pq1inmodel->scale[1]+pq1inmodel->scale_origin[1];
|
|
verts[j][2] = pinframe[j].v[2]*pq1inmodel->scale[2]+pq1inmodel->scale_origin[2];
|
|
|
|
VectorCopy(r_avertexnormals[pinframe[j].lightnormalindex], normals[j]);
|
|
|
|
if (seamremaps[j] != j)
|
|
{
|
|
VectorCopy(verts[j], verts[seamremaps[j]]);
|
|
VectorCopy(normals[j], normals[seamremaps[j]]);
|
|
}
|
|
}
|
|
|
|
// GL_GenerateNormals((float*)verts, (float*)normals, (int *)((char *)galias + galias->ofs_indexes), galias->numindexes/3, galias->numverts);
|
|
|
|
pframetype = (daliasframetype_t *)&pinframe[pq1inmodel->numverts];
|
|
break;
|
|
case ALIAS_GROUP:
|
|
ingroup = (daliasgroup_t *)(pframetype+1);
|
|
|
|
pose = (galiaspose_t *)Hunk_Alloc(ingroup->numframes*(sizeof(galiaspose_t) + sizeof(vec3_t)*2*galias->numverts));
|
|
frame->poseofs = (char *)pose - (char *)frame;
|
|
frame->numposes = LittleLong(ingroup->numframes);
|
|
galias->groups++;
|
|
|
|
verts = (vec3_t *)(pose+frame->numposes);
|
|
normals = &verts[galias->numverts];
|
|
|
|
intervals = (daliasinterval_t *)(ingroup+1);
|
|
frame->rate = 1/LittleFloat(intervals->interval);
|
|
|
|
pinframe = (dtrivertx_t *)(intervals+frame->numposes);
|
|
for (k = 0; k < frame->numposes; k++)
|
|
{
|
|
pose->ofsverts = (char *)verts - (char *)pose;
|
|
pose->ofsnormals = (char *)normals - (char *)pose;
|
|
|
|
pinframe = (dtrivertx_t *)((char *)pinframe + sizeof(daliasframe_t));
|
|
for (j = 0; j < pq1inmodel->numverts; j++)
|
|
{
|
|
verts[j][0] = pinframe[j].v[0]*pq1inmodel->scale[0]+pq1inmodel->scale_origin[0];
|
|
verts[j][1] = pinframe[j].v[1]*pq1inmodel->scale[1]+pq1inmodel->scale_origin[1];
|
|
verts[j][2] = pinframe[j].v[2]*pq1inmodel->scale[2]+pq1inmodel->scale_origin[2];
|
|
|
|
VectorCopy(r_avertexnormals[pinframe[j].lightnormalindex], normals[j]);
|
|
if (seamremaps[j] != j)
|
|
{
|
|
VectorCopy(verts[j], verts[seamremaps[j]]);
|
|
VectorCopy(normals[j], normals[seamremaps[j]]);
|
|
}
|
|
}
|
|
verts = &normals[galias->numverts];
|
|
normals = &verts[galias->numverts];
|
|
pose++;
|
|
|
|
pinframe += pq1inmodel->numverts;
|
|
}
|
|
|
|
// GL_GenerateNormals((float*)verts, (float*)normals, (int *)((char *)galias + galias->ofs_indexes), galias->numindexes/3, galias->numverts);
|
|
|
|
pframetype = (daliasframetype_t *)pinframe;
|
|
break;
|
|
default:
|
|
Sys_Error("Bad frame type\n");
|
|
}
|
|
frame++;
|
|
}
|
|
return pframetype;
|
|
}
|
|
|
|
static void *Q1_LoadSkins (daliasskintype_t *pskintype, qboolean alpha)
|
|
{
|
|
extern int gl_bumpmappingpossible;
|
|
galiastexnum_t *texnums;
|
|
char skinname[MAX_QPATH];
|
|
int i;
|
|
int s, t;
|
|
int *count;
|
|
float *intervals;
|
|
qbyte *data, *saved;
|
|
galiasskin_t *outskin = (galiasskin_t *)((char *)galias + galias->ofsskins);
|
|
|
|
int texture;
|
|
int fbtexture;
|
|
|
|
s = pq1inmodel->skinwidth*pq1inmodel->skinheight;
|
|
for (i = 0; i < pq1inmodel->numskins; i++)
|
|
{
|
|
switch(LittleLong(pskintype->type))
|
|
{
|
|
case ALIAS_SKIN_SINGLE:
|
|
outskin->skinwidth = pq1inmodel->skinwidth;
|
|
outskin->skinheight = pq1inmodel->skinheight;
|
|
|
|
sprintf(skinname, "%s_%i", loadname, i);
|
|
texture = Mod_LoadReplacementTexture(skinname, true, false, true);
|
|
if (!texture)
|
|
{
|
|
sprintf(skinname, "textures/models/%s_%i", loadname, i);
|
|
texture = Mod_LoadReplacementTexture(skinname, true, false, true);
|
|
if (texture && r_fb_models.value)
|
|
{
|
|
sprintf(skinname, "textures/models/%s_%i_luma", loadname, i);
|
|
fbtexture = Mod_LoadReplacementTexture(skinname, true, true, true);
|
|
}
|
|
else
|
|
fbtexture = 0;
|
|
}
|
|
else if (texture && r_fb_models.value)
|
|
{
|
|
sprintf(skinname, "%s_%i_luma", loadname, i);
|
|
fbtexture = Mod_LoadReplacementTexture(skinname, true, true, true);
|
|
}
|
|
else
|
|
fbtexture = 0;
|
|
|
|
if (!texture)
|
|
{
|
|
texnums = Hunk_Alloc(sizeof(*texnums)+s);
|
|
saved = (qbyte*)(texnums+1);
|
|
outskin->ofstexels = (qbyte *)(saved) - (qbyte *)outskin;
|
|
memcpy(saved, pskintype+1, s);
|
|
GLMod_FloodFillSkin(saved, outskin->skinwidth, outskin->skinheight);
|
|
sprintf(skinname, "%s_%i", loadname, i);
|
|
texture = GL_LoadTexture(skinname, outskin->skinwidth, outskin->skinheight, saved, true, alpha);
|
|
if (r_fb_models.value)
|
|
{
|
|
sprintf(skinname, "%s_%i_luma", loadname, i);
|
|
fbtexture = GL_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, true, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
texnums = Hunk_Alloc(sizeof(*texnums));
|
|
outskin->ofstexels = 0;
|
|
}
|
|
outskin->texnums=1;
|
|
|
|
outskin->ofstexnums = (char *)texnums - (char *)outskin;
|
|
|
|
texnums->base = texture;
|
|
texnums->fullbright = fbtexture;
|
|
|
|
pskintype = (daliasskintype_t *)((char *)(pskintype+1)+s);
|
|
break;
|
|
|
|
default:
|
|
outskin->skinwidth = pq1inmodel->skinwidth;
|
|
outskin->skinheight = pq1inmodel->skinheight;
|
|
count = (int *)(pskintype+1);
|
|
intervals = (float *)(count+1);
|
|
outskin->texnums = LittleLong(*count);
|
|
data = (qbyte *)(intervals + outskin->texnums);
|
|
texnums = Hunk_Alloc(sizeof(*texnums)*outskin->texnums);
|
|
outskin->ofstexnums = (char *)texnums - (char *)outskin;
|
|
outskin->ofstexels = 0;
|
|
for (t = 0; t < outskin->texnums; t++,data+=s, texnums++)
|
|
{
|
|
sprintf(skinname, "%s_%i%c", loadname, i, t+'a');
|
|
texture = Mod_LoadReplacementTexture(skinname, true, false, true);
|
|
if (texture)
|
|
{
|
|
texnums->base = texture;
|
|
if (r_fb_models.value)
|
|
{
|
|
sprintf(skinname, "%s_%i%c_luma", loadname, i, t+'a');
|
|
texnums->fullbright = Mod_LoadReplacementTexture(skinname, true, true, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (t == 0)
|
|
{
|
|
saved = Hunk_Alloc(s);
|
|
outskin->ofstexels = (qbyte *)(saved) - (qbyte *)outskin;
|
|
}
|
|
else
|
|
saved = BZ_Malloc(s);
|
|
memcpy(saved, pskintype+1, s);
|
|
GLMod_FloodFillSkin(saved, outskin->skinwidth, outskin->skinheight);
|
|
sprintf(skinname, "%s_%i%c", loadname, i, t+'a');
|
|
texnums->base = GL_LoadTexture(skinname, outskin->skinwidth, outskin->skinheight, saved, true, alpha);
|
|
|
|
if (gl_bumpmappingpossible)
|
|
{
|
|
char name[MAX_QPATH];
|
|
COM_StripExtension(skinname, name); //go for the normalmap
|
|
strcat(name, "_norm");
|
|
texnums->bump = Mod_LoadHiResTexture(name, true, true, false);
|
|
if (!texnums->bump)
|
|
{
|
|
strcpy(name, loadmodel->name);
|
|
COM_StripExtension(COM_SkipPath(skinname), COM_SkipPath(name));
|
|
strcat(name, "_norm");
|
|
texnums->bump = Mod_LoadHiResTexture(name, true, true, false);
|
|
if (!texnums->bump)
|
|
{
|
|
COM_StripExtension(skinname, name); //bother, go for heightmap and convert
|
|
strcat(name, "_bump");
|
|
texnums->bump = Mod_LoadBumpmapTexture(name);
|
|
if (!texnums->bump)
|
|
{
|
|
strcpy(name, loadmodel->name);
|
|
strcpy(COM_SkipPath(name), COM_SkipPath(skinname)); //eviile eh?
|
|
COM_StripExtension(name, name);
|
|
strcat(name, "_bump");
|
|
texnums->bump = Mod_LoadBumpmapTexture(name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r_fb_models.value)
|
|
{
|
|
sprintf(skinname, "%s_%i%c_luma", loadname, i, t+'a');
|
|
texnums->fullbright = GL_LoadTextureFB(skinname, outskin->skinwidth, outskin->skinheight, saved, true, true);
|
|
}
|
|
|
|
if (t != 0) //only keep the first.
|
|
BZ_Free(saved);
|
|
}
|
|
}
|
|
pskintype = (daliasskintype_t *)data;
|
|
break;
|
|
}
|
|
outskin++;
|
|
}
|
|
galias->numskins=pq1inmodel->numskins;
|
|
return pskintype;
|
|
}
|
|
|
|
void GL_LoadQ1Model (model_t *mod, void *buffer)
|
|
{
|
|
vec2_t *st_array;
|
|
int hunkstart, hunkend, hunktotal;
|
|
int version;
|
|
int i, j, onseams;
|
|
dstvert_t *pinstverts;
|
|
dtriangle_t *pintriangles;
|
|
int *seamremap;
|
|
index_t *indexes;
|
|
|
|
int size;
|
|
|
|
loadmodel=mod;
|
|
|
|
//we've got to have this bit
|
|
if (!strcmp(loadmodel->name, "progs/player.mdl") ||
|
|
!strcmp(loadmodel->name, "progs/eyes.mdl"))
|
|
{
|
|
unsigned short crc;
|
|
qbyte *p;
|
|
int len;
|
|
char st[40];
|
|
|
|
CRC_Init(&crc);
|
|
for (len = com_filesize, p = buffer; len; len--, p++)
|
|
CRC_ProcessByte(&crc, *p);
|
|
|
|
sprintf(st, "%d", (int) crc);
|
|
Info_SetValueForKey (cls.userinfo,
|
|
!strcmp(loadmodel->name, "progs/player.mdl") ? pmodel_name : emodel_name,
|
|
st, MAX_INFO_STRING);
|
|
|
|
if (cls.state >= ca_connected)
|
|
{
|
|
CL_SendClientCommand("setinfo %s %d",
|
|
!strcmp(loadmodel->name, "progs/player.mdl") ? pmodel_name : emodel_name,
|
|
(int)crc);
|
|
}
|
|
}
|
|
|
|
hunkstart = Hunk_LowMark ();
|
|
|
|
pq1inmodel = (dmdl_t *)buffer;
|
|
|
|
seamremap = (int*)pq1inmodel; //I like overloading locals.
|
|
for (i = 0; i < sizeof(dmdl_t)/4; i++)
|
|
seamremap[i] = LittleLong(seamremap[i]);
|
|
|
|
version = pq1inmodel->version;
|
|
if (version != ALIAS_VERSION)
|
|
Sys_Error ("%s has wrong version number (%i should be %i)",
|
|
mod->name, version, ALIAS_VERSION);
|
|
|
|
if (pq1inmodel->numframes < 1 ||
|
|
pq1inmodel->numskins < 1 ||
|
|
pq1inmodel->numtris < 1 ||
|
|
pq1inmodel->numverts < 3 ||
|
|
pq1inmodel->skinheight < 1 ||
|
|
pq1inmodel->skinwidth < 1)
|
|
Sys_Error("Model %s has an invalid quantity\n", mod->name);
|
|
|
|
mod->flags = pq1inmodel->flags;
|
|
|
|
size = sizeof(galiasinfo_t)
|
|
+ pq1inmodel->numframes*sizeof(galiasgroup_t)
|
|
+ pq1inmodel->numskins*sizeof(galiasskin_t);
|
|
|
|
galias = Hunk_Alloc(size);
|
|
galias->groupofs = sizeof(*galias);
|
|
galias->ofsskins = sizeof(*galias)+pq1inmodel->numframes*sizeof(galiasgroup_t);
|
|
galias->nextsurf = 0;
|
|
|
|
//skins
|
|
if( mod->flags & EF_HOLEY )
|
|
pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1), 3);
|
|
else if( mod->flags & EF_TRANSPARENT )
|
|
pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1), 2);
|
|
else if( mod->flags & EF_SPECIAL_TRANS )
|
|
pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1), 4);
|
|
else
|
|
pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1), 0);
|
|
|
|
// pinstverts = (dstvert_t *)Q1_LoadSkins((daliasskintype_t *)(pq1inmodel+1));
|
|
|
|
//count number of verts that are onseam.
|
|
for (onseams=0,i = 0; i < pq1inmodel->numverts; i++)
|
|
{
|
|
if (pinstverts[i].onseam)
|
|
onseams++;
|
|
}
|
|
seamremap = BZ_Malloc(sizeof(int)*pq1inmodel->numverts);
|
|
|
|
galias->numverts = pq1inmodel->numverts+onseams;
|
|
//st
|
|
st_array = Hunk_Alloc(sizeof(*st_array)*(pq1inmodel->numverts+onseams));
|
|
galias->ofs_st_array = (char *)st_array - (char *)galias;
|
|
for (j=pq1inmodel->numverts,i = 0; i < pq1inmodel->numverts; i++)
|
|
{
|
|
st_array[i][0] = LittleLong(pinstverts[i].s)/(float)pq1inmodel->skinwidth;
|
|
st_array[i][1] = LittleLong(pinstverts[i].t)/(float)pq1inmodel->skinheight;
|
|
|
|
if (pinstverts[i].onseam)
|
|
{
|
|
st_array[j][0] = st_array[i][0]+0.5;
|
|
st_array[j][1] = st_array[i][1];
|
|
seamremap[i] = j;
|
|
j++;
|
|
}
|
|
else
|
|
seamremap[i] = i;
|
|
}
|
|
|
|
//trianglelists;
|
|
pintriangles = (dtriangle_t *)&pinstverts[pq1inmodel->numverts];
|
|
|
|
galias->numindexes = pq1inmodel->numtris*3;
|
|
indexes = Hunk_Alloc(galias->numindexes*sizeof(*indexes));
|
|
galias->ofs_indexes = (char *)indexes - (char *)galias;
|
|
for (i=0 ; i<pq1inmodel->numtris ; i++)
|
|
{
|
|
if (!pintriangles[i].facesfront)
|
|
{
|
|
indexes[i*3+0] = seamremap[LittleLong(pintriangles[i].vertindex[0])];
|
|
indexes[i*3+1] = seamremap[LittleLong(pintriangles[i].vertindex[1])];
|
|
indexes[i*3+2] = seamremap[LittleLong(pintriangles[i].vertindex[2])];
|
|
}
|
|
else
|
|
{
|
|
indexes[i*3+0] = LittleLong(pintriangles[i].vertindex[0]);
|
|
indexes[i*3+1] = LittleLong(pintriangles[i].vertindex[1]);
|
|
indexes[i*3+2] = LittleLong(pintriangles[i].vertindex[2]);
|
|
}
|
|
}
|
|
|
|
//frames
|
|
Q1_LoadFrameGroup((daliasframetype_t *)&pintriangles[pq1inmodel->numtris], seamremap);
|
|
BZ_Free(seamremap);
|
|
|
|
if (r_shadows.value)
|
|
{
|
|
int *neighbours;
|
|
neighbours = Hunk_Alloc(sizeof(int)*3*pq1inmodel->numtris);
|
|
galias->ofs_trineighbours = (qbyte *)neighbours - (qbyte *)galias;
|
|
R_BuildTriangleNeighbours(neighbours, indexes, pq1inmodel->numtris);
|
|
}
|
|
|
|
VectorCopy (pq1inmodel->scale_origin, mod->mins);
|
|
VectorMA (mod->mins, 255, pq1inmodel->scale, mod->maxs);
|
|
//
|
|
// move the complete, relocatable alias model to the cache
|
|
//
|
|
hunkend = Hunk_LowMark ();
|
|
Hunk_Alloc(0);
|
|
hunktotal = hunkend - hunkstart;
|
|
|
|
Cache_Alloc (&mod->cache, hunktotal, loadname);
|
|
mod->type = mod_alias;
|
|
if (!mod->cache.data)
|
|
{
|
|
Hunk_FreeToLowMark (hunkstart);
|
|
return;
|
|
}
|
|
memcpy (mod->cache.data, galias, hunktotal);
|
|
|
|
Hunk_FreeToLowMark (hunkstart);
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
int Mod_ReadFlagsFromMD1(char *name, int md3version)
|
|
{
|
|
dmdl_t *pinmodel;
|
|
char fname[MAX_QPATH];
|
|
COM_StripExtension(name, fname);
|
|
COM_DefaultExtension(fname, ".mdl");
|
|
|
|
if (strcmp(name, fname)) //md3 renamed as mdl
|
|
{
|
|
COM_StripExtension(name, fname); //seeing as the md3 is named over the mdl,
|
|
COM_DefaultExtension(fname, ".md1");//read from a file with md1 (one, not an ell)
|
|
return 0;
|
|
}
|
|
|
|
pinmodel = (dmdl_t *)COM_LoadTempFile(fname);
|
|
|
|
if (!pinmodel) //not found
|
|
return 0;
|
|
|
|
if (LittleLong(pinmodel->ident) != IDPOLYHEADER)
|
|
return 0;
|
|
if (LittleLong(pinmodel->version) != ALIAS_VERSION)
|
|
return 0;
|
|
return LittleLong(pinmodel->flags);
|
|
}
|
|
|
|
#ifdef MD2MODELS
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//Q2 model loading
|
|
|
|
static galiasinfo_t *galias;
|
|
static model_t *loadmodel;
|
|
static md2_t *pq2inmodel;
|
|
#define Q2NUMVERTEXNORMALS 162
|
|
extern vec3_t bytedirs[Q2NUMVERTEXNORMALS];
|
|
|
|
static void Q2_LoadSkins(char *skins)
|
|
{
|
|
int i;
|
|
galiastexnum_t *texnums;
|
|
galiasskin_t *outskin = (galiasskin_t *)((char *)galias + galias->ofsskins);
|
|
|
|
for (i = 0; i < pq2inmodel->num_skins; i++)
|
|
{
|
|
texnums = Hunk_Alloc(sizeof(*texnums));
|
|
outskin->ofstexnums = (char *)texnums - (char *)outskin;
|
|
outskin->texnums=1;
|
|
|
|
texnums->base = Mod_LoadReplacementTexture(skins, true, false, true);
|
|
outskin->skinwidth = 0;
|
|
outskin->skinheight = 0;
|
|
outskin->skinspeed = 0;
|
|
|
|
skins += MD2MAX_SKINNAME;
|
|
}
|
|
}
|
|
|
|
#define MD2_MAX_TRIANGLES 4096
|
|
void GL_LoadQ2Model (model_t *mod, void *buffer)
|
|
{
|
|
int hunkstart, hunkend, hunktotal;
|
|
int version;
|
|
int i, j;
|
|
dmd2stvert_t *pinstverts;
|
|
dmd2triangle_t *pintri;
|
|
index_t *indexes;
|
|
int numindexes;
|
|
|
|
vec3_t min;
|
|
vec3_t max;
|
|
|
|
galiaspose_t *pose;
|
|
galiasgroup_t *poutframe;
|
|
dmd2aliasframe_t *pinframe;
|
|
int framesize;
|
|
vec3_t *verts;
|
|
vec3_t *normals;
|
|
vec2_t *st_array;
|
|
|
|
int indremap[MD2_MAX_TRIANGLES*3];
|
|
unsigned short ptempindex[MD2_MAX_TRIANGLES*3], ptempstindex[MD2_MAX_TRIANGLES*3];
|
|
|
|
int numverts;
|
|
|
|
int size;
|
|
|
|
|
|
loadmodel=mod;
|
|
|
|
hunkstart = Hunk_LowMark ();
|
|
|
|
pq2inmodel = (md2_t *)buffer;
|
|
|
|
version = LittleLong (pq2inmodel->version);
|
|
if (version != MD2ALIAS_VERSION)
|
|
Sys_Error ("%s has wrong version number (%i should be %i)",
|
|
mod->name, version, MD2ALIAS_VERSION);
|
|
|
|
if (pq2inmodel->num_frames < 1 ||
|
|
pq2inmodel->num_skins < 0 ||
|
|
pq2inmodel->num_tris < 1 ||
|
|
pq2inmodel->num_xyz < 3 ||
|
|
pq2inmodel->num_st < 3 ||
|
|
pq2inmodel->skinheight < 1 ||
|
|
pq2inmodel->skinwidth < 1)
|
|
Sys_Error("Model %s has an invalid quantity\n", mod->name);
|
|
|
|
mod->flags = 0;
|
|
|
|
size = sizeof(galiasinfo_t)
|
|
+ pq2inmodel->num_frames*sizeof(galiasgroup_t)
|
|
+ pq2inmodel->num_skins*sizeof(galiasskin_t);
|
|
|
|
galias = Hunk_Alloc(size);
|
|
galias->groupofs = sizeof(*galias);
|
|
galias->ofsskins = sizeof(*galias)+pq2inmodel->num_frames*sizeof(galiasgroup_t);
|
|
galias->nextsurf = 0;
|
|
|
|
//skins
|
|
Q2_LoadSkins(((char *)pq2inmodel+pq2inmodel->ofs_skins));
|
|
|
|
//trianglelists;
|
|
pintri = (dmd2triangle_t *)((char *)pq2inmodel + pq2inmodel->ofs_tris);
|
|
|
|
|
|
for (i=0 ; i<pq2inmodel->num_tris ; i++, pintri++)
|
|
{
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
ptempindex[i*3+j] = ( unsigned short )LittleShort ( pintri->xyz_index[j] );
|
|
ptempstindex[i*3+j] = ( unsigned short )LittleShort ( pintri->st_index[j] );
|
|
}
|
|
}
|
|
|
|
numindexes = galias->numindexes = pq2inmodel->num_tris*3;
|
|
indexes = Hunk_Alloc(galias->numindexes*sizeof(*indexes));
|
|
galias->ofs_indexes = (char *)indexes - (char *)galias;
|
|
memset ( indremap, -1, sizeof(indremap) );
|
|
numverts=0;
|
|
|
|
for ( i = 0; i < numindexes; i++ )
|
|
{
|
|
if ( indremap[i] != -1 ) {
|
|
continue;
|
|
}
|
|
|
|
for ( j = 0; j < numindexes; j++ )
|
|
{
|
|
if ( j == i ) {
|
|
continue;
|
|
}
|
|
|
|
if ( (ptempindex[i] == ptempindex[j]) && (ptempstindex[i] == ptempstindex[j]) ) {
|
|
indremap[j] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// count unique vertexes
|
|
for ( i = 0; i < numindexes; i++ )
|
|
{
|
|
if ( indremap[i] != -1 ) {
|
|
continue;
|
|
}
|
|
|
|
indexes[i] = numverts++;
|
|
indremap[i] = i;
|
|
}
|
|
|
|
Con_DPrintf ( "%s: remapped %i verts to %i\n", mod->name, pq2inmodel->num_xyz, numverts );
|
|
|
|
galias->numverts = numverts;
|
|
|
|
// remap remaining indexes
|
|
for ( i = 0; i < numindexes; i++ )
|
|
{
|
|
if ( indremap[i] != i ) {
|
|
indexes[i] = indexes[indremap[i]];
|
|
}
|
|
}
|
|
|
|
// s and t vertices
|
|
pinstverts = ( dmd2stvert_t * ) ( ( qbyte * )pq2inmodel + LittleLong (pq2inmodel->ofs_st) );
|
|
st_array = Hunk_Alloc(sizeof(*st_array)*(numverts));
|
|
galias->ofs_st_array = (char *)st_array - (char *)galias;
|
|
|
|
for (j=0 ; j<numindexes; j++)
|
|
{
|
|
st_array[indexes[j]][0] = (float)(((double)LittleShort (pinstverts[ptempstindex[indremap[j]]].s) + 0.5f) /pq2inmodel->skinwidth);
|
|
st_array[indexes[j]][1] = (float)(((double)LittleShort (pinstverts[ptempstindex[indremap[j]]].t) + 0.5f) /pq2inmodel->skinheight);
|
|
}
|
|
|
|
//frames
|
|
ClearBounds ( mod->mins, mod->maxs );
|
|
|
|
poutframe = (galiasgroup_t*)((char *)galias + galias->groupofs);
|
|
framesize = LittleLong (pq2inmodel->framesize);
|
|
for (i=0 ; i<pq2inmodel->num_frames ; i++)
|
|
{
|
|
pose = (galiaspose_t *)Hunk_Alloc(sizeof(galiaspose_t) + sizeof(vec3_t)*2*numverts);
|
|
poutframe->poseofs = (char *)pose - (char *)poutframe;
|
|
poutframe->numposes = 1;
|
|
galias->groups++;
|
|
|
|
verts = (vec3_t *)(pose+1);
|
|
normals = &verts[galias->numverts];
|
|
pose->ofsverts = (char *)verts - (char *)pose;
|
|
pose->ofsnormals = (char *)normals - (char *)pose;
|
|
|
|
|
|
pinframe = ( dmd2aliasframe_t * )( ( qbyte * )pq2inmodel + LittleLong (pq2inmodel->ofs_frames) + i * framesize );
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
{
|
|
pose->scale[j] = LittleFloat (pinframe->scale[j]);
|
|
pose->scale_origin[j] = LittleFloat (pinframe->translate[j]);
|
|
}
|
|
|
|
for (j=0 ; j<numindexes; j++)
|
|
{
|
|
// verts are all 8 bit, so no swapping needed
|
|
verts[indexes[j]][0] = pose->scale_origin[0]+pose->scale[0]*pinframe->verts[ptempindex[indremap[j]]].v[0];
|
|
verts[indexes[j]][1] = pose->scale_origin[1]+pose->scale[1]*pinframe->verts[ptempindex[indremap[j]]].v[1];
|
|
verts[indexes[j]][2] = pose->scale_origin[2]+pose->scale[2]*pinframe->verts[ptempindex[indremap[j]]].v[2];
|
|
VectorCopy(bytedirs[pinframe->verts[ptempindex[indremap[j]]].lightnormalindex], normals[indexes[j]]);
|
|
}
|
|
|
|
// Mod_AliasCalculateVertexNormals ( numindexes, poutindex, numverts, poutvertex, qfalse );
|
|
|
|
VectorCopy ( pose->scale_origin, min );
|
|
VectorMA ( pose->scale_origin, 255, pose->scale, max );
|
|
|
|
// poutframe->radius = RadiusFromBounds ( min, max );
|
|
|
|
// mod->radius = max ( mod->radius, poutframe->radius );
|
|
AddPointToBounds ( min, mod->mins, mod->maxs );
|
|
AddPointToBounds ( max, mod->mins, mod->maxs );
|
|
|
|
// GL_GenerateNormals((float*)verts, (float*)normals, indexes, numindexes/3, numverts);
|
|
|
|
poutframe++;
|
|
}
|
|
|
|
|
|
|
|
|
|
if (r_shadows.value)
|
|
{
|
|
int *neighbours;
|
|
neighbours = Hunk_Alloc(sizeof(int)*3*pq2inmodel->num_tris);
|
|
galias->ofs_trineighbours = (qbyte *)neighbours - (qbyte *)galias;
|
|
R_BuildTriangleNeighbours(neighbours, indexes, pq2inmodel->num_tris);
|
|
}
|
|
|
|
/*
|
|
VectorCopy (pq2inmodel->scale_origin, mod->mins);
|
|
VectorMA (mod->mins, 255, pq2inmodel->scale, mod->maxs);
|
|
*/
|
|
//
|
|
// move the complete, relocatable alias model to the cache
|
|
//
|
|
hunkend = Hunk_LowMark ();
|
|
Hunk_Alloc(0);
|
|
hunktotal = hunkend - hunkstart;
|
|
|
|
Cache_Alloc (&mod->cache, hunktotal, loadname);
|
|
mod->type = mod_alias;
|
|
if (!mod->cache.data)
|
|
{
|
|
Hunk_FreeToLowMark (hunkstart);
|
|
return;
|
|
}
|
|
memcpy (mod->cache.data, galias, hunktotal);
|
|
|
|
Hunk_FreeToLowMark (hunkstart);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
char name[MAX_QPATH];
|
|
vec3_t org;
|
|
float ang[3][3];
|
|
} md3tag_t;
|
|
|
|
|
|
|
|
|
|
void GLMod_GetTag(model_t *model, int tagnum, int frame, float **org, float **axis)
|
|
{
|
|
galiasinfo_t *inf;
|
|
md3tag_t *t;
|
|
|
|
*org = NULL;
|
|
*axis = NULL;
|
|
if (!model || model->type != mod_alias)
|
|
return;
|
|
|
|
inf = Mod_Extradata(model);
|
|
t = (md3tag_t*)((char*)inf + inf->ofstags);
|
|
if (tagnum <= 0 || tagnum > inf->numtags)
|
|
return;
|
|
if (frame < 0 || frame >= inf->numtagframes)
|
|
return;
|
|
tagnum--; //tagnum 0 is 'use my angles/org'
|
|
|
|
t += tagnum;
|
|
t += inf->numtags*frame;
|
|
*org = t->org;
|
|
*axis = (float*)t->ang;
|
|
}
|
|
|
|
int GLMod_TagNumForName(model_t *model, char *name)
|
|
{
|
|
int i;
|
|
galiasinfo_t *inf;
|
|
md3tag_t *t;
|
|
|
|
if (!model || model->type != mod_alias)
|
|
return 0;
|
|
|
|
inf = Mod_Extradata(model);
|
|
t = (md3tag_t*)((char*)inf + inf->ofstags);
|
|
for (i = 0; i < inf->numtags; i++)
|
|
{
|
|
if (!strcmp(t[i].name, name))
|
|
return i+1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef MD3MODELS
|
|
|
|
//structures from Tenebrae
|
|
typedef struct {
|
|
int ident;
|
|
int version;
|
|
|
|
char name[MAX_QPATH];
|
|
|
|
int flags; //Does anyone know what these are?
|
|
|
|
int numFrames;
|
|
int numTags;
|
|
int numSurfaces;
|
|
|
|
int numSkins;
|
|
|
|
int ofsFrames;
|
|
int ofsTags;
|
|
int ofsSurfaces;
|
|
int ofsEnd;
|
|
} md3Header_t;
|
|
|
|
//then has header->numFrames of these at header->ofs_Frames
|
|
typedef struct md3Frame_s {
|
|
vec3_t bounds[2];
|
|
vec3_t localOrigin;
|
|
float radius;
|
|
char name[16];
|
|
} md3Frame_t;
|
|
|
|
//there are header->numSurfaces of these at header->ofsSurfaces, following from ofsEnd
|
|
typedef struct {
|
|
int ident; //
|
|
|
|
char name[MAX_QPATH]; // polyset name
|
|
|
|
int flags;
|
|
int numFrames; // all surfaces in a model should have the same
|
|
|
|
int numShaders; // all surfaces in a model should have the same
|
|
int numVerts;
|
|
|
|
int numTriangles;
|
|
int ofsTriangles;
|
|
|
|
int ofsShaders; // offset from start of md3Surface_t
|
|
int ofsSt; // texture coords are common for all frames
|
|
int ofsXyzNormals; // numVerts * numFrames
|
|
|
|
int ofsEnd; // next surface follows
|
|
} md3Surface_t;
|
|
|
|
//at surf+surf->ofsXyzNormals
|
|
typedef struct {
|
|
short xyz[3];
|
|
qbyte latlong[2];
|
|
} md3XyzNormal_t;
|
|
|
|
//surf->numTriangles at surf+surf->ofsTriangles
|
|
typedef struct {
|
|
int indexes[3];
|
|
} md3Triangle_t;
|
|
|
|
//surf->numVerts at surf+surf->ofsSt
|
|
typedef struct {
|
|
float s;
|
|
float t;
|
|
} md3St_t;
|
|
|
|
typedef struct {
|
|
char name[MAX_QPATH];
|
|
int shaderIndex;
|
|
} md3Shader_t;
|
|
//End of Tenebrae 'assistance'
|
|
|
|
//This is a hack. It uses an assuption about q3 player models.
|
|
void GL_ParseQ3SkinFile(char *out, char *surfname, char *modelname)
|
|
{
|
|
char *f, *p;
|
|
char line[256];
|
|
COM_StripExtension(modelname, line);
|
|
strcat(line, "_default.skin");
|
|
|
|
f = COM_LoadTempFile2(line);
|
|
while(f)
|
|
{
|
|
f = COM_ParseToken(f);
|
|
if (!f)
|
|
return;
|
|
while(*f == ' ' || *f == '\t')
|
|
f++;
|
|
if (*f == ',')
|
|
{
|
|
if (!strcmp(com_token, surfname))
|
|
{
|
|
f++;
|
|
COM_ParseToken(f);
|
|
strcpy(out, com_token);
|
|
return;
|
|
}
|
|
}
|
|
|
|
p = strchr(f, '\n');
|
|
if (!p)
|
|
f = f+strlen(f);
|
|
else
|
|
f = p+1;
|
|
if (!*f)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GL_LoadQ3Model(model_t *mod, void *buffer)
|
|
{
|
|
int hunkstart, hunkend, hunktotal;
|
|
// int version;
|
|
int s, i, j, d;
|
|
|
|
index_t *indexes;
|
|
|
|
vec3_t min;
|
|
vec3_t max;
|
|
|
|
galiaspose_t *pose;
|
|
galiasinfo_t *parent, *root;
|
|
galiasgroup_t *group;
|
|
|
|
galiasskin_t *skin;
|
|
galiastexnum_t *texnum;
|
|
|
|
vec3_t *verts;
|
|
vec3_t *normals;
|
|
vec2_t *st_array;
|
|
|
|
float lat, lng;
|
|
|
|
md3St_t *inst;
|
|
md3Triangle_t *intris;
|
|
md3XyzNormal_t *invert;
|
|
md3Shader_t *inshader;
|
|
|
|
|
|
int size;
|
|
|
|
md3Header_t *header;
|
|
md3Surface_t *surf;
|
|
|
|
|
|
loadmodel=mod;
|
|
|
|
hunkstart = Hunk_LowMark ();
|
|
|
|
header = buffer;
|
|
|
|
// if (header->version != sdfs)
|
|
// Sys_Error("GL_LoadQ3Model: Bad version\n");
|
|
|
|
parent = NULL;
|
|
root = NULL;
|
|
|
|
min[0] = min[1] = min[2] = 0;
|
|
max[0] = max[1] = max[2] = 0;
|
|
|
|
surf = (md3Surface_t *)((qbyte *)header + header->ofsSurfaces);
|
|
for (s = 0; s < header->numSurfaces; s++)
|
|
{
|
|
size = sizeof(galiasinfo_t) + sizeof(galiasgroup_t)*header->numFrames;
|
|
galias = Hunk_Alloc(size);
|
|
galias->groupofs = sizeof(*galias); //frame groups
|
|
galias->groups = header->numFrames;
|
|
galias->numverts = surf->numVerts;
|
|
galias->numindexes = surf->numTriangles*3;
|
|
galias->numskins = 1;
|
|
if (parent)
|
|
parent->nextsurf = (qbyte *)galias - (qbyte *)parent;
|
|
else
|
|
root = galias;
|
|
parent = galias;
|
|
|
|
st_array = Hunk_Alloc(sizeof(vec2_t)*galias->numindexes);
|
|
galias->ofs_st_array = (qbyte*)st_array - (qbyte*)galias;
|
|
|
|
inst = (md3St_t*)((qbyte*)surf + surf->ofsSt);
|
|
for (i = 0; i < galias->numverts; i++)
|
|
{
|
|
st_array[i][0] = inst[i].s;
|
|
st_array[i][1] = inst[i].t;
|
|
}
|
|
|
|
indexes = Hunk_Alloc(sizeof(*indexes)*galias->numindexes);
|
|
galias->ofs_indexes = (qbyte*)indexes - (qbyte*)galias;
|
|
intris = (md3Triangle_t *)((qbyte*)surf + surf->ofsTriangles);
|
|
for (i = 0; i < surf->numTriangles; i++)
|
|
{
|
|
indexes[i*3+0] = intris[i].indexes[0];
|
|
indexes[i*3+1] = intris[i].indexes[1];
|
|
indexes[i*3+2] = intris[i].indexes[2];
|
|
}
|
|
|
|
group = (galiasgroup_t *)(galias+1);
|
|
invert = (md3XyzNormal_t *)((qbyte*)surf + surf->ofsXyzNormals);
|
|
for (i = 0; i < surf->numFrames; i++)
|
|
{
|
|
pose = (galiaspose_t *)Hunk_Alloc(sizeof(galiaspose_t) + sizeof(vec3_t)*2*surf->numVerts);
|
|
normals = (vec3_t*)(pose+1);
|
|
verts = normals + surf->numVerts;
|
|
|
|
pose->ofsnormals = (qbyte*)normals - (qbyte*)pose;
|
|
pose->ofsverts = (qbyte*)verts - (qbyte*)pose;
|
|
|
|
for (j = 0; j < surf->numVerts; j++)
|
|
{
|
|
lat = (float)invert[j].latlong[0] * (2 * M_PI)*(1.0 / 255.0);
|
|
lng = (float)invert[j].latlong[1] * (2 * M_PI)*(1.0 / 255.0);
|
|
normals[j][0] = cos ( lng ) * sin ( lat );
|
|
normals[j][1] = sin ( lng ) * sin ( lat );
|
|
normals[j][2] = cos ( lat );
|
|
|
|
for (d = 0; d < 3; d++)
|
|
{
|
|
verts[j][d] = invert[j].xyz[d]/64.0f;
|
|
if (verts[j][d]<min[d])
|
|
min[d] = verts[j][d];
|
|
if (verts[j][d]>max[d])
|
|
max[d] = verts[j][d];
|
|
}
|
|
}
|
|
|
|
pose->scale[0] = 1;
|
|
pose->scale[1] = 1;
|
|
pose->scale[2] = 1;
|
|
|
|
pose->scale_origin[0] = 0;
|
|
pose->scale_origin[1] = 0;
|
|
pose->scale_origin[2] = 0;
|
|
|
|
group->numposes = 1;
|
|
group->rate = 1;
|
|
group->poseofs = (qbyte*)pose - (qbyte*)group;
|
|
|
|
group++;
|
|
invert += surf->numVerts;
|
|
}
|
|
|
|
if (surf->numShaders)
|
|
{
|
|
#ifndef Q3SHADERS
|
|
char name[1024];
|
|
extern int gl_bumpmappingpossible;
|
|
#endif
|
|
skin = Hunk_Alloc(surf->numShaders*((sizeof(galiasskin_t)+sizeof(galiastexnum_t))));
|
|
galias->ofsskins = (qbyte *)skin - (qbyte *)galias;
|
|
texnum = (galiastexnum_t *)(skin + surf->numShaders);
|
|
inshader = (md3Shader_t *)((qbyte *)surf + surf->ofsShaders);
|
|
for (i = 0; i < surf->numShaders; i++)
|
|
{
|
|
skin->texnums = 1;
|
|
skin->ofstexnums = (qbyte *)texnum - (qbyte *)skin;
|
|
skin->ofstexels = 0;
|
|
skin->skinwidth = 0;
|
|
skin->skinheight = 0;
|
|
skin->skinspeed = 0;
|
|
|
|
if (!*inshader->name) //'fix' the shader by looking the surface name up in a skin file. This isn't perfect, but it does the job for basic models.
|
|
GL_ParseQ3SkinFile(inshader->name, surf->name, loadmodel->name);
|
|
#ifdef Q3SHADERS
|
|
texnum->shader = R_RegisterSkin(inshader->name);
|
|
#else
|
|
|
|
texnum->base = Mod_LoadHiResTexture(inshader->name, true, true, true);
|
|
if (!texnum->base)
|
|
{
|
|
strcpy(name, loadmodel->name);
|
|
strcpy(COM_SkipPath(name), COM_SkipPath(inshader->name)); //eviile eh?
|
|
texnum->base = Mod_LoadHiResTexture(name, true, true, true);
|
|
}
|
|
|
|
texnum->bump = 0;
|
|
if (gl_bumpmappingpossible)
|
|
{
|
|
COM_StripExtension(inshader->name, name); //go for the normalmap
|
|
strcat(name, "_norm");
|
|
texnum->bump = Mod_LoadHiResTexture(name, true, true, false);
|
|
if (!texnum->bump)
|
|
{
|
|
strcpy(name, loadmodel->name);
|
|
COM_StripExtension(COM_SkipPath(inshader->name), COM_SkipPath(name));
|
|
strcat(name, "_norm");
|
|
texnum->bump = Mod_LoadHiResTexture(name, true, true, false);
|
|
if (!texnum->bump)
|
|
{
|
|
COM_StripExtension(inshader->name, name); //bother, go for heightmap and convert
|
|
strcat(name, "_bump");
|
|
texnum->bump = Mod_LoadBumpmapTexture(name);
|
|
if (!texnum->bump)
|
|
{
|
|
strcpy(name, loadmodel->name);
|
|
strcpy(COM_SkipPath(name), COM_SkipPath(inshader->name)); //eviile eh?
|
|
COM_StripExtension(name, name);
|
|
strcat(name, "_bump");
|
|
texnum->bump = Mod_LoadBumpmapTexture(name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (r_fb_models.value)
|
|
{
|
|
COM_StripExtension(inshader->name, name); //go for the normalmap
|
|
strcat(name, "_luma");
|
|
texnum->fullbright = Mod_LoadHiResTexture(name, true, true, true);
|
|
if (!texnum->base)
|
|
{
|
|
strcpy(name, loadmodel->name);
|
|
strcpy(COM_SkipPath(name), COM_SkipPath(inshader->name)); //eviile eh?
|
|
COM_StripExtension(name, name);
|
|
strcat(name, "_luma");
|
|
texnum->fullbright = Mod_LoadBumpmapTexture(name);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
skin++;
|
|
texnum++;
|
|
}
|
|
}
|
|
|
|
VectorCopy(min, loadmodel->mins);
|
|
VectorCopy(max, loadmodel->maxs);
|
|
|
|
|
|
|
|
if (r_shadows.value)
|
|
{
|
|
int *neighbours;
|
|
neighbours = Hunk_Alloc(sizeof(int)*3*surf->numTriangles);
|
|
galias->ofs_trineighbours = (qbyte *)neighbours - (qbyte *)galias;
|
|
R_BuildTriangleNeighbours(neighbours, indexes, surf->numTriangles);
|
|
}
|
|
surf = (md3Surface_t *)((qbyte *)surf + surf->ofsEnd);
|
|
}
|
|
|
|
if (!root)
|
|
root = Hunk_Alloc(sizeof(galiasinfo_t));
|
|
|
|
root->numtagframes = header->numFrames;
|
|
root->numtags = header->numTags;
|
|
root->ofstags = (char*)Hunk_Alloc(header->numTags*sizeof(md3tag_t)*header->numFrames) - (char*)root;
|
|
memcpy((char*)root+root->ofstags, (char*)header+header->ofsTags, header->numTags*sizeof(md3tag_t)*header->numFrames);
|
|
|
|
//
|
|
// move the complete, relocatable alias model to the cache
|
|
//
|
|
|
|
hunkend = Hunk_LowMark ();
|
|
|
|
mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0);
|
|
|
|
Hunk_Alloc(0);
|
|
hunktotal = hunkend - hunkstart;
|
|
|
|
Cache_Alloc (&mod->cache, hunktotal, loadname);
|
|
mod->type = mod_alias;
|
|
if (!mod->cache.data)
|
|
{
|
|
Hunk_FreeToLowMark (hunkstart);
|
|
return;
|
|
}
|
|
memcpy (mod->cache.data, root, hunktotal);
|
|
|
|
Hunk_FreeToLowMark (hunkstart);
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef ZYMOTICMODELS
|
|
|
|
|
|
typedef struct zymlump_s
|
|
{
|
|
int start;
|
|
int length;
|
|
} zymlump_t;
|
|
|
|
typedef struct zymtype1header_s
|
|
{
|
|
char id[12]; // "ZYMOTICMODEL", length 12, no termination
|
|
int type; // 0 (vertex morph) 1 (skeletal pose) or 2 (skeletal scripted)
|
|
int filesize; // size of entire model file
|
|
float mins[3], maxs[3], radius; // for clipping uses
|
|
int numverts;
|
|
int numtris;
|
|
int numshaders;
|
|
int numbones; // this may be zero in the vertex morph format (undecided)
|
|
int numscenes; // 0 in skeletal scripted models
|
|
|
|
// skeletal pose header
|
|
// lump offsets are relative to the file
|
|
zymlump_t lump_scenes; // zymscene_t scene[numscenes]; // name and other information for each scene (see zymscene struct)
|
|
zymlump_t lump_poses; // float pose[numposes][numbones][6]; // animation data
|
|
zymlump_t lump_bones; // zymbone_t bone[numbones];
|
|
zymlump_t lump_vertbonecounts; // int vertbonecounts[numvertices]; // how many bones influence each vertex (separate mainly to make this compress better)
|
|
zymlump_t lump_verts; // zymvertex_t vert[numvertices]; // see vertex struct
|
|
zymlump_t lump_texcoords; // float texcoords[numvertices][2];
|
|
zymlump_t lump_render; // int renderlist[rendersize]; // sorted by shader with run lengths (int count), shaders are sequentially used, each run can be used with glDrawElements (each triangle is 3 int indices)
|
|
zymlump_t lump_shaders; // char shadername[numshaders][32]; // shaders used on this model
|
|
zymlump_t lump_trizone; // byte trizone[numtris]; // see trizone explanation
|
|
} zymtype1header_t;
|
|
|
|
typedef struct zymbone_s
|
|
{
|
|
char name[32];
|
|
int flags;
|
|
int parent; // parent bone number
|
|
} zymbone_t;
|
|
|
|
typedef struct zymscene_s
|
|
{
|
|
char name[32];
|
|
float mins[3], maxs[3], radius; // for clipping
|
|
float framerate; // the scene will animate at this framerate (in frames per second)
|
|
int flags;
|
|
int start, length; // range of poses
|
|
} zymscene_t;
|
|
|
|
typedef struct zymvertex_s
|
|
{
|
|
int bonenum;
|
|
float origin[3];
|
|
} zymvertex_t;
|
|
|
|
//this can generate multiple meshes (one for each shader).
|
|
//but only one set of transforms are ever generated.
|
|
void GLMod_LoadZymoticModel(model_t *mod, void *buffer)
|
|
{
|
|
int i;
|
|
int hunkstart, hunkend, hunktotal;
|
|
|
|
zymtype1header_t *header;
|
|
galiasinfo_t *root;
|
|
|
|
galisskeletaltransforms_t *transforms;
|
|
zymvertex_t *intrans;
|
|
|
|
galiasskin_t *skin;
|
|
galiastexnum_t *texnums;
|
|
|
|
galiasbone_t *bone;
|
|
zymbone_t *inbone;
|
|
int v;
|
|
float multiplier;
|
|
float *matrix, *inmatrix;
|
|
|
|
vec2_t *stcoords;
|
|
vec2_t *inst;
|
|
|
|
int *vertbonecounts;
|
|
|
|
galiasgroup_t *grp;
|
|
zymscene_t *inscene;
|
|
|
|
int *renderlist, count;
|
|
index_t *indexes;
|
|
|
|
char *shadername;
|
|
|
|
|
|
loadmodel=mod;
|
|
|
|
hunkstart = Hunk_LowMark ();
|
|
|
|
header = buffer;
|
|
|
|
if (memcmp(header->id, "ZYMOTICMODEL", 12))
|
|
Sys_Error("GLMod_LoadZymoticModel: doesn't appear to BE a zymotic!\n");
|
|
|
|
if (BigLong(header->type) != 1)
|
|
Sys_Error("GLMod_LoadZymoticModel: only type 1 is supported\n");
|
|
|
|
for (i = 0; i < sizeof(zymtype1header_t)/4; i++)
|
|
((int*)header)[i] = BigLong(((int*)header)[i]);
|
|
|
|
if (!header->numverts)
|
|
Sys_Error("GLMod_LoadZymoticModel: no vertexes\n");
|
|
|
|
if (!header->numshaders)
|
|
Sys_Error("GLMod_LoadZymoticModel: no textures\n");
|
|
|
|
root = Hunk_AllocName(sizeof(galiasinfo_t)*header->numshaders, loadname);
|
|
|
|
root->numtransforms = header->lump_verts.length/sizeof(zymvertex_t);
|
|
transforms = Hunk_Alloc(root->numtransforms*sizeof(*transforms));
|
|
root->ofstransforms = (char*)transforms - (char*)root;
|
|
|
|
vertbonecounts = (int *)((char*)header + header->lump_vertbonecounts.start);
|
|
intrans = (zymvertex_t *)((char*)header + header->lump_verts.start);
|
|
|
|
vertbonecounts[0] = BigLong(vertbonecounts[0]);
|
|
multiplier = 1.0f / vertbonecounts[0];
|
|
for (i = 0, v=0; i < root->numtransforms; i++)
|
|
{
|
|
while(!vertbonecounts[v])
|
|
{
|
|
v++;
|
|
if (v == header->numverts)
|
|
Sys_Error("GLMod_LoadZymoticModel: Too many transformations\n");
|
|
vertbonecounts[v] = BigLong(vertbonecounts[v]);
|
|
multiplier = 1.0f / vertbonecounts[v];
|
|
}
|
|
transforms[i].vertexindex = v;
|
|
transforms[i].boneindex = BigLong(intrans[i].bonenum);
|
|
transforms[i].org[0] = multiplier*BigFloat(intrans[i].origin[0]);
|
|
transforms[i].org[1] = multiplier*BigFloat(intrans[i].origin[1]);
|
|
transforms[i].org[2] = multiplier*BigFloat(intrans[i].origin[2]);
|
|
transforms[i].org[3] = multiplier*1;
|
|
vertbonecounts[v]--;
|
|
}
|
|
if (intrans != (zymvertex_t *)((char*)header + header->lump_verts.start))
|
|
Sys_Error("Vertex transforms list appears corrupt.");
|
|
if (vertbonecounts != (int *)((char*)header + header->lump_vertbonecounts.start))
|
|
Sys_Error("Vertex bone counts list appears corrupt.");
|
|
|
|
|
|
root->numverts = v+1;
|
|
|
|
root->numbones = header->numbones;
|
|
bone = Hunk_Alloc(root->numtransforms*sizeof(*transforms));
|
|
inbone = (zymbone_t*)((char*)header + header->lump_bones.start);
|
|
for (i = 0; i < root->numbones; i++)
|
|
bone[i].parent = BigLong(inbone[i].parent);
|
|
root->ofsbones = (char *)bone - (char *)root;
|
|
|
|
renderlist = (int*)((char*)header + header->lump_render.start);
|
|
for (i = 0;i < header->numshaders; i++)
|
|
{
|
|
count = BigLong(*renderlist++);
|
|
count *= 3;
|
|
indexes = Hunk_Alloc(count*sizeof(*indexes));
|
|
root[i].ofs_indexes = (char *)indexes - (char*)&root[i];
|
|
root[i].numindexes = count;
|
|
while(count)
|
|
{ //invert
|
|
indexes[count-1] = BigLong(renderlist[count-3]);
|
|
indexes[count-2] = BigLong(renderlist[count-2]);
|
|
indexes[count-3] = BigLong(renderlist[count-1]);
|
|
count-=3;
|
|
}
|
|
renderlist += root[i].numindexes;
|
|
}
|
|
if (renderlist != (int*)((char*)header + header->lump_render.start + header->lump_render.length))
|
|
Sys_Error("Render list appears corrupt.");
|
|
|
|
grp = Hunk_Alloc(sizeof(*grp)*header->numscenes*header->numshaders);
|
|
matrix = Hunk_Alloc(header->lump_poses.length);
|
|
inmatrix = (float*)((char*)header + header->lump_poses.start);
|
|
for (i = 0; i < header->lump_poses.length/4; i++)
|
|
matrix[i] = BigFloat(inmatrix[i]);
|
|
inscene = (zymscene_t*)((char*)header + header->lump_scenes.start);
|
|
shadername = ((char*)header + header->lump_shaders.start);
|
|
|
|
stcoords = Hunk_Alloc(root[0].numverts*sizeof(vec2_t));
|
|
inst = (vec2_t *)((char *)header + header->lump_texcoords.start);
|
|
for (i = 0; i < header->lump_texcoords.length/8; i++)
|
|
{
|
|
stcoords[i][0] = BigFloat(inst[i][0]);
|
|
stcoords[i][1] = 1-BigFloat(inst[i][1]); //hmm. upside down skin coords?
|
|
}
|
|
for (i = 0; i < header->numshaders; i++, shadername+=32)
|
|
{
|
|
root[i].ofs_st_array = (char*)stcoords - (char*)&root[i];
|
|
|
|
root[i].groups = header->numscenes;
|
|
root[i].groupofs = (char*)grp - (char*)&root[i];
|
|
|
|
skin = Hunk_Alloc(sizeof(*skin)+sizeof(*texnums));
|
|
texnums = (galiastexnum_t *)(skin+1); //texnums is seperate for skingroups/animating skins... Which this format doesn't support.
|
|
root[i].ofsskins = (char *)skin - (char *)&root[i];
|
|
root[i].numskins = 1;
|
|
skin->ofstexnums = (char *)texnums - (char *)skin;
|
|
skin->texnums = 1;
|
|
texnums->base = Mod_LoadHiResTexture(shadername, true, true, true);
|
|
}
|
|
|
|
|
|
for (i = 0; i < header->numscenes; i++, grp++, inscene++)
|
|
{
|
|
grp->isskeletal = 1;
|
|
grp->rate = BigFloat(inscene->framerate);
|
|
grp->numposes = BigLong(inscene->length);
|
|
grp->poseofs = (char*)matrix - (char*)grp;
|
|
grp->poseofs += BigLong(inscene->start)*12*sizeof(float)*root->numbones;
|
|
}
|
|
|
|
if (inscene != (zymscene_t*)((char*)header + header->lump_scenes.start+header->lump_scenes.length))
|
|
Sys_Error("scene list appears corrupt.");
|
|
|
|
for (i = 0; i < header->numshaders-1; i++)
|
|
root[i].nextsurf = sizeof(galiasinfo_t);
|
|
for (i = 1; i < header->numshaders; i++)
|
|
{
|
|
|
|
root[i].sharesverts = true;
|
|
root[i].numbones = root[0].numbones;
|
|
root[i].numindexes = root[0].numindexes;
|
|
root[i].numverts = root[0].numverts;
|
|
|
|
root[i].ofsbones = root[0].ofsbones;
|
|
}
|
|
|
|
//
|
|
// move the complete, relocatable alias model to the cache
|
|
//
|
|
|
|
hunkend = Hunk_LowMark ();
|
|
|
|
mod->flags = Mod_ReadFlagsFromMD1(mod->name, 0); //file replacement - inherit flags from any defunc mdl files.
|
|
|
|
Hunk_Alloc(0);
|
|
hunktotal = hunkend - hunkstart;
|
|
|
|
Cache_Alloc (&mod->cache, hunktotal, loadname);
|
|
mod->type = mod_alias;
|
|
if (!mod->cache.data)
|
|
{
|
|
Hunk_FreeToLowMark (hunkstart);
|
|
return;
|
|
}
|
|
memcpy (mod->cache.data, root, hunktotal);
|
|
|
|
Hunk_FreeToLowMark (hunkstart);
|
|
}
|
|
|
|
#endif
|
|
#endif
|