Announcement

Collapse
No announcement yet.

Z-Fail Sky Sphere

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Z-Fail Sky Sphere

    I posted this over on Inside3D, but I want to make sure it reaches as many people as possible. It's a neat trick, based on a suggestion by Spike on Inside3D, for modifying my old sky sphere tutorial (which I had posted on QuakeSrc.org years ago) so that (1) it doesn't flood the framebuffer with polys that will eventually be depthmasked out, and (2) world polys that are not visible through the old sky are not visible through the sky sphere.

    If you've implemented my old tutorial, you can see (1) by switching off the main world render and loading any map that has sky. You can see (2) by rendering everything as normal and loading e4m7 - just look up at the sky in the start room.

    Credit for the suggestion to Spike, the implementation is my own.

    The basic technique is:

    * Draw regular sky polys with colormask 0.
    * Invert the depth func.
    * Draw the box/sphere/whatever. For a dual layer box/sphere, draw the back layer nearer than the front, to match the inverted depth func.
    * Switch the depth func back to normal.
    * Draw regular sky polys with colormask 0 again.
    * Draw the world.

    Here it is in the start room of e4m7 (with the world render turned off):



    And here's code for a working implementation (based off my previous sky sphere tutorial on QuakeSrc.org, so if you've implemented that you should be able to do this too):

    Code:
    /*
    =================
    R_DrawSkyChain
    =================
    */
    cvar_t r_skyspeed = {"r_skyspeed", "2.5", true};
    
    float rotateBack = 0;
    float rotateFore = 0;
    
    float skytexes[440];
    float skyverts[660];
    
    void R_DrawSkyArrays (int texnum, float bigscale, float smallscale, float rotatefactor)
    {
    	// go to a new matrix
    	glPushMatrix ();
    
    	// center it on the players position, scale it, orient so the poles are unobtrusive,
    	// make it not always at right angles to the player, and rotate it around the poles
    	glTranslatef (r_origin[0], r_origin[1], r_origin[2]);
    	glTranslatef (0, 0, -500);
    	glScalef (bigscale, bigscale, smallscale);
    	glRotatef (-90, 1, 0, 0);
    	glRotatef (-22, 0, 1, 0);
    	glRotatef (rotatefactor, 0, 0, 1);
    
    	// bind the correct texture
    	glBindTexture (GL_TEXTURE_2D, texnum);
    
    	// draw the sphere
    	// (if this barfs on your implementation, just unroll the single call into 10 batches of 22)
    	glDrawArrays (GL_TRIANGLE_STRIP, 0, 220);
    
    	// restore the previous matrix
    	glPopMatrix ();
    }
    
    
    void R_ClipSky (msurface_t *skychain)
    {
    	int i;
    	glvertex_t *v;
    	msurface_t	*surf;
    	glpoly_t *p;
    
    	// disable texturing and writes to the color buffer
    	glDisable (GL_TEXTURE_2D);
    	glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    
    	for (surf = skychain; surf; surf = surf->texturechain)
    	{
    		for (p = surf->polys; p; p = p->next)
    		{
    			int vpos = 0;
    
    			for (i = 0, v = p->verts; i < p->numverts; i++, v++)
    			{
    				VArrayVerts[vpos++] = v->tv[0];
    				VArrayVerts[vpos++] = v->tv[1];
    				VArrayVerts[vpos++] = v->tv[2];
    			}
    
    			glDrawArrays (GL_TRIANGLE_FAN, 0, p->numverts);
    		}
    	}
    
    	// revert
    	glEnable (GL_TEXTURE_2D);
    	glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    
    	// need to reset primary colour to full as well
    	// as colormask can set it to black on some implementations
    	glColor3f (1, 1, 1);
    }
    
    
    void R_DrawSkyChain (msurface_t *skychain)
    {
    	// sky scaling
    	float fullscale;
    	float halfscale;
    	float reducedfull;
    	float reducedhalf;
    
    	// do these calcs even if we're not drawing
    	// sky rotation
    	// bound rotation speed 0 to 100
    	if (r_skyspeed.value < 0) r_skyspeed.value = 0;
    	if (r_skyspeed.value > 100) r_skyspeed.value = 100;
    
    	// always rotate even if we're paused!!!
    	rotateBack = anglemod (realtime * (2.5 * r_skyspeed.value));
    	rotateFore = anglemod (realtime * (4.0 * r_skyspeed.value));
    
    	// no sky to draw
    	if (!skychain) return;
    
    	// need this as a baseline for everything
    	glEnableClientState (GL_VERTEX_ARRAY);
    	glVertexPointer (3, GL_FLOAT, sizeof (float) * 3, VArrayVerts);
    
    	// write the regular sky polys into the depth buffer to get a baseline
    	R_ClipSky (skychain);
    
    	// calculate the scales in proportion to the far clipping plane for the world
    	fullscale = r_farclip / 4096;
    	halfscale = r_farclip / 8192;
    	reducedfull = (r_farclip / 4096) * 0.9;
    	reducedhalf = (r_farclip / 8192) * 0.9;
    
    	// switch the depth func so that the regular polys will prevent sphere polys outside their area reaching the framebuffer
    	glDepthFunc (GL_GEQUAL);
    	glDepthMask (GL_FALSE);
    
    	// sky texture scaling
    	// note that in terms of size this is *NOT* visually the same as the "classic" quake sky - drawing it on a 
    	// proper sphere generates an "obviously a tiled texture" look if we shrink it more, and looks very wrong, 
    	// so we go for somewhat larger instead.  increase to 8, 4, 4 if that really bothers you.
    	glMatrixMode (GL_TEXTURE);
    	glLoadIdentity ();
    	glScalef (6, 3, 3);
    	glMatrixMode (GL_MODELVIEW);
    
    	// switch vertex pointers
    	glVertexPointer (3, GL_FLOAT, 3 * sizeof (float), skyverts);
    
    	glEnableClientState (GL_TEXTURE_COORD_ARRAY);
    	glTexCoordPointer (2, GL_FLOAT, 2 * sizeof (float), skytexes);
    
    	// background
    	// here we invert the sphere sizes to match the inverted depth func, so that the smaller sphere is actually the back texture!
    	R_DrawSkyArrays (solidskytexture->texnum, reducedfull, reducedhalf, -rotateBack);
    
    	// enable blending for the alpha sky
    	glEnable (GL_BLEND);
    	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    	// foreground
    	// here we invert the sphere sizes to match the inverted depth func, so that the larger sphere is actually the front texture!
    	R_DrawSkyArrays (alphaskytexture->texnum, fullscale, halfscale, -rotateFore);
    
    	// back to normal mode
    	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    	glDisable (GL_BLEND);
    
    	// done with texturing
    	glDisableClientState (GL_TEXTURE_COORD_ARRAY);
    
    	// revert the texture matrix
    	glMatrixMode (GL_TEXTURE);
    	glLoadIdentity ();
    	glMatrixMode (GL_MODELVIEW);
    
    	// revert the depth func
    	glDepthFunc (GL_LEQUAL);
    	glDepthMask (GL_TRUE);
    
    	// now write the regular polys one more time to clip world geometry
    	glVertexPointer (3, GL_FLOAT, sizeof (float) * 3, VArrayVerts);
    
    	R_ClipSky (skychain);
    
    	// done!  phew!  opengl-fu at it's finest
    	glDisableClientState (GL_VERTEX_ARRAY);
    
    	// run a pipeline flush
    	GL_FlushOrFinish (false);
    }
    IT LIVES! http://directq.blogspot.com/

  • #2
    Very nice technique indeed! Although I'm not using vertex arrays (currently) for the skychain, I was able to implement this using under 10 lines of additional code; the correct opengl calls. Also, the really nice thing about this is that it renders even faster now!! Thanks MH, good to see your still around!
    www.quakeone.com/qrack | www.quakeone.com/cax| http://en.twitch.tv/sputnikutah

    Comment


    • #3
      Hi MH
      Quakeone.com - Being exactly one-half good and one-half evil has advantages. When a portal opens to the antimatter universe, my opposite is just me with a goatee.

      So while you guys all have to fight your anti-matter counterparts, me and my evil twin will be drinking a beer laughing at you guys ...

      Comment


      • #4
        Hi there!
        IT LIVES! http://directq.blogspot.com/

        Comment


        • #5
          Incidentally, the glDrawArrays call in R_DrawSkyArrays is incorrect. It looked OK with writes to the colour buffer disabled, but I got paranoid and decided to be sure.

          The correct way is:

          for (i = 0; i < 220; i += 22) glDrawArrays (GL_TRIANGLE_STRIP, i, 22);

          You'll need to also declare int i; at the top, of course.

          Apologies if I've led anyone down the wrong path with that.
          Last edited by MH; 01-24-2008, 11:55 AM.
          IT LIVES! http://directq.blogspot.com/

          Comment


          • #6
            :p i got lucky since i didnt use the arrays.
            BTW do you have a website? QuakeSRC.org is gone so it feels like everyone is scattered about.

            maybe quakeone.com can host a quakesrc 1/2/3/4 like forum additions.
            www.quakeone.com/qrack | www.quakeone.com/cax| http://en.twitch.tv/sputnikutah

            Comment


            • #7
              Inside3d.com would be a better fit. They have a lot of brains hanging out there.

              Everyone here thinks the other Quakes suck. Except for whatever stuff can be mined from their code/map/model formats/etc

              But I *really* miss QuakeSrc.org. I didn't post much, the "search" function in the forums answered most of my questions. I also liked reading random threads and there was always something new to learn.
              Quakeone.com - Being exactly one-half good and one-half evil has advantages. When a portal opens to the antimatter universe, my opposite is just me with a goatee.

              So while you guys all have to fight your anti-matter counterparts, me and my evil twin will be drinking a beer laughing at you guys ...

              Comment

              Working...
              X