fteqw/engine/d3d/d3d_mesh.c

697 lines
18 KiB
C

#include "quakedef.h"
#ifdef D3DQUAKE
#include "d3dquake.h"
#include "com_mesh.h"
extern cvar_t r_fullbrightSkins;
extern cvar_t r_vertexdlights;
typedef struct {
float x, y, z;
float s, t;
} meshvert_t;
typedef struct {
float x, y, z;
unsigned int colour;
float s, t;
} meshcolouredvert_t;
void D3D_DrawMesh(mesh_t *mesh)
{
pD3DDev->lpVtbl->SetTextureStageState(pD3DDev, 0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pD3DDev->lpVtbl->SetTextureStageState(pD3DDev, 0, D3DTSS_COLORARG2, D3DTA_CURRENT);
pD3DDev->lpVtbl->SetTextureStageState(pD3DDev, 0, D3DTSS_COLOROP, D3DTOP_MODULATE);
{
int v;
vec3_t *xyz;
byte_vec4_t *colour;
vec2_t *wm;
xyz = mesh->xyz_array;
wm = mesh->st_array;
colour = mesh->colors_array;
if (colour)
{
meshcolouredvert_t *meshvert = alloca(sizeof(meshcolouredvert_t)*mesh->numvertexes);
for (v = 0; v < mesh->numvertexes; v++, xyz++, wm++, colour++)
{
meshvert[v].x = (*xyz)[0];
meshvert[v].y = (*xyz)[1];
meshvert[v].z = (*xyz)[2];
meshvert[v].colour = *(unsigned int*)colour;
meshvert[v].s = (*wm)[0];
meshvert[v].t = (*wm)[1];
}
pD3DDev->lpVtbl->DrawIndexedPrimitive(pD3DDev, D3DPT_TRIANGLELIST, D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1, meshvert, mesh->numvertexes, mesh->indexes, mesh->numindexes, 0);
}
else
{
meshvert_t *meshvert = alloca(sizeof(meshvert_t)*mesh->numvertexes);
for (v = 0; v < mesh->numvertexes; v++, xyz++, wm++)
{
meshvert[v].x = (*xyz)[0];
meshvert[v].y = (*xyz)[1];
meshvert[v].z = (*xyz)[2];
meshvert[v].s = (*wm)[0];
meshvert[v].t = (*wm)[1];
}
pD3DDev->lpVtbl->DrawIndexedPrimitive(pD3DDev, D3DPT_TRIANGLELIST, D3DFVF_XYZ|D3DFVF_TEX1, meshvert, mesh->numvertexes, mesh->indexes, mesh->numindexes, 0);
}
}
}
hashtable_t skincolourmapped;
void d3d_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 *D3D_ChooseSkin(galiasinfo_t *inf, char *modelname, int surfnum, entity_t *e)
{
galiasskin_t *skins;
galiastexnum_t *texnums;
int frame;
int tc, bc;
int local;
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 && cl.spectator)
{
local = Cam_TrackNum(0);
if (local < 0)
local = cl.playernum[0];
}
else
local = cl.playernum[0];
if (cl.teamplay && !strcmp(e->scoreboard->team, cl.players[local].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;
char hashname[512];
cc = (tc<<4)|bc;
if (e->scoreboard && e->scoreboard->skin && !gl_nocolors.value)
{
snprintf(hashname, sizeof(hashname), "%s$%s$%i", modelname, e->scoreboard->skin->name, surfnum);
skinname = hashname;
}
else if (surfnum)
{
snprintf(hashname, sizeof(hashname), "%s$%i", modelname, surfnum);
skinname = hashname;
}
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;
}
}
if (!inf->numskins)
{
skins = NULL;
texnums = NULL;
}
else
{
skins = (galiasskin_t*)((char *)inf + inf->ofsskins);
if (!skins->texnums)
{
skins = NULL;
texnums = NULL;
}
else
{
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;
if (!texnums)
{ //load just the skin (q2)
/* if (e->scoreboard && e->scoreboard->skin)
{
if (cls.protocol == CP_QUAKE2)
{
original = Skin_Cache32(e->scoreboard->skin);
if (original)
{
inwidth = e->scoreboard->skin->width;
inheight = e->scoreboard->skin->height;
cm->texnum.base = cm->texnum.fullbright = GL_LoadTexture32(e->scoreboard->skin->name, inwidth, inheight, (unsigned int*)original, true, false);
return &cm->texnum;
}
}
else
{
original = Skin_Cache8(e->scoreboard->skin);
if (original)
{
inwidth = e->scoreboard->skin->width;
inheight = e->scoreboard->skin->height;
cm->texnum.base = cm->texnum.fullbright = GL_LoadTexture(e->scoreboard->skin->name, inwidth, inheight, original, true, false);
return &cm->texnum;
}
}
cm->texnum.base = Mod_LoadHiResTexture(e->scoreboard->skin->name, "skins", true, false, true);
return &cm->texnum;
}
*/
return NULL;
}
cm->texnum.bump = texnums[cm->skinnum].bump; //can't colour bumpmapping
if (cls.protocol != CP_QUAKE2 && ((!texnums || !strcmp(modelname, "progs/player.mdl")) && 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;
inheight = 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;
if (gl_max_size.value <= 0)
gl_max_size.value = 512;
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*inheight/scaled_height);
frac = fracstep >> 1;
for (j=0 ; j<scaled_width ; j+=4)
{
out[j] = translate32[inrow[frac>>16]] | 0xff000000;
frac += fracstep;
out[j+1] = translate32[inrow[frac>>16]] | 0xff000000;
frac += fracstep;
out[j+2] = translate32[inrow[frac>>16]] | 0xff000000;
frac += fracstep;
out[j+3] = translate32[inrow[frac>>16]] | 0xff000000;
frac += fracstep;
}
}
texnums->base = D3D_LoadTexture_32 ("", pixels, scaled_width, scaled_height, 0);
/* 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*inheight/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 (!inf->numskins || !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;
}
}
if (!inf->numskins)
return NULL;
skins = (galiasskin_t*)((char *)inf + inf->ofsskins);
if (e->skinnum >= 0 && e->skinnum < inf->numskins)
skins += e->skinnum;
else
{
Con_DPrintf("Skin number out of range\n");
if (!inf->numskins)
return NULL;
}
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;
}
extern vec3_t shadevector;
extern vec3_t ambientlight;
extern vec3_t shadelight;
static void LotsOfLightDirectionHacks(entity_t *e, model_t *m, vec3_t lightaxis[3])
{
int i;
vec3_t dist;
float add;
qboolean nolightdir;
vec3_t lightdir;
if (!(r_refdef.flags & Q2RDF_NOWORLDMODEL))
{
if (e->flags & Q2RF_WEAPONMODEL)
cl.worldmodel->funcs.LightPointValues(cl.worldmodel, r_refdef.vieworg, shadelight, ambientlight, lightdir);
else
cl.worldmodel->funcs.LightPointValues(cl.worldmodel, 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<dlights_running ; 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
nolightdir = false;
if (m->engineflags & MDLF_PLAYER)
{
float fb = r_fullbrightSkins.value;
if (fb > cls.allow_fbskins)
fb = cls.allow_fbskins;
if (fb < 0)
fb = 0;
if (fb)
{
extern cvar_t r_fb_models;
if (fb >= 1 && r_fb_models.value)
{
ambientlight[0] = ambientlight[1] = ambientlight[2] = 4096;
shadelight[0] = shadelight[1] = shadelight[2] = 4096;
nolightdir = true;
}
else
{
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;
}
}
if (m->engineflags & MDLF_FLAME)
{
shadelight[0] = shadelight[1] = shadelight[2] = 4096;
ambientlight[0] = ambientlight[1] = ambientlight[2] = 4096;
nolightdir = true;
}
else
{
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;
}
if ((e->drawflags & MLS_MASKIN) == MLS_FULLBRIGHT || (e->flags & Q2RF_FULLBRIGHT))
{
shadelight[0] = shadelight[1] = shadelight[2] = 255;
ambientlight[0] = ambientlight[1] = ambientlight[2] = 0;
nolightdir = true;
}
//#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]);
if (e->flags & Q2RF_WEAPONMODEL)
{
vec3_t temp;
temp[0] = DotProduct(shadevector, vpn);
temp[1] = DotProduct(shadevector, vright);
temp[2] = DotProduct(shadevector, vup);
VectorCopy(temp, shadevector);
}
VectorNormalize(shadevector);
VectorCopy(shadevector, lightaxis[2]);
VectorVectors(lightaxis[2], lightaxis[1], lightaxis[0]);
VectorInverse(lightaxis[1]);
}
if (e->flags & Q2RF_GLOW)
{
shadelight[0] += sin(cl.time)*0.25;
shadelight[1] += sin(cl.time)*0.25;
shadelight[2] += sin(cl.time)*0.25;
}
//d3d is bgra
//ogl is rgba
//so switch em and use the gl code
add = shadelight[0];
shadelight[0] = shadelight[2];
shadelight[2] = add;
add = ambientlight[0];
ambientlight[0] = ambientlight[2];
ambientlight[2] = add;
}
qboolean R_GAliasBuildMesh(mesh_t *mesh, galiasinfo_t *inf, int frame1, int frame2, float lerp, float alpha, float fg1time, float fg2time, qboolean nolightdir);
//draws currententity
void D3D_DrawAliasModel(void)
{
mesh_t mesh;
extern entity_t *currententity;
entity_t *e = currententity;
galiasinfo_t *inf;
model_t *m;
galiastexnum_t *skin;
int i;
if (r_secondaryview && e->flags & Q2RF_WEAPONMODEL)
return;
{
extern int cl_playerindex;
if (e->scoreboard && e->model == cl.model_precache[cl_playerindex])
{
m = e->scoreboard->model;
if (!m || m->type != mod_alias)
m = e->model;
}
else
m = e->model;
}
if (!(e->flags & Q2RF_WEAPONMODEL))
if (R_CullEntityBox (e, m->mins, m->maxs))
return;
inf = GLMod_Extradata (m);
if (!inf)
return;
LotsOfLightDirectionHacks(e, m, mesh.lightaxis);
{
float matrix[16];
if (e->flags & Q2RF_WEAPONMODEL && r_refdef.currentplayernum>=0)
{ //view weapons need to be rotated onto the screen first
float view[16];
float ent[16];
Matrix4_ModelMatrixFromAxis(view, cl.viewent[r_refdef.currentplayernum].axis[0], cl.viewent[r_refdef.currentplayernum].axis[1], cl.viewent[r_refdef.currentplayernum].axis[2], cl.viewent[r_refdef.currentplayernum].origin);
Matrix4_ModelMatrixFromAxis(ent, e->axis[0], e->axis[1], e->axis[2], e->origin);
Matrix4_Multiply(view, ent, matrix);
}
else
{
Matrix4_ModelMatrixFromAxis(matrix, e->axis[0], e->axis[1], e->axis[2], e->origin);
}
pD3DDev->lpVtbl->SetTransform(pD3DDev, D3DTRANSFORMSTATE_WORLD, (D3DMATRIX*)matrix);
}
pD3DDev->lpVtbl->SetTextureStageState(pD3DDev, 0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pD3DDev->lpVtbl->SetTextureStageState(pD3DDev, 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
pD3DDev->lpVtbl->SetTextureStageState(pD3DDev, 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
if (e->flags & Q2RF_DEPTHHACK)
{ //apply the depth hack to stop things from poking into walls.
//(basically moving it closer to the screen)
D3DVIEWPORT7 viewport;
pD3DDev->lpVtbl->GetViewport(pD3DDev, &viewport);
viewport.dvMinZ = 0;
viewport.dvMaxZ = 0.3;
pD3DDev->lpVtbl->SetViewport(pD3DDev, &viewport);
}
for(i = 0;; i++)
{
R_GAliasBuildMesh(&mesh, inf, e->frame, e->oldframe, e->lerpfrac, e->shaderRGBAf[3], e->frame1time, e->frame2time, 0);
skin = D3D_ChooseSkin(inf, m->name, e->skinnum, e);
if (!skin)
pD3DDev->lpVtbl->SetTexture(pD3DDev, 0, NULL);
else
pD3DDev->lpVtbl->SetTexture(pD3DDev, 0, skin->base);
D3D_DrawMesh(&mesh);
if (inf->nextsurf == 0)
break;
inf = (galiasinfo_t*)((char*)inf + inf->nextsurf);
}
if (e->flags & Q2RF_DEPTHHACK)
{
D3DVIEWPORT7 viewport;
pD3DDev->lpVtbl->GetViewport(pD3DDev, &viewport);
viewport.dvMinZ = 0;
viewport.dvMaxZ = 1;
pD3DDev->lpVtbl->SetViewport(pD3DDev, &viewport);
}
{
float matrix[16];
Matrix4_Identity(matrix);
pD3DDev->lpVtbl->SetTransform(pD3DDev, D3DTRANSFORMSTATE_WORLD, (D3DMATRIX*)matrix);
}
}
#endif