/* ** gl_sky.cpp ** ** Draws the sky. Loosely based on the JDoom sky and the ZDoomGL 0.66.2 sky. ** **--------------------------------------------------------------------------- ** Copyright 2003 Tim Stump ** Copyright 2005 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** 4. Full disclosure of the entire project's source code, except for third ** party libraries is mandatory. (NOTE: This clause is non-negotiable!) ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include "gl/system/gl_system.h" #include "doomtype.h" #include "g_level.h" #include "sc_man.h" #include "w_wad.h" #include "r_state.h" //#include "gl/gl_intern.h" #include "gl/system/gl_interface.h" #include "gl/data/gl_data.h" #include "gl/data/gl_vertexbuffer.h" #include "gl/renderer/gl_lightdata.h" #include "gl/renderer/gl_renderstate.h" #include "gl/scene/gl_drawinfo.h" #include "gl/scene/gl_portal.h" #include "gl/shaders/gl_shader.h" #include "gl/textures/gl_bitmap.h" #include "gl/textures/gl_texture.h" #include "gl/textures/gl_skyboxtexture.h" #include "gl/textures/gl_material.h" //----------------------------------------------------------------------------- // // Shamelessly lifted from Doomsday (written by Jaakko Keränen) // also shamelessly lifted from ZDoomGL! ;) // //----------------------------------------------------------------------------- CVAR(Float, skyoffset, 0, 0) // for testing extern int skyfog; //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- FSkyVertexBuffer::FSkyVertexBuffer() { CreateDome(); } FSkyVertexBuffer::~FSkyVertexBuffer() { } void FSkyVertexBuffer::BindVBO() { glBindBuffer(GL_ARRAY_BUFFER, vbo_id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glVertexPointer(3, GL_FLOAT, sizeof(FSkyVertex), &VSO->x); glTexCoordPointer(2, GL_FLOAT, sizeof(FSkyVertex), &VSO->u); glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(FSkyVertex), &VSO->color); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_COLOR_ARRAY); } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void FSkyVertexBuffer::SkyVertex(int r, int c, bool yflip) { static const angle_t maxSideAngle = ANGLE_180 / 3; static const fixed_t scale = 10000 << FRACBITS; angle_t topAngle= (angle_t)(c / (float)mColumns * ANGLE_MAX); angle_t sideAngle = maxSideAngle * (mRows - r) / mRows; fixed_t height = finesine[sideAngle>>ANGLETOFINESHIFT]; fixed_t realRadius = FixedMul(scale, finecosine[sideAngle>>ANGLETOFINESHIFT]); fixed_t x = FixedMul(realRadius, finecosine[topAngle>>ANGLETOFINESHIFT]); fixed_t y = (!yflip) ? FixedMul(scale, height) : FixedMul(scale, height) * -1; fixed_t z = FixedMul(realRadius, finesine[topAngle>>ANGLETOFINESHIFT]); FSkyVertex vert; vert.color = r == 0 ? 0xffffff : 0xffffffff; // And the texture coordinates. if(!yflip) // Flipped Y is for the lower hemisphere. { vert.u = (-c / (float)mColumns) ; vert.v = (r / (float)mRows); } else { vert.u = (-c / (float)mColumns); vert.v = 1.0f + ((mRows - r) / (float)mRows); } if (r != 4) y+=FRACUNIT*300; // And finally the vertex. vert.x =-FIXED2FLOAT(x); // Doom mirrors the sky vertically! vert.y = FIXED2FLOAT(y) - 1.f; vert.z = FIXED2FLOAT(z); mVertices.Push(vert); } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void FSkyVertexBuffer::CreateSkyHemisphere(int hemi) { int r, c; bool yflip = !!(hemi & SKYHEMI_LOWER); mPrimStart.Push(mVertices.Size()); for (c = 0; c < mColumns; c++) { SkyVertex(1, c, yflip); } // The total number of triangles per hemisphere can be calculated // as follows: rows * columns * 2 + 2 (for the top cap). for (r = 0; r < mRows; r++) { mPrimStart.Push(mVertices.Size()); for (c = 0; c <= mColumns; c++) { SkyVertex(r + yflip, c, yflip); SkyVertex(r + 1 - yflip, c, yflip); } } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void FSkyVertexBuffer::CreateDome() { if (gl.version < 3.0f) { mColumns = 64; mRows = 4; } else { mColumns = 128; mRows = 4; } CreateSkyHemisphere(SKYHEMI_UPPER); CreateSkyHemisphere(SKYHEMI_LOWER); mPrimStart.Push(mVertices.Size()); if (gl.version >= 3.f) { // we won't bother with a real buffer for GL 2.x because the lack of shaders and therefore the objectColor uniform will require different handling. // It'd also prevent changing to core features only. glBindBuffer(GL_ARRAY_BUFFER, vbo_id); glBufferData(GL_ARRAY_BUFFER, mVertices.Size() * sizeof(FSkyVertex), &mVertices[0], GL_STATIC_DRAW); } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void FSkyVertexBuffer::RenderRow(int prim, int row, bool color) { if (gl.version < 3.f) { glBegin(prim); for (unsigned int i = mPrimStart[row]; i < mPrimStart[row + 1]; i++) { if (color) glColor4ubv((GLubyte*)&mVertices[i].color); glTexCoord2fv(&mVertices[i].u); glVertex3fv(&mVertices[i].x); } glEnd(); } else { glDrawArrays(prim, mPrimStart[row], mPrimStart[row + 1] - mPrimStart[row]); } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void FSkyVertexBuffer::RenderDome(FMaterial *tex, int mode) { int rc = mRows + 1; if (mode != SKYMODE_SECONDLAYER) { if (mode == SKYMODE_MAINLAYER && tex != NULL) { PalEntry pe = tex->tex->GetSkyCapColor(false); gl_RenderState.SetObjectColor(pe); gl_RenderState.EnableTexture(false); } gl_RenderState.Apply(); RenderRow(GL_TRIANGLE_FAN, 0, false); if (mode == SKYMODE_MAINLAYER && tex != NULL) { PalEntry pe = tex->tex->GetSkyCapColor(true); gl_RenderState.SetObjectColor(pe); } gl_RenderState.Apply(); RenderRow(GL_TRIANGLE_FAN, rc, false); if (mode == SKYMODE_MAINLAYER && tex != NULL) { gl_RenderState.EnableTexture(true); } } gl_RenderState.SetObjectColor(0xffffffff); gl_RenderState.Apply(); for (int i = 1; i <= mRows; i++) { RenderRow(GL_TRIANGLE_STRIP, i, true); RenderRow(GL_TRIANGLE_STRIP, rc + i, true); } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void RenderDome(FMaterial * tex, float x_offset, float y_offset, bool mirror, int mode) { int texh = 0; int texw = 0; // 57 world units roughly represent one sky texel for the glTranslate call. const float skyoffsetfactor = 57; if (tex) { glPushMatrix(); tex->Bind(0, 0); texw = tex->TextureWidth(GLUSE_TEXTURE); texh = tex->TextureHeight(GLUSE_TEXTURE); glRotatef(-180.0f+x_offset, 0.f, 1.f, 0.f); float xscale = 1024.f / float(texw); float yscale = 1.f; if (texh < 200) { glTranslatef(0.f, -1250.f, 0.f); glScalef(1.f, texh/230.f, 1.f); } else if (texh <= 240) { glTranslatef(0.f, (200 - texh + tex->tex->SkyOffset + skyoffset)*skyoffsetfactor, 0.f); glScalef(1.f, 1.f + ((texh-200.f)/200.f) * 1.17f, 1.f); } else { glTranslatef(0.f, (-40 + tex->tex->SkyOffset + skyoffset)*skyoffsetfactor, 0.f); glScalef(1.f, 1.2f * 1.17f, 1.f); yscale = 240.f / texh; } glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); glScalef(mirror? -xscale : xscale, yscale, 1.f); glTranslatef(1.f, y_offset / texh, 1.f); glMatrixMode(GL_MODELVIEW); } GLRenderer->mSkyVBO->RenderDome(tex, mode); if (tex) { glMatrixMode(GL_TEXTURE); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); } } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- static void RenderBox(FTextureID texno, FMaterial * gltex, float x_offset, bool sky2) { FSkyBox * sb = static_cast(gltex->tex); int faces; FMaterial * tex; if (!sky2) glRotatef(-180.0f+x_offset, glset.skyrotatevector.X, glset.skyrotatevector.Z, glset.skyrotatevector.Y); else glRotatef(-180.0f+x_offset, glset.skyrotatevector2.X, glset.skyrotatevector2.Z, glset.skyrotatevector2.Y); if (sb->faces[5]) { faces=4; // north tex = FMaterial::ValidateTexture(sb->faces[0]); tex->Bind(GLT_CLAMPX|GLT_CLAMPY, 0); gl_RenderState.Apply(); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(0, 0); glVertex3f(128.f, 128.f, -128.f); glTexCoord2f(1, 0); glVertex3f(-128.f, 128.f, -128.f); glTexCoord2f(1, 1); glVertex3f(-128.f, -128.f, -128.f); glTexCoord2f(0, 1); glVertex3f(128.f, -128.f, -128.f); glEnd(); // east tex = FMaterial::ValidateTexture(sb->faces[1]); tex->Bind(GLT_CLAMPX|GLT_CLAMPY, 0); gl_RenderState.Apply(); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(0, 0); glVertex3f(-128.f, 128.f, -128.f); glTexCoord2f(1, 0); glVertex3f(-128.f, 128.f, 128.f); glTexCoord2f(1, 1); glVertex3f(-128.f, -128.f, 128.f); glTexCoord2f(0, 1); glVertex3f(-128.f, -128.f, -128.f); glEnd(); // south tex = FMaterial::ValidateTexture(sb->faces[2]); tex->Bind(GLT_CLAMPX|GLT_CLAMPY, 0); gl_RenderState.Apply(); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(0, 0); glVertex3f(-128.f, 128.f, 128.f); glTexCoord2f(1, 0); glVertex3f(128.f, 128.f, 128.f); glTexCoord2f(1, 1); glVertex3f(128.f, -128.f, 128.f); glTexCoord2f(0, 1); glVertex3f(-128.f, -128.f, 128.f); glEnd(); // west tex = FMaterial::ValidateTexture(sb->faces[3]); tex->Bind(GLT_CLAMPX|GLT_CLAMPY, 0); gl_RenderState.Apply(); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(0, 0); glVertex3f(128.f, 128.f, 128.f); glTexCoord2f(1, 0); glVertex3f(128.f, 128.f, -128.f); glTexCoord2f(1, 1); glVertex3f(128.f, -128.f, -128.f); glTexCoord2f(0, 1); glVertex3f(128.f, -128.f, 128.f); glEnd(); } else { faces=1; // all 4 sides tex = FMaterial::ValidateTexture(sb->faces[0]); tex->Bind(GLT_CLAMPX|GLT_CLAMPY, 0); gl_RenderState.Apply(); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(0, 0); glVertex3f(128.f, 128.f, -128.f); glTexCoord2f(.25f, 0); glVertex3f(-128.f, 128.f, -128.f); glTexCoord2f(.25f, 1); glVertex3f(-128.f, -128.f, -128.f); glTexCoord2f(0, 1); glVertex3f(128.f, -128.f, -128.f); glEnd(); // east glBegin(GL_TRIANGLE_FAN); glTexCoord2f(.25f, 0); glVertex3f(-128.f, 128.f, -128.f); glTexCoord2f(.5f, 0); glVertex3f(-128.f, 128.f, 128.f); glTexCoord2f(.5f, 1); glVertex3f(-128.f, -128.f, 128.f); glTexCoord2f(.25f, 1); glVertex3f(-128.f, -128.f, -128.f); glEnd(); // south glBegin(GL_TRIANGLE_FAN); glTexCoord2f(.5f, 0); glVertex3f(-128.f, 128.f, 128.f); glTexCoord2f(.75f, 0); glVertex3f(128.f, 128.f, 128.f); glTexCoord2f(.75f, 1); glVertex3f(128.f, -128.f, 128.f); glTexCoord2f(.5f, 1); glVertex3f(-128.f, -128.f, 128.f); glEnd(); // west glBegin(GL_TRIANGLE_FAN); glTexCoord2f(.75f, 0); glVertex3f(128.f, 128.f, 128.f); glTexCoord2f(1, 0); glVertex3f(128.f, 128.f, -128.f); glTexCoord2f(1, 1); glVertex3f(128.f, -128.f, -128.f); glTexCoord2f(.75f, 1); glVertex3f(128.f, -128.f, 128.f); glEnd(); } // top tex = FMaterial::ValidateTexture(sb->faces[faces]); tex->Bind(GLT_CLAMPX|GLT_CLAMPY, 0); gl_RenderState.Apply(); glBegin(GL_TRIANGLE_FAN); if (!sb->fliptop) { glTexCoord2f(0, 0); glVertex3f(128.f, 128.f, -128.f); glTexCoord2f(1, 0); glVertex3f(-128.f, 128.f, -128.f); glTexCoord2f(1, 1); glVertex3f(-128.f, 128.f, 128.f); glTexCoord2f(0, 1); glVertex3f(128.f, 128.f, 128.f); } else { glTexCoord2f(0, 0); glVertex3f(128.f, 128.f, 128.f); glTexCoord2f(1, 0); glVertex3f(-128.f, 128.f, 128.f); glTexCoord2f(1, 1); glVertex3f(-128.f, 128.f, -128.f); glTexCoord2f(0, 1); glVertex3f(128.f, 128.f, -128.f); } glEnd(); // bottom tex = FMaterial::ValidateTexture(sb->faces[faces+1]); tex->Bind(GLT_CLAMPX|GLT_CLAMPY, 0); gl_RenderState.Apply(); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(0, 0); glVertex3f(128.f, -128.f, -128.f); glTexCoord2f(1, 0); glVertex3f(-128.f, -128.f, -128.f); glTexCoord2f(1, 1); glVertex3f(-128.f, -128.f, 128.f); glTexCoord2f(0, 1); glVertex3f(128.f, -128.f, 128.f); glEnd(); } //----------------------------------------------------------------------------- // // // //----------------------------------------------------------------------------- void GLSkyPortal::DrawContents() { bool drawBoth = false; PalEntry FadeColor(0,0,0,0); // We have no use for Doom lighting special handling here, so disable it for this function. int oldlightmode = glset.lightmode; if (glset.lightmode == 8) glset.lightmode = 2; if (!gl_fixedcolormap) { FadeColor = origin->fadecolor; } gl_RenderState.ResetColor(); gl_RenderState.EnableFog(false); gl_RenderState.EnableAlphaTest(false); gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glMatrixMode(GL_MODELVIEW); glPushMatrix(); GLRenderer->SetupView(0, 0, 0, viewangle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); if (origin->texture[0] && origin->texture[0]->tex->gl_info.bSkybox) { RenderBox(origin->skytexno1, origin->texture[0], origin->x_offset[0], origin->sky2); gl_RenderState.EnableAlphaTest(true); } else { if (gl.version >= 3.f) { gl_RenderState.SetVertexBuffer(GLRenderer->mSkyVBO); } if (origin->texture[0]==origin->texture[1] && origin->doublesky) origin->doublesky=false; if (origin->texture[0]) { gl_RenderState.SetTextureMode(TM_OPAQUE); RenderDome(origin->texture[0], origin->x_offset[0], origin->y_offset, origin->mirrored, FSkyVertexBuffer::SKYMODE_MAINLAYER); gl_RenderState.SetTextureMode(TM_MODULATE); } gl_RenderState.EnableAlphaTest(true); gl_RenderState.AlphaFunc(GL_GEQUAL,0.05f); if (origin->doublesky && origin->texture[1]) { RenderDome(origin->texture[1], origin->x_offset[1], origin->y_offset, false, FSkyVertexBuffer::SKYMODE_SECONDLAYER); } if (skyfog>0 && (FadeColor.r ||FadeColor.g || FadeColor.b)) { gl_RenderState.EnableTexture(false); gl_RenderState.SetColorAlpha(FadeColor, skyfog / 255.0f); // for the fog layer we must temporarily disable the color part of the vertex buffer. glDisableClientState(GL_COLOR_ARRAY); RenderDome(NULL, 0, 0, false, FSkyVertexBuffer::SKYMODE_FOGLAYER); glEnableClientState(GL_COLOR_ARRAY); gl_RenderState.EnableTexture(true); } if (gl.version >= 3.f) { gl_RenderState.SetVertexBuffer(GLRenderer->mVBO); } } glPopMatrix(); glset.lightmode = oldlightmode; }