From ce1d017a73d0171d7a38814610bb06ea1018b766 Mon Sep 17 00:00:00 2001 From: Hannu Hanhi Date: Wed, 22 Apr 2020 00:18:58 +0300 Subject: [PATCH] Sky dome from SRB2 --- src/hardware/hw_drv.h | 4 + src/hardware/hw_main.c | 193 +++++++++++++---------- src/hardware/hw_main.h | 2 +- src/hardware/r_opengl/r_opengl.c | 259 +++++++++++++++++++++++++++++++ src/sdl/hwsym_sdl.c | 2 + src/sdl/i_video.c | 2 + 6 files changed, 381 insertions(+), 81 deletions(-) diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h index d6ad26c0..b2f79782 100644 --- a/src/hardware/hw_drv.h +++ b/src/hardware/hw_drv.h @@ -51,6 +51,8 @@ EXPORT void HWRAPI(CreateModelVBOs) (model_t *model); EXPORT void HWRAPI(SetTransform) (FTransform *stransform); EXPORT INT32 HWRAPI(GetTextureUsed) (void); +EXPORT void HWRAPI(RenderSkyDome) (INT32 tex, INT32 texture_width, INT32 texture_height, FTransform transform); + EXPORT void HWRAPI(FlushScreenTextures) (void); EXPORT void HWRAPI(StartScreenWipe) (void); EXPORT void HWRAPI(EndScreenWipe) (void); @@ -111,6 +113,8 @@ struct hwdriver_s MakeScreenFinalTexture pfnMakeScreenFinalTexture; DrawScreenFinalTexture pfnDrawScreenFinalTexture; + RenderSkyDome pfnRenderSkyDome; + LoadShaders pfnLoadShaders; KillShaders pfnKillShaders; SetShader pfnSetShader; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 4ac026de..9b7a3523 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -78,6 +78,7 @@ consvar_t cv_grcorrecttricks = {"gr_correcttricks", "Off", 0, CV_OnOff, NULL, 0, consvar_t cv_grsolvetjoin = {"gr_solvetjoin", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grbatching = {"gr_batching", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_grskydome = {"gr_skydome", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; static void CV_filtermode_ONChange(void) { @@ -4513,105 +4514,136 @@ void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) static boolean drewsky = false; -void HWR_DrawSkyBackground(void) +void HWR_DrawSkyBackground(float fpov) { - FOutVector v[4]; - angle_t angle; - float dimensionmultiply; - float aspectratio; - float angleturn; - if (drewsky) return; - - HWR_GetTexture(texturetranslation[skytexture]); - aspectratio = (float)vid.width/(float)vid.height; - - //Hurdler: the sky is the only texture who need 4.0f instead of 1.0 - // because it's called just after clearing the screen - // and thus, the near clipping plane is set to 3.99 - // Sryder: Just use the near clipping plane value then - - // 3--2 - // | /| - // |/ | - // 0--1 - v[0].x = v[3].x = -ZCLIP_PLANE-1; - v[1].x = v[2].x = ZCLIP_PLANE+1; - v[0].y = v[1].y = -ZCLIP_PLANE-1; - v[2].y = v[3].y = ZCLIP_PLANE+1; - - v[0].z = v[1].z = v[2].z = v[3].z = ZCLIP_PLANE+1; - - // X - - // NOTE: This doesn't work right with texture widths greater than 1024 - // software doesn't draw any further than 1024 for skies anyway, but this doesn't overlap properly - // The only time this will probably be an issue is when a sky wider than 1024 is used as a sky AND a regular wall texture - - angle = (viewangle + xtoviewangle[0]); - dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->width/256.0f); - - if (atransform.mirror) + if (cv_grskydome.value) { - angle = InvAngle(angle); - dimensionmultiply *= -1; - } + FTransform dometransform; - v[0].s = v[3].s = ((float) angle / ((ANGLE_90-1)*dimensionmultiply)); - v[2].s = v[1].s = (-1.0f/dimensionmultiply)+((float) angle / ((ANGLE_90-1)*dimensionmultiply)); + memset(&dometransform, 0x00, sizeof(FTransform)); - // Y - angle = aimingangle; - dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->height/(128.0f*aspectratio)); + //04/01/2000: Hurdler: added for T&L + // It should replace all other gr_viewxxx when finished + if (!atransform.shearing) + dometransform.anglex = (float)(aimingangle>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES); + dometransform.angley = (float)((viewangle-ANGLE_270)>>ANGLETOFINESHIFT)*(360.0f/(float)FINEANGLES); - if (splitscreen == 1) - { - dimensionmultiply *= 2; - angle *= 2; - } + dometransform.flip = atransform.flip; + dometransform.mirror = atransform.mirror; + dometransform.shearing = atransform.shearing; + dometransform.viewaiming = atransform.viewaiming; - // Middle of the sky should always be at angle 0 - // need to keep correct aspect ratio with X - if (atransform.flip) - { - // During vertical flip the sky should be flipped and it's y movement should also be flipped obviously - v[3].t = v[2].t = -(0.5f-(0.5f/dimensionmultiply)); - v[0].t = v[1].t = (-1.0f/dimensionmultiply)-(0.5f-(0.5f/dimensionmultiply)); + dometransform.scalex = 1; + dometransform.scaley = (float)vid.width/vid.height; + dometransform.scalez = 1; + dometransform.fovxangle = fpov; // Tails + dometransform.fovyangle = fpov; // Tails + dometransform.splitscreen = splitscreen; + + HWR_GetTexture(texturetranslation[skytexture]); + HWD.pfnSetShader(7); // sky shader + HWD.pfnRenderSkyDome(skytexture, textures[skytexture]->width, textures[skytexture]->height, dometransform); + HWD.pfnSetShader(0); } else { - v[3].t = v[2].t = (-1.0f/dimensionmultiply)-(0.5f-(0.5f/dimensionmultiply)); - v[0].t = v[1].t = -(0.5f-(0.5f/dimensionmultiply)); - } + FOutVector v[4]; + angle_t angle; + float dimensionmultiply; + float aspectratio; + float angleturn; - angleturn = (((float)ANGLE_45-1.0f)*aspectratio)*dimensionmultiply; + HWR_GetTexture(texturetranslation[skytexture]); + aspectratio = (float)vid.width/(float)vid.height; - if (cv_grshearing.value) - { - // Doesn't really make sense, but what can I do? - angle_t dy = FixedAngle(FixedMul(360*FRACUNIT, FixedDiv(AIMINGTODY(aimingangle), 900*FRACUNIT))); - v[3].t = v[2].t -= ((float) dy / angleturn); - v[0].t = v[1].t -= ((float) dy / angleturn); - } - else - { - if (angle > ANGLE_180) // Do this because we don't want the sky to suddenly teleport when crossing over 0 to 360 and vice versa + //Hurdler: the sky is the only texture who need 4.0f instead of 1.0 + // because it's called just after clearing the screen + // and thus, the near clipping plane is set to 3.99 + // Sryder: Just use the near clipping plane value then + + // 3--2 + // | /| + // |/ | + // 0--1 + v[0].x = v[3].x = -ZCLIP_PLANE-1; + v[1].x = v[2].x = ZCLIP_PLANE+1; + v[0].y = v[1].y = -ZCLIP_PLANE-1; + v[2].y = v[3].y = ZCLIP_PLANE+1; + + v[0].z = v[1].z = v[2].z = v[3].z = ZCLIP_PLANE+1; + + // X + + // NOTE: This doesn't work right with texture widths greater than 1024 + // software doesn't draw any further than 1024 for skies anyway, but this doesn't overlap properly + // The only time this will probably be an issue is when a sky wider than 1024 is used as a sky AND a regular wall texture + + angle = (viewangle + xtoviewangle[0]); + dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->width/256.0f); + + if (atransform.mirror) { angle = InvAngle(angle); - v[3].t = v[2].t += ((float) angle / angleturn); - v[0].t = v[1].t += ((float) angle / angleturn); + dimensionmultiply *= -1; + } + + v[0].s = v[3].s = ((float) angle / ((ANGLE_90-1)*dimensionmultiply)); + v[2].s = v[1].s = (-1.0f/dimensionmultiply)+((float) angle / ((ANGLE_90-1)*dimensionmultiply)); + + // Y + angle = aimingangle; + dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->height/(128.0f*aspectratio)); + + if (splitscreen == 1) + { + dimensionmultiply *= 2; + angle *= 2; + } + + // Middle of the sky should always be at angle 0 + // need to keep correct aspect ratio with X + if (atransform.flip) + { + // During vertical flip the sky should be flipped and it's y movement should also be flipped obviously + v[3].t = v[2].t = -(0.5f-(0.5f/dimensionmultiply)); + v[0].t = v[1].t = (-1.0f/dimensionmultiply)-(0.5f-(0.5f/dimensionmultiply)); } else { - v[3].t = v[2].t -= ((float) angle / angleturn); - v[0].t = v[1].t -= ((float) angle / angleturn); + v[3].t = v[2].t = (-1.0f/dimensionmultiply)-(0.5f-(0.5f/dimensionmultiply)); + v[0].t = v[1].t = -(0.5f-(0.5f/dimensionmultiply)); } - } - HWD.pfnSetShader(7); // sky shader - HWD.pfnDrawPolygon(NULL, v, 4, 0); - HWD.pfnSetShader(0); + angleturn = (((float)ANGLE_45-1.0f)*aspectratio)*dimensionmultiply; + + if (cv_grshearing.value) + { + // Doesn't really make sense, but what can I do? + angle_t dy = FixedAngle(FixedMul(360*FRACUNIT, FixedDiv(AIMINGTODY(aimingangle), 900*FRACUNIT))); + v[3].t = v[2].t -= ((float) dy / angleturn); + v[0].t = v[1].t -= ((float) dy / angleturn); + } + else + { + if (angle > ANGLE_180) // Do this because we don't want the sky to suddenly teleport when crossing over 0 to 360 and vice versa + { + angle = InvAngle(angle); + v[3].t = v[2].t += ((float) angle / angleturn); + v[0].t = v[1].t += ((float) angle / angleturn); + } + else + { + v[3].t = v[2].t -= ((float) angle / angleturn); + v[0].t = v[1].t -= ((float) angle / angleturn); + } + } + + HWD.pfnSetShader(7); // sky shader + HWD.pfnDrawPolygon(NULL, v, 4, 0); + HWD.pfnSetShader(0); + } } @@ -4751,7 +4783,7 @@ void HWR_RenderFrame(INT32 viewnumber, player_t *player, boolean skybox) ST_doPaletteStuff(); // Draw the sky background. - HWR_DrawSkyBackground(); + HWR_DrawSkyBackground(fpov); if (skybox) drewsky = true; @@ -4885,6 +4917,7 @@ void HWR_AddCommands(void) CV_RegisterVar(&cv_grsolvetjoin); CV_RegisterVar(&cv_grbatching); + CV_RegisterVar(&cv_grskydome); } // -------------------------------------------------------------------------- diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 8ffb5890..1dba6bba 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -103,7 +103,7 @@ void HWR_ProjectWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blend void HWR_AddTransparentWall(FOutVector *wallVerts, FSurfaceInfo * pSurf, INT32 texnum, FBITFIELD blend, boolean fogwall, INT32 lightlevel, extracolormap_t *wallcolormap); void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum, FSurfaceInfo* Surf, INT32 cutflag, ffloor_t *pfloor); void HWR_DrawSkyWall(FOutVector *wallVerts, FSurfaceInfo *Surf); -void HWR_DrawSkyBackground(void); +void HWR_DrawSkyBackground(float fpov); #ifdef POLYOBJECTS void HWR_AddPolyObjectSegs(void); diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 4e5114e8..2285107d 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -388,6 +388,10 @@ static PFNglMultiTexCoord2fv pglMultiTexCoord2fv; typedef void (APIENTRY *PFNglClientActiveTexture) (GLenum); static PFNglClientActiveTexture pglClientActiveTexture; +// sky dome needs this +typedef void (APIENTRY *PFNglColorPointer) (GLint, GLenum, GLsizei, const GLvoid*); +static PFNglColorPointer pglColorPointer; + /* 1.2 Parms */ /* GL_CLAMP_TO_EDGE_EXT */ #ifndef GL_CLAMP_TO_EDGE @@ -756,6 +760,7 @@ void SetupGLFunc4(void) pglBindBuffer = GetGLFunc("glBindBuffer"); pglBufferData = GetGLFunc("glBufferData"); pglDeleteBuffers = GetGLFunc("glDeleteBuffers"); + pglColorPointer = GetGLFunc("glColorPointer"); #ifdef GL_SHADERS pglCreateShader = GetGLFunc("glCreateShader"); @@ -2318,6 +2323,260 @@ EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUI } } +// Sky dome code, taken/backported from SRB2 + +typedef struct vbo_vertex_s +{ + float x, y, z; + float u, v; + unsigned char r, g, b, a; +} vbo_vertex_t; + +typedef struct +{ + int mode; + int vertexcount; + int vertexindex; + int use_texture; +} GLSkyLoopDef; + +typedef struct +{ + unsigned int id; + int rows, columns; + int loopcount; + GLSkyLoopDef *loops; + vbo_vertex_t *data; +} GLSkyVBO; + +static const boolean gl_ext_arb_vertex_buffer_object = true; + +#define NULL_VBO_VERTEX ((vbo_vertex_t*)NULL) +#define sky_vbo_x (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->x : &vbo->data[0].x) +#define sky_vbo_u (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->u : &vbo->data[0].u) +#define sky_vbo_r (gl_ext_arb_vertex_buffer_object ? &NULL_VBO_VERTEX->r : &vbo->data[0].r) + +// The texture offset to be applied to the texture coordinates in SkyVertex(). +static int rows, columns; +static signed char yflip; +static int texw, texh; +static boolean foglayer; +static float delta = 0.0f; + +static int gl_sky_detail = 16; + +static INT32 lasttex = -1; + +#define MAP_COEFF 128.0f + +static void SkyVertex(vbo_vertex_t *vbo, int r, int c) +{ + const float radians = (float)(M_PIl / 180.0f); + const float scale = 10000.0f; + const float maxSideAngle = 60.0f; + + float topAngle = (c / (float)columns * 360.0f); + float sideAngle = (maxSideAngle * (rows - r) / rows); + float height = (float)(sin(sideAngle * radians)); + float realRadius = (float)(scale * cos(sideAngle * radians)); + float x = (float)(realRadius * cos(topAngle * radians)); + float y = (!yflip) ? scale * height : -scale * height; + float z = (float)(realRadius * sin(topAngle * radians)); + float timesRepeat = (4 * (256.0f / texw)); + if (fpclassify(timesRepeat) == FP_ZERO) + timesRepeat = 1.0f; + + if (!foglayer) + { + vbo->r = 255; + vbo->g = 255; + vbo->b = 255; + vbo->a = (r == 0 ? 0 : 255); + + // And the texture coordinates. + //vbo->u = (-timesRepeat * c / (float)columns); + vbo->u = (timesRepeat * c / (float)columns);// TEST + if (!yflip) // Flipped Y is for the lower hemisphere. + vbo->v = (r / (float)rows) + 0.5f; + else + vbo->v = 1.0f + ((rows - r) / (float)rows) + 0.5f; + } + + if (r != 4) + { + y += 300.0f; + } + + // And finally the vertex. + vbo->x = x; + vbo->y = y + delta; + vbo->z = z; +} + +static GLSkyVBO sky_vbo; + +static void gld_BuildSky(int row_count, int col_count) +{ + int c, r; + vbo_vertex_t *vertex_p; + int vertex_count = 2 * row_count * (col_count * 2 + 2) + col_count * 2; + + GLSkyVBO *vbo = &sky_vbo; + + if ((vbo->columns != col_count) || (vbo->rows != row_count)) + { + free(vbo->loops); + free(vbo->data); + memset(vbo, 0, sizeof(&vbo)); + } + + if (!vbo->data) + { + memset(vbo, 0, sizeof(&vbo)); + vbo->loops = malloc((row_count * 2 + 2) * sizeof(vbo->loops[0])); + // create vertex array + vbo->data = malloc(vertex_count * sizeof(vbo->data[0])); + } + + vbo->columns = col_count; + vbo->rows = row_count; + + vertex_p = &vbo->data[0]; + vbo->loopcount = 0; + + for (yflip = 0; yflip < 2; yflip++) + { + vbo->loops[vbo->loopcount].mode = GL_TRIANGLE_FAN; + vbo->loops[vbo->loopcount].vertexindex = vertex_p - &vbo->data[0]; + vbo->loops[vbo->loopcount].vertexcount = col_count; + vbo->loops[vbo->loopcount].use_texture = false; + vbo->loopcount++; + + delta = 0.0f; + foglayer = true; + for (c = 0; c < col_count; c++) + { + SkyVertex(vertex_p, 1, c); + vertex_p->r = 255; + vertex_p->g = 255; + vertex_p->b = 255; + vertex_p->a = 255; + vertex_p++; + } + foglayer = false; + + delta = (yflip ? 5.0f : -5.0f) / MAP_COEFF; + + for (r = 0; r < row_count; r++) + { + vbo->loops[vbo->loopcount].mode = GL_TRIANGLE_STRIP; + vbo->loops[vbo->loopcount].vertexindex = vertex_p - &vbo->data[0]; + vbo->loops[vbo->loopcount].vertexcount = 2 * col_count + 2; + vbo->loops[vbo->loopcount].use_texture = true; + vbo->loopcount++; + + for (c = 0; c <= col_count; c++) + { + SkyVertex(vertex_p++, r + (yflip ? 1 : 0), (c ? c : 0)); + SkyVertex(vertex_p++, r + (yflip ? 0 : 1), (c ? c : 0)); + } + } + } +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +static void RenderDome(INT32 skytexture) +{ + int i, j; + int vbosize; + GLSkyVBO *vbo = &sky_vbo; + + rows = 4; + columns = 4 * gl_sky_detail; + + vbosize = 2 * rows * (columns * 2 + 2) + columns * 2; + + // Build the sky dome! Yes! + if (lasttex != skytexture) + { + // delete VBO when already exists + if (gl_ext_arb_vertex_buffer_object) + { + if (vbo->id) + pglDeleteBuffers(1, &vbo->id); + } + + lasttex = skytexture; + gld_BuildSky(rows, columns); + + if (gl_ext_arb_vertex_buffer_object) + { + // generate a new VBO and get the associated ID + pglGenBuffers(1, &vbo->id); + + // bind VBO in order to use + pglBindBuffer(GL_ARRAY_BUFFER, vbo->id); + + // upload data to VBO + pglBufferData(GL_ARRAY_BUFFER, vbosize * sizeof(vbo->data[0]), vbo->data, GL_STATIC_DRAW); + } + } + + // bind VBO in order to use + if (gl_ext_arb_vertex_buffer_object) + pglBindBuffer(GL_ARRAY_BUFFER, vbo->id); + + // activate and specify pointers to arrays + pglVertexPointer(3, GL_FLOAT, sizeof(vbo->data[0]), sky_vbo_x); + pglTexCoordPointer(2, GL_FLOAT, sizeof(vbo->data[0]), sky_vbo_u); + pglColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vbo->data[0]), sky_vbo_r); + + // activate color arrays + pglEnableClientState(GL_COLOR_ARRAY); + + // set transforms + pglScalef(1.0f, (float)texh / 230.0f, 1.0f); + pglRotatef(270.0f, 0.0f, 1.0f, 0.0f); + + for (j = 0; j < 2; j++) + { + for (i = 0; i < vbo->loopcount; i++) + { + GLSkyLoopDef *loop = &vbo->loops[i]; + + if (j == 0 ? loop->use_texture : !loop->use_texture) + continue; + + pglDrawArrays(loop->mode, loop->vertexindex, loop->vertexcount); + } + } + + pglScalef(1.0f, 1.0f, 1.0f); + pglColor4ubv(white); + + // bind with 0, so, switch back to normal pointer operation + if (gl_ext_arb_vertex_buffer_object) + pglBindBuffer(GL_ARRAY_BUFFER, 0); + + // deactivate color array + pglDisableClientState(GL_COLOR_ARRAY); +} + +EXPORT void HWRAPI(RenderSkyDome) (INT32 tex, INT32 texture_width, INT32 texture_height, FTransform transform) +{ + SetBlend(PF_Translucent|PF_NoDepthTest|PF_Modulated); + SetTransform(&transform); + texw = texture_width; + texh = texture_height; + RenderDome(tex); + SetBlend(0); +} + // ========================================================================== // // ========================================================================== diff --git a/src/sdl/hwsym_sdl.c b/src/sdl/hwsym_sdl.c index 51426184..2627c3fd 100644 --- a/src/sdl/hwsym_sdl.c +++ b/src/sdl/hwsym_sdl.c @@ -100,6 +100,8 @@ void *hwSym(const char *funcName,void *handle) GETFUNC(MakeScreenTexture); GETFUNC(MakeScreenFinalTexture); GETFUNC(DrawScreenFinalTexture); + + GETFUNC(RenderSkyDome); GETFUNC(LoadShaders); GETFUNC(KillShaders); diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 064fba6f..1e6a5cc1 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1871,6 +1871,8 @@ void I_StartupGraphics(void) HWD.pfnMakeScreenTexture= hwSym("MakeScreenTexture",NULL); HWD.pfnMakeScreenFinalTexture=hwSym("MakeScreenFinalTexture",NULL); HWD.pfnDrawScreenFinalTexture=hwSym("DrawScreenFinalTexture",NULL); + + HWD.pfnRenderSkyDome = hwSym("RenderSkyDome",NULL); HWD.pfnLoadShaders = hwSym("LoadShaders",NULL); HWD.pfnKillShaders = hwSym("KillShaders",NULL);