Index | FAQ | Search | Memberlist | Usergroups | Profile | Log in to check your private messages | Log in | Register

Extending Quake Limits - Part 3: Alias Models

| Inside3d Forums Forum Index -> Programming Tutorials

View previous topic :: View next topic
Author | Message
--- | ---
mh Joined: 12 Jan 2008 Posts: 909 |

Posted: Fri Jan 08, 2010 11:13 pm Post subject: Extending Quake Limits - Part 3: Alias Models |

This one's for GLQuake only I'm afraid; I might study the software Quake code and see if something can be done there too at a later stage. Some true evil lives in the alias model code: the format is horrible, the loader is disgusting, the renderer is awkward to work with. Every time you use the alias model code a homeless baby puppy dog dies somewhere.
Concept
We're going to accomplish a few things here. Firstly we're going to remove all limits on the number of vertexes and triangles in an alias model (unfortunately the number of frames requires a protocol change). Secondly we're going to give ourselves a much cleaner in-memory format (the on-disk format is beyond redemption I'm afraid). Thirdly we're going to have cleaner (and therefore more maintainable) elements in the loader. Fourthly we'll be doing the same for the renderer. Fifthly we're going to save on memory by only storing a single copy of each vertex.
For the sake of clarity and keeping things short I've omitted bbox correction, anything to do with coloured light or interpolation, and shadows. It should be obvious what's needed from reading this code.
I also need to give a heads-up that this is not the most efficient way to render an alias model (although it might be more efficient than GLQuake, depending on your hardware). The most efficient way involves vertex arrays and we might get a look at that as an addendum to this tutorial at some time. There's enough going on here as it is already!
The Code
Quite a bit of code here, so let's take it one step at a time. I might comment briefly after each one if the changes made require it.
Firstly we're going to gl_model.h and replacing our entire aliashdr_t struct with these two:

Code:
typedef struct aliasmesh_s { float s; float t; unsigned short vertindex;
} aliasmesh_t;
typedef struct aliashdr_s
{
vec3_t scale;
vec3_t scale_origin;
float boundingradius;
synctype_t synctype;
int flags;
float size;
int nummeshframes;
int numtris;
int numverts;
int vertsperframe;
int numframes;
int meshverts;
int vertexes;
int framevertexsize;
int skinwidth;
int skinheight;
int numskins;
int gl_texturenum[MAX_SKINS][4];
int texels[MAX_SKINS]; // only for player skins
maliasframedesc_t frames[1];
} aliashdr_t;

This is our new in-memory alias model format. Before we can get to use it we need to load the alias model, so in gl_model.c we go to the "ALIAS MODELS" comment header and replace all the horrible global variables with this:

Code:
aliashdr_t *pheader; void Mod_LoadFrameVerts (trivertx_t *verts) { int i; int mark = Hunk_LowMark ();
trivertx_t *vertexes = (trivertx_t *) Hunk_Alloc (pheader->vertsperframe * sizeof (trivertx_t));
// this should only be done once and stores an offset to the first set of verts for the frame
if (!pheader->vertexes) pheader->vertexes = (int) vertexes - (int) pheader;
for (i = 0; i < pheader->vertsperframe; i++, vertexes++, verts++)
{
vertexes->lightnormalindex = verts->lightnormalindex;
vertexes->v[0] = verts->v[0];
vertexes->v[1] = verts->v[1];
vertexes->v[2] = verts->v[2];
}
// hunk tags will mean that pheader->vertsperframe * sizeof (trivertx_t) is invalid for this
// we only need to do this once but no harm in doing it every frame
pheader->framevertexsize = Hunk_LowMark () - mark;
pheader->nummeshframes++;
}

All this is doing is loading in each vertex individually and one-time only. We're also setting some info in the header - and note the trap I fell into while writing this!
Here's our new Mod_LoadAliasFrame:

Code:
void *Mod_LoadAliasFrame (void *pin, maliasframedesc_t *frame) { int i; trivertx_t *verts; daliasframe_t *pdaliasframe;
pdaliasframe = (daliasframe_t *) pin;
strncpy (frame->name, pdaliasframe->name, 16);
frame->firstpose = pheader->nummeshframes;
frame->numposes = 1;
for (i = 0; i < 3; i++)
{
frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i];
frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i];
}
verts = (trivertx_t *) (pdaliasframe + 1);
// load the frame vertexes
Mod_LoadFrameVerts (verts);
verts += pheader->vertsperframe;
return (void *) verts;
}

And our new Mod_LoadAliasGroup:

Code:
void *Mod_LoadAliasGroup (void *pin, maliasframedesc_t *frame) { daliasgroup_t *pingroup; int i, numframes; daliasinterval_t *pin_intervals;
void *ptemp;
pingroup = (daliasgroup_t *) pin;
numframes = LittleLong (pingroup->numframes);
frame->firstpose = pheader->nummeshframes;
frame->numposes = numframes;
for (i = 0; i < 3; i++)
{
frame->bboxmin.v[i] = pingroup->bboxmin.v[i];
frame->bboxmax.v[i] = pingroup->bboxmax.v[i];
}
pin_intervals = (daliasinterval_t *) (pingroup + 1);
frame->interval = LittleFloat (pin_intervals->interval);
pin_intervals += numframes;
ptemp = (void *) pin_intervals;
for (i = 0; i < numframes; i++)
{
Mod_LoadFrameVerts ((trivertx_t *) ((daliasframe_t *) ptemp + 1));
ptemp = (trivertx_t *) ((daliasframe_t *) ptemp + 1) + pheader->vertsperframe;
}
return ptemp;
}

Nothing much different here aside from calling Mod_LoadFrameVerts where required. Now we need to load the model itself, and as the format has changed the old Mod_LoadAliasModel also needs a few small changes. Rather than going through it line-by-line which would take forever, I'll just give the full function:

Code:
void Mod_LoadAliasModel (model_t *mod, void *buffer) { int i, j; mdl_t *pinmodel; stvert_t *pinstverts;
dtriangle_t *pintriangles;
int version, numframes, numskins;
int size;
daliasframetype_t *pframetype;
daliasskintype_t *pskintype;
int start, end, total;
start = Hunk_LowMark ();
pinmodel = (mdl_t *) buffer;
version = LittleLong (pinmodel->version);
if (version != ALIAS_VERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION);
// allocate space for a working header
pheader = Hunk_AllocName (sizeof (aliashdr_t) + LittleLong (pinmodel->numframes) * sizeof (maliasframedesc_t), loadname);
mod->flags = LittleLong (pinmodel->flags);
mod->type = mod_alias;
// endian-adjust and copy the data, starting with the alias model header
pheader->boundingradius = LittleFloat (pinmodel->boundingradius);
pheader->numskins = LittleLong (pinmodel->numskins);
pheader->skinwidth = LittleLong (pinmodel->skinwidth);
pheader->skinheight = LittleLong (pinmodel->skinheight);
pheader->vertsperframe = LittleLong (pinmodel->numverts);
pheader->numtris = LittleLong (pinmodel->numtris);
pheader->numframes = LittleLong (pinmodel->numframes);
pheader->numverts = pheader->numtris * 3;
// validate the setup
// Sys_Error seems a little harsh here...
if (pheader->numframes < 1) Host_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", pheader->numframes);
if (pheader->numtris <= 0) Host_Error ("model %s has no triangles", mod->name);
if (pheader->vertsperframe <= 0) Host_Error ("model %s has no vertices", mod->name);
pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO;
mod->synctype = (synctype_t) LittleLong (pinmodel->synctype);
mod->numframes = pheader->numframes;
for (i = 0; i < 3; i++)
{
pheader->scale[i] = LittleFloat (pinmodel->scale[i]);
pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]);
}
// load the skins
pskintype = (daliasskintype_t *) &pinmodel[1];
pskintype = Mod_LoadAllSkins (pheader->numskins, pskintype);
// load base s and t vertices
pinstverts = (stvert_t *) pskintype;
for (i = 0; i < pheader->vertsperframe; i++)
{
pinstverts[i].onseam = LittleLong (pinstverts[i].onseam);
pinstverts[i].s = LittleLong (pinstverts[i].s);
pinstverts[i].t = LittleLong (pinstverts[i].t);
}
// load triangle lists
pintriangles = (dtriangle_t *) &pinstverts[pheader->vertsperframe];
for (i = 0; i < pheader->numtris; i++)
{
pintriangles[i].facesfront = LittleLong (pintriangles[i].facesfront);
pintriangles[i].vertindex[0] = LittleLong (pintriangles[i].vertindex[0]);
pintriangles[i].vertindex[1] = LittleLong (pintriangles[i].vertindex[1]);
pintriangles[i].vertindex[2] = LittleLong (pintriangles[i].vertindex[2]);
}
// load the frames
pheader->nummeshframes = 0;
pheader->vertexes = 0;
pheader->framevertexsize = 0;
pframetype = (daliasframetype_t *) &pintriangles[pheader->numtris];
for (i = 0; i < pheader->numframes; i++)
{
aliasframetype_t frametype = LittleLong (pframetype->type);
if (frametype == ALIAS_SINGLE)
pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]);
else pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]);
}
// FIXME: do this right
mod->mins[0] = mod->mins[1] = mod->mins[2] = -16;
mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16;
// build the draw lists
GL_MakeAliasModelDisplayLists (pinstverts, pintriangles);
// move the complete, relocatable alias model to the cache
end = Hunk_LowMark ();
total = end - start;
Cache_Alloc (&mod->cache, total, loadname);
if (!mod->cache.data) return;
memcpy (mod->cache.data, pheader, total);
Hunk_FreeToLowMark (start);
}

The real changes here are that instead of filling temporary buffers with stverts and triangles we're taking them directly from the disk version. Note also that we're feeding them into GL_MakeAliasModelDisplayLists, and setting up a few extra alias header members along the way.
Speaking of GL_MakeAliasModelDisplayLists, here's the entirety of our new gl_mesh.c file:

Code:
#include "quakedef.h" void GL_MakeAliasModelDisplayLists (stvert_t *stverts, dtriangle_t *triangles) { int i, j; aliasmesh_t *mesh = (aliasmesh_t *) Hunk_Alloc (sizeof (aliasmesh_t) * pheader->numverts);
pheader->meshverts = (int) mesh - (int) pheader;
for (i = 0; i < pheader->numtris; i++)
{
for (j = 0; j < 3; j++, mesh++)
{
// emit s/t coords into the vert
mesh->s = stverts[triangles[i].vertindex[j]].s;
mesh->t = stverts[triangles[i].vertindex[j]].t;
// check for back side and adjust texcoord s
if (!triangles[i].facesfront && stverts[triangles[i].vertindex[j]].onseam) mesh->s += pheader->skinwidth / 2;
// final s and t
mesh->s = (mesh->s + 0.5) / pheader->skinwidth;
mesh->t = (mesh->t + 0.5) / pheader->skinheight;
// index into hdr->vertexes
mesh->vertindex = triangles[i].vertindex[j];
}
}
}

At this stage we're definitely moving far away from the original code and getting a lot nicer. The in-memory format is now complete, so it's time to put them on-screen. This is all you need:

Code:
void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum) { int i; float l; aliasmesh_t *mesh = (aliasmesh_t *) ((byte *) paliashdr + paliashdr->meshverts);
trivertx_t *vert = (trivertx_t *) ((byte *) paliashdr + paliashdr->vertexes + paliashdr->framevertexsize * posenum);
lastposenum = posenum;
glBegin (GL_TRIANGLES);
for (i = 0; i < paliashdr->numverts; i++, mesh++)
{
trivertx_t *trivert = &vert[mesh->vertindex];
float l = shadedots[trivert->lightnormalindex] * shadelight;
glTexCoord2f (mesh->s, mesh->t);
glColor3f (l, l, l);
glVertex3f (trivert->v[0], trivert->v[1], trivert->v[2]);
}
glEnd ();
}

And that is roundabout it. If anyone has any specific questions feel free to ask.
Cleaning Up
Nothing really to be done this time, although you may wish to get rid of the defines for the old maximums.
As I mentioned, popping this data into a vertex array would be more efficient, and I'm going to post the code required for that later on.
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

mh Joined: 12 Jan 2008 Posts: 909 |

Posted: Sat Jan 09, 2010 12:36 am Post subject: |

OK, I mentioned something about vertex arrays so here's the changes required to the above code. Using this method we can reduce the number of vertexes in the player model from 1224 to 317. Unfortunately though we're taking a 2 byte overhead on each one of those vertexes, but it's still almost a quarter of the vertex submission as before.
To clear up a misconception about vertex arrays:
If you go by the GLQuake source code you might think that vertex arrays are an extension that you need to check for, make header file changes for, use wglGetProcAddress on, and call the EXT versions of the functions. They're not. Vertex arrays are a core OpenGL 1.1 feature that are required to be supported and you don't need to do any of those things. If you can call glBindTexture without doing those, you can also call glVertexPointer.
So here's our new GL_MakeAliasModelDisplayLists:

Code:
void GL_MakeAliasModelDisplayLists (stvert_t *stverts, dtriangle_t *triangles) { int i, j; aliasmesh_t *hunkmesh; // there can never be more than this number of verts
aliasmesh_t *mesh = (aliasmesh_t *) malloc (sizeof (aliasmesh_t) * pheader->numverts);
// there will always be this number of indexes
unsigned short *indexes = (unsigned short *) Hunk_Alloc (sizeof (unsigned short) * pheader->numtris * 3);
pheader->indexes = (int) indexes - (int) pheader;
pheader->numindexes = 0;
pheader->numverts = 0;
for (i = 0; i < pheader->numtris; i++)
{
for (j = 0; j < 3; j++)
{
int v;
// index into hdr->vertexes
unsigned short vertindex = triangles[i].vertindex[j];
// basic s/t coords
int s = stverts[vertindex].s;
int t = stverts[vertindex].t;
// check for back side and adjust texcoord s
if (!triangles[i].facesfront && stverts[vertindex].onseam) s += pheader->skinwidth / 2;
// see does this vert already exist
for (v = 0; v < pheader->numverts; v++)
{
// it could use the same xyz but have different s and t
if (mesh[v].vertindex == vertindex && (int) mesh[v].st[0] == s && (int) mesh[v].st[1] == t)
{
// exists; emit an index for it
indexes[pheader->numindexes++] = v;
// no need to check any more
break;
}
}
if (v == pheader->numverts)
{
// doesn't exist; emit a new vert and index
indexes[pheader->numindexes++] = pheader->numverts;
mesh[pheader->numverts].vertindex = vertindex;
mesh[pheader->numverts].st[0] = s;
mesh[pheader->numverts++].st[1] = t;
}
}
}
// create a hunk buffer for the final mesh we'll actually use
hunkmesh = (aliasmesh_t *) Hunk_Alloc (sizeof (aliasmesh_t) * pheader->numverts);
pheader->meshverts = (int) hunkmesh - (int) pheader;
// tidy up the verts by calculating final s and t and copying out to the hunk
for (i = 0; i < pheader->numverts; i++)
{
hunkmesh[i].vertindex = mesh[i].vertindex;
hunkmesh[i].st[0] = ((float) mesh[i].st[0] + 0.5f) / (float) pheader->skinwidth;
hunkmesh[i].st[1] = ((float) mesh[i].st[1] + 0.5f) / (float) pheader->skinheight;
}
// don't forget!!!
free (mesh);
}

And here's our new GL_DrawAliasFrame:

Code:
void GL_DrawAliasFrame (aliashdr_t *paliashdr, int posenum) { int i; float l; aliasmesh_t *mesh = (aliasmesh_t *) ((byte *) paliashdr + paliashdr->meshverts);
trivertx_t *vert = (trivertx_t *) ((byte *) paliashdr + paliashdr->vertexes + paliashdr->framevertexsize * posenum);
lastposenum = posenum;
glEnableClientState (GL_VERTEX_ARRAY);
glEnableClientState (GL_COLOR_ARRAY);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
glVertexPointer (3, GL_FLOAT, sizeof (aliasmesh_t), mesh->v);
glColorPointer (3, GL_FLOAT, sizeof (aliasmesh_t), mesh->color);
glTexCoordPointer (2, GL_FLOAT, sizeof (aliasmesh_t), mesh->st);
for (i = 0; i < paliashdr->numverts; i++, mesh++)
{
trivertx_t *trivert = &vert[mesh->vertindex];
mesh->color[0] = mesh->color[1] = mesh->color[2] = shadedots[trivert->lightnormalindex] * shadelight;
mesh->v[0] = trivert->v[0];
mesh->v[1] = trivert->v[1];
mesh->v[2] = trivert->v[2];
}
glDrawElements (GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, (unsigned short *) ((byte *) paliashdr + paliashdr->indexes));
glDisableClientState (GL_VERTEX_ARRAY);
glDisableClientState (GL_COLOR_ARRAY);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
}

And here's the new aliasmesh_t struct:

Code:
typedef struct aliasmesh_s { float v[3]; float color[3]; float st[2];
unsigned short vertindex;
} aliasmesh_t;

And these two get added to aliashdr_t (anywhere you want except after frames!):

Code:
int indexes; int numindexes;

Some points:

- We're using 16 bit indexes here so this reduces the max triangles in an alias model from unlimited to 21845. There's nothing to stop us from using 32 bit indexes, but they're not supported on all hardware and OpenGL might put your code through a software emulation path.
- The method used to check for reused vertexes is a little brute-force, but it shouldn't cause any trouble with loading times.
- numindexes will always be the same as numtris * 3 so you could in theory do away with it.
- Notice once again that I'm using offsets rather than pointers; we can't use pointers here because when the model is moved to the cache it's pointer values will become invalid (they'll still be pointing at the old hunk location).
- I need to malloc a temporary buffer for holding verts because I don't know in adavnce how big it's going to be (I do know it's max possible size though).
- aliasmesh_t has been bloated by 6 floats but it's still more memory-efficient than GLQuake was.

_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

Spike Joined: 05 Nov 2004 Posts: 944 Location: UK |

Posted: Sat Jan 09, 2010 2:07 am Post subject: |

the max verts is actually numverts*2 due to the facesfront thing. that or numtris*3. whichever is the smaller. just numverts is not correct. _________________ What's a signature?

Back to top |

mh Joined: 12 Jan 2008 Posts: 909 |

Posted: Sat Jan 09, 2010 2:20 am Post subject: |

I set numverts to numtris * 3 in the loader so it's actually correct going in there. _________________ DirectQ Engine - New release 1.8.666a, 9th August 2010 MHQuake Blog (General) Direct3D 8 Quake Engines

Back to top |

reckless Joined: 24 Jan 2008 Posts: 390 Location: inside tha debugger |

Posted: Sat Jan 30, 2010 2:26 am Post subject: |

theres a freshly compiled standard gltochris with these changes on my ftp server. works like a champ good job mh

Back to top |

reckless Joined: 24 Jan 2008 Posts: 390 Location: inside tha debugger |

Posted: Sat Jan 30, 2010 10:21 pm Post subject: |

ouch actually ran into one problem after the succes i tried adding lit support to tochris gl port well so far no luck multicolored dots all over and the tut from original qsg site is missing bits and pieces it seems i tried adding in the rest by studying qip quake and others but somethings a mess
also not sure my own hackery is sound so ill leave my modified section of it here comments are velcome.

Code:
/* ============================================================= ALIAS MODELS modified with mh code ;) ============================================================= */
vec3_t shadevector;
vec3_t shadelight;
// precalculated dot products for quantized angles
#define SHADEDOT_QUANT 16
float r_avertexnormal_dots[SHADEDOT_QUANT][256] =
#include "anorm_dots.h"
;
float *shadedots = r_avertexnormal_dots[0];
/*
=============
GL_DrawAliasFrame
Comments ?
=============
*/
void GL_DrawAliasFrame (aliashdr_t *paliashdr, int pose1, int pose2)
{
int i, j;
aliasmesh_t *mesh = (aliasmesh_t *) ((byte *) paliashdr + paliashdr->meshverts);
trivertx_t *vert = (trivertx_t *) ((byte *) paliashdr + paliashdr->vertexes + paliashdr->framevertexsize * pose1);
trivertx_t *lvert = (trivertx_t *) ((byte *) paliashdr + paliashdr->vertexes + paliashdr->framevertexsize * pose2);
glEnableClientState (GL_VERTEX_ARRAY);
glEnableClientState (GL_COLOR_ARRAY);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
glVertexPointer (3, GL_FLOAT, sizeof (aliasmesh_t), mesh->v);
glColorPointer (3, GL_FLOAT, sizeof (aliasmesh_t), mesh->color);
glTexCoordPointer (2, GL_FLOAT, sizeof (aliasmesh_t), mesh->st);
for (i = 0; i < paliashdr->numverts; i++, mesh++)
{
trivertx_t *trivert = &vert[mesh->vertindex];
trivertx_t *ltrivert = &lvert[mesh->vertindex];
for (j=0; j<3; j++)
{
mesh->color[j] = shadedots[trivert->lightnormalindex] + (shadedots[ltrivert->lightnormalindex] - shadedots[trivert->lightnormalindex]) * r_framelerp;
}
bound(0, mesh->color[0] * shadelight[0], 1);
bound(0, mesh->color[1] * shadelight[1], 1);
bound(0, mesh->color[2] * shadelight[2], 1);
mesh->v[0] = (trivert->v[0] + (ltrivert->v[0] - trivert->v[0]) * r_framelerp) * paliashdr->scale[0] + paliashdr->scale_origin[0];
mesh->v[1] = (trivert->v[1] + (ltrivert->v[1] - trivert->v[1]) * r_framelerp) * paliashdr->scale[1] + paliashdr->scale_origin[1];
mesh->v[2] = (trivert->v[2] + (ltrivert->v[2] - trivert->v[2]) * r_framelerp) * paliashdr->scale[2] + paliashdr->scale_origin[2];
}
glDrawElements (GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, (unsigned short *) ((byte *) paliashdr + paliashdr->indexes));
glDisableClientState (GL_VERTEX_ARRAY);
glDisableClientState (GL_COLOR_ARRAY);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
}
/*
=============
GL_DrawAliasShadow
Well ill be damned worked on the first try :P
=============
*/
extern vec3_t lightspot;
void GL_DrawAliasShadow (aliashdr_t *paliashdr, int pose1, int pose2)
{
int i;
vec3_t end;
trace_t tr;
aliasmesh_t *mesh = (aliasmesh_t *) ((byte *) paliashdr + paliashdr->meshverts);
trivertx_t *vert = (trivertx_t *) ((byte *) paliashdr + paliashdr->vertexes + paliashdr->framevertexsize * pose1);
trivertx_t *lvert = (trivertx_t *) ((byte *) paliashdr + paliashdr->vertexes + paliashdr->framevertexsize * pose2);
float height, lheight;
VectorSet (end, currententity->origin[0], currententity->origin[1], currententity->origin[2] - 4096);
tr = CL_TraceLine (currententity->origin, vec3_origin, vec3_origin, end, -1);
if (tr.fraction == 1.0f) return;
lheight = currententity->origin[2] - tr.endpos[2];
height = 0;
glEnableClientState (GL_VERTEX_ARRAY);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
glVertexPointer (3, GL_FLOAT, sizeof (aliasmesh_t), mesh->v);
glTexCoordPointer (2, GL_FLOAT, sizeof (aliasmesh_t), mesh->st);
height = -lheight + 0.1;
for (i = 0; i < paliashdr->numverts; i++, mesh++)
{
trivertx_t *trivert = &vert[mesh->vertindex];
trivertx_t *ltrivert = &lvert[mesh->vertindex];
mesh->v[0] = (trivert->v[0] + (ltrivert->v[0] - trivert->v[0]) * r_framelerp) * paliashdr->scale[0] + paliashdr->scale_origin[0];
mesh->v[1] = (trivert->v[1] + (ltrivert->v[1] - trivert->v[1]) * r_framelerp) * paliashdr->scale[1] + paliashdr->scale_origin[1];
mesh->v[2] = (trivert->v[2] + (ltrivert->v[2] - trivert->v[2]) * r_framelerp) * paliashdr->scale[2] + paliashdr->scale_origin[2];
mesh->v[0] = (mesh->v[0] - shadevector[0]*(mesh->v[2]+lheight));
mesh->v[1] = (mesh->v[1] - shadevector[1]*(mesh->v[2]+lheight));
mesh->v[2] = height;
}
glDrawElements (GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, (unsigned short *) ((byte *) paliashdr + paliashdr->indexes));
glDisableClientState (GL_VERTEX_ARRAY);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
}
/*
=================
R_SetupAliasFrame
=================
*/
void R_SetupAliasFrame (maliasframedesc_t *oldframe, maliasframedesc_t *frame, aliashdr_t *paliashdr)
{
int pose, oldpose, numposes;
float interval;
oldpose = oldframe->firstpose;
numposes = oldframe->numposes;
if (numposes > 1)
{
interval = oldframe->interval;
oldpose += (int)(r_refdef.oldtime / interval) % numposes;
}
pose = frame->firstpose;
numposes = frame->numposes;
if (numposes > 1)
{
interval = frame->interval;
pose += (int)(r_refdef.time / interval) % numposes;
}
GL_DrawAliasFrame (paliashdr, oldpose, pose);
}
/*
=================
R_DrawAliasModel
=================
*/
void R_DrawAliasModel (entity_t *e)
{
int i;
int lnum;
vec3_t dist;
float add;
model_t *clmodel = e->model;
aliashdr_t *paliashdr;
int anim;
maliasframedesc_t *oldframe, *frame;
//
// locate the proper data
//
paliashdr = (aliashdr_t *)Mod_Extradata (clmodel);
if ((e->frame >= paliashdr->numframes) || (e->frame < 0))
{
Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", e->frame);
e->frame = 0;
}
if ((e->oldframe >= paliashdr->numframes) || (e->oldframe < 0))
{
Con_DPrintf ("R_AliasSetupFrame: no such frame %d\n", e->oldframe);
e->oldframe = 0;
}
frame = &paliashdr->frames[e->frame];
oldframe = &paliashdr->frames[e->oldframe];
if (!(e->flags & RF_WEAPONMODEL))
{
if (e->angles[0] || e->angles[1] || e->angles[2])
{
if (R_CullSphere (e->origin, max (oldframe->radius, frame->radius))) return;
}
else
{
vec3_t mins, maxs;
for (i = 0; i < 3; i++)
{
mins[i] = e->origin[i] + min (oldframe->bboxmin.v[i], frame->bboxmin.v[i]);
maxs[i] = e->origin[i] + max (oldframe->bboxmax.v[i], frame->bboxmax.v[i]);
}
if (R_CullBox (mins, maxs)) return;
}
}
VectorCopy (e->origin, r_entorigin);
VectorSubtract (r_origin, r_entorigin, modelorg);
//
// get lighting information
//
if (e->flags & RF_FULLBRIGHT)
{
shadelight[0] = shadelight[1] = shadelight[2] = 255;
}
else
{
if (!(r_refdef.flags & RDF_NOWORLDMODEL))
shadelight[0] = shadelight[1] = shadelight[2] = R_LightPoint (e->origin);
else
shadelight[0] = shadelight[1] = shadelight[2] = 0;
// always give some light
if (e->flags & RF_MINLIGHT)
{
shadelight[0] = shadelight[1] = shadelight[2] = 24;
}
for (lnum=0 ; lnum<r_refdef.num_dlights ; lnum++)
{
VectorSubtract (e->origin, r_refdef.dlights[lnum].origin, dist);
add = r_refdef.dlights[lnum].radius - VectorLength(dist);
if (add > 0)
{
//ZOID models should be affected by dlights as well
shadelight[0] += add * r_refdef.dlights[lnum].color[0];
shadelight[1] += add * r_refdef.dlights[lnum].color[1];
shadelight[2] += add * r_refdef.dlights[lnum].color[2];
}
}
}
// HACK HACK HACK -- no fullbright colors, so make torches full light
if (!strcmp (clmodel->name, "progs/flame2.mdl") || !strcmp (clmodel->name, "progs/flame.mdl") )
{
shadelight[0] = shadelight[1] = shadelight[2] = 255;
}
shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];
VectorScale(shadelight, 1.0f / 200.0f, shadelight);
c_alias_polys += paliashdr->numtris;
//
// draw all the triangles
//
GL_DisableMultitexture();
glPushMatrix ();
R_RotateForEntity (e);
if (e->flags & RF_DEPTHSCALE)
{
glDepthRange (gldepthmin, gldepthmin + 0.3*(gldepthmax-gldepthmin));
}
if (e->model->synctype == ST_RAND)
anim = (int)((r_refdef.time + e->syncbase) * 10) & 3;
else
anim = (int)(r_refdef.time * 10) & 3;
GL_Bind(paliashdr->gl_texturenum[e->skinnum][anim]);
// we can't dynamically colormap textures, so they are cached
// seperately for the players. Heads are just uncolored.
if (e->colormap != vid.colormap && !gl_nocolors.value)
{
i = e->number;
if (i >= 0 && i<=Com_ClientMaxclients())
{
GL_Bind(playertextures + i);
}
}
if (gl_smoothmodels.value)
glShadeModel (GL_SMOOTH);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
if (gl_affinemodels.value)
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
if (!r_lerpmodels.value)
r_framelerp = 1.0f;
else
r_framelerp = bound (0, e->framelerp, 1);
R_SetupAliasFrame (oldframe, frame, paliashdr);
if (e->flags & RF_DEPTHSCALE)
{
glDepthRange (gldepthmin, gldepthmax);
}
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glShadeModel (GL_FLAT);
if (gl_affinemodels.value)
{
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}
glPopMatrix ();
if (r_shadows.value && !(e->flags & RF_NOSHADOW))
{
float an = -e->angles[1]/180*M_PI;
shadevector[0] = cos(an);
shadevector[1] = sin(an);
shadevector[2] = 1;
VectorNormalize (shadevector);
glPushMatrix ();
glTranslatef (e->origin[0], e->origin[1], e->origin[2]);
glRotatef (e->angles[1], 0, 0, 1);
glDisable (GL_TEXTURE_2D);
glEnable (GL_BLEND);
glColor4f (0,0,0,0.5);
GL_DrawAliasShadow (paliashdr, e->oldframe, e->frame);
glEnable (GL_TEXTURE_2D);
glDisable (GL_BLEND);
glColor4f (1,1,1,1);
glPopMatrix ();
}
}

maybe a bugger on my side you newer know

Back to top |

JasonX Joined: 21 Apr 2009 Posts: 92 |

Posted: Tue Feb 23, 2010 12:36 am Post subject: |

Do you have any news on the software version of this?

Back to top |

mh Joined: 12 Jan 2008 Posts: 909 |

Posted: Tue Feb 23, 2010 10:25 am Post subject: |

As in software rendering? Someone who knows more about the software renderer than I do would need to build that; the code is completely different (and large chunks of it in asm). _________________ DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

Display posts from previous:

| Inside3d Forums Forum Index -> Programming Tutorials | All times are GMT
Page 1 of 1

Jump to:

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

Powered by phpBB © 2004 phpBB Group