Inside3D!
     

Code dump - Fixed GLQuake Underwater Warp

 
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: Tue Jul 06, 2010 9:23 pm    Post subject: Code dump - Fixed GLQuake Underwater Warp Reply with quote

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!!! Wink

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


Code:
   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:
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:
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 ();
}

_________________
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