Forum

Code dump - Fixed GLQuake Underwater Warp

Post tutorials on how to do certain tasks within game or engine code here.

Moderator: InsideQC Admins

Code dump - Fixed GLQuake Underwater Warp

Postby mh » Tue Jul 06, 2010 9:23 pm

Make of this what you will. There's likely a lot still to be worked out - correct operation with different viewsizes and optimization of those sin functions with a lookup table springs to mind.

Have to give credit to metlslime here; the warp functions have their ultimate source in his framebuffer water texture update code. They've come a long and winding road since then, but that's where they began. Elements of it also came from DarkPlaces' skysphere code (OpenGL geek pun not intended but it's actually quite appropriate...)

If you don't know what to do with this you probably shouldn't be trying to do it!!! ;)

Code: Select all
// globals
int   underwatertexture = 0;
int underwatertexturew = 0;
int underwatertextureh = 0;


Code: Select all
   if (r_viewleaf->contents == CONTENTS_EMPTY ||
      r_viewleaf->contents == CONTENTS_SKY ||
      r_viewleaf->contents == CONTENTS_SOLID)
   {
      x = r_refdef.vrect.x * glwidth / vid.width;
      x2 = (r_refdef.vrect.x + r_refdef.vrect.width) * glwidth / vid.width;
      y = (vid.height - r_refdef.vrect.y) * glheight / vid.height;
      y2 = (vid.height - (r_refdef.vrect.y + r_refdef.vrect.height)) * glheight / vid.height;

      // fudge around because of frac screen scale
      if (x > 0) x--;
      if (x2 < glwidth) x2++;
      if (y2 < 0) y2--;
      if (y < glheight) y++;

      w = x2 - x;
      h = y - y2;
   }
   else
   {
      glx = gly = 0;
      x = 0;
      y2 = sb_lines;
      w = underwatertexturew;
      h = underwatertextureh;
   }

   glViewport (glx + x, gly + y2, w, h);


Code: Select all
void R_BeginUnderwaterWarp (void)
{
   static int oldwidth = -1;
   static int oldheight = -1;

   if (r_viewleaf->contents == CONTENTS_EMPTY ||
      r_viewleaf->contents == CONTENTS_SKY ||
      r_viewleaf->contents == CONTENTS_SOLID) return;

   // this will create the texture for us the first time we go underwater, which will give a temporary stall.
   // to avoid that, we should pre-create the texture and then just check for size changes.  it's just done this
   // way for simplicity and demonstration purposes.
   if (!underwatertexture || oldwidth != glwidth || oldheight != glheight)
   {
      int maxsize;

      // GLQuake doesn't get the max texture size correctly so we need to get it here
      // you can remove this part if you've modded your engine to get the correct size,
      // but you should still check the size of the underwater update texture against it.
      glGetIntegerv (GL_MAX_TEXTURE_SIZE, &maxsize);

      if (!underwatertexture)
      {
         underwatertexture = texture_extension_number;
         texture_extension_number++;
      }

      // pick a power of 2 equal to or above the screen res
      for (underwatertexturew = 1; underwatertexturew < glwidth; underwatertexturew <<= 1);
      for (underwatertextureh = 1; underwatertextureh < glheight; underwatertextureh <<= 1);

      // take it down one level to save fillrate
      // (we probably don't need to do this if we blend the texture with the polyblend colour)
      underwatertexturew >>= 1;
      underwatertextureh >>= 1;

      // clamp to max supported size
      if (underwatertexturew > maxsize) underwatertexturew = maxsize;
      if (underwatertextureh > maxsize) underwatertextureh = maxsize;

      // create the texture with no pixels
      // http://www.opengl.org/sdk/docs/man/xhtml/glTexImage2D.xml
      // In GL version 1.1 or greater, data may be a null pointer.  In this case, texture memory is allocated to
      // accommodate a texture of width width and height height.  You can then download subtextures to initialize
      // this texture memory.
      GL_Bind (underwatertexture);
      glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, underwatertexturew, underwatertextureh, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
      glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

      // store back
      oldwidth = glwidth;
      oldheight = glheight;
   }
}


#define WARP_TESS   16

typedef struct warpverts_s
{
   float xy[2];
   float st[2];
} warpverts_t;

warpverts_t *warpverts = NULL;
unsigned short *warpindexes = NULL;

void R_EndUnderwaterWarp (void)
{
   int x, y;
   warpverts_t *dst;
   float hscale;
   float textessw, textessh;
   float invtess;
   float rdt;

   // this can run when we're disconnected so make sure it doesn't as viewleaf/etc will be invalid
   // see GL_Set2D in gl_draw.c
   if (!r_viewleaf) return;
   if (cls.state != ca_connected) return;

   if (r_viewleaf->contents == CONTENTS_EMPTY ||
      r_viewleaf->contents == CONTENTS_SKY ||
      r_viewleaf->contents == CONTENTS_SOLID) return;

   // sanity
   if (!underwatertexture) return;

   // copy back from framebuffer to texture
   GL_Bind (underwatertexture);
   glCopyTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, 0, sb_lines, underwatertexturew, underwatertextureh);

   // we need to generate the actual vertex data each frame as the viewsize might change
   if (!warpverts) warpverts = (warpverts_t *) malloc ((WARP_TESS + 1) * (WARP_TESS + 1) * sizeof (warpverts_t));

   // indexes never change and just need to be generated once
   if (!warpindexes)
   {
      int i;
      int ndx;

      warpindexes = (unsigned short *) malloc (WARP_TESS * WARP_TESS * 6 * sizeof (unsigned short));

      for (y = 0, ndx = 0, i = 0; y < WARP_TESS; y++, i++)
      {
         for (x = 0; x < WARP_TESS; x++, i++, ndx += 6)
         {
            warpindexes[ndx + 0] = i;
            warpindexes[ndx + 1] = i + 1;
            warpindexes[ndx + 2] = i + WARP_TESS + 2;
            warpindexes[ndx + 3] = i;
            warpindexes[ndx + 4] = i + WARP_TESS + 2;
            warpindexes[ndx + 5] = i + WARP_TESS + 1;
         }
      }
   }

   hscale = (float) (vid.height - sb_lines) / (float) vid.height;
   textessw = 32.0f;
   textessh = 32.0f * hscale;
   invtess = 1.0f / WARP_TESS;
   rdt = cl.time * 2.0f;

   for (y = 0, dst = warpverts; y <= WARP_TESS; y++)
   {
      for (x = 0; x <= WARP_TESS; x++, dst++)
      {
         float s = invtess * x * textessw;
         float t = (invtess * y) * textessh;

         dst->xy[0] = (float) (x * vid.width / WARP_TESS);
         dst->xy[1] = (float) ((y * vid.height / WARP_TESS) * hscale);

         if (x == 0 || x == WARP_TESS)
            dst->st[0] = (float) dst->xy[0] / (float) vid.width;
         else dst->st[0] = (s + sin (t + rdt) * 0.125f) * 0.03125f;

         if (y == 0 || y == WARP_TESS)
            dst->st[1] = (float) dst->xy[1] / (float) vid.height;
         else dst->st[1] = (t + sin (s + rdt) * 0.125f) * 0.03125f;

         dst->xy[1] += sb_lines;
      }
   }

   glMatrixMode (GL_PROJECTION);
   glPushMatrix ();
   glLoadIdentity ();
   glOrtho (0, vid.width, 0, vid.height, -99999, 99999);

   // triangle strip weenies can go to hell
   glEnableClientState (GL_VERTEX_ARRAY);
   glVertexPointer (2, GL_FLOAT, sizeof (warpverts_t), warpverts->xy);

   glEnableClientState (GL_TEXTURE_COORD_ARRAY);
   glTexCoordPointer (2, GL_FLOAT, sizeof (warpverts_t), warpverts->st);

   glDrawElements (GL_TRIANGLES, WARP_TESS * WARP_TESS * 6, GL_UNSIGNED_SHORT, warpindexes);

   glDisableClientState (GL_VERTEX_ARRAY);
   glDisableClientState (GL_VERTEX_ARRAY);

   glPopMatrix ();
   glMatrixMode (GL_MODELVIEW);
}


/*
================
R_RenderScene

r_refdef must be set before the first call
================
*/
void R_RenderScene (void)
{
   R_SetupFrame ();
   R_SetFrustum ();
   R_BeginUnderwaterWarp ();
   R_SetupGL ();
   R_MarkLeaves ();   // done here so we know if we're in water
   R_DrawWorld ();      // adds static entities to the list
   S_ExtraUpdate ();   // don't let sound get messed up if going slow
   R_DrawEntitiesOnList ();

   R_RenderDlights ();
   R_DrawParticles ();
#ifdef GLTEST
   Test_Draw ();
#endif
}


Code: Select all
void GL_Set2D (void)
{
   glViewport (glx, gly, glwidth, glheight);
   glMatrixMode (GL_PROJECTION);
   glLoadIdentity ();
   glOrtho (0, vid.width, vid.height, 0, -99999, 99999);
   glMatrixMode (GL_MODELVIEW);
   glLoadIdentity ();
   glDisable (GL_DEPTH_TEST);
   glDisable (GL_CULL_FACE);
   glDisable (GL_BLEND);
   glEnable (GL_ALPHA_TEST);
   //   glDisable (GL_ALPHA_TEST);
   glColor4f (1, 1, 1, 1);
   R_EndUnderwaterWarp ();
}
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Return to Programming Tutorials

Who is online

Users browsing this forum: No registered users and 1 guest