Inside3D!
     

Extending Quake Limits - Part 3: Alias Models

 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> Programming Tutorials
View previous topic :: View next topic  
Author Message
mh



Joined: 12 Jan 2008
Posts: 909

PostPosted: Fri Jan 08, 2010 11:13 pm    Post subject: Extending Quake Limits - Part 3: Alias Models Reply with quote

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
View user's profile Send private message Visit poster's website
mh



Joined: 12 Jan 2008
Posts: 909

PostPosted: Sat Jan 09, 2010 12:36 am    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Sat Jan 09, 2010 2:07 am    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
mh



Joined: 12 Jan 2008
Posts: 909

PostPosted: Sat Jan 09, 2010 2:20 am    Post subject: Reply with quote

I set numverts to numtris * 3 in the loader so it's actually correct going in there. Wink
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines
Back to top
View user's profile Send private message Visit poster's website
reckless



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

PostPosted: Sat Jan 30, 2010 2:26 am    Post subject: Reply with quote

theres a freshly compiled standard gltochris with these changes on my ftp server.

works like a champ good job mh Wink
Back to top
View user's profile Send private message
reckless



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

PostPosted: Sat Jan 30, 2010 10:21 pm    Post subject: Reply with quote

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 Sad

i tried adding in the rest by studying qip quake and others but somethings a mess Laughing

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 Wink
Back to top
View user's profile Send private message
JasonX



Joined: 21 Apr 2009
Posts: 92

PostPosted: Tue Feb 23, 2010 12:36 am    Post subject: Reply with quote

Do you have any news on the software version of this? Rolling Eyes
Back to top
View user's profile Send private message
mh



Joined: 12 Jan 2008
Posts: 909

PostPosted: Tue Feb 23, 2010 10:25 am    Post subject: Reply with quote

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
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    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