From 399d0974abb54cb53fa7b643bd1b27cc39523d28 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 23 Jun 2013 09:49:34 +0200 Subject: [PATCH] - added GL render as of SVN revision 1600. --- src/gl/data/gl_data.cpp | 524 +++ src/gl/data/gl_data.h | 61 + src/gl/data/gl_portaldata.cpp | 473 +++ src/gl/data/gl_sections.cpp | 847 ++++ src/gl/data/gl_sections.h | 56 + src/gl/data/gl_setup.cpp | 736 ++++ src/gl/data/gl_vertexbuffer.cpp | 420 ++ src/gl/data/gl_vertexbuffer.h | 62 + src/gl/dynlights/a_dynlight.cpp | 742 ++++ src/gl/dynlights/gl_dynlight.cpp | 1365 +++++++ src/gl/dynlights/gl_dynlight.h | 191 + src/gl/dynlights/gl_dynlight1.cpp | 261 ++ src/gl/dynlights/gl_glow.cpp | 139 + src/gl/dynlights/gl_glow.h | 10 + src/gl/dynlights/gl_lightbuffer.cpp | 243 ++ src/gl/dynlights/gl_lightbuffer.h | 66 + src/gl/gl_builddraw.cpp | 594 +++ src/gl/gl_functions.h | 13 + src/gl/hqnx/Image.cpp | 1174 ++++++ src/gl/hqnx/Image.h | 145 + src/gl/hqnx/gl_hqresize.cpp | 280 ++ src/gl/hqnx/hq2x.cpp | 2987 ++++++++++++++ src/gl/hqnx/hq3x.cpp | 3868 ++++++++++++++++++ src/gl/hqnx/hq4x.cpp | 5469 ++++++++++++++++++++++++++ src/gl/hqnx/hqnx.h | 35 + src/gl/models/gl_models.cpp | 922 +++++ src/gl/models/gl_models.h | 365 ++ src/gl/models/gl_models_md2.cpp | 509 +++ src/gl/models/gl_models_md3.cpp | 313 ++ src/gl/models/gl_voxels.cpp | 504 +++ src/gl/models/tab_anorms.h | 489 +++ src/gl/renderer/gl_colormap.h | 79 + src/gl/renderer/gl_lightdata.cpp | 654 +++ src/gl/renderer/gl_lightdata.h | 66 + src/gl/renderer/gl_renderer.cpp | 634 +++ src/gl/renderer/gl_renderer.h | 178 + src/gl/renderer/gl_renderstate.cpp | 352 ++ src/gl/renderer/gl_renderstate.h | 278 ++ src/gl/scene/gl_bsp.cpp | 563 +++ src/gl/scene/gl_clipper.cpp | 458 +++ src/gl/scene/gl_clipper.h | 152 + src/gl/scene/gl_decal.cpp | 428 ++ src/gl/scene/gl_drawinfo.cpp | 1221 ++++++ src/gl/scene/gl_drawinfo.h | 279 ++ src/gl/scene/gl_fakeflat.cpp | 397 ++ src/gl/scene/gl_flats.cpp | 809 ++++ src/gl/scene/gl_portal.cpp | 1081 +++++ src/gl/scene/gl_portal.h | 300 ++ src/gl/scene/gl_renderhacks.cpp | 1247 ++++++ src/gl/scene/gl_scene.cpp | 1270 ++++++ src/gl/scene/gl_sky.cpp | 353 ++ src/gl/scene/gl_skydome.cpp | 613 +++ src/gl/scene/gl_sprite.cpp | 991 +++++ src/gl/scene/gl_spritelight.cpp | 300 ++ src/gl/scene/gl_vertex.cpp | 195 + src/gl/scene/gl_wall.h | 365 ++ src/gl/scene/gl_walls.cpp | 1793 +++++++++ src/gl/scene/gl_walls_draw.cpp | 592 +++ src/gl/scene/gl_weapon.cpp | 407 ++ src/gl/shaders/gl_shader.cpp | 679 ++++ src/gl/shaders/gl_shader.h | 160 + src/gl/shaders/gl_texshader.cpp | 698 ++++ src/gl/shaders/gl_texshader.h | 96 + src/gl/system/gl_cvars.h | 59 + src/gl/system/gl_framebuffer.cpp | 559 +++ src/gl/system/gl_framebuffer.h | 117 + src/gl/system/gl_interface.cpp | 1084 +++++ src/gl/system/gl_interface.h | 249 ++ src/gl/system/gl_menu.cpp | 137 + src/gl/system/gl_system.h | 124 + src/gl/system/gl_threads.cpp | 0 src/gl/system/gl_threads.h | 247 ++ src/gl/system/gl_wipe.cpp | 551 +++ src/gl/textures/gl_bitmap.cpp | 286 ++ src/gl/textures/gl_bitmap.h | 34 + src/gl/textures/gl_hirestex.cpp | 373 ++ src/gl/textures/gl_hqresize.cpp | 291 ++ src/gl/textures/gl_hwtexture.cpp | 420 ++ src/gl/textures/gl_hwtexture.h | 77 + src/gl/textures/gl_material.cpp | 1123 ++++++ src/gl/textures/gl_material.h | 254 ++ src/gl/textures/gl_skyboxtexture.cpp | 229 ++ src/gl/textures/gl_skyboxtexture.h | 45 + src/gl/textures/gl_texture.cpp | 857 ++++ src/gl/textures/gl_texture.h | 88 + src/gl/textures/gl_translate.cpp | 88 + src/gl/textures/gl_translate.h | 42 + src/gl/utility/gl_clock.cpp | 222 ++ src/gl/utility/gl_clock.h | 122 + src/gl/utility/gl_convert.h | 9 + src/gl/utility/gl_cycler.cpp | 155 + src/gl/utility/gl_cycler.h | 52 + src/gl/utility/gl_geometric.cpp | 258 ++ src/gl/utility/gl_geometric.h | 251 ++ src/gl/utility/gl_templates.h | 82 + src/sdl/hardware.cpp | 44 +- src/sdl/sdlglvideo.cpp | 417 ++ src/sdl/sdlglvideo.h | 78 + src/textures/textures.h | 72 +- src/win32/hardware.cpp | 59 +- src/win32/win32gliface.cpp | 564 +++ src/win32/win32gliface.h | 147 + src/win32/zdoom.rc | 20 +- 103 files changed, 51414 insertions(+), 23 deletions(-) create mode 100644 src/gl/data/gl_data.cpp create mode 100644 src/gl/data/gl_data.h create mode 100644 src/gl/data/gl_portaldata.cpp create mode 100644 src/gl/data/gl_sections.cpp create mode 100644 src/gl/data/gl_sections.h create mode 100644 src/gl/data/gl_setup.cpp create mode 100644 src/gl/data/gl_vertexbuffer.cpp create mode 100644 src/gl/data/gl_vertexbuffer.h create mode 100644 src/gl/dynlights/a_dynlight.cpp create mode 100644 src/gl/dynlights/gl_dynlight.cpp create mode 100644 src/gl/dynlights/gl_dynlight.h create mode 100644 src/gl/dynlights/gl_dynlight1.cpp create mode 100644 src/gl/dynlights/gl_glow.cpp create mode 100644 src/gl/dynlights/gl_glow.h create mode 100644 src/gl/dynlights/gl_lightbuffer.cpp create mode 100644 src/gl/dynlights/gl_lightbuffer.h create mode 100644 src/gl/gl_builddraw.cpp create mode 100644 src/gl/gl_functions.h create mode 100644 src/gl/hqnx/Image.cpp create mode 100644 src/gl/hqnx/Image.h create mode 100644 src/gl/hqnx/gl_hqresize.cpp create mode 100644 src/gl/hqnx/hq2x.cpp create mode 100644 src/gl/hqnx/hq3x.cpp create mode 100644 src/gl/hqnx/hq4x.cpp create mode 100644 src/gl/hqnx/hqnx.h create mode 100644 src/gl/models/gl_models.cpp create mode 100644 src/gl/models/gl_models.h create mode 100644 src/gl/models/gl_models_md2.cpp create mode 100644 src/gl/models/gl_models_md3.cpp create mode 100644 src/gl/models/gl_voxels.cpp create mode 100644 src/gl/models/tab_anorms.h create mode 100644 src/gl/renderer/gl_colormap.h create mode 100644 src/gl/renderer/gl_lightdata.cpp create mode 100644 src/gl/renderer/gl_lightdata.h create mode 100644 src/gl/renderer/gl_renderer.cpp create mode 100644 src/gl/renderer/gl_renderer.h create mode 100644 src/gl/renderer/gl_renderstate.cpp create mode 100644 src/gl/renderer/gl_renderstate.h create mode 100644 src/gl/scene/gl_bsp.cpp create mode 100644 src/gl/scene/gl_clipper.cpp create mode 100644 src/gl/scene/gl_clipper.h create mode 100644 src/gl/scene/gl_decal.cpp create mode 100644 src/gl/scene/gl_drawinfo.cpp create mode 100644 src/gl/scene/gl_drawinfo.h create mode 100644 src/gl/scene/gl_fakeflat.cpp create mode 100644 src/gl/scene/gl_flats.cpp create mode 100644 src/gl/scene/gl_portal.cpp create mode 100644 src/gl/scene/gl_portal.h create mode 100644 src/gl/scene/gl_renderhacks.cpp create mode 100644 src/gl/scene/gl_scene.cpp create mode 100644 src/gl/scene/gl_sky.cpp create mode 100644 src/gl/scene/gl_skydome.cpp create mode 100644 src/gl/scene/gl_sprite.cpp create mode 100644 src/gl/scene/gl_spritelight.cpp create mode 100644 src/gl/scene/gl_vertex.cpp create mode 100644 src/gl/scene/gl_wall.h create mode 100644 src/gl/scene/gl_walls.cpp create mode 100644 src/gl/scene/gl_walls_draw.cpp create mode 100644 src/gl/scene/gl_weapon.cpp create mode 100644 src/gl/shaders/gl_shader.cpp create mode 100644 src/gl/shaders/gl_shader.h create mode 100644 src/gl/shaders/gl_texshader.cpp create mode 100644 src/gl/shaders/gl_texshader.h create mode 100644 src/gl/system/gl_cvars.h create mode 100644 src/gl/system/gl_framebuffer.cpp create mode 100644 src/gl/system/gl_framebuffer.h create mode 100644 src/gl/system/gl_interface.cpp create mode 100644 src/gl/system/gl_interface.h create mode 100644 src/gl/system/gl_menu.cpp create mode 100644 src/gl/system/gl_system.h create mode 100644 src/gl/system/gl_threads.cpp create mode 100644 src/gl/system/gl_threads.h create mode 100644 src/gl/system/gl_wipe.cpp create mode 100644 src/gl/textures/gl_bitmap.cpp create mode 100644 src/gl/textures/gl_bitmap.h create mode 100644 src/gl/textures/gl_hirestex.cpp create mode 100644 src/gl/textures/gl_hqresize.cpp create mode 100644 src/gl/textures/gl_hwtexture.cpp create mode 100644 src/gl/textures/gl_hwtexture.h create mode 100644 src/gl/textures/gl_material.cpp create mode 100644 src/gl/textures/gl_material.h create mode 100644 src/gl/textures/gl_skyboxtexture.cpp create mode 100644 src/gl/textures/gl_skyboxtexture.h create mode 100644 src/gl/textures/gl_texture.cpp create mode 100644 src/gl/textures/gl_texture.h create mode 100644 src/gl/textures/gl_translate.cpp create mode 100644 src/gl/textures/gl_translate.h create mode 100644 src/gl/utility/gl_clock.cpp create mode 100644 src/gl/utility/gl_clock.h create mode 100644 src/gl/utility/gl_convert.h create mode 100644 src/gl/utility/gl_cycler.cpp create mode 100644 src/gl/utility/gl_cycler.h create mode 100644 src/gl/utility/gl_geometric.cpp create mode 100644 src/gl/utility/gl_geometric.h create mode 100644 src/gl/utility/gl_templates.h create mode 100644 src/sdl/sdlglvideo.cpp create mode 100644 src/sdl/sdlglvideo.h create mode 100644 src/win32/win32gliface.cpp create mode 100644 src/win32/win32gliface.h diff --git a/src/gl/data/gl_data.cpp b/src/gl/data/gl_data.cpp new file mode 100644 index 000000000..5a2508968 --- /dev/null +++ b/src/gl/data/gl_data.cpp @@ -0,0 +1,524 @@ +/* +** gl_data.cpp +** Maintenance data for GL renderer (mostly to handle rendering hacks) +** +**--------------------------------------------------------------------------- +** 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "colormatcher.h" +#include "i_system.h" +#include "p_local.h" +#include "p_lnspec.h" +#include "c_dispatch.h" +#include "r_sky.h" +#include "sc_man.h" +#include "w_wad.h" +#include "gi.h" +#include "g_level.h" + +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/data/gl_data.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/dynlights/gl_glow.h" +#include "gl/models/gl_models.h" +#include "gl/utility/gl_clock.h" +#include "gl/shaders/gl_shader.h" +#include "gl/gl_functions.h" + +GLRenderSettings glset; +long gl_frameMS; +long gl_frameCount; + +EXTERN_CVAR(Int, gl_lightmode) +EXTERN_CVAR(Bool, gl_brightfog) + +CUSTOM_CVAR(Float, maxviewpitch, 90.f, CVAR_ARCHIVE|CVAR_SERVERINFO) +{ + if (self>90.f) self=90.f; + else if (self<-90.f) self=-90.f; +} + +CUSTOM_CVAR(Bool, gl_notexturefill, false, 0) +{ + glset.notexturefill = self; +} + + +CUSTOM_CVAR(Bool, gl_nocoloredspritelighting, false, 0) +{ + glset.nocoloredspritelighting = self; +} + +void gl_CreateSections(); + +//----------------------------------------------------------------------------- +// +// Adjust sprite offsets for GL rendering (IWAD resources only) +// +//----------------------------------------------------------------------------- + +void AdjustSpriteOffsets() +{ + static bool done=false; + char name[30]; + + if (done) return; + done=true; + + mysnprintf(name, countof(name), "sprofs/%s.sprofs", GameNames[gameinfo.gametype]); + int lump = Wads.CheckNumForFullName(name); + if (lump>=0) + { + FScanner sc; + sc.OpenLumpNum(lump); + GLRenderer->FlushTextures(); + while (sc.GetString()) + { + int x,y; + FTextureID texno = TexMan.CheckForTexture(sc.String, FTexture::TEX_Sprite); + sc.GetNumber(); + x=sc.Number; + sc.GetNumber(); + y=sc.Number; + + if (texno.isValid()) + { + FTexture * tex = TexMan[texno]; + + int lumpnum = tex->GetSourceLump(); + // We only want to change texture offsets for sprites in the IWAD! + if (lumpnum >= 0 && lumpnum < Wads.GetNumLumps()) + { + int wadno = Wads.GetLumpFile(lumpnum); + if (wadno==FWadCollection::IWAD_FILENUM) + { + tex->LeftOffset=x; + tex->TopOffset=y; + tex->KillNative(); + } + } + } + } + } +} + + + +// Normally this would be better placed in p_lnspec.cpp. +// But I have accidentally overwritten that file several times +// so I'd rather place it here. +static int LS_Sector_SetPlaneReflection (line_t *ln, AActor *it, bool backSide, + int arg0, int arg1, int arg2, int arg3, int arg4) +{ +// Sector_SetPlaneReflection (tag, floor, ceiling) + int secnum = -1; + + while ((secnum = P_FindSectorFromTag (arg0, secnum)) >= 0) + { + sector_t * s = §ors[secnum]; + if (s->floorplane.a==0 && s->floorplane.b==0) s->reflect[sector_t::floor] = arg1/255.f; + if (s->ceilingplane.a==0 && s->ceilingplane.b==0) sectors[secnum].reflect[sector_t::ceiling] = arg2/255.f; + } + + return true; +} + +static int LS_SetGlobalFogParameter (line_t *ln, AActor *it, bool backSide, + int arg0, int arg1, int arg2, int arg3, int arg4) +{ +// SetGlobalFogParameter (type, value) + switch(arg0) + { + case 0: + fogdensity = arg1>>1; + return true; + + case 1: + outsidefogdensity = arg1>>1; + return true; + + case 2: + skyfog = arg1; + return true; + + default: + return false; + } +} + + +//========================================================================== +// +// Portal identifier lists +// +//========================================================================== + + +//========================================================================== +// +// MAPINFO stuff +// +//========================================================================== + +struct FGLROptions : public FOptionalMapinfoData +{ + FGLROptions() + { + identifier = "gl_renderer"; + fogdensity = 0; + outsidefogdensity = 0; + skyfog = 0; + lightmode = -1; + nocoloredspritelighting = -1; + notexturefill = -1; + skyrotatevector = FVector3(0,0,1); + skyrotatevector2 = FVector3(0,0,1); + } + virtual FOptionalMapinfoData *Clone() const + { + FGLROptions *newopt = new FGLROptions; + newopt->identifier = identifier; + newopt->fogdensity = fogdensity; + newopt->outsidefogdensity = outsidefogdensity; + newopt->skyfog = skyfog; + newopt->lightmode = lightmode; + newopt->nocoloredspritelighting = nocoloredspritelighting; + newopt->notexturefill = notexturefill; + newopt->skyrotatevector = skyrotatevector; + newopt->skyrotatevector2 = skyrotatevector2; + return newopt; + } + int fogdensity; + int outsidefogdensity; + int skyfog; + int lightmode; + int brightfog; + SBYTE nocoloredspritelighting; + SBYTE notexturefill; + FVector3 skyrotatevector; + FVector3 skyrotatevector2; +}; + +DEFINE_MAP_OPTION(fogdensity, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + parse.ParseAssign(); + parse.sc.MustGetNumber(); + opt->fogdensity = parse.sc.Number; +} + +DEFINE_MAP_OPTION(brightfog, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + parse.ParseAssign(); + parse.sc.MustGetNumber(); + opt->brightfog = parse.sc.Number; +} + +DEFINE_MAP_OPTION(outsidefogdensity, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + parse.ParseAssign(); + parse.sc.MustGetNumber(); + opt->outsidefogdensity = parse.sc.Number; +} + +DEFINE_MAP_OPTION(skyfog, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + parse.ParseAssign(); + parse.sc.MustGetNumber(); + opt->skyfog = parse.sc.Number; +} + +DEFINE_MAP_OPTION(lightmode, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + parse.ParseAssign(); + parse.sc.MustGetNumber(); + opt->lightmode = BYTE(parse.sc.Number); +} + +DEFINE_MAP_OPTION(nocoloredspritelighting, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + if (parse.CheckAssign()) + { + parse.sc.MustGetNumber(); + opt->nocoloredspritelighting = !!parse.sc.Number; + } + else + { + opt->nocoloredspritelighting = true; + } +} + +DEFINE_MAP_OPTION(notexturefill, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + if (parse.CheckAssign()) + { + parse.sc.MustGetNumber(); + opt->notexturefill = !!parse.sc.Number; + } + else + { + opt->notexturefill = true; + } +} + +DEFINE_MAP_OPTION(skyrotate, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + + parse.ParseAssign(); + parse.sc.MustGetFloat(); + opt->skyrotatevector.X = (float)parse.sc.Float; + if (parse.format_type == FMapInfoParser::FMT_New) parse.sc.MustGetStringName(","); + parse.sc.MustGetFloat(); + opt->skyrotatevector.Y = (float)parse.sc.Float; + if (parse.format_type == FMapInfoParser::FMT_New) parse.sc.MustGetStringName(","); + parse.sc.MustGetFloat(); + opt->skyrotatevector.Z = (float)parse.sc.Float; + opt->skyrotatevector.MakeUnit(); +} + +DEFINE_MAP_OPTION(skyrotate2, false) +{ + FGLROptions *opt = info->GetOptData("gl_renderer"); + + parse.ParseAssign(); + parse.sc.MustGetFloat(); + opt->skyrotatevector2.X = (float)parse.sc.Float; + if (parse.format_type == FMapInfoParser::FMT_New) parse.sc.MustGetStringName(","); + parse.sc.MustGetFloat(); + opt->skyrotatevector2.Y = (float)parse.sc.Float; + if (parse.format_type == FMapInfoParser::FMT_New) parse.sc.MustGetStringName(","); + parse.sc.MustGetFloat(); + opt->skyrotatevector2.Z = (float)parse.sc.Float; + opt->skyrotatevector2.MakeUnit(); +} + +bool IsLightmodeValid() +{ + return (glset.map_lightmode >= 0 && glset.map_lightmode <= 4) || glset.map_lightmode == 8; +} + +void InitGLRMapinfoData() +{ + FGLROptions *opt = level.info->GetOptData("gl_renderer", false); + + if (opt != NULL) + { + gl_SetFogParams(opt->fogdensity, level.info->outsidefog, opt->outsidefogdensity, opt->skyfog); + glset.map_lightmode = opt->lightmode; + glset.map_brightfog = opt->brightfog; + glset.map_nocoloredspritelighting = opt->nocoloredspritelighting; + glset.map_notexturefill = opt->notexturefill; + glset.skyrotatevector = opt->skyrotatevector; + glset.skyrotatevector2 = opt->skyrotatevector2; + if (gl.shadermodel == 2 && glset.map_lightmode ==2) glset.map_lightmode = 3; + } + else + { + gl_SetFogParams(0, level.info->outsidefog, 0, 0); + glset.map_lightmode = -1; + glset.map_brightfog = -1; + glset.map_nocoloredspritelighting = -1; + glset.map_notexturefill = -1; + glset.skyrotatevector = FVector3(0,0,1); + glset.skyrotatevector2 = FVector3(0,0,1); + } + + if (!IsLightmodeValid()) glset.lightmode = gl_lightmode; + else glset.lightmode = glset.map_lightmode; + if (glset.map_nocoloredspritelighting == -1) glset.nocoloredspritelighting = gl_nocoloredspritelighting; + else glset.nocoloredspritelighting = !!glset.map_nocoloredspritelighting; + if (glset.map_notexturefill == -1) glset.notexturefill = gl_notexturefill; + else glset.notexturefill = !!glset.map_notexturefill; + if (glset.map_brightfog == -1) glset.brightfog = gl_brightfog; + else glset.brightfog = !!glset.map_brightfog; +} + +CCMD(gl_resetmap) +{ + if (!IsLightmodeValid()) glset.lightmode = gl_lightmode; + else glset.lightmode = glset.map_lightmode; + if (glset.map_nocoloredspritelighting == -1) glset.nocoloredspritelighting = gl_nocoloredspritelighting; + else glset.nocoloredspritelighting = !!glset.map_nocoloredspritelighting; + if (glset.map_notexturefill == -1) glset.notexturefill = gl_notexturefill; + else glset.notexturefill = !!glset.map_notexturefill; + if (glset.map_brightfog == -1) glset.brightfog = gl_brightfog; + else glset.brightfog = !!glset.map_brightfog; +} + + +//=========================================================================== +// +// Gets the texture index for a sprite frame +// +//=========================================================================== + +FTextureID gl_GetSpriteFrame(unsigned sprite, int frame, int rot, angle_t ang, bool *mirror) +{ + spritedef_t *sprdef = &sprites[sprite]; + if (frame >= sprdef->numframes) + { + // If there are no frames at all for this sprite, don't draw it. + return FNullTextureID(); + } + else + { + //picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0]; + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + frame]; + if (rot==-1) + { + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + rot = (ang + (angle_t)(ANGLE_45/2)*9) >> 28; + } + else + { + rot = (ang + (angle_t)(ANGLE_45/2)*9-(angle_t)(ANGLE_180/16)) >> 28; + } + } + if (mirror) *mirror = !!(sprframe->Flip&(1<Texture[rot]; + } +} + + +//========================================================================== +// +// Recalculate all heights affectting this vertex. +// +//========================================================================== +void gl_RecalcVertexHeights(vertex_t * v) +{ + int i,j,k; + float height; + + //@sync-vertexheights + v->numheights=0; + for(i=0;inumsectors;i++) + { + for(j=0;j<2;j++) + { + if (j==0) height=FIXED2FLOAT(v->sectors[i]->ceilingplane.ZatPoint(v)); + else height=FIXED2FLOAT(v->sectors[i]->floorplane.ZatPoint(v)); + + for(k=0;knumheights;k++) + { + if (height == v->heightlist[k]) break; + if (height < v->heightlist[k]) + { + memmove(&v->heightlist[k+1], &v->heightlist[k], sizeof(float) * (v->numheights-k)); + v->heightlist[k]=height; + v->numheights++; + break; + } + } + if (k==v->numheights) v->heightlist[v->numheights++]=height; + } + } + if (v->numheights<=2) v->numheights=0; // is not in need of any special attention + v->dirty = false; +} + + + + +void gl_InitData() +{ + LineSpecials[157]=LS_SetGlobalFogParameter; + LineSpecials[159]=LS_Sector_SetPlaneReflection; + gl_InitModels(); + AdjustSpriteOffsets(); +} + +//========================================================================== +// +// dumpgeometry +// +//========================================================================== + +CCMD(dumpgeometry) +{ + for(int i=0;isubsectorcount;j++) + { + subsector_t * sub = sector->subsectors[j]; + + Printf(PRINT_LOG, " Subsector %d - real sector = %d - %s\n", int(sub-subsectors), sub->sector->sectornum, sub->hacked&1? "hacked":""); + for(DWORD k=0;knumlines;k++) + { + seg_t * seg = sub->firstline + k; + if (seg->linedef) + { + Printf(PRINT_LOG, " (%4.4f, %4.4f), (%4.4f, %4.4f) - seg %d, linedef %d, side %d", + FIXED2FLOAT(seg->v1->x), FIXED2FLOAT(seg->v1->y), FIXED2FLOAT(seg->v2->x), FIXED2FLOAT(seg->v2->y), + int(seg-segs), int(seg->linedef-lines), seg->sidedef != seg->linedef->sidedef[0]); + } + else + { + Printf(PRINT_LOG, " (%4.4f, %4.4f), (%4.4f, %4.4f) - seg %d, miniseg", + FIXED2FLOAT(seg->v1->x), FIXED2FLOAT(seg->v1->y), FIXED2FLOAT(seg->v2->x), FIXED2FLOAT(seg->v2->y), + int(seg-segs)); + } + if (seg->PartnerSeg) + { + subsector_t * sub2 = seg->PartnerSeg->Subsector; + Printf(PRINT_LOG, ", back sector = %d, real back sector = %d", sub2->render_sector->sectornum, seg->PartnerSeg->frontsector->sectornum); + } + else if (seg->backsector) + { + Printf(PRINT_LOG, ", back sector = %d (no partnerseg)", seg->backsector->sectornum); + } + + Printf(PRINT_LOG, "\n"); + } + } + } +} diff --git a/src/gl/data/gl_data.h b/src/gl/data/gl_data.h new file mode 100644 index 000000000..6a71dd1ac --- /dev/null +++ b/src/gl/data/gl_data.h @@ -0,0 +1,61 @@ + +#ifndef __GLC_DATA_H +#define __GLC_DATA_H + +#include "doomtype.h" + +struct GLRenderSettings +{ + + SBYTE lightmode; + bool nocoloredspritelighting; + bool notexturefill; + bool brightfog; + + SBYTE map_lightmode; + SBYTE map_nocoloredspritelighting; + SBYTE map_notexturefill; + SBYTE map_brightfog; + + FVector3 skyrotatevector; + FVector3 skyrotatevector2; + +}; + +extern GLRenderSettings glset; + +#include "r_defs.h" +#include "a_sharedglobal.h" +#include "c_cvars.h" + +extern int extralight; +EXTERN_CVAR(Int, gl_weaponlight); + +inline int getExtraLight() +{ + return extralight * gl_weaponlight; // ((glset.lightmode == 8)? 16:8); +} + +void gl_RecalcVertexHeights(vertex_t * v); +FTextureID gl_GetSpriteFrame(unsigned sprite, int frame, int rot, angle_t ang, bool *mirror); + +class AStackPoint; +struct GLSectorStackPortal; + +struct FPortal +{ + fixed_t xDisplacement; + fixed_t yDisplacement; + int plane; + GLSectorStackPortal *glportal; // for quick access to the render data. This is only valid during BSP traversal! + + GLSectorStackPortal *GetGLPortal(); +}; + +extern TArray portals; +extern TArray currentmapsection; + +void gl_InitPortals(); +void gl_BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, FPortal *portal); + +#endif diff --git a/src/gl/data/gl_portaldata.cpp b/src/gl/data/gl_portaldata.cpp new file mode 100644 index 000000000..11989b4a7 --- /dev/null +++ b/src/gl/data/gl_portaldata.cpp @@ -0,0 +1,473 @@ +/* +** gl_setup.cpp +** Initializes the data structures required by the GL renderer to handle +** a level +** +**--------------------------------------------------------------------------- +** 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "doomtype.h" +#include "colormatcher.h" +#include "i_system.h" +#include "p_local.h" +#include "p_lnspec.h" +#include "c_dispatch.h" +#include "r_sky.h" +#include "sc_man.h" +#include "w_wad.h" +#include "gi.h" +#include "g_level.h" +#include "a_sharedglobal.h" + +#include "gl/renderer/gl_renderer.h" +#include "gl/data/gl_data.h" +#include "gl/data/gl_vertexbuffer.h" +#include "gl/scene/gl_clipper.h" +#include "gl/scene/gl_portal.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/dynlights/gl_glow.h" +#include "gl/utility/gl_clock.h" +#include "gl/gl_functions.h" + +struct FPortalID +{ + fixed_t mXDisplacement; + fixed_t mYDisplacement; + + // for the hash code + operator intptr_t() const { return (mXDisplacement >> 8) + (mYDisplacement << 8); } + bool operator != (const FPortalID &other) const + { + return mXDisplacement != other.mXDisplacement || + mYDisplacement != other.mYDisplacement; + } +}; + +struct FPortalSector +{ + sector_t *mSub; + int mPlane; +}; + +typedef TArray FPortalSectors; + +typedef TMap FPortalMap; + +TArray portals; + +//========================================================================== +// +// +// +//========================================================================== + +GLSectorStackPortal *FPortal::GetGLPortal() +{ + if (glportal == NULL) glportal = new GLSectorStackPortal(this); + return glportal; +} + +//========================================================================== +// +// +// +//========================================================================== + +struct FCoverageVertex +{ + fixed_t x, y; + + bool operator !=(FCoverageVertex &other) + { + return x != other.x || y != other.y; + } +}; + +struct FCoverageLine +{ + FCoverageVertex v[2]; +}; + +struct FCoverageBuilder +{ + subsector_t *target; + FPortal *portal; + TArray collect; + FCoverageVertex center; + + //========================================================================== + // + // + // + //========================================================================== + + FCoverageBuilder(subsector_t *sub, FPortal *port) + { + target = sub; + portal = port; + } + + //========================================================================== + // + // GetIntersection + // + // adapted from P_InterceptVector + // + //========================================================================== + + bool GetIntersection(FCoverageVertex *v1, FCoverageVertex *v2, node_t *bsp, FCoverageVertex *v) + { + double frac; + double num; + double den; + + double v2x = (double)v1->x; + double v2y = (double)v1->y; + double v2dx = (double)(v2->x - v1->x); + double v2dy = (double)(v2->y - v1->y); + double v1x = (double)bsp->x; + double v1y = (double)bsp->y; + double v1dx = (double)bsp->dx; + double v1dy = (double)bsp->dy; + + den = v1dy*v2dx - v1dx*v2dy; + + if (den == 0) + return false; // parallel + + num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; + frac = num / den; + + if (frac < 0. || frac > 1.) return false; + + v->x = xs_RoundToInt(v2x + frac * v2dx); + v->y = xs_RoundToInt(v2y + frac * v2dy); + return true; + } + + //========================================================================== + // + // + // + //========================================================================== + + double PartitionDistance(FCoverageVertex *vt, node_t *node) + { + return fabs(double(-node->dy) * (vt->x - node->x) + double(node->dx) * (vt->y - node->y)) / node->len; + } + + //========================================================================== + // + // + // + //========================================================================== + + int PointOnSide(FCoverageVertex *vt, node_t *node) + { + return R_PointOnSide(vt->x, vt->y, node); + } + + //========================================================================== + // + // adapted from polyobject splitter + // + //========================================================================== + + void CollectNode(void *node, TArray &shape) + { + static TArray lists[2]; + const double COVERAGE_EPSILON = 6.; // same epsilon as the node builder + + if (!((size_t)node & 1)) // Keep going until found a subsector + { + node_t *bsp = (node_t *)node; + + int centerside = R_PointOnSide(center.x, center.y, bsp); + + lists[0].Clear(); + lists[1].Clear(); + for(unsigned i=0;ichildren[0], shape); + } + else if (lists[0].Size() == 0) + { + CollectNode(bsp->children[1], shape); + } + else + { + // copy the static arrays into local ones + TArray locallists[2]; + + for(int l=0;l<2;l++) + { + for (unsigned i=0;ichildren[0], locallists[0]); + CollectNode(bsp->children[1], locallists[1]); + } + } + else + { + // we reached a subsector so we can link the node with this subsector + subsector_t *sub = (subsector_t *)((BYTE *)node - 1); + collect.Push(int(sub-subsectors)); + } + } +}; + +//========================================================================== +// +// Calculate portal coverage for a single subsector +// +//========================================================================== + +void gl_BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, FPortal *portal) +{ + TArray shape; + double centerx=0, centery=0; + + shape.Resize(subsector->numlines); + for(unsigned i=0; inumlines; i++) + { + centerx += (shape[i].x = subsector->firstline[i].v1->x + portal->xDisplacement); + centery += (shape[i].y = subsector->firstline[i].v1->y + portal->yDisplacement); + } + + FCoverageBuilder build(subsector, portal); + build.center.x = xs_CRoundToInt(centerx / subsector->numlines); + build.center.y = xs_CRoundToInt(centery / subsector->numlines); + + build.CollectNode(nodes + numnodes - 1, shape); + coverage->subsectors = new DWORD[build.collect.Size()]; + coverage->sscount = build.collect.Size(); + memcpy(coverage->subsectors, &build.collect[0], build.collect.Size() * sizeof(DWORD)); +} + +//========================================================================== +// +// portal initialization +// +//========================================================================== + +static void CollectPortalSectors(FPortalMap &collection) +{ + for (int i=0;iCeilingSkyBox != NULL && sec->CeilingSkyBox->bAlways && sec->CeilingSkyBox->Mate != NULL) + { + FPortalID id = { sec->CeilingSkyBox->x - sec->CeilingSkyBox->Mate->x, + sec->CeilingSkyBox->y - sec->CeilingSkyBox->Mate->y}; + + FPortalSectors &sss = collection[id]; + FPortalSector ss = { sec, sector_t::ceiling }; + sss.Push(ss); + } + + if (sec->FloorSkyBox != NULL && sec->FloorSkyBox->bAlways && sec->FloorSkyBox->Mate != NULL) + { + FPortalID id = { sec->FloorSkyBox->x - sec->FloorSkyBox->Mate->x, + sec->FloorSkyBox->y - sec->FloorSkyBox->Mate->y }; + + FPortalSectors &sss = collection[id]; + FPortalSector ss = { sec, sector_t::floor }; + sss.Push(ss); + } + } +} + +void gl_InitPortals() +{ + FPortalMap collection; + + if (numnodes == 0) return; + + for(int i=0;idx; + double fdy = (double)no->dy; + no->len = (float)sqrt(fdx * fdx + fdy * fdy); + } + + CollectPortalSectors(collection); + portals.Clear(); + + FPortalMap::Iterator it(collection); + FPortalMap::Pair *pair; + int c = 0; + int planeflags = 0; + while (it.NextPair(pair)) + { + for(unsigned i=0;iValue.Size(); i++) + { + if (pair->Value[i].mPlane == sector_t::floor) planeflags |= 1; + else if (pair->Value[i].mPlane == sector_t::ceiling) planeflags |= 2; + } + for (int i=1;i<=2;i<<=1) + { + // For now, add separate portals for floor and ceiling. They can be merged once + // proper plane clipping is in. + if (planeflags & i) + { + FPortal *portal = new FPortal; + portal->xDisplacement = pair->Key.mXDisplacement; + portal->yDisplacement = pair->Key.mYDisplacement; + portal->plane = (i==1? sector_t::floor : sector_t::ceiling); /**/ + portal->glportal = NULL; + portals.Push(portal); + for(unsigned j=0;jValue.Size(); j++) + { + sector_t *sec = pair->Value[j].mSub; + int plane = pair->Value[j].mPlane; + if (portal->plane == plane) + { + for(int k=0;ksubsectorcount; k++) + { + subsector_t *sub = sec->subsectors[k]; + gl_BuildPortalCoverage(&sub->portalcoverage[plane], sub, portal); + } + sec->portals[plane] = portal; + } + } + } + } + } +} + +CCMD(dumpportals) +{ + for(unsigned i=0;ixDisplacement/65536.; + double ydisp = portals[i]->yDisplacement/65536.; + Printf(PRINT_LOG, "Portal #%d, %s, displacement = (%f,%f)\n", i, portals[i]->plane==0? "floor":"ceiling", + xdisp, ydisp); + Printf(PRINT_LOG, "Coverage:\n"); + for(int j=0;jrender_sector->portals[portals[i]->plane]; + if (port == portals[i]) + { + Printf(PRINT_LOG, "\tSubsector %d (%d):\n\t\t", j, sub->render_sector->sectornum); + for(unsigned k = 0;k< sub->numlines; k++) + { + Printf(PRINT_LOG, "(%.3f,%.3f), ", sub->firstline[k].v1->x/65536. + xdisp, sub->firstline[k].v1->y/65536. + ydisp); + } + Printf(PRINT_LOG, "\n\t\tCovered by subsectors:\n"); + FPortalCoverage *cov = &sub->portalcoverage[portals[i]->plane]; + for(int l = 0;l< cov->sscount; l++) + { + subsector_t *csub = &subsectors[cov->subsectors[l]]; + Printf(PRINT_LOG, "\t\t\t%5d (%4d): ", cov->subsectors[l], csub->render_sector->sectornum); + for(unsigned m = 0;m< csub->numlines; m++) + { + Printf(PRINT_LOG, "(%.3f,%.3f), ", csub->firstline[m].v1->x/65536., csub->firstline[m].v1->y/65536.); + } + Printf(PRINT_LOG, "\n"); + } + } + } + } +} diff --git a/src/gl/data/gl_sections.cpp b/src/gl/data/gl_sections.cpp new file mode 100644 index 000000000..34dbabd1d --- /dev/null +++ b/src/gl/data/gl_sections.cpp @@ -0,0 +1,847 @@ +/* +** gl_sections.cpp +** Splits sectors into continuous separate parts +** +**--------------------------------------------------------------------------- +** Copyright 2008 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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. +**--------------------------------------------------------------------------- +** +*/ +#if 0 + +#include "gl/system/gl_system.h" +#include "i_system.h" +#include "p_local.h" +#include "c_dispatch.h" +#include "gl/data/gl_sections.h" + +typedef void (CALLBACK *tessFunc)(); + +TArray SectionLines; +TArray SectionLoops; +TArray Sections; +TArray SectionForSubsector; + +CVAR (Bool, dumpsections, false, 0) + +#define ISDONE(no, p) (p[(no)>>3] & (1 << ((no)&7))) +#define SETDONE(no, p) p[(no)>>3] |= (1 << ((no)&7)) + +inline vertex_t *V1(side_t *s) +{ + line_t *ln = s->linedef; + return s == ln->sidedef[0]? ln->v1: ln->v2; +} + +inline vertex_t *V2(side_t *s) +{ + line_t *ln = s->linedef; + return s == ln->sidedef[0]? ln->v2: ln->v1; +} + +//========================================================================== +// +// +// +//========================================================================== + +class FSectionCreator +{ + static FSectionCreator *creator; + + BYTE *processed_segs; + BYTE *processed_subsectors; + int *section_for_segs; + + vertex_t *v1_l1, *v2_l1; + + FGLSectionLoop *loop; + FGLSection *section; // current working section + +public: + //========================================================================== + // + // + // + //========================================================================== + + FSectionCreator() + { + processed_segs = new BYTE[(numsegs+7)/8]; + processed_subsectors = new BYTE[(numsubsectors+7)/8]; + + memset(processed_segs, 0, (numsegs+7)/8); + memset(processed_subsectors, 0, (numsubsectors+7)/8); + + section_for_segs = new int[numsegs]; + memset(section_for_segs, -1, numsegs * sizeof(int)); + } + + //========================================================================== + // + // + // + //========================================================================== + + ~FSectionCreator() + { + delete [] processed_segs; + delete [] processed_subsectors; + delete [] section_for_segs; + } + + //========================================================================== + // + // + // + //========================================================================== + + void NewLoop() + { + section->numloops++; + loop = &SectionLoops[SectionLoops.Reserve(1)]; + loop->startline = SectionLines.Size(); + loop->numlines = 0 ; + } + + void NewSection(sector_t *sec) + { + section = &Sections[Sections.Reserve(1)]; + section->sector = sec; + section->subsectors.Clear(); + section->numloops = 0; + section->startloop = SectionLoops.Size(); + section->validcount = -1; + NewLoop(); + } + + void FinalizeSection() + { + } + + //========================================================================== + // + // + // + //========================================================================== + + bool AddSeg(seg_t *seg) + { + FGLSectionLine &line = SectionLines[SectionLines.Reserve(1)]; + + + bool firstline = loop->numlines == 0; + + if (ISDONE(seg-segs, processed_segs)) + { + // should never happen! + DPrintf("Tried to add seg %d to Sections twice. Cannot create Sections.\n", seg-segs); + return false; + } + + SETDONE(seg-segs, processed_segs); + section_for_segs[seg-segs] = Sections.Size()-1; + + line.start = seg->v1; + line.end = seg->v2; + line.sidedef = seg->sidedef; + line.linedef = seg->linedef; + line.refseg = seg; + line.polysub = NULL; + line.otherside = -1; + + if (loop->numlines == 0) + { + v1_l1 = seg->v1; + v2_l1 = seg->v2; + } + loop->numlines++; + return true; + } + + //========================================================================== + // + // Utility stuff + // + //========================================================================== + + sector_t *FrontRenderSector(seg_t *seg) + { + return seg->Subsector->render_sector; + } + + sector_t *BackRenderSector(seg_t *seg) + { + if (seg->PartnerSeg == NULL) return NULL; + return seg->PartnerSeg->Subsector->render_sector; + } + + bool IntraSectorSeg(seg_t *seg) + { + return FrontRenderSector(seg) == BackRenderSector(seg); + } + + + //========================================================================== + // + // returns the seg whose partner seg determines where this + // section continues + // + //========================================================================== + bool AddSubSector(subsector_t *subsec, vertex_t *startpt, seg_t **pNextSeg) + { + unsigned i = 0; + if (startpt != NULL) + { + // find the seg in this subsector that starts at the given vertex + for(i = 0; i < subsec->numlines; i++) + { + if (subsec->firstline[i].v1 == startpt) break; + } + if (i == subsec->numlines) + { + DPrintf("Vertex not found in subsector %d. Cannot create Sections.\n", subsec-subsectors); + return false; // Nodes are bad + } + } + else + { + // Find the first unprocessed non-miniseg + for(i = 0; i < subsec->numlines; i++) + { + seg_t *seg = subsec->firstline + i; + + if (seg->sidedef == NULL) continue; + if (IntraSectorSeg(seg)) continue; + if (ISDONE(seg-segs, processed_segs)) continue; + break; + } + if (i == subsec->numlines) + { + DPrintf("Unable to find a start seg. Cannot create Sections.\n"); + return false; // Nodes are bad + } + + startpt = subsec->firstline[i].v1; + } + + seg_t *thisseg = subsec->firstline + i; + if (IntraSectorSeg(thisseg)) + { + SETDONE(thisseg-segs, processed_segs); + // continue with the loop in the adjoining subsector + *pNextSeg = thisseg; + return true; + } + + while(1) + { + if (loop->numlines > 0 && thisseg->v1 == v1_l1 && thisseg->v2 == v2_l1) + { + // This loop is complete + *pNextSeg = NULL; + return true; + } + + if (!AddSeg(thisseg)) return NULL; + + i = (i+1) % subsec->numlines; + seg_t *nextseg = subsec->firstline + i; + + if (thisseg->v2 != nextseg->v1) + { + DPrintf("Segs in subsector %d are not continuous. Cannot create Sections.\n", subsec-subsectors); + return false; // Nodes are bad + } + + if (IntraSectorSeg(nextseg)) + { + SETDONE(nextseg-segs, processed_segs); + // continue with the loop in the adjoining subsector + *pNextSeg = nextseg; + return true; + } + thisseg = nextseg; + } + } + + //============================================================================= + // + // + // + //============================================================================= + + bool FindNextSeg(seg_t **pSeg) + { + // find an unprocessed non-miniseg or a miniseg with an unprocessed + // partner subsector that belongs to the same rendersector + for (unsigned i = 0; i < section->subsectors.Size(); i++) + { + for(unsigned j = 0; j < section->subsectors[i]->numlines; j++) + { + seg_t *seg = section->subsectors[i]->firstline + j; + bool intra = IntraSectorSeg(seg); + + if (!intra && !ISDONE(seg-segs, processed_segs)) + { + *pSeg = seg; + return true; + } + else if (intra && + !ISDONE(seg->PartnerSeg->Subsector-subsectors, processed_subsectors)) + { + *pSeg = seg->PartnerSeg; + return true; + } + } + } + *pSeg = NULL; + return true; + } + + //============================================================================= + // + // all segs and subsectors must be grouped into Sections + // + //============================================================================= + bool CheckSections() + { + bool res = true; + for (int i = 0; i < numsegs; i++) + { + if (segs[i].sidedef != NULL && !ISDONE(i, processed_segs) && !IntraSectorSeg(&segs[i])) + { + Printf("Seg %d (Linedef %d) not processed during section creation\n", i, segs[i].linedef-lines); + res = false; + } + } + for (int i = 0; i < numsubsectors; i++) + { + if (!ISDONE(i, processed_subsectors)) + { + Printf("Subsector %d (Sector %d) not processed during section creation\n", i, subsectors[i].sector-sectors); + res = false; + } + } + return res; + } + + //============================================================================= + // + // + // + //============================================================================= + void DeleteLine(int i) + { + SectionLines.Delete(i); + for(int i = SectionLoops.Size() - 1; i >= 0; i--) + { + FGLSectionLoop *loop = &SectionLoops[i]; + if (loop->startline > i) loop->startline--; + } + } + + //============================================================================= + // + // + // + //============================================================================= + void MergeLines(FGLSectionLoop *loop) + { + int i; + int deleted = 0; + FGLSectionLine *ln1; + FGLSectionLine *ln2; + // Merge identical lines in the list + for(i = loop->numlines - 1; i > 0; i--) + { + ln1 = loop->GetLine(i); + ln2 = loop->GetLine(i-1); + + if (ln1->sidedef == ln2->sidedef && ln1->otherside == ln2->otherside) + { + // identical references. These 2 lines can be merged. + ln2->end = ln1->end; + SectionLines.Delete(loop->startline + i); + loop->numlines--; + deleted++; + } + } + + // If we started in the middle of a sidedef the first and last lines + // may reference the same sidedef. check that, too. + + int loopstart = 0; + + ln1 = loop->GetLine(0); + for(i = loop->numlines - 1; i > 0; i--) + { + ln2 = loop->GetLine(i); + if (ln1->sidedef != ln2->sidedef || ln1->otherside != ln2->otherside) + break; + } + if (i < loop->numlines-1) + { + i++; + ln2 = loop->GetLine(i); + ln1->start = ln2->start; + SectionLines.Delete(loop->startline + i, loop->numlines - i); + deleted += loop->numlines - i; + loop->numlines = i; + } + + // Adjust all following loops + for(unsigned ii = unsigned(loop - &SectionLoops[0]) + 1; ii < SectionLoops.Size(); ii++) + { + SectionLoops[ii].startline -= deleted; + } + } + + //============================================================================= + // + // + // + //============================================================================= + void SetReferences() + { + for(unsigned i = 0; i < SectionLines.Size(); i++) + { + FGLSectionLine *ln = &SectionLines[i]; + seg_t *seg = ln->refseg; + + if (seg != NULL) + { + seg_t *partner = seg->PartnerSeg; + + if (seg->PartnerSeg == NULL) + { + ln->otherside = -1; + } + else + { + ln->otherside = section_for_segs[partner-segs]; + } + } + else + { + ln->otherside = -1; + } + } + + for(unsigned i = 0; i < SectionLoops.Size(); i++) + { + MergeLines(&SectionLoops[i]); + } + } + + //============================================================================= + // + // cbTessBegin + // + // called when the tesselation of a new loop starts + // + //============================================================================= + + static void CALLBACK cbTessBegin(GLenum type, void *section) + { + FGLSection *sect = (FGLSection*)section; + sect->vertices.Push(-int(type)); + } + + //============================================================================= + // + // cbTessError + // + // called when the tesselation failed + // + //============================================================================= + + static void CALLBACK cbTessError(GLenum error, void *section) + { + } + + //============================================================================= + // + // cbTessCombine + // + // called when the two or more vertexes are on the same coordinate + // + //============================================================================= + + static void CALLBACK cbTessCombine( GLdouble coords[3], void *vert[4], GLfloat w[4], void **dataOut ) + { + *dataOut = vert[0]; + } + + //============================================================================= + // + // cbTessVertex + // + // called when a vertex is found + // + //============================================================================= + + static void CALLBACK cbTessVertex( void *vert, void *section ) + { + FGLSection *sect = (FGLSection*)section; + sect->vertices.Push(int(intptr_t(vert))); + } + + //============================================================================= + // + // cbTessEnd + // + // called when the tesselation of a the current loop ends + // + //============================================================================= + + static void CALLBACK cbTessEnd(void *section) + { + } + + //============================================================================= + // + // + // + //============================================================================= + void tesselateSections() + { + // init tesselator + GLUtesselator *tess = gluNewTess(); + if (!tess) + { + return; + } + // set callbacks + gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (tessFunc)cbTessBegin); + gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (tessFunc)cbTessVertex); + gluTessCallback(tess, GLU_TESS_ERROR_DATA, (tessFunc)cbTessError); + gluTessCallback(tess, GLU_TESS_COMBINE, (tessFunc)cbTessCombine); + gluTessCallback(tess, GLU_TESS_END_DATA, (tessFunc)cbTessEnd); + + for(unsigned int i=0;inumloops; j++) + { + gluTessBeginContour(tess); + FGLSectionLoop *loop = sect->GetLoop(j); + for(int k=0; knumlines; k++) + { + FGLSectionLine *line = loop->GetLine(k); + vertex_t *vert = line->start; + GLdouble v[3] = { + -(double)vert->x/(double)FRACUNIT, // negate to get proper winding + 0.0, + (double)vert->y/(double)FRACUNIT + }; + gluTessVertex(tess, v, (void*)(vert - vertexes)); + } + gluTessEndContour(tess); + } + gluTessEndPolygon(tess); + sect->vertices.Push(-1337); + sect->vertices.ShrinkToFit(); + } + gluDeleteTess(tess); + } + + + //============================================================================= + // + // First mark all subsectors that have no outside boundaries as processed + // No line in such a subsector will ever be part of a section's border + // + //============================================================================= + + void MarkInternalSubsectors() + { + for(int i=0; i < numsubsectors; i++) + { + subsector_t *sub = &subsectors[i]; + int j; + + for(j=0; j < sub->numlines; j++) + { + seg_t *seg = sub->firstline + j; + if (!IntraSectorSeg(seg)) break; + } + if (j==sub->numlines) + { + // All lines are intra-sector so mark this as processed + SETDONE(i, processed_subsectors); + for(j=0; j < sub->numlines; j++) + { + seg_t *seg = sub->firstline + j; + SETDONE((sub->firstline-segs)+j, processed_segs); + if (seg->PartnerSeg != NULL) + { + SETDONE(int(seg->PartnerSeg - segs), processed_segs); + } + } + } + } + } + + //============================================================================= + // + // + // + //============================================================================= + bool CreateSections() + { + int pick = 0; + + MarkInternalSubsectors(); + while (pick < numsubsectors) + { + if (ISDONE(pick, processed_subsectors)) + { + pick++; + continue; + } + + + subsector_t *subsector = &subsectors[pick]; + + seg_t *workseg = NULL; + vertex_t *startpt = NULL; + + NewSection(subsector->render_sector); + while (1) + { + if (!ISDONE(subsector-subsectors, processed_subsectors)) + { + SETDONE(subsector-subsectors, processed_subsectors); + section->subsectors.Push(subsector); + SectionForSubsector[subsector - subsectors] = int(section - &Sections[0]); + } + + bool result = AddSubSector(subsector, startpt, &workseg); + + if (!result) + { + return false; // couldn't create Sections + } + else if (workseg != NULL) + { + // crossing into another subsector + seg_t *partner = workseg->PartnerSeg; + if (workseg->v2 != partner->v1) + { + DPrintf("Inconsistent subsector references in seg %d. Cannot create Sections.\n", workseg-segs); + return false; + } + subsector = partner->Subsector; + startpt = workseg->v1; + } + else + { + // loop complete. Check adjoining subsectors for other loops to + // be added to this section + if (!FindNextSeg(&workseg)) + { + return false; + } + else if (workseg == NULL) + { + // No more subsectors found. This section is complete! + FinalizeSection(); + break; + } + else + { + subsector = workseg->Subsector; + // If this is a regular seg, start there, otherwise start + // at the subsector's first seg + startpt = workseg->sidedef == NULL? NULL : workseg->v1; + + NewLoop(); + } + } + } + } + + if (!CheckSections()) return false; + SetReferences(); + + Sections.ShrinkToFit(); + SectionLoops.ShrinkToFit(); + SectionLines.ShrinkToFit(); + + tesselateSections(); + + return true; + } +}; + +FSectionCreator *FSectionCreator::creator; + + +//============================================================================= +// +// +// +//============================================================================= + +void DumpSection(int no, FGLSection *sect) +{ + Printf(PRINT_LOG, "Section %d, sector %d\n{\n", no, sect->sector->sectornum); + + for(int i = 0; i < sect->numloops; i++) + { + Printf(PRINT_LOG, "\tLoop %d\n\t{\n", i); + + FGLSectionLoop *loop = sect->GetLoop(i); + + for(int i = 0; i < loop->numlines; i++) + { + FGLSectionLine *ln = loop->GetLine(i); + if (ln->sidedef != NULL) + { + vertex_t *v1 = V1(ln->sidedef); + vertex_t *v2 = V2(ln->sidedef); + double dx = FIXED2FLOAT(v2->x-v1->x); + double dy = FIXED2FLOAT(v2->y-v1->y); + double dx1 = FIXED2FLOAT(ln->start->x-v1->x); + double dy1 = FIXED2FLOAT(ln->start->y-v1->y); + double dx2 = FIXED2FLOAT(ln->end->x-v1->x); + double dy2 = FIXED2FLOAT(ln->end->y-v1->y); + double d = sqrt(dx*dx+dy*dy); + double d1 = sqrt(dx1*dx1+dy1*dy1); + double d2 = sqrt(dx2*dx2+dy2*dy2); + + Printf(PRINT_LOG, "\t\tLinedef %d, %s: Start (%1.2f, %1.2f), End (%1.2f, %1.2f)", + ln->linedef - lines, ln->sidedef == ln->linedef->sidedef[0]? "front":"back", + ln->start->x/65536.f, ln->start->y/65536.f, + ln->end->x/65536.f, ln->end->y/65536.f); + + if (ln->otherside != -1) + { + Printf (PRINT_LOG, ", other side = %d", ln->otherside); + } + if (d1 > 0.005 || d2 < 0.995) + { + Printf(PRINT_LOG, ", Range = %1.3f, %1.3f", d1/d, d2/d); + } + } + else + { + Printf(PRINT_LOG, "\t\tMiniseg: Start (%1.3f, %1.3f), End (%1.3f, %1.3f)\n", + ln->start->x/65536.f, ln->start->y/65536.f, ln->end->x/65536.f, ln->end->y/65536.f); + + if (ln->otherside != -1) + { + Printf (PRINT_LOG, ", other side = %d", ln->otherside); + } + } + Printf(PRINT_LOG, "\n"); + } + Printf(PRINT_LOG, "\t}\n"); + } + int prim = 1; + for(unsigned i = 0; i < sect->vertices.Size(); i++) + { + int v = sect->vertices[i]; + if (v < 0) + { + if (i > 0) + { + Printf(PRINT_LOG, "\t}\n"); + } + switch (v) + { + case -GL_TRIANGLE_FAN: + Printf(PRINT_LOG, "\t%d: Triangle fan\n\t{\n", prim); + break; + + case -GL_TRIANGLE_STRIP: + Printf(PRINT_LOG, "\t%d: Triangle strip\n\t{\n", prim); + break; + + case -GL_TRIANGLES: + Printf(PRINT_LOG, "\t%d: Triangles\n\t{\n", prim); + break; + + default: + break; + } + prim++; + } + else + { + Printf(PRINT_LOG, "\t\tVertex %d: (%1.2f, %1.2f)\n", + v, vertexes[v].x/65536.f, vertexes[v].y/65536.f); + } + } + Printf(PRINT_LOG, "}\n\n"); +} + +//============================================================================= +// +// +// +//============================================================================= + +void DumpSections() +{ + for(unsigned i = 0; i < Sections.Size(); i++) + { + DumpSection(i, &Sections[i]); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void gl_CreateSections() +{ + SectionLines.Clear(); + SectionLoops.Clear(); + Sections.Clear(); + SectionForSubsector.Resize(numsubsectors); + memset(&SectionForSubsector[0], -1, numsubsectors * sizeof(SectionForSubsector[0])); + FSectionCreator creat; + creat.CreateSections(); + if (dumpsections) DumpSections(); +} + + + +#endif diff --git a/src/gl/data/gl_sections.h b/src/gl/data/gl_sections.h new file mode 100644 index 000000000..b64886791 --- /dev/null +++ b/src/gl/data/gl_sections.h @@ -0,0 +1,56 @@ + +#ifndef __GL_SECTIONS_H +#define __GL_SECTIONS_H + +#include "tarray.h" +#include "r_defs.h" + +struct FGLSectionLine +{ + vertex_t *start; + vertex_t *end; + side_t *sidedef; + line_t *linedef; + seg_t *refseg; // we need to reference at least one seg for each line. + subsector_t *polysub; // If this is part of a polyobject we need a reference to the containing subsector + int otherside; +}; + +struct FGLSectionLoop +{ + int startline; + int numlines; + + FGLSectionLine *GetLine(int no); +}; + +struct FGLSection +{ + sector_t *sector; + TArray subsectors; + TArray vertices; + int startloop; + int numloops; + int validcount; + + FGLSectionLoop *GetLoop(int no); +}; + +extern TArray SectionLines; +extern TArray SectionLoops; +extern TArray Sections; +extern TArray SectionForSubsector; + +inline FGLSectionLine *FGLSectionLoop::GetLine(int no) +{ + return &SectionLines[startline + no]; +} + +inline FGLSectionLoop *FGLSection::GetLoop(int no) +{ + return &SectionLoops[startloop + no]; +} + +void gl_CreateSections(); + +#endif \ No newline at end of file diff --git a/src/gl/data/gl_setup.cpp b/src/gl/data/gl_setup.cpp new file mode 100644 index 000000000..d822ac440 --- /dev/null +++ b/src/gl/data/gl_setup.cpp @@ -0,0 +1,736 @@ +/* +** gl_setup.cpp +** Initializes the data structures required by the GL renderer to handle +** a level +** +**--------------------------------------------------------------------------- +** 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "colormatcher.h" +#include "i_system.h" +#include "p_local.h" +#include "p_lnspec.h" +#include "c_dispatch.h" +#include "r_sky.h" +#include "sc_man.h" +#include "w_wad.h" +#include "gi.h" +#include "p_setup.h" +#include "g_level.h" + +#include "gl/renderer/gl_renderer.h" +#include "gl/data/gl_data.h" +#include "gl/data/gl_vertexbuffer.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/dynlights/gl_glow.h" +#include "gl/utility/gl_clock.h" +#include "gl/gl_functions.h" + +void InitGLRMapinfoData(); +void gl_InitData(); + +//========================================================================== +// +// +// +//========================================================================== + +static void DoSetMapSection(subsector_t *sub, int num) +{ + sub->mapsection = num; + + for(DWORD i=0;inumlines;i++) + { + seg_t * seg = sub->firstline + i; + + if (seg->PartnerSeg) + { + subsector_t * sub2 = seg->PartnerSeg->Subsector; + + if (sub2->mapsection != num) + { + assert(sub2->mapsection == 0); + DoSetMapSection(sub2, num); + } + } + } +} + +//========================================================================== +// +// Merge sections. This is needed in case the map contains errors +// like overlapping lines resulting in abnormal subsectors. +// +// This function ensures that any vertex position can only be in one section. +// +//========================================================================== + +struct cvertex_t +{ + fixed_t x, y; + + operator int () const { return ((x>>16)&0xffff) | y; } + bool operator!= (const cvertex_t &other) const { return x != other.x || y != other.y; } + cvertex_t& operator =(const vertex_t *v) { x = v->x; y = v->y; return *this; } +}; + +typedef TMap FSectionVertexMap; + +static int MergeMapSections(int num) +{ + FSectionVertexMap vmap; + FSectionVertexMap::Pair *pair; + TArray sectmap; + TArray sectvalid; + sectmap.Resize(num); + sectvalid.Resize(num); + for(int i=0;iSubsector->mapsection; + for(int j=0;j<2;j++) + { + vt = j==0? seg->v1:seg->v2; + vmap[vt] = section; + } + } + + // second step: Check if any seg references more than one mapsection, either by subsector or by vertex + for(DWORD i=0;iSubsector->mapsection; + for(int j=0;j<2;j++) + { + vt = j==0? seg->v1:seg->v2; + int vsection = vmap[vt]; + + if (vsection != section) + { + // These 2 sections should be merged + for(int k=0;kValue == vsection) pair->Value = section; + } + sectvalid[vsection-1] = false; + } + } + } + for(int i=0;inumlines;i++) + { + seg_t * seg = sub->firstline + i; + + if (seg->PartnerSeg) + { + subsector_t * sub2 = seg->PartnerSeg->Subsector; + + if (!(sub2->hacked&1) && sub2->render_sector == sub->render_sector) + { + sub2->hacked|=1; + SpreadHackedFlag (sub2); + } + } + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +static void PrepareSectorData() +{ + int i; + TArray undetermined; + subsector_t * ss; + + // now group the subsectors by sector + subsector_t ** subsectorbuffer = new subsector_t * [numsubsectors]; + + for(i=0, ss=subsectors; irender_sector->subsectorcount++; + } + + for (i=0; irender_sector->subsectors[ss->render_sector->subsectorcount++]=ss; + } + + // marks all malformed subsectors so rendering tricks using them can be handled more easily + for (i = 0; i < numsubsectors; i++) + { + if (subsectors[i].sector == subsectors[i].render_sector) + { + seg_t * seg = subsectors[i].firstline; + for(DWORD j=0;jSubsector->render_sector) + { + DPrintf("Found hack: (%d,%d) (%d,%d)\n", seg[j].v1->x>>16, seg[j].v1->y>>16, seg[j].v2->x>>16, seg[j].v2->y>>16); + subsectors[i].hacked|=1; + SpreadHackedFlag(&subsectors[i]); + } + if (seg[j].PartnerSeg==NULL) subsectors[i].hacked|=2; // used for quick termination checks + } + } + } + SetMapSections(); +} + +//========================================================================== +// +// Some processing for transparent door hacks using a floor raised by 1 map unit +// - This will be used to lower the floor of such sectors by one map unit +// +//========================================================================== + +static void PrepareTransparentDoors(sector_t * sector) +{ + bool solidwall=false; + int notextures=0; + int nobtextures=0; + int selfref=0; + int i; + sector_t * nextsec=NULL; + +#ifdef _MSC_VER +#ifdef _DEBUG + if (sector-sectors==2) + { + __asm nop + } +#endif +#endif + + P_Recalculate3DFloors(sector); + if (sector->subsectorcount==0) return; + + sector->transdoorheight=sector->GetPlaneTexZ(sector_t::floor); + sector->transdoor= !(sector->e->XFloor.ffloors.Size() || sector->heightsec || sector->floorplane.a || sector->floorplane.b); + + if (sector->transdoor) + { + for (i=0; ilinecount; i++) + { + if (sector->lines[i]->frontsector==sector->lines[i]->backsector) + { + selfref++; + continue; + } + + sector_t * sec=getNextSector(sector->lines[i], sector); + if (sec==NULL) + { + solidwall=true; + continue; + } + else + { + nextsec=sec; + + int side = sector->lines[i]->sidedef[0]->sector == sec; + + if (sector->GetPlaneTexZ(sector_t::floor)!=sec->GetPlaneTexZ(sector_t::floor)+FRACUNIT) + { + sector->transdoor=false; + return; + } + if (!sector->lines[i]->sidedef[1-side]->GetTexture(side_t::top).isValid()) notextures++; + if (!sector->lines[i]->sidedef[1-side]->GetTexture(side_t::bottom).isValid()) nobtextures++; + } + } + if (sector->GetTexture(sector_t::ceiling)==skyflatnum) + { + sector->transdoor=false; + return; + } + + if (selfref+nobtextures!=sector->linecount) + { + sector->transdoor=false; + } + + if (selfref+notextures!=sector->linecount) + { + // This is a crude attempt to fix an incorrect transparent door effect I found in some + // WolfenDoom maps but considering the amount of code required to handle it I left it in. + // Do this only if the sector only contains one-sided walls or ones with no lower texture. + if (solidwall) + { + if (solidwall+nobtextures+selfref==sector->linecount && nextsec) + { + sector->heightsec=nextsec; + sector->heightsec->MoreFlags=0; + } + sector->transdoor=false; + } + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +static void AddToVertex(const sector_t * sec, TArray & list) +{ + int secno = int(sec-sectors); + + for(unsigned i=0;i * vt_sectorlists; + + int i,j,k; + unsigned int l; + + vt_sectorlists = new TArray[numvertexes]; + + + for(i=0;iv1 : line->v2; + + for(k=0;k<2;k++) + { + sector_t * sec = k==0? line->frontsector : line->backsector; + + if (sec) + { + extsector_t::xfloor &x = sec->e->XFloor; + + AddToVertex(sec, vt_sectorlists[v-vertexes]); + if (sec->heightsec) AddToVertex(sec->heightsec, vt_sectorlists[v-vertexes]); + + for(l=0;lflags & FF_EXISTS)) continue; + if (rover->flags&FF_NOSHADE) continue; // FF_NOSHADE doesn't create any wall splits + + AddToVertex(rover->model, vt_sectorlists[v-vertexes]); + } + } + } + } + } + + for(i=0;i1) + { + vertexes[i].numsectors= cnt; + vertexes[i].sectors=new sector_t*[cnt]; + vertexes[i].heightlist = new float[cnt*2]; + for(int j=0;jsidedef[0] == &sides[sdnum]) + { + v1->X = ln->v1->fx; + v1->Y = ln->v1->fy; + v2->X = ln->v2->fx; + v2->Y = ln->v2->fy; + } + else + { + v2->X = ln->v1->fx; + v2->Y = ln->v1->fy; + v1->X = ln->v2->fx; + v1->Y = ln->v2->fy; + } +} + +static int STACK_ARGS segcmp(const void *a, const void *b) +{ + seg_t *A = *(seg_t**)a; + seg_t *B = *(seg_t**)b; + return xs_RoundToInt(FRACUNIT*(A->sidefrac - B->sidefrac)); +} + +//========================================================================== +// +// Group segs to sidedefs +// +//========================================================================== + +static void PrepareSegs() +{ + int *segcount = new int[numsides]; + int realsegs = 0; + + // Get floatng point coordinates of vertices + for(int i = 0; i < numvertexes; i++) + { + vertexes[i].fx = FIXED2FLOAT(vertexes[i].x); + vertexes[i].fy = FIXED2FLOAT(vertexes[i].y); + vertexes[i].dirty = true; + } + + // count the segs + memset(segcount, 0, numsides * sizeof(int)); + + // set up the extra data in case the map was loaded with regular nodes that might pass as GL nodes. + if (glsegextras == NULL) + { + for(int i=0;iPartnerSeg = &segs[partner]; + else seg->PartnerSeg = NULL; + seg->Subsector = glsegextras[i].Subsector; + } + } + + for(int i=0;isidedef == NULL) continue; // miniseg + int sidenum = int(seg->sidedef - sides); + + realsegs++; + segcount[sidenum]++; + FVector2 sidestart, sideend, segend(seg->v2->fx, seg->v2->fy); + GetSideVertices(sidenum, &sidestart, &sideend); + + sideend -=sidestart; + segend -= sidestart; + + seg->sidefrac = float(segend.Length() / sideend.Length()); + } + + // allocate memory + sides[0].segs = new seg_t*[realsegs]; + sides[0].numsegs = 0; + + for(int i = 1; i < numsides; i++) + { + sides[i].segs = sides[i-1].segs + segcount[i-1]; + sides[i].numsegs = 0; + } + delete [] segcount; + + // assign the segs + for(int i=0;isidedef != NULL) seg->sidedef->segs[seg->sidedef->numsegs++] = seg; + } + + // sort the segs + for(int i = 0; i < numsides; i++) + { + if (sides[i].numsegs > 1) qsort(sides[i].segs, sides[i].numsegs, sizeof(seg_t*), segcmp); + } +} + +//========================================================================== +// +// Initialize the level data for the GL renderer +// +//========================================================================== +extern int restart; + +void gl_PreprocessLevel() +{ + int i; + + static int datadone=-1; + + + if (datadone != restart) + { + datadone = restart; + gl_InitData(); + } + + PrepareSegs(); + PrepareSectorData(); + InitVertexData(); + for(i=0;iSetupLevel(); + } + +#if 0 + gl_CreateSections(); +#endif + + InitGLRMapinfoData(); +} + + + +//========================================================================== +// +// Cleans up all the GL data for the last level +// +//========================================================================== + +void gl_CleanLevelData() +{ + // Dynamic lights must be destroyed before the sector information here is deleted. + TThinkerIterator it(STAT_DLIGHT); + AActor * mo=it.Next(); + while (mo) + { + AActor * next = it.Next(); + mo->Destroy(); + mo=next; + } + + if (vertexes != NULL) + { + for(int i = 0; i < numvertexes; i++) if (vertexes[i].numsectors > 0) + { + if (vertexes[i].sectors != NULL) + { + delete [] vertexes[i].sectors; + vertexes[i].sectors = NULL; + } + if (vertexes[i].heightlist != NULL) + { + delete [] vertexes[i].heightlist; + vertexes[i].heightlist = NULL; + } + } + } + + if (sides && sides[0].segs) + { + delete [] sides[0].segs; + sides[0].segs = NULL; + } + if (sectors && sectors[0].subsectors) + { + delete [] sectors[0].subsectors; + sectors[0].subsectors = NULL; + } + for (int i=0;isectornum, int(subsectors[j].firstline->linedef-lines)); + break; + } + } + } +} \ No newline at end of file diff --git a/src/gl/data/gl_vertexbuffer.cpp b/src/gl/data/gl_vertexbuffer.cpp new file mode 100644 index 000000000..036224338 --- /dev/null +++ b/src/gl/data/gl_vertexbuffer.cpp @@ -0,0 +1,420 @@ +/* +** glc_vertexbuffer.cpp +** Vertex buffer handling. +** +**--------------------------------------------------------------------------- +** 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "p_local.h" +#include "m_argv.h" +#include "c_cvars.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/data/gl_data.h" +#include "gl/data/gl_vertexbuffer.h" + + +CUSTOM_CVAR(Int, gl_usevbo, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (self < -1 || self > 2 || !(gl.flags&RFL_VBO)) + { + self = 0; + } + else if (self == -1) + { + if (!(gl.flags & RFL_NVIDIA)) self = 0; + else self = 2; + } + else if (GLRenderer != NULL && GLRenderer->mVBO != NULL && GLRenderer->mVBO->vbo_arg != self) + { + Printf("Vertex buffer use will be changed for the next level.\n"); + } +} + +//========================================================================== +// +// Create / destroy the VBO +// +//========================================================================== + +FVertexBuffer::FVertexBuffer() +{ + vbo_id = 0; + if (gl.flags&RFL_VBO) + { + if (gl_usevbo == -1) gl_usevbo.Callback(); + gl.GenBuffers(1, &vbo_id); + } +} + +FVertexBuffer::~FVertexBuffer() +{ + if (vbo_id != 0) + { + gl.DeleteBuffers(1, &vbo_id); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +FFlatVertexBuffer::FFlatVertexBuffer() +: FVertexBuffer() +{ + if (!(gl.flags&RFL_VBO)) + { + vbo_arg = 0; + } + else + { + vbo_arg = gl_usevbo; + } + map = NULL; +} + +FFlatVertexBuffer::~FFlatVertexBuffer() +{ + UnmapVBO(); +} + +//========================================================================== +// +// Initialize a single vertex +// +//========================================================================== + +void FFlatVertex::SetFlatVertex(vertex_t *vt, const secplane_t & plane) +{ + x = vt->fx; + y = vt->fy; + z = plane.ZatPoint(vt->fx, vt->fy); + u = vt->fx/64.f; + v = -vt->fy/64.f; + w = dc = df = 0; +} + +//========================================================================== +// +// Find a 3D floor +// +//========================================================================== + +static F3DFloor *Find3DFloor(sector_t *target, sector_t *model) +{ + for(unsigned i=0; ie->XFloor.ffloors.Size(); i++) + { + F3DFloor *ffloor = target->e->XFloor.ffloors[i]; + if (ffloor->model == model) return ffloor; + } + return NULL; +} + +//========================================================================== +// +// Creates the vertices for one plane in one subsector +// +//========================================================================== + +int FFlatVertexBuffer::CreateSubsectorVertices(subsector_t *sub, const secplane_t &plane, int floor) +{ + int idx = vbo_shadowdata.Reserve(sub->numlines); + for(unsigned int k=0; knumlines; k++, idx++) + { + vbo_shadowdata[idx].SetFlatVertex(sub->firstline[k].v1, plane); + if (sub->sector->transdoor && floor) vbo_shadowdata[idx].z -= 1.f; + } + return idx; +} + +//========================================================================== +// +// Creates the vertices for one plane in one subsector +// +//========================================================================== + +int FFlatVertexBuffer::CreateSectorVertices(sector_t *sec, const secplane_t &plane, int floor) +{ + int rt = vbo_shadowdata.Size(); + // First calculate the vertices for the sector itself + for(int j=0; jsubsectorcount; j++) + { + subsector_t *sub = sec->subsectors[j]; + CreateSubsectorVertices(sub, plane, floor); + } + return rt; +} + +//========================================================================== +// +// +// +//========================================================================== + +int FFlatVertexBuffer::CreateVertices(int h, sector_t *sec, const secplane_t &plane, int floor) +{ + // First calculate the vertices for the sector itself + sec->vboheight[h] = sec->GetPlaneTexZ(h); + sec->vboindex[h] = CreateSectorVertices(sec, plane, floor); + + // Next are all sectors using this one as heightsec + TArray &fakes = sec->e->FakeFloor.Sectors; + for (unsigned g=0; gvboindex[2+h] = CreateSectorVertices(fsec, plane, false); + } + + // and finally all attached 3D floors + TArray &xf = sec->e->XFloor.attached; + for (unsigned g=0; gflags & FF_RENDERPLANES) + { + bool dotop = (ffloor->top.model == sec) && (ffloor->top.isceiling == h); + bool dobottom = (ffloor->bottom.model == sec) && (ffloor->bottom.isceiling == h); + + if (dotop || dobottom) + { + if (dotop) ffloor->top.vindex = vbo_shadowdata.Size(); + if (dobottom) ffloor->bottom.vindex = vbo_shadowdata.Size(); + + CreateSectorVertices(fsec, plane, false); + } + } + } + sec->vbocount[h] = vbo_shadowdata.Size() - sec->vboindex[h]; + return sec->vboindex[h]; +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FFlatVertexBuffer::CreateFlatVBO() +{ + for (int h = sector_t::floor; h <= sector_t::ceiling; h++) + { + for(int i=0; iXFloor.ffloors.Size(); j++) + { + F3DFloor *ff = sectors[i].e->XFloor.ffloors[j]; + + if (ff->top.model == §ors[i]) + { + ff->top.vindex = sectors[i].vboindex[ff->top.isceiling]; + } + if (ff->bottom.model == §ors[i]) + { + ff->bottom.vindex = sectors[i].vboindex[ff->top.isceiling]; + } + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FFlatVertexBuffer::MapVBO() +{ + if (map == NULL) + { + gl.BindBuffer(GL_ARRAY_BUFFER, vbo_id); + map = (FFlatVertex*)gl.MapBufferRange(GL_ARRAY_BUFFER, 0, vbo_shadowdata.Size() * sizeof(FFlatVertex), + GL_MAP_WRITE_BIT|GL_MAP_FLUSH_EXPLICIT_BIT|GL_MAP_UNSYNCHRONIZED_BIT); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FFlatVertexBuffer::UnmapVBO() +{ + if (map != NULL) + { + gl.UnmapBuffer(GL_ARRAY_BUFFER); + map = NULL; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FFlatVertexBuffer::UpdatePlaneVertices(sector_t *sec, int plane) +{ + int startvt = sec->vboindex[plane]; + int countvt = sec->vbocount[plane]; + secplane_t &splane = sec->GetSecPlane(plane); + FFlatVertex *vt = &vbo_shadowdata[startvt]; + for(int i=0; iz = splane.ZatPoint(vt->x, vt->y); + if (plane == sector_t::floor && sec->transdoor) vt->z -= 1; + } + if (gl.flags & RFL_MAP_BUFFER_RANGE) + { + MapVBO(); + if (map == NULL) return; // Error + memcpy(&map[startvt], &vbo_shadowdata[startvt], countvt * sizeof(FFlatVertex)); + gl.FlushMappedBufferRange(GL_ARRAY_BUFFER, startvt * sizeof(FFlatVertex), countvt * sizeof(FFlatVertex)); + } + else + { + gl.BufferSubData(GL_ARRAY_BUFFER, startvt * sizeof(FFlatVertex), countvt * sizeof(FFlatVertex), &vbo_shadowdata[startvt]); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FFlatVertexBuffer::CreateVBO() +{ + vbo_shadowdata.Clear(); + if (vbo_arg > 0) + { + CreateFlatVBO(); + gl.BindBuffer(GL_ARRAY_BUFFER, vbo_id); + gl.BufferData(GL_ARRAY_BUFFER, vbo_shadowdata.Size() * sizeof(FFlatVertex), &vbo_shadowdata[0], GL_DYNAMIC_DRAW); + } + else if (sectors) + { + // set all VBO info to invalid values so that we can save some checks in the rendering code + for(int i=0;i 0) + { + UnmapVBO(); + gl.BindBuffer(GL_ARRAY_BUFFER, vbo_id); + gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glVertexPointer(3,GL_FLOAT, sizeof(FFlatVertex), &VTO->x); + glTexCoordPointer(2,GL_FLOAT, sizeof(FFlatVertex), &VTO->u); + gl.EnableClientState(GL_VERTEX_ARRAY); + gl.EnableClientState(GL_TEXTURE_COORD_ARRAY); + gl.DisableClientState(GL_INDEX_ARRAY); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FFlatVertexBuffer::CheckPlanes(sector_t *sector) +{ + if (sector->GetPlaneTexZ(sector_t::ceiling) != sector->vboheight[sector_t::ceiling]) + { + if (sector->ceilingdata == NULL) // only update if there's no thinker attached + { + UpdatePlaneVertices(sector, sector_t::ceiling); + sector->vboheight[sector_t::ceiling] = sector->GetPlaneTexZ(sector_t::ceiling); + } + } + if (sector->GetPlaneTexZ(sector_t::floor) != sector->vboheight[sector_t::floor]) + { + if (sector->floordata == NULL) // only update if there's no thinker attached + { + UpdatePlaneVertices(sector, sector_t::floor); + sector->vboheight[sector_t::floor] = sector->GetPlaneTexZ(sector_t::floor); + } + } +} + +//========================================================================== +// +// checks the validity of all planes attached to this sector +// and updates them if possible. Anything moving will not be +// updated unless it stops. This is to ensure that we never +// have to synchronize with the rendering process. +// +//========================================================================== + +void FFlatVertexBuffer::CheckUpdate(sector_t *sector) +{ + if (vbo_arg == 2) + { + CheckPlanes(sector); + sector_t *hs = sector->GetHeightSec(); + if (hs != NULL) CheckPlanes(hs); + for(unsigned i = 0; i < sector->e->XFloor.ffloors.Size(); i++) + CheckPlanes(sector->e->XFloor.ffloors[i]->model); + } +} \ No newline at end of file diff --git a/src/gl/data/gl_vertexbuffer.h b/src/gl/data/gl_vertexbuffer.h new file mode 100644 index 000000000..6c2b83a15 --- /dev/null +++ b/src/gl/data/gl_vertexbuffer.h @@ -0,0 +1,62 @@ +#ifndef __VERTEXBUFFER_H +#define __VERTEXBUFFER_H + +#include "tarray.h" + +struct vertex_t; +struct secplane_t; +struct subsector_t; +struct sector_t; + + +class FVertexBuffer +{ +protected: + unsigned int vbo_id; + +public: + FVertexBuffer(); + virtual ~FVertexBuffer(); + virtual void BindVBO() = 0; +}; + +struct FFlatVertex // exactly 32 bytes large +{ + float x,z,y,w; // w only for padding to make one vertex 32 bytes - maybe it will find some use later + float u,v; // texture coordinates + float dc, df; // distance to floor and ceiling on walls - used for glowing + + void SetFlatVertex(vertex_t *vt, const secplane_t &plane); +}; + +#define VTO ((FFlatVertex*)NULL) + + +class FFlatVertexBuffer : public FVertexBuffer +{ + FFlatVertex *map; + + void MapVBO(); + void CheckPlanes(sector_t *sector); + +public: + int vbo_arg; + TArray vbo_shadowdata; // this is kept around for non-VBO rendering + +public: + FFlatVertexBuffer(); + ~FFlatVertexBuffer(); + + int CreateSubsectorVertices(subsector_t *sub, const secplane_t &plane, int floor); + int CreateSectorVertices(sector_t *sec, const secplane_t &plane, int floor); + int CreateVertices(int h, sector_t *sec, const secplane_t &plane, int floor); + void CreateFlatVBO(); + void CreateVBO(); + void UpdatePlaneVertices(sector_t *sec, int plane); + void BindVBO(); + void CheckUpdate(sector_t *sector); + void UnmapVBO(); + +}; + +#endif \ No newline at end of file diff --git a/src/gl/dynlights/a_dynlight.cpp b/src/gl/dynlights/a_dynlight.cpp new file mode 100644 index 000000000..731b4dfd2 --- /dev/null +++ b/src/gl/dynlights/a_dynlight.cpp @@ -0,0 +1,742 @@ +/* +** a_dynlight.cpp +** Implements actors representing dynamic lights (hardware independent) +** +**--------------------------------------------------------------------------- +** Copyright 2003 Timothy 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 "templates.h" +#include "m_random.h" +#include "p_local.h" +#include "c_dispatch.h" +#include "g_level.h" +#include "thingdef/thingdef.h" +#include "i_system.h" + + +#include "gl/renderer/gl_renderer.h" +#include "gl/data/gl_data.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/utility/gl_convert.h" +#include "gl/utility/gl_templates.h" + +EXTERN_CVAR (Float, gl_lights_size); +EXTERN_CVAR (Bool, gl_lights_additive); + + +//========================================================================== +// +//========================================================================== +DEFINE_CLASS_PROPERTY(type, S, DynamicLight) +{ + PROP_STRING_PARM(str, 0); + static const char * ltype_names[]={ + "Point","Pulse","Flicker","Sector","RandomFlicker", "ColorPulse", "ColorFlicker", "RandomColorFlicker", NULL}; + + static const int ltype_values[]={ + PointLight, PulseLight, FlickerLight, SectorLight, RandomFlickerLight, ColorPulseLight, ColorFlickerLight, RandomColorFlickerLight }; + + int style = MatchString(str, ltype_names); + if (style < 0) I_Error("Unknown light type '%s'", str); + defaults->lighttype = ltype_values[style]; +} + +//========================================================================== +// +// Actor classes +// +// For flexibility all functionality has been packed into a single class +// which is controlled by flags +// +//========================================================================== +IMPLEMENT_CLASS (ADynamicLight) +IMPLEMENT_CLASS (AVavoomLight) +IMPLEMENT_CLASS (AVavoomLightWhite) +IMPLEMENT_CLASS (AVavoomLightColor) + +void AVavoomLight::BeginPlay () +{ + // This must not call Super::BeginPlay! + ChangeStatNum(STAT_DLIGHT); + if (Sector) z -= Sector->floorplane.ZatPoint(x, y); + lighttype = PointLight; +} + +void AVavoomLightWhite::BeginPlay () +{ + m_intensity[0] = args[0] * 4; + args[LIGHT_RED] = 128; + args[LIGHT_GREEN] = 128; + args[LIGHT_BLUE] = 128; + + Super::BeginPlay(); +} + +void AVavoomLightColor::BeginPlay () +{ + int l_args[5]; + memcpy(l_args, args, sizeof(l_args)); + memset(args, 0, 5); + m_intensity[0] = l_args[0] * 4; + args[LIGHT_RED] = l_args[1] >> 1; + args[LIGHT_GREEN] = l_args[2] >> 1; + args[LIGHT_BLUE] = l_args[3] >> 1; + + Super::BeginPlay(); +} + +static FRandom randLight; + +//========================================================================== +// +// Base class +// +//========================================================================== + +//========================================================================== +// +// +// +//========================================================================== +void ADynamicLight::Serialize(FArchive &arc) +{ + Super::Serialize (arc); + arc << lightflags << lighttype; + arc << m_tickCount << m_currentIntensity; + arc << m_intensity[0] << m_intensity[1]; + + if (lighttype == PulseLight) arc << m_lastUpdate << m_cycler; + if (arc.IsLoading()) LinkLight(); + +} + + +//========================================================================== +// +// +// +//========================================================================== +void ADynamicLight::BeginPlay() +{ + //Super::BeginPlay(); + ChangeStatNum(STAT_DLIGHT); + + m_intensity[0] = args[LIGHT_INTENSITY]; + m_intensity[1] = args[LIGHT_SECONDARY_INTENSITY]; +} + +//========================================================================== +// +// +// +//========================================================================== +void ADynamicLight::PostBeginPlay() +{ + Super::PostBeginPlay(); + + if (!(SpawnFlags & MTF_DORMANT)) + { + Activate (NULL); + } + + subsector = R_PointInSubsector(x,y); +} + + +//========================================================================== +// +// +// +//========================================================================== +void ADynamicLight::Activate(AActor *activator) +{ + //Super::Activate(activator); + flags2&=~MF2_DORMANT; + + m_currentIntensity = float(m_intensity[0]); + m_tickCount = 0; + + if (lighttype == PulseLight) + { + float pulseTime = ANGLE_TO_FLOAT(this->angle) / TICRATE; + + m_lastUpdate = level.maptime; + m_cycler.SetParams(float(m_intensity[1]), float(m_intensity[0]), pulseTime); + m_cycler.ShouldCycle(true); + m_cycler.SetCycleType(CYCLE_Sin); + m_currentIntensity = (BYTE)m_cycler.GetVal(); + } +} + + +//========================================================================== +// +// +// +//========================================================================== +void ADynamicLight::Deactivate(AActor *activator) +{ + //Super::Deactivate(activator); + flags2|=MF2_DORMANT; +} + + +//========================================================================== +// +// +// +//========================================================================== +void ADynamicLight::Tick() +{ + + if (IsOwned()) + { + if (!target || !target->state) + { + this->Destroy(); + return; + } + if (target->flags & MF_UNMORPHED) return; + } + + // Don't bother if the light won't be shown + if (!IsActive()) return; + + // I am doing this with a type field so that I can dynamically alter the type of light + // without having to create or maintain multiple objects. + switch(lighttype) + { + case PulseLight: + { + float diff = (level.maptime - m_lastUpdate) / (float)TICRATE; + + m_lastUpdate = level.maptime; + m_cycler.Update(diff); + m_currentIntensity = m_cycler.GetVal(); + break; + } + + case FlickerLight: + { + BYTE rnd = randLight(); + float pct = ANGLE_TO_FLOAT(angle)/360.f; + + m_currentIntensity = float(m_intensity[rnd >= pct * 255]); + break; + } + + case RandomFlickerLight: + { + int flickerRange = m_intensity[1] - m_intensity[0]; + float amt = randLight() / 255.f; + + m_tickCount++; + + if (m_tickCount > ANGLE_TO_FLOAT(angle)) + { + m_currentIntensity = float(m_intensity[0] + (amt * flickerRange)); + m_tickCount = 0; + } + break; + } + +#if 0 + // These need some more work elsewhere + case ColorFlickerLight: + { + BYTE rnd = randLight(); + float pct = ANGLE_TO_FLOAT(angle)/360.f; + + m_currentIntensity = m_intensity[rnd >= pct * 255]; + break; + } + + case RandomColorFlickerLight: + { + int flickerRange = m_intensity[1] - m_intensity[0]; + float amt = randLight() / 255.f; + + m_tickCount++; + + if (m_tickCount > ANGLE_TO_FLOAT(angle)) + { + m_currentIntensity = m_intensity[0] + (amt * flickerRange); + m_tickCount = 0; + } + break; + } +#endif + + case SectorLight: + { + float intensity; + float scale = args[LIGHT_SCALE] / 8.f; + + if (scale == 0.f) scale = 1.f; + + intensity = Sector->lightlevel * scale; + intensity = clamp(intensity, 0.f, 255.f); + + m_currentIntensity = intensity; + break; + } + + case PointLight: + m_currentIntensity = float(m_intensity[0]); + break; + } + + UpdateLocation(); +} + + + + +//========================================================================== +// +// +// +//========================================================================== +void ADynamicLight::UpdateLocation() +{ + fixed_t oldx=x; + fixed_t oldy=y; + fixed_t oldradius=radius; + float intensity; + + if (IsActive()) + { + if (target) + { + angle_t angle = target->angle>>ANGLETOFINESHIFT; + PrevX = x = target->x + FixedMul(m_offX, finecosine[angle]) + FixedMul(m_offZ, finesine[angle]); + PrevY = y = target->y + FixedMul(m_offX, finesine[angle]) - FixedMul(m_offZ, finecosine[angle]); + PrevZ = z = target->z + m_offY + target->GetBobOffset(); + subsector = R_PointInSubsector(x, y); + Sector = subsector->sector; + } + + + // The radius being used here is always the maximum possible with the + // current settings. This avoids constant relinking of flickering lights + + if (lighttype == FlickerLight || lighttype == RandomFlickerLight) + { + intensity = float(m_intensity[1]); + } + else + { + intensity = m_currentIntensity; + } + radius = FLOAT2FIXED(intensity * 2.0f * gl_lights_size); + + if (x!=oldx || y!=oldy || radius!=oldradius) + { + //Update the light lists + LinkLight(); + } + } +} + + +//========================================================================== +// +// +// +//========================================================================== +void ADynamicLight::SetOffset(fixed_t x, fixed_t y, fixed_t z) +{ + m_offX = x; + m_offY = y; + m_offZ = z; + UpdateLocation(); +} + + +//========================================================================== +// +// The target pointer in dynamic lights should never be substituted unless +// notOld is NULL (which indicates that the object was destroyed by force.) +// +//========================================================================== +size_t ADynamicLight::PointerSubstitution (DObject *old, DObject *notOld) +{ + AActor *saved_target = target; + size_t ret = Super::PointerSubstitution(old, notOld); + if (notOld != NULL) target = saved_target; + return ret; +} + +//============================================================================= +// +// These have been copied from the secnode code and modified for the light links +// +// P_AddSecnode() searches the current list to see if this sector is +// already there. If not, it adds a sector node at the head of the list of +// sectors this object appears in. This is called when creating a list of +// nodes that will get linked in later. Returns a pointer to the new node. +// +//============================================================================= +static FreeList freelist; + +FLightNode * AddLightNode(FLightNode ** thread, void * linkto, ADynamicLight * light, FLightNode *& nextnode) +{ + FLightNode * node; + + node = nextnode; + while (node) + { + if (node->targ==linkto) // Already have a node for this sector? + { + node->lightsource = light; // Yes. Setting m_thing says 'keep it'. + return(nextnode); + } + node = node->nextTarget; + } + + // Couldn't find an existing node for this sector. Add one at the head + // of the list. + + node = freelist.GetNew(); + + node->targ = linkto; + node->lightsource = light; + + node->prevTarget = &nextnode; + node->nextTarget = nextnode; + + if (nextnode) nextnode->prevTarget = &node->nextTarget; + + // Add new node at head of sector thread starting at s->touching_thinglist + + node->prevLight = thread; + node->nextLight = *thread; + if (node->nextLight) node->nextLight->prevLight=&node->nextLight; + *thread = node; + return(node); +} + + +//============================================================================= +// +// P_DelSecnode() deletes a sector node from the list of +// sectors this object appears in. Returns a pointer to the next node +// on the linked list, or NULL. +// +//============================================================================= + +static FLightNode * DeleteLightNode(FLightNode * node) +{ + FLightNode * tn; // next node on thing thread + + if (node) + { + + *node->prevTarget = node->nextTarget; + if (node->nextTarget) node->nextTarget->prevTarget=node->prevTarget; + + *node->prevLight = node->nextLight; + if (node->nextLight) node->nextLight->prevLight=node->prevLight; + + // Return this node to the freelist + tn=node->nextTarget; + freelist.Release(node); + return(tn); + } + return(NULL); +} // phares 3/13/98 + + + +//========================================================================== +// +// Gets the light's distance to a line +// +//========================================================================== + +float ADynamicLight::DistToSeg(seg_t *seg) +{ + float u, px, py; + + float seg_dx = FIXED2FLOAT(seg->v2->x - seg->v1->x); + float seg_dy = FIXED2FLOAT(seg->v2->y - seg->v1->y); + float seg_length_sq = seg_dx * seg_dx + seg_dy * seg_dy; + + u = ( FIXED2FLOAT(x - seg->v1->x) * seg_dx + FIXED2FLOAT(y - seg->v1->y) * seg_dy) / seg_length_sq; + if (u < 0.f) u = 0.f; // clamp the test point to the line segment + if (u > 1.f) u = 1.f; + + px = FIXED2FLOAT(seg->v1->x) + (u * seg_dx); + py = FIXED2FLOAT(seg->v1->y) + (u * seg_dy); + + px -= FIXED2FLOAT(x); + py -= FIXED2FLOAT(y); + + return (px*px) + (py*py); +} + + +//========================================================================== +// +// Collect all touched sidedefs and subsectors +// to sidedefs and sector parts. +// +//========================================================================== + +void ADynamicLight::CollectWithinRadius(subsector_t *subSec, float radius) +{ + if (!subSec) return; + + bool additive = (flags4&MF4_ADDITIVE) || gl_lights_additive; + + subSec->validcount = ::validcount; + + touching_subsectors = AddLightNode(&subSec->lighthead[additive], subSec, this, touching_subsectors); + + for (unsigned int i = 0; i < subSec->numlines; i++) + { + seg_t * seg = subSec->firstline + i; + + if (seg->sidedef && seg->linedef && seg->linedef->validcount!=::validcount) + { + // light is in front of the seg + if (DMulScale32 (y-seg->v1->y, seg->v2->x-seg->v1->x, seg->v1->x-x, seg->v2->y-seg->v1->y) <=0) + { + seg->linedef->validcount=validcount; + touching_sides = AddLightNode(&seg->sidedef->lighthead[additive], + seg->sidedef, this, touching_sides); + } + } + + seg_t *partner = seg->PartnerSeg; + if (partner) + { + subsector_t *sub = partner->Subsector; + if (sub != NULL && sub->validcount!=::validcount) + { + // check distance from x/y to seg and if within radius add opposing subsector (lather/rinse/repeat) + if (DistToSeg(seg) <= radius) + { + CollectWithinRadius(sub, radius); + } + } + } + } +} + +//========================================================================== +// +// Link the light into the world +// +//========================================================================== + +void ADynamicLight::LinkLight() +{ + // mark the old light nodes + FLightNode * node; + + node = touching_sides; + while (node) + { + node->lightsource = NULL; + node = node->nextTarget; + } + node = touching_subsectors; + while (node) + { + node->lightsource = NULL; + node = node->nextTarget; + } + + if (radius>0) + { + // passing in radius*radius allows us to do a distance check without any calls to sqrtf + ::validcount++; + subsector_t * subSec = R_PointInSubsector(x, y); + if (subSec) + { + float fradius = FIXED2FLOAT(radius); + CollectWithinRadius(subSec, fradius*fradius); + } + } + + // Now delete any nodes that won't be used. These are the ones where + // m_thing is still NULL. + + node = touching_sides; + while (node) + { + if (node->lightsource == NULL) + { + node = DeleteLightNode(node); + } + else + node = node->nextTarget; + } + + node = touching_subsectors; + while (node) + { + if (node->lightsource == NULL) + { + node = DeleteLightNode(node); + } + else + node = node->nextTarget; + } +} + + +//========================================================================== +// +// Deletes the link lists +// +//========================================================================== +void ADynamicLight::UnlinkLight () +{ + if (owned && target != NULL) + { + // Delete reference in owning actor + for(int c=target->dynamiclights.Size()-1; c>=0; c--) + { + if (target->dynamiclights[c] == this) + { + target->dynamiclights.Delete(c); + break; + } + } + } + while (touching_sides) touching_sides = DeleteLightNode(touching_sides); + while (touching_subsectors) touching_subsectors = DeleteLightNode(touching_subsectors); +} + +void ADynamicLight::Destroy() +{ + UnlinkLight(); + Super::Destroy(); +} + + +//========================================================================== +// +// Needed for garbage collection +// +//========================================================================== + +size_t AActor::PropagateMark() +{ + for (unsigned i=0; i it; + + while ((dl=it.Next())) + { + walls=0; + sectors=0; + Printf("%s at (%f, %f, %f), color = 0x%02x%02x%02x, radius = %f ", + dl->target? dl->target->GetClass()->TypeName.GetChars() : dl->GetClass()->TypeName.GetChars(), + FIXED2FLOAT(dl->x), FIXED2FLOAT(dl->y), FIXED2FLOAT(dl->z), dl->args[LIGHT_RED], + dl->args[LIGHT_GREEN], dl->args[LIGHT_BLUE], FIXED2FLOAT(dl->radius)); + i++; + + if (dl->target) + { + FTextureID spr = gl_GetSpriteFrame(dl->target->sprite, dl->target->frame, 0, 0, NULL); + Printf(", frame = %s ", TexMan[spr]->Name); + } + + + FLightNode * node; + + node=dl->touching_sides; + + while (node) + { + walls++; + allwalls++; + node = node->nextTarget; + } + + node=dl->touching_subsectors; + + while (node) + { + allsectors++; + sectors++; + node = node->nextTarget; + } + + Printf("- %d walls, %d subsectors\n", walls, sectors); + + } + Printf("%i dynamic lights, %d walls, %d subsectors\n\n\n", i, allwalls, allsectors); +} + +CCMD(listsublights) +{ + for(int i=0;ilighthead[0]; + while (node != NULL) + { + lights++; + node = node->nextLight; + } + + node = sub->lighthead[1]; + while (node != NULL) + { + addlights++; + node = node->nextLight; + } + Printf(PRINT_LOG, "Subsector %d - %d lights, %d additive lights\n", i, lights, addlights); + } +} + diff --git a/src/gl/dynlights/gl_dynlight.cpp b/src/gl/dynlights/gl_dynlight.cpp new file mode 100644 index 000000000..6c6209664 --- /dev/null +++ b/src/gl/dynlights/gl_dynlight.cpp @@ -0,0 +1,1365 @@ +/* +** gl_dynlight.cpp +** Light definitions for actors. +** +**--------------------------------------------------------------------------- +** Copyright 2003 Timothy 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 +#include "i_system.h" +#include "doomtype.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "m_random.h" +#include "sc_man.h" +#include "templates.h" +#include "w_wad.h" +#include "gi.h" +#include "r_state.h" +#include "stats.h" +#include "zstring.h" +#include "d_dehacked.h" + + +#include "gl/dynlights/gl_dynlight.h" +#include "gl/textures/gl_skyboxtexture.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_convert.h" + +EXTERN_CVAR (Float, gl_lights_intensity); +EXTERN_CVAR (Float, gl_lights_size); +int ScriptDepth; +void gl_InitGlow(FScanner &sc); +void gl_ParseBrightmap(FScanner &sc, int); +void gl_DestroyUserShaders(); +void gl_ParseHardwareShader(FScanner &sc, int deflump); +void gl_ParseSkybox(FScanner &sc); +void gl_ParseDetailTexture(FScanner &sc); +void gl_ParseVavoomSkybox(); + +//========================================================================== +// +// Dehacked aliasing +// +//========================================================================== + +inline const PClass * GetRealType(const PClass * ti) +{ + FActorInfo *rep = ti->ActorInfo->GetReplacement(false); + if (rep != ti->ActorInfo && rep != NULL && rep->Class->IsDescendantOf(RUNTIME_CLASS(ADehackedPickup))) + { + return rep->Class; + } + return ti; +} + + + +//========================================================================== +// +// Light associations +// +//========================================================================== +class FLightAssociation +{ +public: + //FLightAssociation(); + FLightAssociation(FName actorName, const char *frameName, FName lightName) + : m_ActorName(actorName), m_AssocLight(lightName) + { + mysnprintf(m_FrameName, 8, "%s", frameName); + } + + FName ActorName() { return m_ActorName; } + const char *FrameName() { return m_FrameName; } + FName Light() { return m_AssocLight; } + void ReplaceLightName(FName newName) { m_AssocLight = newName; } +protected: + char m_FrameName[8]; + FName m_ActorName, m_AssocLight; +}; + +TArray LightAssociations; + + +//========================================================================== +// +// Light definitions +// +//========================================================================== +class FLightDefaults +{ +public: + FLightDefaults(FName name, ELightType type); + + void ApplyProperties(ADynamicLight * light) const; + FName GetName() const { return m_Name; } + void SetAngle(angle_t angle) { m_Angle = angle; } + void SetArg(int arg, BYTE val) { m_Args[arg] = val; } + BYTE GetArg(int arg) { return m_Args[arg]; } + void SetOffset(float* ft) { m_X = FLOAT2FIXED(ft[0]); m_Y = FLOAT2FIXED(ft[1]); m_Z = FLOAT2FIXED(ft[2]); } + void SetSubtractive(bool subtract) { m_subtractive = subtract; } + void SetAdditive(bool add) { m_additive = add; } + void SetDontLightSelf(bool add) { m_dontlightself = add; } + void SetHalo(bool halo) { m_halo = halo; } +protected: + FName m_Name; + unsigned char m_Args[5]; + angle_t m_Angle; + fixed_t m_X, m_Y, m_Z; + ELightType m_type; + bool m_subtractive, m_additive, m_halo, m_dontlightself; +}; + +TArray LightDefaults; + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +FLightDefaults::FLightDefaults(FName name, ELightType type) +{ + m_Name = name; + m_type = type; + + m_X = m_Y = m_Z = 0; + memset(m_Args, 0, 5); + + m_subtractive = false; + m_additive = false; + m_halo = false; + m_dontlightself = false; +} + +void FLightDefaults::ApplyProperties(ADynamicLight * light) const +{ + light->lighttype = m_type; + light->angle = m_Angle; + light->SetOffset(m_X, m_Y, m_Z); + light->halo = m_halo; + for (int a = 0; a < 3; a++) light->args[a] = clamp((int)(m_Args[a] * gl_lights_intensity), 0, 255); + light->m_intensity[0] = int(m_Args[LIGHT_INTENSITY]); + light->m_intensity[1] = int(m_Args[LIGHT_SECONDARY_INTENSITY]); + light->flags4&=~(MF4_ADDITIVE|MF4_SUBTRACTIVE|MF4_DONTLIGHTSELF); + if (m_subtractive) light->flags4|=MF4_SUBTRACTIVE; + if (m_additive) light->flags4|=MF4_ADDITIVE; + if (m_dontlightself) light->flags4|=MF4_DONTLIGHTSELF; +} + + +//========================================================================== +// +// light definition file parser +// +//========================================================================== + + +static const char *LightTags[]= +{ + "color", + "size", + "secondarySize", + "offset", + "chance", + "interval", + "scale", + "frame", + "light", + "{", + "}", + "subtractive", + "additive", + "halo", + "dontlightself", + NULL +}; + + +enum { + LIGHTTAG_COLOR, + LIGHTTAG_SIZE, + LIGHTTAG_SECSIZE, + LIGHTTAG_OFFSET, + LIGHTTAG_CHANCE, + LIGHTTAG_INTERVAL, + LIGHTTAG_SCALE, + LIGHTTAG_FRAME, + LIGHTTAG_LIGHT, + LIGHTTAG_OPENBRACE, + LIGHTTAG_CLOSEBRACE, + LIGHTTAG_SUBTRACTIVE, + LIGHTTAG_ADDITIVE, + LIGHTTAG_HALO, + LIGHTTAG_DONTLIGHTSELF, +}; + + +extern int ScriptDepth; + + +//========================================================================== +// +// +// +//========================================================================== + +inline float gl_ParseFloat(FScanner &sc) +{ + sc.GetFloat(); + + return float(sc.Float); +} + + +inline int gl_ParseInt(FScanner &sc) +{ + sc.GetNumber(); + + return sc.Number; +} + + +inline char *gl_ParseString(FScanner &sc) +{ + sc.GetString(); + + return sc.String; +} + + +void gl_ParseTriple(FScanner &sc, float floatVal[3]) +{ + for (int i = 0; i < 3; i++) + { + floatVal[i] = gl_ParseFloat(sc); + } +} + + +void gl_AddLightDefaults(FLightDefaults *defaults) +{ + FLightDefaults *temp; + unsigned int i; + + // remove duplicates + for (i = 0; i < LightDefaults.Size(); i++) + { + temp = LightDefaults[i]; + if (temp->GetName() == defaults->GetName()) + { + delete temp; + LightDefaults.Delete(i); + break; + } + } + + LightDefaults.Push(defaults); +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void gl_ParsePointLight(FScanner &sc) +{ + int type; + float floatTriple[3]; + int intVal; + FLightDefaults *defaults; + + // get name + sc.GetString(); + FName name = sc.String; + + // check for opening brace + sc.GetString(); + if (sc.Compare("{")) + { + defaults = new FLightDefaults(name, PointLight); + ScriptDepth++; + while (ScriptDepth) + { + sc.GetString(); + type = sc.MatchString(LightTags); + switch (type) + { + case LIGHTTAG_OPENBRACE: + ScriptDepth++; + break; + case LIGHTTAG_CLOSEBRACE: + ScriptDepth--; + break; + case LIGHTTAG_COLOR: + gl_ParseTriple(sc, floatTriple); + defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); + defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); + defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); + break; + case LIGHTTAG_OFFSET: + gl_ParseTriple(sc, floatTriple); + defaults->SetOffset(floatTriple); + break; + case LIGHTTAG_SIZE: + intVal = clamp(gl_ParseInt(sc), 0, 255); + defaults->SetArg(LIGHT_INTENSITY, intVal); + break; + case LIGHTTAG_SUBTRACTIVE: + defaults->SetSubtractive(gl_ParseInt(sc) != 0); + break; + case LIGHTTAG_ADDITIVE: + defaults->SetAdditive(gl_ParseInt(sc) != 0); + break; + case LIGHTTAG_HALO: + defaults->SetHalo(gl_ParseInt(sc) != 0); + break; + case LIGHTTAG_DONTLIGHTSELF: + defaults->SetDontLightSelf(gl_ParseInt(sc) != 0); + break; + default: + sc.ScriptError("Unknown tag: %s\n", sc.String); + } + } + gl_AddLightDefaults(defaults); + } + else + { + sc.ScriptError("Expected '{'.\n"); + } +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void gl_ParsePulseLight(FScanner &sc) +{ + int type; + float floatVal, floatTriple[3]; + int intVal; + FLightDefaults *defaults; + + // get name + sc.GetString(); + FName name = sc.String; + + // check for opening brace + sc.GetString(); + if (sc.Compare("{")) + { + defaults = new FLightDefaults(name, PulseLight); + ScriptDepth++; + while (ScriptDepth) + { + sc.GetString(); + type = sc.MatchString(LightTags); + switch (type) + { + case LIGHTTAG_OPENBRACE: + ScriptDepth++; + break; + case LIGHTTAG_CLOSEBRACE: + ScriptDepth--; + break; + case LIGHTTAG_COLOR: + gl_ParseTriple(sc, floatTriple); + defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); + defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); + defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); + break; + case LIGHTTAG_OFFSET: + gl_ParseTriple(sc, floatTriple); + defaults->SetOffset(floatTriple); + break; + case LIGHTTAG_SIZE: + intVal = clamp(gl_ParseInt(sc), 0, 255); + defaults->SetArg(LIGHT_INTENSITY, intVal); + break; + case LIGHTTAG_SECSIZE: + intVal = clamp(gl_ParseInt(sc), 0, 255); + defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal); + break; + case LIGHTTAG_INTERVAL: + floatVal = gl_ParseFloat(sc); + defaults->SetAngle(FLOAT_TO_ANGLE(floatVal * TICRATE)); + break; + case LIGHTTAG_SUBTRACTIVE: + defaults->SetSubtractive(gl_ParseInt(sc) != 0); + break; + case LIGHTTAG_HALO: + defaults->SetHalo(gl_ParseInt(sc) != 0); + break; + case LIGHTTAG_DONTLIGHTSELF: + defaults->SetDontLightSelf(gl_ParseInt(sc) != 0); + break; + default: + sc.ScriptError("Unknown tag: %s\n", sc.String); + } + } + gl_AddLightDefaults(defaults); + } + else + { + sc.ScriptError("Expected '{'.\n"); + } +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void gl_ParseFlickerLight(FScanner &sc) +{ + int type; + float floatVal, floatTriple[3]; + int intVal; + FLightDefaults *defaults; + + // get name + sc.GetString(); + FName name = sc.String; + + // check for opening brace + sc.GetString(); + if (sc.Compare("{")) + { + defaults = new FLightDefaults(name, FlickerLight); + ScriptDepth++; + while (ScriptDepth) + { + sc.GetString(); + type = sc.MatchString(LightTags); + switch (type) + { + case LIGHTTAG_OPENBRACE: + ScriptDepth++; + break; + case LIGHTTAG_CLOSEBRACE: + ScriptDepth--; + break; + case LIGHTTAG_COLOR: + gl_ParseTriple(sc, floatTriple); + defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); + defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); + defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); + break; + case LIGHTTAG_OFFSET: + gl_ParseTriple(sc, floatTriple); + defaults->SetOffset(floatTriple); + break; + case LIGHTTAG_SIZE: + intVal = clamp(gl_ParseInt(sc), 0, 255); + defaults->SetArg(LIGHT_INTENSITY, intVal); + break; + case LIGHTTAG_SECSIZE: + intVal = clamp(gl_ParseInt(sc), 0, 255); + defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal); + break; + case LIGHTTAG_CHANCE: + floatVal = gl_ParseFloat(sc); + defaults->SetAngle((angle_t)(floatVal * ANGLE_MAX)); + break; + case LIGHTTAG_SUBTRACTIVE: + defaults->SetSubtractive(gl_ParseInt(sc) != 0); + break; + case LIGHTTAG_HALO: + defaults->SetHalo(gl_ParseInt(sc) != 0); + break; + case LIGHTTAG_DONTLIGHTSELF: + defaults->SetDontLightSelf(gl_ParseInt(sc) != 0); + break; + default: + sc.ScriptError("Unknown tag: %s\n", sc.String); + } + } + gl_AddLightDefaults(defaults); + } + else + { + sc.ScriptError("Expected '{'.\n"); + } +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void gl_ParseFlickerLight2(FScanner &sc) +{ + int type; + float floatVal, floatTriple[3]; + int intVal; + FLightDefaults *defaults; + + // get name + sc.GetString(); + FName name = sc.String; + + // check for opening brace + sc.GetString(); + if (sc.Compare("{")) + { + defaults = new FLightDefaults(name, RandomFlickerLight); + ScriptDepth++; + while (ScriptDepth) + { + sc.GetString(); + type = sc.MatchString(LightTags); + switch (type) + { + case LIGHTTAG_OPENBRACE: + ScriptDepth++; + break; + case LIGHTTAG_CLOSEBRACE: + ScriptDepth--; + break; + case LIGHTTAG_COLOR: + gl_ParseTriple(sc, floatTriple); + defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); + defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); + defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); + break; + case LIGHTTAG_OFFSET: + gl_ParseTriple(sc, floatTriple); + defaults->SetOffset(floatTriple); + break; + case LIGHTTAG_SIZE: + intVal = clamp(gl_ParseInt(sc), 0, 255); + defaults->SetArg(LIGHT_INTENSITY, intVal); + break; + case LIGHTTAG_SECSIZE: + intVal = clamp(gl_ParseInt(sc), 0, 255); + defaults->SetArg(LIGHT_SECONDARY_INTENSITY, intVal); + break; + case LIGHTTAG_INTERVAL: + floatVal = gl_ParseFloat(sc); + defaults->SetAngle((angle_t)(floatVal * ANGLE_MAX)); + break; + case LIGHTTAG_SUBTRACTIVE: + defaults->SetSubtractive(gl_ParseInt(sc) != 0); + break; + case LIGHTTAG_HALO: + defaults->SetHalo(gl_ParseInt(sc) != 0); + break; + case LIGHTTAG_DONTLIGHTSELF: + defaults->SetDontLightSelf(gl_ParseInt(sc) != 0); + break; + default: + sc.ScriptError("Unknown tag: %s\n", sc.String); + } + } + if (defaults->GetArg(LIGHT_SECONDARY_INTENSITY) < defaults->GetArg(LIGHT_INTENSITY)) + { + BYTE v = defaults->GetArg(LIGHT_SECONDARY_INTENSITY); + defaults->SetArg(LIGHT_SECONDARY_INTENSITY, defaults->GetArg(LIGHT_INTENSITY)); + defaults->SetArg(LIGHT_INTENSITY, v); + } + gl_AddLightDefaults(defaults); + } + else + { + sc.ScriptError("Expected '{'.\n"); + } +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void gl_ParseSectorLight(FScanner &sc) +{ + int type; + float floatVal; + float floatTriple[3]; + FLightDefaults *defaults; + + // get name + sc.GetString(); + FName name = sc.String; + + // check for opening brace + sc.GetString(); + if (sc.Compare("{")) + { + defaults = new FLightDefaults(name, SectorLight); + ScriptDepth++; + while (ScriptDepth) + { + sc.GetString(); + type = sc.MatchString(LightTags); + switch (type) + { + case LIGHTTAG_OPENBRACE: + ScriptDepth++; + break; + case LIGHTTAG_CLOSEBRACE: + ScriptDepth--; + break; + case LIGHTTAG_COLOR: + gl_ParseTriple(sc, floatTriple); + defaults->SetArg(LIGHT_RED, clamp((int)(floatTriple[0] * 255), 0, 255)); + defaults->SetArg(LIGHT_GREEN, clamp((int)(floatTriple[1] * 255), 0, 255)); + defaults->SetArg(LIGHT_BLUE, clamp((int)(floatTriple[2] * 255), 0, 255)); + break; + case LIGHTTAG_OFFSET: + gl_ParseTriple(sc, floatTriple); + defaults->SetOffset(floatTriple); + break; + case LIGHTTAG_SCALE: + floatVal = gl_ParseFloat(sc); + defaults->SetArg(LIGHT_SCALE, (BYTE)(floatVal * 255)); + break; + case LIGHTTAG_SUBTRACTIVE: + defaults->SetSubtractive(gl_ParseInt(sc) != 0); + break; + case LIGHTTAG_HALO: + defaults->SetHalo(gl_ParseInt(sc) != 0); + break; + case LIGHTTAG_DONTLIGHTSELF: + defaults->SetDontLightSelf(gl_ParseInt(sc) != 0); + break; + default: + sc.ScriptError("Unknown tag: %s\n", sc.String); + } + } + gl_AddLightDefaults(defaults); + } + else + { + sc.ScriptError("Expected '{'.\n"); + } +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void gl_AddLightAssociation(const char *actor, const char *frame, const char *light) +{ + FLightAssociation *temp; + unsigned int i; + FLightAssociation assoc(actor, frame, light); + + for (i = 0; i < LightAssociations.Size(); i++) + { + temp = &LightAssociations[i]; + if (temp->ActorName() == assoc.ActorName()) + { + if (strcmp(temp->FrameName(), assoc.FrameName()) == 0) + { + temp->ReplaceLightName(assoc.Light()); + return; + } + } + } + + LightAssociations.Push(assoc); +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void gl_ParseFrame(FScanner &sc, FString name) +{ + int type, startDepth; + FString frameName; + + // get name + sc.GetString(); + if (strlen(sc.String) > 8) + { + sc.ScriptError("Name longer than 8 characters: %s\n", sc.String); + } + frameName = sc.String; + + startDepth = ScriptDepth; + + // check for opening brace + sc.GetString(); + if (sc.Compare("{")) + { + ScriptDepth++; + while (ScriptDepth > startDepth) + { + sc.GetString(); + type = sc.MatchString(LightTags); + switch (type) + { + case LIGHTTAG_OPENBRACE: + ScriptDepth++; + break; + case LIGHTTAG_CLOSEBRACE: + ScriptDepth--; + break; + case LIGHTTAG_LIGHT: + gl_ParseString(sc); + gl_AddLightAssociation(name, frameName, sc.String); + break; + default: + sc.ScriptError("Unknown tag: %s\n", sc.String); + } + } + } + else + { + sc.ScriptError("Expected '{'.\n"); + } +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void gl_ParseObject(FScanner &sc) +{ + int type; + FString name; + + // get name + sc.GetString(); + name = sc.String; + if (!PClass::FindClass(name)) + sc.ScriptMessage("Warning: dynamic lights attached to non-existent actor %s\n", name.GetChars()); + + // check for opening brace + sc.GetString(); + if (sc.Compare("{")) + { + ScriptDepth++; + while (ScriptDepth) + { + sc.GetString(); + type = sc.MatchString(LightTags); + switch (type) + { + case LIGHTTAG_OPENBRACE: + ScriptDepth++; + break; + case LIGHTTAG_CLOSEBRACE: + ScriptDepth--; + break; + case LIGHTTAG_FRAME: + gl_ParseFrame(sc, name); + break; + default: + sc.ScriptError("Unknown tag: %s\n", sc.String); + } + } + } + else + { + sc.ScriptError("Expected '{'.\n"); + } +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void gl_ReleaseLights() +{ + unsigned int i; + + for (i = 0; i < LightDefaults.Size(); i++) + { + delete LightDefaults[i]; + } + + LightAssociations.Clear(); + LightDefaults.Clear(); +} + +//========================================================================== +// +// +// +//========================================================================== + +// these are the core types available in the *DEFS lump +static const char *CoreKeywords[]= +{ + "pointlight", + "pulselight", + "flickerlight", + "flickerlight2", + "sectorlight", + "object", + "clearlights", + "shader", + "clearshaders", + "skybox", + "glow", + "brightmap", + "disable_fullbright", + "hardwareshader", + "detail", + "#include", + NULL +}; + + +enum +{ + LIGHT_POINT, + LIGHT_PULSE, + LIGHT_FLICKER, + LIGHT_FLICKER2, + LIGHT_SECTOR, + LIGHT_OBJECT, + LIGHT_CLEAR, + TAG_SHADER, + TAG_CLEARSHADERS, + TAG_SKYBOX, + TAG_GLOW, + TAG_BRIGHTMAP, + TAG_DISABLE_FB, + TAG_HARDWARESHADER, + TAG_DETAIL, + TAG_INCLUDE, +}; + + +//========================================================================== +// +// This is only here so any shader definition for ZDoomGL can be skipped +// There is no functionality for this stuff! +// +//========================================================================== +bool gl_ParseShader(FScanner &sc) +{ + int ShaderDepth = 0; + + if (sc.GetString()) + { + char *tmp; + + tmp = strstr(sc.String, "{"); + while (tmp) + { + ShaderDepth++; + tmp++; + tmp = strstr(tmp, "{"); + } + + tmp = strstr(sc.String, "}"); + while (tmp) + { + ShaderDepth--; + tmp++; + tmp = strstr(tmp, "}"); + } + + if (ShaderDepth == 0) return true; + } + return false; +} + +//========================================================================== +// +// Light associations per actor class +// +// Turn this inefficient mess into something that can be used at run time. +// +//========================================================================== + +class FInternalLightAssociation +{ +public: + FInternalLightAssociation(FLightAssociation * asso); + int Sprite() const { return m_sprite; } + int Frame() const { return m_frame; } + const FLightDefaults *Light() const { return m_AssocLight; } +protected: + int m_sprite; + int m_frame; + FLightDefaults * m_AssocLight; +}; + +//========================================================================== +// +// +// +//========================================================================== + +FInternalLightAssociation::FInternalLightAssociation(FLightAssociation * asso) +{ + + m_AssocLight=NULL; + for(unsigned int i=0;iGetName() == asso->Light()) + { + m_AssocLight = LightDefaults[i]; + break; + } + } + + m_sprite=-1; + m_frame = -1; + for (unsigned i = 0; i < sprites.Size (); ++i) + { + if (strncmp (sprites[i].name, asso->FrameName(), 4) == 0) + { + m_sprite = (int)i; + break; + } + } + + // Only handle lights for full frames. + // I won't bother with special lights for single rotations + // because there is no decent use for them! + if (strlen(asso->FrameName())==5 || asso->FrameName()[5]=='0') + { + m_frame = toupper(asso->FrameName()[4])-'A'; + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +inline TDeletingArray * gl_GetActorLights(AActor * actor) +{ + return (TDeletingArray*)actor->lightassociations; +} + +TDeletingArray< TDeletingArray * > AssoDeleter; + +//========================================================================== +// +// State lights +// +//========================================================================== +TArray ParsedStateLights; +TArray StateLights; + + + +//========================================================================== +// +// +// +//========================================================================== + +void gl_InitializeActorLights() +{ + for(unsigned int i=0;ilightassociations) + { + TDeletingArray *p =new TDeletingArray; + defaults->lightassociations = p; + AssoDeleter.Push(p); + } + TDeletingArray * lights = gl_GetActorLights(defaults); + if (iasso->Light()==NULL) + { + // The definition was not valid. + delete iasso; + } + else + { + lights->Push(iasso); + } + } + } + } + // we don't need the parser data for the light associations anymore + LightAssociations.Clear(); + LightAssociations.ShrinkToFit(); + + StateLights.Resize(ParsedStateLights.Size()+1); + for(unsigned i=0; iGetName() == ParsedStateLights[i]) + { + StateLights[i] = LightDefaults[j]; + break; + } + } + } + else StateLights[i] = NULL; + } + StateLights[StateLights.Size()-1] = NULL; // terminator + ParsedStateLights.Clear(); + ParsedStateLights.ShrinkToFit(); +} + + +//========================================================================== +// +// +// +//========================================================================== + +void gl_AttachLight(AActor *actor, unsigned int count, const FLightDefaults *lightdef) +{ + ADynamicLight *light; + + // I'm skipping the single rotations because that really doesn't make sense! + if (count < actor->dynamiclights.Size()) + { + light = barrier_cast(actor->dynamiclights[count]); + assert(light != NULL); + } + else + { + light = Spawn(actor->x, actor->y, actor->z, NO_REPLACE); + light->target = actor; + light->owned = true; + actor->dynamiclights.Push(light); + } + light->flags2&=~MF2_DORMANT; + lightdef->ApplyProperties(light); +} + +//========================================================================== +// +// per-state light adjustment +// +//========================================================================== + +void gl_SetActorLights(AActor *actor) +{ + TArray * l = gl_GetActorLights(actor); + unsigned int count = 0; + + All.Clock(); + if (actor->state == NULL) return; + if (l) + { + TArray & LightAssociations=*l; + ADynamicLight *lights, *tmpLight; + unsigned int i; + + int sprite = actor->state->sprite; + int frame = actor->state->GetFrame(); + + lights = tmpLight = NULL; + + + for (i = 0; i < LightAssociations.Size(); i++) + { + if (LightAssociations[i]->Sprite() == sprite && + (LightAssociations[i]->Frame()==frame || LightAssociations[i]->Frame()==-1)) + { + gl_AttachLight(actor, count++, LightAssociations[i]->Light()); + } + } + } + if (count == 0 && actor->state->Light > 0) + { + for(int i= actor->state->Light; StateLights[i] != NULL; i++) + { + if (StateLights[i] != (FLightDefaults*)-1) + { + gl_AttachLight(actor, count++, StateLights[i]); + } + } + } + + for(;countdynamiclights.Size();count++) + { + actor->dynamiclights[count]->flags2|=MF2_DORMANT; + memset(actor->dynamiclights[count]->args, 0, sizeof(actor->args)); + } + All.Unclock(); +} + +//========================================================================== +// +// This is called before saving the game +// +//========================================================================== + +void gl_DeleteAllAttachedLights() +{ + TThinkerIterator it; + AActor * a; + ADynamicLight * l; + + while ((a=it.Next())) + { + a->dynamiclights.Clear(); + } + + TThinkerIterator it2; + + l=it2.Next(); + while (l) + { + ADynamicLight * ll = it2.Next(); + if (l->owned) l->Destroy(); + l=ll; + } + + +} + +//========================================================================== +// +// +// +//========================================================================== + +void gl_RecreateAllAttachedLights() +{ + TThinkerIterator it; + AActor * a; + + while ((a=it.Next())) + { + gl_SetActorLights(a); + } +} + + +//========================================================================== +// The actual light def parsing code is there. +// DoParseDefs is no longer called directly by ParseDefs, now it's called +// by LoadDynLightDefs, which wasn't simply integrated into ParseDefs +// because of the way the code needs to load two out of five lumps. +//========================================================================== +void gl_DoParseDefs(FScanner &sc, int workingLump) +{ + int recursion=0; + int lump, type; + + // Get actor class name. + while (true) + { + sc.SavePos(); + if (!sc.GetToken ()) + { + return; + } + type = sc.MatchString(CoreKeywords); + switch (type) + { + case TAG_INCLUDE: + { + sc.MustGetString(); + // This is not using sc.Open because it can print a more useful error message when done here + lump = Wads.CheckNumForFullName(sc.String, true); + if (lump==-1) + sc.ScriptError("Lump '%s' not found", sc.String); + + FScanner newscanner(lump); + gl_DoParseDefs(newscanner, lump); + break; + } + case LIGHT_POINT: + gl_ParsePointLight(sc); + break; + case LIGHT_PULSE: + gl_ParsePulseLight(sc); + break; + case LIGHT_FLICKER: + gl_ParseFlickerLight(sc); + break; + case LIGHT_FLICKER2: + gl_ParseFlickerLight2(sc); + break; + case LIGHT_SECTOR: + gl_ParseSectorLight(sc); + break; + case LIGHT_OBJECT: + gl_ParseObject(sc); + break; + case LIGHT_CLEAR: + gl_ReleaseLights(); + break; + case TAG_SHADER: + gl_ParseShader(sc); + break; + case TAG_CLEARSHADERS: + break; + case TAG_SKYBOX: + gl_ParseSkybox(sc); + break; + case TAG_GLOW: + gl_InitGlow(sc); + break; + case TAG_BRIGHTMAP: + gl_ParseBrightmap(sc, workingLump); + break; + case TAG_HARDWARESHADER: + gl_ParseHardwareShader(sc, workingLump); + break; + case TAG_DETAIL: + gl_ParseDetailTexture(sc); + break; + case TAG_DISABLE_FB: + { + /* not implemented. + sc.MustGetString(); + const PClass *cls = PClass::FindClass(sc.String); + if (cls) GetDefaultByType(cls)->renderflags |= RF_NEVERFULLBRIGHT; + */ + } + break; + default: + sc.ScriptError("Error parsing defs. Unknown tag: %s.\n", sc.String); + break; + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void gl_LoadGLDefs(const char * defsLump) +{ + int workingLump, lastLump; + + lastLump = 0; + while ((workingLump = Wads.FindLump(defsLump, &lastLump)) != -1) + { + FScanner sc(workingLump); + gl_DoParseDefs(sc, workingLump); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void gl_ParseDefs() +{ + const char *defsLump = NULL; + + atterm( gl_ReleaseLights ); + gl_ReleaseLights(); + gl_DestroyUserShaders(); + switch (gameinfo.gametype) + { + case GAME_Heretic: + defsLump = "HTICDEFS"; + break; + case GAME_Hexen: + defsLump = "HEXNDEFS"; + break; + case GAME_Strife: + defsLump = "STRFDEFS"; + break; + case GAME_Doom: + defsLump = "DOOMDEFS"; + break; + case GAME_Chex: + defsLump = "CHEXDEFS"; + break; + default: // silence GCC + break; + } + gl_ParseVavoomSkybox(); + if (defsLump != NULL) gl_LoadGLDefs(defsLump); + gl_LoadGLDefs("GLDEFS"); + gl_InitializeActorLights(); +} + + +//========================================================================== +// +// +// +//========================================================================== + +void AddStateLight(FState *State, const char *lname) +{ + if (State->Light == 0) + { + ParsedStateLights.Push(NAME_None); + State->Light = ParsedStateLights.Push(FName(lname)); + } + else + { + ParsedStateLights.Push(FName(lname)); + } +} diff --git a/src/gl/dynlights/gl_dynlight.h b/src/gl/dynlights/gl_dynlight.h new file mode 100644 index 000000000..e4242d8f9 --- /dev/null +++ b/src/gl/dynlights/gl_dynlight.h @@ -0,0 +1,191 @@ +#ifndef __GLC_DYNLIGHT_H +#define __GLC_DYNLIGHT_H + +#include "c_cvars.h" +#include "gl/utility/gl_geometric.h" +#include "gl/utility/gl_cycler.h" + + +EXTERN_CVAR(Bool, gl_lights) +EXTERN_CVAR(Bool, gl_attachedlights) + +class ADynamicLight; +class FArchive; + + + +enum +{ + LIGHT_RED = 0, + LIGHT_GREEN = 1, + LIGHT_BLUE = 2, + LIGHT_INTENSITY = 3, + LIGHT_SECONDARY_INTENSITY = 4, + LIGHT_SCALE = 3, +}; + +// This is as good as something new - and it can be set directly in the ActorInfo! +#define MF4_SUBTRACTIVE MF4_MISSILEEVENMORE +#define MF4_ADDITIVE MF4_MISSILEMORE +#define MF4_DONTLIGHTSELF MF4_SEESDAGGERS + +enum ELightType +{ + PointLight, + PulseLight, + FlickerLight, + RandomFlickerLight, + SectorLight, + SpotLight, + ColorPulseLight, + ColorFlickerLight, + RandomColorFlickerLight +}; + + +struct FLightNode +{ + FLightNode ** prevTarget; + FLightNode * nextTarget; + FLightNode ** prevLight; + FLightNode * nextLight; + ADynamicLight * lightsource; + union + { + side_t * targLine; + subsector_t * targSubsector; + void * targ; + }; +}; + + +// +// Base class +// +// [CO] I merged everything together in this one class so that I don't have +// to create and re-create an excessive amount of objects +// + +class ADynamicLight : public AActor +{ + DECLARE_CLASS (ADynamicLight, AActor) +public: + virtual void Tick(); + void Serialize(FArchive &arc); + BYTE GetRed() const { return args[LIGHT_RED]; } + BYTE GetGreen() const { return args[LIGHT_GREEN]; } + BYTE GetBlue() const { return args[LIGHT_BLUE]; } + float GetIntensity() const { return m_currentIntensity; } + float GetRadius() const { return (IsActive() ? GetIntensity() * 2.f : 0.f); } + void LinkLight(); + void UnlinkLight(); + size_t PointerSubstitution (DObject *old, DObject *notOld); + + virtual void BeginPlay(); + void PostBeginPlay(); + void Destroy(); + void Activate(AActor *activator); + void Deactivate(AActor *activator); + void SetOffset(fixed_t x, fixed_t y, fixed_t z); + void UpdateLocation(); + bool IsOwned() const { return owned; } + bool IsActive() const { return !(flags2&MF2_DORMANT); } + bool IsSubtractive() { return !!(flags4&MF4_SUBTRACTIVE); } + bool IsAdditive() { return !!(flags4&MF4_ADDITIVE); } + FState *targetState; + FLightNode * touching_sides; + FLightNode * touching_subsectors; + +private: + float DistToSeg(seg_t *seg); + void CollectWithinRadius(subsector_t *subSec, float radius); + +protected: + fixed_t m_offX, m_offY, m_offZ; + float m_currentIntensity; + int m_tickCount; + unsigned int m_lastUpdate; + FCycler m_cycler; + subsector_t * subsector; + +public: + int m_intensity[2]; + BYTE lightflags; + BYTE lighttype; + bool owned; + bool halo; + BYTE color2[3]; + int bufferindex; + + // intermediate texture coordinate data + // this is stored in the light object to avoid recalculating it + // several times during rendering of a flat + Vector nearPt, up, right; + float scale; + +}; + +class AVavoomLight : public ADynamicLight +{ + DECLARE_CLASS (AVavoomLight, ADynamicLight) +public: + virtual void BeginPlay(); +}; + +class AVavoomLightWhite : public AVavoomLight +{ + DECLARE_CLASS (AVavoomLightWhite, AVavoomLight) +public: + virtual void BeginPlay(); +}; + +class AVavoomLightColor : public AVavoomLight +{ + DECLARE_CLASS (AVavoomLightColor, AVavoomLight) +public: + void BeginPlay(); +}; + + +enum +{ + STAT_DLIGHT=64 +}; + +struct FDynLightData +{ + TArray arrays[3]; + + void Clear() + { + arrays[0].Clear(); + arrays[1].Clear(); + arrays[2].Clear(); + } + + void Combine(int *siz, int max) + { + siz[0] = arrays[0].Size(); + siz[1] = siz[0] + arrays[1].Size(); + siz[2] = siz[1] + arrays[2].Size(); + arrays[0].Resize(arrays[0].Size() + arrays[1].Size() + arrays[2].Size()); + memcpy(&arrays[0][siz[0]], &arrays[1][0], arrays[1].Size() * sizeof(float)); + memcpy(&arrays[0][siz[1]], &arrays[2][0], arrays[2].Size() * sizeof(float)); + siz[0]>>=2; + siz[1]>>=2; + siz[2]>>=2; + max<<=1; + if (siz[0] > max) siz[0] = max; + if (siz[1] > max) siz[1] = max; + if (siz[2] > max) siz[2] = max; + } +}; + + + +bool gl_GetLight(Plane & p, ADynamicLight * light, int desaturation, bool checkside, bool forceadditive, FDynLightData &data); +bool gl_SetupLight(Plane & p, ADynamicLight * light, Vector & nearPt, Vector & up, Vector & right, float & scale, int desaturation, bool checkside=true, bool forceadditive=true); +bool gl_SetupLightTexture(); + + +#endif diff --git a/src/gl/dynlights/gl_dynlight1.cpp b/src/gl/dynlights/gl_dynlight1.cpp new file mode 100644 index 000000000..4c9f929f4 --- /dev/null +++ b/src/gl/dynlights/gl_dynlight1.cpp @@ -0,0 +1,261 @@ +/* +** gl_dynlight1.cpp +** dynamic light application +** +**--------------------------------------------------------------------------- +** Copyright 2002-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "c_dispatch.h" +#include "p_local.h" +#include "vectors.h" +#include "gl/gl_functions.h" +#include "g_level.h" + +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/data/gl_data.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_portal.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_material.h" + + +//========================================================================== +// +// Light related CVARs +// +//========================================================================== + +CUSTOM_CVAR (Bool, gl_lights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (self) gl_RecreateAllAttachedLights(); + else gl_DeleteAllAttachedLights(); +} + +CUSTOM_CVAR (Bool, gl_dynlight_shader, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (self && (gl.maxuniforms < 1024 || gl.shadermodel < 4)) self = false; +} + +CVAR (Bool, gl_attachedlights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); +CVAR (Bool, gl_lights_checkside, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); +CVAR (Float, gl_lights_intensity, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); +CVAR (Float, gl_lights_size, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); +CVAR (Bool, gl_light_sprites, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); +CVAR (Bool, gl_light_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); +CUSTOM_CVAR (Bool, gl_lights_additive, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + gl_DeleteAllAttachedLights(); + gl_RecreateAllAttachedLights(); +} + +//========================================================================== +// +// Sets up the parameters to render one dynamic light onto one plane +// +//========================================================================== +bool gl_GetLight(Plane & p, ADynamicLight * light, + int desaturation, bool checkside, bool forceadditive, FDynLightData &ldata) +{ + Vector fn, pos; + int i = 0; + + float x = FIXED2FLOAT(light->x); + float y = FIXED2FLOAT(light->y); + float z = FIXED2FLOAT(light->z); + + float dist = fabsf(p.DistToPoint(x, z, y)); + float radius = (light->GetRadius() * gl_lights_size); + + if (radius <= 0.f) return false; + if (dist > radius) return false; + if (checkside && gl_lights_checkside && p.PointOnSide(x, z, y)) + { + return false; + } + + + float cs; + if (gl_lights_additive || light->flags4&MF4_ADDITIVE || forceadditive) + { + cs = 0.2f; + i = 2; + } + else + { + cs = 1.0f; + } + + float r = light->GetRed() / 255.0f * cs * gl_lights_intensity; + float g = light->GetGreen() / 255.0f * cs * gl_lights_intensity; + float b = light->GetBlue() / 255.0f * cs * gl_lights_intensity; + + if (light->IsSubtractive()) + { + Vector v; + + v.Set(r, g, b); + r = v.Length() - r; + g = v.Length() - g; + b = v.Length() - b; + i = 1; + } + + if (desaturation>0) + { + float gray=(r*77 + g*143 + b*37)/257; + + r= (r*(32-desaturation)+ gray*desaturation)/32; + g= (g*(32-desaturation)+ gray*desaturation)/32; + b= (b*(32-desaturation)+ gray*desaturation)/32; + } + float *data = &ldata.arrays[i][ldata.arrays[i].Reserve(8)]; + data[0] = x; + data[1] = z; + data[2] = y; + data[3] = radius; + data[4] = r; + data[5] = g; + data[6] = b; + data[7] = 0; + return true; +} + + + + +//========================================================================== +// +// Sets up the parameters to render one dynamic light onto one plane +// +//========================================================================== +bool gl_SetupLight(Plane & p, ADynamicLight * light, Vector & nearPt, Vector & up, Vector & right, + float & scale, int desaturation, bool checkside, bool forceadditive) +{ + Vector fn, pos; + + float x = FIXED2FLOAT(light->x); + float y = FIXED2FLOAT(light->y); + float z = FIXED2FLOAT(light->z); + + float dist = fabsf(p.DistToPoint(x, z, y)); + float radius = (light->GetRadius() * gl_lights_size); + + if (radius <= 0.f) return false; + if (dist > radius) return false; + if (checkside && gl_lights_checkside && p.PointOnSide(x, z, y)) + { + return false; + } + + scale = 1.0f / ((2.f * radius) - dist); + + // project light position onto plane (find closest point on plane) + + + pos.Set(x,z,y); + fn=p.Normal(); + fn.GetRightUp(right, up); + +#ifdef _MSC_VER + nearPt = pos + fn * dist; +#else + Vector tmpVec = fn * dist; + nearPt = pos + tmpVec; +#endif + + float cs = 1.0f - (dist / radius); + if (gl_lights_additive || light->flags4&MF4_ADDITIVE || forceadditive) cs*=0.2f; // otherwise the light gets too strong. + float r = light->GetRed() / 255.0f * cs * gl_lights_intensity; + float g = light->GetGreen() / 255.0f * cs * gl_lights_intensity; + float b = light->GetBlue() / 255.0f * cs * gl_lights_intensity; + + if (light->IsSubtractive()) + { + Vector v; + + gl_RenderState.BlendEquation(GL_FUNC_REVERSE_SUBTRACT); + v.Set(r, g, b); + r = v.Length() - r; + g = v.Length() - g; + b = v.Length() - b; + } + else + { + gl_RenderState.BlendEquation(GL_FUNC_ADD); + } + if (desaturation>0) + { + float gray=(r*77 + g*143 + b*37)/257; + + r= (r*(32-desaturation)+ gray*desaturation)/32; + g= (g*(32-desaturation)+ gray*desaturation)/32; + b= (b*(32-desaturation)+ gray*desaturation)/32; + } + gl.Color3f(r,g,b); + return true; +} + + +//========================================================================== +// +// +// +//========================================================================== + +bool gl_SetupLightTexture() +{ + + if (GLRenderer->gllight == NULL) return false; + FMaterial * pat = FMaterial::ValidateTexture(GLRenderer->gllight); + pat->BindPatch(CM_DEFAULT, 0); + return true; +} + + +//========================================================================== +// +// +// +//========================================================================== + +inline fixed_t P_AproxDistance3(fixed_t dx, fixed_t dy, fixed_t dz) +{ + return P_AproxDistance(P_AproxDistance(dx,dy),dz); +} + diff --git a/src/gl/dynlights/gl_glow.cpp b/src/gl/dynlights/gl_glow.cpp new file mode 100644 index 000000000..ce763109d --- /dev/null +++ b/src/gl/dynlights/gl_glow.cpp @@ -0,0 +1,139 @@ +/* +** gl_glow.cpp +** Glowing flats like Doomsday +** +**--------------------------------------------------------------------------- +** 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "w_wad.h" +#include "sc_man.h" +#include "v_video.h" +#include "r_defs.h" +#include "textures/textures.h" + +#include "gl/dynlights/gl_glow.h" + +//=========================================================================== +// +// Reads glow definitions from GLDEFS +// +//=========================================================================== +void gl_InitGlow(FScanner &sc) +{ + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("FLATS")) + { + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + FTextureID flump=TexMan.CheckForTexture(sc.String, FTexture::TEX_Flat,FTextureManager::TEXMAN_TryAny); + FTexture *tex = TexMan[flump]; + if (tex) tex->gl_info.bGlowing = tex->gl_info.bFullbright = true; + } + } + else if (sc.Compare("WALLS")) + { + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + FTextureID flump=TexMan.CheckForTexture(sc.String, FTexture::TEX_Wall,FTextureManager::TEXMAN_TryAny); + FTexture *tex = TexMan[flump]; + if (tex) tex->gl_info.bGlowing = tex->gl_info.bFullbright = true; + } + } + else if (sc.Compare("TEXTURE")) + { + sc.SetCMode(true); + sc.MustGetString(); + FTextureID flump=TexMan.CheckForTexture(sc.String, FTexture::TEX_Flat,FTextureManager::TEXMAN_TryAny); + FTexture *tex = TexMan[flump]; + sc.MustGetStringName(","); + sc.MustGetString(); + PalEntry color = V_GetColor(NULL, sc.String); + //sc.MustGetStringName(","); + //sc.MustGetNumber(); + if (sc.CheckString(",")) + { + if (sc.CheckNumber()) + { + if (tex) tex->gl_info.GlowHeight = sc.Number; + if (!sc.CheckString(",")) goto skip_fb; + } + + sc.MustGetStringName("fullbright"); + if (tex) tex->gl_info.bFullbright = true; + } + skip_fb: + sc.SetCMode(false); + + if (tex && color != 0) + { + tex->gl_info.bGlowing = true; + tex->gl_info.GlowColor = color; + } + } + } +} + + +//========================================================================== +// +// Checks whether a sprite should be affected by a glow +// +//========================================================================== +int gl_CheckSpriteGlow(sector_t *sec, int lightlevel, int x, int y, int z) +{ + FTextureID floorpic = sec->GetTexture(sector_t::floor); + FTexture *tex = TexMan[floorpic]; + if (tex != NULL && tex->isGlowing()) + { + fixed_t floordiff = z - sec->floorplane.ZatPoint(x, y); + if (floordiff < tex->gl_info.GlowHeight*FRACUNIT && tex->gl_info.GlowHeight != 0) + { + int maxlight = (255+lightlevel)>>1; + fixed_t lightfrac = floordiff / tex->gl_info.GlowHeight; + if (lightfrac<0) lightfrac=0; + lightlevel= (lightfrac*lightlevel + maxlight*(FRACUNIT-lightfrac))>>FRACBITS; + } + } + return lightlevel; +} + diff --git a/src/gl/dynlights/gl_glow.h b/src/gl/dynlights/gl_glow.h new file mode 100644 index 000000000..bc3235685 --- /dev/null +++ b/src/gl/dynlights/gl_glow.h @@ -0,0 +1,10 @@ + +#ifndef __GL_GLOW +#define __GL_GLOW + +struct sector_t; + +void gl_InitGlow(const char * lumpnm); +int gl_CheckSpriteGlow(sector_t *sec, int lightlevel, int x, int y, int z); + +#endif diff --git a/src/gl/dynlights/gl_lightbuffer.cpp b/src/gl/dynlights/gl_lightbuffer.cpp new file mode 100644 index 000000000..d3a62a718 --- /dev/null +++ b/src/gl/dynlights/gl_lightbuffer.cpp @@ -0,0 +1,243 @@ +/* +** gl_dynlight1.cpp +** dynamic light buffer for shader rendering +** +**--------------------------------------------------------------------------- +** Copyright 2009 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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. +**--------------------------------------------------------------------------- +** +*/ + +#if 0 // unused for now. Code doesn't work + +#include "gl/system/gl_system.h" +#include "c_dispatch.h" +#include "p_local.h" +#include "vectors.h" +#include "g_level.h" + +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/data/gl_data.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/dynlights/gl_lightbuffer.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_portal.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_material.h" + + +//========================================================================== +// +// +// +//========================================================================== + +FLightBuffer::FLightBuffer() +{ + gl.GenBuffers(1, &mIDbuf_RGB); + gl.BindBuffer(GL_TEXTURE_BUFFER, mIDbuf_RGB); + + gl.GenBuffers(1, &mIDbuf_Position); + gl.BindBuffer(GL_TEXTURE_BUFFER, mIDbuf_Position); + + gl.GenTextures(1, &mIDtex_RGB); + gl.BindTexture(GL_TEXTURE_BUFFER, mIDtex_RGB); + gl.TexBufferARB(GL_TEXTURE_BUFFER, GL_RGBA8, mIDbuf_RGB); + + gl.GenTextures(1, &mIDtex_Position); + gl.BindTexture(GL_TEXTURE_BUFFER, mIDtex_Position); + gl.TexBufferARB(GL_TEXTURE_BUFFER, GL_RGBA32F, mIDbuf_Position); +} + + +//========================================================================== +// +// +// +//========================================================================== + +FLightBuffer::~FLightBuffer() +{ + gl.BindBuffer(GL_TEXTURE_BUFFER, 0); + gl.DeleteBuffers(1, &mIDbuf_RGB); + gl.DeleteBuffers(1, &mIDbuf_Position); + + gl.BindTexture(GL_TEXTURE_BUFFER, 0); + gl.DeleteTextures(1, &mIDtex_RGB); + gl.DeleteTextures(1, &mIDtex_Position); + +} + +//========================================================================== +// +// +// +//========================================================================== + +void FLightBuffer::BindTextures(int texunit1, int texunit2) +{ + gl.ActiveTexture(texunit1); + gl.BindTexture(GL_TEXTURE_BUFFER, mIDtex_RGB); + gl.ActiveTexture(texunit2); + gl.BindTexture(GL_TEXTURE_BUFFER, mIDtex_Position); + gl.ActiveTexture(GL_TEXTURE0); +} + + +//========================================================================== +// +// This collects all currently actove +// +//========================================================================== + +void FLightBuffer::CollectLightSources() +{ + if (gl_dynlight_shader && gl_lights && GLRenderer->mLightCount && gl_fixedcolormap == CM_DEFAULT) + { + TArray pLights(100); + TArray pPos(100); + TThinkerIterator it(STAT_DLIGHT); + + ADynamicLight *light; + + while ((light = it.Next()) != NULL) + { + if (!(light->flags2 & MF2_DORMANT)) + { + FLightRGB rgb; + FLightPosition pos; + + rgb.R = light->GetRed(); + rgb.G = light->GetGreen(); + rgb.B = light->GetBlue(); + rgb.Type = (light->flags4 & MF4_SUBTRACTIVE)? 128 : (light->flags4 & MF4_ADDITIVE || foggy)? 255:0; + pos.X = FIXED2FLOAT(light->x); + pos.Y = FIXED2FLOAT(light->y); + pos.Z = FIXED2FLOAT(light->z); + pos.Distance = (light->GetRadius() * gl_lights_size); + light->bufferindex = pPos.Size(); + pLights.Push(rgb); + pPos.Push(pos); + } + else light->bufferindex = -1; + } + GLRenderer->mLightCount = pPos.Size(); + + gl.BindBuffer(GL_TEXTURE_BUFFER, mIDbuf_RGB); + gl.BufferData(GL_TEXTURE_BUFFER, pLights.Size() * sizeof (FLightRGB), &pLights[0], GL_STREAM_DRAW); + + gl.BindBuffer(GL_TEXTURE_BUFFER, mIDbuf_Position); + gl.BufferData(GL_TEXTURE_BUFFER, pPos.Size() * sizeof (FLightPosition), &pPos[0], GL_STREAM_DRAW); + + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +FLightIndexBuffer::FLightIndexBuffer() +{ + gl.GenBuffers(1, &mIDBuffer); + gl.BindBuffer(GL_TEXTURE_BUFFER, mIDBuffer); + + gl.GenTextures(1, &mIDTexture); + gl.BindTexture(GL_TEXTURE_BUFFER, mIDTexture); + gl.TexBufferARB(GL_TEXTURE_BUFFER, GL_R16UI, mIDBuffer); +} + +//========================================================================== +// +// +// +//========================================================================== + +FLightIndexBuffer::~FLightIndexBuffer() +{ + gl.BindBuffer(GL_TEXTURE_BUFFER, 0); + gl.DeleteBuffers(1, &mIDBuffer); + + gl.BindTexture(GL_TEXTURE_BUFFER, 0); + gl.DeleteTextures(1, &mIDTexture); +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FLightIndexBuffer::AddLight(ADynamicLight *light) +{ + if (light->bufferindex >= 0) + { + mBuffer.Push(light->bufferindex); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FLightIndexBuffer::SendBuffer() +{ + gl.BindBuffer(GL_TEXTURE_BUFFER, mIDBuffer); + gl.BufferData(GL_TEXTURE_BUFFER, mBuffer.Size() * sizeof (short), &mBuffer[0], GL_STREAM_DRAW); + gl.BindBuffer(GL_TEXTURE_BUFFER, 0); +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FLightIndexBuffer::BindTexture(int texunit1) +{ + gl.ActiveTexture(texunit1); + gl.BindTexture(GL_TEXTURE_BUFFER, mIDTexture); + gl.ActiveTexture(GL_TEXTURE0); +} + + + +#endif \ No newline at end of file diff --git a/src/gl/dynlights/gl_lightbuffer.h b/src/gl/dynlights/gl_lightbuffer.h new file mode 100644 index 000000000..d0da46b98 --- /dev/null +++ b/src/gl/dynlights/gl_lightbuffer.h @@ -0,0 +1,66 @@ +#ifndef __GL_LIGHTBUFFER_H +#define __GL_LIGHTBUFFER_H + +#if 0 +class ADynamicLight; + +const int MAX_DYNLIGHTS = 40000; // should hopefully be enough + +struct FLightRGB +{ + unsigned char R,G,B,Type; // Type is 0 for normal, 1 for additive and 2 for subtractive +}; + +struct FLightPosition +{ + float X,Z,Y,Distance; +}; + +class FLightBuffer +{ + unsigned int mIDbuf_RGB; + unsigned int mIDbuf_Position; + + unsigned int mIDtex_RGB; + unsigned int mIDtex_Position; + +public: + FLightBuffer(); + ~FLightBuffer(); + //void MapBuffer(); + //void UnmapBuffer(); + void BindTextures(int uniloc1, int uniloc2); + //void AddLight(ADynamicLight *light, bool foggy); + void CollectLightSources(); +}; + +class FLightIndexBuffer +{ + unsigned int mIDBuffer; + unsigned int mIDTexture; + + TArray mBuffer; + +public: + + FLightIndexBuffer(); + ~FLightIndexBuffer(); + void AddLight(ADynamicLight *light); + void SendBuffer(); + void BindTexture(int loc1); + + void ClearBuffer() + { + mBuffer.Clear(); + } + + int GetLightIndex() + { + return mBuffer.Size(); + } + +}; + +#endif + +#endif \ No newline at end of file diff --git a/src/gl/gl_builddraw.cpp b/src/gl/gl_builddraw.cpp new file mode 100644 index 000000000..60b94b984 --- /dev/null +++ b/src/gl/gl_builddraw.cpp @@ -0,0 +1,594 @@ +/* +** gl_builddraw.cpp +** a build-like rendering algorithm +** Uses the sections created in gl_sections.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2008 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "i_system.h" +#include "p_local.h" +#include "c_dispatch.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/scene/gl_clipper.h" +#include "gl/utility/gl_clock.h" +#include "gl/data/gl_sections.h" +#include "gl/scene/gl_wall.h" + +#ifdef BUILD_TEST +#define D(x) x +#else +#define D(x) do{}while(0) +#endif + +EXTERN_CVAR (Bool, dumpsections) + +struct FBunch +{ + int startline; + int endline; + angle_t startangle; + angle_t endangle; + fixed_t minviewdist; + fixed_t maxviewdist; +}; + +void DoSubsector(subsector_t * sub, bool handlelines); + +EXTERN_CVAR(Bool, gl_render_walls) + +//========================================================================== +// +// From Build but changed to use doubles to prevent overflows +// +//========================================================================== + +static int WallInFront(FGLSectionLine *wal1, FGLSectionLine *wal2) +{ + double x11, y11, x21, y21, x12, y12, x22, y22, dx, dy, t1, t2; + + x11 = wal1->start->x; + y11 = wal1->start->y; + x21 = wal1->end->x; + y21 = wal1->end->y; + x12 = wal2->start->x; + y12 = wal2->start->y; + x22 = wal2->end->x; + y22 = wal2->end->y; + + dx = x21-x11; dy = y21-y11; + + t1 = (x12-x11)*dy - (y12-y11)*dx; + t2 = (x22-x11)*dy - (y22-y11)*dx; + if (t1 == 0) + { + t1 = t2; + if (t1 == 0) return(-1); + } + if (t2 == 0) t2 = t1; + + if ((t1*t2) >= 0) + { + t2 = (double(viewx)-x11) * dy - (double(viewy)-y11)*dx; + return((t2*t1) < 0); + } + + dx = x22-x12; dy = y22-y12; + t1 = (x11-x12)*dy - (y11-y12)*dx; + t2 = (x21-x12)*dy - (y21-y12)*dx; + if (t1 == 0) + { + t1 = t2; + if (t1 == 0) return(-1); + } + if (t2 == 0) t2 = t1; + if ((t1*t2) >= 0) + { + t2 = (double(viewx)-x12) * dy - (double(viewy)-y12)*dx; + return((t2*t1) >= 0); + } + return(-2); +} + +//========================================================================== +// +// This is a bit more complicated than it looks because angles can wrap +// around so we can only compare angle differences. +// +// Rules: +// 1. Any bunch can span at most 180°. +// 2. 2 bunches can never overlap at both ends +// 3. if there is an overlap one of the 2 starting points must be in the +// overlapping area. +// +//========================================================================== + +static int BunchInFront(FBunch *b1, FBunch *b2) +{ + angle_t anglecheck, endang; + + if (b2->startangle - b1->startangle < b1->endangle - b1->startangle) + { + // we have an overlap at b2->startangle + anglecheck = b2->startangle - b1->startangle; + + // Find the wall in b1 that overlaps b2->startangle + for(int i = b1->startline; i <= b1->endline; i++) + { + #ifdef _DEBUG + angle_t startang = SectionLines[i].start->GetClipAngleInverse() - b1->startangle; + #endif + endang = SectionLines[i].end->GetClipAngleInverse() - b1->startangle; + if (endang > anglecheck) + { + assert (startang <= anglecheck); + + // found a line + int ret = WallInFront(&SectionLines[b2->startline], &SectionLines[i]); + + D(Printf (PRINT_LOG, "Line %d <-> line %d: Result = %d.\n", + SectionLines[b2->startline].linedef-lines, + SectionLines[i].linedef-lines, ret)); + + return ret; + } + } + } + else if (b1->startangle - b2->startangle < b2->endangle - b2->startangle) + { + // we have an overlap at b1->startangle + anglecheck = b1->startangle - b2->startangle; + + // Find the wall in b2 that overlaps b1->startangle + for(int i = b2->startline; i <= b2->endline; i++) + { + #ifdef _DEBUG + angle_t startang = SectionLines[i].start->GetClipAngleInverse() - b2->startangle; + #endif + endang = SectionLines[i].end->GetClipAngleInverse() - b2->startangle; + if (endang > anglecheck) + { + assert (startang <= anglecheck); + + // found a line + int ret = WallInFront(&SectionLines[i], &SectionLines[b1->startline]); + + D(Printf (PRINT_LOG, "Line %d <-> line %d: Result = %d,\n", + SectionLines[i].linedef-lines, + SectionLines[b1->endline].linedef-lines, ret)); + + return ret; + } + } + } + // we have no overlap + return -1; +} + + +// ---------------------------------------------------------------------------- +// +// Bunches are groups of continuous lines +// This array stores the amount of points per bunch, +// the view angles for each point and the line index for the starting line +// +// ---------------------------------------------------------------------------- + +class BunchDrawer +{ + int LastBunch; + int StartTime; + TArray Bunches; + TArray CompareData; + sector_t fakebacksec; + + //========================================================================== + // + // + // + //========================================================================== +public: + BunchDrawer() + { + StartScene(); + } + + //========================================================================== + // + // + // + //========================================================================== +private: + void StartScene() + { + LastBunch = 0; + StartTime = I_MSTime(); + Bunches.Clear(); + } + + //========================================================================== + // + // + // + //========================================================================== + + void StartBunch(int linenum, angle_t startan, angle_t endan, vertex_t *startpt, vertex_t *endpt) + { + FBunch *bunch = &Bunches[LastBunch = Bunches.Reserve(1)]; + + bunch->startline = bunch->endline = linenum; + bunch->startangle = startan; + bunch->endangle = endan; + } + + //========================================================================== + // + // + // + //========================================================================== + + void AddLineToBunch(int newan) + { + Bunches[LastBunch].endline++; + Bunches[LastBunch].endangle = newan; + } + + //========================================================================== + // + // + // + //========================================================================== + + void DeleteBunch(int index) + { + Bunches.Delete(index); + } + + //========================================================================== + // + // ClipLine + // Clips the given segment + // + //========================================================================== + + enum + { + CL_Skip = 0, + CL_Draw = 1, + CL_Pass = 2, + }; + + + int ClipLine (FGLSectionLine *line, sector_t * sector, sector_t **pbacksector) + { + angle_t startAngle, endAngle; + sector_t * backsector = NULL; + bool blocking; + + startAngle = line->end->GetClipAngle(); + endAngle = line->start->GetClipAngle(); + *pbacksector = NULL; + + // Back side, i.e. backface culling - read: endAngle >= startAngle! + if (startAngle-endAngleotherside == -1) + { + // one-sided + clipper.SafeAddClipRange(startAngle, endAngle); + return CL_Draw; + } + else if (line->polysub == NULL) + { + // two sided and not a polyobject + if (line->linedef == NULL) + { + // Miniseg + return CL_Pass; + } + if (sector->sectornum == line->refseg->backsector->sectornum) + { + FTexture *tex = TexMan(line->sidedef->GetTexture(side_t::mid)); + if (!tex || tex->UseType==FTexture::TEX_Null) + { + // no mid texture: nothing to do here + return CL_Pass; + } + *pbacksector = sector; + return CL_Draw|CL_Pass; + } + else + { + // clipping checks are only needed when the backsector is not the same as the front sector + gl_CheckViewArea(line->start, line->end, line->refseg->frontsector, line->refseg->backsector); + + *pbacksector = backsector = gl_FakeFlat(line->refseg->backsector, &fakebacksec, true); + + blocking = gl_CheckClip(line->sidedef, sector, backsector); + if (blocking) + { + clipper.SafeAddClipRange(startAngle, endAngle); + return CL_Draw; + } + return CL_Draw|CL_Pass; + } + } + else + { + *pbacksector = sector; + return CL_Draw; + } + } + + //========================================================================== + // + // + // + //========================================================================== + + void ProcessBunch(int bnch) + { + FBunch *bunch = &Bunches[bnch]; + + sector_t fake; + sector_t *sec; + sector_t *backsector; + + D(Printf(PRINT_LOG, "------------------------------\nProcessing bunch %d (Startline %d)\n",bnch,SectionLines[bunch->startline].linedef-lines)); + ClipWall.Clock(); + for(int i=bunch->startline; i <= bunch->endline; i++) + { + FGLSectionLine *ln = &SectionLines[i]; + + + // Draw this line. todo: optimize + sec = gl_FakeFlat(ln->refseg->frontsector, &fake, false); + + int clipped = ClipLine(ln, sec, &backsector); + + D(Printf(PRINT_LOG, "line %d clip result is %d\n", ln->linedef - lines, clipped)); + + if (clipped & CL_Draw) + { + ln->linedef->flags |= ML_MAPPED; + + if (ln->linedef->validcount!=validcount) + { + ln->linedef->validcount=validcount; + + #ifndef BUILD_TEST + if (gl_render_walls) + { + SetupWall.Clock(); + + GLWall wall; + wall.Process(ln->refseg, sec, backsector, ln->polysub); + rendered_lines++; + + SetupWall.Unclock(); + } + #endif + } + } + + if (clipped & CL_Pass) + { + ClipWall.Unclock(); + ProcessSection(ln->otherside); + ClipWall.Clock(); + } + } + D(Printf(PRINT_LOG, "Bunch %d done\n------------------------------\n",bnch)); + ClipWall.Unclock(); + } + + //========================================================================== + // + // + // + //========================================================================== + + int FindClosestBunch() + { + int closest = 0; //Almost works, but not quite :( + + CompareData.Clear(); + for(unsigned i = 1; i < Bunches.Size(); i++) + { + switch (BunchInFront(&Bunches[i], &Bunches[closest])) + { + case 0: // i is in front + closest = i; + continue; + + case 1: // i is behind + continue; + + default: // can't determine + CompareData.Push(i); // mark for later comparison + continue; + } + } + + // we need to do a second pass to see how the marked bunches relate to the currently closest one. + for(unsigned i = 0; i < CompareData.Size(); i++) + { + switch (BunchInFront(&Bunches[CompareData[i]], &Bunches[closest])) + { + case 0: // is in front + closest = i; + CompareData.Delete(i); + i = 0; // we need to recheck everything that's still marked. + continue; + + case 1: // is behind + CompareData.Delete(i); + i--; + continue; + + default: + continue; + + } + } + return closest; + } + + //========================================================================== + // + // + // + //========================================================================== + + void ProcessSection(int sectnum) + { + FGLSection *sect = &Sections[sectnum]; + bool inbunch; + angle_t startangle; + + if (sect->validcount == StartTime) return; + sect->validcount = StartTime; + D(Printf(PRINT_LOG, "------------------------------\nProcessing section %d (sector %d)\n",sectnum, sect->sector->sectornum)); + + #ifndef BUILD_TEST + for(unsigned i = 0; i < sect->subsectors.Size(); i++) + { + DoSubsector(sect->subsectors[i], false); + if (sect->subsectors[i]->poly != NULL) + { + // ProcessPolyobject() + } + } + #endif + + //Todo: process subsectors + for(int i=0; inumloops; i++) + { + FGLSectionLoop *loop = sect->GetLoop(i); + inbunch = false; + + for(int j=0; jnumlines; j++) + { + FGLSectionLine *ln = loop->GetLine(j); + + angle_t ang1 = ln->start->GetClipAngle(); + angle_t ang2 = ln->end->GetClipAngle(); + + if (ang2 - ang1 < ANGLE_180) + { + // Backside + D(Printf(PRINT_LOG, "line %d facing backwards\n", ln->linedef - lines)); + inbunch = false; + } + else if (!clipper.SafeCheckRange(ang2, ang1)) + { + // is it visible? + D(Printf(PRINT_LOG, "line %d not in view\n", ln->linedef - lines)); + inbunch = false; + } + else if (!inbunch || startangle - ang2 >= ANGLE_180) + { + // don't let a bunch span more than 180° to avoid problems. + // This limitation ensures that the combined range of 2 + // bunches will always be less than 360° which simplifies + // the distance comparison code because it prevents a + // situation where 2 bunches may overlap at both ends. + D(Printf(PRINT_LOG, "Starting bunch %d at line %d\n",Bunches.Size(), ln->linedef - lines)); + + startangle = ang2; + // Clipping angles are backward which makes this code very hard to read so let's use the inverse + StartBunch(loop->startline + j, 0 - ang1, 0 - ang2); + inbunch = true; + } + else + { + D(Printf(PRINT_LOG, " Adding line %d\n", ln->linedef - lines)); + AddLineToBunch(0 - ang2); + } + } + } + D(Printf(PRINT_LOG, "Section %d done\n------------------------------\n",sectnum)); + } + + //========================================================================== + // + // + // + //========================================================================== + +public: + void RenderScene(int viewsection) + { + ProcessSection(viewsection); + while (Bunches.Size() > 0) + { + int closest = FindClosestBunch(); + ProcessBunch(closest); + DeleteBunch(closest); + } + } +}; + + +void gl_RenderBuild() +{ + subsector_t *sub = R_PointInSubsector(viewx, viewy); + + clipper.Clear(); + angle_t a1 = GLRenderer->FrustumAngle(); + clipper.SafeAddClipRangeRealAngles(viewangle+a1, viewangle-a1); + if (Sections.Size() == 0) gl_CreateSections(); + + int startsection = SectionForSubsector[sub-subsectors]; + + BunchDrawer bd; + bd.RenderScene(startsection); +} + +#ifdef BUILD_TEST +CCMD(testrender) +{ + gl_RenderBuild(); +} +#endif diff --git a/src/gl/gl_functions.h b/src/gl/gl_functions.h new file mode 100644 index 000000000..aa08d19be --- /dev/null +++ b/src/gl/gl_functions.h @@ -0,0 +1,13 @@ +#ifndef __GL_FUNCT +#define __GL_FUNCT + +#include "v_palette.h" + +class AActor; + +void gl_PreprocessLevel(); +void gl_CleanLevelData(); +void gl_LinkLights(); +void gl_SetActorLights(AActor *); + +#endif diff --git a/src/gl/hqnx/Image.cpp b/src/gl/hqnx/Image.cpp new file mode 100644 index 000000000..089036365 --- /dev/null +++ b/src/gl/hqnx/Image.cpp @@ -0,0 +1,1174 @@ +//CImage class - loading and saving BMP and TGA files +//---------------------------------------------------------- +//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) +// +//This program is free software; you can redistribute it and/or +//modify it under the terms of the GNU Lesser General Public +//License as published by the Free Software Foundation; either +//version 2.1 of the License, or (at your option) any later version. +// +//This program is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +//Lesser General Public License for more details. +// +//You should have received a copy of the GNU Lesser General Public +//License along with this program; if not, write to the Free Software +//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include +#include +#include "Image.h" + +DLL CImage::CImage() +{ + m_Xres = m_Yres = m_NumPixel = 0; + m_pBitmap = NULL; +} + +DLL CImage::~CImage() +{ + Destroy(); +} + +int DLL CImage::Init( int X, int Y, unsigned short BitPerPixel ) +{ + if (m_pBitmap != NULL) + free(m_pBitmap); + + m_Xres = X; + m_Yres = Y; + m_BitPerPixel = BitPerPixel<=8 ? 8 : BitPerPixel<=16 ? 16 : BitPerPixel<=24 ? 24 : 32; + m_BytePerPixel = m_BitPerPixel >> 3; + m_NumPixel = m_Xres*m_Yres; + int size = m_NumPixel*((m_BitPerPixel+7)/8); + m_pBitmap=(unsigned char *)malloc(size); + return (m_pBitmap != NULL) ? 0 : 1; +} + +int DLL CImage::SetImage(unsigned char *img, int width, int height, int bpp) +{ + Init(width, height, bpp); + + memcpy(m_pBitmap, img, m_NumPixel * m_BytePerPixel); + + return 0; +} + +int DLL CImage::Destroy() +{ + if (m_pBitmap) + { + free(m_pBitmap); + m_pBitmap = NULL; + } + m_Xres = 0; + m_Yres = 0; + m_NumPixel = 0; + m_BitPerPixel = 0; + return 0; +} + +int DLL CImage::Convert32To17( void ) +{ + int nRes = eConvUnknownFormat; + + if ( m_BitPerPixel == 32 ) + { + if ( m_pBitmap != NULL ) + { + unsigned char * pTemp8 = m_pBitmap; + unsigned int * pTemp32 = (unsigned int *)m_pBitmap; + unsigned int a, r, g, b; + for ( int i=0; i> 3; + g = (*(pTemp8++)) >> 2; + r = (*(pTemp8++)) >> 3; + a = *(pTemp8++); + *pTemp32 = (r << 11) + (g << 5) + b + (a > 127 ? 0x10000 : 0); + pTemp32++; + } + } + else + nRes = eConvSourceMemory; + + nRes = 0; + } + + return nRes; +} + +int DLL CImage::ConvertTo32( void ) +{ + int nRes = eConvUnknownFormat; + + if ( m_pBitmap == NULL ) + return eConvSourceMemory; + + switch ( m_BitPerPixel ) + { + case 8: + { + nRes = 0; + m_BitPerPixel = 32; + unsigned char * pNewBitmap = (unsigned char *)malloc(m_NumPixel*4); + if ( pNewBitmap != NULL ) + { + unsigned char * pTemp8 = m_pBitmap; + unsigned char * pTemp32 = pNewBitmap; + unsigned char c; + for ( int i=0; i> 3); + *(pTemp24++) = ((rgb & 0xF800) >> 8); + } + free(m_pBitmap); + m_pBitmap = pNewBitmap; + } + else + nRes = eConvDestMemory; + + break; + } + case 32: + { + nRes = 0; + m_BitPerPixel = 24; + unsigned char * pNewBitmap = (unsigned char *)malloc(m_NumPixel*3); + if ( pNewBitmap != NULL ) + { + unsigned char * pTemp32 = m_pBitmap; + unsigned char * pTemp24 = pNewBitmap; + for ( int i=0; i> 3; + g = m_Pal[c].g >> 2; + b = m_Pal[c].b >> 3; + *(pTemp16++) = (r << 11) + (g << 5) + b; + } + free(m_pBitmap); + m_pBitmap = pNewBitmap; + } + else + nRes = eConvDestMemory; + + break; + } + case 24: + { + nRes = 0; + m_BitPerPixel = 16; + unsigned char * pNewBitmap = (unsigned char *)malloc(m_NumPixel*2); + if ( pNewBitmap != NULL ) + { + unsigned char * pTemp24 = m_pBitmap; + unsigned short * pTemp16 = (unsigned short *)pNewBitmap; + unsigned short r, g, b; + for ( int i=0; i> 3; + g = (*(pTemp24++)) >> 2; + r = (*(pTemp24++)) >> 3; + *(pTemp16++) = (r << 11) + (g << 5) + b; + } + free(m_pBitmap); + m_pBitmap = pNewBitmap; + } + else + nRes = eConvDestMemory; + + break; + } + case 32: + { + nRes = 0; + m_BitPerPixel = 16; + unsigned char * pNewBitmap = (unsigned char *)malloc(m_NumPixel*2); + if ( pNewBitmap != NULL ) + { + unsigned char * pTemp32 = m_pBitmap; + unsigned short * pTemp16 = (unsigned short *)pNewBitmap; + unsigned short r, g, b; + for ( int i=0; i> 3; + g = (*(pTemp32++)) >> 2; + r = (*(pTemp32++)) >> 3; + pTemp32++; + *(pTemp16++) = (r << 11) + (g << 5) + b; + } + free(m_pBitmap); + m_pBitmap = pNewBitmap; + } + else + nRes = eConvDestMemory; + + break; + } + } + + return nRes; +} + +int CImage::Convert8To17( int transindex ) +{ + int nRes = eConvUnknownFormat; + + if ( m_BitPerPixel == 8 ) + { + m_BitPerPixel = 32; + unsigned char * pNewBitmap = (unsigned char *)malloc(m_NumPixel*4); + if ( pNewBitmap != NULL ) + { + unsigned char * pTemp8 = m_pBitmap; + unsigned int * pTemp32 = (unsigned int *)pNewBitmap; + unsigned int r, g, b; + unsigned char c; + for ( int i=0; i> 3; + g = m_Pal[c].g >> 2; + b = m_Pal[c].b >> 3; + *(pTemp32++) = (r << 11) + (g << 5) + b + (transindex != c ? 0x10000 : 0); + } + free(m_pBitmap); + m_pBitmap = pNewBitmap; + } + else + nRes = eConvDestMemory; + + nRes = 0; + } + + return nRes; +} + +int CImage::SaveBmp(char *szFilename) +{ + _BMPFILEHEADER fh; + _BMPIMAGEHEADER ih; + unsigned char BmpPal[256][4]; + long int SuffLen; + long int Dummy = 0; + unsigned char * pBuf; + short i; + + if (!(f = fopen(szFilename, "wb"))) return eSaveBmpFileOpen; + if ( m_pBitmap == NULL ) return eSaveBmpSourceMemory; + + fh.bfType=0x4D42; + if (m_BitPerPixel==8) + { + SuffLen=((m_Xres+3)/4)*4-m_Xres; + ih.biSize=0x28; + ih.biWidth=m_Xres; + ih.biHeight=m_Yres; + ih.biPlanes=1; + ih.biBitCount=8; + ih.biCompression=0; + ih.biSizeImage=(m_Xres+SuffLen)*m_Yres; + ih.biXPelsPerMeter=ih.biYPelsPerMeter=0x2E23; // 300dpi (pixels per meter) + ih.biClrUsed=ih.biClrImportant=0; + fh.bfSize=(ih.biSizeImage)+0x0436; + fh.bfRes1=0; + fh.bfOffBits=0x0436; + if (fwrite(&fh, 14, 1, f) != 1) return eSaveBmpFileWrite; + if (fwrite(&ih, 40, 1, f) != 1) return eSaveBmpFileWrite; + for (i=0; i<256; i++) + { + BmpPal[i][0]=m_Pal[i].b; + BmpPal[i][1]=m_Pal[i].g; + BmpPal[i][2]=m_Pal[i].r; + BmpPal[i][3]=0; + } + if (fwrite(&BmpPal, 1024, 1, f) != 1) return eSaveBmpFileWrite; + pBuf=m_pBitmap; + pBuf+=m_NumPixel; + for (i=0; i0) + { + if (fwrite(&Dummy, SuffLen, 1, f) != 1) return eSaveBmpFileWrite; + } + } + } + else + if (m_BitPerPixel==24) + { + SuffLen=((m_Xres*3+3)/4)*4-m_Xres*3; + ih.biSize=0x28; + ih.biWidth=m_Xres; + ih.biHeight=m_Yres; + ih.biPlanes=1; + ih.biBitCount=24; + ih.biCompression=0; + ih.biSizeImage=(m_Xres*3+SuffLen)*m_Yres; + ih.biXPelsPerMeter=ih.biYPelsPerMeter=0x2E23; // 300dpi (pixels per meter) + ih.biClrUsed=ih.biClrImportant=0; + fh.bfSize=(ih.biSizeImage)+0x0036; + fh.bfRes1=0; + fh.bfOffBits=0x0036; + if (fwrite(&fh, 14, 1, f) != 1) return eSaveBmpFileWrite; + if (fwrite(&ih, 40, 1, f) != 1) return eSaveBmpFileWrite; + pBuf=m_pBitmap; + pBuf+=m_NumPixel*3; + for (i=0; i0) + { + if (fwrite(&Dummy, SuffLen, 1, f) != 1) return eSaveBmpFileWrite; + } + } + } + else + return eSaveBmpColorDepth; + + fclose(f); + + return 0; +} + +int CImage::LoadBmp(char *szFilename) +{ + _BMPFILEHEADER fh; + _BMPIMAGEHEADEROLD ih_old; + _BMPIMAGEHEADER ih; + unsigned char BmpPal[256][4]; + long int biSize; + long int SuffLen; + long int Dummy = 0; + unsigned char * pBuf; + short i; + long int xres, yres; + unsigned short bits; + + if (!(f = fopen(szFilename, "rb"))) return eLoadBmpFileOpen; + if (fread(&fh, 14, 1, f) != 1) return eLoadBmpFileRead; + if (fh.bfType != 0x4D42) return eLoadBmpBadFormat; + if (fread(&biSize, 4, 1, f) != 1) return eLoadBmpFileRead; + if (biSize > 12) + { + fseek( f, -4, SEEK_CUR ); + if (fread(&ih, biSize, 1, f) != 1) return eLoadBmpFileRead; + xres = ih.biWidth; + yres = ih.biHeight; + bits = ih.biBitCount; + } + else + { + fseek( f, -4, SEEK_CUR ); + if (fread(&ih_old, biSize, 1, f) != 1) return eLoadBmpFileRead; + xres = ih_old.biWidth; + yres = ih_old.biHeight; + bits = ih_old.biBitCount; + } + + if ( Init( xres, yres, bits ) != 0 ) return eLoadBmpInit; + if (m_BitPerPixel==8) + { + SuffLen=((m_Xres+3)/4)*4-m_Xres; + if (fread(&BmpPal, 1024, 1, f) != 1) return eLoadBmpFileRead; + for (i=0; i<256; i++) + { + m_Pal[i].b=BmpPal[i][0]; + m_Pal[i].g=BmpPal[i][1]; + m_Pal[i].r=BmpPal[i][2]; + } + pBuf=m_pBitmap; + pBuf+=m_NumPixel; + for (i=0; i0) + { + if (fread(&Dummy, SuffLen, 1, f) != 1) return eLoadBmpFileRead; + } + } + } + else + if (m_BitPerPixel==24) + { + SuffLen=((m_Xres*3+3)/4)*4-(m_Xres*3); + pBuf=m_pBitmap; + pBuf+=m_NumPixel*3; + for (i=0; i0) + { + if (fread(&Dummy, SuffLen, 1, f) != 1) return eLoadBmpFileRead; + } + } + } + else + return eLoadBmpColorDepth; + + fclose(f); + + return 0; +} + +void CImage::Output( void ) +{ + fwrite(m_cBuf, m_nCount, 1, f); + m_nCount=0; +} + +void CImage::Output( char c ) +{ + if ( m_nCount == sizeof(m_cBuf) ) + { + fwrite(m_cBuf, m_nCount, 1, f); + m_nCount=0; + } + m_cBuf[m_nCount++] = c; +} + +void CImage::Output( char * pcData, int nSize ) +{ + for ( int i=0; i 0 ) + { + Output( nDif-1 ); + Output( (char*)(pcolBuf+i-nDif-nRep), nDif ); + } + nDif = 1; + } + } + else + { + if ( bEqual && (nRep<127) ) + nRep++; + else + { + Output( nRep+128 ); + Output( (char*)&colOld, 1 ); + nRep = 0; + nDif = 1; + } + } + } + + if ( nRep == 0 ) + { + Output( nDif-1 ); + Output( (char*)(pcolBuf+m_Xres-nDif), nDif ); + } + else + { + Output( nRep+128 ); + Output( (char*)&colOld, 1 ); + } + Output(); + } + } + } + else + if (m_BitPerPixel==24) + { + fh.tiImageType = bCompressed ? 10 : 2; + if (fwrite(&fh, sizeof(fh), 1, f) != 1) return eSaveTgaFileWrite; + + _BGR * pcolBuf = (_BGR *)m_pBitmap; + pcolBuf += m_NumPixel; + + if ( !bCompressed ) + { + for (j=0; j 0 ) + { + Output( nDif-1 ); + Output( (char*)(pcolBuf+i-nDif-nRep), sizeof(_BGR)*nDif ); + } + nDif = 1; + } + } + else + { + if ( bEqual && (nRep<127) ) + nRep++; + else + { + Output( nRep+128 ); + Output( (char*)&colOld, sizeof(_BGR) ); + nRep = 0; + nDif = 1; + } + } + } + + if ( nRep == 0 ) + { + Output( nDif-1 ); + Output( (char*)(pcolBuf+m_Xres-nDif), sizeof(_BGR)*nDif ); + } + else + { + Output( nRep+128 ); + Output( (char*)&colOld, sizeof(_BGR) ); + } + Output(); + } + } + } + else + if (m_BitPerPixel==32) + { + fh.tiImageType = bCompressed ? 10 : 2; + fh.tiAttrBits = 8; + if (fwrite(&fh, sizeof(fh), 1, f) != 1) return eSaveTgaFileWrite; + + _BGRA * pcolBuf = (_BGRA *)m_pBitmap; + pcolBuf += m_NumPixel; + + if ( !bCompressed ) + { + for (j=0; j 0 ) + { + Output( nDif-1 ); + Output( (char*)(pcolBuf+i-nDif-nRep), sizeof(_BGRA)*nDif ); + } + nDif = 1; + } + } + else + { + if ( bEqual && (nRep<127) ) + nRep++; + else + { + Output( nRep+128 ); + Output( (char*)&colOld, sizeof(_BGRA) ); + nRep = 0; + nDif = 1; + } + } + } + + if ( nRep == 0 ) + { + Output( nDif-1 ); + Output( (char*)(pcolBuf+m_Xres-nDif), sizeof(_BGRA)*nDif ); + } + else + { + Output( nRep+128 ); + Output( (char*)&colOld, sizeof(_BGRA) ); + } + Output(); + } + } + } + else + return eSaveTgaColorDepth; + + fclose(f); + + return 0; +} + +int CImage::LoadTga(char *szFilename) +{ + _TGAHEADER fh; + int i, j, k; + unsigned char nCount; + + if (!(f = fopen(szFilename, "rb"))) return eLoadTgaFileOpen; + if (fread(&fh, sizeof(fh), 1, f) != 1) return eLoadTgaFileRead; + bool bCompressed = (( fh.tiImageType & 8 ) != 0); + if ((fh.tiBitPerPixel<=0) || (fh.tiBitPerPixel>32)) + return eLoadTgaBadFormat; + + if ( Init( fh.tiXres, fh.tiYres, fh.tiBitPerPixel ) != 0 ) + return eLoadTgaInit; + + if ( m_BitPerPixel == 8 ) + { + if ( fh.tiPaletteIncluded == 1 ) + { + if ( fh.tiPaletteBpp == 24) + { + if (fread(&m_Pal, 3, fh.tiPaletteSize, f) != fh.tiPaletteSize) + return eLoadTgaFileRead; + } + else + if ( fh.tiPaletteBpp == 32) + { + unsigned char BmpPal[256][4]; + + if (fread(&BmpPal, 4, fh.tiPaletteSize, f) != fh.tiPaletteSize) + return eLoadTgaFileRead; + + for (i=0; i= m_pBitmap ) + { + nCount = Input(); + if ((nCount & 128)==0) + { + for (k=0; k<=nCount; k++) + { + colCur = Input(); + *(pcolBuf+i) = colCur; + if ( (++i) == m_Xres ) + { + i=0; + pcolBuf -= m_Xres; + break; + } + } + } + else + { + colCur = Input(); + for (k=0; k<=nCount-128; k++) + { + *(pcolBuf+i) = colCur; + if ( (++i) == m_Xres ) + { + i=0; + pcolBuf -= m_Xres; + break; + } + } + } + } + } + } + else + if ( m_BitPerPixel == 24 ) + { + _BGR * pcolBuf = (_BGR *)m_pBitmap; + pcolBuf += m_NumPixel; + + if ( !bCompressed ) + { + for (j=0; j= (_BGR *)m_pBitmap ) + { + nCount = Input(); + if ((nCount & 128)==0) + { + for (k=0; k<=nCount; k++) + { + colCur.b = Input(); + colCur.g = Input(); + colCur.r = Input(); + *(pcolBuf+i) = colCur; + if ( (++i) == m_Xres ) + { + i=0; + pcolBuf -= m_Xres; + break; + } + } + } + else + { + colCur.b = Input(); + colCur.g = Input(); + colCur.r = Input(); + for (k=0; k<=nCount-128; k++) + { + *(pcolBuf+i) = colCur; + if ( (++i) == m_Xres ) + { + i=0; + pcolBuf -= m_Xres; + break; + } + } + } + } + } + } + else + if ( m_BitPerPixel == 32 ) + { + _BGRA * pcolBuf = (_BGRA *)m_pBitmap; + pcolBuf += m_NumPixel; + + if ( !bCompressed ) + { + for (j=0; j= (_BGRA *)m_pBitmap ) + { + nCount = Input(); + if ((nCount & 128)==0) + { + for (k=0; k<=nCount; k++) + { + colCur.b = Input(); + colCur.g = Input(); + colCur.r = Input(); + colCur.a = Input(); + *(pcolBuf+i) = colCur; + if ( (++i) == m_Xres ) + { + i=0; + pcolBuf -= m_Xres; + break; + } + } + } + else + { + colCur.b = Input(); + colCur.g = Input(); + colCur.r = Input(); + colCur.a = Input(); + for (k=0; k<=nCount-128; k++) + { + *(pcolBuf+i) = colCur; + if ( (++i) == m_Xres ) + { + i=0; + pcolBuf -= m_Xres; + break; + } + } + } + } + } + } + else + return eLoadTgaColorDepth; + + fclose(f); + + return 0; +} + +int DLL CImage::Load(char *szFilename) +{ + int nRes = 0; + + if ( szFilename != NULL ) + { + char * szExt = strrchr( szFilename, '.' ); + int nNotTGA = 1; + + if ( szExt != NULL ) + nNotTGA = _stricmp( szExt, ".tga" ); + + if ( nNotTGA != 0 ) + nRes = LoadBmp( szFilename ); + else + nRes = LoadTga( szFilename ); + } + else + nRes = eLoadFilename; + + return nRes; +} + +int DLL CImage::Save(char *szFilename) +{ + int nRes = 0; + int nNotTGA = 1; + + if ( szFilename != NULL ) + { + char * szExt = strrchr( szFilename, '.' ); + + if ( szExt != NULL ) + nNotTGA = _stricmp( szExt, ".tga" ); + + if ( nNotTGA != 0 ) + { + if (( m_BitPerPixel == 16 ) || ( m_BitPerPixel == 32 )) + nRes = ConvertTo24(); + + if (nRes == 0) + nRes = SaveBmp( szFilename ); + } + else + { + if ( m_BitPerPixel == 16 ) + nRes = ConvertTo24(); + + if (nRes == 0) + nRes = SaveTga( szFilename, true ); + } + } + else + nRes = eSaveFilename; + + return nRes; +} diff --git a/src/gl/hqnx/Image.h b/src/gl/hqnx/Image.h new file mode 100644 index 000000000..684aba164 --- /dev/null +++ b/src/gl/hqnx/Image.h @@ -0,0 +1,145 @@ +//CImage class - loading and saving BMP and TGA files +//---------------------------------------------------------- +//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) +// +//This program is free software; you can redistribute it and/or +//modify it under the terms of the GNU Lesser General Public +//License as published by the Free Software Foundation; either +//version 2.1 of the License, or (at your option) any later version. +// +//This program is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +//Lesser General Public License for more details. +// +//You should have received a copy of the GNU Lesser General Public +//License along with this program; if not, write to the Free Software +//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +//#ifdef WIN32 +//#define DLL __declspec(dllexport) +//#else +#define DLL +//#endif + +#include +#pragma once +#pragma warning(disable: 4103) +#pragma pack(1) + +typedef struct { unsigned char b, g, r; } _BGR; +typedef struct { unsigned char b, g, r, a; } _BGRA; + +class CImage +{ + public: + DLL CImage(); + DLL ~CImage(); + + enum CImageErrors + { + eConvUnknownFormat = 10, + eConvSourceMemory = 11, + eConvDestMemory = 12, + + eSaveBmpFileOpen = 20, + eSaveBmpFileWrite = 21, + eSaveBmpSourceMemory = 22, + eSaveBmpColorDepth = 23, + + eLoadBmpFileOpen = 30, + eLoadBmpFileRead = 31, + eLoadBmpBadFormat = 32, + eLoadBmpInit = 33, + eLoadBmpColorDepth = 34, + + eSaveTgaFileOpen = 40, + eSaveTgaFileWrite = 41, + eSaveTgaSourceMemory = 42, + eSaveTgaColorDepth = 43, + + eLoadTgaFileOpen = 50, + eLoadTgaFileRead = 51, + eLoadTgaBadFormat = 52, + eLoadTgaInit = 53, + eLoadTgaColorDepth = 54, + + eLoadFilename = 60, + eSaveFilename = 61, + }; + + struct _BMPFILEHEADER + { + unsigned short bfType; + long int bfSize, bfRes1, bfOffBits; + }; + + struct _BMPIMAGEHEADEROLD + { + long int biSize; + unsigned short biWidth, biHeight; + unsigned short biPlanes, biBitCount; + }; + + struct _BMPIMAGEHEADER + { + long int biSize, biWidth, biHeight; + unsigned short biPlanes, biBitCount; + long int biCompression, biSizeImage; + long int biXPelsPerMeter, biYPelsPerMeter; + long int biClrUsed, biClrImportant; + }; + + struct _TGAHEADER + { + unsigned char tiIdentSize; + unsigned char tiPaletteIncluded; + unsigned char tiImageType; + unsigned short tiPaletteStart; + unsigned short tiPaletteSize; + unsigned char tiPaletteBpp; + unsigned short tiX0; + unsigned short tiY0; + unsigned short tiXres; + unsigned short tiYres; + unsigned char tiBitPerPixel; + unsigned char tiAttrBits; + }; + + public: + int DLL Init( int Xres, int Yres, unsigned short BitPerPixel ); + int DLL SetImage(unsigned char *img, int width, int height, int bpp); + int DLL Destroy(); + int DLL ConvertTo32( void ); + int DLL ConvertTo24( void ); + int DLL ConvertTo16( void ); + int DLL Convert8To17( int transindex ); + int DLL Convert32To17( void ); + int SaveBmp(char *szFilename); + int LoadBmp(char *szFilename); + int SaveTga(char *szFilename, bool bCompressed ); + int LoadTga(char *szFilename); + int DLL Load(char *szFilename); + int DLL Save(char *szFilename); + + private: + void Output( char * pcData, int nSize ); + void Output( char c ); + void Output( void ); + unsigned char Input( void ); + + public: + int m_Xres, m_Yres; + unsigned short m_BitPerPixel; + unsigned short m_BytePerPixel; + unsigned char * m_pBitmap; + _BGR m_Pal[256]; + + private: + int m_NumPixel; + FILE * f; + int m_nCount; + char m_cBuf[32768]; +}; + +#pragma pack(8) diff --git a/src/gl/hqnx/gl_hqresize.cpp b/src/gl/hqnx/gl_hqresize.cpp new file mode 100644 index 000000000..2aa8f2085 --- /dev/null +++ b/src/gl/hqnx/gl_hqresize.cpp @@ -0,0 +1,280 @@ +/* +** gl_hqresize.cpp +** Contains high quality upsampling functions. +** So far supports Scale2x/3x/4x as described in http://scale2x.sourceforge.net/ +** and Maxim Stepin's hq2x/3x/4x. +** +**--------------------------------------------------------------------------- +** Copyright 2008-2009 Benjamin Berkels +** 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. +** +** 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_pch.h" +#include "gl_hqresize.h" +#include "gl_intern.h" +#include "c_cvars.h" +// [BB] hqnx scaling is only supported with the MS compiler. +#ifdef _MSC_VER +#include "../hqnx/hqnx.h" +#endif + +CUSTOM_CVAR(Int, gl_texture_hqresize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ +#ifdef _MSC_VER + if (self < 0 || self > 6) +#else + if (self < 0 || self > 3) +#endif + self = 0; + FGLTexture::FlushAll(); +} + +CUSTOM_CVAR(Int, gl_texture_hqresize_maxinputsize, 512, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (self > 1024) self = 1024; + FGLTexture::FlushAll(); +} + +CUSTOM_CVAR(Int, gl_texture_hqresize_targets, 7, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + FGLTexture::FlushAll(); +} + +CVAR (Flag, gl_texture_hqresize_textures, gl_texture_hqresize_targets, 1); +CVAR (Flag, gl_texture_hqresize_sprites, gl_texture_hqresize_targets, 2); +CVAR (Flag, gl_texture_hqresize_fonts, gl_texture_hqresize_targets, 4); + + +static void scale2x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, int inHeight ) +{ + const int width = 2* inWidth; + const int height = 2 * inHeight; + + for ( int i = 0; i < inWidth; ++i ) + { + const int iMinus = (i > 0) ? (i-1) : 0; + const int iPlus = (i < inWidth - 1 ) ? (i+1) : i; + for ( int j = 0; j < inHeight; ++j ) + { + const int jMinus = (j > 0) ? (j-1) : 0; + const int jPlus = (j < inHeight - 1 ) ? (j+1) : j; + const uint32 A = inputBuffer[ iMinus +inWidth*jMinus]; + const uint32 B = inputBuffer[ iMinus +inWidth*j ]; + const uint32 C = inputBuffer[ iMinus +inWidth*jPlus]; + const uint32 D = inputBuffer[ i +inWidth*jMinus]; + const uint32 E = inputBuffer[ i +inWidth*j ]; + const uint32 F = inputBuffer[ i +inWidth*jPlus]; + const uint32 G = inputBuffer[ iPlus +inWidth*jMinus]; + const uint32 H = inputBuffer[ iPlus +inWidth*j ]; + const uint32 I = inputBuffer[ iPlus +inWidth*jPlus]; + if (B != H && D != F) { + outputBuffer[2*i + width*2*j ] = D == B ? D : E; + outputBuffer[2*i + width*(2*j+1)] = B == F ? F : E; + outputBuffer[2*i+1 + width*2*j ] = D == H ? D : E; + outputBuffer[2*i+1 + width*(2*j+1)] = H == F ? F : E; + } else { + outputBuffer[2*i + width*2*j ] = E; + outputBuffer[2*i + width*(2*j+1)] = E; + outputBuffer[2*i+1 + width*2*j ] = E; + outputBuffer[2*i+1 + width*(2*j+1)] = E; + } + } + } +} + +static void scale3x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, int inHeight ) +{ + const int width = 3* inWidth; + const int height = 3 * inHeight; + + for ( int i = 0; i < inWidth; ++i ) + { + const int iMinus = (i > 0) ? (i-1) : 0; + const int iPlus = (i < inWidth - 1 ) ? (i+1) : i; + for ( int j = 0; j < inHeight; ++j ) + { + const int jMinus = (j > 0) ? (j-1) : 0; + const int jPlus = (j < inHeight - 1 ) ? (j+1) : j; + const uint32 A = inputBuffer[ iMinus +inWidth*jMinus]; + const uint32 B = inputBuffer[ iMinus +inWidth*j ]; + const uint32 C = inputBuffer[ iMinus +inWidth*jPlus]; + const uint32 D = inputBuffer[ i +inWidth*jMinus]; + const uint32 E = inputBuffer[ i +inWidth*j ]; + const uint32 F = inputBuffer[ i +inWidth*jPlus]; + const uint32 G = inputBuffer[ iPlus +inWidth*jMinus]; + const uint32 H = inputBuffer[ iPlus +inWidth*j ]; + const uint32 I = inputBuffer[ iPlus +inWidth*jPlus]; + if (B != H && D != F) { + outputBuffer[3*i + width*3*j ] = D == B ? D : E; + outputBuffer[3*i + width*(3*j+1)] = (D == B && E != C) || (B == F && E != A) ? B : E; + outputBuffer[3*i + width*(3*j+2)] = B == F ? F : E; + outputBuffer[3*i+1 + width*3*j ] = (D == B && E != G) || (D == H && E != A) ? D : E; + outputBuffer[3*i+1 + width*(3*j+1)] = E; + outputBuffer[3*i+1 + width*(3*j+2)] = (B == F && E != I) || (H == F && E != C) ? F : E; + outputBuffer[3*i+2 + width*3*j ] = D == H ? D : E; + outputBuffer[3*i+2 + width*(3*j+1)] = (D == H && E != I) || (H == F && E != G) ? H : E; + outputBuffer[3*i+2 + width*(3*j+2)] = H == F ? F : E; + } else { + outputBuffer[3*i + width*3*j ] = E; + outputBuffer[3*i + width*(3*j+1)] = E; + outputBuffer[3*i + width*(3*j+2)] = E; + outputBuffer[3*i+1 + width*3*j ] = E; + outputBuffer[3*i+1 + width*(3*j+1)] = E; + outputBuffer[3*i+1 + width*(3*j+2)] = E; + outputBuffer[3*i+2 + width*3*j ] = E; + outputBuffer[3*i+2 + width*(3*j+1)] = E; + outputBuffer[3*i+2 + width*(3*j+2)] = E; + } + } + } +} + +static void scale4x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, int inHeight ) +{ + int width = 2* inWidth; + int height = 2 * inHeight; + uint32 * buffer2x = new uint32[width*height]; + + scale2x ( reinterpret_cast ( inputBuffer ), reinterpret_cast ( buffer2x ), inWidth, inHeight ); + width *= 2; + height *= 2; + scale2x ( reinterpret_cast ( buffer2x ), reinterpret_cast ( outputBuffer ), 2*inWidth, 2*inHeight ); + delete[] buffer2x; +} + + +static unsigned char *scaleNxHelper( void (*scaleNxFunction) ( uint32* , uint32* , int , int), + const int N, + unsigned char *inputBuffer, + const int inWidth, + const int inHeight, + int &outWidth, + int &outHeight ) +{ + outWidth = N * inWidth; + outHeight = N *inHeight; + unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4]; + + scaleNxFunction ( reinterpret_cast ( inputBuffer ), reinterpret_cast ( newBuffer ), inWidth, inHeight ); + delete[] inputBuffer; + return newBuffer; +} + +// [BB] hqnx scaling is only supported with the MS compiler. +#ifdef _MSC_VER +static unsigned char *hqNxHelper( void (*hqNxFunction) ( int*, unsigned char*, int, int, int ), + const int N, + unsigned char *inputBuffer, + const int inWidth, + const int inHeight, + int &outWidth, + int &outHeight ) +{ + outWidth = N * inWidth; + outHeight = N *inHeight; + + CImage cImageIn; + cImageIn.SetImage(inputBuffer, inWidth, inHeight, 32); + cImageIn.Convert32To17(); + + unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4]; + hqNxFunction( reinterpret_cast(cImageIn.m_pBitmap), newBuffer, cImageIn.m_Xres, cImageIn.m_Yres, outWidth*4 ); + delete[] inputBuffer; + return newBuffer; +} +#endif + +//=========================================================================== +// +// [BB] Upsamples the texture in inputBuffer, frees inputBuffer and returns +// the upsampled buffer. +// +//=========================================================================== +unsigned char *gl_CreateUpsampledTextureBuffer ( const FGLTexture *inputGLTexture, unsigned char *inputBuffer, const int inWidth, const int inHeight, int &outWidth, int &outHeight ) +{ + // [BB] Make sure that outWidth and outHeight denote the size of + // the returned buffer even if we don't upsample the input buffer. + outWidth = inWidth; + outHeight = inHeight; + + // [BB] Don't resample if the width or height of the input texture is bigger than gl_texture_hqresize_maxinputsize. + if ( ( inWidth > gl_texture_hqresize_maxinputsize ) || ( inHeight > gl_texture_hqresize_maxinputsize ) ) + return inputBuffer; + + // [BB] The hqnx upsampling (not the scaleN one) destroys partial transparency, don't upsamle textures using it. + if ( inputGLTexture->bIsTransparent == 1 ) + return inputBuffer; + + // [BB] Don't try to upsample textures based off FCanvasTexture. + if ( inputGLTexture->tex->bHasCanvas ) + return inputBuffer; + + // [BB] Don't upsample non-shader handled warped textures. Needs too much memory. + if ( (!(gl.flags & RFL_GLSL) || !gl_warp_shader) && inputGLTexture->tex->bWarped ) + return inputBuffer; + + switch (inputGLTexture->tex->UseType) + { + case FTexture::TEX_Sprite: + case FTexture::TEX_SkinSprite: + if (!(gl_texture_hqresize_targets & 2)) return inputBuffer; + break; + + case FTexture::TEX_FontChar: + if (!(gl_texture_hqresize_targets & 4)) return inputBuffer; + break; + + default: + if (!(gl_texture_hqresize_targets & 1)) return inputBuffer; + break; + } + + if (inputBuffer) + { + int type = gl_texture_hqresize; + switch (type) + { + case 1: + return scaleNxHelper( &scale2x, 2, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + case 2: + return scaleNxHelper( &scale3x, 3, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + case 3: + return scaleNxHelper( &scale4x, 4, inputBuffer, inWidth, inHeight, outWidth, outHeight ); +// [BB] hqnx scaling is only supported with the MS compiler. +#ifdef _MSC_VER + case 4: + return hqNxHelper( &hq2x_32, 2, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + case 5: + return hqNxHelper( &hq3x_32, 3, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + case 6: + return hqNxHelper( &hq4x_32, 4, inputBuffer, inWidth, inHeight, outWidth, outHeight ); +#endif + } + } + return inputBuffer; +} diff --git a/src/gl/hqnx/hq2x.cpp b/src/gl/hqnx/hq2x.cpp new file mode 100644 index 000000000..073fe06b2 --- /dev/null +++ b/src/gl/hqnx/hq2x.cpp @@ -0,0 +1,2987 @@ +//hq2x filter demo program +//---------------------------------------------------------- +//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) +// +//This program is free software; you can redistribute it and/or +//modify it under the terms of the GNU Lesser General Public +//License as published by the Free Software Foundation; either +//version 2.1 of the License, or (at your option) any later version. +// +//This program is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +//Lesser General Public License for more details. +// +//You should have received a copy of the GNU Lesser General Public +//License along with this program; if not, write to the Free Software +//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "hqnx.h" + +extern int LUT16to32[65536*2]; +extern int RGBtoYUV[65536*2]; + +static const __int64 reg_blank = 0; +static const __int64 const3 = 0x0003000300030003; +static const __int64 const5 = 0x0005000500050005; +static const __int64 const6 = 0x0006000600060006; +static const __int64 const14 = 0x000E000E000E000E; +static const __int64 treshold = 0x0000000000300706; + +inline void Interp1(unsigned char * pc, int c1, int c2) +{ + //*((int*)pc) = (c1*3+c2)/4; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + pmullw mm1, const3 + paddw mm1, mm2 + psrlw mm1, 2 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +inline void Interp2(unsigned char * pc, int c1, int c2, int c3) +{ + //*((int*)pc) = (c1*2+c2+c3) >> 2; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + movd mm3, c3 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + punpcklbw mm3, reg_blank + psllw mm1, 1 + paddw mm1, mm2 + paddw mm1, mm3 + psrlw mm1, 2 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +inline void Interp5(unsigned char * pc, int c1, int c2) +{ + //*((int*)pc) = (c1+c2)/2; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + paddw mm1, mm2 + psrlw mm1, 1 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +inline void Interp6(unsigned char * pc, int c1, int c2, int c3) +{ + //*((int*)pc) = (c1*5+c2*2+c3)/8; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + movd mm3, c3 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + punpcklbw mm3, reg_blank + pmullw mm1, const5 + psllw mm2, 1 + paddw mm1, mm3 + paddw mm1, mm2 + psrlw mm1, 3 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +inline void Interp7(unsigned char * pc, int c1, int c2, int c3) +{ + //*((int*)pc) = (c1*6+c2+c3)/8; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + movd mm3, c3 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + punpcklbw mm3, reg_blank + pmullw mm1, const6 + paddw mm2, mm3 + paddw mm1, mm2 + psrlw mm1, 3 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +inline void Interp9(unsigned char * pc, int c1, int c2, int c3) +{ + //*((int*)pc) = (c1*2+(c2+c3)*3)/8; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + movd mm3, c3 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + punpcklbw mm3, reg_blank + psllw mm1, 1 + paddw mm2, mm3 + pmullw mm2, const3 + paddw mm1, mm2 + psrlw mm1, 3 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +inline void Interp10(unsigned char * pc, int c1, int c2, int c3) +{ + //*((int*)pc) = (c1*14+c2+c3)/16; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + movd mm3, c3 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + punpcklbw mm3, reg_blank + pmullw mm1, const14 + paddw mm2, mm3 + paddw mm1, mm2 + psrlw mm1, 4 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +#define PIXEL00_0 *((int*)(pOut)) = c[5]; +#define PIXEL00_10 Interp1(pOut, c[5], c[1]); +#define PIXEL00_11 Interp1(pOut, c[5], c[4]); +#define PIXEL00_12 Interp1(pOut, c[5], c[2]); +#define PIXEL00_20 Interp2(pOut, c[5], c[4], c[2]); +#define PIXEL00_21 Interp2(pOut, c[5], c[1], c[2]); +#define PIXEL00_22 Interp2(pOut, c[5], c[1], c[4]); +#define PIXEL00_60 Interp6(pOut, c[5], c[2], c[4]); +#define PIXEL00_61 Interp6(pOut, c[5], c[4], c[2]); +#define PIXEL00_70 Interp7(pOut, c[5], c[4], c[2]); +#define PIXEL00_90 Interp9(pOut, c[5], c[4], c[2]); +#define PIXEL00_100 Interp10(pOut, c[5], c[4], c[2]); +#define PIXEL01_0 *((int*)(pOut+4)) = c[5]; +#define PIXEL01_10 Interp1(pOut+4, c[5], c[3]); +#define PIXEL01_11 Interp1(pOut+4, c[5], c[2]); +#define PIXEL01_12 Interp1(pOut+4, c[5], c[6]); +#define PIXEL01_20 Interp2(pOut+4, c[5], c[2], c[6]); +#define PIXEL01_21 Interp2(pOut+4, c[5], c[3], c[6]); +#define PIXEL01_22 Interp2(pOut+4, c[5], c[3], c[2]); +#define PIXEL01_60 Interp6(pOut+4, c[5], c[6], c[2]); +#define PIXEL01_61 Interp6(pOut+4, c[5], c[2], c[6]); +#define PIXEL01_70 Interp7(pOut+4, c[5], c[2], c[6]); +#define PIXEL01_90 Interp9(pOut+4, c[5], c[2], c[6]); +#define PIXEL01_100 Interp10(pOut+4, c[5], c[2], c[6]); +#define PIXEL10_0 *((int*)(pOut+BpL)) = c[5]; +#define PIXEL10_10 Interp1(pOut+BpL, c[5], c[7]); +#define PIXEL10_11 Interp1(pOut+BpL, c[5], c[8]); +#define PIXEL10_12 Interp1(pOut+BpL, c[5], c[4]); +#define PIXEL10_20 Interp2(pOut+BpL, c[5], c[8], c[4]); +#define PIXEL10_21 Interp2(pOut+BpL, c[5], c[7], c[4]); +#define PIXEL10_22 Interp2(pOut+BpL, c[5], c[7], c[8]); +#define PIXEL10_60 Interp6(pOut+BpL, c[5], c[4], c[8]); +#define PIXEL10_61 Interp6(pOut+BpL, c[5], c[8], c[4]); +#define PIXEL10_70 Interp7(pOut+BpL, c[5], c[8], c[4]); +#define PIXEL10_90 Interp9(pOut+BpL, c[5], c[8], c[4]); +#define PIXEL10_100 Interp10(pOut+BpL, c[5], c[8], c[4]); +#define PIXEL11_0 *((int*)(pOut+BpL+4)) = c[5]; +#define PIXEL11_10 Interp1(pOut+BpL+4, c[5], c[9]); +#define PIXEL11_11 Interp1(pOut+BpL+4, c[5], c[6]); +#define PIXEL11_12 Interp1(pOut+BpL+4, c[5], c[8]); +#define PIXEL11_20 Interp2(pOut+BpL+4, c[5], c[6], c[8]); +#define PIXEL11_21 Interp2(pOut+BpL+4, c[5], c[9], c[8]); +#define PIXEL11_22 Interp2(pOut+BpL+4, c[5], c[9], c[6]); +#define PIXEL11_60 Interp6(pOut+BpL+4, c[5], c[8], c[6]); +#define PIXEL11_61 Interp6(pOut+BpL+4, c[5], c[6], c[8]); +#define PIXEL11_70 Interp7(pOut+BpL+4, c[5], c[6], c[8]); +#define PIXEL11_90 Interp9(pOut+BpL+4, c[5], c[6], c[8]); +#define PIXEL11_100 Interp10(pOut+BpL+4, c[5], c[6], c[8]); + + +int Diff(unsigned int w5, unsigned int w1); + +void DLL hq2x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL ) +{ + int i, j, k; + int w[10]; + unsigned int c[10]; + + // +----+----+----+ + // | | | | + // | w1 | w2 | w3 | + // +----+----+----+ + // | | | | + // | w4 | w5 | w6 | + // +----+----+----+ + // | | | | + // | w7 | w8 | w9 | + // +----+----+----+ + + for (j=0; j0) + { + w[1] = *(pIn - Xres - 1); + } + else + { + w[1] = 0; + } + + w[2] = *(pIn - Xres); + + if (i0) + { + w[4] = *(pIn - 1); + } + else + { + w[4] = 0; + } + + w[5] = *(pIn); + if (i0) + { + w[7] = *(pIn + Xres - 1); + } + else + { + w[7] = 0; + } + + w[8] = *(pIn + Xres); + if (i0) w[1] = *(pIn - Xres - 1); else w[1] = 0; + w[2] = *(pIn - Xres); + if (i0) w[4] = *(pIn - 1); else w[4] = 0; + w[5] = *(pIn); + if (i0) w[7] = *(pIn + Xres - 1); else w[7] = 0; + w[8] = *(pIn + Xres); + if (i +#include +#include +#include +#include "hqnx.h" + +int LUT16to32[65536*2]; +int RGBtoYUV[65536*2]; + +static const __int64 reg_blank = 0; +static const __int64 const3 = 0x0003000300030003; +static const __int64 const5 = 0x0005000500050005; +static const __int64 const6 = 0x0006000600060006; +static const __int64 const7 = 0x0007000700070007; +static const __int64 treshold = 0x0000000000300706; + + +inline void Interp1(unsigned char * pc, int c1, int c2) +{ + //*((int*)pc) = (c1*3+c2)/4; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + pmullw mm1, const3 + paddw mm1, mm2 + psrlw mm1, 2 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +inline void Interp2(unsigned char * pc, int c1, int c2, int c3) +{ +// *((int*)pc) = (c1*2+c2+c3)/4; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + movd mm3, c3 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + punpcklbw mm3, reg_blank + psllw mm1, 1 + paddw mm1, mm2 + paddw mm1, mm3 + psrlw mm1, 2 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +inline void Interp3(unsigned char * pc, int c1, int c2) +{ + //*((int*)pc) = (c1*7+c2)/8; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + pmullw mm1, const7 + paddw mm1, mm2 + psrlw mm1, 3 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +inline void Interp5(unsigned char * pc, int c1, int c2) +{ + //*((int*)pc) = (c1+c2)/2; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + paddw mm1, mm2 + psrlw mm1, 1 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +inline void Interp6(unsigned char * pc, int c1, int c2, int c3) +{ + //*((int*)pc) = (c1*5+c2*2+c3)/8; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + movd mm3, c3 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + punpcklbw mm3, reg_blank + pmullw mm1, const5 + psllw mm2, 1 + paddw mm1, mm3 + paddw mm1, mm2 + psrlw mm1, 3 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +inline void Interp7(unsigned char * pc, int c1, int c2, int c3) +{ + //*((int*)pc) = (c1*6+c2+c3)/8; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + movd mm3, c3 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + punpcklbw mm3, reg_blank + pmullw mm1, const6 + paddw mm2, mm3 + paddw mm1, mm2 + psrlw mm1, 3 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +inline void Interp8(unsigned char * pc, int c1, int c2) +{ + //*((int*)pc) = (c1*5+c2*3)/8; + __asm + { + mov eax, pc + movd mm1, c1 + movd mm2, c2 + punpcklbw mm1, reg_blank + punpcklbw mm2, reg_blank + pmullw mm1, const5 + pmullw mm2, const3 + paddw mm1, mm2 + psrlw mm1, 3 + packuswb mm1, reg_blank + movd [eax], mm1 + } +} + +#define PIXEL00_0 *((int*)(pOut)) = c[5]; +#define PIXEL00_11 Interp1(pOut, c[5], c[4]); +#define PIXEL00_12 Interp1(pOut, c[5], c[2]); +#define PIXEL00_20 Interp2(pOut, c[5], c[2], c[4]); +#define PIXEL00_50 Interp5(pOut, c[2], c[4]); +#define PIXEL00_80 Interp8(pOut, c[5], c[1]); +#define PIXEL00_81 Interp8(pOut, c[5], c[4]); +#define PIXEL00_82 Interp8(pOut, c[5], c[2]); +#define PIXEL01_0 *((int*)(pOut+4)) = c[5]; +#define PIXEL01_10 Interp1(pOut+4, c[5], c[1]); +#define PIXEL01_12 Interp1(pOut+4, c[5], c[2]); +#define PIXEL01_14 Interp1(pOut+4, c[2], c[5]); +#define PIXEL01_21 Interp2(pOut+4, c[2], c[5], c[4]); +#define PIXEL01_31 Interp3(pOut+4, c[5], c[4]); +#define PIXEL01_50 Interp5(pOut+4, c[2], c[5]); +#define PIXEL01_60 Interp6(pOut+4, c[5], c[2], c[4]); +#define PIXEL01_61 Interp6(pOut+4, c[5], c[2], c[1]); +#define PIXEL01_82 Interp8(pOut+4, c[5], c[2]); +#define PIXEL01_83 Interp8(pOut+4, c[2], c[4]); +#define PIXEL02_0 *((int*)(pOut+8)) = c[5]; +#define PIXEL02_10 Interp1(pOut+8, c[5], c[3]); +#define PIXEL02_11 Interp1(pOut+8, c[5], c[2]); +#define PIXEL02_13 Interp1(pOut+8, c[2], c[5]); +#define PIXEL02_21 Interp2(pOut+8, c[2], c[5], c[6]); +#define PIXEL02_32 Interp3(pOut+8, c[5], c[6]); +#define PIXEL02_50 Interp5(pOut+8, c[2], c[5]); +#define PIXEL02_60 Interp6(pOut+8, c[5], c[2], c[6]); +#define PIXEL02_61 Interp6(pOut+8, c[5], c[2], c[3]); +#define PIXEL02_81 Interp8(pOut+8, c[5], c[2]); +#define PIXEL02_83 Interp8(pOut+8, c[2], c[6]); +#define PIXEL03_0 *((int*)(pOut+12)) = c[5]; +#define PIXEL03_11 Interp1(pOut+12, c[5], c[2]); +#define PIXEL03_12 Interp1(pOut+12, c[5], c[6]); +#define PIXEL03_20 Interp2(pOut+12, c[5], c[2], c[6]); +#define PIXEL03_50 Interp5(pOut+12, c[2], c[6]); +#define PIXEL03_80 Interp8(pOut+12, c[5], c[3]); +#define PIXEL03_81 Interp8(pOut+12, c[5], c[2]); +#define PIXEL03_82 Interp8(pOut+12, c[5], c[6]); +#define PIXEL10_0 *((int*)(pOut+BpL)) = c[5]; +#define PIXEL10_10 Interp1(pOut+BpL, c[5], c[1]); +#define PIXEL10_11 Interp1(pOut+BpL, c[5], c[4]); +#define PIXEL10_13 Interp1(pOut+BpL, c[4], c[5]); +#define PIXEL10_21 Interp2(pOut+BpL, c[4], c[5], c[2]); +#define PIXEL10_32 Interp3(pOut+BpL, c[5], c[2]); +#define PIXEL10_50 Interp5(pOut+BpL, c[4], c[5]); +#define PIXEL10_60 Interp6(pOut+BpL, c[5], c[4], c[2]); +#define PIXEL10_61 Interp6(pOut+BpL, c[5], c[4], c[1]); +#define PIXEL10_81 Interp8(pOut+BpL, c[5], c[4]); +#define PIXEL10_83 Interp8(pOut+BpL, c[4], c[2]); +#define PIXEL11_0 *((int*)(pOut+BpL+4)) = c[5]; +#define PIXEL11_30 Interp3(pOut+BpL+4, c[5], c[1]); +#define PIXEL11_31 Interp3(pOut+BpL+4, c[5], c[4]); +#define PIXEL11_32 Interp3(pOut+BpL+4, c[5], c[2]); +#define PIXEL11_70 Interp7(pOut+BpL+4, c[5], c[4], c[2]); +#define PIXEL12_0 *((int*)(pOut+BpL+8)) = c[5]; +#define PIXEL12_30 Interp3(pOut+BpL+8, c[5], c[3]); +#define PIXEL12_31 Interp3(pOut+BpL+8, c[5], c[2]); +#define PIXEL12_32 Interp3(pOut+BpL+8, c[5], c[6]); +#define PIXEL12_70 Interp7(pOut+BpL+8, c[5], c[6], c[2]); +#define PIXEL13_0 *((int*)(pOut+BpL+12)) = c[5]; +#define PIXEL13_10 Interp1(pOut+BpL+12, c[5], c[3]); +#define PIXEL13_12 Interp1(pOut+BpL+12, c[5], c[6]); +#define PIXEL13_14 Interp1(pOut+BpL+12, c[6], c[5]); +#define PIXEL13_21 Interp2(pOut+BpL+12, c[6], c[5], c[2]); +#define PIXEL13_31 Interp3(pOut+BpL+12, c[5], c[2]); +#define PIXEL13_50 Interp5(pOut+BpL+12, c[6], c[5]); +#define PIXEL13_60 Interp6(pOut+BpL+12, c[5], c[6], c[2]); +#define PIXEL13_61 Interp6(pOut+BpL+12, c[5], c[6], c[3]); +#define PIXEL13_82 Interp8(pOut+BpL+12, c[5], c[6]); +#define PIXEL13_83 Interp8(pOut+BpL+12, c[6], c[2]); +#define PIXEL20_0 *((int*)(pOut+BpL+BpL)) = c[5]; +#define PIXEL20_10 Interp1(pOut+BpL+BpL, c[5], c[7]); +#define PIXEL20_12 Interp1(pOut+BpL+BpL, c[5], c[4]); +#define PIXEL20_14 Interp1(pOut+BpL+BpL, c[4], c[5]); +#define PIXEL20_21 Interp2(pOut+BpL+BpL, c[4], c[5], c[8]); +#define PIXEL20_31 Interp3(pOut+BpL+BpL, c[5], c[8]); +#define PIXEL20_50 Interp5(pOut+BpL+BpL, c[4], c[5]); +#define PIXEL20_60 Interp6(pOut+BpL+BpL, c[5], c[4], c[8]); +#define PIXEL20_61 Interp6(pOut+BpL+BpL, c[5], c[4], c[7]); +#define PIXEL20_82 Interp8(pOut+BpL+BpL, c[5], c[4]); +#define PIXEL20_83 Interp8(pOut+BpL+BpL, c[4], c[8]); +#define PIXEL21_0 *((int*)(pOut+BpL+BpL+4)) = c[5]; +#define PIXEL21_30 Interp3(pOut+BpL+BpL+4, c[5], c[7]); +#define PIXEL21_31 Interp3(pOut+BpL+BpL+4, c[5], c[8]); +#define PIXEL21_32 Interp3(pOut+BpL+BpL+4, c[5], c[4]); +#define PIXEL21_70 Interp7(pOut+BpL+BpL+4, c[5], c[4], c[8]); +#define PIXEL22_0 *((int*)(pOut+BpL+BpL+8)) = c[5]; +#define PIXEL22_30 Interp3(pOut+BpL+BpL+8, c[5], c[9]); +#define PIXEL22_31 Interp3(pOut+BpL+BpL+8, c[5], c[6]); +#define PIXEL22_32 Interp3(pOut+BpL+BpL+8, c[5], c[8]); +#define PIXEL22_70 Interp7(pOut+BpL+BpL+8, c[5], c[6], c[8]); +#define PIXEL23_0 *((int*)(pOut+BpL+BpL+12)) = c[5]; +#define PIXEL23_10 Interp1(pOut+BpL+BpL+12, c[5], c[9]); +#define PIXEL23_11 Interp1(pOut+BpL+BpL+12, c[5], c[6]); +#define PIXEL23_13 Interp1(pOut+BpL+BpL+12, c[6], c[5]); +#define PIXEL23_21 Interp2(pOut+BpL+BpL+12, c[6], c[5], c[8]); +#define PIXEL23_32 Interp3(pOut+BpL+BpL+12, c[5], c[8]); +#define PIXEL23_50 Interp5(pOut+BpL+BpL+12, c[6], c[5]); +#define PIXEL23_60 Interp6(pOut+BpL+BpL+12, c[5], c[6], c[8]); +#define PIXEL23_61 Interp6(pOut+BpL+BpL+12, c[5], c[6], c[9]); +#define PIXEL23_81 Interp8(pOut+BpL+BpL+12, c[5], c[6]); +#define PIXEL23_83 Interp8(pOut+BpL+BpL+12, c[6], c[8]); +#define PIXEL30_0 *((int*)(pOut+BpL+BpL+BpL)) = c[5]; +#define PIXEL30_11 Interp1(pOut+BpL+BpL+BpL, c[5], c[8]); +#define PIXEL30_12 Interp1(pOut+BpL+BpL+BpL, c[5], c[4]); +#define PIXEL30_20 Interp2(pOut+BpL+BpL+BpL, c[5], c[8], c[4]); +#define PIXEL30_50 Interp5(pOut+BpL+BpL+BpL, c[8], c[4]); +#define PIXEL30_80 Interp8(pOut+BpL+BpL+BpL, c[5], c[7]); +#define PIXEL30_81 Interp8(pOut+BpL+BpL+BpL, c[5], c[8]); +#define PIXEL30_82 Interp8(pOut+BpL+BpL+BpL, c[5], c[4]); +#define PIXEL31_0 *((int*)(pOut+BpL+BpL+BpL+4)) = c[5]; +#define PIXEL31_10 Interp1(pOut+BpL+BpL+BpL+4, c[5], c[7]); +#define PIXEL31_11 Interp1(pOut+BpL+BpL+BpL+4, c[5], c[8]); +#define PIXEL31_13 Interp1(pOut+BpL+BpL+BpL+4, c[8], c[5]); +#define PIXEL31_21 Interp2(pOut+BpL+BpL+BpL+4, c[8], c[5], c[4]); +#define PIXEL31_32 Interp3(pOut+BpL+BpL+BpL+4, c[5], c[4]); +#define PIXEL31_50 Interp5(pOut+BpL+BpL+BpL+4, c[8], c[5]); +#define PIXEL31_60 Interp6(pOut+BpL+BpL+BpL+4, c[5], c[8], c[4]); +#define PIXEL31_61 Interp6(pOut+BpL+BpL+BpL+4, c[5], c[8], c[7]); +#define PIXEL31_81 Interp8(pOut+BpL+BpL+BpL+4, c[5], c[8]); +#define PIXEL31_83 Interp8(pOut+BpL+BpL+BpL+4, c[8], c[4]); +#define PIXEL32_0 *((int*)(pOut+BpL+BpL+BpL+8)) = c[5]; +#define PIXEL32_10 Interp1(pOut+BpL+BpL+BpL+8, c[5], c[9]); +#define PIXEL32_12 Interp1(pOut+BpL+BpL+BpL+8, c[5], c[8]); +#define PIXEL32_14 Interp1(pOut+BpL+BpL+BpL+8, c[8], c[5]); +#define PIXEL32_21 Interp2(pOut+BpL+BpL+BpL+8, c[8], c[5], c[6]); +#define PIXEL32_31 Interp3(pOut+BpL+BpL+BpL+8, c[5], c[6]); +#define PIXEL32_50 Interp5(pOut+BpL+BpL+BpL+8, c[8], c[5]); +#define PIXEL32_60 Interp6(pOut+BpL+BpL+BpL+8, c[5], c[8], c[6]); +#define PIXEL32_61 Interp6(pOut+BpL+BpL+BpL+8, c[5], c[8], c[9]); +#define PIXEL32_82 Interp8(pOut+BpL+BpL+BpL+8, c[5], c[8]); +#define PIXEL32_83 Interp8(pOut+BpL+BpL+BpL+8, c[8], c[6]); +#define PIXEL33_0 *((int*)(pOut+BpL+BpL+BpL+12)) = c[5]; +#define PIXEL33_11 Interp1(pOut+BpL+BpL+BpL+12, c[5], c[6]); +#define PIXEL33_12 Interp1(pOut+BpL+BpL+BpL+12, c[5], c[8]); +#define PIXEL33_20 Interp2(pOut+BpL+BpL+BpL+12, c[5], c[8], c[6]); +#define PIXEL33_50 Interp5(pOut+BpL+BpL+BpL+12, c[8], c[6]); +#define PIXEL33_80 Interp8(pOut+BpL+BpL+BpL+12, c[5], c[9]); +#define PIXEL33_81 Interp8(pOut+BpL+BpL+BpL+12, c[5], c[6]); +#define PIXEL33_82 Interp8(pOut+BpL+BpL+BpL+12, c[5], c[8]); + + +#pragma warning(disable: 4035) + +int Diff(unsigned int w5, unsigned int w1) +{ + __asm + { + xor eax,eax + mov ebx,w5 + mov edx,w1 + cmp ebx,edx + je FIN + mov ecx,offset RGBtoYUV + movd mm1,[ecx + ebx*4] + movq mm5,mm1 + movd mm2,[ecx + edx*4] + psubusb mm1,mm2 + psubusb mm2,mm5 + por mm1,mm2 + psubusb mm1,treshold + movd eax,mm1 +FIN: + } +} +// returns result in eax register + +#pragma warning(default: 4035) + +void DLL hq4x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL ) +{ + int i, j, k; + int w[10]; + int c[10]; + + // +----+----+----+ + // | | | | + // | w1 | w2 | w3 | + // +----+----+----+ + // | | | | + // | w4 | w5 | w6 | + // +----+----+----+ + // | | | | + // | w7 | w8 | w9 | + // +----+----+----+ + + for (j = 0; j < Yres; j++) + { + for (i = 0; i < Xres; i++) + { + if (j == 0) + { + w[1] = 0; + w[2] = 0; + w[3] = 0; + } + else + { + if (i > 0) + w[1] = *(pIn - Xres - 1); + else + w[1] = 0; + + w[2] = *(pIn - Xres); + + if (i < Xres - 1) + w[3] = *(pIn - Xres + 1); + else + w[3] = 0; + } + + if (i > 0) + w[4] = *(pIn - 1); + else + w[4] = 0; + + w[5] = *(pIn); + + if (i < Xres - 1) + w[6] = *(pIn + 1); + else + w[6] = 0; + + if (j == Yres - 1) + { + w[7] = 0; + w[8] = 0; + w[9] = 0; + } + else + { + if (i > 0) + w[7] = *(pIn + Xres - 1); + else + w[7] = 0; + + w[8] = *(pIn + Xres); + + if (i < Xres-1) + w[9] = *(pIn + Xres + 1); + else + w[9] = 0; + } + + int pattern = 0; + + if ( Diff(w[5],w[1]) ) pattern |= 0x0001; + if ( Diff(w[5],w[2]) ) pattern |= 0x0002; + if ( Diff(w[5],w[3]) ) pattern |= 0x0004; + if ( Diff(w[5],w[4]) ) pattern |= 0x0008; + if ( Diff(w[5],w[6]) ) pattern |= 0x0010; + if ( Diff(w[5],w[7]) ) pattern |= 0x0020; + if ( Diff(w[5],w[8]) ) pattern |= 0x0040; + if ( Diff(w[5],w[9]) ) pattern |= 0x0080; + + for (k=1; k<=9; k++) + c[k] = LUT16to32[w[k]]; + + switch (pattern) + { + case 0: + case 1: + case 4: + case 32: + case 128: + case 5: + case 132: + case 160: + case 33: + case 129: + case 36: + case 133: + case 164: + case 161: + case 37: + case 165: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + break; + } + case 2: + case 34: + case 130: + case 162: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + break; + } + case 16: + case 17: + case 48: + case 49: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + break; + } + case 64: + case 65: + case 68: + case 69: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 8: + case 12: + case 136: + case 140: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + break; + } + case 3: + case 35: + case 131: + case 163: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + break; + } + case 6: + case 38: + case 134: + case 166: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + break; + } + case 20: + case 21: + case 52: + case 53: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + break; + } + case 144: + case 145: + case 176: + case 177: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + break; + } + case 192: + case 193: + case 196: + case 197: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } + case 96: + case 97: + case 100: + case 101: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } + case 40: + case 44: + case 168: + case 172: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } + case 9: + case 13: + case 137: + case 141: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + break; + } + case 18: + case 50: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL12_0 + PIXEL13_50 + } + PIXEL10_61 + PIXEL11_30 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + break; + } + case 80: + case 81: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 72: + case 76: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_50 + PIXEL21_0 + PIXEL30_50 + PIXEL31_50 + } + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } + case 10: + case 138: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + PIXEL11_0 + } + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + break; + } + case 66: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 24: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } + case 7: + case 39: + case 135: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + break; + } + case 148: + case 149: + case 180: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + break; + } + case 224: + case 228: + case 225: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + break; + } + case 41: + case 169: + case 45: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } + case 22: + case 54: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + break; + } + case 208: + case 209: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 104: + case 108: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } + case 11: + case 139: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + break; + } + case 19: + case 51: + { + if (Diff(w[2], w[6])) + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL00_12 + PIXEL01_14 + PIXEL02_83 + PIXEL03_50 + PIXEL12_70 + PIXEL13_21 + } + PIXEL10_81 + PIXEL11_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + break; + } + case 146: + case 178: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + PIXEL23_32 + PIXEL33_82 + } + else + { + PIXEL02_21 + PIXEL03_50 + PIXEL12_70 + PIXEL13_83 + PIXEL23_13 + PIXEL33_11 + } + PIXEL10_61 + PIXEL11_30 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + break; + } + case 84: + case 85: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + if (Diff(w[6], w[8])) + { + PIXEL03_81 + PIXEL13_31 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL03_12 + PIXEL13_14 + PIXEL22_70 + PIXEL23_83 + PIXEL32_21 + PIXEL33_50 + } + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL20_61 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + break; + } + case 112: + case 113: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_82 + PIXEL21_32 + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_70 + PIXEL23_21 + PIXEL30_11 + PIXEL31_13 + PIXEL32_83 + PIXEL33_50 + } + break; + } + case 200: + case 204: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + } + else + { + PIXEL20_21 + PIXEL21_70 + PIXEL30_50 + PIXEL31_83 + PIXEL32_14 + PIXEL33_12 + } + PIXEL22_31 + PIXEL23_81 + break; + } + case 73: + case 77: + { + if (Diff(w[8], w[4])) + { + PIXEL00_82 + PIXEL10_32 + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL00_11 + PIXEL10_13 + PIXEL20_83 + PIXEL21_70 + PIXEL30_50 + PIXEL31_21 + } + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } + case 42: + case 170: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + PIXEL20_31 + PIXEL30_81 + } + else + { + PIXEL00_50 + PIXEL01_21 + PIXEL10_83 + PIXEL11_70 + PIXEL20_14 + PIXEL30_12 + } + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } + case 14: + case 142: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_50 + PIXEL01_83 + PIXEL02_13 + PIXEL03_11 + PIXEL10_21 + PIXEL11_70 + } + PIXEL12_32 + PIXEL13_82 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + break; + } + case 67: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 70: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 28: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } + case 152: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } + case 194: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } + case 98: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } + case 56: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } + case 25: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } + case 26: + case 31: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL11_0 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } + case 82: + case 214: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 88: + case 248: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + break; + } + case 74: + case 107: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } + case 27: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } + case 86: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 216: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 106: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } + case 30: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } + case 210: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 120: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 75: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 29: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_61 + PIXEL32_61 + PIXEL33_80 + break; + } + case 198: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } + case 184: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_61 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } + case 99: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } + case 57: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } + case 71: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_30 + PIXEL23_61 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 156: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } + case 226: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_61 + PIXEL11_30 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + break; + } + case 60: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } + case 195: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } + case 102: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } + case 153: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } + case 58: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } + case 83: + { + PIXEL00_81 + PIXEL01_31 + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL10_81 + PIXEL11_31 + PIXEL20_61 + PIXEL21_30 + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 92: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } + case 202: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + break; + } + case 78: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + PIXEL02_32 + PIXEL03_82 + PIXEL12_32 + PIXEL13_82 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } + case 154: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } + case 114: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL10_61 + PIXEL11_30 + PIXEL20_82 + PIXEL21_32 + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + PIXEL30_82 + PIXEL31_32 + break; + } + case 89: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } + case 90: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } + case 55: + case 23: + { + if (Diff(w[2], w[6])) + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + PIXEL03_0 + PIXEL12_0 + PIXEL13_0 + } + else + { + PIXEL00_12 + PIXEL01_14 + PIXEL02_83 + PIXEL03_50 + PIXEL12_70 + PIXEL13_21 + } + PIXEL10_81 + PIXEL11_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_30 + PIXEL23_10 + PIXEL30_20 + PIXEL31_60 + PIXEL32_61 + PIXEL33_80 + break; + } + case 182: + case 150: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL12_0 + PIXEL13_0 + PIXEL23_32 + PIXEL33_82 + } + else + { + PIXEL02_21 + PIXEL03_50 + PIXEL12_70 + PIXEL13_83 + PIXEL23_13 + PIXEL33_11 + } + PIXEL10_61 + PIXEL11_30 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + break; + } + case 213: + case 212: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + if (Diff(w[6], w[8])) + { + PIXEL03_81 + PIXEL13_31 + PIXEL22_0 + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL03_12 + PIXEL13_14 + PIXEL22_70 + PIXEL23_83 + PIXEL32_21 + PIXEL33_50 + } + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL20_61 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + break; + } + case 241: + case 240: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_61 + PIXEL03_80 + PIXEL10_60 + PIXEL11_70 + PIXEL12_30 + PIXEL13_10 + PIXEL20_82 + PIXEL21_32 + if (Diff(w[6], w[8])) + { + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL22_70 + PIXEL23_21 + PIXEL30_11 + PIXEL31_13 + PIXEL32_83 + PIXEL33_50 + } + break; + } + case 236: + case 232: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_60 + PIXEL03_20 + PIXEL10_10 + PIXEL11_30 + PIXEL12_70 + PIXEL13_60 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL21_0 + PIXEL30_0 + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + } + else + { + PIXEL20_21 + PIXEL21_70 + PIXEL30_50 + PIXEL31_83 + PIXEL32_14 + PIXEL33_12 + } + PIXEL22_31 + PIXEL23_81 + break; + } + case 109: + case 105: + { + if (Diff(w[8], w[4])) + { + PIXEL00_82 + PIXEL10_32 + PIXEL20_0 + PIXEL21_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL00_11 + PIXEL10_13 + PIXEL20_83 + PIXEL21_70 + PIXEL30_50 + PIXEL31_21 + } + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } + case 171: + case 43: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + PIXEL11_0 + PIXEL20_31 + PIXEL30_81 + } + else + { + PIXEL00_50 + PIXEL01_21 + PIXEL10_83 + PIXEL11_70 + PIXEL20_14 + PIXEL30_12 + } + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } + case 143: + case 15: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + } + else + { + PIXEL00_50 + PIXEL01_83 + PIXEL02_13 + PIXEL03_11 + PIXEL10_21 + PIXEL11_70 + } + PIXEL12_32 + PIXEL13_82 + PIXEL20_10 + PIXEL21_30 + PIXEL22_70 + PIXEL23_60 + PIXEL30_80 + PIXEL31_61 + PIXEL32_60 + PIXEL33_20 + break; + } + case 124: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 203: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_10 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } + case 62: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } + case 211: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_10 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 118: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_10 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } + case 217: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 110: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_10 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } + case 155: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } + case 188: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } + case 185: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } + case 61: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } + case 157: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } + case 103: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_61 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } + case 227: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_61 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + break; + } + case 230: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_61 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + break; + } + case 199: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_61 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } + case 220: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + break; + } + case 158: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } + case 234: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_61 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + break; + } + case 242: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL10_61 + PIXEL11_30 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_82 + PIXEL31_32 + break; + } + case 59: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL11_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } + case 121: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } + case 87: + { + PIXEL00_81 + PIXEL01_31 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL20_61 + PIXEL21_30 + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 79: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_32 + PIXEL03_82 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } + case 122: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } + case 94: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL12_0 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } + case 218: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + break; + } + case 91: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL11_0 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } + case 229: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_60 + PIXEL03_20 + PIXEL10_60 + PIXEL11_70 + PIXEL12_70 + PIXEL13_60 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + break; + } + case 167: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_60 + PIXEL21_70 + PIXEL22_70 + PIXEL23_60 + PIXEL30_20 + PIXEL31_60 + PIXEL32_60 + PIXEL33_20 + break; + } + case 173: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } + case 181: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + break; + } + case 186: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } + case 115: + { + PIXEL00_81 + PIXEL01_31 + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL10_81 + PIXEL11_31 + PIXEL20_82 + PIXEL21_32 + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + PIXEL30_82 + PIXEL31_32 + break; + } + case 93: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + break; + } + case 206: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + PIXEL02_32 + PIXEL03_82 + PIXEL12_32 + PIXEL13_82 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + break; + } + case 205: + case 201: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + if (Diff(w[8], w[4])) + { + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + } + else + { + PIXEL20_12 + PIXEL21_0 + PIXEL30_20 + PIXEL31_11 + } + PIXEL22_31 + PIXEL23_81 + PIXEL32_31 + PIXEL33_81 + break; + } + case 174: + case 46: + { + if (Diff(w[4], w[2])) + { + PIXEL00_80 + PIXEL01_10 + PIXEL10_10 + PIXEL11_30 + } + else + { + PIXEL00_20 + PIXEL01_12 + PIXEL10_11 + PIXEL11_0 + } + PIXEL02_32 + PIXEL03_82 + PIXEL12_32 + PIXEL13_82 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } + case 179: + case 147: + { + PIXEL00_81 + PIXEL01_31 + if (Diff(w[2], w[6])) + { + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + } + else + { + PIXEL02_11 + PIXEL03_20 + PIXEL12_0 + PIXEL13_12 + } + PIXEL10_81 + PIXEL11_31 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + break; + } + case 117: + case 116: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_82 + PIXEL21_32 + if (Diff(w[6], w[8])) + { + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + } + else + { + PIXEL22_0 + PIXEL23_11 + PIXEL32_12 + PIXEL33_20 + } + PIXEL30_82 + PIXEL31_32 + break; + } + case 189: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } + case 231: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_32 + PIXEL03_82 + PIXEL10_81 + PIXEL11_31 + PIXEL12_32 + PIXEL13_82 + PIXEL20_82 + PIXEL21_32 + PIXEL22_31 + PIXEL23_81 + PIXEL30_82 + PIXEL31_32 + PIXEL32_31 + PIXEL33_81 + break; + } + case 126: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 219: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 125: + { + if (Diff(w[8], w[4])) + { + PIXEL00_82 + PIXEL10_32 + PIXEL20_0 + PIXEL21_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL00_11 + PIXEL10_13 + PIXEL20_83 + PIXEL21_70 + PIXEL30_50 + PIXEL31_21 + } + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 221: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + if (Diff(w[6], w[8])) + { + PIXEL03_81 + PIXEL13_31 + PIXEL22_0 + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL03_12 + PIXEL13_14 + PIXEL22_70 + PIXEL23_83 + PIXEL32_21 + PIXEL33_50 + } + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL20_10 + PIXEL21_30 + PIXEL30_80 + PIXEL31_10 + break; + } + case 207: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + } + else + { + PIXEL00_50 + PIXEL01_83 + PIXEL02_13 + PIXEL03_11 + PIXEL10_21 + PIXEL11_70 + } + PIXEL12_32 + PIXEL13_82 + PIXEL20_10 + PIXEL21_30 + PIXEL22_31 + PIXEL23_81 + PIXEL30_80 + PIXEL31_10 + PIXEL32_31 + PIXEL33_81 + break; + } + case 238: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_32 + PIXEL03_82 + PIXEL10_10 + PIXEL11_30 + PIXEL12_32 + PIXEL13_82 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL21_0 + PIXEL30_0 + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + } + else + { + PIXEL20_21 + PIXEL21_70 + PIXEL30_50 + PIXEL31_83 + PIXEL32_14 + PIXEL33_12 + } + PIXEL22_31 + PIXEL23_81 + break; + } + case 190: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL12_0 + PIXEL13_0 + PIXEL23_32 + PIXEL33_82 + } + else + { + PIXEL02_21 + PIXEL03_50 + PIXEL12_70 + PIXEL13_83 + PIXEL23_13 + PIXEL33_11 + } + PIXEL10_10 + PIXEL11_30 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + break; + } + case 187: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + PIXEL11_0 + PIXEL20_31 + PIXEL30_81 + } + else + { + PIXEL00_50 + PIXEL01_21 + PIXEL10_83 + PIXEL11_70 + PIXEL20_14 + PIXEL30_12 + } + PIXEL02_10 + PIXEL03_80 + PIXEL12_30 + PIXEL13_10 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } + case 243: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_10 + PIXEL03_80 + PIXEL10_81 + PIXEL11_31 + PIXEL12_30 + PIXEL13_10 + PIXEL20_82 + PIXEL21_32 + if (Diff(w[6], w[8])) + { + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL22_70 + PIXEL23_21 + PIXEL30_11 + PIXEL31_13 + PIXEL32_83 + PIXEL33_50 + } + break; + } + case 119: + { + if (Diff(w[2], w[6])) + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + PIXEL03_0 + PIXEL12_0 + PIXEL13_0 + } + else + { + PIXEL00_12 + PIXEL01_14 + PIXEL02_83 + PIXEL03_50 + PIXEL12_70 + PIXEL13_21 + } + PIXEL10_81 + PIXEL11_31 + PIXEL20_82 + PIXEL21_32 + PIXEL22_30 + PIXEL23_10 + PIXEL30_82 + PIXEL31_32 + PIXEL32_10 + PIXEL33_80 + break; + } + case 237: + case 233: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_60 + PIXEL03_20 + PIXEL10_32 + PIXEL11_32 + PIXEL12_70 + PIXEL13_60 + PIXEL20_0 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + if (Diff(w[8], w[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + break; + } + case 175: + case 47: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + PIXEL20_31 + PIXEL21_31 + PIXEL22_70 + PIXEL23_60 + PIXEL30_81 + PIXEL31_81 + PIXEL32_60 + PIXEL33_20 + break; + } + case 183: + case 151: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + if (Diff(w[2], w[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL13_0 + PIXEL20_60 + PIXEL21_70 + PIXEL22_32 + PIXEL23_32 + PIXEL30_20 + PIXEL31_60 + PIXEL32_82 + PIXEL33_82 + break; + } + case 245: + case 244: + { + PIXEL00_20 + PIXEL01_60 + PIXEL02_81 + PIXEL03_81 + PIXEL10_60 + PIXEL11_70 + PIXEL12_31 + PIXEL13_31 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + if (Diff(w[6], w[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } + case 250: + { + PIXEL00_80 + PIXEL01_10 + PIXEL02_10 + PIXEL03_80 + PIXEL10_10 + PIXEL11_30 + PIXEL12_30 + PIXEL13_10 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + break; + } + case 123: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 95: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL11_0 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_30 + PIXEL23_10 + PIXEL30_80 + PIXEL31_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 222: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 252: + { + PIXEL00_80 + PIXEL01_61 + PIXEL02_81 + PIXEL03_81 + PIXEL10_10 + PIXEL11_30 + PIXEL12_31 + PIXEL13_31 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + PIXEL32_0 + if (Diff(w[6], w[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } + case 249: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_61 + PIXEL03_80 + PIXEL10_32 + PIXEL11_32 + PIXEL12_30 + PIXEL13_10 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + if (Diff(w[8], w[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + break; + } + case 235: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_61 + PIXEL20_0 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + if (Diff(w[8], w[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + break; + } + case 111: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_61 + PIXEL32_10 + PIXEL33_80 + break; + } + case 63: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_30 + PIXEL23_10 + PIXEL30_81 + PIXEL31_81 + PIXEL32_61 + PIXEL33_80 + break; + } + case 159: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_0 + if (Diff(w[2], w[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_32 + PIXEL23_32 + PIXEL30_80 + PIXEL31_61 + PIXEL32_82 + PIXEL33_82 + break; + } + case 215: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + if (Diff(w[2], w[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL13_0 + PIXEL20_61 + PIXEL21_30 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 246: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_61 + PIXEL11_30 + PIXEL12_0 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + if (Diff(w[6], w[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } + case 254: + { + PIXEL00_80 + PIXEL01_10 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_10 + PIXEL11_30 + PIXEL12_0 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + PIXEL32_0 + if (Diff(w[6], w[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } + case 253: + { + PIXEL00_82 + PIXEL01_82 + PIXEL02_81 + PIXEL03_81 + PIXEL10_32 + PIXEL11_32 + PIXEL12_31 + PIXEL13_31 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + if (Diff(w[8], w[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + PIXEL32_0 + if (Diff(w[6], w[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } + case 251: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_10 + PIXEL03_80 + PIXEL11_0 + PIXEL12_30 + PIXEL13_10 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + if (Diff(w[8], w[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + break; + } + case 239: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + PIXEL02_32 + PIXEL03_82 + PIXEL10_0 + PIXEL11_0 + PIXEL12_32 + PIXEL13_82 + PIXEL20_0 + PIXEL21_0 + PIXEL22_31 + PIXEL23_81 + if (Diff(w[8], w[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + PIXEL32_31 + PIXEL33_81 + break; + } + case 127: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + if (Diff(w[2], w[6])) + { + PIXEL02_0 + PIXEL03_0 + PIXEL13_0 + } + else + { + PIXEL02_50 + PIXEL03_50 + PIXEL13_50 + } + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + if (Diff(w[8], w[4])) + { + PIXEL20_0 + PIXEL30_0 + PIXEL31_0 + } + else + { + PIXEL20_50 + PIXEL30_50 + PIXEL31_50 + } + PIXEL21_0 + PIXEL22_30 + PIXEL23_10 + PIXEL32_10 + PIXEL33_80 + break; + } + case 191: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + PIXEL02_0 + if (Diff(w[2], w[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_31 + PIXEL21_31 + PIXEL22_32 + PIXEL23_32 + PIXEL30_81 + PIXEL31_81 + PIXEL32_82 + PIXEL33_82 + break; + } + case 223: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_0 + PIXEL10_0 + } + else + { + PIXEL00_50 + PIXEL01_50 + PIXEL10_50 + } + PIXEL02_0 + if (Diff(w[2], w[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_10 + PIXEL21_30 + PIXEL22_0 + if (Diff(w[6], w[8])) + { + PIXEL23_0 + PIXEL32_0 + PIXEL33_0 + } + else + { + PIXEL23_50 + PIXEL32_50 + PIXEL33_50 + } + PIXEL30_80 + PIXEL31_10 + break; + } + case 247: + { + PIXEL00_81 + PIXEL01_31 + PIXEL02_0 + if (Diff(w[2], w[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL10_81 + PIXEL11_31 + PIXEL12_0 + PIXEL13_0 + PIXEL20_82 + PIXEL21_32 + PIXEL22_0 + PIXEL23_0 + PIXEL30_82 + PIXEL31_32 + PIXEL32_0 + if (Diff(w[6], w[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } + case 255: + { + if (Diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_0 + PIXEL02_0 + if (Diff(w[2], w[6])) + { + PIXEL03_0 + } + else + { + PIXEL03_20 + } + PIXEL10_0 + PIXEL11_0 + PIXEL12_0 + PIXEL13_0 + PIXEL20_0 + PIXEL21_0 + PIXEL22_0 + PIXEL23_0 + if (Diff(w[8], w[4])) + { + PIXEL30_0 + } + else + { + PIXEL30_20 + } + PIXEL31_0 + PIXEL32_0 + if (Diff(w[6], w[8])) + { + PIXEL33_0 + } + else + { + PIXEL33_20 + } + break; + } + } + pIn++; // next source pixel (just increment since it's an int*) + pOut += 16; // skip 4 pixels (4 bytes * 4 pixels) + } + pOut += BpL; // skip next 3 rows + pOut += BpL; + pOut += BpL; + } + __asm emms +} + +void DLL InitLUTs() +{ + int i, j, k, r, g, b, Y, u, v; + +#if 0 // colorOutlines() after hqresize + for (i=0; i<65536; i++) + LUT16to32[i] = 0x00404040; + for (i=0; i<65536; i++) + LUT16to32[i+65536] = 0xFF000000 + ((i & 0xF800) << 8) + ((i & 0x07E0) << 5) + ((i & 0x001F) << 3); +#else // colorOutlines() before hqresize + for (i=0; i<65536; i++) + LUT16to32[i] = ((i & 0xF800) << 8) + ((i & 0x07E0) << 5) + ((i & 0x001F) << 3); + for (i=0; i<65536; i++) + LUT16to32[i+65536] = 0xFF000000 + LUT16to32[i]; +#endif + + for (i=0; i<65536; i++) + RGBtoYUV[i] = 0xFF000000; + + for (i=0; i<32; i++) + for (j=0; j<64; j++) + for (k=0; k<32; k++) + { + r = i << 3; + g = j << 2; + b = k << 3; + Y = (r + g + b) >> 2; + u = 128 + ((r - b) >> 2); + v = 128 + ((-r + 2*g -b)>>3); + RGBtoYUV[ 65536 + (i << 11) + (j << 5) + k ] = (Y<<16) + (u<<8) + v; + } +} + +/* +int DLL hq4x_32 ( CImage &ImageIn, CImage &ImageOut ) +{ + if ( ImageIn.Convert32To17() != 0 ) + { + printf( "ERROR: conversion to 17 bit failed\n" ); + return 1; + } + + if ( ImageOut.Init( ImageIn.m_Xres*4, ImageIn.m_Yres*4, 32 ) != 0 ) + { + printf( "ERROR: ImageOut.Init()\n" ); + return 1; + }; + + InitLUTs(); + hq4x_32( (int*)ImageIn.m_pBitmap, ImageOut.m_pBitmap, ImageIn.m_Xres, ImageIn.m_Yres, ImageOut.m_Xres*4 ); + + printf( "\nOK\n" ); + return 0; +} +*/ \ No newline at end of file diff --git a/src/gl/hqnx/hqnx.h b/src/gl/hqnx/hqnx.h new file mode 100644 index 000000000..ce0aeced4 --- /dev/null +++ b/src/gl/hqnx/hqnx.h @@ -0,0 +1,35 @@ +//hqnx filter library +//---------------------------------------------------------- +//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) +//Copyright (C) 2009 Benjamin Berkels +// +//This program is free software; you can redistribute it and/or +//modify it under the terms of the GNU Lesser General Public +//License as published by the Free Software Foundation; either +//version 2.1 of the License, or (at your option) any later version. +// +//This program is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +//Lesser General Public License for more details. +// +//You should have received a copy of the GNU Lesser General Public +//License along with this program; if not, write to the Free Software +//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef __HQNX_H__ +#define __HQNX_H__ + +#pragma warning(disable:4799) + +#include "Image.h" + +void DLL hq2x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL ); +void DLL hq3x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL ); +void DLL hq4x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL ); +int DLL hq4x_32 ( CImage &ImageIn, CImage &ImageOut ); + +void DLL InitLUTs(); + + +#endif //__HQNX_H__ \ No newline at end of file diff --git a/src/gl/models/gl_models.cpp b/src/gl/models/gl_models.cpp new file mode 100644 index 000000000..9f13bee7d --- /dev/null +++ b/src/gl/models/gl_models.cpp @@ -0,0 +1,922 @@ +/* +** gl_models.cpp +** +** General model handling code +** +**--------------------------------------------------------------------------- +** 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** +** 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 "w_wad.h" +#include "cmdlib.h" +#include "sc_man.h" +#include "m_crc32.h" +#include "c_console.h" +#include "g_game.h" +#include "doomstat.h" +#include "g_level.h" +#include "r_state.h" +#include "d_player.h" +//#include "resources/voxels.h" +//#include "gl/gl_intern.h" + +#include "gl/renderer/gl_renderer.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/models/gl_models.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_geometric.h" +#include "gl/utility/gl_convert.h" +#include "gl/renderer/gl_renderstate.h" + +static inline float GetTimeFloat() +{ + return (float)I_MSTime() * (float)TICRATE / 1000.0f; +} + +CVAR(Bool, gl_interpolate_model_frames, true, CVAR_ARCHIVE) +CVAR(Bool, gl_light_models, true, CVAR_ARCHIVE) +EXTERN_CVAR(Int, gl_fogmode) +EXTERN_CVAR(Bool, gl_dynlight_shader) + +extern TDeletingArray Voxels; +extern TDeletingArray VoxelDefs; + + +class DeletingModelArray : public TArray +{ +public: + +#if 0 + ~DeletingModelArray() + { + for(unsigned i=0;i SpriteModelFrames; +static int * SpriteModelHash; +//TArray StateModelFrames; + +static void DeleteModelHash() +{ + if (SpriteModelHash != NULL) delete [] SpriteModelHash; + SpriteModelHash = NULL; +} + +//=========================================================================== +// +// FindGFXFile +// +//=========================================================================== + +static int FindGFXFile(FString & fn) +{ + int best = -1; + int dot = fn.LastIndexOf('.'); + int slash = fn.LastIndexOf('/'); + if (dot > slash) fn.Truncate(dot); + + static const char * extensions[] = { ".png", ".jpg", ".tga", ".pcx", NULL }; + + for (const char ** extp=extensions; *extp; extp++) + { + int lump = Wads.CheckNumForFullName(fn + *extp); + if (lump >= best) best = lump; + } + return best; +} + + +//=========================================================================== +// +// LoadSkin +// +//=========================================================================== + +FTexture * LoadSkin(const char * path, const char * fn) +{ + FString buffer; + + buffer.Format("%s%s", path, fn); + + int texlump = FindGFXFile(buffer); + if (texlump>=0) + { + FTextureID texno = TexMan.FindTextureByLumpNum(texlump); + if (!texno.isValid()) + { + FTexture *tex = FTexture::CreateTexture("", texlump, FTexture::TEX_Override); + TexMan.AddTexture(tex); + return tex; + } + return TexMan[texno]; + } + else + { + return NULL; + } +} + +//=========================================================================== +// +// ModelFrameHash +// +//=========================================================================== + +static int ModelFrameHash(FSpriteModelFrame * smf) +{ + const DWORD *table = GetCRCTable (); + DWORD hash = 0xffffffff; + + const char * s = (const char *)(&smf->type); // this uses type, sprite and frame for hashing + const char * se= (const char *)(&smf->hashnext); + + for (; smFileName.CompareNoCase(fullname)) return Models[i]; + } + + int len = Wads.LumpLength(lump); + FMemLump lumpd = Wads.ReadLump(lump); + char * buffer = (char*)lumpd.GetMem(); + + if (!memcmp(buffer, "DMDM", 4)) + { + model = new FDMDModel; + } + else if (!memcmp(buffer, "IDP2", 4)) + { + model = new FMD2Model; + } + else if (!memcmp(buffer, "IDP3", 4)) + { + model = new FMD3Model; + } + + if (model != NULL) + { + if (!model->Load(path, lump, buffer, len)) + { + delete model; + return NULL; + } + } + else + { + // try loading as a voxel + FVoxel *voxel = R_LoadKVX(lump); + if (voxel != NULL) + { + model = new FVoxelModel(voxel, true); + } + else + { + Printf("LoadModel: Unknown model format in '%s'\n", fullname.GetChars()); + return NULL; + } + } + + model->mFileName = fullname; + Models.Push(model); + return model; +} + +//=========================================================================== +// +// gl_InitModels +// +//=========================================================================== + +void gl_InitModels() +{ + int Lump, lastLump; + FString path; + int index; + int i; + + FSpriteModelFrame smf; + + lastLump = 0; + + for(unsigned i=0;iVoxelIndex = Models.Push(md); + } + // now create GL model frames for the voxeldefs + for (unsigned i = 0; i < VoxelDefs.Size(); i++) + { + FVoxelModel *md = (FVoxelModel*)Models[VoxelDefs[i]->Voxel->VoxelIndex]; + memset(&smf, 0, sizeof(smf)); + smf.models[0] = md; + smf.skins[0] = md->GetPaletteTexture(); + smf.xscale = smf.yscale = smf.zscale = FIXED2FLOAT(VoxelDefs[i]->Scale); + smf.angleoffset = VoxelDefs[i]->AngleOffset; + if (VoxelDefs[i]->PlacedSpin != 0) + { + smf.yrotate = 1.f; + smf.rotationSpeed = VoxelDefs[i]->PlacedSpin / 55.55f; + smf.flags |= MDL_ROTATING; + } + VoxelDefs[i]->VoxeldefIndex = SpriteModelFrames.Push(smf); + if (VoxelDefs[i]->PlacedSpin != VoxelDefs[i]->DroppedSpin) + { + if (VoxelDefs[i]->DroppedSpin != 0) + { + smf.yrotate = 1.f; + smf.rotationSpeed = VoxelDefs[i]->DroppedSpin / 55.55f; + smf.flags |= MDL_ROTATING; + } + else + { + smf.yrotate = 0; + smf.rotationSpeed = 0; + smf.flags &= ~MDL_ROTATING; + } + SpriteModelFrames.Push(smf); + } + } + + memset(&smf, 0, sizeof(smf)); + while ((Lump = Wads.FindLump("MODELDEF", &lastLump)) != -1) + { + FScanner sc(Lump); + while (sc.GetString()) + { + if (sc.Compare("model")) + { + sc.MustGetString(); + memset(&smf, 0, sizeof(smf)); + smf.xscale=smf.yscale=smf.zscale=1.f; + + smf.type = PClass::FindClass(sc.String); + if (!smf.type || smf.type->Defaults == NULL) + { + sc.ScriptError("MODELDEF: Unknown actor type '%s'\n", sc.String); + } + GetDefaultByType(smf.type)->hasmodel=true; + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("path")) + { + sc.MustGetString(); + FixPathSeperator(sc.String); + path = sc.String; + if (path[(int)path.Len()-1]!='/') path+='/'; + } + else if (sc.Compare("model")) + { + sc.MustGetNumber(); + index=sc.Number; + if (index<0 || index>=MAX_MODELS_PER_FRAME) + { + sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars()); + } + sc.MustGetString(); + FixPathSeperator(sc.String); + smf.models[index] = FindModel(path.GetChars(), sc.String); + if (!smf.models[index]) + { + Printf("%s: model not found in %s\n", sc.String, path.GetChars()); + } + } + else if (sc.Compare("scale")) + { + sc.MustGetFloat(); + smf.xscale=sc.Float; + sc.MustGetFloat(); + smf.yscale=sc.Float; + sc.MustGetFloat(); + smf.zscale=sc.Float; + } + // [BB] Added zoffset reading. + // Now it must be considered deprecated. + else if (sc.Compare("zoffset")) + { + sc.MustGetFloat(); + smf.zoffset=sc.Float; + } + // Offset reading. + else if (sc.Compare("offset")) + { + sc.MustGetFloat(); + smf.xoffset = sc.Float; + sc.MustGetFloat(); + smf.yoffset = sc.Float; + sc.MustGetFloat(); + smf.zoffset = sc.Float; + } + // angleoffset, pitchoffset and rolloffset reading. + else if (sc.Compare("angleoffset")) + { + sc.MustGetFloat(); + smf.angleoffset = FLOAT_TO_ANGLE(sc.Float); + } + else if (sc.Compare("pitchoffset")) + { + sc.MustGetFloat(); + smf.pitchoffset = sc.Float; + } + else if (sc.Compare("rolloffset")) + { + sc.MustGetFloat(); + smf.rolloffset = sc.Float; + } + // [BB] Added model flags reading. + else if (sc.Compare("ignoretranslation")) + { + smf.flags |= MDL_IGNORETRANSLATION; + } + else if (sc.Compare("pitchfrommomentum")) + { + smf.flags |= MDL_PITCHFROMMOMENTUM; + } + else if (sc.Compare("inheritactorpitch")) + { + smf.flags |= MDL_INHERITACTORPITCH; + } + else if (sc.Compare("inheritactorroll")) + { + smf.flags |= MDL_INHERITACTORROLL; + } + else if (sc.Compare("rotating")) + { + smf.flags |= MDL_ROTATING; + smf.xrotate = 0.; + smf.yrotate = 1.; + smf.zrotate = 0.; + smf.rotationCenterX = 0.; + smf.rotationCenterY = 0.; + smf.rotationCenterZ = 0.; + smf.rotationSpeed = 1.; + } + else if (sc.Compare("rotation-speed")) + { + sc.MustGetFloat(); + smf.rotationSpeed = sc.Float; + } + else if (sc.Compare("rotation-vector")) + { + sc.MustGetFloat(); + smf.xrotate = sc.Float; + sc.MustGetFloat(); + smf.yrotate = sc.Float; + sc.MustGetFloat(); + smf.zrotate = sc.Float; + } + else if (sc.Compare("rotation-center")) + { + sc.MustGetFloat(); + smf.rotationCenterX = sc.Float; + sc.MustGetFloat(); + smf.rotationCenterY = sc.Float; + sc.MustGetFloat(); + smf.rotationCenterZ = sc.Float; + } + else if (sc.Compare("interpolatedoubledframes")) + { + smf.flags |= MDL_INTERPOLATEDOUBLEDFRAMES; + } + else if (sc.Compare("nointerpolation")) + { + smf.flags |= MDL_NOINTERPOLATION; + } + else if (sc.Compare("skin")) + { + sc.MustGetNumber(); + index=sc.Number; + if (index<0 || index>=MAX_MODELS_PER_FRAME) + { + sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars()); + } + sc.MustGetString(); + FixPathSeperator(sc.String); + if (sc.Compare("")) + { + smf.skins[index]=NULL; + } + else + { + smf.skins[index]=LoadSkin(path.GetChars(), sc.String); + if (smf.skins[index] == NULL) + { + Printf("Skin '%s' not found in '%s'\n", + sc.String, smf.type->TypeName.GetChars()); + } + } + } + else if (sc.Compare("frameindex") || sc.Compare("frame")) + { + bool isframe=!!sc.Compare("frame"); + + sc.MustGetString(); + smf.sprite = -1; + for (i = 0; i < (int)sprites.Size (); ++i) + { + if (strncmp (sprites[i].name, sc.String, 4) == 0) + { + if (sprites[i].numframes==0) + { + //sc.ScriptError("Sprite %s has no frames", sc.String); + } + smf.sprite = i; + break; + } + } + if (smf.sprite==-1) + { + sc.ScriptError("Unknown sprite %s in model definition for %s", sc.String, smf.type->TypeName.GetChars()); + } + + sc.MustGetString(); + FString framechars = sc.String; + + sc.MustGetNumber(); + index=sc.Number; + if (index<0 || index>=MAX_MODELS_PER_FRAME) + { + sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars()); + } + if (isframe) + { + sc.MustGetString(); + if (smf.models[index]!=NULL) + { + smf.modelframes[index] = smf.models[index]->FindFrame(sc.String); + if (smf.modelframes[index]==-1) sc.ScriptError("Unknown frame '%s' in %s", sc.String, smf.type->TypeName.GetChars()); + } + else smf.modelframes[index] = -1; + } + else + { + sc.MustGetNumber(); + smf.modelframes[index] = sc.Number; + } + + for(i=0; framechars[i]>0; i++) + { + char map[29]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + int c = toupper(framechars[i])-'A'; + + if (c<0 || c>=29) + { + sc.ScriptError("Invalid frame character %c found", c+'A'); + } + if (map[c]) continue; + smf.frame=c; + SpriteModelFrames.Push(smf); + map[c]=1; + } + } + else + { + sc.ScriptMessage("Unrecognized string \"%s\"", sc.String); + } + } + } + } + } + + // create a hash table for quick access + SpriteModelHash = new int[SpriteModelFrames.Size ()]; + atterm(DeleteModelHash); + memset(SpriteModelHash, 0xff, SpriteModelFrames.Size () * sizeof(int)); + + for (i = 0; i < (int)SpriteModelFrames.Size (); i++) + { + int j = ModelFrameHash(&SpriteModelFrames[i]) % SpriteModelFrames.Size (); + + SpriteModelFrames[i].hashnext = SpriteModelHash[j]; + SpriteModelHash[j]=i; + } +} + + +//=========================================================================== +// +// gl_FindModelFrame +// +//=========================================================================== +EXTERN_CVAR (Bool, r_drawvoxels) + +FSpriteModelFrame * gl_FindModelFrame(const PClass * ti, int sprite, int frame, bool dropped) +{ + if (GetDefaultByType(ti)->hasmodel) + { + FSpriteModelFrame smf; + + memset(&smf, 0, sizeof(smf)); + smf.type=ti; + smf.sprite=sprite; + smf.frame=frame; + + int hash = SpriteModelHash[ModelFrameHash(&smf) % SpriteModelFrames.Size()]; + + while (hash>=0) + { + FSpriteModelFrame * smff = &SpriteModelFrames[hash]; + if (smff->type==ti && smff->sprite==sprite && smff->frame==frame) return smff; + hash=smff->hashnext; + } + } + + // Check for voxel replacements + if (r_drawvoxels) + { + spritedef_t *sprdef = &sprites[sprite]; + if (frame < sprdef->numframes) + { + spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + frame]; + if (sprframe->Voxel != NULL) + { + int index = sprframe->Voxel->VoxeldefIndex; + if (dropped && sprframe->Voxel->DroppedSpin !=sprframe->Voxel->PlacedSpin) index++; + return &SpriteModelFrames[index]; + } + } + } + return NULL; +} + + +//=========================================================================== +// +// gl_RenderModel +// +//=========================================================================== + +void gl_RenderFrameModels( const FSpriteModelFrame *smf, + const FState *curState, + const int curTics, + const PClass *ti, + int cm, + Matrix3x4 *normaltransform, + int translation) +{ + // [BB] Frame interpolation: Find the FSpriteModelFrame smfNext which follows after smf in the animation + // and the scalar value inter ( element of [0,1) ), both necessary to determine the interpolated frame. + FSpriteModelFrame * smfNext = NULL; + double inter = 0.; + if( gl_interpolate_model_frames && !(smf->flags & MDL_NOINTERPOLATION) ) + { + FState *nextState = curState->GetNextState( ); + if( curState != nextState && nextState ) + { + // [BB] To interpolate at more than 35 fps we take tic fractions into account. + float ticFraction = 0.; + // [BB] In case the tic counter is frozen we have to leave ticFraction at zero. + if ( ConsoleState == c_up && menuactive != MENU_On && !(level.flags2 & LEVEL2_FROZEN) ) + { + float time = GetTimeFloat(); + ticFraction = (time - static_cast(time)); + } + inter = static_cast(curState->Tics - curTics - ticFraction)/static_cast(curState->Tics); + + // [BB] For some actors (e.g. ZPoisonShroom) spr->actor->tics can be bigger than curState->Tics. + // In this case inter is negative and we need to set it to zero. + if ( inter < 0. ) + inter = 0.; + else + { + // [BB] Workaround for actors that use the same frame twice in a row. + // Most of the standard Doom monsters do this in their see state. + if ( (smf->flags & MDL_INTERPOLATEDOUBLEDFRAMES) ) + { + const FState *prevState = curState - 1; + if ( (curState->sprite == prevState->sprite) && ( curState->Frame == prevState->Frame) ) + { + inter /= 2.; + inter += 0.5; + } + if ( (curState->sprite == nextState->sprite) && ( curState->Frame == nextState->Frame) ) + { + inter /= 2.; + nextState = nextState->GetNextState( ); + } + } + if ( inter != 0.0 ) + smfNext = gl_FindModelFrame(ti, nextState->sprite, nextState->Frame, false); + } + } + } + + for(int i=0; imodels[i]; + + if (mdl!=NULL) + { + if ( smfNext && smf->modelframes[i] != smfNext->modelframes[i] ) + mdl->RenderFrameInterpolated(smf->skins[i], smf->modelframes[i], smfNext->modelframes[i], inter, cm, translation); + else + mdl->RenderFrame(smf->skins[i], smf->modelframes[i], cm, translation); + } + } +} + +void gl_RenderModel(GLSprite * spr, int cm) +{ + FSpriteModelFrame * smf = spr->modelframe; + + + // Setup transformation. + gl.DepthFunc(GL_LEQUAL); + gl_RenderState.SetTextureMode(TM_MODULATE); + gl_RenderState.EnableTexture(true); + // [BB] In case the model should be rendered translucent, do back face culling. + // This solves a few of the problems caused by the lack of depth sorting. + // TO-DO: Implement proper depth sorting. + if (!( spr->actor->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) + { + gl.Enable(GL_CULL_FACE); + glFrontFace(GL_CW); + } + + int translation = 0; + if ( !(smf->flags & MDL_IGNORETRANSLATION) ) + translation = spr->actor->Translation; + + + // y scale for a sprite means height, i.e. z in the world! + float scaleFactorX = FIXED2FLOAT(spr->actor->scaleX) * smf->xscale; + float scaleFactorY = FIXED2FLOAT(spr->actor->scaleX) * smf->yscale; + float scaleFactorZ = FIXED2FLOAT(spr->actor->scaleY) * smf->zscale; + float pitch = 0; + float roll = 0; + float rotateOffset = 0; + float angle = ANGLE_TO_FLOAT(spr->actor->angle); + + // [BB] Workaround for the missing pitch information. + if ( (smf->flags & MDL_PITCHFROMMOMENTUM) ) + { + const double x = static_cast(spr->actor->velx); + const double y = static_cast(spr->actor->vely); + const double z = static_cast(spr->actor->velz); + + // [BB] Calculate the pitch using spherical coordinates. + if(z || x || y) pitch = float(atan( z/sqrt(x*x+y*y) ) / M_PI * 180); + + // Correcting pitch if model is moving backwards + if(x || y) + { + if((x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180)) / sqrt(x * x + y * y) < 0) pitch *= -1; + } + else pitch = abs(pitch); + } + + if( smf->flags & MDL_ROTATING ) + { + const float time = smf->rotationSpeed*GetTimeFloat()/200.f; + rotateOffset = float((time - xs_FloorToInt(time)) *360.f ); + } + + // Added MDL_INHERITACTORPITCH and MDL_INHERITACTORROLL flags processing. + // If both flags MDL_INHERITACTORPITCH and MDL_PITCHFROMMOMENTUM are set, the pitch sums up the actor pitch and the momentum vector pitch. + // This is rather crappy way to transfer fixet_t type into angle in degrees, but its works! + if(smf->flags & MDL_INHERITACTORPITCH) pitch += float(static_cast(spr->actor->pitch >> 16) / (1 << 13) * 45 + static_cast(spr->actor->pitch & 0x0000FFFF) / (1 << 29) * 45); + if(smf->flags & MDL_INHERITACTORROLL) roll += float(static_cast(spr->actor->roll >> 16) / (1 << 13) * 45 + static_cast(spr->actor->roll & 0x0000FFFF) / (1 << 29) * 45); + + if (gl.shadermodel < 4) + { + gl.MatrixMode(GL_MODELVIEW); + gl.PushMatrix(); + } + else + { + gl.ActiveTexture(GL_TEXTURE7); // Hijack the otherwise unused seventh texture matrix for the model to world transformation. + gl.MatrixMode(GL_TEXTURE); + gl.LoadIdentity(); + } + + // Model space => World space + gl.Translatef(spr->x, spr->z, spr->y ); + + // Applying model transformations: + // 1) Applying actor angle, pitch and roll to the model + gl.Rotatef(-angle, 0, 1, 0); + gl.Rotatef(pitch, 0, 0, 1); + gl.Rotatef(-roll, 1, 0, 0); + + // 2) Applying Doomsday like rotation of the weapon pickup models + // The rotation angle is based on the elapsed time. + + if( smf->flags & MDL_ROTATING ) + { + gl.Translatef(smf->rotationCenterX, smf->rotationCenterY, smf->rotationCenterZ); + gl.Rotatef(rotateOffset, smf->xrotate, smf->yrotate, smf->zrotate); + gl.Translatef(-smf->rotationCenterX, -smf->rotationCenterY, -smf->rotationCenterZ); + } + + // 3) Scaling model. + gl.Scalef(scaleFactorX, scaleFactorZ, scaleFactorY); + + // 4) Aplying model offsets (model offsets do not depend on model scalings). + gl.Translatef(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); + + // 5) Applying model rotations. + gl.Rotatef(-ANGLE_TO_FLOAT(smf->angleoffset), 0, 1, 0); + gl.Rotatef(smf->pitchoffset, 0, 0, 1); + gl.Rotatef(-smf->rolloffset, 1, 0, 0); + + if (gl.shadermodel >= 4) gl.ActiveTexture(GL_TEXTURE0); + +#if 0 + if (gl_light_models) + { + // The normal transform matrix only contains the inverse rotations and scalings but not the translations + NormalTransform.MakeIdentity(); + + NormalTransform.Scale(1.f/scaleFactorX, 1.f/scaleFactorZ, 1.f/scaleFactorY); + if( smf->flags & MDL_ROTATING ) NormalTransform.Rotate(smf->xrotate, smf->yrotate, smf->zrotate, -rotateOffset); + if (pitch != 0) NormalTransform.Rotate(0,0,1,-pitch); + if (angle != 0) NormalTransform.Rotate(0,1,0, angle); + + gl_RenderFrameModels( smf, spr->actor->state, spr->actor->tics, RUNTIME_TYPE(spr->actor), cm, &ModelToWorld, &NormalTransform, translation ); + } +#endif + + gl_RenderFrameModels( smf, spr->actor->state, spr->actor->tics, RUNTIME_TYPE(spr->actor), cm, NULL, translation ); + + if (gl.shadermodel < 4) + { + gl.MatrixMode(GL_MODELVIEW); + gl.PopMatrix(); + } + else + { + gl.ActiveTexture(GL_TEXTURE7); + gl.MatrixMode(GL_TEXTURE); + gl.LoadIdentity(); + gl.ActiveTexture(GL_TEXTURE0); + gl.MatrixMode(GL_MODELVIEW); + } + + gl.DepthFunc(GL_LESS); + if (!( spr->actor->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) + gl.Disable(GL_CULL_FACE); +} + + +//=========================================================================== +// +// gl_RenderHUDModel +// +//=========================================================================== + +void gl_RenderHUDModel(pspdef_t *psp, fixed_t ofsx, fixed_t ofsy, int cm) +{ + AActor * playermo=players[consoleplayer].camera; + FSpriteModelFrame *smf = gl_FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->state->sprite, psp->state->GetFrame(), false); + + // [BB] No model found for this sprite, so we can't render anything. + if ( smf == NULL ) + return; + + // [BB] The model has to be drawn independtly from the position of the player, + // so we have to reset the GL_MODELVIEW matrix. + gl.MatrixMode(GL_MODELVIEW); + gl.PushMatrix(); + gl.LoadIdentity(); + gl.DepthFunc(GL_LEQUAL); + + // [BB] In case the model should be rendered translucent, do back face culling. + // This solves a few of the problems caused by the lack of depth sorting. + // TO-DO: Implement proper depth sorting. + if (!( playermo->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) + { + gl.Enable(GL_CULL_FACE); + glFrontFace(GL_CCW); + } + + // Scaling model (y scale for a sprite means height, i.e. z in the world!). + gl.Scalef(smf->xscale, smf->zscale, smf->yscale); + + // Aplying model offsets (model offsets do not depend on model scalings). + gl.Translatef(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); + + // [BB] Weapon bob, very similar to the normal Doom weapon bob. + gl.Rotatef(FIXED2FLOAT(ofsx)/4, 0, 1, 0); + gl.Rotatef(-FIXED2FLOAT(ofsy-WEAPONTOP)/4, 1, 0, 0); + + // [BB] For some reason the jDoom models need to be rotated. + gl.Rotatef(90., 0, 1, 0); + + // Applying angleoffset, pitchoffset, rolloffset. + gl.Rotatef(-ANGLE_TO_FLOAT(smf->angleoffset), 0, 1, 0); + gl.Rotatef(smf->pitchoffset, 0, 0, 1); + gl.Rotatef(-smf->rolloffset, 1, 0, 0); + + gl_RenderFrameModels( smf, psp->state, psp->tics, playermo->player->ReadyWeapon->GetClass(), cm, NULL, 0 ); + + gl.MatrixMode(GL_MODELVIEW); + gl.PopMatrix(); + gl.DepthFunc(GL_LESS); + if (!( playermo->RenderStyle == LegacyRenderStyles[STYLE_Normal] )) + gl.Disable(GL_CULL_FACE); +} + +//=========================================================================== +// +// gl_IsHUDModelForPlayerAvailable +// +//=========================================================================== + +bool gl_IsHUDModelForPlayerAvailable (player_t * player) +{ + if ( (player == NULL) || (player->ReadyWeapon == NULL) || (player->psprites[0].state == NULL) ) + return false; + + FState* state = player->psprites[0].state; + FSpriteModelFrame *smf = gl_FindModelFrame(player->ReadyWeapon->GetClass(), state->sprite, state->GetFrame(), false); + return ( smf != NULL ); +} + +//=========================================================================== +// +// gl_CleanModelData +// +//=========================================================================== + +void gl_CleanModelData() +{ + for (unsigned i=0;iCleanGLData(); + } +} diff --git a/src/gl/models/gl_models.h b/src/gl/models/gl_models.h new file mode 100644 index 000000000..c3d564f9d --- /dev/null +++ b/src/gl/models/gl_models.h @@ -0,0 +1,365 @@ +#ifndef __GL_MODELS_H_ +#define __GL_MODELS_H_ + +#include "gl/utility/gl_geometric.h" +#include "p_pspr.h" +#include "r_data/voxels.h" + +#define MAX_LODS 4 + +enum { VX, VZ, VY }; + +static const float rModelAspectMod = 1 / 1.2f; //.833334f; + +#define MD2_MAGIC 0x32504449 +#define DMD_MAGIC 0x4D444D44 +#define MD3_MAGIC 0x33504449 +#define NUMVERTEXNORMALS 162 + +FTexture * LoadSkin(const char * path, const char * fn); + + +class FModel +{ +public: + FModel() { } + virtual ~FModel() { } + + virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) = 0; + virtual int FindFrame(const char * name) = 0; + virtual void RenderFrame(FTexture * skin, int frame, int cm, int translation=0) = 0; + // [BB] Added RenderFrameInterpolated + virtual void RenderFrameInterpolated(FTexture * skin, int frame, int frame2, double inter, int cm, int translation=0) = 0; + virtual void MakeGLData() {} + virtual void CleanGLData() {} + + + + FString mFileName; +}; + +class FDMDModel : public FModel +{ +protected: + + struct FTriangle + { + short vertexIndices[3]; + short textureIndices[3]; + }; + + + struct DMDHeader + { + int magic; + int version; + int flags; + }; + + struct FModelVertex + { + float xyz[3]; + }; + + struct FTexCoord + { + short s, t; + }; + + struct FGLCommandVertex + { + float s, t; + int index; + }; + + struct DMDInfo + { + int skinWidth; + int skinHeight; + int frameSize; + int numSkins; + int numVertices; + int numTexCoords; + int numFrames; + int numLODs; + int offsetSkins; + int offsetTexCoords; + int offsetFrames; + int offsetLODs; + int offsetEnd; + }; + + struct ModelFrame + { + char name[16]; + FModelVertex *vertices; + FModelVertex *normals; + + ModelFrame() + { + vertices = normals = NULL; + } + + ~ModelFrame() + { + if (vertices) delete [] vertices; + if (normals) delete [] normals; + } + }; + + struct DMDLoDInfo + { + int numTriangles; + int numGlCommands; + int offsetTriangles; + int offsetGlCommands; + }; + + struct DMDLoD + { + FTriangle * triangles; + int * glCommands; + }; + + + bool loaded; + DMDHeader header; + DMDInfo info; + FTexture ** skins; + FTexCoord * texCoords; + + ModelFrame * frames; + DMDLoDInfo lodInfo[MAX_LODS]; + DMDLoD lods[MAX_LODS]; + char *vertexUsage; // Bitfield for each vertex. + bool allowTexComp; // Allow texture compression with this. + + static void RenderGLCommands(void *glCommands, unsigned int numVertices,FModelVertex * vertices); + +public: + FDMDModel() + { + loaded = false; + frames = NULL; + skins = NULL; + lods[0].glCommands = NULL; + info.numLODs = 0; + } + virtual ~FDMDModel(); + + virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length); + virtual int FindFrame(const char * name); + virtual void RenderFrame(FTexture * skin, int frame, int cm, int translation=0); + virtual void RenderFrameInterpolated(FTexture * skin, int frame, int frame2, double inter, int cm, int translation=0); + +}; + +// This uses the same internal representation as DMD +class FMD2Model : public FDMDModel +{ +public: + FMD2Model() {} + virtual ~FMD2Model(); + + virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length); + +}; + + +class FMD3Model : public FModel +{ + struct MD3Tag + { + // Currently I have no use for this + }; + + struct MD3TexCoord + { + float s,t; + }; + + struct MD3Vertex + { + float x,y,z; + float nx,ny,nz; + }; + + struct MD3Triangle + { + int VertIndex[3]; + }; + + struct MD3Surface + { + int numVertices; + int numTriangles; + int numSkins; + + FTexture ** skins; + MD3Triangle * tris; + MD3TexCoord * texcoords; + MD3Vertex * vertices; + + MD3Surface() + { + tris=NULL; + vertices=NULL; + texcoords=NULL; + } + + ~MD3Surface() + { + if (tris) delete [] tris; + if (vertices) delete [] vertices; + if (texcoords) delete [] texcoords; + for (int i=0;i FVoxelMap; + + +class FVoxelModel : public FModel +{ +protected: + FVoxel *mVoxel; + bool mOwningVoxel; // if created through MODELDEF deleting this object must also delete the voxel object + TArray mVertices; + TArray mIndices; + FVoxelVertexBuffer *mVBO; + FTexture *mPalette; + + void MakeSlabPolys(int x, int y, kvxslab_t *voxptr, FVoxelMap &check); + void AddFace(int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3, int x4, int y4, int z4, BYTE color, FVoxelMap &check); + void AddVertex(FVoxelVertex &vert, FVoxelMap &check); + +public: + FVoxelModel(FVoxel *voxel, bool owned); + ~FVoxelModel(); + bool Load(const char * fn, int lumpnum, const char * buffer, int length); + void Initialize(); + void MakeGLData(); + void CleanGLData(); + virtual int FindFrame(const char * name); + virtual void RenderFrame(FTexture * skin, int frame, int cm, int translation=0); + virtual void RenderFrameInterpolated(FTexture * skin, int frame, int frame2, double inter, int cm, int translation=0); + FTexture *GetPaletteTexture() const { return mPalette; } +}; + + + +#define MAX_MODELS_PER_FRAME 4 + +// +// [BB] Model rendering flags. +// +enum +{ + // [BB] Color translations for the model skin are ignored. This is + // useful if the skin texture is not using the game palette. + MDL_IGNORETRANSLATION = 1, + MDL_PITCHFROMMOMENTUM = 2, + MDL_ROTATING = 4, + MDL_INTERPOLATEDOUBLEDFRAMES = 8, + MDL_NOINTERPOLATION = 16, + MDL_INHERITACTORPITCH = 32, + MDL_INHERITACTORROLL = 64, // useless for now +}; + +struct FSpriteModelFrame +{ + FModel * models[MAX_MODELS_PER_FRAME]; + FTexture * skins[MAX_MODELS_PER_FRAME]; + int modelframes[MAX_MODELS_PER_FRAME]; + float xscale, yscale, zscale; + // [BB] Added zoffset, rotation parameters and flags. + // Added xoffset, yoffset + float xoffset, yoffset, zoffset; + float xrotate, yrotate, zrotate; + float rotationCenterX, rotationCenterY, rotationCenterZ; + float rotationSpeed; + unsigned int flags; + const PClass * type; + short sprite; + short frame; + FState * state; // for later! + int hashnext; + angle_t angleoffset; + // added pithoffset, rolloffset. + float pitchoffset, rolloffset; // I don't want to bother with type transformations, so I made this variables float. +}; + +class GLSprite; + +void gl_InitModels(); +FSpriteModelFrame * gl_FindModelFrame(const PClass * ti, int sprite, int frame, bool dropped); + +void gl_RenderModel(GLSprite * spr, int cm); +// [BB] HUD weapon model rendering functions. +void gl_RenderHUDModel(pspdef_t *psp, fixed_t ofsx, fixed_t ofsy, int cm); +bool gl_IsHUDModelForPlayerAvailable (player_t * player); +void gl_CleanModelData(); + +#endif diff --git a/src/gl/models/gl_models_md2.cpp b/src/gl/models/gl_models_md2.cpp new file mode 100644 index 000000000..95a344c6e --- /dev/null +++ b/src/gl/models/gl_models_md2.cpp @@ -0,0 +1,509 @@ +/* +** gl_models.cpp +** +** MD2/DMD model format code +** +**--------------------------------------------------------------------------- +** 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** +** 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 "w_wad.h" +#include "cmdlib.h" +#include "sc_man.h" +#include "m_crc32.h" + +#include "gl/renderer/gl_renderstate.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/models/gl_models.h" +#include "gl/textures/gl_material.h" +#include "gl/shaders/gl_shader.h" + +static float avertexnormals[NUMVERTEXNORMALS][3] = { +#include "tab_anorms.h" +}; + +//=========================================================================== +// +// UnpackVector +// Packed: pppppppy yyyyyyyy. Yaw is on the XY plane. +// +//=========================================================================== + +static void UnpackVector(unsigned short packed, float vec[3]) +{ + float yaw = (packed & 511) / 512.0f * 2 * PI; + float pitch = ((packed >> 9) / 127.0f - 0.5f) * PI; + float cosp = (float) cos(pitch); + + vec[VX] = (float) cos(yaw) * cosp; + vec[VY] = (float) sin(yaw) * cosp; + vec[VZ] = (float) sin(pitch); +} + + +//=========================================================================== +// +// FDMDModel::Load +// +//=========================================================================== + +bool FDMDModel::Load(const char * path, int, const char * buffer, int length) +{ + struct dmd_chunk_t + { + int type; + int length; // Next chunk follows... + }; + +#pragma pack(1) + struct dmd_packedVertex_t + { + byte vertex[3]; + unsigned short normal; // Yaw and pitch. + }; + + struct dmd_packedFrame_t + { + float scale[3]; + float translate[3]; + char name[16]; + dmd_packedVertex_t vertices[1]; + } ; +#pragma pack() + + // Chunk types. + enum + { + DMC_END, // Must be the last chunk. + DMC_INFO // Required; will be expected to exist. + }; + + dmd_chunk_t * chunk = (dmd_chunk_t*)(buffer+12); + char *temp; + ModelFrame *frame; + int i, k, c; + FTriangle *triangles[MAX_LODS]; + int axis[3] = { VX, VY, VZ }; + + int fileoffset=12+sizeof(dmd_chunk_t); + + chunk->type = LittleLong(chunk->type); + while(chunk->type != DMC_END) + { + switch (chunk->type) + { + case DMC_INFO: // Standard DMD information chunk. + memcpy(&info, buffer + fileoffset, LittleLong(chunk->length)); + info.skinWidth = LittleLong(info.skinWidth); + info.skinHeight = LittleLong(info.skinHeight); + info.frameSize = LittleLong(info.frameSize); + info.numSkins = LittleLong(info.numSkins); + info.numVertices = LittleLong(info.numVertices); + info.numTexCoords = LittleLong(info.numTexCoords); + info.numFrames = LittleLong(info.numFrames); + info.numLODs = LittleLong(info.numLODs); + info.offsetSkins = LittleLong(info.offsetSkins); + info.offsetTexCoords = LittleLong(info.offsetTexCoords); + info.offsetFrames = LittleLong(info.offsetFrames); + info.offsetLODs = LittleLong(info.offsetLODs); + info.offsetEnd = LittleLong(info.offsetEnd); + fileoffset += chunk->length; + break; + + default: + // Just skip all unknown chunks. + fileoffset += chunk->length; + break; + } + // Read the next chunk header. + chunk = (dmd_chunk_t*)(buffer+fileoffset); + chunk->type = LittleLong(chunk->type); + fileoffset += sizeof(dmd_chunk_t); + } + + // Allocate and load in the data. + skins = new FTexture *[info.numSkins]; + + for(i = 0; i < info.numSkins; i++) + { + skins[i] = LoadSkin(path, buffer + info.offsetSkins + i*64); + } + + + temp = (char*)buffer + info.offsetFrames; + frames = new ModelFrame[info.numFrames]; + + for(i = 0, frame = frames; i < info.numFrames; i++, frame++) + { + dmd_packedFrame_t *pfr = (dmd_packedFrame_t *) (temp + info.frameSize * i); + dmd_packedVertex_t *pVtx; + + memcpy(frame->name, pfr->name, sizeof(pfr->name)); + frame->vertices = new FModelVertex[info.numVertices]; + frame->normals = new FModelVertex[info.numVertices]; + + // Translate each vertex. + for(k = 0, pVtx = pfr->vertices; k < info.numVertices; k++, pVtx++) + { + UnpackVector(USHORT(pVtx->normal), frame->normals[k].xyz); + for(c = 0; c < 3; c++) + { + frame->vertices[k].xyz[axis[c]] = + (pVtx->vertex[c] * FLOAT(pfr->scale[c]) + FLOAT(pfr->translate[c])); + } + // Aspect undo. + frame->vertices[k].xyz[VZ] *= rModelAspectMod; + } + } + + memcpy(lodInfo, buffer+info.offsetLODs, info.numLODs * sizeof(DMDLoDInfo)); + for(i = 0; i < info.numLODs; i++) + { + lodInfo[i].numTriangles = LittleLong(lodInfo[i].numTriangles); + lodInfo[i].numGlCommands = LittleLong(lodInfo[i].numGlCommands); + lodInfo[i].offsetTriangles = LittleLong(lodInfo[i].offsetTriangles); + lodInfo[i].offsetGlCommands = LittleLong(lodInfo[i].offsetGlCommands); + + triangles[i] = (FTriangle*)(buffer + lodInfo[i].offsetTriangles); + + lods[i].glCommands = new int[lodInfo[i].numGlCommands]; + memcpy(lods[i].glCommands, buffer + lodInfo[i].offsetGlCommands, sizeof(int) * lodInfo[i].numGlCommands); + } + + // Determine vertex usage at each LOD level. + vertexUsage = new char[info.numVertices]; + memset(vertexUsage, 0, info.numVertices); + + for(i = 0; i < info.numLODs; i++) + for(k = 0; k < lodInfo[i].numTriangles; k++) + for(c = 0; c < 3; c++) + vertexUsage[SHORT(triangles[i][k].vertexIndices[c])] |= 1 << i; + + loaded=true; + return true; +} + + +FDMDModel::~FDMDModel() +{ + int i; + + // clean up + if (skins != NULL) + { + for (i=0;i 0 ? GL_TRIANGLE_STRIP : GL_TRIANGLE_FAN); + count = abs(count); + + while(count--) + { + v = (FGLCommandVertex *) pos; + pos += sizeof(FGLCommandVertex); + + gl.TexCoord2fv(&v->s); + gl.Vertex3fv((float*)&vertices[v->index]); + } + + gl.End(); + } +} + + +void FDMDModel::RenderFrame(FTexture * skin, int frameno, int cm, int translation) +{ + int activeLod; + + if (frameno>=info.numFrames) return; + + ModelFrame * frame = &frames[frameno]; + //int mainFlags = mf->flags; + + if (!skin) + { + if (info.numSkins==0) return; + skin = skins[0]; + if (!skin) return; + } + + FMaterial * tex = FMaterial::ValidateTexture(skin); + + tex->Bind(cm, 0, translation); + + int numVerts = info.numVertices; + + // Determine the suitable LOD. + /* + if(info.numLODs > 1 && rend_model_lod != 0) + { + float lodFactor = rend_model_lod * screen->Width() / 640.0f / (GLRenderer->mCurrentFoV / 90.0f); + if(lodFactor) lodFactor = 1 / lodFactor; + + // Determine the LOD we will be using. + activeLod = (int) (lodFactor * spr->distance); + if(activeLod < 0) activeLod = 0; + if(activeLod >= mdl->info.numLODs) activeLod = mdl->info.numLODs - 1; + vertexUsage = mdl->vertexUsage; + } + else + */ + { + activeLod = 0; + } + + RenderGLCommands(lods[activeLod].glCommands, numVerts, frame->vertices/*, modelColors, NULL*/); +} + +void FDMDModel::RenderFrameInterpolated(FTexture * skin, int frameno, int frameno2, double inter, int cm, int translation) +{ + int activeLod = 0; + + if (frameno>=info.numFrames || frameno2>=info.numFrames) return; + + FModelVertex *vertices1 = frames[frameno].vertices; + FModelVertex *vertices2 = frames[frameno2].vertices; + + if (!skin) + { + if (info.numSkins==0) return; + skin = skins[0]; + if (!skin) return; + } + + FMaterial * tex = FMaterial::ValidateTexture(skin); + + tex->Bind(cm, 0, translation); + + int numVerts = info.numVertices; + + // [BB] Calculate the interpolated vertices by linear interpolation. + FModelVertex *verticesInterpolated = new FModelVertex[numVerts]; + for( int k = 0; k < numVerts; k++ ) + { + for ( int i = 0; i < 3; i++ ) + verticesInterpolated[k].xyz[i] = (1-inter)*vertices1[k].xyz[i]+ (inter)*vertices2[k].xyz[i]; + } + + RenderGLCommands(lods[activeLod].glCommands, numVerts, verticesInterpolated/*, modelColors, NULL*/); + delete[] verticesInterpolated; +} + + +//=========================================================================== +// +// FMD2Model::Load +// +//=========================================================================== + +bool FMD2Model::Load(const char * path, int, const char * buffer, int length) +{ + // Internal data structures of MD2 files - only used during loading! + struct md2_header_t + { + int magic; + int version; + int skinWidth; + int skinHeight; + int frameSize; + int numSkins; + int numVertices; + int numTexCoords; + int numTriangles; + int numGlCommands; + int numFrames; + int offsetSkins; + int offsetTexCoords; + int offsetTriangles; + int offsetFrames; + int offsetGlCommands; + int offsetEnd; + } ; + + struct md2_triangleVertex_t + { + byte vertex[3]; + byte lightNormalIndex; + }; + + struct md2_packedFrame_t + { + float scale[3]; + float translate[3]; + char name[16]; + md2_triangleVertex_t vertices[1]; + }; + + md2_header_t * md2header = (md2_header_t *)buffer; + ModelFrame *frame; + byte *md2_frames; + int i, k, c; + int axis[3] = { VX, VY, VZ }; + + // Convert it to DMD. + header.magic = MD2_MAGIC; + header.version = 8; + header.flags = 0; + vertexUsage = NULL; + info.skinWidth = LittleLong(md2header->skinWidth); + info.skinHeight = LittleLong(md2header->skinHeight); + info.frameSize = LittleLong(md2header->frameSize); + info.numLODs = 1; + info.numSkins = LittleLong(md2header->numSkins); + info.numTexCoords = LittleLong(md2header->numTexCoords); + info.numVertices = LittleLong(md2header->numVertices); + info.numFrames = LittleLong(md2header->numFrames); + info.offsetSkins = LittleLong(md2header->offsetSkins); + info.offsetTexCoords = LittleLong(md2header->offsetTexCoords); + info.offsetFrames = LittleLong(md2header->offsetFrames); + info.offsetLODs = LittleLong(md2header->offsetEnd); // Doesn't exist. + lodInfo[0].numTriangles = LittleLong(md2header->numTriangles); + lodInfo[0].numGlCommands = LittleLong(md2header->numGlCommands); + lodInfo[0].offsetTriangles = LittleLong(md2header->offsetTriangles); + lodInfo[0].offsetGlCommands = LittleLong(md2header->offsetGlCommands); + info.offsetEnd = LittleLong(md2header->offsetEnd); + + if (info.offsetFrames + info.frameSize * info.numFrames > length) + { + Printf("LoadModel: Model '%s' file too short\n", path); + return false; + } + if (lodInfo[0].numGlCommands <= 0) + { + Printf("LoadModel: Model '%s' invalid NumGLCommands\n", path); + return false; + } + + // The frames need to be unpacked. + md2_frames = (byte*)buffer + info.offsetFrames; + + frames = new ModelFrame[info.numFrames]; + + for(i = 0, frame = frames; i < info.numFrames; i++, frame++) + { + md2_packedFrame_t *pfr = (md2_packedFrame_t *) (md2_frames + info.frameSize * i); + md2_triangleVertex_t *pVtx; + + memcpy(frame->name, pfr->name, sizeof(pfr->name)); + frame->vertices = new FModelVertex[info.numVertices]; + frame->normals = new FModelVertex[info.numVertices]; + + // Translate each vertex. + for(k = 0, pVtx = pfr->vertices; k < info.numVertices; k++, pVtx++) + { + memcpy(frame->normals[k].xyz, + avertexnormals[pVtx->lightNormalIndex], sizeof(float) * 3); + + for(c = 0; c < 3; c++) + { + frame->vertices[k].xyz[axis[c]] = + (pVtx->vertex[c] * pfr->scale[c] + pfr->translate[c]); + } + // Aspect ratio adjustment (1.33 -> 1.6.) + frame->vertices[k].xyz[VZ] *= rModelAspectMod; + } + } + + + lods[0].glCommands = new int[lodInfo[0].numGlCommands]; + memcpy(lods[0].glCommands, buffer + lodInfo[0].offsetGlCommands, sizeof(int) * lodInfo[0].numGlCommands); + + skins = new FTexture *[info.numSkins]; + + for(i = 0; i < info.numSkins; i++) + { + skins[i] = LoadSkin(path, buffer + info.offsetSkins + i*64); + } + loaded=true; + return true; +} + +FMD2Model::~FMD2Model() +{ +} + diff --git a/src/gl/models/gl_models_md3.cpp b/src/gl/models/gl_models_md3.cpp new file mode 100644 index 000000000..72bf6d655 --- /dev/null +++ b/src/gl/models/gl_models_md3.cpp @@ -0,0 +1,313 @@ +/* +** gl_models_md3.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2006 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** +** 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 "w_wad.h" +#include "cmdlib.h" +#include "sc_man.h" +#include "m_crc32.h" + +#include "gl/renderer/gl_renderstate.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/models/gl_models.h" +#include "gl/textures/gl_material.h" +#include "gl/shaders/gl_shader.h" + +#define MAX_QPATH 64 + +static void UnpackVector(unsigned short packed, float & nx, float & ny, float & nz) +{ + // decode the lat/lng normal to a 3 float normal + double lat = ( packed >> 8 ) & 0xff; + double lng = ( packed & 0xff ); + lat *= PI/128; + lng *= PI/128; + + nx = cos(lat) * sin(lng); + ny = sin(lat) * sin(lng); + nz = cos(lng); +} + + + +bool FMD3Model::Load(const char * path, int, const char * buffer, int length) +{ + #pragma pack(4) + struct md3_header_t + { + DWORD Magic; + DWORD Version; + char Name[MAX_QPATH]; + DWORD Flags; + DWORD Num_Frames; + DWORD Num_Tags; + DWORD Num_Surfaces; + DWORD Num_Skins; + DWORD Ofs_Frames; + DWORD Ofs_Tags; + DWORD Ofs_Surfaces; + DWORD Ofs_Eof; + }; + + struct md3_surface_t + { + DWORD Magic; + char Name[MAX_QPATH]; + DWORD Flags; + DWORD Num_Frames; + DWORD Num_Shaders; + DWORD Num_Verts; + DWORD Num_Triangles; + DWORD Ofs_Triangles; + DWORD Ofs_Shaders; + DWORD Ofs_Texcoord; + DWORD Ofs_XYZNormal; + DWORD Ofs_End; + }; + + struct md3_triangle_t + { + DWORD vt_index[3]; + }; + + struct md3_shader_t + { + char Name[MAX_QPATH]; + DWORD index; + }; + + struct md3_texcoord_t + { + float s,t; + }; + + struct md3_vertex_t + { + short x,y,z,n; + }; + + struct md3_frame_t + { + float min_Bounds[3]; + float max_Bounds[3]; + float localorigin[3]; + float radius; + char Name[16]; + }; + #pragma pack() + + md3_header_t * hdr=(md3_header_t *)buffer; + + numFrames = LittleLong(hdr->Num_Frames); + numTags = LittleLong(hdr->Num_Tags); + numSurfaces = LittleLong(hdr->Num_Surfaces); + + md3_frame_t * frm = (md3_frame_t*)(buffer + LittleLong(hdr->Ofs_Frames)); + + frames = new MD3Frame[numFrames]; + for(int i=0;iOfs_Surfaces)); + + surfaces = new MD3Surface[numSurfaces]; + for(int i=0;iOfs_End)); + + s->numSkins = LittleLong(ss->Num_Shaders); + s->numTriangles = LittleLong(ss->Num_Triangles); + s->numVertices = LittleLong(ss->Num_Verts); + + // copy triangle indices + md3_triangle_t * tris = (md3_triangle_t*)(((char*)ss)+LittleLong(ss->Ofs_Triangles)); + s->tris = new MD3Triangle[s->numTriangles]; + + for(int i=0;inumTriangles;i++) for (int j=0;j<3;j++) + { + s->tris[i].VertIndex[j]=LittleLong(tris[i].vt_index[j]); + } + + // copy shaders (skins) + md3_shader_t * shader = (md3_shader_t*)(((char*)ss)+LittleLong(ss->Ofs_Shaders)); + s->skins = new FTexture *[s->numSkins]; + + for(int i=0;inumSkins;i++) + { + // [BB] According to the MD3 spec, Name is supposed to include the full path. + s->skins[i] = LoadSkin("", shader[i].Name); + // [BB] Fall back and check if Name is relative. + if ( s->skins[i] == NULL ) + s->skins[i] = LoadSkin(path, shader[i].Name); + } + + // Load texture coordinates + md3_texcoord_t * tc = (md3_texcoord_t*)(((char*)ss)+LittleLong(ss->Ofs_Texcoord)); + s->texcoords = new MD3TexCoord[s->numVertices]; + + for(int i=0;inumVertices;i++) + { + s->texcoords[i].s = tc[i].s; + s->texcoords[i].t = tc[i].t; + } + + // Load vertices and texture coordinates + md3_vertex_t * vt = (md3_vertex_t*)(((char*)ss)+LittleLong(ss->Ofs_XYZNormal)); + s->vertices = new MD3Vertex[s->numVertices * numFrames]; + + for(int i=0;inumVertices * numFrames;i++) + { + s->vertices[i].x = LittleShort(vt[i].x)/64.f; + s->vertices[i].y = LittleShort(vt[i].y)/64.f; + s->vertices[i].z = LittleShort(vt[i].z)/64.f * rModelAspectMod; + UnpackVector( LittleShort(vt[i].n), s->vertices[i].nx, s->vertices[i].ny, s->vertices[i].nz); + } + } + return true; +} + +int FMD3Model::FindFrame(const char * name) +{ + for (int i=0;inumTriangles;i++) + { + for(int j=0;j<3;j++) + { + int x = surf->tris[i].VertIndex[j]; + + gl.TexCoord2fv(&surf->texcoords[x].s); + gl.Vertex3f(vert[x].x, vert[x].z, vert[x].y); + } + } + gl.End(); +} + +void FMD3Model::RenderFrame(FTexture * skin, int frameno, int cm, int translation) +{ + if (frameno>=numFrames) return; + + MD3Frame * frame = &frames[frameno]; + + // I can't confirm correctness of this because no model I have tested uses this information + // gl.MatrixMode(GL_MODELVIEW); + // gl.Translatef(frame->origin[0], frame->origin[1], frame->origin[2]); + + for(int i=0;inumSkins==0) return; + surfaceSkin = surf->skins[0]; + if (!surfaceSkin) return; + } + + FMaterial * tex = FMaterial::ValidateTexture(surfaceSkin); + + tex->Bind(cm, 0, translation); + RenderTriangles(surf, surf->vertices + frameno * surf->numVertices); + } +} + +void FMD3Model::RenderFrameInterpolated(FTexture * skin, int frameno, int frameno2, double inter, int cm, int translation) +{ + if (frameno>=numFrames || frameno2>=numFrames) return; + + for(int i=0;inumSkins==0) return; + surfaceSkin = surf->skins[0]; + if (!surfaceSkin) return; + } + + FMaterial * tex = FMaterial::ValidateTexture(surfaceSkin); + + tex->Bind(cm, 0, translation); + + MD3Vertex* verticesInterpolated = new MD3Vertex[surfaces[i].numVertices]; + MD3Vertex* vertices1 = surf->vertices + frameno * surf->numVertices; + MD3Vertex* vertices2 = surf->vertices + frameno2 * surf->numVertices; + + // [BB] Calculate the interpolated vertices by linear interpolation. + for( int k = 0; k < surf->numVertices; k++ ) + { + verticesInterpolated[k].x = (1-inter)*vertices1[k].x+ (inter)*vertices2[k].x; + verticesInterpolated[k].y = (1-inter)*vertices1[k].y+ (inter)*vertices2[k].y; + verticesInterpolated[k].z = (1-inter)*vertices1[k].z+ (inter)*vertices2[k].z; + // [BB] Apparently RenderTriangles doesn't use nx, ny, nz, so don't interpolate them. + } + + RenderTriangles(surf, verticesInterpolated); + + delete[] verticesInterpolated; + } +} + +FMD3Model::~FMD3Model() +{ + if (frames) delete [] frames; + if (surfaces) delete [] surfaces; + frames = NULL; + surfaces = NULL; +} diff --git a/src/gl/models/gl_voxels.cpp b/src/gl/models/gl_voxels.cpp new file mode 100644 index 000000000..450c19d64 --- /dev/null +++ b/src/gl/models/gl_voxels.cpp @@ -0,0 +1,504 @@ +/* +** gl_voxels.cpp +** +** Voxel management +** +**--------------------------------------------------------------------------- +** Copyright 2010 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** +** 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 "w_wad.h" +#include "cmdlib.h" +#include "sc_man.h" +#include "m_crc32.h" +#include "c_console.h" +#include "g_game.h" +#include "doomstat.h" +#include "g_level.h" +#include "textures/bitmap.h" +//#include "gl/gl_intern.h" + +#include "gl/renderer/gl_renderer.h" +#include "gl/data/gl_vertexbuffer.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/models/gl_models.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_geometric.h" +#include "gl/utility/gl_convert.h" +#include "gl/renderer/gl_renderstate.h" + + +//=========================================================================== +// +// Creates a 16x16 texture from the palette so that we can +// use the existing palette manipulation code to render the voxel +// Otherwise all shaders had to be duplicated and the non-shader code +// would be a lot less efficient. +// +//=========================================================================== + +class FVoxelTexture : public FTexture +{ +public: + + FVoxelTexture(FVoxel *voxel); + ~FVoxelTexture(); + const BYTE *GetColumn (unsigned int column, const Span **spans_out); + const BYTE *GetPixels (); + void Unload (); + + int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf); + bool UseBasePalette() { return false; } + +protected: + FVoxel *SourceVox; + +}; + +//=========================================================================== +// +// +// +//=========================================================================== + +FVoxelTexture::FVoxelTexture(FVoxel *vox) +{ + SourceVox = vox; + Width = 16; + Height = 16; + WidthBits = 4; + HeightBits = 4; + WidthMask = 15; + gl_info.bNoFilter = true; + gl_info.bNoCompress = true; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +FVoxelTexture::~FVoxelTexture() +{ +} + +const BYTE *FVoxelTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + // not needed + return NULL; +} + +const BYTE *FVoxelTexture::GetPixels () +{ + // not needed + return NULL; +} + +void FVoxelTexture::Unload () +{ +} + +//=========================================================================== +// +// FVoxelTexture::CopyTrueColorPixels +// +// This creates a dummy 16x16 paletted bitmap and converts that using the +// voxel palette +// +//=========================================================================== + +int FVoxelTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) +{ + PalEntry pe[256]; + BYTE bitmap[256]; + BYTE *pp = SourceVox->Palette; + + for(int i=0;i<256;i++, pp+=3) + { + bitmap[i] = (BYTE)i; + pe[i].r = (pp[0] << 2) | (pp[0] >> 4); + pe[i].g = (pp[1] << 2) | (pp[1] >> 4); + pe[i].b = (pp[2] << 2) | (pp[2] >> 4); + pe[i].a = 255; + } + + bmp->CopyPixelData(x, y, bitmap, Width, Height, 1, 16, rotate, pe, inf); + return 0; +} + +//=========================================================================== +// +// +// +//=========================================================================== + + +class FVoxelVertexBuffer : public FVertexBuffer +{ + unsigned int ibo_id; + bool isint; + +public: + FVoxelVertexBuffer(TArray &verts, TArray &indices); + ~FVoxelVertexBuffer(); + void BindVBO(); + bool IsInt() const { return isint; } +}; + + +//=========================================================================== +// +// +// +//=========================================================================== + +FVoxelVertexBuffer::FVoxelVertexBuffer(TArray &verts, TArray &indices) +{ + ibo_id = 0; + if (gl.flags&RFL_VBO) + { + gl.GenBuffers(1, &ibo_id); + + gl.BindBuffer(GL_ARRAY_BUFFER, vbo_id); + gl.BufferData(GL_ARRAY_BUFFER, verts.Size() * sizeof(FVoxelVertex), &verts[0], GL_STATIC_DRAW); + gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id); + if (verts.Size() > 65535) + { + gl.BufferData(GL_ELEMENT_ARRAY_BUFFER, indices.Size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW); + isint = true; + } + else + { + unsigned short *sbuffer = new unsigned short[indices.Size()]; + for(unsigned i=0;ix); + glTexCoordPointer(2,GL_FLOAT, sizeof(FVoxelVertex), &VVO->u); + gl.EnableClientState(GL_VERTEX_ARRAY); + gl.EnableClientState(GL_TEXTURE_COORD_ARRAY); + gl.EnableClientState(GL_INDEX_ARRAY); +} + + + +//=========================================================================== +// +// +// +//=========================================================================== + +FVoxelModel::FVoxelModel(FVoxel *voxel, bool owned) +{ + mVoxel = voxel; + mOwningVoxel = owned; + mVBO = NULL; + mPalette = new FVoxelTexture(voxel); + Initialize(); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +FVoxelModel::~FVoxelModel() +{ + CleanGLData(); + delete mPalette; + if (mOwningVoxel) delete mVoxel; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FVoxelModel::AddVertex(FVoxelVertex &vert, FVoxelMap &check) +{ + unsigned int index = check[vert]; + if (index == 0xffffffff) + { + index = check[vert] =mVertices.Push(vert); + } + mIndices.Push(index); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FVoxelModel::AddFace(int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3, int x4, int y4, int z4, BYTE col, FVoxelMap &check) +{ + float PivotX = mVoxel->Mips[0].PivotX / 256.f; + float PivotY = mVoxel->Mips[0].PivotY / 256.f; + float PivotZ = mVoxel->Mips[0].PivotZ / 256.f; + int h = mVoxel->Mips[0].SizeZ; + FVoxelVertex vert; + + + vert.u = (((col & 15) * 255 / 16) + 7) / 255.f; + vert.v = (((col / 16) * 255 / 16) + 7) / 255.f; + + vert.x = x1 - PivotX; + vert.z = -y1 + PivotY; + vert.y = -z1 + PivotZ; + AddVertex(vert, check); + + vert.x = x2 - PivotX; + vert.z = -y2 + PivotY; + vert.y = -z2 + PivotZ; + AddVertex(vert, check); + + vert.x = x4 - PivotX; + vert.z = -y4 + PivotY; + vert.y = -z4 + PivotZ; + AddVertex(vert, check); + + vert.x = x3 - PivotX; + vert.z = -y3 + PivotY; + vert.y = -z3 + PivotZ; + AddVertex(vert, check); + +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FVoxelModel::MakeSlabPolys(int x, int y, kvxslab_t *voxptr, FVoxelMap &check) +{ + const BYTE *col = voxptr->col; + int zleng = voxptr->zleng; + int ztop = voxptr->ztop; + int cull = voxptr->backfacecull; + + if (cull & 16) + { + AddFace(x, y, ztop, x+1, y, ztop, x, y+1, ztop, x+1, y+1, ztop, *col, check); + } + int z = ztop; + while (z < ztop+zleng) + { + int c = 0; + while (z+c < ztop+zleng && col[c] == col[0]) c++; + + if (cull & 1) + { + AddFace(x, y, z, x, y+1, z, x, y, z+c, x, y+1, z+c, *col, check); + } + if (cull & 2) + { + AddFace(x+1, y+1, z, x+1, y, z, x+1, y+1, z+c, x+1, y, z+c, *col, check); + } + if (cull & 4) + { + AddFace(x, y, z, x+1, y, z, x, y, z+c, x+1, y, z+c, *col, check); + } + if (cull & 8) + { + AddFace(x+1, y+1, z, x, y+1, z, x+1, y+1, z+c, x, y+1, z+c, *col, check); + } + z+=c; + col+=c; + } + if (cull & 32) + { + int z = ztop+zleng-1; + AddFace(x+1, y, z+1, x, y, z+1, x+1, y+1, z+1, x, y+1, z+1, voxptr->col[zleng-1], check); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FVoxelModel::Initialize() +{ + FVoxelMap check; + FVoxelMipLevel *mip = &mVoxel->Mips[0]; + for (int x = 0; x < mip->SizeX; x++) + { + BYTE *slabxoffs = &mip->SlabData[mip->OffsetX[x]]; + short *xyoffs = &mip->OffsetXY[x * (mip->SizeY + 1)]; + for (int y = 0; y < mip->SizeY; y++) + { + kvxslab_t *voxptr = (kvxslab_t *)(slabxoffs + xyoffs[y]); + kvxslab_t *voxend = (kvxslab_t *)(slabxoffs + xyoffs[y+1]); + for (; voxptr < voxend; voxptr = (kvxslab_t *)((BYTE *)voxptr + voxptr->zleng + 3)) + { + MakeSlabPolys(x, y, voxptr, check); + } + } + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +bool FVoxelModel::Load(const char * fn, int lumpnum, const char * buffer, int length) +{ + return false; // not needed +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FVoxelModel::MakeGLData() +{ + if (gl.flags&RFL_VBO) + { + mVBO = new FVoxelVertexBuffer(mVertices, mIndices); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FVoxelModel::CleanGLData() +{ + if (mVBO != NULL) + { + delete mVBO; + mVBO = NULL; + } +} + +//=========================================================================== +// +// Voxels don't have frames so always return 0 +// +//=========================================================================== + +int FVoxelModel::FindFrame(const char * name) +{ + return 0; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FVoxelModel::RenderFrame(FTexture * skin, int frame, int cm, int translation) +{ + FMaterial * tex = FMaterial::ValidateTexture(skin); + tex->Bind(cm, 0, translation); + gl_RenderState.Apply(); + + if (gl.flags&RFL_VBO) + { + if (mVBO == NULL) MakeGLData(); + if (mVBO != NULL) + { + mVBO->BindVBO(); + glDrawElements(GL_QUADS, mIndices.Size(), mVBO->IsInt()? GL_UNSIGNED_INT:GL_UNSIGNED_SHORT, 0); + GLRenderer->mVBO->BindVBO(); + return; + } + } + + gl.Begin(GL_QUADS); + for(unsigned i=0;i < mIndices.Size(); i++) + { + FVoxelVertex *vert = &mVertices[mIndices[i]]; + gl.TexCoord2fv(&vert->u); + gl.Vertex3fv(&vert->x); + } + gl.End(); +} + +//=========================================================================== +// +// Voxels never interpolate between frames +// +//=========================================================================== + +void FVoxelModel::RenderFrameInterpolated(FTexture * skin, int frame, int frame2, double inter, int cm, int translation) +{ + RenderFrame(skin, frame, cm, translation); +} + diff --git a/src/gl/models/tab_anorms.h b/src/gl/models/tab_anorms.h new file mode 100644 index 000000000..46467b460 --- /dev/null +++ b/src/gl/models/tab_anorms.h @@ -0,0 +1,489 @@ +#ifdef WIN32 +#pragma warning(disable:4305) +#endif + +{ +-0.525731, 0.000000, 0.850651}, + +{ +-0.442863, 0.238856, 0.864188}, + +{ +-0.295242, 0.000000, 0.955423}, + +{ +-0.309017, 0.500000, 0.809017}, + +{ +-0.162460, 0.262866, 0.951056}, + +{ +0.000000, 0.000000, 1.000000}, + +{ +0.000000, 0.850651, 0.525731}, + +{ +-0.147621, 0.716567, 0.681718}, + +{ +0.147621, 0.716567, 0.681718}, + +{ +0.000000, 0.525731, 0.850651}, + +{ +0.309017, 0.500000, 0.809017}, + +{ +0.525731, 0.000000, 0.850651}, + +{ +0.295242, 0.000000, 0.955423}, + +{ +0.442863, 0.238856, 0.864188}, + +{ +0.162460, 0.262866, 0.951056}, + +{ +-0.681718, 0.147621, 0.716567}, + +{ +-0.809017, 0.309017, 0.500000}, + +{ +-0.587785, 0.425325, 0.688191}, + +{ +-0.850651, 0.525731, 0.000000}, + +{ +-0.864188, 0.442863, 0.238856}, + +{ +-0.716567, 0.681718, 0.147621}, + +{ +-0.688191, 0.587785, 0.425325}, + +{ +-0.500000, 0.809017, 0.309017}, + +{ +-0.238856, 0.864188, 0.442863}, + +{ +-0.425325, 0.688191, 0.587785}, + +{ +-0.716567, 0.681718, -0.147621}, + +{ +-0.500000, 0.809017, -0.309017}, + +{ +-0.525731, 0.850651, 0.000000}, + +{ +0.000000, 0.850651, -0.525731}, + +{ +-0.238856, 0.864188, -0.442863}, + +{ +0.000000, 0.955423, -0.295242}, + +{ +-0.262866, 0.951056, -0.162460}, + +{ +0.000000, 1.000000, 0.000000}, + +{ +0.000000, 0.955423, 0.295242}, + +{ +-0.262866, 0.951056, 0.162460}, + +{ +0.238856, 0.864188, 0.442863}, + +{ +0.262866, 0.951056, 0.162460}, + +{ +0.500000, 0.809017, 0.309017}, + +{ +0.238856, 0.864188, -0.442863}, + +{ +0.262866, 0.951056, -0.162460}, + +{ +0.500000, 0.809017, -0.309017}, + +{ +0.850651, 0.525731, 0.000000}, + +{ +0.716567, 0.681718, 0.147621}, + +{ +0.716567, 0.681718, -0.147621}, + +{ +0.525731, 0.850651, 0.000000}, + +{ +0.425325, 0.688191, 0.587785}, + +{ +0.864188, 0.442863, 0.238856}, + +{ +0.688191, 0.587785, 0.425325}, + +{ +0.809017, 0.309017, 0.500000}, + +{ +0.681718, 0.147621, 0.716567}, + +{ +0.587785, 0.425325, 0.688191}, + +{ +0.955423, 0.295242, 0.000000}, + +{ +1.000000, 0.000000, 0.000000}, + +{ +0.951056, 0.162460, 0.262866}, + +{ +0.850651, -0.525731, 0.000000}, + +{ +0.955423, -0.295242, 0.000000}, + +{ +0.864188, -0.442863, 0.238856}, + +{ +0.951056, -0.162460, 0.262866}, + +{ +0.809017, -0.309017, 0.500000}, + +{ +0.681718, -0.147621, 0.716567}, + +{ +0.850651, 0.000000, 0.525731}, + +{ +0.864188, 0.442863, -0.238856}, + +{ +0.809017, 0.309017, -0.500000}, + +{ +0.951056, 0.162460, -0.262866}, + +{ +0.525731, 0.000000, -0.850651}, + +{ +0.681718, 0.147621, -0.716567}, + +{ +0.681718, -0.147621, -0.716567}, + +{ +0.850651, 0.000000, -0.525731}, + +{ +0.809017, -0.309017, -0.500000}, + +{ +0.864188, -0.442863, -0.238856}, + +{ +0.951056, -0.162460, -0.262866}, + +{ +0.147621, 0.716567, -0.681718}, + +{ +0.309017, 0.500000, -0.809017}, + +{ +0.425325, 0.688191, -0.587785}, + +{ +0.442863, 0.238856, -0.864188}, + +{ +0.587785, 0.425325, -0.688191}, + +{ +0.688191, 0.587785, -0.425325}, + +{ +-0.147621, 0.716567, -0.681718}, + +{ +-0.309017, 0.500000, -0.809017}, + +{ +0.000000, 0.525731, -0.850651}, + +{ +-0.525731, 0.000000, -0.850651}, + +{ +-0.442863, 0.238856, -0.864188}, + +{ +-0.295242, 0.000000, -0.955423}, + +{ +-0.162460, 0.262866, -0.951056}, + +{ +0.000000, 0.000000, -1.000000}, + +{ +0.295242, 0.000000, -0.955423}, + +{ +0.162460, 0.262866, -0.951056}, + +{ +-0.442863, -0.238856, -0.864188}, + +{ +-0.309017, -0.500000, -0.809017}, + +{ +-0.162460, -0.262866, -0.951056}, + +{ +0.000000, -0.850651, -0.525731}, + +{ +-0.147621, -0.716567, -0.681718}, + +{ +0.147621, -0.716567, -0.681718}, + +{ +0.000000, -0.525731, -0.850651}, + +{ +0.309017, -0.500000, -0.809017}, + +{ +0.442863, -0.238856, -0.864188}, + +{ +0.162460, -0.262866, -0.951056}, + +{ +0.238856, -0.864188, -0.442863}, + +{ +0.500000, -0.809017, -0.309017}, + +{ +0.425325, -0.688191, -0.587785}, + +{ +0.716567, -0.681718, -0.147621}, + +{ +0.688191, -0.587785, -0.425325}, + +{ +0.587785, -0.425325, -0.688191}, + +{ +0.000000, -0.955423, -0.295242}, + +{ +0.000000, -1.000000, 0.000000}, + +{ +0.262866, -0.951056, -0.162460}, + +{ +0.000000, -0.850651, 0.525731}, + +{ +0.000000, -0.955423, 0.295242}, + +{ +0.238856, -0.864188, 0.442863}, + +{ +0.262866, -0.951056, 0.162460}, + +{ +0.500000, -0.809017, 0.309017}, + +{ +0.716567, -0.681718, 0.147621}, + +{ +0.525731, -0.850651, 0.000000}, + +{ +-0.238856, -0.864188, -0.442863}, + +{ +-0.500000, -0.809017, -0.309017}, + +{ +-0.262866, -0.951056, -0.162460}, + +{ +-0.850651, -0.525731, 0.000000}, + +{ +-0.716567, -0.681718, -0.147621}, + +{ +-0.716567, -0.681718, 0.147621}, + +{ +-0.525731, -0.850651, 0.000000}, + +{ +-0.500000, -0.809017, 0.309017}, + +{ +-0.238856, -0.864188, 0.442863}, + +{ +-0.262866, -0.951056, 0.162460}, + +{ +-0.864188, -0.442863, 0.238856}, + +{ +-0.809017, -0.309017, 0.500000}, + +{ +-0.688191, -0.587785, 0.425325}, + +{ +-0.681718, -0.147621, 0.716567}, + +{ +-0.442863, -0.238856, 0.864188}, + +{ +-0.587785, -0.425325, 0.688191}, + +{ +-0.309017, -0.500000, 0.809017}, + +{ +-0.147621, -0.716567, 0.681718}, + +{ +-0.425325, -0.688191, 0.587785}, + +{ +-0.162460, -0.262866, 0.951056}, + +{ +0.442863, -0.238856, 0.864188}, + +{ +0.162460, -0.262866, 0.951056}, + +{ +0.309017, -0.500000, 0.809017}, + +{ +0.147621, -0.716567, 0.681718}, + +{ +0.000000, -0.525731, 0.850651}, + +{ +0.425325, -0.688191, 0.587785}, + +{ +0.587785, -0.425325, 0.688191}, + +{ +0.688191, -0.587785, 0.425325}, + +{ +-0.955423, 0.295242, 0.000000}, + +{ +-0.951056, 0.162460, 0.262866}, + +{ +-1.000000, 0.000000, 0.000000}, + +{ +-0.850651, 0.000000, 0.525731}, + +{ +-0.955423, -0.295242, 0.000000}, + +{ +-0.951056, -0.162460, 0.262866}, + +{ +-0.864188, 0.442863, -0.238856}, + +{ +-0.951056, 0.162460, -0.262866}, + +{ +-0.809017, 0.309017, -0.500000}, + +{ +-0.864188, -0.442863, -0.238856}, + +{ +-0.951056, -0.162460, -0.262866}, + +{ +-0.809017, -0.309017, -0.500000}, + +{ +-0.681718, 0.147621, -0.716567}, + +{ +-0.681718, -0.147621, -0.716567}, + +{ +-0.850651, 0.000000, -0.525731}, + +{ +-0.688191, 0.587785, -0.425325}, + +{ +-0.587785, 0.425325, -0.688191}, + +{ +-0.425325, 0.688191, -0.587785}, + +{ +-0.425325, -0.688191, -0.587785}, + +{ +-0.587785, -0.425325, -0.688191}, + +{ +-0.688191, -0.587785, -0.425325}, diff --git a/src/gl/renderer/gl_colormap.h b/src/gl/renderer/gl_colormap.h new file mode 100644 index 000000000..a3f4cf83b --- /dev/null +++ b/src/gl/renderer/gl_colormap.h @@ -0,0 +1,79 @@ +#ifndef __GL_COLORMAP +#define __GL_COLORMAP + +#include "doomtype.h" +#include "v_palette.h" +#include "r_data/colormaps.h" + +extern DWORD gl_fixedcolormap; + + +enum EColorManipulation +{ + + CM_INVALID=-1, + CM_DEFAULT=0, // untranslated + CM_DESAT0=CM_DEFAULT, + CM_DESAT1, // minimum desaturation + CM_DESAT31=CM_DESAT1+30, // maximum desaturation = grayscale + CM_FIRSTSPECIALCOLORMAP, // first special fixed colormap + + // special internal values for texture creation + CM_SHADE= 0x10000002, // alpha channel texture + + // These are not to be passed to the texture manager + CM_LITE = 0x20000000, // special values to handle these items without excessive hacking + CM_TORCH= 0x20000010, // These are not real color manipulations +}; + +#define CM_MAXCOLORMAP int(CM_FIRSTSPECIALCOLORMAP + SpecialColormaps.Size()) + + // for internal use +struct FColormap +{ + PalEntry LightColor; // a is saturation (0 full, 31=b/w, other=custom colormap) + PalEntry FadeColor; // a is fadedensity>>1 + int colormap; + int blendfactor; + + void Clear() + { + LightColor=0xffffff; + FadeColor=0; + colormap = CM_DEFAULT; + blendfactor=0; + } + + void ClearColor() + { + LightColor.r=LightColor.g=LightColor.b=0xff; + blendfactor=0; + } + + + void GetFixedColormap() + { + Clear(); + colormap = gl_fixedcolormap >= CM_LITE? CM_DEFAULT : gl_fixedcolormap; + } + + FColormap & operator=(FDynamicColormap * from) + { + LightColor = from->Color; + colormap = from->Desaturate>>3; + FadeColor = from->Fade; + blendfactor = from->Color.a; + return * this; + } + + void CopyLightColor(FDynamicColormap * from) + { + LightColor = from->Color; + colormap = from->Desaturate>>3; + blendfactor = from->Color.a; + } +}; + + + +#endif diff --git a/src/gl/renderer/gl_lightdata.cpp b/src/gl/renderer/gl_lightdata.cpp new file mode 100644 index 000000000..9c77803fc --- /dev/null +++ b/src/gl/renderer/gl_lightdata.cpp @@ -0,0 +1,654 @@ +/* +** gl_light.cpp +** Light level / fog management / dynamic lights +** +**--------------------------------------------------------------------------- +** Copyright 2002-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "gl/system/gl_cvars.h" +#include "gl/data/gl_data.h" +#include "gl/renderer/gl_colormap.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/shaders/gl_shader.h" +#include "gl/scene/gl_portal.h" +#include "c_dispatch.h" +#include "p_local.h" +#include "g_level.h" +#include "r_sky.h" + +// externally settable lighting properties +static float distfogtable[2][256]; // light to fog conversion table for black fog +static PalEntry outsidefogcolor; +int fogdensity; +int outsidefogdensity; +int skyfog; + +CUSTOM_CVAR (Int, gl_light_ambient, 20, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + // ambient of 0 does not work correctly because light level 0 is special. + if (self < 1) self = 1; +} + +CVAR(Int, gl_weaponlight, 8, CVAR_ARCHIVE); +CVAR(Bool,gl_enhanced_nightvision,true,CVAR_ARCHIVE) +CVAR(Bool, gl_brightfog, false, CVAR_ARCHIVE); + + + +//========================================================================== +// +// +// +//========================================================================== + +bool gl_BrightmapsActive() +{ + return gl.shadermodel == 4 || (gl.shadermodel == 3 && gl_brightmap_shader); +} + +bool gl_GlowActive() +{ + return gl.shadermodel == 4 || (gl.shadermodel == 3 && gl_glow_shader); +} + +//========================================================================== +// +// Sets up the fog tables +// +//========================================================================== + +CUSTOM_CVAR (Int, gl_distfog, 70, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + for (int i=0;i<256;i++) + { + + if (i<164) + { + distfogtable[0][i]= (gl_distfog>>1) + (gl_distfog)*(164-i)/164; + } + else if (i<230) + { + distfogtable[0][i]= (gl_distfog>>1) - (gl_distfog>>1)*(i-164)/(230-164); + } + else distfogtable[0][i]=0; + + if (i<128) + { + distfogtable[1][i]= 6.f + (gl_distfog>>1) + (gl_distfog)*(128-i)/48; + } + else if (i<216) + { + distfogtable[1][i]= (216.f-i) / ((216.f-128.f)) * gl_distfog / 10; + } + else distfogtable[1][i]=0; + } +} + +CUSTOM_CVAR(Int,gl_fogmode,1,CVAR_ARCHIVE|CVAR_NOINITCALL) +{ + if (self>2) self=2; + if (self<0) self=0; + if (self == 2 && gl.shadermodel < 4) self = 1; +} + +CUSTOM_CVAR(Int, gl_lightmode, 3 ,CVAR_ARCHIVE|CVAR_NOINITCALL) +{ + int newself = self; + if (newself > 4) newself=8; // use 8 for software lighting to avoid conflicts with the bit mask + if (newself < 0) newself=0; + if ((newself == 2 || newself == 8) && gl.shadermodel < 4) newself = 3; + if (self != newself) self = newself; + glset.lightmode = newself; +} + + + + +//========================================================================== +// +// Sets render state to draw the given render style +// includes: Texture mode, blend equation and blend mode +// +//========================================================================== + +void gl_GetRenderStyle(FRenderStyle style, bool drawopaque, bool allowcolorblending, + int *tm, int *sb, int *db, int *be) +{ + static int blendstyles[] = { GL_ZERO, GL_ONE, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA }; + static int renderops[] = { 0, GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; + + int srcblend = blendstyles[style.SrcAlpha&3]; + int dstblend = blendstyles[style.DestAlpha&3]; + int blendequation = renderops[style.BlendOp&15]; + int texturemode = drawopaque? TM_OPAQUE : TM_MODULATE; + + if (style.Flags & STYLEF_ColorIsFixed) + { + texturemode = TM_MASK; + } + else if (style.Flags & STYLEF_InvertSource) + { + texturemode = drawopaque? TM_INVERTOPAQUE : TM_INVERT; + } + + if (blendequation == -1) + { + srcblend = GL_DST_COLOR; + dstblend = GL_ONE_MINUS_SRC_ALPHA; + blendequation = GL_FUNC_ADD; + } + + if (allowcolorblending && srcblend == GL_SRC_ALPHA && dstblend == GL_ONE && blendequation == GL_FUNC_ADD) + { + srcblend = GL_SRC_COLOR; + } + + *tm = texturemode; + *be = blendequation; + *sb = srcblend; + *db = dstblend; +} + + +//========================================================================== +// +// Set fog parameters for the level +// +//========================================================================== +void gl_SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog) +{ + fogdensity=_fogdensity; + outsidefogcolor=_outsidefogcolor; + outsidefogdensity=_outsidefogdensity; + skyfog=_skyfog; + + outsidefogdensity>>=1; + fogdensity>>=1; +} + + +//========================================================================== +// +// Get current light level +// +//========================================================================== + +int gl_CalcLightLevel(int lightlevel, int rellight, bool weapon) +{ + int light; + + if (lightlevel == 0) return 0; + + if ((glset.lightmode & 2) && lightlevel < 192 && !weapon) + { + light = xs_CRoundToInt(192.f - (192-lightlevel)* 1.95f); + } + else + { + light=lightlevel; + } + + if (light>=1; + } + return clamp(light+rellight, 0, 255); +} + +//========================================================================== +// +// Get current light color +// +//========================================================================== + +PalEntry gl_CalcLightColor(int light, PalEntry pe, int blendfactor, bool force) +{ + int r,g,b; + + if (glset.lightmode == 8 && !force) + { + return pe; + } + else if (blendfactor == 0) + { + r = pe.r * light / 255; + g = pe.g * light / 255; + b = pe.b * light / 255; + } + else + { + int mixlight = light * (255 - blendfactor); + + r = (mixlight + pe.r * blendfactor) / 255; + g = (mixlight + pe.g * blendfactor) / 255; + b = (mixlight + pe.b * blendfactor) / 255; + } + return PalEntry(BYTE(r), BYTE(g), BYTE(b)); +} + +//========================================================================== +// +// Get current light color +// +//========================================================================== +void gl_GetLightColor(int lightlevel, int rellight, const FColormap * cm, float * pred, float * pgreen, float * pblue, bool weapon) +{ + float & r=*pred,& g=*pgreen,& b=*pblue; + int torch=0; + + if (gl_fixedcolormap) + { + if (gl_fixedcolormap==CM_LITE) + { + if (gl_enhanced_nightvision) r=0.375f, g=1.0f, b=0.375f; + else r=g=b=1.0f; + } + else if (gl_fixedcolormap>=CM_TORCH) + { + int flicker=gl_fixedcolormap-CM_TORCH; + r=(0.8f+(7-flicker)/70.0f); + if (r>1.0f) r=1.0f; + b=g=r; + if (gl_enhanced_nightvision) b*=0.75f; + } + else r=g=b=1.0f; + return; + } + + PalEntry lightcolor = cm? cm->LightColor : PalEntry(255,255,255); + int blendfactor = cm? cm->blendfactor : 0; + + lightlevel = gl_CalcLightLevel(lightlevel, rellight, weapon); + PalEntry pe = gl_CalcLightColor(lightlevel, lightcolor, blendfactor); + r = pe.r/255.f; + g = pe.g/255.f; + b = pe.b/255.f; +} + +//========================================================================== +// +// set current light color +// +//========================================================================== +void gl_SetColor(int light, int rellight, const FColormap * cm, float *red, float *green, float *blue, PalEntry ThingColor, bool weapon) +{ + float r,g,b; + gl_GetLightColor(light, rellight, cm, &r, &g, &b, weapon); + + *red = r * ThingColor.r/255.0f; + *green = g * ThingColor.g/255.0f; + *blue = b * ThingColor.b/255.0f; +} + +//========================================================================== +// +// set current light color +// +//========================================================================== +void gl_SetColor(int light, int rellight, const FColormap * cm, float alpha, PalEntry ThingColor, bool weapon) +{ + float r,g,b; + + gl_GetLightColor(light, rellight, cm, &r, &g, &b, weapon); + + if (glset.lightmode != 8) + { + gl.Color4f(r * ThingColor.r/255.0f, g * ThingColor.g/255.0f, b * ThingColor.b/255.0f, alpha); + } + else + { + gl.Color4f(r, g, b, alpha); + + if (gl_fixedcolormap) + { + gl.VertexAttrib1f(VATTR_LIGHTLEVEL, 1.0); + } + else + { + float lightlevel = gl_CalcLightLevel(light, rellight, weapon) / 255.0f; + gl.VertexAttrib1f(VATTR_LIGHTLEVEL, lightlevel); + } + } +} + +//========================================================================== +// +// calculates the current fog density +// +// Rules for fog: +// +// 1. If bit 4 of gl_lightmode is set always use the level's fog density. +// This is what Legacy's GL render does. +// 2. black fog means no fog and always uses the distfogtable based on the level's fog density setting +// 3. If outside fog is defined and the current fog color is the same as the outside fog +// the engine always uses the outside fog density to make the fog uniform across the level. +// If the outside fog's density is undefined it uses the level's fog density and if that is +// not defined it uses a default of 70. +// 4. If a global fog density is specified it is being used for all fog on the level +// 5. If none of the above apply fog density is based on the light level as for the software renderer. +// +//========================================================================== + +float gl_GetFogDensity(int lightlevel, PalEntry fogcolor) +{ + float density; + + if (glset.lightmode&4) + { + // uses approximations of Legacy's default settings. + density = fogdensity? fogdensity : 18; + } + else if ((fogcolor.d & 0xffffff) == 0) + { + // case 1: black fog + if (glset.lightmode != 8) + { + density=distfogtable[glset.lightmode!=0][lightlevel]; + } + else + { + density = 0; + } + } + else if (outsidefogdensity != 0 && outsidefogcolor.a!=0xff && (fogcolor.d & 0xffffff) == (outsidefogcolor.d & 0xffffff)) + { + // case 2. outsidefogdensity has already been set as needed + density=outsidefogdensity; + } + else if (fogdensity!=0) + { + // case 3: level has fog density set + density=fogdensity; + } + else if (lightlevel < 248) + { + // case 4: use light level + density=clamp(255-lightlevel,30,255); + } + else + { + density = 0.f; + } + return density; +} + + +//========================================================================== +// +// Check fog by current lighting info +// +//========================================================================== + +bool gl_CheckFog(FColormap *cm, int lightlevel) +{ + // Check for fog boundaries. This needs a few more checks for the sectors + bool frontfog; + + PalEntry fogcolor = cm->FadeColor; + + if ((fogcolor.d & 0xffffff) == 0) + { + frontfog = false; + } + else if (outsidefogdensity != 0 && outsidefogcolor.a!=0xff && (fogcolor.d & 0xffffff) == (outsidefogcolor.d & 0xffffff)) + { + frontfog = true; + } + else if (fogdensity!=0 || (glset.lightmode & 4)) + { + // case 3: level has fog density set + frontfog = true; + } + else + { + // case 4: use light level + frontfog = lightlevel < 248; + } + return frontfog; +} + +//========================================================================== +// +// Check if the current linedef is a candidate for a fog boundary +// +//========================================================================== + +bool gl_CheckFog(sector_t *frontsector, sector_t *backsector) +{ + // Check for fog boundaries. This needs a few more checks for the sectors + bool frontfog, backfog; + + PalEntry fogcolor = frontsector->ColorMap->Fade; + + if ((fogcolor.d & 0xffffff) == 0) + { + frontfog = false; + } + else if (outsidefogdensity != 0 && outsidefogcolor.a!=0xff && (fogcolor.d & 0xffffff) == (outsidefogcolor.d & 0xffffff)) + { + frontfog = true; + } + else if (fogdensity!=0 || (glset.lightmode & 4)) + { + // case 3: level has fog density set + frontfog = true; + } + else + { + // case 4: use light level + frontfog = frontsector->lightlevel < 248; + } + + if (backsector == NULL) return frontfog; + + fogcolor = backsector->ColorMap->Fade; + + if ((fogcolor.d & 0xffffff) == 0) + { + backfog = false; + } + else if (outsidefogdensity != 0 && outsidefogcolor.a!=0xff && (fogcolor.d & 0xffffff) == (outsidefogcolor.d & 0xffffff)) + { + backfog = true; + } + else if (fogdensity!=0 || (glset.lightmode & 4)) + { + // case 3: level has fog density set + backfog = true; + } + else + { + // case 4: use light level + backfog = backsector->lightlevel < 248; + } + + // in all other cases this might create more problems than it solves. + return (frontfog && !backfog && !gl_fixedcolormap && + (frontsector->GetTexture(sector_t::ceiling)!=skyflatnum || + backsector->GetTexture(sector_t::ceiling)!=skyflatnum)); +} + +//========================================================================== +// +// Lighting stuff +// +//========================================================================== + +void gl_SetShaderLight(float level, float olight) +{ +#if 1 //ndef _DEBUG + const float MAXDIST = 256.f; + const float THRESHOLD = 96.f; + const float FACTOR = 0.75f; +#else + const float MAXDIST = 256.f; + const float THRESHOLD = 96.f; + const float FACTOR = 2.75f; +#endif + + if (level > 0) + { + float lightdist, lightfactor; + + if (olight < THRESHOLD) + { + lightdist = (MAXDIST/2) + (olight * MAXDIST / THRESHOLD / 2); + olight = THRESHOLD; + } + else lightdist = MAXDIST; + + lightfactor = 1.f + ((olight/level) - 1.f) * FACTOR; + if (lightfactor == 1.f) lightdist = 0.f; // save some code in the shader + gl_RenderState.SetLightParms(lightfactor, lightdist); + } + else + { + gl_RenderState.SetLightParms(1.f, 0.f); + } +} + + +//========================================================================== +// +// Sets the fog for the current polygon +// +//========================================================================== + +void gl_SetFog(int lightlevel, int rellight, const FColormap *cmap, bool isadditive) +{ + PalEntry fogcolor; + float fogdensity; + + if (level.flags&LEVEL_HASFADETABLE) + { + fogdensity=70; + fogcolor=0x808080; + } + else if (cmap != NULL && gl_fixedcolormap == 0) + { + fogcolor = cmap->FadeColor; + fogdensity = gl_GetFogDensity(lightlevel, fogcolor); + fogcolor.a=0; + } + else + { + fogcolor = 0; + fogdensity = 0; + } + + // Make fog a little denser when inside a skybox + if (GLPortal::inskybox) fogdensity+=fogdensity/2; + + + // no fog in enhanced vision modes! + if (fogdensity==0 || gl_fogmode == 0) + { + gl_RenderState.EnableFog(false); + gl_RenderState.SetFog(0,0); + } + else + { + if (glset.lightmode == 2 && fogcolor == 0) + { + float light = gl_CalcLightLevel(lightlevel, rellight, false); + gl_SetShaderLight(light, lightlevel); + } + else + { + gl_RenderState.SetLightParms(1.f, 0.f); + } + + // For additive rendering using the regular fog color here would mean applying it twice + // so always use black + if (isadditive) + { + fogcolor=0; + } + // Handle desaturation + if (cmap->colormap != CM_DEFAULT) + gl_ModifyColor(fogcolor.r, fogcolor.g, fogcolor.b, cmap->colormap); + + gl_RenderState.EnableFog(true); + gl_RenderState.SetFog(fogcolor, fogdensity); + + // Korshun: fullbright fog like in software renderer. + if (glset.brightfog && fogdensity != 0 && fogcolor != 0) + gl.VertexAttrib1f(VATTR_LIGHTLEVEL, 1.0); + } +} + +//========================================================================== +// +// Modifies a color according to a specified colormap +// +//========================================================================== + +void gl_ModifyColor(BYTE & red, BYTE & green, BYTE & blue, int cm) +{ + int gray = (red*77 + green*143 + blue*36)>>8; + if (cm >= CM_FIRSTSPECIALCOLORMAP && cm < CM_MAXCOLORMAP) + { + PalEntry pe = SpecialColormaps[cm - CM_FIRSTSPECIALCOLORMAP].GrayscaleToColor[gray]; + red = pe.r; + green = pe.g; + blue = pe.b; + } + else if (cm >= CM_DESAT1 && cm <= CM_DESAT31) + { + gl_Desaturate(gray, red, green, blue, red, green, blue, cm - CM_DESAT0); + } +} + + + +//========================================================================== +// +// For testing sky fog sheets +// +//========================================================================== +CCMD(skyfog) +{ + if (argv.argc()>1) + { + skyfog=strtol(argv[1],NULL,0); + } +} + diff --git a/src/gl/renderer/gl_lightdata.h b/src/gl/renderer/gl_lightdata.h new file mode 100644 index 000000000..33d179b27 --- /dev/null +++ b/src/gl/renderer/gl_lightdata.h @@ -0,0 +1,66 @@ +#ifndef __GL_LIGHTDATA +#define __GL_LIGHTDATA + +#include "v_palette.h" +#include "r_data/renderstyle.h" +#include "gl/renderer/gl_colormap.h" + +bool gl_BrightmapsActive(); +bool gl_GlowActive(); + +inline int gl_ClampLight(int lightlevel) +{ + return clamp(lightlevel, 0, 255); +} + +void gl_GetRenderStyle(FRenderStyle style, bool drawopaque, bool allowcolorblending, + int *tm, int *sb, int *db, int *be); +void gl_SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog); + +int gl_CalcLightLevel(int lightlevel, int rellight, bool weapon); +PalEntry gl_CalcLightColor(int light, PalEntry pe, int blendfactor, bool force = false); +void gl_GetLightColor(int lightlevel, int rellight, const FColormap * cm, float * pred, float * pgreen, float * pblue, bool weapon=false); +void gl_SetColor(int light, int rellight, const FColormap * cm, float alpha, PalEntry ThingColor = 0xffffff, bool weapon=false); +void gl_SetColor(int light, int rellight, const FColormap * cm, float *red, float *green, float *blue, PalEntry ThingColor=0xffffff, bool weapon=false); + +float gl_GetFogDensity(int lightlevel, PalEntry fogcolor); +struct sector_t; +bool gl_CheckFog(FColormap *cm, int lightlevel); +bool gl_CheckFog(sector_t *frontsector, sector_t *backsector); + +void gl_SetFog(int lightlevel, int rellight, const FColormap *cm, bool isadditive); + +inline bool gl_isBlack(PalEntry color) +{ + return color.r + color.g + color.b == 0; +} + +inline bool gl_isWhite(PalEntry color) +{ + return color.r + color.g + color.b == 3*0xff; +} + +extern DWORD gl_fixedcolormap; + +inline bool gl_isFullbright(PalEntry color, int lightlevel) +{ + return gl_fixedcolormap || (gl_isWhite(color) && lightlevel==255); +} + +__forceinline void gl_Desaturate(int gray, int ired, int igreen, int iblue, BYTE & red, BYTE & green, BYTE & blue, int fac) +{ + red = (ired*(31-fac) + gray*fac)/31; + green = (igreen*(31-fac) + gray*fac)/31; + blue = (iblue*(31-fac) + gray*fac)/31; +} + +void gl_ModifyColor(BYTE & red, BYTE & green, BYTE & blue, int cm); +void gl_DeleteAllAttachedLights(); +void gl_RecreateAllAttachedLights(); + +extern int fogdensity; +extern int outsidefogdensity; +extern int skyfog; + + +#endif \ No newline at end of file diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp new file mode 100644 index 000000000..62983f213 --- /dev/null +++ b/src/gl/renderer/gl_renderer.cpp @@ -0,0 +1,634 @@ +/* +** gl1_renderer.cpp +** Renderer interface +** +**--------------------------------------------------------------------------- +** Copyright 2008 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "r_data/r_translate.h" +#include "m_png.h" +#include "m_crc32.h" +#include "w_wad.h" +//#include "gl/gl_intern.h" +#include "gl/gl_functions.h" +#include "vectors.h" + +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_threads.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/data/gl_data.h" +#include "gl/data/gl_vertexbuffer.h" +#include "gl/dynlights/gl_lightbuffer.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_texture.h" +#include "gl/textures/gl_translate.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_templates.h" +#include "gl/models/gl_models.h" + +//=========================================================================== +// +// Renderer interface +// +//=========================================================================== + +EXTERN_CVAR(Bool, gl_render_segs) + +//----------------------------------------------------------------------------- +// +// Initialize +// +//----------------------------------------------------------------------------- + +void FGLRenderer::Initialize() +{ + glpart2 = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/glpart2.png"), FTexture::TEX_MiscPatch); + glpart = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/glpart.png"), FTexture::TEX_MiscPatch); + mirrortexture = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/mirror.png"), FTexture::TEX_MiscPatch); + gllight = FTexture::CreateTexture(Wads.GetNumForFullName("glstuff/gllight.png"), FTexture::TEX_MiscPatch); + + mVBO = new FFlatVertexBuffer; + mFBID = 0; + SetupLevel(); + mShaderManager = new FShaderManager; + //mThreadManager = new FGLThreadManager; +} + +FGLRenderer::~FGLRenderer() +{ + gl_CleanModelData(); + gl_DeleteAllAttachedLights(); + FMaterial::FlushAll(); + //if (mThreadManager != NULL) delete mThreadManager; + if (mShaderManager != NULL) delete mShaderManager; + if (mVBO != NULL) delete mVBO; + if (glpart2) delete glpart2; + if (glpart) delete glpart; + if (mirrortexture) delete mirrortexture; + if (gllight) delete gllight; + if (mFBID != 0) gl.DeleteFramebuffers(1, &mFBID); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLRenderer::SetupLevel() +{ + mVBO->CreateVBO(); +} + +void FGLRenderer::Begin2D() +{ + gl_RenderState.EnableFog(false); + gl_RenderState.Set2DMode(true); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLRenderer::ProcessLowerMiniseg(seg_t *seg, sector_t * frontsector, sector_t * backsector) +{ + GLWall wall; + wall.ProcessLowerMiniseg(seg, frontsector, backsector); + rendered_lines++; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLRenderer::ProcessSprite(AActor *thing, sector_t *sector) +{ + GLSprite glsprite; + glsprite.Process(thing, sector); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLRenderer::ProcessParticle(particle_t *part, sector_t *sector) +{ + GLSprite glsprite; + glsprite.ProcessParticle(part, sector);//, 0, 0); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLRenderer::ProcessSector(sector_t *fakesector) +{ + GLFlat glflat; + glflat.ProcessSector(fakesector); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLRenderer::FlushTextures() +{ + FMaterial::FlushAll(); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +bool FGLRenderer::StartOffscreen() +{ + if (gl.flags & RFL_FRAMEBUFFER) + { + if (mFBID == 0) gl.GenFramebuffers(1, &mFBID); + gl.BindFramebuffer(GL_FRAMEBUFFER, mFBID); + return true; + } + return false; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLRenderer::EndOffscreen() +{ + if (gl.flags & RFL_FRAMEBUFFER) + { + gl.BindFramebuffer(GL_FRAMEBUFFER, 0); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +unsigned char *FGLRenderer::GetTextureBuffer(FTexture *tex, int &w, int &h) +{ + FMaterial * gltex = FMaterial::ValidateTexture(tex); + if (gltex) + { + return gltex->CreateTexBuffer(CM_DEFAULT, 0, w, h); + } + return NULL; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLRenderer::ClearBorders() +{ + OpenGLFrameBuffer *glscreen = static_cast(screen); + + // Letterbox time! Draw black top and bottom borders. + int width = glscreen->GetWidth(); + int height = glscreen->GetHeight(); + int trueHeight = glscreen->GetTrueHeight(); + + int borderHeight = (trueHeight - height) / 2; + + gl.Viewport(0, 0, width, trueHeight); + gl.MatrixMode(GL_PROJECTION); + gl.LoadIdentity(); + gl.Ortho(0.0, width * 1.0, 0.0, trueHeight, -1.0, 1.0); + gl.MatrixMode(GL_MODELVIEW); + gl.Color3f(0.f, 0.f, 0.f); + gl_RenderState.Set2DMode(true); + gl_RenderState.EnableTexture(false); + gl_RenderState.Apply(true); + + gl.Begin(GL_QUADS); + // upper quad + gl.Vertex2i(0, borderHeight); + gl.Vertex2i(0, 0); + gl.Vertex2i(width, 0); + gl.Vertex2i(width, borderHeight); + + // lower quad + gl.Vertex2i(0, trueHeight); + gl.Vertex2i(0, trueHeight - borderHeight); + gl.Vertex2i(width, trueHeight - borderHeight); + gl.Vertex2i(width, trueHeight); + gl.End(); + + gl_RenderState.EnableTexture(true); + + gl.Viewport(0, (trueHeight - height) / 2, width, height); +} + +//========================================================================== +// +// Draws a texture +// +//========================================================================== + +void FGLRenderer::DrawTexture(FTexture *img, DCanvas::DrawParms &parms) +{ + double xscale = parms.destwidth / parms.texwidth; + double yscale = parms.destheight / parms.texheight; + double x = parms.x - parms.left * xscale; + double y = parms.y - parms.top * yscale; + double w = parms.destwidth; + double h = parms.destheight; + float u1, v1, u2, v2, r, g, b; + float light = 1.f; + + FMaterial * gltex = FMaterial::ValidateTexture(img); + + if (parms.colorOverlay && (parms.colorOverlay & 0xffffff) == 0) + { + // Right now there's only black. Should be implemented properly later + light = 1.f - APART(parms.colorOverlay)/255.f; + parms.colorOverlay = 0; + } + + if (!img->bHasCanvas) + { + if (!parms.alphaChannel) + { + int translation = 0; + if (parms.remap != NULL && !parms.remap->Inactive) + { + GLTranslationPalette * pal = static_cast(parms.remap->GetNative()); + if (pal) translation = -pal->GetIndex(); + } + gltex->BindPatch(CM_DEFAULT, translation); + } + else + { + // This is an alpha texture + gltex->BindPatch(CM_SHADE, 0); + } + + u1 = gltex->GetUL(); + v1 = gltex->GetVT(); + u2 = gltex->GetUR(); + v2 = gltex->GetVB(); + } + else + { + gltex->Bind(CM_DEFAULT, 0, 0); + u2=1.f; + v2=-1.f; + u1 = v1 = 0.f; + gl_RenderState.SetTextureMode(TM_OPAQUE); + } + + if (parms.flipX) + { + float temp = u1; + u1 = u2; + u2 = temp; + } + + + if (parms.windowleft > 0 || parms.windowright < parms.texwidth) + { + x += parms.windowleft * xscale; + w -= (parms.texwidth - parms.windowright + parms.windowleft) * xscale; + + u1 = float(u1 + parms.windowleft / parms.texwidth); + u2 = float(u2 - (parms.texwidth - parms.windowright) / parms.texwidth); + } + + if (parms.style.Flags & STYLEF_ColorIsFixed) + { + r = RPART(parms.fillcolor)/255.0f; + g = GPART(parms.fillcolor)/255.0f; + b = BPART(parms.fillcolor)/255.0f; + } + else + { + r = g = b = light; + } + + // scissor test doesn't use the current viewport for the coordinates, so use real screen coordinates + int btm = (SCREENHEIGHT - screen->GetHeight()) / 2; + btm = SCREENHEIGHT - btm; + + gl.Enable(GL_SCISSOR_TEST); + int space = (static_cast(screen)->GetTrueHeight()-screen->GetHeight())/2; + gl.Scissor(parms.lclip, btm - parms.dclip + space, parms.rclip - parms.lclip, parms.dclip - parms.uclip); + + gl_SetRenderStyle(parms.style, !parms.masked, false); + if (img->bHasCanvas) + { + gl_RenderState.SetTextureMode(TM_OPAQUE); + } + + gl.Color4f(r, g, b, FIXED2FLOAT(parms.alpha)); + + gl_RenderState.EnableAlphaTest(false); + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_STRIP); + gl.TexCoord2f(u1, v1); + glVertex2d(x, y); + gl.TexCoord2f(u1, v2); + glVertex2d(x, y + h); + gl.TexCoord2f(u2, v1); + glVertex2d(x + w, y); + gl.TexCoord2f(u2, v2); + glVertex2d(x + w, y + h); + gl.End(); + + if (parms.colorOverlay) + { + gl_RenderState.SetTextureMode(TM_MASK); + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_RenderState.BlendEquation(GL_FUNC_ADD); + gl_RenderState.Apply(); + gl.Color4ub(RPART(parms.colorOverlay),GPART(parms.colorOverlay),BPART(parms.colorOverlay),APART(parms.colorOverlay)); + gl.Begin(GL_TRIANGLE_STRIP); + gl.TexCoord2f(u1, v1); + glVertex2d(x, y); + gl.TexCoord2f(u1, v2); + glVertex2d(x, y + h); + gl.TexCoord2f(u2, v1); + glVertex2d(x + w, y); + gl.TexCoord2f(u2, v2); + glVertex2d(x + w, y + h); + gl.End(); + } + + gl_RenderState.EnableAlphaTest(true); + + gl.Scissor(0, 0, screen->GetWidth(), screen->GetHeight()); + gl.Disable(GL_SCISSOR_TEST); + gl_RenderState.SetTextureMode(TM_MODULATE); + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_RenderState.BlendEquation(GL_FUNC_ADD); +} + +//========================================================================== +// +// +// +//========================================================================== +void FGLRenderer::DrawLine(int x1, int y1, int x2, int y2, int palcolor, uint32 color) +{ + PalEntry p = color? (PalEntry)color : GPalette.BaseColors[palcolor]; + gl_RenderState.EnableTexture(false); + gl_RenderState.Apply(true); + gl.Color3ub(p.r, p.g, p.b); + gl.Begin(GL_LINES); + gl.Vertex2i(x1, y1); + gl.Vertex2i(x2, y2); + gl.End(); + gl_RenderState.EnableTexture(true); +} + +//========================================================================== +// +// +// +//========================================================================== +void FGLRenderer::DrawPixel(int x1, int y1, int palcolor, uint32 color) +{ + PalEntry p = color? (PalEntry)color : GPalette.BaseColors[palcolor]; + gl_RenderState.EnableTexture(false); + gl_RenderState.Apply(true); + gl.Color3ub(p.r, p.g, p.b); + gl.Begin(GL_POINTS); + gl.Vertex2i(x1, y1); + gl.End(); + gl_RenderState.EnableTexture(true); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLRenderer::Dim(PalEntry color, float damount, int x1, int y1, int w, int h) +{ + float r, g, b; + + gl_RenderState.EnableTexture(false); + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_RenderState.AlphaFunc(GL_GREATER,0); + gl_RenderState.Apply(true); + + r = color.r/255.0f; + g = color.g/255.0f; + b = color.b/255.0f; + + gl.Begin(GL_TRIANGLE_FAN); + gl.Color4f(r, g, b, damount); + gl.Vertex2i(x1, y1); + gl.Vertex2i(x1, y1 + h); + gl.Vertex2i(x1 + w, y1 + h); + gl.Vertex2i(x1 + w, y1); + gl.End(); + + gl_RenderState.EnableTexture(true); +} + +//========================================================================== +// +// +// +//========================================================================== +void FGLRenderer::FlatFill (int left, int top, int right, int bottom, FTexture *src, bool local_origin) +{ + float fU1,fU2,fV1,fV2; + + FMaterial *gltexture=FMaterial::ValidateTexture(src); + + if (!gltexture) return; + + gltexture->Bind(CM_DEFAULT, 0, 0); + + // scaling is not used here. + if (!local_origin) + { + fU1 = float(left) / src->GetWidth(); + fV1 = float(top) / src->GetHeight(); + fU2 = float(right) / src->GetWidth(); + fV2 = float(bottom) / src->GetHeight(); + } + else + { + fU1 = 0; + fV1 = 0; + fU2 = float(right-left) / src->GetWidth(); + fV2 = float(bottom-top) / src->GetHeight(); + } + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_STRIP); + gl.Color4f(1, 1, 1, 1); + gl.TexCoord2f(fU1, fV1); gl.Vertex2f(left, top); + gl.TexCoord2f(fU1, fV2); gl.Vertex2f(left, bottom); + gl.TexCoord2f(fU2, fV1); gl.Vertex2f(right, top); + gl.TexCoord2f(fU2, fV2); gl.Vertex2f(right, bottom); + gl.End(); +} + +//========================================================================== +// +// +// +//========================================================================== +void FGLRenderer::Clear(int left, int top, int right, int bottom, int palcolor, uint32 color) +{ + int rt; + int offY = 0; + PalEntry p = palcolor==-1 || color != 0? (PalEntry)color : GPalette.BaseColors[palcolor]; + int width = right-left; + int height= bottom-top; + + + rt = screen->GetHeight() - top; + + int space = (static_cast(screen)->GetTrueHeight()-screen->GetHeight())/2; // ugh... + rt += space; + /* + if (!m_windowed && (m_trueHeight != m_height)) + { + offY = (m_trueHeight - m_height) / 2; + rt += offY; + } + */ + + gl.Enable(GL_SCISSOR_TEST); + gl.Scissor(left, rt - height, width, height); + + gl.ClearColor(p.r/255.0f, p.g/255.0f, p.b/255.0f, 0.f); + gl.Clear(GL_COLOR_BUFFER_BIT); + gl.ClearColor(0.f, 0.f, 0.f, 0.f); + + gl.Disable(GL_SCISSOR_TEST); +} + +//========================================================================== +// +// D3DFB :: FillSimplePoly +// +// Here, "simple" means that a simple triangle fan can draw it. +// +//========================================================================== + +void FGLRenderer::FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, + angle_t rotation, FDynamicColormap *colormap, int lightlevel) +{ + if (npoints < 3) + { // This is no polygon. + return; + } + + FMaterial *gltexture = FMaterial::ValidateTexture(texture); + + if (gltexture == NULL) + { + return; + } + + FColormap cm; + cm = colormap; + + lightlevel = gl_CalcLightLevel(lightlevel, 0, true); + PalEntry pe = gl_CalcLightColor(lightlevel, cm.LightColor, cm.blendfactor, true); + gl.Color3ub(pe.r, pe.g, pe.b); + + gltexture->Bind(cm.colormap); + + int i; + float rot = float(rotation * M_PI / float(1u << 31)); + bool dorotate = rot != 0; + + float cosrot = cos(rot); + float sinrot = sin(rot); + + //float yoffs = GatheringWipeScreen ? 0 : LBOffset; + float uscale = float(1.f / (texture->GetScaledWidth() * scalex)); + float vscale = float(1.f / (texture->GetScaledHeight() * scaley)); + if (gltexture->tex->bHasCanvas) + { + vscale = 0 - vscale; + } + float ox = float(originx); + float oy = float(originy); + + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_FAN); + for (i = 0; i < npoints; ++i) + { + float u = points[i].X - 0.5f - ox; + float v = points[i].Y - 0.5f - oy; + if (dorotate) + { + float t = u; + u = t * cosrot - v * sinrot; + v = v * cosrot + t * sinrot; + } + gl.TexCoord2f(u * uscale, v * vscale); + gl.Vertex3f(points[i].X, points[i].Y /* + yoffs */, 0); + } + gl.End(); +} + diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h new file mode 100644 index 000000000..f1b39fc46 --- /dev/null +++ b/src/gl/renderer/gl_renderer.h @@ -0,0 +1,178 @@ +#ifndef __GL_RENDERER_H +#define __GL_RENDERER_H + +#include "r_defs.h" +#include "v_video.h" +#include "vectors.h" +#include "r_renderer.h" + +struct particle_t; +class FCanvasTexture; +class FFlatVertexBuffer; +class OpenGLFrameBuffer; +struct FDrawInfo; +struct pspdef_t; +class FShaderManager; +class GLPortal; +class FGLThreadManager; + +enum SectorRenderFlags +{ + // This is used to avoid creating too many drawinfos + SSRF_RENDERFLOOR = 1, + SSRF_RENDERCEILING = 2, + SSRF_RENDER3DPLANES = 4, + SSRF_RENDERALL = 7, + SSRF_PROCESSED = 8, + SSRF_SEEN = 16, +}; + +struct GL_IRECT +{ + int left,top; + int width,height; + + + void Offset(int xofs,int yofs) + { + left+=xofs; + top+=yofs; + } +}; + + +class FGLRenderer +{ +public: + + OpenGLFrameBuffer *framebuffer; + GLPortal *mCurrentPortal; + int mMirrorCount; + int mPlaneMirrorCount; + int mLightCount; + float mCurrentFoV; + AActor *mViewActor; + FShaderManager *mShaderManager; + FGLThreadManager *mThreadManager; + int gl_spriteindex; + unsigned int mFBID; + + FTexture *glpart2; + FTexture *glpart; + FTexture *mirrortexture; + FTexture *gllight; + + float mSky1Pos, mSky2Pos; + + FRotator mAngles; + FVector2 mViewVector; + FVector3 mCameraPos; + + FFlatVertexBuffer *mVBO; + + + FGLRenderer(OpenGLFrameBuffer *fb) + { + framebuffer = fb; + mCurrentPortal = NULL; + mMirrorCount = 0; + mPlaneMirrorCount = 0; + mLightCount = 0; + mAngles = FRotator(0,0,0); + mViewVector = FVector2(0,0); + mCameraPos = FVector3(0,0,0); + mVBO = NULL; + gl_spriteindex = 0; + mShaderManager = NULL; + glpart2 = glpart = gllight = mirrortexture = NULL; + } + ~FGLRenderer() ; + + angle_t FrustumAngle(); + void SetViewArea(); + void ResetViewport(); + void SetViewport(GL_IRECT *bounds); + sector_t *RenderViewpoint (AActor * camera, GL_IRECT * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); + void RenderView(player_t *player); + void SetCameraPos(fixed_t viewx, fixed_t viewy, fixed_t viewz, angle_t viewangle); + void SetupView(fixed_t viewx, fixed_t viewy, fixed_t viewz, angle_t viewangle, bool mirror, bool planemirror); + + void Initialize(); + + void CreateScene(); + void RenderScene(int recursion); + void RenderTranslucent(); + void DrawScene(bool toscreen = false); + void DrawBlend(sector_t * viewsector); + + void DrawPSprite (player_t * player,pspdef_t *psp,fixed_t sx, fixed_t sy, int cm_index, bool hudModelStep, int OverrideShader); + void DrawPlayerSprites(sector_t * viewsector, bool hudModelStep); + void DrawTargeterSprites(); + + void Begin2D(); + void ClearBorders(); + void DrawTexture(FTexture *img, DCanvas::DrawParms &parms); + void DrawLine(int x1, int y1, int x2, int y2, int palcolor, uint32 color); + void DrawPixel(int x1, int y1, int palcolor, uint32 color); + void Dim(PalEntry color, float damount, int x1, int y1, int w, int h); + void FlatFill (int left, int top, int right, int bottom, FTexture *src, bool local_origin); + void Clear(int left, int top, int right, int bottom, int palcolor, uint32 color); + + void ProcessLowerMiniseg(seg_t *seg, sector_t * frontsector, sector_t * backsector); + void ProcessSprite(AActor *thing, sector_t *sector); + void ProcessParticle(particle_t *part, sector_t *sector); + void ProcessSector(sector_t *fakesector); + void FlushTextures(); + unsigned char *GetTextureBuffer(FTexture *tex, int &w, int &h); + void SetupLevel(); + + void SetFixedColormap (player_t *player); + void WriteSavePic (player_t *player, FILE *file, int width, int height); + void EndDrawScene(sector_t * viewsector); + void Flush() {} + + void SetProjection(float fov, float ratio, float fovratio); + void SetViewMatrix(bool mirror, bool planemirror); + void ProcessScene(bool toscreen = false); + + bool StartOffscreen(); + void EndOffscreen(); + + void FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, + angle_t rotation, FDynamicColormap *colormap, int lightlevel); +}; + +// Global functions. Make them members of GLRenderer later? +void gl_RenderBSPNode (void *node); +bool gl_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector); +void gl_CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, sector_t *backsector); + +typedef enum +{ + area_normal, + area_below, + area_above, + area_default +} area_t; + +extern area_t in_area; + + +sector_t * gl_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back); +inline sector_t * gl_FakeFlat(sector_t * sec, sector_t * dest, bool back) +{ + return gl_FakeFlat(sec, dest, in_area, back); +} + +struct TexFilter_s +{ + int minfilter; + int magfilter; + bool mipmapping; +} ; + + +extern FGLRenderer *GLRenderer; + +#endif \ No newline at end of file diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp new file mode 100644 index 000000000..31c46bc8a --- /dev/null +++ b/src/gl/renderer/gl_renderstate.cpp @@ -0,0 +1,352 @@ +/* +** gl_renderstate.cpp +** Render state maintenance +** +**--------------------------------------------------------------------------- +** Copyright 2009 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "gl/data/gl_data.h" +#include "gl/system/gl_cvars.h" +#include "gl/shaders/gl_shader.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/renderer/gl_colormap.h" + +FRenderState gl_RenderState; +int FStateAttr::ChangeCounter; + +CVAR(Bool, gl_direct_state_change, true, 0) + + +//========================================================================== +// +// +// +//========================================================================== + +void FRenderState::Reset() +{ + mTextureEnabled = true; + mBrightmapEnabled = mFogEnabled = mGlowEnabled = mLightEnabled = false; + ffTextureEnabled = ffFogEnabled = false; + mSpecialEffect = ffSpecialEffect = EFF_NONE; + mFogColor.d = ffFogColor.d = -1; + mFogDensity = ffFogDensity = 0; + mTextureMode = ffTextureMode = -1; + mSrcBlend = GL_SRC_ALPHA; + mDstBlend = GL_ONE_MINUS_SRC_ALPHA; + glSrcBlend = glDstBlend = -1; + glAlphaFunc = -1; + mAlphaFunc = GL_GEQUAL; + mAlphaThreshold = 0.5f; + mBlendEquation = GL_FUNC_ADD; + glBlendEquation = -1; + m2D = true; +} + + +//========================================================================== +// +// Set texture shader info +// +//========================================================================== + +int FRenderState::SetupShader(bool cameratexture, int &shaderindex, int &cm, float warptime) +{ + bool usecmshader; + int softwarewarp = 0; + + if (shaderindex == 3) + { + // Brightmap should not be used. + if (!mBrightmapEnabled || cm >= CM_FIRSTSPECIALCOLORMAP) + { + shaderindex = 0; + } + } + + if (gl.shadermodel == 4) + { + usecmshader = cm > CM_DEFAULT && cm < CM_MAXCOLORMAP && mTextureMode != TM_MASK; + } + else if (gl.shadermodel == 3) + { + usecmshader = (cameratexture || gl_colormap_shader) && + cm > CM_DEFAULT && cm < CM_MAXCOLORMAP && mTextureMode != TM_MASK; + + if (!gl_brightmap_shader && shaderindex == 3) + { + shaderindex = 0; + } + else if (!gl_warp_shader && shaderindex !=3) + { + if (shaderindex <= 2) softwarewarp = shaderindex; + shaderindex = 0; + } + } + else + { + usecmshader = cameratexture; + softwarewarp = shaderindex > 0 && shaderindex < 3? shaderindex : 0; + shaderindex = 0; + } + + mEffectState = shaderindex; + mColormapState = usecmshader? cm : CM_DEFAULT; + if (usecmshader) cm = CM_DEFAULT; + mWarpTime = warptime; + return softwarewarp; +} + + +//========================================================================== +// +// Apply shader settings +// +//========================================================================== + +bool FRenderState::ApplyShader() +{ + bool useshaders = false; + FShader *activeShader = NULL; + + if (mSpecialEffect > 0 && gl.shadermodel > 2) + { + activeShader = GLRenderer->mShaderManager->BindEffect(mSpecialEffect); + } + else + { + switch (gl.shadermodel) + { + case 2: + useshaders = (mTextureEnabled && mColormapState != CM_DEFAULT); + break; + + case 3: + useshaders = ( + mEffectState != 0 || // special shaders + (mFogEnabled && (gl_fogmode == 2 || gl_fog_shader) && gl_fogmode != 0) || // fog requires a shader + (mTextureEnabled && (mEffectState != 0 || mColormapState)) || // colormap + mGlowEnabled // glow requires a shader + ); + break; + + case 4: + useshaders = (!m2D || mEffectState != 0 || mColormapState); // all 3D rendering and 2D with texture effects. + break; + + default: + break; + } + + if (useshaders) + { + FShaderContainer *shd = GLRenderer->mShaderManager->Get(mTextureEnabled? mEffectState : 4); + + if (shd != NULL) + { + activeShader = shd->Bind(mColormapState, mGlowEnabled, mWarpTime, mLightEnabled); + } + } + } + + if (activeShader) + { + int fogset = 0; + if (mFogEnabled) + { + if ((mFogColor & 0xffffff) == 0) + { + fogset = gl_fogmode; + } + else + { + fogset = -gl_fogmode; + } + } + + if (fogset != activeShader->currentfogenabled) + { + gl.Uniform1i(activeShader->fogenabled_index, (activeShader->currentfogenabled = fogset)); + } + if (mTextureMode != activeShader->currenttexturemode) + { + gl.Uniform1i(activeShader->texturemode_index, (activeShader->currenttexturemode = mTextureMode)); + } + if (activeShader->currentcamerapos.Update(&mCameraPos)) + { + gl.Uniform3fv(activeShader->camerapos_index, 1, mCameraPos.vec); + } + /*if (mLightParms[0] != activeShader->currentlightfactor || + mLightParms[1] != activeShader->currentlightdist || + mFogDensity != activeShader->currentfogdensity)*/ + { + const float LOG2E = 1.442692f; // = 1/log(2) + //activeShader->currentlightdist = mLightParms[1]; + //activeShader->currentlightfactor = mLightParms[0]; + //activeShader->currentfogdensity = mFogDensity; + // premultiply the density with as much as possible here to reduce shader + // execution time. + gl.VertexAttrib4f(VATTR_FOGPARAMS, mLightParms[0], mLightParms[1], mFogDensity * (-LOG2E / 64000.f), 0); + } + if (mFogColor != activeShader->currentfogcolor) + { + activeShader->currentfogcolor = mFogColor; + + gl.Uniform4f (activeShader->fogcolor_index, mFogColor.r/255.f, mFogColor.g/255.f, + mFogColor.b/255.f, 0); + } + if (mGlowEnabled) + { + gl.Uniform4fv(activeShader->glowtopcolor_index, 1, mGlowTop.vec); + gl.Uniform4fv(activeShader->glowbottomcolor_index, 1, mGlowBottom.vec); + } + if (mLightEnabled) + { + gl.Uniform3iv(activeShader->lightrange_index, 1, mNumLights); + gl.Uniform4fv(activeShader->lights_index, mNumLights[2], mLightData); + } + if (glset.lightmode == 8) + { + gl.Uniform3fv(activeShader->dlightcolor_index, 1, mDynLight); + } + + return true; + } + return false; +} + + +//========================================================================== +// +// Apply State +// +//========================================================================== + +void FRenderState::Apply(bool forcenoshader) +{ + if (!gl_direct_state_change) + { + if (mSrcBlend != glSrcBlend || mDstBlend != glDstBlend) + { + glSrcBlend = mSrcBlend; + glDstBlend = mDstBlend; + glBlendFunc(mSrcBlend, mDstBlend); + } + if (mAlphaFunc != glAlphaFunc || mAlphaThreshold != glAlphaThreshold) + { + glAlphaFunc = mAlphaFunc; + glAlphaThreshold = mAlphaThreshold; + ::glBlendFunc(mAlphaFunc, mAlphaThreshold); + } + if (mAlphaTest != glAlphaTest) + { + glAlphaTest = mAlphaTest; + if (mAlphaTest) glEnable(GL_ALPHA_TEST); + else glDisable(GL_ALPHA_TEST); + } + if (mBlendEquation != glBlendEquation) + { + glBlendEquation = mBlendEquation; + gl.BlendEquation(mBlendEquation); + } + } + + if (forcenoshader || !ApplyShader()) + { + GLRenderer->mShaderManager->SetActiveShader(NULL); + if (mTextureMode != ffTextureMode) + { + gl.SetTextureMode((ffTextureMode = mTextureMode)); + } + if (mTextureEnabled != ffTextureEnabled) + { + if ((ffTextureEnabled = mTextureEnabled)) gl.Enable(GL_TEXTURE_2D); + else gl.Disable(GL_TEXTURE_2D); + } + if (mFogEnabled != ffFogEnabled) + { + if ((ffFogEnabled = mFogEnabled)) + { + gl.Enable(GL_FOG); + } + else gl.Disable(GL_FOG); + } + if (mFogEnabled) + { + if (ffFogColor != mFogColor) + { + ffFogColor = mFogColor; + GLfloat FogColor[4]={mFogColor.r/255.0f,mFogColor.g/255.0f,mFogColor.b/255.0f,0.0f}; + gl.Fogfv(GL_FOG_COLOR, FogColor); + } + if (ffFogDensity != mFogDensity) + { + gl.Fogf(GL_FOG_DENSITY, mFogDensity/64000.f); + ffFogDensity=mFogDensity; + } + } + if (mSpecialEffect != ffSpecialEffect) + { + switch (ffSpecialEffect) + { + case EFF_SPHEREMAP: + gl.Disable(GL_TEXTURE_GEN_T); + gl.Disable(GL_TEXTURE_GEN_S); + + default: + break; + } + switch (mSpecialEffect) + { + case EFF_SPHEREMAP: + // Use sphere mapping for this + gl.Enable(GL_TEXTURE_GEN_T); + gl.Enable(GL_TEXTURE_GEN_S); + gl.TexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP); + gl.TexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP); + break; + + default: + break; + } + ffSpecialEffect = mSpecialEffect; + } + } + +} + diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h new file mode 100644 index 000000000..e8a4d1940 --- /dev/null +++ b/src/gl/renderer/gl_renderstate.h @@ -0,0 +1,278 @@ +#ifndef __GL_RENDERSTATE_H +#define __GL_RENDERSTATE_H + +#include +#include "c_cvars.h" + +EXTERN_CVAR(Bool, gl_direct_state_change) + +struct FStateAttr +{ + static int ChangeCounter; + int mLastChange; + + FStateAttr() + { + mLastChange = -1; + } + + bool operator == (const FStateAttr &other) + { + return mLastChange == other.mLastChange; + } + + bool operator != (const FStateAttr &other) + { + return mLastChange != other.mLastChange; + } + +}; + +struct FStateVec3 : public FStateAttr +{ + float vec[3]; + + bool Update(FStateVec3 *other) + { + if (mLastChange != other->mLastChange) + { + *this = *other; + return true; + } + return false; + } + + void Set(float x, float y, float z) + { + vec[0] = x; + vec[1] = z; + vec[2] = y; + mLastChange = ++ChangeCounter; + } +}; + +struct FStateVec4 : public FStateAttr +{ + float vec[4]; + + bool Update(FStateVec4 *other) + { + if (mLastChange != other->mLastChange) + { + *this = *other; + return true; + } + return false; + } + + void Set(float r, float g, float b, float a) + { + vec[0] = r; + vec[1] = g; + vec[2] = b; + vec[3] = a; + mLastChange = ++ChangeCounter; + } +}; + + +enum EEffect +{ + EFF_NONE, + EFF_FOGBOUNDARY, + EFF_SPHEREMAP, +}; + +class FRenderState +{ + bool mTextureEnabled; + bool mFogEnabled; + bool mGlowEnabled; + bool mLightEnabled; + bool mBrightmapEnabled; + int mSpecialEffect; + int mTextureMode; + float mDynLight[3]; + float mLightParms[2]; + int mNumLights[3]; + float *mLightData; + int mSrcBlend, mDstBlend; + int mAlphaFunc; + float mAlphaThreshold; + bool mAlphaTest; + int mBlendEquation; + bool m2D; + + FStateVec3 mCameraPos; + FStateVec4 mGlowTop, mGlowBottom; + PalEntry mFogColor; + float mFogDensity; + + int mEffectState; + int mColormapState; + float mWarpTime; + + int glSrcBlend, glDstBlend; + int glAlphaFunc; + float glAlphaThreshold; + bool glAlphaTest; + int glBlendEquation; + + bool ffTextureEnabled; + bool ffFogEnabled; + int ffTextureMode; + int ffSpecialEffect; + PalEntry ffFogColor; + float ffFogDensity; + + bool ApplyShader(); + +public: + FRenderState() + { + Reset(); + } + + void Reset(); + + int SetupShader(bool cameratexture, int &shaderindex, int &cm, float warptime); + void Apply(bool forcenoshader = false); + + void SetTextureMode(int mode) + { + mTextureMode = mode; + } + + void EnableTexture(bool on) + { + mTextureEnabled = on; + } + + void EnableFog(bool on) + { + mFogEnabled = on; + } + + void SetEffect(int eff) + { + mSpecialEffect = eff; + } + + void EnableGlow(bool on) + { + mGlowEnabled = on; + } + + void EnableLight(bool on) + { + mLightEnabled = on; + } + + void EnableBrightmap(bool on) + { + mBrightmapEnabled = on; + } + + void SetCameraPos(float x, float y, float z) + { + mCameraPos.Set(x,y,z); + } + + void SetGlowParams(float *t, float *b) + { + mGlowTop.Set(t[0], t[1], t[2], t[3]); + mGlowBottom.Set(b[0], b[1], b[2], b[3]); + } + + void SetDynLight(float r,float g, float b) + { + mDynLight[0] = r; + mDynLight[1] = g; + mDynLight[2] = b; + } + + void SetFog(PalEntry c, float d) + { + mFogColor = c; + if (d >= 0.0f) mFogDensity = d; + } + + void SetLightParms(float f, float d) + { + mLightParms[0] = f; + mLightParms[1] = d; + } + + void SetLights(int *numlights, float *lightdata) + { + mNumLights[0] = numlights[0]; + mNumLights[1] = numlights[1]; + mNumLights[2] = numlights[2]; + mLightData = lightdata; // caution: the data must be preserved by the caller until the 'apply' call! + } + + PalEntry GetFogColor() const + { + return mFogColor; + } + + void BlendFunc(int src, int dst) + { + if (!gl_direct_state_change) + { + mSrcBlend = src; + mDstBlend = dst; + } + else + { + glBlendFunc(src, dst); + } + } + + void AlphaFunc(int func, float thresh) + { + if (!gl_direct_state_change) + { + mAlphaFunc = func; + mAlphaThreshold = thresh; + } + else + { + ::glAlphaFunc(func, thresh); + } + } + + void EnableAlphaTest(bool on) + { + if (!gl_direct_state_change) + { + mAlphaTest = on; + } + else + { + if (on) glEnable(GL_ALPHA_TEST); + else glDisable(GL_ALPHA_TEST); + } + } + + void BlendEquation(int eq) + { + if (!gl_direct_state_change) + { + mBlendEquation = eq; + } + else + { + gl.BlendEquation(eq); + } + } + + void Set2DMode(bool on) + { + m2D = on; + } +}; + +extern FRenderState gl_RenderState; + +#endif diff --git a/src/gl/scene/gl_bsp.cpp b/src/gl/scene/gl_bsp.cpp new file mode 100644 index 000000000..b8a1eeec0 --- /dev/null +++ b/src/gl/scene/gl_bsp.cpp @@ -0,0 +1,563 @@ +/* +** gl_bsp.cpp +** Main rendering loop / BSP traversal / visibility clipping +** +**--------------------------------------------------------------------------- +** Copyright 2000-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "p_lnspec.h" +#include "p_local.h" +#include "a_sharedglobal.h" +#include "r_sky.h" +#include "p_effect.h" +#include "po_man.h" + +#include "gl/renderer/gl_renderer.h" +#include "gl/data/gl_data.h" +#include "gl/data/gl_vertexbuffer.h" +#include "gl/scene/gl_clipper.h" +#include "gl/scene/gl_portal.h" +#include "gl/scene/gl_wall.h" +#include "gl/utility/gl_clock.h" + +EXTERN_CVAR(Bool, gl_render_segs) + +Clipper clipper; + + +CVAR(Bool, gl_render_things, true, 0) +CVAR(Bool, gl_render_walls, true, 0) +CVAR(Bool, gl_render_flats, true, 0) + + +static void UnclipSubsector(subsector_t *sub) +{ + int count = sub->numlines; + seg_t * seg = sub->firstline; + + while (count--) + { + angle_t startAngle = seg->v2->GetClipAngle(); + angle_t endAngle = seg->v1->GetClipAngle(); + + // Back side, i.e. backface culling - read: endAngle >= startAngle! + if (startAngle-endAngle >= ANGLE_180) + { + clipper.SafeRemoveClipRange(startAngle, endAngle); + } + seg++; + } +} + +//========================================================================== +// +// R_AddLine +// Clips the given segment +// and adds any visible pieces to the line list. +// +//========================================================================== + +// making these 2 variables global instead of passing them as function parameters is faster. +static subsector_t *currentsubsector; +static sector_t *currentsector; + +static void AddLine (seg_t *seg) +{ +#ifdef _MSC_VER +#ifdef _DEBUG + if (seg->linedef-lines==38) + __asm nop +#endif +#endif + + angle_t startAngle, endAngle; + sector_t * backsector = NULL; + sector_t bs; + + if (GLRenderer->mCurrentPortal) + { + int clipres = GLRenderer->mCurrentPortal->ClipSeg(seg); + if (clipres == GLPortal::PClip_InFront) return; + } + + startAngle = seg->v2->GetClipAngle(); + endAngle = seg->v1->GetClipAngle(); + + // Back side, i.e. backface culling - read: endAngle >= startAngle! + if (startAngle-endAnglesidedef == NULL) + { + if (!(currentsubsector->flags & SSECF_DRAWN)) + { + if (clipper.SafeCheckRange(startAngle, endAngle)) + { + currentsubsector->flags |= SSECF_DRAWN; + } + } + return; + } + + if (!clipper.SafeCheckRange(startAngle, endAngle)) + { + return; + } + currentsubsector->flags |= SSECF_DRAWN; + + if (!seg->backsector) + { + clipper.SafeAddClipRange(startAngle, endAngle); + } + else if (!(seg->sidedef->Flags & WALLF_POLYOBJ)) // Two-sided polyobjects never obstruct the view + { + if (currentsector->sectornum == seg->backsector->sectornum) + { + FTexture * tex = TexMan(seg->sidedef->GetTexture(side_t::mid)); + if (!tex || tex->UseType==FTexture::TEX_Null) + { + // nothing to do here! + seg->linedef->validcount=validcount; + return; + } + backsector=currentsector; + } + else + { + // clipping checks are only needed when the backsector is not the same as the front sector + gl_CheckViewArea(seg->v1, seg->v2, seg->frontsector, seg->backsector); + + backsector = gl_FakeFlat(seg->backsector, &bs, true); + + if (gl_CheckClip(seg->sidedef, currentsector, backsector)) + { + clipper.SafeAddClipRange(startAngle, endAngle); + } + } + } + else + { + // Backsector for polyobj segs is always the containing sector itself + backsector = currentsector; + } + + seg->linedef->flags |= ML_MAPPED; + + if ((seg->sidedef->Flags & WALLF_POLYOBJ) || seg->linedef->validcount!=validcount) + { + if (!(seg->sidedef->Flags & WALLF_POLYOBJ)) seg->linedef->validcount=validcount; + + if (gl_render_walls) + { + SetupWall.Clock(); + + //if (!gl_multithreading) + { + GLWall wall; + wall.sub = currentsubsector; + wall.Process(seg, currentsector, backsector); + } + /* + else + { + FJob *job = new FGLJobProcessWall(currentsubsector, seg, + currentsector->sectornum, backsector != NULL? backsector->sectornum : -1); + GLRenderer->mThreadManager->AddJob(job); + } + */ + rendered_lines++; + + SetupWall.Unclock(); + } + } +} + +//========================================================================== +// +// R_Subsector +// Determine floor/ceiling planes. +// Add sprites of things in sector. +// Draw one or more line segments. +// +//========================================================================== + +static void PolySubsector(subsector_t * sub) +{ + int count = sub->numlines; + seg_t * line = sub->firstline; + + while (count--) + { + if (line->linedef) + { + AddLine (line); + } + line++; + } +} + +//========================================================================== +// +// RenderBSPNode +// Renders all subsectors below a given node, +// traversing subtree recursively. +// Just call with BSP root. +// +//========================================================================== + +static void RenderPolyBSPNode (void *node) +{ + while (!((size_t)node & 1)) // Keep going until found a subsector + { + node_t *bsp = (node_t *)node; + + // Decide which side the view point is on. + int side = R_PointOnSide(viewx, viewy, bsp); + + // Recursively divide front space (toward the viewer). + RenderPolyBSPNode (bsp->children[side]); + + // Possibly divide back space (away from the viewer). + side ^= 1; + + // It is not necessary to use the slower precise version here + if (!clipper.CheckBox(bsp->bbox[side])) + { + return; + } + + node = bsp->children[side]; + } + PolySubsector ((subsector_t *)((BYTE *)node - 1)); +} + +//========================================================================== +// +// Unlilke the software renderer this function will only draw the walls, +// not the flats. Those are handled as a whole by the parent subsector. +// +//========================================================================== + +static void AddPolyobjs(subsector_t *sub) +{ + if (sub->BSP == NULL || sub->BSP->bDirty) + { + sub->BuildPolyBSP(); + } + if (sub->BSP->Nodes.Size() == 0) + { + PolySubsector(&sub->BSP->Subsectors[0]); + } + else + { + RenderPolyBSPNode(&sub->BSP->Nodes.Last()); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +static inline void AddLines(subsector_t * sub, sector_t * sector) +{ + currentsector = sector; + currentsubsector = sub; + + ClipWall.Clock(); + if (sub->polys != NULL) + { + AddPolyobjs(sub); + } + else + { + int count = sub->numlines; + seg_t * seg = sub->firstline; + + while (count--) + { + if (seg->linedef == NULL) + { + if (!(sub->flags & SSECF_DRAWN)) AddLine (seg); + } + else if (!(seg->sidedef->Flags & WALLF_POLYOBJ)) + { + AddLine (seg); + } + seg++; + } + } + ClipWall.Unclock(); +} + + +//========================================================================== +// +// R_RenderThings +// +//========================================================================== + +static inline void RenderThings(subsector_t * sub, sector_t * sector) +{ + + SetupSprite.Clock(); + sector_t * sec=sub->sector; + if (sec->thinglist != NULL) + { + //if (!gl_multithreading) + { + // Handle all things in sector. + for (AActor * thing = sec->thinglist; thing; thing = thing->snext) + { + GLRenderer->ProcessSprite(thing, sector); + } + } + /* + else if (sec->thinglist != NULL) + { + FJob *job = new FGLJobProcessSprites(sector); + GLRenderer->mThreadManager->AddJob(job); + } + */ + } + SetupSprite.Unclock(); +} + + +//========================================================================== +// +// R_Subsector +// Determine floor/ceiling planes. +// Add sprites of things in sector. +// Draw one or more line segments. +// +//========================================================================== + +static void DoSubsector(subsector_t * sub) +{ + unsigned int i; + sector_t * sector; + sector_t * fakesector; + sector_t fake; + + // check for visibility of this entire subsector. This requires GL nodes. + // (disabled because it costs more time than it saves.) + //if (!clipper.CheckBox(sub->bbox)) return; + + +#ifdef _MSC_VER +#ifdef _DEBUG + if (sub->sector-sectors==931) + { + __asm nop + } +#endif +#endif + + sector=sub->sector; + if (!sector) return; + + // If the mapsections differ this subsector can't possibly be visible from the current view point + if (!(currentmapsection[sub->mapsection>>3] & (1 << (sub->mapsection & 7)))) return; + + if (gl_drawinfo->ss_renderflags[sub-subsectors] & SSRF_SEEN) + { + // This means that we have reached a subsector in a portal that has been marked 'seen' + // from the other side of the portal. This means we must clear the clipper for the + // range this subsector spans before going on. + UnclipSubsector(sub); + } + + fakesector=gl_FakeFlat(sector, &fake, false); + + if (sector->validcount != validcount) + { + GLRenderer->mVBO->CheckUpdate(sector); + } + + // [RH] Add particles + //int shade = LIGHT2SHADE((floorlightlevel + ceilinglightlevel)/2 + r_actualextralight); + if (gl_render_things) + { + SetupSprite.Clock(); + + //if (!gl_multithreading) + { + for (i = ParticlesInSubsec[DWORD(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext) + { + GLRenderer->ProcessParticle(&Particles[i], fakesector); + } + } + /* + else if (ParticlesInSubsec[DWORD(sub-subsectors)] != NO_PARTICLE) + { + FJob job = new FGLJobProcessParticles(sub); + GLRenderer->mThreadManager->AddJob(job); + } + */ + SetupSprite.Unclock(); + } + + AddLines(sub, fakesector); + + // BSP is traversed by subsector. + // A sector might have been split into several + // subsectors during BSP building. + // Thus we check whether it was already added. + if (sector->validcount != validcount) + { + // Well, now it will be done. + sector->validcount = validcount; + + if (gl_render_things) + { + RenderThings(sub, fakesector); + } + sector->MoreFlags |= SECF_DRAWN; + } + + if (gl_render_flats) + { + // Subsectors with only 2 lines cannot have any area! + if (sub->numlines>2 || (sub->hacked&1)) + { + // Exclude the case when it tries to render a sector with a heightsec + // but undetermined heightsec state. This can only happen if the + // subsector is obstructed but not excluded due to a large bounding box. + // Due to the way a BSP works such a subsector can never be visible + if (!sector->heightsec || sector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC || in_area!=area_default) + { + if (sector != sub->render_sector) + { + sector = sub->render_sector; + // the planes of this subsector are faked to belong to another sector + // This means we need the heightsec parts and light info of the render sector, not the actual one. + fakesector = gl_FakeFlat(sector, &fake, false); + } + + BYTE &srf = gl_drawinfo->sectorrenderflags[sub->render_sector->sectornum]; + if (!(srf & SSRF_PROCESSED)) + { + srf |= SSRF_PROCESSED; + + SetupFlat.Clock(); + //if (!gl_multithreading) + { + GLRenderer->ProcessSector(fakesector); + } + /* + else + { + FJob *job = new FGLJobProcessFlats(sub); + GLRenderer->mThreadManager->AddJob(job); + } + */ + SetupFlat.Unclock(); + } + // mark subsector as processed - but mark for rendering only if it has an actual area. + gl_drawinfo->ss_renderflags[sub-subsectors] = + (sub->numlines > 2) ? SSRF_PROCESSED|SSRF_RENDERALL : SSRF_PROCESSED; + if (sub->hacked & 1) gl_drawinfo->AddHackedSubsector(sub); + + FPortal *portal; + + portal = fakesector->portals[sector_t::ceiling]; + if (portal != NULL) + { + GLSectorStackPortal *glportal = portal->GetGLPortal(); + glportal->AddSubsector(sub); + } + + portal = fakesector->portals[sector_t::floor]; + if (portal != NULL) + { + GLSectorStackPortal *glportal = portal->GetGLPortal(); + glportal->AddSubsector(sub); + } + } + } + } +} + + + + +//========================================================================== +// +// RenderBSPNode +// Renders all subsectors below a given node, +// traversing subtree recursively. +// Just call with BSP root. +// +//========================================================================== + +void gl_RenderBSPNode (void *node) +{ + if (numnodes == 0) + { + DoSubsector (subsectors); + return; + } + while (!((size_t)node & 1)) // Keep going until found a subsector + { + node_t *bsp = (node_t *)node; + + // Decide which side the view point is on. + int side = R_PointOnSide(viewx, viewy, bsp); + + // Recursively divide front space (toward the viewer). + gl_RenderBSPNode (bsp->children[side]); + + // Possibly divide back space (away from the viewer). + side ^= 1; + + // It is not necessary to use the slower precise version here + if (!clipper.CheckBox(bsp->bbox[side])) + { + if (!(gl_drawinfo->no_renderflags[bsp-nodes] & SSRF_SEEN)) + return; + } + + node = bsp->children[side]; + } + DoSubsector ((subsector_t *)((BYTE *)node - 1)); +} + + diff --git a/src/gl/scene/gl_clipper.cpp b/src/gl/scene/gl_clipper.cpp new file mode 100644 index 000000000..c6f8d9019 --- /dev/null +++ b/src/gl/scene/gl_clipper.cpp @@ -0,0 +1,458 @@ +/* +* +** gl_clipper.cpp +** +** Handles visibility checks. +** Loosely based on the JDoom clipper. +** +**--------------------------------------------------------------------------- +** Copyright 2003 Tim Stump +** 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. +** +** 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/scene/gl_clipper.h" + + + +ClipNode * ClipNode::freelist; +int Clipper::anglecache; + + +//----------------------------------------------------------------------------- +// +// Destructor +// +//----------------------------------------------------------------------------- + +Clipper::~Clipper() +{ + Clear(); + while (ClipNode::freelist != NULL) + { + ClipNode * node = ClipNode::freelist; + ClipNode::freelist = node->next; + delete node; + } +} + +//----------------------------------------------------------------------------- +// +// RemoveRange +// +//----------------------------------------------------------------------------- + +void Clipper::RemoveRange(ClipNode * range) +{ + if (range == cliphead) + { + cliphead = cliphead->next; + } + else + { + if (range->prev) range->prev->next = range->next; + if (range->next) range->next->prev = range->prev; + } + + range->Free(); +} + +//----------------------------------------------------------------------------- +// +// Clear +// +//----------------------------------------------------------------------------- + +void Clipper::Clear() +{ + ClipNode *node = cliphead; + ClipNode *temp; + + while (node != NULL) + { + temp = node; + node = node->next; + temp->Free(); + } + node = silhouette; + + while (node != NULL) + { + temp = node; + node = node->next; + temp->Free(); + } + + cliphead = NULL; + silhouette = NULL; + anglecache++; +} + +//----------------------------------------------------------------------------- +// +// SetSilhouette +// +//----------------------------------------------------------------------------- + +void Clipper::SetSilhouette() +{ + ClipNode *node = cliphead; + ClipNode *last = NULL; + + while (node != NULL) + { + ClipNode *snode = ClipNode::NewRange(node->start, node->end); + if (silhouette == NULL) silhouette = snode; + snode->prev = last; + if (last != NULL) last->next = snode; + last = snode; + node = node->next; + } +} + + +//----------------------------------------------------------------------------- +// +// IsRangeVisible +// +//----------------------------------------------------------------------------- + +bool Clipper::IsRangeVisible(angle_t startAngle, angle_t endAngle) +{ + ClipNode *ci; + ci = cliphead; + + if (endAngle==0 && ci && ci->start==0) return false; + + while (ci != NULL && ci->start < endAngle) + { + if (startAngle >= ci->start && endAngle <= ci->end) + { + return false; + } + ci = ci->next; + } + + return true; +} + +//----------------------------------------------------------------------------- +// +// AddClipRange +// +//----------------------------------------------------------------------------- + +void Clipper::AddClipRange(angle_t start, angle_t end) +{ + ClipNode *node, *temp, *prevNode; + + if (cliphead) + { + //check to see if range contains any old ranges + node = cliphead; + while (node != NULL && node->start < end) + { + if (node->start >= start && node->end <= end) + { + temp = node; + node = node->next; + RemoveRange(temp); + } + else if (node->start<=start && node->end>=end) + { + return; + } + else + { + node = node->next; + } + } + + //check to see if range overlaps a range (or possibly 2) + node = cliphead; + while (node != NULL && node->start <= end) + { + if (node->end >= start) + { + // we found the first overlapping node + if (node->start > start) + { + // the new range overlaps with this node's start point + node->start = start; + } + + if (node->end < end) + { + node->end = end; + } + + ClipNode *node2 = node->next; + while (node2 && node2->start <= node->end) + { + if (node2->end > node->end) node->end = node2->end; + ClipNode *delnode = node2; + node2 = node2->next; + RemoveRange(delnode); + } + return; + } + node = node->next; + } + + //just add range + node = cliphead; + prevNode = NULL; + temp = ClipNode::NewRange(start, end); + + while (node != NULL && node->start < end) + { + prevNode = node; + node = node->next; + } + + temp->next = node; + if (node == NULL) + { + temp->prev = prevNode; + if (prevNode) prevNode->next = temp; + if (!cliphead) cliphead = temp; + } + else + { + if (node == cliphead) + { + cliphead->prev = temp; + cliphead = temp; + } + else + { + temp->prev = prevNode; + prevNode->next = temp; + node->prev = temp; + } + } + } + else + { + temp = ClipNode::NewRange(start, end); + cliphead = temp; + return; + } +} + + +//----------------------------------------------------------------------------- +// +// RemoveClipRange +// +//----------------------------------------------------------------------------- + +void Clipper::RemoveClipRange(angle_t start, angle_t end) +{ + ClipNode *node; + + if (silhouette) + { + node = silhouette; + while (node != NULL && node->end <= start) + { + node = node->next; + } + if (node != NULL && node->start <= start) + { + if (node->end >= end) return; + start = node->end; + node = node->next; + } + while (node != NULL && node->start < end) + { + DoRemoveClipRange(start, node->start); + start = node->end; + node = node->next; + } + if (start >= end) return; + } + DoRemoveClipRange(start, end); +} + +//----------------------------------------------------------------------------- +// +// RemoveClipRange worker function +// +//----------------------------------------------------------------------------- + +void Clipper::DoRemoveClipRange(angle_t start, angle_t end) +{ + ClipNode *node, *temp; + + if (cliphead) + { + //check to see if range contains any old ranges + node = cliphead; + while (node != NULL && node->start < end) + { + if (node->start >= start && node->end <= end) + { + temp = node; + node = node->next; + RemoveRange(temp); + } + else + { + node = node->next; + } + } + + //check to see if range overlaps a range (or possibly 2) + node = cliphead; + while (node != NULL) + { + if (node->start >= start && node->start <= end) + { + node->start = end; + break; + } + else if (node->end >= start && node->end <= end) + { + node->end=start; + } + else if (node->start < start && node->end > end) + { + temp=ClipNode::NewRange(end, node->end); + node->end=start; + temp->next=node->next; + temp->prev=node; + node->next=temp; + if (temp->next) temp->next->prev=temp; + break; + } + node = node->next; + } + } +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +angle_t Clipper::AngleToPseudo(angle_t ang) +{ + double vecx = cos(ang * M_PI / ANGLE_180); + double vecy = sin(ang * M_PI / ANGLE_180); + + double result = vecy / (fabs(vecx) + fabs(vecy)); + if (vecx < 0) + { + result = 2.f - result; + } + return xs_Fix<30>::ToFix(result); +} + +//----------------------------------------------------------------------------- +// +// ! Returns the pseudoangle between the line p1 to (infinity, p1.y) and the +// line from p1 to p2. The pseudoangle has the property that the ordering of +// points by true angle anround p1 and ordering of points by pseudoangle are the +// same. +// +// For clipping exact angles are not needed. Only the ordering matters. +// This is about as fast as the fixed point R_PointToAngle2 but without +// the precision issues associated with that function. +// +//----------------------------------------------------------------------------- + +angle_t R_PointToPseudoAngle (fixed_t viewx, fixed_t viewy, fixed_t x, fixed_t y) +{ + // Note: float won't work here as it's less precise than the BAM values being passed as parameters + double vecx = double(x-viewx); + double vecy = double(y-viewy); + + if (vecx == 0 && vecy == 0) + { + return 0; + } + else + { + double result = vecy / (fabs(vecx) + fabs(vecy)); + if (vecx < 0) + { + result = 2.f - result; + } + return xs_Fix<30>::ToFix(result); + } +} + + + + +//----------------------------------------------------------------------------- +// +// R_CheckBBox +// Checks BSP node/subtree bounding box. +// Returns true +// if some part of the bbox might be visible. +// +//----------------------------------------------------------------------------- + static const int checkcoord[12][4] = // killough -- static const + { + {3,0,2,1}, + {3,0,2,0}, + {3,1,2,0}, + {0}, + {2,0,2,1}, + {0,0,0,0}, + {3,1,3,0}, + {0}, + {2,0,3,1}, + {2,1,3,1}, + {2,1,3,0} + }; + +bool Clipper::CheckBox(const fixed_t *bspcoord) +{ + angle_t angle1, angle2; + + int boxpos; + const int* check; + + // Find the corners of the box + // that define the edges from current viewpoint. + boxpos = (viewx <= bspcoord[BOXLEFT] ? 0 : viewx < bspcoord[BOXRIGHT ] ? 1 : 2) + + (viewy >= bspcoord[BOXTOP ] ? 0 : viewy > bspcoord[BOXBOTTOM] ? 4 : 8); + + if (boxpos == 5) return true; + + check = checkcoord[boxpos]; + angle1 = R_PointToPseudoAngle (viewx, viewy, bspcoord[check[0]], bspcoord[check[1]]); + angle2 = R_PointToPseudoAngle (viewx, viewy, bspcoord[check[2]], bspcoord[check[3]]); + + return SafeCheckRange(angle2, angle1); +} + diff --git a/src/gl/scene/gl_clipper.h b/src/gl/scene/gl_clipper.h new file mode 100644 index 000000000..637839f4f --- /dev/null +++ b/src/gl/scene/gl_clipper.h @@ -0,0 +1,152 @@ +#ifndef __GL_CLIPPER +#define __GL_CLIPPER + +#include "doomtype.h" +#include "tables.h" +#include "xs_Float.h" +#include "r_utility.h" + +class ClipNode +{ + friend class Clipper; + friend class ClipNodesFreer; + + ClipNode *prev, *next; + angle_t start, end; + static ClipNode * freelist; + + bool operator== (const ClipNode &other) + { + return other.start == start && other.end == end; + } + + void Free() + { + next=freelist; + freelist=this; + } + + static ClipNode * GetNew() + { + if (freelist) + { + ClipNode * p=freelist; + freelist=p->next; + return p; + } + else return new ClipNode; + } + + static ClipNode * NewRange(angle_t start, angle_t end) + { + ClipNode * c=GetNew(); + + c->start=start; + c->end=end; + c->next=c->prev=NULL; + return c; + } + +}; + + +class Clipper +{ + ClipNode * clipnodes; + ClipNode * cliphead; + ClipNode * silhouette; // will be preserved even when RemoveClipRange is called + + static angle_t AngleToPseudo(angle_t ang); + bool IsRangeVisible(angle_t startangle, angle_t endangle); + void RemoveRange(ClipNode * cn); + void AddClipRange(angle_t startangle, angle_t endangle); + void RemoveClipRange(angle_t startangle, angle_t endangle); + void DoRemoveClipRange(angle_t start, angle_t end); + +public: + + static int anglecache; + + Clipper() + { + clipnodes=cliphead=NULL; + } + + ~Clipper(); + + void Clear(); + + + void SetSilhouette(); + + bool SafeCheckRange(angle_t startAngle, angle_t endAngle) + { + if(startAngle > endAngle) + { + return (IsRangeVisible(startAngle, ANGLE_MAX) || IsRangeVisible(0, endAngle)); + } + + return IsRangeVisible(startAngle, endAngle); + } + + void SafeAddClipRange(angle_t startangle, angle_t endangle) + { + if(startangle > endangle) + { + // The range has to added in two parts. + AddClipRange(startangle, ANGLE_MAX); + AddClipRange(0, endangle); + } + else + { + // Add the range as usual. + AddClipRange(startangle, endangle); + } + } + + void SafeAddClipRangeRealAngles(angle_t startangle, angle_t endangle) + { + SafeAddClipRange(AngleToPseudo(startangle), AngleToPseudo(endangle)); + } + + + void SafeRemoveClipRange(angle_t startangle, angle_t endangle) + { + if(startangle > endangle) + { + // The range has to added in two parts. + RemoveClipRange(startangle, ANGLE_MAX); + RemoveClipRange(0, endangle); + } + else + { + // Add the range as usual. + RemoveClipRange(startangle, endangle); + } + } + + void SafeRemoveClipRangeRealAngles(angle_t startangle, angle_t endangle) + { + SafeRemoveClipRange(AngleToPseudo(startangle), AngleToPseudo(endangle)); + } + + bool CheckBox(const fixed_t *bspcoord); +}; + + +extern Clipper clipper; + +angle_t R_PointToPseudoAngle (fixed_t viewx, fixed_t viewy, fixed_t x, fixed_t y); + +inline angle_t R_PointToAnglePrecise (fixed_t viewx, fixed_t viewy, fixed_t x, fixed_t y) +{ + return xs_RoundToUInt(atan2(double(y-viewy), double(x-viewx)) * (ANGLE_180/M_PI)); +} + +// Used to speed up angle calculations during clipping +inline angle_t vertex_t::GetClipAngle() +{ + return angletime == Clipper::anglecache? viewangle : (angletime = Clipper::anglecache, viewangle = R_PointToPseudoAngle(viewx, viewy, x,y)); +} + +#endif \ No newline at end of file diff --git a/src/gl/scene/gl_decal.cpp b/src/gl/scene/gl_decal.cpp new file mode 100644 index 000000000..b6478dd22 --- /dev/null +++ b/src/gl/scene/gl_decal.cpp @@ -0,0 +1,428 @@ +/* +** gl_decal.cpp +** OpenGL decal rendering code +** +**--------------------------------------------------------------------------- +** Copyright 2003-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "doomdata.h" +#include "gl/system/gl_system.h" +#include "a_sharedglobal.h" +#include "r_utility.h" + +#include "gl/system/gl_cvars.h" +#include "gl/data/gl_data.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_texture.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_clock.h" + +struct DecalVertex +{ + float x,y,z; + float u,v; +}; + +//========================================================================== +// +// +// +//========================================================================== +void GLWall::DrawDecal(DBaseDecal *decal) +{ + line_t * line=seg->linedef; + side_t * side=seg->sidedef; + int i; + fixed_t zpos; + int light; + int rel; + float a; + bool flipx, flipy, loadAlpha; + DecalVertex dv[4]; + FTextureID decalTile; + + + if (decal->RenderFlags & RF_INVISIBLE) return; + if (type==RENDERWALL_FFBLOCK && gltexture->isMasked()) return; // No decals on 3D floors with transparent textures. + + //if (decal->sprite != 0xffff) + { + decalTile = decal->PicNum; + flipx = !!(decal->RenderFlags & RF_XFLIP); + flipy = !!(decal->RenderFlags & RF_YFLIP); + } + /* + else + { + decalTile = SpriteFrames[sprites[decal->sprite].spriteframes + decal->frame].lump[0]; + flipx = SpriteFrames[sprites[decal->sprite].spriteframes + decal->frame].flip & 1; + } + */ + + FTexture *texture = TexMan[decalTile]; + if (texture == NULL) return; + + FMaterial *tex; + + + if (texture->UseType == FTexture::TEX_MiscPatch) + { + // We need to create a clone of this texture where we can force the + // texture filtering offset in. + if (texture->gl_info.DecalTexture == NULL) + { + texture->gl_info.DecalTexture = new FCloneTexture(texture, FTexture::TEX_Decal); + } + tex = FMaterial::ValidateTexture(texture->gl_info.DecalTexture); + } + else tex = FMaterial::ValidateTexture(texture); + + + // the sectors are only used for their texture origin coordinates + // so we don't need the fake sectors for deep water etc. + // As this is a completely split wall fragment no further splits are + // necessary for the decal. + sector_t *frontsector; + + // for 3d-floor segments use the model sector as reference + if ((decal->RenderFlags&RF_CLIPMASK)==RF_CLIPMID) frontsector=decal->Sector; + else frontsector=seg->frontsector; + + switch (decal->RenderFlags & RF_RELMASK) + { + default: + // No valid decal can have this type. If one is encountered anyway + // it is in some way invalid so skip it. + return; + //zpos = decal->z; + //break; + + case RF_RELUPPER: + if (type!=RENDERWALL_TOP) return; + if (line->flags & ML_DONTPEGTOP) + { + zpos = decal->Z + frontsector->GetPlaneTexZ(sector_t::ceiling); + } + else + { + zpos = decal->Z + seg->backsector->GetPlaneTexZ(sector_t::ceiling); + } + break; + case RF_RELLOWER: + if (type!=RENDERWALL_BOTTOM) return; + if (line->flags & ML_DONTPEGBOTTOM) + { + zpos = decal->Z + frontsector->GetPlaneTexZ(sector_t::ceiling); + } + else + { + zpos = decal->Z + seg->backsector->GetPlaneTexZ(sector_t::floor); + } + break; + case RF_RELMID: + if (type==RENDERWALL_TOP || type==RENDERWALL_BOTTOM) return; + if (line->flags & ML_DONTPEGBOTTOM) + { + zpos = decal->Z + frontsector->GetPlaneTexZ(sector_t::floor); + } + else + { + zpos = decal->Z + frontsector->GetPlaneTexZ(sector_t::ceiling); + } + } + + if (decal->RenderFlags & RF_FULLBRIGHT) + { + light = 255; + rel = 0; + } + else + { + light = lightlevel; + rel = rellight + getExtraLight(); + } + + int r = RPART(decal->AlphaColor); + int g = GPART(decal->AlphaColor); + int b = BPART(decal->AlphaColor); + FColormap p = Colormap; + + if (glset.nocoloredspritelighting) + { + int v = (Colormap.LightColor.r * 77 + Colormap.LightColor.g*143 + Colormap.LightColor.b*35)/255; + p.LightColor = PalEntry(p.colormap, v, v, v); + } + + float red, green, blue; + + if (decal->RenderStyle.Flags & STYLEF_RedIsAlpha) + { + loadAlpha = true; + p.colormap=CM_SHADE; + + if (glset.lightmode != 8) + { + gl_GetLightColor(light, rel, &p, &red, &green, &blue); + } + else + { + gl_GetLightColor(lightlevel, rellight, &p, &red, &green, &blue); + } + + if (gl_lights && GLRenderer->mLightCount && !gl_fixedcolormap && gl_light_sprites) + { + float result[3]; + fixed_t x, y; + decal->GetXY(seg->sidedef, x, y); + gl_GetSpriteLight(NULL, x, y, zpos, sub, Colormap.colormap-CM_DESAT0, result, line, side == line->sidedef[0]? 0:1); + if (glset.lightmode != 8) + { + red = clamp(result[0]+red, 0, 1.0f); + green = clamp(result[1]+green, 0, 1.0f); + blue = clamp(result[2]+blue, 0, 1.0f); + } + else + { + gl_RenderState.SetDynLight(result[0], result[1], result[2]); + } + } + + BYTE R = xs_RoundToInt(r * red); + BYTE G = xs_RoundToInt(g * green); + BYTE B = xs_RoundToInt(b * blue); + + gl_ModifyColor(R,G,B, Colormap.colormap); + + red = R/255.f; + green = G/255.f; + blue = B/255.f; + } + else + { + loadAlpha = false; + + red = 1.f; + green = 1.f; + blue = 1.f; + } + + + a = FIXED2FLOAT(decal->Alpha); + + // now clip the decal to the actual polygon + float decalwidth = tex->TextureWidth(GLUSE_PATCH) * FIXED2FLOAT(decal->ScaleX); + float decalheight= tex->TextureHeight(GLUSE_PATCH) * FIXED2FLOAT(decal->ScaleY); + float decallefto = tex->GetLeftOffset(GLUSE_PATCH) * FIXED2FLOAT(decal->ScaleX); + float decaltopo = tex->GetTopOffset(GLUSE_PATCH) * FIXED2FLOAT(decal->ScaleY); + + + float leftedge = glseg.fracleft * side->TexelLength; + float linelength = glseg.fracright * side->TexelLength - leftedge; + + // texel index of the decal's left edge + float decalpixpos = (float)side->TexelLength * decal->LeftDistance / (1<<30) - (flipx? decalwidth-decallefto : decallefto) - leftedge; + + float left,right; + float lefttex,righttex; + + // decal is off the left edge + if (decalpixpos < 0) + { + left = 0; + lefttex = -decalpixpos; + } + else + { + left = decalpixpos; + lefttex = 0; + } + + // decal is off the right edge + if (decalpixpos + decalwidth > linelength) + { + right = linelength; + righttex = right - decalpixpos; + } + else + { + right = decalpixpos + decalwidth; + righttex = decalwidth; + } + if (right<=left) return; // nothing to draw + + // one texture unit on the wall as vector + float vx=(glseg.x2-glseg.x1)/linelength; + float vy=(glseg.y2-glseg.y1)/linelength; + + dv[1].x=dv[0].x=glseg.x1+vx*left; + dv[1].y=dv[0].y=glseg.y1+vy*left; + + dv[3].x=dv[2].x=glseg.x1+vx*right; + dv[3].y=dv[2].y=glseg.y1+vy*right; + + zpos+= FRACUNIT*(flipy? decalheight-decaltopo : decaltopo); + + tex->BindPatch(p.colormap, decal->Translation); + + dv[1].z=dv[2].z = FIXED2FLOAT(zpos); + dv[0].z=dv[3].z = dv[1].z - decalheight; + dv[1].v=dv[2].v = tex->GetVT(); + + dv[1].u=dv[0].u = tex->GetU(lefttex / FIXED2FLOAT(decal->ScaleX)); + dv[3].u=dv[2].u = tex->GetU(righttex / FIXED2FLOAT(decal->ScaleX)); + dv[0].v=dv[3].v = tex->GetVB(); + + + // now clip to the top plane + float vzt=(ztop[1]-ztop[0])/linelength; + float topleft=this->ztop[0]+vzt*left; + float topright=this->ztop[0]+vzt*right; + + // completely below the wall + if (topleftzbottom[0]+vzb*left; + float bottomright=this->zbottom[0]+vzb*right; + + // completely above the wall + if (bottomleft>dv[1].z && bottomright>dv[2].z) + return; + + if (bottomleft>dv[0].z || bottomright>dv[3].z) + { + // decal has to be clipped at the bottom + // let texture clamping handle all extreme cases + dv[0].v=(dv[1].z-bottomleft)/(dv[1].z-dv[0].z)*(dv[0].v-dv[1].v) + dv[1].v; + dv[3].v=(dv[2].z-bottomright)/(dv[2].z-dv[3].z)*(dv[3].v-dv[2].v) + dv[2].v; + dv[0].z=bottomleft; + dv[3].z=bottomright; + } + + + if (flipx) + { + float ur = tex->GetUR(); + for(i=0;i<4;i++) dv[i].u=ur-dv[i].u; + } + if (flipy) + { + float vb = tex->GetVB(); + for(i=0;i<4;i++) dv[i].v=vb-dv[i].v; + } + // fog is set once per wall in the calling function and not per decal! + + if (loadAlpha) + { + gl.Color4f(red, green, blue, a); + + if (glset.lightmode == 8) + { + if (gl_fixedcolormap) + gl.VertexAttrib1f(VATTR_LIGHTLEVEL, 1.0); + else + gl.VertexAttrib1f(VATTR_LIGHTLEVEL, gl_CalcLightLevel(light, rel, false) / 255.0); + } + } + else + { + if (glset.lightmode == 8) + { + gl_SetColor(light, rel, &p, a, extralight); // Korshun. + } + else + { + gl_SetColor(light, rel, &p, a); + } + } + + PalEntry fc = gl_RenderState.GetFogColor(); + if (decal->RenderStyle.BlendOp == STYLEOP_Add && decal->RenderStyle.DestAlpha == STYLEALPHA_One) + { + gl_RenderState.SetFog(0,-1); + } + + + gl_SetRenderStyle(decal->RenderStyle, false, false); + + // If srcalpha is one it looks better with a higher alpha threshold + if (decal->RenderStyle.SrcAlpha == STYLEALPHA_One) gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold); + else gl_RenderState.AlphaFunc(GL_GREATER, 0.f); + + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_FAN); + for(i=0;i<4;i++) + { + gl.TexCoord2f(dv[i].u,dv[i].v); + gl.Vertex3f(dv[i].x,dv[i].z,dv[i].y); + } + gl.End(); + rendered_decals++; + gl_RenderState.SetFog(fc,-1); + gl_RenderState.SetDynLight(0,0,0); +} + +//========================================================================== +// +// +// +//========================================================================== +void GLWall::DoDrawDecals() +{ + DBaseDecal *decal = seg->sidedef->AttachedDecals; + while (decal) + { + DrawDecal(decal); + decal = decal->WallNext; + } +} + diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp new file mode 100644 index 000000000..a03cb01e2 --- /dev/null +++ b/src/gl/scene/gl_drawinfo.cpp @@ -0,0 +1,1221 @@ +/* +** gl_drawinfo.cpp +** Implements the draw info structure which contains most of the +** data in a scene and the draw lists - including a very thorough BSP +** style sorting algorithm for translucent objects. +** +**--------------------------------------------------------------------------- +** Copyright 2002-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "r_sky.h" +#include "r_utility.h" +#include "r_state.h" +#include "doomstat.h" + +#include "gl/system/gl_cvars.h" +#include "gl/data/gl_data.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_portal.h" +#include "gl/dynlights/gl_lightbuffer.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_templates.h" +#include "gl/shaders/gl_shader.h" + +FDrawInfo * gl_drawinfo; + +CVAR(Bool, gl_sort_textures, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +//========================================================================== +// +// +// +//========================================================================== +class StaticSortNodeArray : public TDeletingArray +{ + unsigned usecount; +public: + unsigned Size() { return usecount; } + void Clear() { usecount=0; } + void Release(int start) { usecount=start; } + SortNode * GetNew(); +}; + + +SortNode * StaticSortNodeArray::GetNew() +{ + if (usecount==TArray::Size()) + { + Push(new SortNode); + } + return operator[](usecount++); +} + + +static StaticSortNodeArray SortNodes; + +//========================================================================== +// +// +// +//========================================================================== +void GLDrawList::Reset() +{ + if (sorted) SortNodes.Release(SortNodeStart); + sorted=NULL; + walls.Clear(); + flats.Clear(); + sprites.Clear(); + drawitems.Clear(); +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// Translucent polygon sorting - uses a BSP algorithm with an additional 'equal' branch + +inline double GLSprite::CalcIntersectionVertex(GLWall * w2) +{ + float ax = x1, ay=y1; + float bx = x2, by=y2; + float cx = w2->glseg.x1, cy=w2->glseg.y1; + float dx = w2->glseg.x2, dy=w2->glseg.y2; + return ((ay-cy)*(dx-cx)-(ax-cx)*(dy-cy)) / ((bx-ax)*(dy-cy)-(by-ay)*(dx-cx)); +} + + + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::UnlinkFromChain() +{ + if (parent) parent->next=next; + if (next) next->parent=parent; + parent=next=NULL; +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::Link(SortNode * hook) +{ + if (hook) + { + parent=hook->parent; + hook->parent=this; + } + next=hook; + if (parent) parent->next=this; +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::AddToEqual(SortNode *child) +{ + child->UnlinkFromChain(); + child->equal=equal; + equal=child; +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::AddToLeft(SortNode * child) +{ + child->UnlinkFromChain(); + child->Link(left); + left=child; +} + +//========================================================================== +// +// +// +//========================================================================== +inline void SortNode::AddToRight(SortNode * child) +{ + child->UnlinkFromChain(); + child->Link(right); + right=child; +} + + +//========================================================================== +// +// +// +//========================================================================== +void GLDrawList::MakeSortList() +{ + SortNode * p, * n, * c; + unsigned i; + + SortNodeStart=SortNodes.Size(); + p=NULL; + n=SortNodes.GetNew(); + for(i=0;iitemindex=(int)i; + n->left=n->equal=n->right=NULL; + n->parent=p; + p=n; + if (i!=drawitems.Size()-1) + { + c=SortNodes.GetNew(); + n->next=c; + n=c; + } + else + { + n->next=NULL; + } + } +} + + +//========================================================================== +// +// +// +//========================================================================== +SortNode * GLDrawList::FindSortPlane(SortNode * head) +{ + while (head->next && drawitems[head->itemindex].rendertype!=GLDIT_FLAT) + head=head->next; + if (drawitems[head->itemindex].rendertype==GLDIT_FLAT) return head; + return NULL; +} + + +//========================================================================== +// +// +// +//========================================================================== +SortNode * GLDrawList::FindSortWall(SortNode * head) +{ + fixed_t farthest=INT_MIN; + fixed_t nearest=INT_MAX; + SortNode * best=NULL; + SortNode * node=head; + fixed_t bestdist=INT_MAX; + + while (node) + { + GLDrawItem * it=&drawitems[node->itemindex]; + if (it->rendertype==GLDIT_WALL) + { + fixed_t d=walls[it->index].viewdistance; + if (d>farthest) farthest=d; + if (dnext; + } + if (farthest==INT_MIN) return NULL; + node=head; + farthest=(farthest+nearest)>>1; + while (node) + { + GLDrawItem * it=&drawitems[node->itemindex]; + if (it->rendertype==GLDIT_WALL) + { + fixed_t di=abs(walls[it->index].viewdistance-farthest); + if (!best || dinext; + } + return best; +} + +//========================================================================== +// +// Note: sloped planes are a huge problem... +// +//========================================================================== +void GLDrawList::SortPlaneIntoPlane(SortNode * head,SortNode * sort) +{ + GLFlat * fh=&flats[drawitems[head->itemindex].index]; + GLFlat * fs=&flats[drawitems[sort->itemindex].index]; + + if (fh->z==fs->z) + head->AddToEqual(sort); + else if ( (fh->zz && fh->ceiling) || (fh->z>fs->z && !fh->ceiling)) + head->AddToLeft(sort); + else + head->AddToRight(sort); +} + + +//========================================================================== +// +// +// +//========================================================================== +void GLDrawList::SortWallIntoPlane(SortNode * head,SortNode * sort) +{ + GLFlat * fh=&flats[drawitems[head->itemindex].index]; + GLWall * ws=&walls[drawitems[sort->itemindex].index]; + GLWall * ws1; + + bool ceiling = fh->z > FIXED2FLOAT(viewz); + + + if (ws->ztop[0]>fh->z && ws->zbottom[0]z) + { + // We have to split this wall! + + // WARNING: NEVER EVER push a member of an array onto the array itself. + // Bad things will happen if the memory must be reallocated! + GLWall w=*ws; + AddWall(&w); + + ws1=&walls[walls.Size()-1]; + ws=&walls[drawitems[sort->itemindex].index]; // may have been reallocated! + float newtexv = ws->uplft.v + ((ws->lolft.v - ws->uplft.v) / (ws->zbottom[0] - ws->ztop[0])) * (fh->z - ws->ztop[0]); + + // I make the very big assumption here that translucent walls in sloped sectors + // and 3D-floors never coexist in the same level. If that were the case this + // code would become extremely more complicated. + if (!ceiling) + { + ws->ztop[1] = ws1->zbottom[1] = ws->ztop[0] = ws1->zbottom[0] = fh->z; + ws->uprgt.v = ws1->lorgt.v = ws->uplft.v = ws1->lolft.v = newtexv; + } + else + { + ws1->ztop[1] = ws->zbottom[1] = ws1->ztop[0] = ws->zbottom[0] = fh->z; + ws1->uplft.v = ws->lolft.v = ws1->uprgt.v = ws->lorgt.v=newtexv; + } + + SortNode * sort2=SortNodes.GetNew(); + memset(sort2,0,sizeof(SortNode)); + sort2->itemindex=drawitems.Size()-1; + + head->AddToLeft(sort); + head->AddToRight(sort2); + } + else if ((ws->zbottom[0]z && !ceiling) || (ws->ztop[0]>fh->z && ceiling)) // completely on the left side + { + head->AddToLeft(sort); + } + else + { + head->AddToRight(sort); + } + +} + +//========================================================================== +// +// +// +//========================================================================== +void GLDrawList::SortSpriteIntoPlane(SortNode * head,SortNode * sort) +{ + GLFlat * fh=&flats[drawitems[head->itemindex].index]; + GLSprite * ss=&sprites[drawitems[sort->itemindex].index]; + GLSprite * ss1; + + bool ceiling = fh->z > FIXED2FLOAT(viewz); + + if (ss->z1>fh->z && ss->z2z) + { + // We have to split this sprite! + GLSprite s=*ss; + AddSprite(&s); + ss1=&sprites[sprites.Size()-1]; + ss=&sprites[drawitems[sort->itemindex].index]; // may have been reallocated! + float newtexv=ss->vt + ((ss->vb-ss->vt)/(ss->z2-ss->z1))*(fh->z-ss->z1); + + if (!ceiling) + { + ss->z1=ss1->z2=fh->z; + ss->vt=ss1->vb=newtexv; + } + else + { + ss1->z1=ss->z2=fh->z; + ss1->vt=ss->vb=newtexv; + } + + SortNode * sort2=SortNodes.GetNew(); + memset(sort2,0,sizeof(SortNode)); + sort2->itemindex=drawitems.Size()-1; + + head->AddToLeft(sort); + head->AddToRight(sort2); + } + else if ((ss->z2z && !ceiling) || (ss->z1>fh->z && ceiling)) // completely on the left side + { + head->AddToLeft(sort); + } + else + { + head->AddToRight(sort); + } + +} + + +//========================================================================== +// +// +// +//========================================================================== +#define MIN_EQ (0.0005f) + +void GLDrawList::SortWallIntoWall(SortNode * head,SortNode * sort) +{ + GLWall * wh=&walls[drawitems[head->itemindex].index]; + GLWall * ws=&walls[drawitems[sort->itemindex].index]; + GLWall * ws1; + float v1=wh->PointOnSide(ws->glseg.x1,ws->glseg.y1); + float v2=wh->PointOnSide(ws->glseg.x2,ws->glseg.y2); + + if (fabs(v1)type==RENDERWALL_FOGBOUNDARY && wh->type!=RENDERWALL_FOGBOUNDARY) + { + head->AddToRight(sort); + } + else if (ws->type!=RENDERWALL_FOGBOUNDARY && wh->type==RENDERWALL_FOGBOUNDARY) + { + head->AddToLeft(sort); + } + else + { + head->AddToEqual(sort); + } + } + else if (v1AddToLeft(sort); + } + else if (v1>-MIN_EQ && v2>-MIN_EQ) + { + head->AddToRight(sort); + } + else + { + double r=ws->CalcIntersectionVertex(wh); + + float ix=(float)(ws->glseg.x1+r*(ws->glseg.x2-ws->glseg.x1)); + float iy=(float)(ws->glseg.y1+r*(ws->glseg.y2-ws->glseg.y1)); + float iu=(float)(ws->uplft.u + r * (ws->uprgt.u - ws->uplft.u)); + float izt=(float)(ws->ztop[0]+r*(ws->ztop[1]-ws->ztop[0])); + float izb=(float)(ws->zbottom[0]+r*(ws->zbottom[1]-ws->zbottom[0])); + + GLWall w=*ws; + AddWall(&w); + ws1=&walls[walls.Size()-1]; + ws=&walls[drawitems[sort->itemindex].index]; // may have been reallocated! + + ws1->glseg.x1=ws->glseg.x2=ix; + ws1->glseg.y1=ws->glseg.y2=iy; + ws1->ztop[0]=ws->ztop[1]=izt; + ws1->zbottom[0]=ws->zbottom[1]=izb; + ws1->lolft.u = ws1->uplft.u = ws->lorgt.u = ws->uprgt.u = iu; + + SortNode * sort2=SortNodes.GetNew(); + memset(sort2,0,sizeof(SortNode)); + sort2->itemindex=drawitems.Size()-1; + + if (v1>0) + { + head->AddToLeft(sort2); + head->AddToRight(sort); + } + else + { + head->AddToLeft(sort); + head->AddToRight(sort2); + } + } +} + + +//========================================================================== +// +// +// +//========================================================================== +void GLDrawList::SortSpriteIntoWall(SortNode * head,SortNode * sort) +{ + GLWall * wh=&walls[drawitems[head->itemindex].index]; + GLSprite * ss=&sprites[drawitems[sort->itemindex].index]; + GLSprite * ss1; + + float v1 = wh->PointOnSide(ss->x1, ss->y1); + float v2 = wh->PointOnSide(ss->x2, ss->y2); + + if (fabs(v1)type==RENDERWALL_FOGBOUNDARY) + { + head->AddToLeft(sort); + } + else + { + head->AddToEqual(sort); + } + } + else if (v1AddToLeft(sort); + } + else if (v1>-MIN_EQ && v2>-MIN_EQ) + { + head->AddToRight(sort); + } + else + { + double r=ss->CalcIntersectionVertex(wh); + + float ix=(float)(ss->x1 + r * (ss->x2-ss->x1)); + float iy=(float)(ss->y1 + r * (ss->y2-ss->y1)); + float iu=(float)(ss->ul + r * (ss->ur-ss->ul)); + + GLSprite s=*ss; + AddSprite(&s); + ss1=&sprites[sprites.Size()-1]; + ss=&sprites[drawitems[sort->itemindex].index]; // may have been reallocated! + + ss1->x1=ss->x2=ix; + ss1->y1=ss->y2=iy; + ss1->ul=ss->ur=iu; + + SortNode * sort2=SortNodes.GetNew(); + memset(sort2,0,sizeof(SortNode)); + sort2->itemindex=drawitems.Size()-1; + + if (v1>0) + { + head->AddToLeft(sort2); + head->AddToRight(sort); + } + else + { + head->AddToLeft(sort); + head->AddToRight(sort2); + } + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +inline int GLDrawList::CompareSprites(SortNode * a,SortNode * b) +{ + GLSprite * s1=&sprites[drawitems[a->itemindex].index]; + GLSprite * s2=&sprites[drawitems[b->itemindex].index]; + + int res = s1->depth - s2->depth; + + if (res != 0) return -res; + else return (i_compatflags & COMPATF_SPRITESORT)? s1->index-s2->index : s2->index-s1->index; +} + +//========================================================================== +// +// +// +//========================================================================== +static GLDrawList * gd; +int __cdecl CompareSprite(const void * a,const void * b) +{ + return gd->CompareSprites(*(SortNode**)a,*(SortNode**)b); +} + +//========================================================================== +// +// +// +//========================================================================== +SortNode * GLDrawList::SortSpriteList(SortNode * head) +{ + SortNode * n; + int count; + unsigned i; + + static TArray sortspritelist; + + SortNode * parent=head->parent; + + sortspritelist.Clear(); + for(count=0,n=head;n;n=n->next) sortspritelist.Push(n); + gd=this; + qsort(&sortspritelist[0],sortspritelist.Size(),sizeof(SortNode *),CompareSprite); + for(i=0;inext=NULL; + if (parent) parent->equal=sortspritelist[i]; + parent=sortspritelist[i]; + } + return sortspritelist[0]; +} + +//========================================================================== +// +// +// +//========================================================================== +SortNode * GLDrawList::DoSort(SortNode * head) +{ + SortNode * node, * sn, * next; + + sn=FindSortPlane(head); + if (sn) + { + if (sn==head) head=head->next; + sn->UnlinkFromChain(); + node=head; + head=sn; + while (node) + { + next=node->next; + switch(drawitems[node->itemindex].rendertype) + { + case GLDIT_FLAT: + SortPlaneIntoPlane(head,node); + break; + + case GLDIT_WALL: + SortWallIntoPlane(head,node); + break; + + case GLDIT_SPRITE: + SortSpriteIntoPlane(head,node); + break; + case GLDIT_POLY: break; + } + node=next; + } + } + else + { + sn=FindSortWall(head); + if (sn) + { + if (sn==head) head=head->next; + sn->UnlinkFromChain(); + node=head; + head=sn; + while (node) + { + next=node->next; + switch(drawitems[node->itemindex].rendertype) + { + case GLDIT_WALL: + SortWallIntoWall(head,node); + break; + + case GLDIT_SPRITE: + SortSpriteIntoWall(head,node); + break; + case GLDIT_POLY: break; + case GLDIT_FLAT: break; + } + node=next; + } + } + else + { + return SortSpriteList(head); + } + } + if (head->left) head->left=DoSort(head->left); + if (head->right) head->right=DoSort(head->right); + return sn; +} + + +//========================================================================== +// +// +// +//========================================================================== +void GLDrawList::DoDraw(int pass, int i) +{ + switch(drawitems[i].rendertype) + { + case GLDIT_FLAT: + { + GLFlat * f=&flats[drawitems[i].index]; + RenderFlat.Clock(); + f->Draw(pass); + RenderFlat.Unclock(); + } + break; + + case GLDIT_WALL: + { + GLWall * w=&walls[drawitems[i].index]; + RenderWall.Clock(); + w->Draw(pass); + RenderWall.Unclock(); + } + break; + + case GLDIT_SPRITE: + { + GLSprite * s=&sprites[drawitems[i].index]; + RenderSprite.Clock(); + s->Draw(pass); + RenderSprite.Unclock(); + } + break; + case GLDIT_POLY: break; + } +} + +//========================================================================== +// +// +// +//========================================================================== +void GLDrawList::DoDrawSorted(SortNode * head) +{ + do + { + if (head->left) + { + DoDrawSorted(head->left); + } + DoDraw(GLPASS_TRANSLUCENT, head->itemindex); + if (head->equal) + { + SortNode * ehead=head->equal; + while (ehead) + { + DoDraw(GLPASS_TRANSLUCENT, ehead->itemindex); + ehead=ehead->equal; + } + } + } + while ((head=head->right)); +} + +//========================================================================== +// +// +// +//========================================================================== +void GLDrawList::DrawSorted() +{ + if (drawitems.Size()==0) return; + + if (!sorted) + { + MakeSortList(); + sorted=DoSort(SortNodes[SortNodeStart]); + } + DoDrawSorted(sorted); +} + +//========================================================================== +// +// +// +//========================================================================== +void GLDrawList::Draw(int pass) +{ + for(unsigned i=0;irendertype) + { + case GLDIT_FLAT: + { + GLFlat * f=&sortinfo->flats[di[i]->index]; + tx[i]=f->gltexture; + lights[i]=f->lightlevel; + clamp[i] = 0; + } + break; + + case GLDIT_WALL: + { + GLWall * w=&sortinfo->walls[di[i]->index]; + tx[i]=w->gltexture; + lights[i]=w->lightlevel; + clamp[i] = w->flags & 3; + } + break; + + case GLDIT_SPRITE: + { + GLSprite * s=&sortinfo->sprites[di[i]->index]; + tx[i]=s->gltexture; + lights[i]=s->lightlevel; + clamp[i] = 4; + } + break; + case GLDIT_POLY: break; + } + } + if (tx[0]!=tx[1]) return tx[0]-tx[1]; + if (clamp[0]!=clamp[1]) return clamp[0]-clamp[1]; // clamping forces different textures. + return lights[0]-lights[1]; +} + + +void GLDrawList::Sort() +{ + if (drawitems.Size()!=0 && gl_sort_textures) + { + sortinfo=this; + qsort(&drawitems[0], drawitems.Size(), sizeof(drawitems[0]), dicmp); + } +} + +//========================================================================== +// +// +// +//========================================================================== +void GLDrawList::AddWall(GLWall * wall) +{ + //@sync-drawinfo + drawitems.Push(GLDrawItem(GLDIT_WALL,walls.Push(*wall))); +} + +//========================================================================== +// +// +// +//========================================================================== +void GLDrawList::AddFlat(GLFlat * flat) +{ + //@sync-drawinfo + drawitems.Push(GLDrawItem(GLDIT_FLAT,flats.Push(*flat))); +} + +//========================================================================== +// +// +// +//========================================================================== +void GLDrawList::AddSprite(GLSprite * sprite) +{ + //@sync-drawinfo + drawitems.Push(GLDrawItem(GLDIT_SPRITE,sprites.Push(*sprite))); +} + + +//========================================================================== +// +// Try to reuse the lists as often as possible as they contain resources that +// are expensive to create and delete. +// +//========================================================================== + +FDrawInfo *FDrawInfoList::GetNew() +{ + if (mList.Size() > 0) + { + FDrawInfo *di; + mList.Pop(di); + return di; + } + return new FDrawInfo; +} + +void FDrawInfoList::Release(FDrawInfo * di) +{ + di->ClearBuffers(); + mList.Push(di); +} + +static FDrawInfoList di_list; + +//========================================================================== +// +// +// +//========================================================================== + +FDrawInfo::FDrawInfo() +{ + next = NULL; +} + +FDrawInfo::~FDrawInfo() +{ + ClearBuffers(); +} + + +//========================================================================== +// +// Sets up a new drawinfo struct +// +//========================================================================== +void FDrawInfo::StartDrawInfo() +{ + FDrawInfo *di=di_list.GetNew(); + di->StartScene(); +} + +void FDrawInfo::StartScene() +{ + ClearBuffers(); + + sectorrenderflags.Resize(numsectors); + ss_renderflags.Resize(numsubsectors); + no_renderflags.Resize(numsubsectors); + + memset(§orrenderflags[0], 0, numsectors*sizeof(sectorrenderflags[0])); + memset(&ss_renderflags[0], 0, numsubsectors*sizeof(ss_renderflags[0])); + memset(&no_renderflags[0], 0, numnodes*sizeof(no_renderflags[0])); + + next=gl_drawinfo; + gl_drawinfo=this; + for(int i=0;idrawlists[i].Reset(); + gl_drawinfo=di->next; + di_list.Release(di); +} + + +//========================================================================== +// +// Flood gaps with the back side's ceiling/floor texture +// This requires a stencil because the projected plane interferes with +// the depth buffer +// +//========================================================================== + +void FDrawInfo::SetupFloodStencil(wallseg * ws) +{ + int recursion = GLPortal::GetRecursion(); + + // Create stencil + gl.StencilFunc(GL_EQUAL,recursion,~0); // create stencil + gl.StencilOp(GL_KEEP,GL_KEEP,GL_INCR); // increment stencil of valid pixels + gl.ColorMask(0,0,0,0); // don't write to the graphics buffer + gl_RenderState.EnableTexture(false); + gl.Color3f(1,1,1); + gl.Enable(GL_DEPTH_TEST); + gl.DepthMask(true); + + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_FAN); + gl.Vertex3f(ws->x1, ws->z1, ws->y1); + gl.Vertex3f(ws->x1, ws->z2, ws->y1); + gl.Vertex3f(ws->x2, ws->z2, ws->y2); + gl.Vertex3f(ws->x2, ws->z1, ws->y2); + gl.End(); + + gl.StencilFunc(GL_EQUAL,recursion+1,~0); // draw sky into stencil + gl.StencilOp(GL_KEEP,GL_KEEP,GL_KEEP); // this stage doesn't modify the stencil + + gl.ColorMask(1,1,1,1); // don't write to the graphics buffer + gl_RenderState.EnableTexture(true); + gl.Disable(GL_DEPTH_TEST); + gl.DepthMask(false); +} + +void FDrawInfo::ClearFloodStencil(wallseg * ws) +{ + int recursion = GLPortal::GetRecursion(); + + gl.StencilOp(GL_KEEP,GL_KEEP,GL_DECR); + gl_RenderState.EnableTexture(false); + gl.ColorMask(0,0,0,0); // don't write to the graphics buffer + gl.Color3f(1,1,1); + + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_FAN); + gl.Vertex3f(ws->x1, ws->z1, ws->y1); + gl.Vertex3f(ws->x1, ws->z2, ws->y1); + gl.Vertex3f(ws->x2, ws->z2, ws->y2); + gl.Vertex3f(ws->x2, ws->z1, ws->y2); + gl.End(); + + // restore old stencil op. + gl.StencilOp(GL_KEEP,GL_KEEP,GL_KEEP); + gl.StencilFunc(GL_EQUAL,recursion,~0); + gl_RenderState.EnableTexture(true); + gl.ColorMask(1,1,1,1); + gl.Enable(GL_DEPTH_TEST); + gl.DepthMask(true); +} + +//========================================================================== +// +// Draw the plane segment into the gap +// +//========================================================================== +void FDrawInfo::DrawFloodedPlane(wallseg * ws, float planez, sector_t * sec, bool ceiling) +{ + GLSectorPlane plane; + int lightlevel; + FColormap Colormap; + FMaterial * gltexture; + + plane.GetFromSector(sec, ceiling); + + gltexture=FMaterial::ValidateTexture(plane.texture, true); + if (!gltexture) return; + + if (gl_fixedcolormap) + { + Colormap.GetFixedColormap(); + lightlevel=255; + } + else + { + Colormap=sec->ColorMap; + if (gltexture->tex->isFullbright()) + { + Colormap.LightColor.r = Colormap.LightColor.g = Colormap.LightColor.b = 0xff; + lightlevel=255; + } + else lightlevel=abs(ceiling? sec->GetCeilingLight() : sec->GetFloorLight()); + } + + int rel = getExtraLight(); + gl_SetColor(lightlevel, rel, &Colormap, 1.0f); + gl_SetFog(lightlevel, rel, &Colormap, false); + gltexture->Bind(Colormap.colormap); + + float fviewx = FIXED2FLOAT(viewx); + float fviewy = FIXED2FLOAT(viewy); + float fviewz = FIXED2FLOAT(viewz); + + gl_RenderState.Apply(); + + bool pushed = gl_SetPlaneTextureRotation(&plane, gltexture); + + gl.Begin(GL_TRIANGLE_FAN); + float prj_fac1 = (planez-fviewz)/(ws->z1-fviewz); + float prj_fac2 = (planez-fviewz)/(ws->z2-fviewz); + + float px1 = fviewx + prj_fac1 * (ws->x1-fviewx); + float py1 = fviewy + prj_fac1 * (ws->y1-fviewy); + + float px2 = fviewx + prj_fac2 * (ws->x1-fviewx); + float py2 = fviewy + prj_fac2 * (ws->y1-fviewy); + + float px3 = fviewx + prj_fac2 * (ws->x2-fviewx); + float py3 = fviewy + prj_fac2 * (ws->y2-fviewy); + + float px4 = fviewx + prj_fac1 * (ws->x2-fviewx); + float py4 = fviewy + prj_fac1 * (ws->y2-fviewy); + + gl.TexCoord2f(px1 / 64, -py1 / 64); + gl.Vertex3f(px1, planez, py1); + + gl.TexCoord2f(px2 / 64, -py2 / 64); + gl.Vertex3f(px2, planez, py2); + + gl.TexCoord2f(px3 / 64, -py3 / 64); + gl.Vertex3f(px3, planez, py3); + + gl.TexCoord2f(px4 / 64, -py4 / 64); + gl.Vertex3f(px4, planez, py4); + + gl.End(); + + if (pushed) + { + gl.PopMatrix(); + gl.MatrixMode(GL_MODELVIEW); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FDrawInfo::FloodUpperGap(seg_t * seg) +{ + wallseg ws; + sector_t ffake, bfake; + sector_t * fakefsector = gl_FakeFlat(seg->frontsector, &ffake, true); + sector_t * fakebsector = gl_FakeFlat(seg->backsector, &bfake, false); + + vertex_t * v1, * v2; + + // Although the plane can be sloped this code will only be called + // when the edge itself is not. + fixed_t backz = fakebsector->ceilingplane.ZatPoint(seg->v1); + fixed_t frontz = fakefsector->ceilingplane.ZatPoint(seg->v1); + + if (fakebsector->GetTexture(sector_t::ceiling)==skyflatnum) return; + if (backz < viewz) return; + + if (seg->sidedef == seg->linedef->sidedef[0]) + { + v1=seg->linedef->v1; + v2=seg->linedef->v2; + } + else + { + v1=seg->linedef->v2; + v2=seg->linedef->v1; + } + + ws.x1= FIXED2FLOAT(v1->x); + ws.y1= FIXED2FLOAT(v1->y); + ws.x2= FIXED2FLOAT(v2->x); + ws.y2= FIXED2FLOAT(v2->y); + + ws.z1= FIXED2FLOAT(frontz); + ws.z2= FIXED2FLOAT(backz); + + // Step1: Draw a stencil into the gap + SetupFloodStencil(&ws); + + // Step2: Project the ceiling plane into the gap + DrawFloodedPlane(&ws, ws.z2, fakebsector, true); + + // Step3: Delete the stencil + ClearFloodStencil(&ws); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FDrawInfo::FloodLowerGap(seg_t * seg) +{ + wallseg ws; + sector_t ffake, bfake; + sector_t * fakefsector = gl_FakeFlat(seg->frontsector, &ffake, true); + sector_t * fakebsector = gl_FakeFlat(seg->backsector, &bfake, false); + + vertex_t * v1, * v2; + + // Although the plane can be sloped this code will only be called + // when the edge itself is not. + fixed_t backz = fakebsector->floorplane.ZatPoint(seg->v1); + fixed_t frontz = fakefsector->floorplane.ZatPoint(seg->v1); + + + if (fakebsector->GetTexture(sector_t::floor) == skyflatnum) return; + if (fakebsector->GetPlaneTexZ(sector_t::floor) > viewz) return; + + if (seg->sidedef == seg->linedef->sidedef[0]) + { + v1=seg->linedef->v1; + v2=seg->linedef->v2; + } + else + { + v1=seg->linedef->v2; + v2=seg->linedef->v1; + } + + ws.x1= FIXED2FLOAT(v1->x); + ws.y1= FIXED2FLOAT(v1->y); + ws.x2= FIXED2FLOAT(v2->x); + ws.y2= FIXED2FLOAT(v2->y); + + ws.z2= FIXED2FLOAT(frontz); + ws.z1= FIXED2FLOAT(backz); + + // Step1: Draw a stencil into the gap + SetupFloodStencil(&ws); + + // Step2: Project the ceiling plane into the gap + DrawFloodedPlane(&ws, ws.z1, fakebsector, false); + + // Step3: Delete the stencil + ClearFloodStencil(&ws); +} diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h new file mode 100644 index 000000000..07735eb3f --- /dev/null +++ b/src/gl/scene/gl_drawinfo.h @@ -0,0 +1,279 @@ +#ifndef __GL_DRAWINFO_H +#define __GL_DRAWINFO_H + +#include "gl/scene/gl_wall.h" + +enum GLDrawItemType +{ + GLDIT_WALL, + GLDIT_FLAT, + GLDIT_SPRITE, + GLDIT_POLY, +}; + +enum DrawListType +{ + // These are organized so that the various multipass rendering modes + // have to be set as few times as possible + GLDL_LIGHT, + GLDL_LIGHTBRIGHT, + GLDL_LIGHTMASKED, + GLDL_LIGHTFOG, + GLDL_LIGHTFOGMASKED, + + GLDL_PLAIN, + GLDL_MASKED, + GLDL_FOG, + GLDL_FOGMASKED, + + GLDL_TRANSLUCENT, + GLDL_TRANSLUCENTBORDER, + + GLDL_TYPES, + + GLDL_FIRSTLIGHT = GLDL_LIGHT, + GLDL_LASTLIGHT = GLDL_LIGHTFOGMASKED, + GLDL_FIRSTNOLIGHT = GLDL_PLAIN, + GLDL_LASTNOLIGHT = GLDL_FOGMASKED, +}; + +enum Drawpasses +{ + GLPASS_BASE, // Draws the untextured surface only + GLPASS_BASE_MASKED, // Draws an untextured surface that is masked by the texture + GLPASS_PLAIN, // Draws a texture that isn't affected by dynamic lights with sector light settings + GLPASS_LIGHT, // Draws dynamic lights + GLPASS_LIGHT_ADDITIVE, // Draws additive dynamic lights + GLPASS_TEXTURE, // Draws the texture to be modulated with the light information on the base surface + GLPASS_DECALS, // Draws a decal + GLPASS_DECALS_NOFOG,// Draws a decal without setting the fog (used for passes that need a fog layer) + GLPASS_TRANSLUCENT, // Draws translucent objects + GLPASS_ALL // Everything at once, using shaders for dynamic lights +}; + +//========================================================================== +// +// Intermediate struct to link one draw item into a draw list +// +// unfortunately this struct must not contain pointers because +// the arrays may be reallocated! +// +//========================================================================== + +struct GLDrawItem +{ + GLDrawItemType rendertype; + int index; + + GLDrawItem(GLDrawItemType _rendertype,int _index) : rendertype(_rendertype),index(_index) {} +}; + +struct SortNode +{ + int itemindex; + SortNode * parent; + SortNode * next; // unsorted successor + SortNode * left; // left side of this node + SortNode * equal; // equal to this node + SortNode * right; // right side of this node + + + void UnlinkFromChain(); + void Link(SortNode * hook); + void AddToEqual(SortNode * newnode); + void AddToLeft (SortNode * newnode); + void AddToRight(SortNode * newnode); +}; + +//========================================================================== +// +// One draw list. This contains all info for one type of rendering data +// +//========================================================================== + +struct GLDrawList +{ +//private: + TArray walls; + TArray flats; + TArray sprites; + TArray drawitems; + int SortNodeStart; + SortNode * sorted; + +public: + GLDrawList() + { + next=NULL; + SortNodeStart=-1; + sorted=NULL; + } + + ~GLDrawList() + { + Reset(); + } + + void AddWall(GLWall * wall); + void AddFlat(GLFlat * flat); + void AddSprite(GLSprite * sprite); + void Reset(); + void Sort(); + + + void MakeSortList(); + SortNode * FindSortPlane(SortNode * head); + SortNode * FindSortWall(SortNode * head); + void SortPlaneIntoPlane(SortNode * head,SortNode * sort); + void SortWallIntoPlane(SortNode * head,SortNode * sort); + void SortSpriteIntoPlane(SortNode * head,SortNode * sort); + void SortWallIntoWall(SortNode * head,SortNode * sort); + void SortSpriteIntoWall(SortNode * head,SortNode * sort); + int CompareSprites(SortNode * a,SortNode * b); + SortNode * SortSpriteList(SortNode * head); + SortNode * DoSort(SortNode * head); + + void DoDraw(int pass, int index); + void DoDrawSorted(SortNode * node); + void DrawSorted(); + void Draw(int pass); + + GLDrawList * next; +} ; + + +//========================================================================== +// +// these are used to link faked planes due to missing textures to a sector +// +//========================================================================== +struct gl_subsectorrendernode +{ + gl_subsectorrendernode * next; + subsector_t * sub; +}; + + +struct FDrawInfo +{ + struct wallseg + { + float x1, y1, z1, x2, y2, z2; + }; + + bool temporary; + + + + struct MissingTextureInfo + { + seg_t * seg; + subsector_t * sub; + fixed_t planez; + fixed_t planezfront; + }; + + struct MissingSegInfo + { + seg_t * seg; + int MTI_Index; // tells us which MissingTextureInfo represents this seg. + }; + + struct SubsectorHackInfo + { + subsector_t * sub; + BYTE flags; + }; + + TArray sectorrenderflags; + TArray ss_renderflags; + TArray no_renderflags; + + TArray MissingUpperTextures; + TArray MissingLowerTextures; + + TArray MissingUpperSegs; + TArray MissingLowerSegs; + + TArray SubsectorHacks; + + TArray otherfloorplanes; + TArray otherceilingplanes; + + TArray CeilingStacks; + TArray FloorStacks; + + TArray HandledSubsectors; + + FDrawInfo * next; + GLDrawList drawlists[GLDL_TYPES]; + + FDrawInfo(); + ~FDrawInfo(); + void ClearBuffers(); + + bool DoOneSectorUpper(subsector_t * subsec, fixed_t planez); + bool DoOneSectorLower(subsector_t * subsec, fixed_t planez); + bool DoFakeBridge(subsector_t * subsec, fixed_t planez); + bool DoFakeCeilingBridge(subsector_t * subsec, fixed_t planez); + + bool CheckAnchorFloor(subsector_t * sub); + bool CollectSubsectorsFloor(subsector_t * sub, sector_t * anchor); + bool CheckAnchorCeiling(subsector_t * sub); + bool CollectSubsectorsCeiling(subsector_t * sub, sector_t * anchor); + void CollectSectorStacksCeiling(subsector_t * sub, sector_t * anchor); + void CollectSectorStacksFloor(subsector_t * sub, sector_t * anchor); + + void AddUpperMissingTexture(side_t * side, subsector_t *sub, fixed_t backheight); + void AddLowerMissingTexture(side_t * side, subsector_t *sub, fixed_t backheight); + void HandleMissingTextures(); + void DrawUnhandledMissingTextures(); + void AddHackedSubsector(subsector_t * sub); + void HandleHackedSubsectors(); + void AddFloorStack(sector_t * sec); + void AddCeilingStack(sector_t * sec); + void ProcessSectorStacks(); + + void AddOtherFloorPlane(int sector, gl_subsectorrendernode * node); + void AddOtherCeilingPlane(int sector, gl_subsectorrendernode * node); + + void StartScene(); + void SetupFloodStencil(wallseg * ws); + void ClearFloodStencil(wallseg * ws); + void DrawFloodedPlane(wallseg * ws, float planez, sector_t * sec, bool ceiling); + void FloodUpperGap(seg_t * seg); + void FloodLowerGap(seg_t * seg); + + static void StartDrawInfo(); + static void EndDrawInfo(); + + gl_subsectorrendernode * GetOtherFloorPlanes(unsigned int sector) + { + if (sector mList; + +public: + + FDrawInfo *GetNew(); + void Release(FDrawInfo *); +}; + + +extern FDrawInfo * gl_drawinfo; + +bool gl_SetPlaneTextureRotation(const GLSectorPlane * secplane, FMaterial * gltexture); +void gl_SetRenderStyle(FRenderStyle style, bool drawopaque, bool allowcolorblending); + +#endif \ No newline at end of file diff --git a/src/gl/scene/gl_fakeflat.cpp b/src/gl/scene/gl_fakeflat.cpp new file mode 100644 index 000000000..122bae0e6 --- /dev/null +++ b/src/gl/scene/gl_fakeflat.cpp @@ -0,0 +1,397 @@ +/* +** gl_fakeflat.cpp +** Fake flat functions to render stacked sectors +** +**--------------------------------------------------------------------------- +** Copyright 2001-2011 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "p_lnspec.h" +#include "p_local.h" +#include "a_sharedglobal.h" +#include "r_sky.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/scene/gl_clipper.h" +#include "gl/data/gl_data.h" + + +//========================================================================== +// +// Check whether the player can look beyond this line +// +//========================================================================== +CVAR(Bool, gltest_slopeopt, false, 0) + +bool gl_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector) +{ + line_t *linedef = sidedef->linedef; + fixed_t bs_floorheight1; + fixed_t bs_floorheight2; + fixed_t bs_ceilingheight1; + fixed_t bs_ceilingheight2; + fixed_t fs_floorheight1; + fixed_t fs_floorheight2; + fixed_t fs_ceilingheight1; + fixed_t fs_ceilingheight2; + + // Mirrors and horizons always block the view + //if (linedef->special==Line_Mirror || linedef->special==Line_Horizon) return true; + + // Lines with stacked sectors must never block! + + if (backsector->portals[sector_t::ceiling] != NULL || backsector->portals[sector_t::floor] != NULL || + frontsector->portals[sector_t::ceiling] != NULL || frontsector->portals[sector_t::floor] != NULL) + { + return false; + } + + // on large levels this distinction can save some time + // That's a lot of avoided multiplications if there's a lot to see! + + if (frontsector->ceilingplane.a | frontsector->ceilingplane.b) + { + fs_ceilingheight1=frontsector->ceilingplane.ZatPoint(linedef->v1); + fs_ceilingheight2=frontsector->ceilingplane.ZatPoint(linedef->v2); + } + else + { + fs_ceilingheight2=fs_ceilingheight1=frontsector->ceilingplane.d; + } + + if (frontsector->floorplane.a | frontsector->floorplane.b) + { + fs_floorheight1=frontsector->floorplane.ZatPoint(linedef->v1); + fs_floorheight2=frontsector->floorplane.ZatPoint(linedef->v2); + } + else + { + fs_floorheight2=fs_floorheight1=-frontsector->floorplane.d; + } + + if (backsector->ceilingplane.a | backsector->ceilingplane.b) + { + bs_ceilingheight1=backsector->ceilingplane.ZatPoint(linedef->v1); + bs_ceilingheight2=backsector->ceilingplane.ZatPoint(linedef->v2); + } + else + { + bs_ceilingheight2=bs_ceilingheight1=backsector->ceilingplane.d; + } + + if (backsector->floorplane.a | backsector->floorplane.b) + { + bs_floorheight1=backsector->floorplane.ZatPoint(linedef->v1); + bs_floorheight2=backsector->floorplane.ZatPoint(linedef->v2); + } + else + { + bs_floorheight2=bs_floorheight1=-backsector->floorplane.d; + } + + // now check for closed sectors! + if (bs_ceilingheight1<=fs_floorheight1 && bs_ceilingheight2<=fs_floorheight2) + { + FTexture * tex = TexMan(sidedef->GetTexture(side_t::top)); + if (!tex || tex->UseType==FTexture::TEX_Null) return false; + if (backsector->GetTexture(sector_t::ceiling)==skyflatnum && + frontsector->GetTexture(sector_t::ceiling)==skyflatnum) return false; + return true; + } + + if (fs_ceilingheight1<=bs_floorheight1 && fs_ceilingheight2<=bs_floorheight2) + { + FTexture * tex = TexMan(sidedef->GetTexture(side_t::bottom)); + if (!tex || tex->UseType==FTexture::TEX_Null) return false; + + // properly render skies (consider door "open" if both floors are sky): + if (backsector->GetTexture(sector_t::ceiling)==skyflatnum && + frontsector->GetTexture(sector_t::ceiling)==skyflatnum) return false; + return true; + } + + if (bs_ceilingheight1<=bs_floorheight1 && bs_ceilingheight2<=bs_floorheight2) + { + // preserve a kind of transparent door/lift special effect: + if (bs_ceilingheight1 < fs_ceilingheight1 || bs_ceilingheight2 < fs_ceilingheight2) + { + FTexture * tex = TexMan(sidedef->GetTexture(side_t::top)); + if (!tex || tex->UseType==FTexture::TEX_Null) return false; + } + if (bs_floorheight1 > fs_floorheight1 || bs_floorheight2 > fs_floorheight2) + { + FTexture * tex = TexMan(sidedef->GetTexture(side_t::bottom)); + if (!tex || tex->UseType==FTexture::TEX_Null) return false; + } + if (backsector->GetTexture(sector_t::ceiling)==skyflatnum && + frontsector->GetTexture(sector_t::ceiling)==skyflatnum) return false; + if (backsector->GetTexture(sector_t::floor)==skyflatnum && frontsector->GetTexture(sector_t::floor) + ==skyflatnum) return false; + return true; + } + + return false; +} + +//========================================================================== +// +// check for levels with exposed lower areas +// +//========================================================================== + +void gl_CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, sector_t *backsector) +{ + if (in_area==area_default && + (backsector->heightsec && !(backsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) && + (!frontsector->heightsec || frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) + { + sector_t * s = backsector->heightsec; + + fixed_t cz1 = frontsector->ceilingplane.ZatPoint(v1); + fixed_t cz2 = frontsector->ceilingplane.ZatPoint(v2); + fixed_t fz1 = s->floorplane.ZatPoint(v1); + fixed_t fz2 = s->floorplane.ZatPoint(v2); + + // allow some tolerance in case slopes are involved + if (cz1 <= fz1 + FRACUNIT/100 && cz2<=fz2 + FRACUNIT/100) + in_area=area_below; + else + in_area=area_normal; + } +} + + +//========================================================================== +// +// This is mostly like R_FakeFlat but with a few alterations necessitated +// by hardware rendering +// +//========================================================================== +sector_t * gl_FakeFlat(sector_t * sec, sector_t * dest, area_t in_area, bool back) +{ + if (!sec->heightsec || sec->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC || sec->heightsec==sec) + { + // check for backsectors with the ceiling lower than the floor. These will create + // visual glitches because upper amd lower textures overlap. + if (back && sec->planes[sector_t::floor].TexZ > sec->planes[sector_t::ceiling].TexZ) + { + if (!(sec->floorplane.a | sec->floorplane.b | sec->ceilingplane.a | sec->ceilingplane.b)) + { + *dest = *sec; + dest->ceilingplane=sec->floorplane; + dest->ceilingplane.FlipVert(); + dest->planes[sector_t::ceiling].TexZ = dest->planes[sector_t::floor].TexZ; + return dest; + } + } + return sec; + } + +#ifdef _MSC_VER +#ifdef _DEBUG + if (sec-sectors==560) + { + __asm nop + } +#endif +#endif + + if (in_area==area_above) + { + if (sec->heightsec->MoreFlags&SECF_FAKEFLOORONLY || sec->GetTexture(sector_t::ceiling)==skyflatnum) in_area=area_normal; + } + + int diffTex = (sec->heightsec->MoreFlags & SECF_CLIPFAKEPLANES); + sector_t * s = sec->heightsec; + +#if 0 + *dest=*sec; // This will invoke the copy operator which isn't really needed here. Memcpy is faster. +#else + memcpy(dest, sec, sizeof(sector_t)); +#endif + + // Replace floor and ceiling height with control sector's heights. + if (diffTex) + { + if (s->floorplane.CopyPlaneIfValid (&dest->floorplane, &sec->ceilingplane)) + { + dest->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); + dest->SetPlaneTexZ(sector_t::floor, s->GetPlaneTexZ(sector_t::floor)); + dest->vboindex[sector_t::floor] = sec->vboindex[sector_t::vbo_fakefloor]; + dest->vboheight[sector_t::floor] = s->vboheight[sector_t::floor]; + } + else if (s->MoreFlags & SECF_FAKEFLOORONLY) + { + if (in_area==area_below) + { + dest->ColorMap=s->ColorMap; + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + dest->lightlevel = s->lightlevel; + dest->SetPlaneLight(sector_t::floor, s->GetPlaneLight(sector_t::floor)); + dest->SetPlaneLight(sector_t::ceiling, s->GetPlaneLight(sector_t::ceiling)); + dest->ChangeFlags(sector_t::floor, -1, s->GetFlags(sector_t::floor)); + dest->ChangeFlags(sector_t::ceiling, -1, s->GetFlags(sector_t::ceiling)); + } + return dest; + } + return sec; + } + } + else + { + dest->SetPlaneTexZ(sector_t::floor, s->GetPlaneTexZ(sector_t::floor)); + dest->floorplane = s->floorplane; + + dest->vboindex[sector_t::floor] = sec->vboindex[sector_t::vbo_fakefloor]; + dest->vboheight[sector_t::floor] = s->vboheight[sector_t::floor]; + } + + if (!(s->MoreFlags&SECF_FAKEFLOORONLY)) + { + if (diffTex) + { + if (s->ceilingplane.CopyPlaneIfValid (&dest->ceilingplane, &sec->floorplane)) + { + dest->SetTexture(sector_t::ceiling, s->GetTexture(sector_t::ceiling), false); + dest->SetPlaneTexZ(sector_t::ceiling, s->GetPlaneTexZ(sector_t::ceiling)); + dest->vboindex[sector_t::ceiling] = sec->vboindex[sector_t::vbo_fakeceiling]; + dest->vboheight[sector_t::ceiling] = s->vboheight[sector_t::ceiling]; + } + } + else + { + dest->ceilingplane = s->ceilingplane; + dest->SetPlaneTexZ(sector_t::ceiling, s->GetPlaneTexZ(sector_t::ceiling)); + dest->vboindex[sector_t::ceiling] = sec->vboindex[sector_t::vbo_fakeceiling]; + dest->vboheight[sector_t::ceiling] = s->vboheight[sector_t::ceiling]; + } + } + + if (in_area==area_below) + { + dest->ColorMap=s->ColorMap; + dest->SetPlaneTexZ(sector_t::floor, sec->GetPlaneTexZ(sector_t::floor)); + dest->SetPlaneTexZ(sector_t::ceiling, s->GetPlaneTexZ(sector_t::floor)); + dest->floorplane=sec->floorplane; + dest->ceilingplane=s->floorplane; + dest->ceilingplane.FlipVert(); + + dest->vboindex[sector_t::floor] = sec->vboindex[sector_t::floor]; + dest->vboheight[sector_t::floor] = sec->vboheight[sector_t::floor]; + + dest->vboindex[sector_t::ceiling] = sec->vboindex[sector_t::vbo_fakefloor]; + dest->vboheight[sector_t::ceiling] = s->vboheight[sector_t::floor]; + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + dest->lightlevel = s->lightlevel; + } + + if (!back) + { + dest->SetTexture(sector_t::floor, diffTex ? sec->GetTexture(sector_t::floor) : s->GetTexture(sector_t::floor), false); + dest->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; + + //dest->ceilingplane = s->floorplane; + + if (s->GetTexture(sector_t::ceiling) == skyflatnum) + { + dest->SetTexture(sector_t::ceiling, dest->GetTexture(sector_t::floor), false); + //dest->floorplane = dest->ceilingplane; + //dest->floorplane.FlipVert (); + //dest->floorplane.ChangeHeight (+1); + dest->planes[sector_t::ceiling].xform = dest->planes[sector_t::floor].xform; + + } + else + { + dest->SetTexture(sector_t::ceiling, diffTex ? s->GetTexture(sector_t::floor) : s->GetTexture(sector_t::ceiling), false); + dest->planes[sector_t::ceiling].xform = s->planes[sector_t::ceiling].xform; + } + + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + dest->SetPlaneLight(sector_t::floor, s->GetPlaneLight(sector_t::floor)); + dest->SetPlaneLight(sector_t::ceiling, s->GetPlaneLight(sector_t::ceiling)); + dest->ChangeFlags(sector_t::floor, -1, s->GetFlags(sector_t::floor)); + dest->ChangeFlags(sector_t::ceiling, -1, s->GetFlags(sector_t::ceiling)); + } + } + } + else if (in_area==area_above) + { + dest->ColorMap=s->ColorMap; + dest->SetPlaneTexZ(sector_t::ceiling, sec->GetPlaneTexZ(sector_t::ceiling)); + dest->SetPlaneTexZ(sector_t::floor, s->GetPlaneTexZ(sector_t::ceiling)); + dest->ceilingplane= sec->ceilingplane; + dest->floorplane = s->ceilingplane; + dest->floorplane.FlipVert(); + + dest->vboindex[sector_t::floor] = sec->vboindex[sector_t::vbo_fakeceiling]; + dest->vboheight[sector_t::floor] = s->vboheight[sector_t::ceiling]; + + dest->vboindex[sector_t::ceiling] = sec->vboindex[sector_t::ceiling]; + dest->vboheight[sector_t::ceiling] = sec->vboheight[sector_t::ceiling]; + + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + dest->lightlevel = s->lightlevel; + } + + if (!back) + { + dest->SetTexture(sector_t::ceiling, diffTex ? sec->GetTexture(sector_t::ceiling) : s->GetTexture(sector_t::ceiling), false); + dest->SetTexture(sector_t::floor, s->GetTexture(sector_t::ceiling), false); + dest->planes[sector_t::ceiling].xform = dest->planes[sector_t::floor].xform = s->planes[sector_t::ceiling].xform; + + if (s->GetTexture(sector_t::floor) != skyflatnum) + { + dest->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); + dest->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; + } + + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + dest->lightlevel = s->lightlevel; + dest->SetPlaneLight(sector_t::floor, s->GetPlaneLight(sector_t::floor)); + dest->SetPlaneLight(sector_t::ceiling, s->GetPlaneLight(sector_t::ceiling)); + dest->ChangeFlags(sector_t::floor, -1, s->GetFlags(sector_t::floor)); + dest->ChangeFlags(sector_t::ceiling, -1, s->GetFlags(sector_t::ceiling)); + } + } + } + return dest; +} + + diff --git a/src/gl/scene/gl_flats.cpp b/src/gl/scene/gl_flats.cpp new file mode 100644 index 000000000..71f2bb6c9 --- /dev/null +++ b/src/gl/scene/gl_flats.cpp @@ -0,0 +1,809 @@ +/* +** gl_flat.cpp +** Flat rendering +** +**--------------------------------------------------------------------------- +** Copyright 2000-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "a_sharedglobal.h" +#include "r_defs.h" +#include "r_sky.h" +#include "r_utility.h" +#include "g_level.h" +#include "doomstat.h" +#include "d_player.h" + +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/data/gl_data.h" +#include "gl/data/gl_vertexbuffer.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/dynlights/gl_glow.h" +#include "gl/dynlights/gl_lightbuffer.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_convert.h" +#include "gl/utility/gl_templates.h" + +//========================================================================== +// +// Sets the texture matrix according to the plane's texture positioning +// information +// +//========================================================================== + +bool gl_SetPlaneTextureRotation(const GLSectorPlane * secplane, FMaterial * gltexture) +{ + // only manipulate the texture matrix if needed. + if (secplane->xoffs != 0 || secplane->yoffs != 0 || + secplane->xscale != FRACUNIT || secplane->yscale != FRACUNIT || + secplane->angle != 0 || + gltexture->TextureWidth(GLUSE_TEXTURE) != 64 || + gltexture->TextureHeight(GLUSE_TEXTURE) != 64) + { + float uoffs=FIXED2FLOAT(secplane->xoffs)/gltexture->TextureWidth(GLUSE_TEXTURE); + float voffs=FIXED2FLOAT(secplane->yoffs)/gltexture->TextureHeight(GLUSE_TEXTURE); + + float xscale1=FIXED2FLOAT(secplane->xscale); + float yscale1=FIXED2FLOAT(secplane->yscale); + if (gltexture->tex->bHasCanvas) + { + yscale1 = 0 - yscale1; + } + float angle=-ANGLE_TO_FLOAT(secplane->angle); + + float xscale2=64.f/gltexture->TextureWidth(GLUSE_TEXTURE); + float yscale2=64.f/gltexture->TextureHeight(GLUSE_TEXTURE); + + gl.MatrixMode(GL_TEXTURE); + gl.PushMatrix(); + gl.Scalef(xscale1 ,yscale1,1.0f); + gl.Translatef(uoffs,voffs,0.0f); + gl.Scalef(xscale2 ,yscale2,1.0f); + gl.Rotatef(angle,0.0f,0.0f,1.0f); + return true; + } + return false; +} + + +//========================================================================== +// +// Flats +// +//========================================================================== + +void GLFlat::DrawSubsectorLights(subsector_t * sub, int pass) +{ + Plane p; + Vector nearPt, up, right, t1; + float scale; + unsigned int k; + seg_t *v; + + FLightNode * node = sub->lighthead[pass==GLPASS_LIGHT_ADDITIVE]; + gl_RenderState.Apply(); + while (node) + { + ADynamicLight * light = node->lightsource; + + if (light->flags2&MF2_DORMANT) + { + node=node->nextLight; + continue; + } + iter_dlightf++; + + // we must do the side check here because gl_SetupLight needs the correct plane orientation + // which we don't have for Legacy-style 3D-floors + fixed_t planeh = plane.plane.ZatPoint(light->x, light->y); + if (gl_lights_checkside && ((planehz && ceiling) || (planeh>light->z && !ceiling))) + { + node=node->nextLight; + continue; + } + + p.Set(plane.plane); + if (!gl_SetupLight(p, light, nearPt, up, right, scale, Colormap.colormap, false, foggy)) + { + node=node->nextLight; + continue; + } + draw_dlightf++; + + // Render the light + gl.Begin(GL_TRIANGLE_FAN); + for(k = 0, v = sub->firstline; k < sub->numlines; k++, v++) + { + vertex_t *vt = v->v1; + float zc = plane.plane.ZatPoint(vt->fx, vt->fy) + dz; + + t1.Set(vt->fx, zc, vt->fy); + Vector nearToVert = t1 - nearPt; + gl.TexCoord2f( (nearToVert.Dot(right) * scale) + 0.5f, (nearToVert.Dot(up) * scale) + 0.5f); + + gl.Vertex3f(vt->fx, zc, vt->fy); + } + + gl.End(); + node = node->nextLight; + } +} + + +//========================================================================== +// +// Flats +// +//========================================================================== +extern FDynLightData lightdata; + +bool GLFlat::SetupSubsectorLights(bool lightsapplied, subsector_t * sub) +{ + Plane p; + + lightdata.Clear(); + for(int i=0;i<2;i++) + { + FLightNode * node = sub->lighthead[i]; + while (node) + { + ADynamicLight * light = node->lightsource; + + if (light->flags2&MF2_DORMANT) + { + node=node->nextLight; + continue; + } + iter_dlightf++; + + // we must do the side check here because gl_SetupLight needs the correct plane orientation + // which we don't have for Legacy-style 3D-floors + fixed_t planeh = plane.plane.ZatPoint(light->x, light->y); + if (gl_lights_checkside && ((planehz && ceiling) || (planeh>light->z && !ceiling))) + { + node=node->nextLight; + continue; + } + + p.Set(plane.plane); + gl_GetLight(p, light, Colormap.colormap, false, false, lightdata); + node = node->nextLight; + } + } + + int numlights[3]; + + lightdata.Combine(numlights, gl.MaxLights()); + if (numlights[2] > 0) + { + draw_dlightf+=numlights[2]/2; + gl_RenderState.EnableLight(true); + gl_RenderState.SetLights(numlights, &lightdata.arrays[0][0]); + gl_RenderState.Apply(); + return true; + } + if (lightsapplied) + { + gl_RenderState.EnableLight(false); + gl_RenderState.Apply(); + } + return false; +} + +//========================================================================== +// +// +// +//========================================================================== + +void GLFlat::DrawSubsector(subsector_t * sub) +{ + gl.Begin(GL_TRIANGLE_FAN); + + for(unsigned int k=0; knumlines; k++) + { + vertex_t *vt = sub->firstline[k].v1; + gl.TexCoord2f(vt->fx/64.f, -vt->fy/64.f); + float zc = plane.plane.ZatPoint(vt->fx, vt->fy) + dz; + gl.Vertex3f(vt->fx, zc, vt->fy); + } + gl.End(); + + flatvertices += sub->numlines; + flatprimitives++; +} + +//========================================================================== +// +// +// +//========================================================================== + +void GLFlat::DrawSubsectors(int pass, bool istrans) +{ + bool lightsapplied = false; + + gl_RenderState.Apply(); + if (sub) + { + // This represents a single subsector + if (pass == GLPASS_ALL) lightsapplied = SetupSubsectorLights(lightsapplied, sub); + DrawSubsector(sub); + } + else + { + if (vboindex >= 0) + { + //gl.Color3f( 1.f,.5f,.5f); + int index = vboindex; + for (int i=0; isubsectorcount; i++) + { + subsector_t * sub = sector->subsectors[i]; + // This is just a quick hack to make translucent 3D floors and portals work. + if (gl_drawinfo->ss_renderflags[sub-subsectors]&renderflags || istrans) + { + if (pass == GLPASS_ALL) lightsapplied = SetupSubsectorLights(lightsapplied, sub); + gl.DrawArrays(GL_TRIANGLE_FAN, index, sub->numlines); + flatvertices += sub->numlines; + flatprimitives++; + } + index += sub->numlines; + } + } + else + { + //gl.Color3f( .5f,1.f,.5f); // these are for testing the VBO stuff. + // Draw the subsectors belonging to this sector + for (int i=0; isubsectorcount; i++) + { + subsector_t * sub = sector->subsectors[i]; + if (gl_drawinfo->ss_renderflags[sub-subsectors]&renderflags || istrans) + { + if (pass == GLPASS_ALL) lightsapplied = SetupSubsectorLights(lightsapplied, sub); + DrawSubsector(sub); + } + } + } + + // Draw the subsectors assigned to it due to missing textures + if (!(renderflags&SSRF_RENDER3DPLANES)) + { + gl_subsectorrendernode * node = (renderflags&SSRF_RENDERFLOOR)? + gl_drawinfo->GetOtherFloorPlanes(sector->sectornum) : + gl_drawinfo->GetOtherCeilingPlanes(sector->sectornum); + + while (node) + { + if (pass == GLPASS_ALL) lightsapplied = SetupSubsectorLights(lightsapplied, node->sub); + DrawSubsector(node->sub); + node = node->next; + } + } + } + gl_RenderState.EnableLight(false); +} + + +//========================================================================== +// +// +// +//========================================================================== +void GLFlat::Draw(int pass) +{ + int i; + int rel = getExtraLight(); + +#ifdef _MSC_VER +#ifdef _DEBUG + if (sector->sectornum == 2) + __asm nop +#endif +#endif + + + switch (pass) + { + case GLPASS_BASE: + gl_SetColor(lightlevel, rel, &Colormap,1.0f); + if (!foggy) gl_SetFog(lightlevel, rel, &Colormap, false); + DrawSubsectors(pass, false); + break; + + case GLPASS_PLAIN: // Single-pass rendering + case GLPASS_ALL: + case GLPASS_BASE_MASKED: + gl_SetColor(lightlevel, rel, &Colormap,1.0f); + if (!foggy || pass != GLPASS_BASE_MASKED) gl_SetFog(lightlevel, rel, &Colormap, false); + // fall through + case GLPASS_TEXTURE: + { + gltexture->Bind(Colormap.colormap); + bool pushed = gl_SetPlaneTextureRotation(&plane, gltexture); + DrawSubsectors(pass, false); + if (pushed) + { + gl.PopMatrix(); + gl.MatrixMode(GL_MODELVIEW); + } + break; + } + + case GLPASS_LIGHT: + case GLPASS_LIGHT_ADDITIVE: + + if (!foggy) gl_SetFog((255+lightlevel)>>1, 0, &Colormap, false); + else gl_SetFog(lightlevel, 0, &Colormap, true); + + if (sub) + { + DrawSubsectorLights(sub, pass); + } + else + { + // Draw the subsectors belonging to this sector + for (i=0; isubsectorcount; i++) + { + subsector_t * sub = sector->subsectors[i]; + + if (gl_drawinfo->ss_renderflags[sub-subsectors]&renderflags) + { + DrawSubsectorLights(sub, pass); + } + } + + // Draw the subsectors assigned to it due to missing textures + if (!(renderflags&SSRF_RENDER3DPLANES)) + { + gl_subsectorrendernode * node = (renderflags&SSRF_RENDERFLOOR)? + gl_drawinfo->GetOtherFloorPlanes(sector->sectornum) : + gl_drawinfo->GetOtherCeilingPlanes(sector->sectornum); + + while (node) + { + DrawSubsectorLights(node->sub, pass); + node = node->next; + } + } + } + break; + + case GLPASS_TRANSLUCENT: + if (renderstyle==STYLE_Add) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE); + gl_SetColor(lightlevel, rel, &Colormap, alpha); + gl_SetFog(lightlevel, rel, &Colormap, false); + gl_RenderState.AlphaFunc(GL_GEQUAL,gl_mask_threshold*(alpha)); + if (!gltexture) + { + gl_RenderState.EnableTexture(false); + DrawSubsectors(pass, true); + gl_RenderState.EnableTexture(true); + } + else + { + if (foggy) gl_RenderState.EnableBrightmap(false); + gltexture->Bind(Colormap.colormap); + bool pushed = gl_SetPlaneTextureRotation(&plane, gltexture); + DrawSubsectors(pass, true); + gl_RenderState.EnableBrightmap(true); + if (pushed) + { + gl.PopMatrix(); + gl.MatrixMode(GL_MODELVIEW); + } + } + if (renderstyle==STYLE_Add) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + } +} + + +//========================================================================== +// +// GLFlat::PutFlat +// +// Checks texture, lighting and translucency settings and puts this +// plane in the appropriate render list. +// +//========================================================================== +inline void GLFlat::PutFlat(bool fog) +{ + int list; + + if (gl_fixedcolormap) + { + Colormap.GetFixedColormap(); + } + if (renderstyle!=STYLE_Translucent || alpha < 1.f - FLT_EPSILON || fog) + { + int list = (renderflags&SSRF_RENDER3DPLANES) ? GLDL_TRANSLUCENT : GLDL_TRANSLUCENTBORDER; + gl_drawinfo->drawlists[list].AddFlat (this); + } + else if (gltexture != NULL) + { + static DrawListType list_indices[2][2][2]={ + { { GLDL_PLAIN, GLDL_FOG }, { GLDL_MASKED, GLDL_FOGMASKED } }, + { { GLDL_LIGHT, GLDL_LIGHTFOG }, { GLDL_LIGHTMASKED, GLDL_LIGHTFOGMASKED } } + }; + + bool light = gl_forcemultipass; + bool masked = gltexture->isMasked() && ((renderflags&SSRF_RENDER3DPLANES) || stack); + + if (!gl_fixedcolormap) + { + foggy = gl_CheckFog(&Colormap, lightlevel) || level.flags&LEVEL_HASFADETABLE; + + if (gl_lights && !gl_dynlight_shader && GLRenderer->mLightCount) // Are lights touching this sector? + { + for(int i=0;isubsectorcount;i++) if (sector->subsectors[i]->lighthead[0] != NULL) + { + light=true; + } + } + } + else foggy = false; + + list = list_indices[light][masked][foggy]; + if (list == GLDL_LIGHT && gltexture->tex->gl_info.Brightmap && gl_BrightmapsActive()) list = GLDL_LIGHTBRIGHT; + + gl_drawinfo->drawlists[list].AddFlat (this); + } +} + +//========================================================================== +// +// This draws one flat +// The passed sector does not indicate the area which is rendered. +// It is only used as source for the plane data. +// The whichplane boolean indicates if the flat is a floor(false) or a ceiling(true) +// +//========================================================================== + +void GLFlat::Process(sector_t * model, int whichplane, bool fog) +{ + plane.GetFromSector(model, whichplane); + + if (!fog) + { + if (plane.texture==skyflatnum) return; + + gltexture=FMaterial::ValidateTexture(plane.texture, true); + if (!gltexture) return; + if (gltexture->tex->isFullbright()) + { + Colormap.LightColor.r = Colormap.LightColor.g = Colormap.LightColor.b = 0xff; + lightlevel=255; + } + } + else + { + gltexture = NULL; + lightlevel = abs(lightlevel); + } + + // get height from vplane + if (whichplane == sector_t::floor && sector->transdoor) dz = -1; + else dz = 0; + + z = plane.plane.ZatPoint(0.f, 0.f); + + PutFlat(fog); + rendered_flats++; +} + +//========================================================================== +// +// Sets 3D floor info. Common code for all 4 cases +// +//========================================================================== + +void GLFlat::SetFrom3DFloor(F3DFloor *rover, bool top, bool underside) +{ + F3DFloor::planeref & plane = top? rover->top : rover->bottom; + + // FF_FOG requires an inverted logic where to get the light from + lightlist_t *light = P_GetPlaneLight(sector, plane.plane, underside); + lightlevel = *light->p_lightlevel; + + if (rover->flags & FF_FOG) Colormap.LightColor = (light->extra_colormap)->Fade; + else Colormap.CopyLightColor(light->extra_colormap); + + alpha = rover->alpha/255.0f; + renderstyle = rover->flags&FF_ADDITIVETRANS? STYLE_Add : STYLE_Translucent; + if (plane.model->VBOHeightcheck(plane.isceiling)) + { + vboindex = plane.vindex; + } + else + { + vboindex = -1; + } +} + +//========================================================================== +// +// Process a sector's flats for rendering +// This function is only called once per sector. +// Subsequent subsectors are just quickly added to the ss_renderflags array +// +//========================================================================== + +void GLFlat::ProcessSector(sector_t * frontsector) +{ + lightlist_t * light; + +#ifdef _MSC_VER +#ifdef _DEBUG + if (frontsector==NULL) + { + __asm int 3 + } + if (frontsector->sectornum==0) + { + __asm nop + } +#endif +#endif + + // Get the real sector for this one. + sector=§ors[frontsector->sectornum]; + extsector_t::xfloor &x = sector->e->XFloor; + this->sub=NULL; + + byte &srf = gl_drawinfo->sectorrenderflags[sector->sectornum]; + + // + // + // + // do floors + // + // + // + if (frontsector->floorplane.ZatPoint(FIXED2FLOAT(viewx), FIXED2FLOAT(viewy)) <= FIXED2FLOAT(viewz)) + { + // process the original floor first. + + srf |= SSRF_RENDERFLOOR; + + lightlevel = gl_ClampLight(frontsector->GetFloorLight()); + Colormap=frontsector->ColorMap; + if ((stack = (frontsector->portals[sector_t::floor] != NULL))) + { + gl_drawinfo->AddFloorStack(sector); + alpha = frontsector->GetAlpha(sector_t::floor)/65536.0f; + } + else + { + alpha = 1.0f-frontsector->GetReflect(sector_t::floor); + } + if (frontsector->VBOHeightcheck(sector_t::floor)) + { + vboindex = frontsector->vboindex[sector_t::floor]; + } + else + { + vboindex = -1; + } + + ceiling=false; + renderflags=SSRF_RENDERFLOOR; + + if (x.ffloors.Size()) + { + light = P_GetPlaneLight(sector, &frontsector->floorplane, false); + if ((!(sector->GetFlags(sector_t::floor)&PLANEF_ABSLIGHTING) || light!=&x.lightlist[0]) + && (light->p_lightlevel != &frontsector->lightlevel)) + { + lightlevel = *light->p_lightlevel; + } + + Colormap.CopyLightColor(light->extra_colormap); + } + renderstyle = STYLE_Translucent; + if (alpha!=0.0f) Process(frontsector, false, false); + } + + // + // + // + // do ceilings + // + // + // + if (frontsector->ceilingplane.ZatPoint(FIXED2FLOAT(viewx), FIXED2FLOAT(viewy)) >= FIXED2FLOAT(viewz)) + { + // process the original ceiling first. + + srf |= SSRF_RENDERCEILING; + + lightlevel = gl_ClampLight(frontsector->GetCeilingLight()); + Colormap=frontsector->ColorMap; + if ((stack = (frontsector->portals[sector_t::ceiling] != NULL))) + { + gl_drawinfo->AddCeilingStack(sector); + alpha = frontsector->GetAlpha(sector_t::ceiling)/65536.0f; + } + else + { + alpha = 1.0f-frontsector->GetReflect(sector_t::ceiling); + } + + if (frontsector->VBOHeightcheck(sector_t::ceiling)) + { + vboindex = frontsector->vboindex[sector_t::ceiling]; + } + else + { + vboindex = -1; + } + + ceiling=true; + renderflags=SSRF_RENDERCEILING; + + if (x.ffloors.Size()) + { + light = P_GetPlaneLight(sector, §or->ceilingplane, true); + + if ((!(sector->GetFlags(sector_t::ceiling)&PLANEF_ABSLIGHTING)) + && (light->p_lightlevel != &frontsector->lightlevel)) + { + lightlevel = *light->p_lightlevel; + } + Colormap.CopyLightColor(light->extra_colormap); + } + renderstyle = STYLE_Translucent; + if (alpha!=0.0f) Process(frontsector, true, false); + } + + // + // + // + // do 3D floors + // + // + // + + stack=false; + if (x.ffloors.Size()) + { + player_t * player=players[consoleplayer].camera->player; + + renderflags=SSRF_RENDER3DPLANES; + srf |= SSRF_RENDER3DPLANES; + // 3d-floors must not overlap! + fixed_t lastceilingheight=sector->CenterCeiling(); // render only in the range of the + fixed_t lastfloorheight=sector->CenterFloor(); // current sector part (if applicable) + F3DFloor * rover; + int k; + + // floors are ordered now top to bottom so scanning the list for the best match + // is no longer necessary. + + ceiling=true; + for(k=0;k<(int)x.ffloors.Size();k++) + { + rover=x.ffloors[k]; + + if ((rover->flags&(FF_EXISTS|FF_RENDERPLANES|FF_THISINSIDE))==(FF_EXISTS|FF_RENDERPLANES)) + { + if (rover->flags&FF_FOG && gl_fixedcolormap) continue; + if (rover->flags&(FF_INVERTPLANES|FF_BOTHPLANES)) + { + fixed_t ff_top=rover->top.plane->ZatPoint(CenterSpot(sector)); + if (ff_toptop.plane->ZatPoint(FIXED2FLOAT(viewx), FIXED2FLOAT(viewy))) + { + SetFrom3DFloor(rover, true, !!(rover->flags&FF_FOG)); + Colormap.FadeColor=frontsector->ColorMap->Fade; + Process(rover->top.model, rover->top.isceiling, !!(rover->flags&FF_FOG)); + } + lastceilingheight=ff_top; + } + } + if (!(rover->flags&FF_INVERTPLANES)) + { + fixed_t ff_bottom=rover->bottom.plane->ZatPoint(CenterSpot(sector)); + if (ff_bottombottom.plane->ZatPoint(FIXED2FLOAT(viewx), FIXED2FLOAT(viewy))) + { + SetFrom3DFloor(rover, false, !(rover->flags&FF_FOG)); + Colormap.FadeColor=frontsector->ColorMap->Fade; + Process(rover->bottom.model, rover->bottom.isceiling, !!(rover->flags&FF_FOG)); + } + lastceilingheight=ff_bottom; + if (rover->alpha<255) lastceilingheight++; + } + } + } + } + + ceiling=false; + for(k=x.ffloors.Size()-1;k>=0;k--) + { + rover=x.ffloors[k]; + + if ((rover->flags&(FF_EXISTS|FF_RENDERPLANES|FF_THISINSIDE))==(FF_EXISTS|FF_RENDERPLANES)) + { + if (rover->flags&FF_FOG && gl_fixedcolormap) continue; + if (rover->flags&(FF_INVERTPLANES|FF_BOTHPLANES)) + { + fixed_t ff_bottom=rover->bottom.plane->ZatPoint(CenterSpot(sector)); + if (ff_bottom>lastfloorheight || (rover->flags&FF_FIX)) + { + if (FIXED2FLOAT(viewz) >= rover->bottom.plane->ZatPoint(FIXED2FLOAT(viewx), FIXED2FLOAT(viewy))) + { + SetFrom3DFloor(rover, false, !(rover->flags&FF_FOG)); + Colormap.FadeColor=frontsector->ColorMap->Fade; + + if (rover->flags&FF_FIX) + { + lightlevel = gl_ClampLight(rover->model->lightlevel); + Colormap = rover->GetColormap(); + } + + Process(rover->bottom.model, rover->bottom.isceiling, !!(rover->flags&FF_FOG)); + } + lastfloorheight=ff_bottom; + } + } + if (!(rover->flags&FF_INVERTPLANES)) + { + fixed_t ff_top=rover->top.plane->ZatPoint(CenterSpot(sector)); + if (ff_top>lastfloorheight) + { + if (FIXED2FLOAT(viewz) >= rover->top.plane->ZatPoint(FIXED2FLOAT(viewx), FIXED2FLOAT(viewy))) + { + SetFrom3DFloor(rover, true, !!(rover->flags&FF_FOG)); + Colormap.FadeColor=frontsector->ColorMap->Fade; + Process(rover->top.model, rover->top.isceiling, !!(rover->flags&FF_FOG)); + } + lastfloorheight=ff_top; + if (rover->alpha<255) lastfloorheight--; + } + } + } + } + } +} + diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp new file mode 100644 index 000000000..6c72c2219 --- /dev/null +++ b/src/gl/scene/gl_portal.cpp @@ -0,0 +1,1081 @@ +/* +** gl_portal.cpp +** Generalized portal maintenance classes for skyboxes, horizons etc. +** Requires a stencil buffer! +** +**--------------------------------------------------------------------------- +** Copyright 2004-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "p_local.h" +#include "vectors.h" +#include "c_dispatch.h" +#include "doomstat.h" +#include "a_sharedglobal.h" + +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/dynlights/gl_glow.h" +#include "gl/data/gl_data.h" +#include "gl/scene/gl_clipper.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_portal.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_templates.h" +#include "gl/utility/gl_geometric.h" + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// General portal handling code +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +EXTERN_CVAR(Bool, gl_portals) +EXTERN_CVAR(Bool, gl_noquery) +EXTERN_CVAR(Int, r_mirror_recursions) + +TArray GLPortal::portals; +int GLPortal::recursion; +int GLPortal::MirrorFlag; +int GLPortal::PlaneMirrorFlag; +int GLPortal::renderdepth; +int GLPortal::PlaneMirrorMode; +GLuint GLPortal::QueryObject; + +int GLPortal::instack[2]; +bool GLPortal::inskybox; + +UniqueList UniqueSkies; +UniqueList UniqueHorizons; +UniqueList UniquePlaneMirrors; + + + +//========================================================================== +// +// +// +//========================================================================== + +void GLPortal::BeginScene() +{ + UniqueSkies.Clear(); + UniqueHorizons.Clear(); + UniquePlaneMirrors.Clear(); +} + +//========================================================================== +// +// +// +//========================================================================== +void GLPortal::ClearScreen() +{ + bool multi = !!gl.IsEnabled(GL_MULTISAMPLE); + gl.MatrixMode(GL_MODELVIEW); + gl.PushMatrix(); + gl.MatrixMode(GL_PROJECTION); + gl.PushMatrix(); + screen->Begin2D(false); + screen->Dim(0, 1.f, 0, 0, SCREENWIDTH, SCREENHEIGHT); + gl.Enable(GL_DEPTH_TEST); + gl.MatrixMode(GL_PROJECTION); + gl.PopMatrix(); + gl.MatrixMode(GL_MODELVIEW); + gl.PopMatrix(); + if (multi) gl.Enable(GL_MULTISAMPLE); + gl_RenderState.Set2DMode(false); +} + + +//----------------------------------------------------------------------------- +// +// DrawPortalStencil +// +//----------------------------------------------------------------------------- +void GLPortal::DrawPortalStencil() +{ + for(unsigned int i=0;i 1) + { + // Cap the stencil at the top and bottom + // (cheap ass version) + gl.Begin(GL_TRIANGLE_FAN); + gl.Vertex3f(-32767.0f,32767.0f,-32767.0f); + gl.Vertex3f(-32767.0f,32767.0f, 32767.0f); + gl.Vertex3f( 32767.0f,32767.0f, 32767.0f); + gl.Vertex3f( 32767.0f,32767.0f,-32767.0f); + gl.End(); + gl.Begin(GL_TRIANGLE_FAN); + gl.Vertex3f(-32767.0f,-32767.0f,-32767.0f); + gl.Vertex3f(-32767.0f,-32767.0f, 32767.0f); + gl.Vertex3f( 32767.0f,-32767.0f, 32767.0f); + gl.Vertex3f( 32767.0f,-32767.0f,-32767.0f); + gl.End(); + } +} + + + +//----------------------------------------------------------------------------- +// +// Start +// +//----------------------------------------------------------------------------- + +bool GLPortal::Start(bool usestencil, bool doquery) +{ + rendered_portals++; + PortalAll.Clock(); + if (usestencil) + { + if (!gl_portals) + { + PortalAll.Unclock(); + return false; + } + + // Create stencil + gl.StencilFunc(GL_EQUAL,recursion,~0); // create stencil + gl.StencilOp(GL_KEEP,GL_KEEP,GL_INCR); // increment stencil of valid pixels + gl.ColorMask(0,0,0,0); // don't write to the graphics buffer + gl_RenderState.EnableTexture(false); + gl.Color3f(1,1,1); + gl.DepthFunc(GL_LESS); + gl_RenderState.Apply(); + + if (NeedDepthBuffer()) + { + gl.DepthMask(false); // don't write to Z-buffer! + if (!NeedDepthBuffer()) doquery = false; // too much overhead and nothing to gain. + else if (gl_noquery) doquery = false; + + // If occlusion query is supported let's use it to avoid rendering portals that aren't visible + if (doquery && gl.flags&RFL_OCCLUSION_QUERY) + { + if (!QueryObject) gl.GenQueries(1, &QueryObject); + if (QueryObject) + { + gl.BeginQuery(GL_SAMPLES_PASSED_ARB, QueryObject); + } + else doquery = false; // some kind of error happened + + } + + DrawPortalStencil(); + + if (doquery && gl.flags&RFL_OCCLUSION_QUERY) + { + gl.EndQuery(GL_SAMPLES_PASSED_ARB); + } + + // Clear Z-buffer + gl.StencilFunc(GL_EQUAL,recursion+1,~0); // draw sky into stencil + gl.StencilOp(GL_KEEP,GL_KEEP,GL_KEEP); // this stage doesn't modify the stencil + gl.DepthMask(true); // enable z-buffer again + gl.DepthRange(1,1); + gl.DepthFunc(GL_ALWAYS); + DrawPortalStencil(); + + // set normal drawing mode + gl_RenderState.EnableTexture(true); + gl.DepthFunc(GL_LESS); + gl.ColorMask(1,1,1,1); + gl.DepthRange(0,1); + + if (doquery && gl.flags&RFL_OCCLUSION_QUERY) + { + GLuint sampleCount; + + gl.GetQueryObjectuiv(QueryObject, GL_QUERY_RESULT_ARB, &sampleCount); + + if (sampleCount==0) // not visible + { + // restore default stencil op. + gl.StencilOp(GL_KEEP,GL_KEEP,GL_KEEP); + gl.StencilFunc(GL_EQUAL,recursion,~0); // draw sky into stencil + PortalAll.Unclock(); + return false; + } + } + FDrawInfo::StartDrawInfo(); + } + else + { + // No z-buffer is needed therefore we can skip all the complicated stuff that is involved + // No occlusion queries will be done here. For these portals the overhead is far greater + // than the benefit. + // Note: We must draw the stencil with z-write enabled here because there is no second pass! + + gl.DepthMask(true); + DrawPortalStencil(); + gl.StencilFunc(GL_EQUAL,recursion+1,~0); // draw sky into stencil + gl.StencilOp(GL_KEEP,GL_KEEP,GL_KEEP); // this stage doesn't modify the stencil + gl_RenderState.EnableTexture(true); + gl.ColorMask(1,1,1,1); + gl.Disable(GL_DEPTH_TEST); + gl.DepthMask(false); // don't write to Z-buffer! + } + recursion++; + + + } + else + { + if (NeedDepthBuffer()) + { + FDrawInfo::StartDrawInfo(); + } + else + { + gl.DepthMask(false); + gl.Disable(GL_DEPTH_TEST); + } + } + // The clip plane from the previous portal must be deactivated for this one. + clipsave = gl.IsEnabled(GL_CLIP_PLANE0+renderdepth-1); + if (clipsave) gl.Disable(GL_CLIP_PLANE0+renderdepth-1); + + // save viewpoint + savedviewx=viewx; + savedviewy=viewy; + savedviewz=viewz; + savedviewactor=GLRenderer->mViewActor; + savedviewangle=viewangle; + savedviewarea=in_area; + + NextPortal = GLRenderer->mCurrentPortal; + GLRenderer->mCurrentPortal = NULL; // Portals which need this have to set it themselves + PortalAll.Unclock(); + return true; +} + + +inline void GLPortal::ClearClipper() +{ + fixed_t angleOffset = viewangle - savedviewangle; + + clipper.Clear(); + + static int call=0; + + // Set the clipper to the minimal visible area + clipper.SafeAddClipRange(0,0xffffffff); + for(unsigned int i=0;i0) + { + clipper.SafeRemoveClipRangeRealAngles(startAngle + angleOffset, endAngle + angleOffset); + } + } + + // and finally clip it to the visible area + angle_t a1 = GLRenderer->FrustumAngle(); + if (a1mCurrentPortal = NextPortal; + if (clipsave) gl.Enable (GL_CLIP_PLANE0+renderdepth-1); + if (usestencil) + { + if (needdepth) FDrawInfo::EndDrawInfo(); + + // Restore the old view + viewx=savedviewx; + viewy=savedviewy; + viewz=savedviewz; + viewangle=savedviewangle; + GLRenderer->mViewActor=savedviewactor; + in_area=savedviewarea; + GLRenderer->SetupView(viewx, viewy, viewz, viewangle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + + gl.Color4f(1,1,1,1); + gl.ColorMask(0,0,0,0); // no graphics + gl.Color3f(1,1,1); + gl_RenderState.EnableTexture(false); + gl_RenderState.Apply(); + + if (needdepth) + { + // first step: reset the depth buffer to max. depth + gl.DepthRange(1,1); // always + gl.DepthFunc(GL_ALWAYS); // write the farthest depth value + DrawPortalStencil(); + } + else + { + gl.Enable(GL_DEPTH_TEST); + } + + // second step: restore the depth buffer to the previous values and reset the stencil + gl.DepthFunc(GL_LEQUAL); + gl.DepthRange(0,1); + gl.StencilOp(GL_KEEP,GL_KEEP,GL_DECR); + gl.StencilFunc(GL_EQUAL,recursion,~0); // draw sky into stencil + DrawPortalStencil(); + gl.DepthFunc(GL_LESS); + + + gl_RenderState.EnableTexture(true); + gl.ColorMask(1,1,1,1); + recursion--; + + // restore old stencil op. + gl.StencilOp(GL_KEEP,GL_KEEP,GL_KEEP); + gl.StencilFunc(GL_EQUAL,recursion,~0); // draw sky into stencil + } + else + { + if (needdepth) + { + FDrawInfo::EndDrawInfo(); + gl.Clear(GL_DEPTH_BUFFER_BIT); + } + else + { + gl.Enable(GL_DEPTH_TEST); + gl.DepthMask(true); + } + // Restore the old view + viewx=savedviewx; + viewy=savedviewy; + viewz=savedviewz; + viewangle=savedviewangle; + GLRenderer->mViewActor=savedviewactor; + in_area=savedviewarea; + GLRenderer->SetupView(viewx, viewy, viewz, viewangle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + + // This draws a valid z-buffer into the stencil's contents to ensure it + // doesn't get overwritten by the level's geometry. + + gl.Color4f(1,1,1,1); + gl.DepthFunc(GL_LEQUAL); + gl.DepthRange(0,1); + gl.ColorMask(0,0,0,0); // no graphics + gl_RenderState.EnableTexture(false); + DrawPortalStencil(); + gl_RenderState.EnableTexture(true); + gl.ColorMask(1,1,1,1); + gl.DepthFunc(GL_LESS); + } + PortalAll.Unclock(); +} + + +//----------------------------------------------------------------------------- +// +// StartFrame +// +//----------------------------------------------------------------------------- +void GLPortal::StartFrame() +{ + GLPortal * p=NULL; + portals.Push(p); + if (renderdepth==0) + { + inskybox=false; + instack[sector_t::floor]=instack[sector_t::ceiling]=0; + } + renderdepth++; +} + + +//----------------------------------------------------------------------------- +// +// Portal info +// +//----------------------------------------------------------------------------- + +static bool gl_portalinfo; + +CCMD(gl_portalinfo) +{ + gl_portalinfo = true; +} + +FString indent; + +//----------------------------------------------------------------------------- +// +// EndFrame +// +//----------------------------------------------------------------------------- + +void GLPortal::EndFrame() +{ + GLPortal * p; + + if (gl.flags & RFL_NOSTENCIL) + { + while (portals.Pop(p) && p) + { + delete p; + } + return; + } + + if (gl_portalinfo) + { + Printf("%s%d portals, depth = %d\n%s{\n", indent.GetChars(), portals.Size(), renderdepth, indent.GetChars()); + indent += " "; + } + + // Only use occlusion query if there are more than 2 portals. + // Otherwise there's too much overhead. + // (And don't forget to consider the separating NULL pointers!) + bool usequery = portals.Size() > 2 + (unsigned)renderdepth; + + while (portals.Pop(p) && p) + { + if (gl_portalinfo) + { + Printf("%sProcessing %s, depth = %d, query = %d\n", indent.GetChars(), p->GetName(), renderdepth, usequery); + } + if (p->lines.Size() > 0) + { + p->RenderPortal(true, usequery); + } + delete p; + } + renderdepth--; + + if (gl_portalinfo) + { + indent.Truncate(long(indent.Len()-2)); + Printf("%s}\n", indent.GetChars()); + if (portals.Size() == 0) gl_portalinfo = false; + } +} + + +//----------------------------------------------------------------------------- +// +// Renders one sky portal without a stencil. +// In more complex scenes using a stencil for skies can severly stall +// the GPU and there's rarely more than one sky visible at a time. +// +//----------------------------------------------------------------------------- +bool GLPortal::RenderFirstSkyPortal(int recursion) +{ + GLPortal * p; + GLPortal * best = NULL; + unsigned bestindex=0; + + // Find the one with the highest amount of lines. + // Normally this is also the one that saves the largest amount + // of time by drawing it before the scene itself. + for(int i = portals.Size()-1; i >= 0 && portals[i] != NULL; --i) + { + p=portals[i]; + if (p->lines.Size() > 0 && p->IsSky()) + { + // Cannot clear the depth buffer inside a portal recursion + if (recursion && p->NeedDepthBuffer()) continue; + + if (!best || p->lines.Size()>best->lines.Size()) + { + best=p; + bestindex=i; + } + } + } + + if (best) + { + portals.Delete(bestindex); + best->RenderPortal(false, false); + delete best; + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// +// FindPortal +// +//----------------------------------------------------------------------------- + +GLPortal * GLPortal::FindPortal(const void * src) +{ + int i=portals.Size()-1; + + while (i>=0 && portals[i] && portals[i]->GetSource()!=src) i--; + return i>=0? portals[i]:NULL; +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void GLPortal::SaveMapSection() +{ + savedmapsection.Resize(currentmapsection.Size()); + memcpy(&savedmapsection[0], ¤tmapsection[0], currentmapsection.Size()); + memset(¤tmapsection[0], 0, currentmapsection.Size()); +} + +void GLPortal::RestoreMapSection() +{ + memcpy(¤tmapsection[0], &savedmapsection[0], currentmapsection.Size()); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Skybox Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// GLSkyboxPortal::DrawContents +// +//----------------------------------------------------------------------------- +static int skyboxrecursion=0; +void GLSkyboxPortal::DrawContents() +{ + int old_pm=PlaneMirrorMode; + int saved_extralight = extralight; + + if (skyboxrecursion>=3) + { + ClearScreen(); + return; + } + + skyboxrecursion++; + origin->flags|=MF_JUSTHIT; + extralight = 0; + + PlaneMirrorMode=0; + + gl.Disable(GL_DEPTH_CLAMP_NV); + + viewx = origin->PrevX + FixedMul(r_TicFrac, origin->x - origin->PrevX); + viewy = origin->PrevY + FixedMul(r_TicFrac, origin->y - origin->PrevY); + viewz = origin->PrevZ + FixedMul(r_TicFrac, origin->z - origin->PrevZ); + viewangle += origin->PrevAngle + FixedMul(r_TicFrac, origin->angle - origin->PrevAngle); + + // Don't let the viewpoint be too close to a floor or ceiling! + fixed_t floorh = origin->Sector->floorplane.ZatPoint(origin->x, origin->y); + fixed_t ceilh = origin->Sector->ceilingplane.ZatPoint(origin->x, origin->y); + if (viewzceilh-4*FRACUNIT) viewz=ceilh-4*FRACUNIT; + + + GLRenderer->mViewActor = origin; + + validcount++; + inskybox=true; + GLRenderer->SetupView(viewx, viewy, viewz, viewangle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + GLRenderer->SetViewArea(); + ClearClipper(); + + int mapsection = R_PointInSubsector(viewx, viewy)->mapsection; + + SaveMapSection(); + currentmapsection[mapsection>>3] |= 1 << (mapsection & 7); + + GLRenderer->DrawScene(); + origin->flags&=~MF_JUSTHIT; + inskybox=false; + gl.Enable(GL_DEPTH_CLAMP_NV); + skyboxrecursion--; + + PlaneMirrorMode=old_pm; + extralight = saved_extralight; + + RestoreMapSection(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Sector stack Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +GLSectorStackPortal::~GLSectorStackPortal() +{ + if (origin != NULL && origin->glportal == this) + { + origin->glportal = NULL; + } +} + +//----------------------------------------------------------------------------- +// +// GLSectorStackPortal::SetupCoverage +// +//----------------------------------------------------------------------------- + +static BYTE SetCoverage(void *node) +{ + if (numnodes == 0) + { + return 0; + } + if (!((size_t)node & 1)) // Keep going until found a subsector + { + node_t *bsp = (node_t *)node; + BYTE coverage = SetCoverage(bsp->children[0]) | SetCoverage(bsp->children[1]); + gl_drawinfo->no_renderflags[bsp-nodes] = coverage; + return coverage; + } + else + { + subsector_t *sub = (subsector_t *)((BYTE *)node - 1); + return gl_drawinfo->ss_renderflags[sub-subsectors] & SSRF_SEEN; + } +} + +void GLSectorStackPortal::SetupCoverage() +{ + for(unsigned i=0; iplane; + for(int j=0;jportalcoverage[plane].sscount; j++) + { + subsector_t *dsub = &::subsectors[sub->portalcoverage[plane].subsectors[j]]; + currentmapsection[dsub->mapsection>>3] |= 1 << (dsub->mapsection&7); + gl_drawinfo->ss_renderflags[dsub-::subsectors] |= SSRF_SEEN; + } + } + SetCoverage(&nodes[numnodes-1]); +} + +//----------------------------------------------------------------------------- +// +// GLSectorStackPortal::DrawContents +// +//----------------------------------------------------------------------------- +void GLSectorStackPortal::DrawContents() +{ + FPortal *portal = origin; + + viewx += origin->xDisplacement; + viewy += origin->yDisplacement; + GLRenderer->mViewActor = NULL; + GLRenderer->mCurrentPortal = this; + + + validcount++; + + // avoid recursions! + if (origin->plane != -1) instack[origin->plane]++; + + GLRenderer->SetupView(viewx, viewy, viewz, viewangle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + SaveMapSection(); + SetupCoverage(); + ClearClipper(); + GLRenderer->DrawScene(); + RestoreMapSection(); + + if (origin->plane != -1) instack[origin->plane]--; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Plane Mirror Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// GLPlaneMirrorPortal::DrawContents +// +//----------------------------------------------------------------------------- + +void GLPlaneMirrorPortal::DrawContents() +{ + if (renderdepth>r_mirror_recursions) + { + ClearScreen(); + return; + } + + int old_pm=PlaneMirrorMode; + + fixed_t planez = origin->ZatPoint(viewx, viewy); + viewz = 2*planez - viewz; + GLRenderer->mViewActor = NULL; + PlaneMirrorMode = ksgn(origin->c); + + validcount++; + + PlaneMirrorFlag++; + GLRenderer->SetupView(viewx, viewy, viewz, viewangle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + ClearClipper(); + + gl.Enable(GL_CLIP_PLANE0+renderdepth); + // This only works properly for non-sloped planes so don't bother with the math. + //double d[4]={origin->a/65536., origin->c/65536., origin->b/65536., FIXED2FLOAT(origin->d)}; + double d[4]={0, PlaneMirrorMode, 0, FIXED2FLOAT(origin->d)}; + gl.ClipPlane(GL_CLIP_PLANE0+renderdepth, d); + + GLRenderer->DrawScene(); + gl.Disable(GL_CLIP_PLANE0+renderdepth); + PlaneMirrorFlag--; + PlaneMirrorMode=old_pm; +} + +//----------------------------------------------------------------------------- +// +// GLPlaneMirrorPortal::DrawContents +// +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Mirror Portal +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// +// R_EnterMirror +// +//----------------------------------------------------------------------------- +void GLMirrorPortal::DrawContents() +{ + if (renderdepth>r_mirror_recursions) + { + ClearScreen(); + return; + } + + GLRenderer->mCurrentPortal = this; + angle_t startang = viewangle; + fixed_t startx = viewx; + fixed_t starty = viewy; + + vertex_t *v1 = linedef->v1; + vertex_t *v2 = linedef->v2; + + // Reflect the current view behind the mirror. + if (linedef->dx == 0) + { + // vertical mirror + viewx = v1->x - startx + v1->x; + + // Compensation for reendering inaccuracies + if (startxx) viewx -= FRACUNIT/2; + else viewx += FRACUNIT/2; + } + else if (linedef->dy == 0) + { + // horizontal mirror + viewy = v1->y - starty + v1->y; + + // Compensation for reendering inaccuracies + if (startyy) viewy -= FRACUNIT/2; + else viewy += FRACUNIT/2; + } + else + { + // any mirror--use floats to avoid integer overflow. + // Use doubles to avoid losing precision which is very important here. + + double dx = FIXED2FLOAT(v2->x - v1->x); + double dy = FIXED2FLOAT(v2->y - v1->y); + double x1 = FIXED2FLOAT(v1->x); + double y1 = FIXED2FLOAT(v1->y); + double x = FIXED2FLOAT(startx); + double y = FIXED2FLOAT(starty); + + // the above two cases catch len == 0 + double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); + + viewx = FLOAT2FIXED((x1 + r * dx)*2 - x); + viewy = FLOAT2FIXED((y1 + r * dy)*2 - y); + + // Compensation for reendering inaccuracies + FVector2 v(-dx, dy); + v.MakeUnit(); + + viewx+= FLOAT2FIXED(v[1] * renderdepth / 2); + viewy+= FLOAT2FIXED(v[0] * renderdepth / 2); + } + // we cannot afford any imprecisions caused by R_PointToAngle2 here. They'd be visible as seams around the mirror. + viewangle = 2*R_PointToAnglePrecise (linedef->v1->x, linedef->v1->y, + linedef->v2->x, linedef->v2->y) - startang; + + GLRenderer->mViewActor = NULL; + + validcount++; + + MirrorFlag++; + GLRenderer->SetupView(viewx, viewy, viewz, viewangle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + + clipper.Clear(); + + angle_t af = GLRenderer->FrustumAngle(); + if (afv1->GetClipAngle(); + angle_t a1 = linedef->v2->GetClipAngle(); + clipper.SafeAddClipRange(a1,a2); + + GLRenderer->DrawScene(); + + MirrorFlag--; +} + + +int GLMirrorPortal::ClipSeg(seg_t *seg) +{ + // this seg is completely behind the mirror! + if (P_PointOnLineSide(seg->v1->x, seg->v1->y, linedef) && + P_PointOnLineSide(seg->v2->x, seg->v2->y, linedef)) + { + return PClip_InFront; + } + return PClip_Inside; +} + +int GLMirrorPortal::ClipPoint(fixed_t x, fixed_t y) +{ + if (P_PointOnLineSide(x, y, linedef)) + { + return PClip_InFront; + } + return PClip_Inside; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// +// Horizon Portal +// +// This simply draws the area in medium sized squares. Drawing it as a whole +// polygon creates visible inaccuracies. +// +// Originally I tried to minimize the amount of data to be drawn but there +// are 2 problems with it: +// +// 1. Setting this up completely negates any performance gains. +// 2. It doesn't work with a 360° field of view (as when you are looking up.) +// +// +// So the brute force mechanism is just as good. +// +// +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// +// GLHorizonPortal::DrawContents +// +//----------------------------------------------------------------------------- +void GLHorizonPortal::DrawContents() +{ + PortalAll.Clock(); + + GLSectorPlane * sp=&origin->plane; + FMaterial * gltexture; + PalEntry color; + float z; + player_t * player=&players[consoleplayer]; + + gltexture=FMaterial::ValidateTexture(sp->texture, true); + if (!gltexture) + { + ClearScreen(); + PortalAll.Unclock(); + return; + } + + + z=FIXED2FLOAT(sp->texheight); + + + if (gltexture && gltexture->tex->isFullbright()) + { + // glowing textures are always drawn full bright without color + gl_SetColor(255, 0, NULL, 1.f); + gl_SetFog(255, 0, &origin->colormap, false); + } + else + { + int rel = getExtraLight(); + gl_SetColor(origin->lightlevel, rel, &origin->colormap, 1.0f); + gl_SetFog(origin->lightlevel, rel, &origin->colormap, false); + } + + + gltexture->Bind(origin->colormap.colormap); + + gl_RenderState.EnableAlphaTest(false); + gl_RenderState.BlendFunc(GL_ONE,GL_ZERO); + gl_RenderState.Apply(); + + + bool pushed = gl_SetPlaneTextureRotation(sp, gltexture); + + float vx=FIXED2FLOAT(viewx); + float vy=FIXED2FLOAT(viewy); + + // Draw to some far away boundary + for(float x=-32768+vx; x<32768+vx; x+=4096) + { + for(float y=-32768+vy; y<32768+vy;y+=4096) + { + gl.Begin(GL_TRIANGLE_FAN); + + gl.TexCoord2f(x/64, -y/64); + gl.Vertex3f(x, z, y); + + gl.TexCoord2f(x/64 + 64, -y/64); + gl.Vertex3f(x + 4096, z, y); + + gl.TexCoord2f(x/64 + 64, -y/64 - 64); + gl.Vertex3f(x + 4096, z, y + 4096); + + gl.TexCoord2f(x/64, -y/64 - 64); + gl.Vertex3f(x, z, y + 4096); + + gl.End(); + + } + } + + float vz=FIXED2FLOAT(viewz); + float tz=(z-vz);///64.0f; + + // fill the gap between the polygon and the true horizon + // Since I can't draw into infinity there can always be a + // small gap + + gl.Begin(GL_TRIANGLE_STRIP); + + gl.TexCoord2f(512.f, 0); + gl.Vertex3f(-32768+vx, z, -32768+vy); + gl.TexCoord2f(512.f, tz); + gl.Vertex3f(-32768+vx, vz, -32768+vy); + + gl.TexCoord2f(-512.f, 0); + gl.Vertex3f(-32768+vx, z, 32768+vy); + gl.TexCoord2f(-512.f, tz); + gl.Vertex3f(-32768+vx, vz, 32768+vy); + + gl.TexCoord2f(512.f, 0); + gl.Vertex3f( 32768+vx, z, 32768+vy); + gl.TexCoord2f(512.f, tz); + gl.Vertex3f( 32768+vx, vz, 32768+vy); + + gl.TexCoord2f(-512.f, 0); + gl.Vertex3f( 32768+vx, z, -32768+vy); + gl.TexCoord2f(-512.f, tz); + gl.Vertex3f( 32768+vx, vz, -32768+vy); + + gl.TexCoord2f(512.f, 0); + gl.Vertex3f(-32768+vx, z, -32768+vy); + gl.TexCoord2f(512.f, tz); + gl.Vertex3f(-32768+vx, vz, -32768+vy); + + gl.End(); + + if (pushed) + { + gl.PopMatrix(); + gl.MatrixMode(GL_MODELVIEW); + } + + PortalAll.Unclock(); + +} + +const char *GLSkyPortal::GetName() { return "Sky"; } +const char *GLSkyboxPortal::GetName() { return "Skybox"; } +const char *GLSectorStackPortal::GetName() { return "Sectorstack"; } +const char *GLPlaneMirrorPortal::GetName() { return "Planemirror"; } +const char *GLMirrorPortal::GetName() { return "Mirror"; } +const char *GLHorizonPortal::GetName() { return "Horizon"; } diff --git a/src/gl/scene/gl_portal.h b/src/gl/scene/gl_portal.h new file mode 100644 index 000000000..b04873ec8 --- /dev/null +++ b/src/gl/scene/gl_portal.h @@ -0,0 +1,300 @@ +/* +** gl_renderstruct.h +** Generalized portal maintenance classes to make rendering special effects easier +** and help add future extensions +** +**--------------------------------------------------------------------------- +** Copyright 2002-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. +** +** 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. +**--------------------------------------------------------------------------- +** +*/ + +#ifndef __GL_PORTAL_H +#define __GL_PORTAL_H + +#include "tarray.h" +//#include "gl/gl_intern.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/utility/gl_templates.h" + +class ASkyViewpoint; + +struct GLHorizonInfo +{ + GLSectorPlane plane; + int lightlevel; + FColormap colormap; +}; + +struct GLSkyInfo +{ + float x_offset[2]; + float y_offset; // doubleskies don't have a y-offset + FMaterial * texture[2]; + FTextureID skytexno1; + bool mirrored; + bool doublesky; + bool sky2; + PalEntry fadecolor; // if this isn't made part of the dome things will become more complicated when sky fog is used. + + bool operator==(const GLSkyInfo & inf) + { + return !memcmp(this, &inf, sizeof(*this)); + } + bool operator!=(const GLSkyInfo & inf) + { + return !!memcmp(this, &inf, sizeof(*this)); + } +}; + +extern UniqueList UniqueSkies; +extern UniqueList UniqueHorizons; +extern UniqueList UniquePlaneMirrors; + +class GLPortal +{ + static TArray portals; + static int recursion; + static unsigned int QueryObject; +protected: + static int MirrorFlag; + static int PlaneMirrorFlag; + static int renderdepth; + +public: + static int PlaneMirrorMode; + static int inupperstack; + static int instack[2]; + static bool inskybox; + +private: + void DrawPortalStencil(); + + fixed_t savedviewx; + fixed_t savedviewy; + fixed_t savedviewz; + angle_t savedviewangle; + AActor * savedviewactor; + area_t savedviewarea; + unsigned char clipsave; + GLPortal *NextPortal; + TArray savedmapsection; + +protected: + TArray lines; + int level; + + GLPortal() { portals.Push(this); } + virtual ~GLPortal() { } + + bool Start(bool usestencil, bool doquery); + void End(bool usestencil); + virtual void DrawContents()=0; + virtual void * GetSource() const =0; // GetSource MUST be implemented! + void ClearClipper(); + virtual bool IsSky() { return false; } + virtual bool NeedCap() { return true; } + virtual bool NeedDepthBuffer() { return true; } + void ClearScreen(); + virtual const char *GetName() = 0; + void SaveMapSection(); + void RestoreMapSection(); + +public: + + enum + { + PClip_InFront, + PClip_Inside, + PClip_Behind + }; + + void RenderPortal(bool usestencil, bool doquery) + { + // Start may perform an occlusion query. If that returns 0 there + // is no need to draw the stencil's contents and there's also no + // need to restore the affected area becasue there is none! + if (Start(usestencil, doquery)) + { + DrawContents(); + End(usestencil); + } + } + + void AddLine(GLWall * l) + { + lines.Push(*l); + } + + static int GetRecursion() + { + return recursion; + } + + virtual int ClipSeg(seg_t *seg) { return PClip_Inside; } + virtual int ClipPoint(fixed_t x, fixed_t y) { return PClip_Inside; } + + static void BeginScene(); + static void StartFrame(); + static bool RenderFirstSkyPortal(int recursion); + static void EndFrame(); + static GLPortal * FindPortal(const void * src); +}; + + +struct GLMirrorPortal : public GLPortal +{ + // mirror portals always consist of single linedefs! + line_t * linedef; + +protected: + virtual void DrawContents(); + virtual void * GetSource() const { return linedef; } + virtual const char *GetName(); + +public: + + GLMirrorPortal(line_t * line) + { + linedef=line; + } + + virtual bool NeedCap() { return false; } + virtual int ClipSeg(seg_t *seg); + virtual int ClipPoint(fixed_t x, fixed_t y); +}; + + +struct GLSkyboxPortal : public GLPortal +{ + AActor * origin; + +protected: + virtual void DrawContents(); + virtual void * GetSource() const { return origin; } + virtual bool IsSky() { return true; } // later! + virtual const char *GetName(); + +public: + + + GLSkyboxPortal(AActor * pt) + { + origin=pt; + } + +}; + + +struct GLSkyPortal : public GLPortal +{ + GLSkyInfo * origin; + +protected: + virtual void DrawContents(); + virtual void * GetSource() const { return origin; } + virtual bool IsSky() { return true; } + virtual bool NeedDepthBuffer() { return false; } + virtual const char *GetName(); + +public: + + + GLSkyPortal(GLSkyInfo * pt) + { + origin=pt; + } + +}; + + + +struct GLSectorStackPortal : public GLPortal +{ + TArray subsectors; +protected: + virtual ~GLSectorStackPortal(); + virtual void DrawContents(); + virtual void * GetSource() const { return origin; } + virtual bool IsSky() { return true; } // although this isn't a real sky it can be handled as one. + virtual const char *GetName(); + FPortal *origin; + +public: + + GLSectorStackPortal(FPortal *pt) + { + origin=pt; + } + void SetupCoverage(); + void AddSubsector(subsector_t *sub) + { + subsectors.Push(sub); + } + +}; + +struct GLPlaneMirrorPortal : public GLPortal +{ +protected: + virtual void DrawContents(); + virtual void * GetSource() const { return origin; } + virtual const char *GetName(); + secplane_t * origin; + +public: + + GLPlaneMirrorPortal(secplane_t * pt) + { + origin=pt; + } + +}; + + +struct GLHorizonPortal : public GLPortal +{ + GLHorizonInfo * origin; + +protected: + virtual void DrawContents(); + virtual void * GetSource() const { return origin; } + virtual bool NeedDepthBuffer() { return false; } + virtual bool NeedCap() { return false; } + virtual const char *GetName(); + +public: + + GLHorizonPortal(GLHorizonInfo * pt) + { + origin=pt; + } + +}; + +#endif diff --git a/src/gl/scene/gl_renderhacks.cpp b/src/gl/scene/gl_renderhacks.cpp new file mode 100644 index 000000000..a6452a01f --- /dev/null +++ b/src/gl/scene/gl_renderhacks.cpp @@ -0,0 +1,1247 @@ +/* +** gl_missingtexture.cpp +** Handles missing upper and lower textures and self referencing sector hacks +** +**--------------------------------------------------------------------------- +** Copyright 2000-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "a_sharedglobal.h" +#include "r_utility.h" +#include "r_defs.h" +#include "r_sky.h" +#include "g_level.h" + + +#include "gl/renderer/gl_renderer.h" +#include "gl/data/gl_data.h" +#include "gl/dynlights/gl_glow.h" +#include "gl/dynlights/gl_lightbuffer.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_portal.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_templates.h" + + +// This is for debugging maps. + +FreeList SSR_List; + +// profiling data +static int totalupper, totallower; +static int lowershcount, uppershcount; +static glcycle_t totalms, showtotalms; +static glcycle_t totalssms; +static sector_t fakesec; + +void FDrawInfo::ClearBuffers() +{ + for(unsigned int i=0;i< otherfloorplanes.Size();i++) + { + gl_subsectorrendernode * node = otherfloorplanes[i]; + while (node) + { + gl_subsectorrendernode * n = node; + node = node->next; + SSR_List.Release(n); + } + } + otherfloorplanes.Clear(); + + for(unsigned int i=0;i< otherceilingplanes.Size();i++) + { + gl_subsectorrendernode * node = otherceilingplanes[i]; + while (node) + { + gl_subsectorrendernode * n = node; + node = node->next; + SSR_List.Release(n); + } + } + otherceilingplanes.Clear(); + + // clear all the lists that might not have been cleared already + MissingUpperTextures.Clear(); + MissingLowerTextures.Clear(); + MissingUpperSegs.Clear(); + MissingLowerSegs.Clear(); + SubsectorHacks.Clear(); + CeilingStacks.Clear(); + FloorStacks.Clear(); + HandledSubsectors.Clear(); + +} +//========================================================================== +// +// Adds a subsector plane to a sector's render list +// +//========================================================================== + +void FDrawInfo::AddOtherFloorPlane(int sector, gl_subsectorrendernode * node) +{ + int oldcnt = otherfloorplanes.Size(); + + if (oldcnt<=sector) + { + otherfloorplanes.Resize(sector+1); + for(int i=oldcnt;i<=sector;i++) otherfloorplanes[i]=NULL; + } + node->next = otherfloorplanes[sector]; + otherfloorplanes[sector] = node; +} + +void FDrawInfo::AddOtherCeilingPlane(int sector, gl_subsectorrendernode * node) +{ + int oldcnt = otherceilingplanes.Size(); + + if (oldcnt<=sector) + { + otherceilingplanes.Resize(sector+1); + for(int i=oldcnt;i<=sector;i++) otherceilingplanes[i]=NULL; + } + node->next = otherceilingplanes[sector]; + otherceilingplanes[sector] = node; +} + + +//========================================================================== +// +// Collects all sectors that might need a fake ceiling +// +//========================================================================== +void FDrawInfo::AddUpperMissingTexture(side_t * side, subsector_t *sub, fixed_t backheight) +{ + if (!side->segs[0]->backsector) return; + + totalms.Clock(); + for(int i=0; inumsegs; i++) + { + seg_t *seg = side->segs[i]; + + // we need find the seg belonging to the passed subsector + if (seg->Subsector == sub) + { + MissingTextureInfo mti = {}; + MissingSegInfo msi; + + + if (sub->render_sector != sub->sector || seg->frontsector != sub->sector) + { + totalms.Unclock(); + return; + } + + //@sync-hack + for(unsigned int i=0;isegs[0]->backsector; + if (!backsec) return; + if (backsec->transdoor) + { + // Transparent door hacks alter the backsector's floor height so we should not + // process the missing texture for them. + if (backsec->transdoorheight == backsec->GetPlaneTexZ(sector_t::floor)) return; + } + + totalms.Clock(); + // we need to check all segs of this sidedef + for(int i=0; inumsegs; i++) + { + seg_t *seg = side->segs[i]; + + // we need find the seg belonging to the passed subsector + if (seg->Subsector == sub) + { + MissingTextureInfo mti = {}; + MissingSegInfo msi; + + subsector_t * sub = seg->Subsector; + + if (sub->render_sector != sub->sector || seg->frontsector != sub->sector) + { + totalms.Unclock(); + return; + } + + // Ignore FF_FIX's because they are designed to abuse missing textures + if (seg->backsector->e->XFloor.ffloors.Size() && seg->backsector->e->XFloor.ffloors[0]->flags&FF_FIX) + { + totalms.Unclock(); + return; + } + + //@sync-hack + for(unsigned int i=0;i MissingLowerTextures[i].planez) + { + MissingLowerTextures[i].planez = backheight; + MissingLowerTextures[i].seg = seg; + } + + msi.MTI_Index = i; + msi.seg=seg; + MissingLowerSegs.Push(msi); + totalms.Unclock(); + return; + } + } + mti.seg=seg; + mti.sub = sub; + mti.planez=backheight; + msi.MTI_Index = MissingLowerTextures.Push(mti); + msi.seg=seg; + MissingLowerSegs.Push(msi); + } + } + totalms.Unclock(); +} + + +//========================================================================== +// +// +// +//========================================================================== +bool FDrawInfo::DoOneSectorUpper(subsector_t * subsec, fixed_t planez) +{ + // Is there a one-sided wall in this sector? + // Do this first to avoid unnecessary recursion + for(DWORD i=0; i< subsec->numlines; i++) + { + if (subsec->firstline[i].backsector == NULL) return false; + if (subsec->firstline[i].PartnerSeg == NULL) return false; + } + + for(DWORD i=0; i< subsec->numlines; i++) + { + seg_t * seg = subsec->firstline +i; + subsector_t * backsub = seg->PartnerSeg->Subsector; + + // already checked? + if (backsub->validcount == validcount) continue; + backsub->validcount=validcount; + + if (seg->frontsector != seg->backsector && seg->linedef) + { + // Note: if this is a real line between sectors + // we can be sure that render_sector is the real sector! + + sector_t * sec = gl_FakeFlat(seg->backsector, &fakesec, true); + + // Don't bother with slopes + if (sec->ceilingplane.a!=0 || sec->ceilingplane.b!=0) return false; + + // Is the neighboring ceiling lower than the desired height? + if (sec->GetPlaneTexZ(sector_t::ceiling)GetPlaneTexZ(sector_t::ceiling)==planez) + { + // If there's a texture abort + FTexture * tex = TexMan[seg->sidedef->GetTexture(side_t::top)]; + if (!tex || tex->UseType==FTexture::TEX_Null) continue; + else return false; + } + } + if (!DoOneSectorUpper(backsub, planez)) return false; + } + // all checked ok. This subsector is part of the current fake plane + + HandledSubsectors.Push(subsec); + return 1; +} + +//========================================================================== +// +// +// +//========================================================================== +bool FDrawInfo::DoOneSectorLower(subsector_t * subsec, fixed_t planez) +{ + // Is there a one-sided wall in this subsector? + // Do this first to avoid unnecessary recursion + for(DWORD i=0; i< subsec->numlines; i++) + { + if (subsec->firstline[i].backsector == NULL) return false; + if (subsec->firstline[i].PartnerSeg == NULL) return false; + } + + for(DWORD i=0; i< subsec->numlines; i++) + { + seg_t * seg = subsec->firstline + i; + subsector_t * backsub = seg->PartnerSeg->Subsector; + + // already checked? + if (backsub->validcount == validcount) continue; + backsub->validcount=validcount; + + if (seg->frontsector != seg->backsector && seg->linedef) + { + // Note: if this is a real line between sectors + // we can be sure that render_sector is the real sector! + + sector_t * sec = gl_FakeFlat(seg->backsector, &fakesec, true); + + // Don't bother with slopes + if (sec->floorplane.a!=0 || sec->floorplane.b!=0) return false; + + // Is the neighboring floor higher than the desired height? + if (sec->GetPlaneTexZ(sector_t::floor)>planez) + { + // todo: check for missing textures. + return false; + } + + // This is an exact height match which means we don't have to do any further checks for this sector + if (sec->GetPlaneTexZ(sector_t::floor)==planez) + { + // If there's a texture abort + FTexture * tex = TexMan[seg->sidedef->GetTexture(side_t::bottom)]; + if (!tex || tex->UseType==FTexture::TEX_Null) continue; + else return false; + } + } + if (!DoOneSectorLower(backsub, planez)) return false; + } + // all checked ok. This sector is part of the current fake plane + + HandledSubsectors.Push(subsec); + return 1; +} + + +//========================================================================== +// +// +// +//========================================================================== +bool FDrawInfo::DoFakeBridge(subsector_t * subsec, fixed_t planez) +{ + // Is there a one-sided wall in this sector? + // Do this first to avoid unnecessary recursion + for(DWORD i=0; i< subsec->numlines; i++) + { + if (subsec->firstline[i].backsector == NULL) return false; + if (subsec->firstline[i].PartnerSeg == NULL) return false; + } + + for(DWORD i=0; i< subsec->numlines; i++) + { + seg_t * seg = subsec->firstline + i; + subsector_t * backsub = seg->PartnerSeg->Subsector; + + // already checked? + if (backsub->validcount == validcount) continue; + backsub->validcount=validcount; + + if (seg->frontsector != seg->backsector && seg->linedef) + { + // Note: if this is a real line between sectors + // we can be sure that render_sector is the real sector! + + sector_t * sec = gl_FakeFlat(seg->backsector, &fakesec, true); + + // Don't bother with slopes + if (sec->floorplane.a!=0 || sec->floorplane.b!=0) return false; + + // Is the neighboring floor higher than the desired height? + if (sec->GetPlaneTexZ(sector_t::floor)GetPlaneTexZ(sector_t::floor)==planez) continue; + } + if (!DoFakeBridge(backsub, planez)) return false; + } + // all checked ok. This sector is part of the current fake plane + + HandledSubsectors.Push(subsec); + return 1; +} + +//========================================================================== +// +// +// +//========================================================================== +bool FDrawInfo::DoFakeCeilingBridge(subsector_t * subsec, fixed_t planez) +{ + // Is there a one-sided wall in this sector? + // Do this first to avoid unnecessary recursion + for(DWORD i=0; i< subsec->numlines; i++) + { + if (subsec->firstline[i].backsector == NULL) return false; + if (subsec->firstline[i].PartnerSeg == NULL) return false; + } + + for(DWORD i=0; i< subsec->numlines; i++) + { + seg_t * seg = subsec->firstline + i; + subsector_t * backsub = seg->PartnerSeg->Subsector; + + // already checked? + if (backsub->validcount == validcount) continue; + backsub->validcount=validcount; + + if (seg->frontsector != seg->backsector && seg->linedef) + { + // Note: if this is a real line between sectors + // we can be sure that render_sector is the real sector! + + sector_t * sec = gl_FakeFlat(seg->backsector, &fakesec, true); + + // Don't bother with slopes + if (sec->ceilingplane.a!=0 || sec->ceilingplane.b!=0) return false; + + // Is the neighboring ceiling higher than the desired height? + if (sec->GetPlaneTexZ(sector_t::ceiling)>planez) + { + // todo: check for missing textures. + return false; + } + + // This is an exact height match which means we don't have to do any further checks for this sector + // No texture checks though! + if (sec->GetPlaneTexZ(sector_t::ceiling)==planez) continue; + } + if (!DoFakeCeilingBridge(backsub, planez)) return false; + } + // all checked ok. This sector is part of the current fake plane + + HandledSubsectors.Push(subsec); + return 1; +} + + +//========================================================================== +// +// Draws the fake planes +// +//========================================================================== +void FDrawInfo::HandleMissingTextures() +{ + sector_t fake; + totalms.Clock(); + totalupper=MissingUpperTextures.Size(); + totallower=MissingLowerTextures.Size(); + + for(unsigned int i=0;i viewz) + { + // close the hole only if all neighboring sectors are an exact height match + // Otherwise just fill in the missing textures. + MissingUpperTextures[i].sub->validcount=validcount; + if (DoOneSectorUpper(MissingUpperTextures[i].sub, MissingUpperTextures[i].planez)) + { + sector_t * sec = MissingUpperTextures[i].seg->backsector; + // The mere fact that this seg has been added to the list means that the back sector + // will be rendered so we can safely assume that it is already in the render list + + for(unsigned int j=0;jsub = HandledSubsectors[j]; + + AddOtherCeilingPlane(sec->sectornum, node); + } + + if (HandledSubsectors.Size()!=1) + { + // mark all subsectors in the missing list that got processed by this + for(unsigned int j=0;jPartnerSeg) continue; + subsector_t *backsub = MissingUpperTextures[i].seg->PartnerSeg->Subsector; + if (!backsub) continue; + validcount++; + HandledSubsectors.Clear(); + + { + // It isn't a hole. Now check whether it might be a fake bridge + sector_t * fakesector = gl_FakeFlat(MissingUpperTextures[i].seg->frontsector, &fake, false); + fixed_t planez = fakesector->GetPlaneTexZ(sector_t::ceiling); + + backsub->validcount=validcount; + if (DoFakeCeilingBridge(backsub, planez)) + { + // The mere fact that this seg has been added to the list means that the back sector + // will be rendered so we can safely assume that it is already in the render list + + for(unsigned int j=0;jsub = HandledSubsectors[j]; + AddOtherCeilingPlane(fakesector->sectornum, node); + } + } + continue; + } + } + + for(unsigned int i=0;ivalidcount=validcount; + if (DoOneSectorLower(MissingLowerTextures[i].sub, MissingLowerTextures[i].planez)) + { + sector_t * sec = MissingLowerTextures[i].seg->backsector; + // The mere fact that this seg has been added to the list means that the back sector + // will be rendered so we can safely assume that it is already in the render list + + for(unsigned int j=0;jsub = HandledSubsectors[j]; + AddOtherFloorPlane(sec->sectornum, node); + } + + if (HandledSubsectors.Size()!=1) + { + // mark all subsectors in the missing list that got processed by this + for(unsigned int j=0;jPartnerSeg) continue; + subsector_t *backsub = MissingLowerTextures[i].seg->PartnerSeg->Subsector; + if (!backsub) continue; + validcount++; + HandledSubsectors.Clear(); + + { + // It isn't a hole. Now check whether it might be a fake bridge + sector_t * fakesector = gl_FakeFlat(MissingLowerTextures[i].seg->frontsector, &fake, false); + fixed_t planez = fakesector->GetPlaneTexZ(sector_t::floor); + + backsub->validcount=validcount; + if (DoFakeBridge(backsub, planez)) + { + // The mere fact that this seg has been added to the list means that the back sector + // will be rendered so we can safely assume that it is already in the render list + + for(unsigned int j=0;jsub = HandledSubsectors[j]; + AddOtherFloorPlane(fakesector->sectornum, node); + } + } + continue; + } + } + + totalms.Unclock(); + showtotalms=totalms; + totalms.Reset(); +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FDrawInfo::DrawUnhandledMissingTextures() +{ + validcount++; + for(int i=MissingUpperSegs.Size()-1; i>=0; i--) + { + int index = MissingUpperSegs[i].MTI_Index; + if (index>=0 && MissingUpperTextures[index].seg==NULL) continue; + + seg_t * seg = MissingUpperSegs[i].seg; + + // already done! + if (seg->linedef->validcount==validcount) continue; // already done + seg->linedef->validcount=validcount; + if (seg->frontsector->GetPlaneTexZ(sector_t::ceiling) < viewz) continue; // out of sight + //if (seg->frontsector->ceilingpic==skyflatnum) continue; + + // FIXME: The check for degenerate subsectors should be more precise + if (seg->PartnerSeg && (seg->PartnerSeg->Subsector->flags & SSECF_DEGENERATE)) continue; + if (seg->backsector->transdoor) continue; + if (seg->backsector->GetTexture(sector_t::ceiling)==skyflatnum) continue; + if (seg->backsector->portals[sector_t::ceiling] != NULL) continue; + + if (!glset.notexturefill) FloodUpperGap(seg); + } + + validcount++; + for(int i=MissingLowerSegs.Size()-1; i>=0; i--) + { + int index = MissingLowerSegs[i].MTI_Index; + if (index>=0 && MissingLowerTextures[index].seg==NULL) continue; + + seg_t * seg = MissingLowerSegs[i].seg; + + // already done! + if (seg->linedef->validcount==validcount) continue; // already done + seg->linedef->validcount=validcount; + if (!(sectorrenderflags[seg->backsector->sectornum] & SSRF_RENDERFLOOR)) continue; + if (seg->frontsector->GetPlaneTexZ(sector_t::floor) > viewz) continue; // out of sight + if (seg->backsector->transdoor) continue; + if (seg->backsector->GetTexture(sector_t::floor)==skyflatnum) continue; + if (seg->backsector->portals[sector_t::floor] != NULL) continue; + + if (!glset.notexturefill) FloodLowerGap(seg); + } + MissingUpperTextures.Clear(); + MissingLowerTextures.Clear(); + MissingUpperSegs.Clear(); + MissingLowerSegs.Clear(); + +} + +void AppendMissingTextureStats(FString &out) +{ + out.AppendFormat("Missing textures: %d upper, %d lower, %.3f ms\n", + totalupper, totallower, showtotalms.TimeMS()); +} + +ADD_STAT(missingtextures) +{ + FString out; + AppendMissingTextureStats(out); + return out; +} + + +//========================================================================== +// +// Multi-sector deep water hacks +// +//========================================================================== + +void FDrawInfo::AddHackedSubsector(subsector_t * sub) +{ + if (!(level.maptype == MAPTYPE_HEXEN)) + { + //@sync-hack (probably not, this is only called from the main thread) + SubsectorHackInfo sh={sub, 0}; + SubsectorHacks.Push (sh); + } +} + +//========================================================================== +// +// Finds a subsector whose plane can be used for rendering +// +//========================================================================== + +bool FDrawInfo::CheckAnchorFloor(subsector_t * sub) +{ + // This subsector has a one sided wall and can be used. + if (sub->hacked==3) return true; + if (sub->flags & SSECF_DEGENERATE) return false; + + for(DWORD j=0;jnumlines;j++) + { + seg_t * seg = sub->firstline + j; + if (!seg->PartnerSeg) return true; + + subsector_t * backsub = seg->PartnerSeg->Subsector; + + // Find a linedef with a different visplane on the other side. + if (!(backsub->flags & SSECF_DEGENERATE) && seg->linedef && + (sub->render_sector != backsub->render_sector && sub->sector != backsub->sector)) + { + // I'm ignoring slopes, scaling and rotation here. The likelihood of ZDoom maps + // using such crap hacks is simply too small + if (sub->render_sector->GetTexture(sector_t::floor)==backsub->render_sector->GetTexture(sector_t::floor) && + sub->render_sector->GetPlaneTexZ(sector_t::floor)==backsub->render_sector->GetPlaneTexZ(sector_t::floor) && + sub->render_sector->GetFloorLight() == backsub->render_sector->GetFloorLight()) + { + continue; + } + // This means we found an adjoining subsector that clearly would go into another + // visplane. That means that this subsector can be used as an anchor. + return true; + } + } + return false; +} + +//========================================================================== +// +// Collect connected subsectors that have to be rendered with the same plane +// +//========================================================================== +static bool inview; +static subsector_t * viewsubsector; +static TArray lowersegs; + +bool FDrawInfo::CollectSubsectorsFloor(subsector_t * sub, sector_t * anchor) +{ + + // mark it checked + sub->validcount=validcount; + + + // We must collect any subsector that either is connected to this one with a miniseg + // or has the same visplane. + // We must not collect any subsector that has the anchor's visplane! + if (!(sub->flags & SSECF_DEGENERATE)) + { + // Is not being rendered so don't bother. + if (!(ss_renderflags[DWORD(sub-subsectors)]&SSRF_PROCESSED)) return true; + + if (sub->render_sector->GetTexture(sector_t::floor) != anchor->GetTexture(sector_t::floor) || + sub->render_sector->GetPlaneTexZ(sector_t::floor)!=anchor->GetPlaneTexZ(sector_t::floor) || + sub->render_sector->GetFloorLight() != anchor->GetFloorLight()) + { + if (sub==viewsubsector && viewzGetPlaneTexZ(sector_t::floor)) inview=true; + HandledSubsectors.Push (sub); + } + } + + // We can assume that all segs in this subsector are connected to a subsector that has + // to be checked as well + for(DWORD j=0;jnumlines;j++) + { + seg_t * seg = sub->firstline + j; + if (seg->PartnerSeg) + { + subsector_t * backsub = seg->PartnerSeg->Subsector; + + // could be an anchor itself. + if (!CheckAnchorFloor (backsub)) // must not be an anchor itself! + { + if (backsub->validcount!=validcount) + { + if (!CollectSubsectorsFloor (backsub, anchor)) return false; + } + } + else if (sub->render_sector == backsub->render_sector) + { + // Any anchor not within the original anchor's visplane terminates the processing. + if (sub->render_sector->GetTexture(sector_t::floor)!=anchor->GetTexture(sector_t::floor) || + sub->render_sector->GetPlaneTexZ(sector_t::floor)!=anchor->GetPlaneTexZ(sector_t::floor) || + sub->render_sector->GetFloorLight() != anchor->GetFloorLight()) + { + return false; + } + } + if (!seg->linedef || (seg->frontsector==seg->backsector && sub->render_sector!=backsub->render_sector)) + lowersegs.Push(seg); + } + } + return true; +} + +//========================================================================== +// +// Finds a subsector whose plane can be used for rendering +// +//========================================================================== + +bool FDrawInfo::CheckAnchorCeiling(subsector_t * sub) +{ + // This subsector has a one sided wall and can be used. + if (sub->hacked==3) return true; + if (sub->flags & SSECF_DEGENERATE) return false; + + for(DWORD j=0;jnumlines;j++) + { + seg_t * seg = sub->firstline + j; + if (!seg->PartnerSeg) return true; + + subsector_t * backsub = seg->PartnerSeg->Subsector; + + // Find a linedef with a different visplane on the other side. + if (!(backsub->flags & SSECF_DEGENERATE) && seg->linedef && + (sub->render_sector != backsub->render_sector && sub->sector != backsub->sector)) + { + // I'm ignoring slopes, scaling and rotation here. The likelihood of ZDoom maps + // using such crap hacks is simply too small + if (sub->render_sector->GetTexture(sector_t::ceiling)==backsub->render_sector->GetTexture(sector_t::ceiling) && + sub->render_sector->GetPlaneTexZ(sector_t::ceiling)==backsub->render_sector->GetPlaneTexZ(sector_t::ceiling) && + sub->render_sector->GetCeilingLight() == backsub->render_sector->GetCeilingLight()) + { + continue; + } + // This means we found an adjoining subsector that clearly would go into another + // visplane. That means that this subsector can be used as an anchor. + return true; + } + } + return false; +} + +//========================================================================== +// +// Collect connected subsectors that have to be rendered with the same plane +// +//========================================================================== + +bool FDrawInfo::CollectSubsectorsCeiling(subsector_t * sub, sector_t * anchor) +{ + // mark it checked + sub->validcount=validcount; + + + // We must collect any subsector that either is connected to this one with a miniseg + // or has the same visplane. + // We must not collect any subsector that has the anchor's visplane! + if (!(sub->flags & SSECF_DEGENERATE)) + { + // Is not being rendererd so don't bother. + if (!(ss_renderflags[DWORD(sub-subsectors)]&SSRF_PROCESSED)) return true; + + if (sub->render_sector->GetTexture(sector_t::ceiling)!=anchor->GetTexture(sector_t::ceiling) || + sub->render_sector->GetPlaneTexZ(sector_t::ceiling)!=anchor->GetPlaneTexZ(sector_t::ceiling) || + sub->render_sector->GetCeilingLight() != anchor->GetCeilingLight()) + { + HandledSubsectors.Push (sub); + } + } + + // We can assume that all segs in this subsector are connected to a subsector that has + // to be checked as well + for(DWORD j=0;jnumlines;j++) + { + seg_t * seg = sub->firstline + j; + if (seg->PartnerSeg) + { + subsector_t * backsub = seg->PartnerSeg->Subsector; + + // could be an anchor itself. + if (!CheckAnchorCeiling (backsub)) // must not be an anchor itself! + { + if (backsub->validcount!=validcount) + { + if (!CollectSubsectorsCeiling (backsub, anchor)) return false; + } + } + else if (sub->render_sector == backsub->render_sector) + { + // Any anchor not within the original anchor's visplane terminates the processing. + if (sub->render_sector->GetTexture(sector_t::ceiling)!=anchor->GetTexture(sector_t::ceiling) || + sub->render_sector->GetPlaneTexZ(sector_t::ceiling)!=anchor->GetPlaneTexZ(sector_t::ceiling) || + sub->render_sector->GetCeilingLight() != anchor->GetCeilingLight()) + { + return false; + } + } + } + } + return true; +} + +//========================================================================== +// +// Process the subsectors that have been marked as hacked +// +//========================================================================== + +void FDrawInfo::HandleHackedSubsectors() +{ + lowershcount=uppershcount=0; + totalssms.Reset(); + totalssms.Clock(); + + viewsubsector = R_PointInSubsector(viewx, viewy); + + // Each subsector may only be processed once in this loop! + validcount++; + for(unsigned int i=0;ivalidcount!=validcount && CheckAnchorFloor(sub)) + { + // Now collect everything that is connected with this subsector. + HandledSubsectors.Clear(); + inview=false; + lowersegs.Clear(); + if (CollectSubsectorsFloor(sub, sub->render_sector)) + { + for(unsigned int j=0;jsub = HandledSubsectors[j]; + AddOtherFloorPlane(sub->render_sector->sectornum, node); + } + if (inview) for(unsigned int j=0;jProcessLowerMiniseg (seg, seg->Subsector->render_sector, seg->PartnerSeg->Subsector->render_sector); + } + lowershcount+=HandledSubsectors.Size(); + } + } + } + + // Each subsector may only be processed once in this loop! + validcount++; + for(unsigned int i=0;ivalidcount!=validcount && CheckAnchorCeiling(sub)) + { + // Now collect everything that is connected with this subsector. + HandledSubsectors.Clear(); + if (CollectSubsectorsCeiling(sub, sub->render_sector)) + { + for(unsigned int j=0;jsub = HandledSubsectors[j]; + AddOtherCeilingPlane(sub->render_sector->sectornum, node); + } + uppershcount+=HandledSubsectors.Size(); + } + } + } + + + SubsectorHacks.Clear(); + totalssms.Unclock(); +} + +ADD_STAT(sectorhacks) +{ + FString out; + out.Format("sectorhacks = %.3f ms, %d upper, %d lower\n", totalssms.TimeMS(), uppershcount, lowershcount); + return out; +} + + +//========================================================================== +// +// This merges visplanes that lie inside a sector stack together +// to avoid rendering these unneeded flats +// +//========================================================================== + +void FDrawInfo::AddFloorStack(sector_t * sec) +{ + //@sync-hack + FloorStacks.Push(sec); +} + +void FDrawInfo::AddCeilingStack(sector_t * sec) +{ + //@sync-hack + CeilingStacks.Push(sec); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FDrawInfo::CollectSectorStacksCeiling(subsector_t * sub, sector_t * anchor) +{ + sector_t fake; + + // mark it checked + sub->validcount=validcount; + + // Has a sector stack or skybox itself! + if (sub->render_sector->portals[sector_t::ceiling] != NULL) return; + + // Don't bother processing unrendered subsectors + if (sub->numlines>2 && !(ss_renderflags[DWORD(sub-subsectors)]&SSRF_PROCESSED)) return; + + // Must be the exact same visplane + sector_t * me = gl_FakeFlat(sub->render_sector, &fake, false); + if (me->GetTexture(sector_t::ceiling) != anchor->GetTexture(sector_t::ceiling) || + me->ceilingplane != anchor->ceilingplane || + me->GetCeilingLight() != anchor->GetCeilingLight() || + me->ColorMap != anchor->ColorMap || + me->GetXOffset(sector_t::ceiling) != anchor->GetXOffset(sector_t::ceiling) || + me->GetYOffset(sector_t::ceiling) != anchor->GetYOffset(sector_t::ceiling) || + me->GetXScale(sector_t::ceiling) != anchor->GetXScale(sector_t::ceiling) || + me->GetYScale(sector_t::ceiling) != anchor->GetYScale(sector_t::ceiling) || + me->GetAngle(sector_t::ceiling) != anchor->GetAngle(sector_t::ceiling)) + { + // different visplane so it can't belong to this stack + return; + } + + HandledSubsectors.Push (sub); + + for(DWORD j=0;jnumlines;j++) + { + seg_t * seg = sub->firstline + j; + if (seg->PartnerSeg) + { + subsector_t * backsub = seg->PartnerSeg->Subsector; + + if (backsub->validcount!=validcount) CollectSectorStacksCeiling (backsub, anchor); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FDrawInfo::CollectSectorStacksFloor(subsector_t * sub, sector_t * anchor) +{ + sector_t fake; + // mark it checked + sub->validcount=validcount; + + // Has a sector stack or skybox itself! + if (sub->render_sector->portals[sector_t::floor] != NULL) return; + + // Don't bother processing unrendered subsectors + if (sub->numlines>2 && !(ss_renderflags[DWORD(sub-subsectors)]&SSRF_PROCESSED)) return; + + // Must be the exact same visplane + sector_t * me = gl_FakeFlat(sub->render_sector, &fake, false); + if (me->GetTexture(sector_t::floor) != anchor->GetTexture(sector_t::floor) || + me->floorplane != anchor->floorplane || + me->GetFloorLight() != anchor->GetFloorLight() || + me->ColorMap != anchor->ColorMap || + me->GetXOffset(sector_t::floor) != anchor->GetXOffset(sector_t::floor) || + me->GetYOffset(sector_t::floor) != anchor->GetYOffset(sector_t::floor) || + me->GetXScale(sector_t::floor) != anchor->GetXScale(sector_t::floor) || + me->GetYScale(sector_t::floor) != anchor->GetYScale(sector_t::floor) || + me->GetAngle(sector_t::floor) != anchor->GetAngle(sector_t::floor)) + { + // different visplane so it can't belong to this stack + return; + } + + HandledSubsectors.Push (sub); + + for(DWORD j=0;jnumlines;j++) + { + seg_t * seg = sub->firstline + j; + if (seg->PartnerSeg) + { + subsector_t * backsub = seg->PartnerSeg->Subsector; + + if (backsub->validcount!=validcount) CollectSectorStacksFloor (backsub, anchor); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FDrawInfo::ProcessSectorStacks() +{ + unsigned int i; + sector_t fake; + + validcount++; + for (i=0;iportals[sector_t::ceiling]; + if (portal != NULL) for(int k=0;ksubsectorcount;k++) + { + subsector_t * sub = sec->subsectors[k]; + if (ss_renderflags[sub-subsectors] & SSRF_PROCESSED) + { + HandledSubsectors.Clear(); + for(DWORD j=0;jnumlines;j++) + { + seg_t * seg = sub->firstline + j; + if (seg->PartnerSeg) + { + subsector_t * backsub = seg->PartnerSeg->Subsector; + + if (backsub->validcount!=validcount) CollectSectorStacksCeiling (backsub, sec); + } + } + for(unsigned int j=0;jportalcoverage[sector_t::ceiling].subsectors == NULL) + { + gl_BuildPortalCoverage(&sub->portalcoverage[sector_t::ceiling], sub, portal); + } + + portal->GetGLPortal()->AddSubsector(sub); + + if (sec->GetAlpha(sector_t::ceiling) != 0 && sec->GetTexture(sector_t::ceiling) != skyflatnum) + { + gl_subsectorrendernode * node = SSR_List.GetNew(); + node->sub = sub; + AddOtherCeilingPlane(sec->sectornum, node); + } + } + } + } + } + + validcount++; + for (i=0;iportals[sector_t::floor]; + if (portal != NULL) for(int k=0;ksubsectorcount;k++) + { + subsector_t * sub = sec->subsectors[k]; + if (ss_renderflags[sub-subsectors] & SSRF_PROCESSED) + { + HandledSubsectors.Clear(); + for(DWORD j=0;jnumlines;j++) + { + seg_t * seg = sub->firstline + j; + if (seg->PartnerSeg) + { + subsector_t * backsub = seg->PartnerSeg->Subsector; + + if (backsub->validcount!=validcount) CollectSectorStacksFloor (backsub, sec); + } + } + + for(unsigned int j=0;jportalcoverage[sector_t::floor].subsectors == NULL) + { + gl_BuildPortalCoverage(&sub->portalcoverage[sector_t::floor], sub, portal); + } + + GLSectorStackPortal *glportal = portal->GetGLPortal(); + glportal->AddSubsector(sub); + + if (sec->GetAlpha(sector_t::floor) != 0 && sec->GetTexture(sector_t::floor) != skyflatnum) + { + gl_subsectorrendernode * node = SSR_List.GetNew(); + node->sub = sub; + AddOtherFloorPlane(sec->sectornum, node); + } + } + } + } + } + + FloorStacks.Clear(); + CeilingStacks.Clear(); +} + diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp new file mode 100644 index 000000000..99660267c --- /dev/null +++ b/src/gl/scene/gl_scene.cpp @@ -0,0 +1,1270 @@ +/* +** gl_scene.cpp +** manages the rendering of the player's view +** +**--------------------------------------------------------------------------- +** Copyright 2004-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "gi.h" +#include "m_png.h" +#include "m_random.h" +#include "st_stuff.h" +#include "dobject.h" +#include "doomstat.h" +#include "g_level.h" +#include "r_data/r_interpolate.h" +#include "r_utility.h" +#include "d_player.h" +#include "p_effect.h" +#include "sbar.h" +#include "po_man.h" +#include "r_utility.h" +#include "a_hexenglobal.h" +#include "p_local.h" +#include "gl/gl_functions.h" + +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/data/gl_data.h" +#include "gl/data/gl_vertexbuffer.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/dynlights/gl_lightbuffer.h" +#include "gl/models/gl_models.h" +#include "gl/scene/gl_clipper.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_portal.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_convert.h" +#include "gl/utility/gl_templates.h" + +//========================================================================== +// +// CVARs +// +//========================================================================== +CVAR(Bool, gl_texture, true, 0) +CVAR(Bool, gl_no_skyclear, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Float, gl_mask_threshold, 0.5f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Float, gl_mask_sprite_threshold, 0.5f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Bool, gl_forcemultipass, false, 0) + +EXTERN_CVAR (Int, screenblocks) +EXTERN_CVAR (Bool, cl_capfps) +EXTERN_CVAR (Bool, r_deathcamera) + + +extern int viewpitch; + +DWORD gl_fixedcolormap; +area_t in_area; +TArray currentmapsection; + +void gl_ParseDefs(); + +//----------------------------------------------------------------------------- +// +// R_FrustumAngle +// +//----------------------------------------------------------------------------- +angle_t FGLRenderer::FrustumAngle() +{ + float tilt= fabs(mAngles.Pitch); + + // If the pitch is larger than this you can look all around at a FOV of 90° + if (tilt>46.0f) return 0xffffffff; + + // ok, this is a gross hack that barely works... + // but at least it doesn't overestimate too much... + double floatangle=2.0+(45.0+((tilt/1.9)))*mCurrentFoV*48.0/BaseRatioSizes[WidescreenRatio][3]/90.0; + angle_t a1 = FLOAT_TO_ANGLE(floatangle); + if (a1>=ANGLE_180) return 0xffffffff; + return a1; +} + +//----------------------------------------------------------------------------- +// +// Sets the area the camera is in +// +//----------------------------------------------------------------------------- +void FGLRenderer::SetViewArea() +{ + // The render_sector is better suited to represent the current position in GL + viewsector = R_PointInSubsector(viewx, viewy)->render_sector; + + // keep the view within the render sector's floor and ceiling + fixed_t theZ = viewsector->ceilingplane.ZatPoint (viewx, viewy) - 4*FRACUNIT; + if (viewz > theZ) + { + viewz = theZ; + } + + theZ = viewsector->floorplane.ZatPoint (viewx, viewy) + 4*FRACUNIT; + if (viewz < theZ) + { + viewz = theZ; + } + + // Get the heightsec state from the render sector, not the current one! + if (viewsector->heightsec && !(viewsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) + { + in_area = viewz<=viewsector->heightsec->floorplane.ZatPoint(viewx,viewy) ? area_below : + (viewz>viewsector->heightsec->ceilingplane.ZatPoint(viewx,viewy) && + !(viewsector->heightsec->MoreFlags&SECF_FAKEFLOORONLY)) ? area_above:area_normal; + } + else + { + in_area=area_default; // depends on exposed lower sectors + } +} + +//----------------------------------------------------------------------------- +// +// resets the 3D viewport +// +//----------------------------------------------------------------------------- + +void FGLRenderer::ResetViewport() +{ + int trueheight = static_cast(screen)->GetTrueHeight(); // ugh... + gl.Viewport(0, (trueheight-screen->GetHeight())/2, screen->GetWidth(), screen->GetHeight()); +} + +//----------------------------------------------------------------------------- +// +// sets 3D viewport and initial state +// +//----------------------------------------------------------------------------- + +void FGLRenderer::SetViewport(GL_IRECT *bounds) +{ + if (!bounds) + { + int height, width; + + // Special handling so the view with a visible status bar displays properly + + if (screenblocks >= 10) + { + height = SCREENHEIGHT; + width = SCREENWIDTH; + } + else + { + height = (screenblocks*SCREENHEIGHT/10) & ~7; + width = (screenblocks*SCREENWIDTH/10); + } + + int trueheight = static_cast(screen)->GetTrueHeight(); // ugh... + int bars = (trueheight-screen->GetHeight())/2; + + int vw = viewwidth; + int vh = viewheight; + gl.Viewport(viewwindowx, trueheight-bars-(height+viewwindowy-((height-vh)/2)), vw, height); + gl.Scissor(viewwindowx, trueheight-bars-(vh+viewwindowy), vw, vh); + } + else + { + gl.Viewport(bounds->left, bounds->top, bounds->width, bounds->height); + gl.Scissor(bounds->left, bounds->top, bounds->width, bounds->height); + } + gl.Enable(GL_SCISSOR_TEST); + + #ifdef _DEBUG + gl.ClearColor(0.0f, 0.0f, 0.0f, 0.0f); + gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + #else + gl.Clear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + #endif + + gl.Enable(GL_MULTISAMPLE); + gl.Enable(GL_DEPTH_TEST); + gl.Enable(GL_STENCIL_TEST); + gl.StencilFunc(GL_ALWAYS,0,~0); // default stencil + gl.StencilOp(GL_KEEP,GL_KEEP,GL_REPLACE); +} + +//----------------------------------------------------------------------------- +// +// Setup the camera position +// +//----------------------------------------------------------------------------- + +void FGLRenderer::SetCameraPos(fixed_t viewx, fixed_t viewy, fixed_t viewz, angle_t viewangle) +{ + float fviewangle=(float)(viewangle>>ANGLETOFINESHIFT)*360.0f/FINEANGLES; + + mAngles.Yaw = 270.0f-fviewangle; + mViewVector = FVector2(cos(DEG2RAD(fviewangle)), sin(DEG2RAD(fviewangle))); + mCameraPos = FVector3(FIXED2FLOAT(viewx), FIXED2FLOAT(viewy), FIXED2FLOAT(viewz)); + + R_SetViewAngle(); +} + + +//----------------------------------------------------------------------------- +// +// SetProjection +// sets projection matrix +// +//----------------------------------------------------------------------------- + +void FGLRenderer::SetProjection(float fov, float ratio, float fovratio) +{ + gl.MatrixMode(GL_PROJECTION); + gl.LoadIdentity(); + + float fovy = 2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovratio)); + gluPerspective(fovy, ratio, 5.f, 65536.f); + gl_RenderState.Set2DMode(false); +} + +//----------------------------------------------------------------------------- +// +// Setup the modelview matrix +// +//----------------------------------------------------------------------------- + +void FGLRenderer::SetViewMatrix(bool mirror, bool planemirror) +{ + if (gl.shadermodel >= 4) + { + gl.ActiveTexture(GL_TEXTURE7); + gl.MatrixMode(GL_TEXTURE); + gl.LoadIdentity(); + } + gl.ActiveTexture(GL_TEXTURE0); + gl.MatrixMode(GL_TEXTURE); + gl.LoadIdentity(); + + gl.MatrixMode(GL_MODELVIEW); + gl.LoadIdentity(); + + float mult = mirror? -1:1; + float planemult = planemirror? -1:1; + + gl.Rotatef(GLRenderer->mAngles.Roll, 0.0f, 0.0f, 1.0f); + gl.Rotatef(GLRenderer->mAngles.Pitch, 1.0f, 0.0f, 0.0f); + gl.Rotatef(GLRenderer->mAngles.Yaw, 0.0f, mult, 0.0f); + gl.Translatef( GLRenderer->mCameraPos.X * mult, -GLRenderer->mCameraPos.Z*planemult, -GLRenderer->mCameraPos.Y); + gl.Scalef(-mult, planemult, 1); +} + + +//----------------------------------------------------------------------------- +// +// SetupView +// Setup the view rotation matrix for the given viewpoint +// +//----------------------------------------------------------------------------- +void FGLRenderer::SetupView(fixed_t viewx, fixed_t viewy, fixed_t viewz, angle_t viewangle, bool mirror, bool planemirror) +{ + SetCameraPos(viewx, viewy, viewz, viewangle); + SetViewMatrix(mirror, planemirror); +} + +//----------------------------------------------------------------------------- +// +// CreateScene +// +// creates the draw lists for the current scene +// +//----------------------------------------------------------------------------- + +void FGLRenderer::CreateScene() +{ + // reset the portal manager + GLPortal::StartFrame(); + PO_LinkToSubsectors(); + + ProcessAll.Clock(); + + // clip the scene and fill the drawlists + for(unsigned i=0;iglportal = NULL; + gl_spriteindex=0; + Bsp.Clock(); + gl_RenderBSPNode (nodes + numnodes - 1); + Bsp.Unclock(); + + // And now the crappy hacks that have to be done to avoid rendering anomalies: + + gl_drawinfo->HandleMissingTextures(); // Missing upper/lower textures + gl_drawinfo->HandleHackedSubsectors(); // open sector hacks for deep water + gl_drawinfo->ProcessSectorStacks(); // merge visplanes of sector stacks + + GLRenderer->mVBO->UnmapVBO (); + ProcessAll.Unclock(); + +} + +//----------------------------------------------------------------------------- +// +// RenderScene +// +// Draws the current draw lists for the non GLSL renderer +// +//----------------------------------------------------------------------------- + +void FGLRenderer::RenderScene(int recursion) +{ + RenderAll.Clock(); + + gl.DepthMask(true); + if (!gl_no_skyclear) GLPortal::RenderFirstSkyPortal(recursion); + + gl_RenderState.SetCameraPos(FIXED2FLOAT(viewx), FIXED2FLOAT(viewy), FIXED2FLOAT(viewz)); + + gl_RenderState.EnableFog(true); + gl_RenderState.BlendFunc(GL_ONE,GL_ZERO); + + // First draw all single-pass stuff + + // Part 1: solid geometry. This is set up so that there are no transparent parts + gl.DepthFunc(GL_LESS); + + + gl_RenderState.EnableAlphaTest(false); + + gl.Disable(GL_POLYGON_OFFSET_FILL); // just in case + + int pass; + + if (mLightCount > 0 && gl_fixedcolormap == CM_DEFAULT && gl_lights && gl_dynlight_shader) + { + pass = GLPASS_ALL; + } + else if (gl_texture) + { + pass = GLPASS_PLAIN; + } + else + { + pass = GLPASS_BASE; + } + + gl_RenderState.EnableTexture(gl_texture); + gl_RenderState.EnableBrightmap(gl_fixedcolormap == CM_DEFAULT); + gl_drawinfo->drawlists[GLDL_PLAIN].Sort(); + gl_drawinfo->drawlists[GLDL_PLAIN].Draw(pass); + gl_RenderState.EnableBrightmap(false); + gl_drawinfo->drawlists[GLDL_FOG].Sort(); + gl_drawinfo->drawlists[GLDL_FOG].Draw(pass); + gl_drawinfo->drawlists[GLDL_LIGHTFOG].Sort(); + gl_drawinfo->drawlists[GLDL_LIGHTFOG].Draw(pass); + + + gl_RenderState.EnableAlphaTest(true); + + // Part 2: masked geometry. This is set up so that only pixels with alpha>0.5 will show + if (!gl_texture) + { + gl_RenderState.EnableTexture(true); + gl_RenderState.SetTextureMode(TM_MASK); + } + if (pass == GLPASS_BASE) pass = GLPASS_BASE_MASKED; + gl_RenderState.AlphaFunc(GL_GEQUAL,gl_mask_threshold); + gl_RenderState.EnableBrightmap(true); + gl_drawinfo->drawlists[GLDL_MASKED].Sort(); + gl_drawinfo->drawlists[GLDL_MASKED].Draw(pass); + gl_RenderState.EnableBrightmap(false); + gl_drawinfo->drawlists[GLDL_FOGMASKED].Sort(); + gl_drawinfo->drawlists[GLDL_FOGMASKED].Draw(pass); + gl_drawinfo->drawlists[GLDL_LIGHTFOGMASKED].Sort(); + gl_drawinfo->drawlists[GLDL_LIGHTFOGMASKED].Draw(pass); + + // And now the multipass stuff + if (!gl_dynlight_shader && gl_lights) + { + // First pass: empty background with sector light only + + // Part 1: solid geometry. This is set up so that there are no transparent parts + + // remove any remaining texture bindings and shaders whick may get in the way. + gl_RenderState.EnableTexture(false); + gl_RenderState.EnableBrightmap(false); + gl_RenderState.Apply(); + gl_drawinfo->drawlists[GLDL_LIGHT].Draw(GLPASS_BASE); + gl_RenderState.EnableTexture(true); + + // Part 2: masked geometry. This is set up so that only pixels with alpha>0.5 will show + // This creates a blank surface that only fills the nontransparent parts of the texture + gl_RenderState.SetTextureMode(TM_MASK); + gl_RenderState.EnableBrightmap(true); + gl_drawinfo->drawlists[GLDL_LIGHTBRIGHT].Draw(GLPASS_BASE_MASKED); + gl_drawinfo->drawlists[GLDL_LIGHTMASKED].Draw(GLPASS_BASE_MASKED); + gl_RenderState.EnableBrightmap(false); + gl_RenderState.SetTextureMode(TM_MODULATE); + + + // second pass: draw lights (on fogged surfaces they are added to the textures!) + gl.DepthMask(false); + if (mLightCount && !gl_fixedcolormap) + { + if (gl_SetupLightTexture()) + { + gl_RenderState.BlendFunc(GL_ONE, GL_ONE); + gl.DepthFunc(GL_EQUAL); + if (glset.lightmode == 8) gl.VertexAttrib1f(VATTR_LIGHTLEVEL, 1.0f); // Korshun. + for(int i=GLDL_FIRSTLIGHT; i<=GLDL_LASTLIGHT; i++) + { + gl_drawinfo->drawlists[i].Draw(GLPASS_LIGHT); + } + gl_RenderState.BlendEquation(GL_FUNC_ADD); + } + else gl_lights=false; + } + + // third pass: modulated texture + gl.Color3f(1.0f, 1.0f, 1.0f); + gl_RenderState.BlendFunc(GL_DST_COLOR, GL_ZERO); + gl_RenderState.EnableFog(false); + gl.DepthFunc(GL_LEQUAL); + if (gl_texture) + { + gl_RenderState.EnableAlphaTest(false); + gl_drawinfo->drawlists[GLDL_LIGHT].Sort(); + gl_drawinfo->drawlists[GLDL_LIGHT].Draw(GLPASS_TEXTURE); + gl_RenderState.EnableAlphaTest(true); + gl_drawinfo->drawlists[GLDL_LIGHTBRIGHT].Sort(); + gl_drawinfo->drawlists[GLDL_LIGHTBRIGHT].Draw(GLPASS_TEXTURE); + gl_drawinfo->drawlists[GLDL_LIGHTMASKED].Sort(); + gl_drawinfo->drawlists[GLDL_LIGHTMASKED].Draw(GLPASS_TEXTURE); + } + + // fourth pass: additive lights + gl_RenderState.EnableFog(true); + if (gl_lights && mLightCount && !gl_fixedcolormap) + { + gl_RenderState.BlendFunc(GL_ONE, GL_ONE); + gl.DepthFunc(GL_EQUAL); + if (gl_SetupLightTexture()) + { + for(int i=0; idrawlists[i].Draw(GLPASS_LIGHT_ADDITIVE); + } + gl_RenderState.BlendEquation(GL_FUNC_ADD); + } + else gl_lights=false; + } + } + + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // Draw decals (not a real pass) + gl.DepthFunc(GL_LEQUAL); + gl.Enable(GL_POLYGON_OFFSET_FILL); + gl.PolygonOffset(-1.0f, -128.0f); + gl.DepthMask(false); + + for(int i=0; idrawlists[i].Draw(GLPASS_DECALS); + } + + gl_RenderState.SetTextureMode(TM_MODULATE); + + gl.DepthMask(true); + + + // Push bleeding floor/ceiling textures back a little in the z-buffer + // so they don't interfere with overlapping mid textures. + gl.PolygonOffset(1.0f, 128.0f); + + // flood all the gaps with the back sector's flat texture + // This will always be drawn like GLDL_PLAIN or GLDL_FOG, depending on the fog settings + + if (!(gl.flags&RFL_NOSTENCIL)) // needs a stencil to work! + { + gl.DepthMask(false); // don't write to Z-buffer! + gl_RenderState.EnableFog(true); + gl_RenderState.EnableAlphaTest(false); + gl_RenderState.BlendFunc(GL_ONE,GL_ZERO); + gl_drawinfo->DrawUnhandledMissingTextures(); + gl_RenderState.EnableAlphaTest(true); + } + gl.DepthMask(true); + + gl.PolygonOffset(0.0f, 0.0f); + gl.Disable(GL_POLYGON_OFFSET_FILL); + + RenderAll.Unclock(); +} + +//----------------------------------------------------------------------------- +// +// RenderTranslucent +// +// Draws the current draw lists for the non GLSL renderer +// +//----------------------------------------------------------------------------- + +void FGLRenderer::RenderTranslucent() +{ + RenderAll.Clock(); + + gl.DepthMask(false); + gl_RenderState.SetCameraPos(FIXED2FLOAT(viewx), FIXED2FLOAT(viewy), FIXED2FLOAT(viewz)); + + // final pass: translucent stuff + gl_RenderState.EnableAlphaTest(true); + gl_RenderState.AlphaFunc(GL_GEQUAL,gl_mask_sprite_threshold); + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + gl_RenderState.EnableBrightmap(true); + gl_drawinfo->drawlists[GLDL_TRANSLUCENTBORDER].Draw(GLPASS_TRANSLUCENT); + gl_drawinfo->drawlists[GLDL_TRANSLUCENT].DrawSorted(); + gl_RenderState.EnableBrightmap(false); + + gl.DepthMask(true); + + gl_RenderState.AlphaFunc(GL_GEQUAL,0.5f); + RenderAll.Unclock(); +} + + +//----------------------------------------------------------------------------- +// +// gl_drawscene - this function renders the scene from the current +// viewpoint, including mirrors and skyboxes and other portals +// It is assumed that the GLPortal::EndFrame returns with the +// stencil, z-buffer and the projection matrix intact! +// +//----------------------------------------------------------------------------- +EXTERN_CVAR(Bool, gl_draw_sync) + +void FGLRenderer::DrawScene(bool toscreen) +{ + static int recursion=0; + + CreateScene(); + GLRenderer->mCurrentPortal = NULL; // this must be reset before any portal recursion takes place. + + // Up to this point in the main draw call no rendering is performed so we can wait + // with swapping the render buffer until now. + if (!gl_draw_sync && toscreen) + { + All.Unclock(); + static_cast(screen)->Swap(); + All.Clock(); + } + RenderScene(recursion); + + // Handle all portals after rendering the opaque objects but before + // doing all translucent stuff + recursion++; + GLPortal::EndFrame(); + recursion--; + RenderTranslucent(); +} + + +//========================================================================== +// +// Draws a blend over the entire view +// +//========================================================================== +void FGLRenderer::DrawBlend(sector_t * viewsector) +{ + float blend[4]={0,0,0,0}; + PalEntry blendv=0; + float extra_red; + float extra_green; + float extra_blue; + player_t *player = NULL; + + if (players[consoleplayer].camera != NULL) + { + player=players[consoleplayer].camera->player; + } + + // don't draw sector based blends when an invulnerability colormap is active + if (!gl_fixedcolormap) + { + if (!viewsector->e->XFloor.ffloors.Size()) + { + if (viewsector->heightsec && !(viewsector->MoreFlags&SECF_IGNOREHEIGHTSEC)) + { + switch(in_area) + { + default: + case area_normal: blendv=viewsector->heightsec->midmap; break; + case area_above: blendv=viewsector->heightsec->topmap; break; + case area_below: blendv=viewsector->heightsec->bottommap; break; + } + } + } + else + { + TArray & lightlist = viewsector->e->XFloor.lightlist; + + for(unsigned int i=0;ifloorplane.ZatPoint(viewx,viewy); + + if (lightbottomflags&FF_FADEWALLS))) + { + // 3d floor 'fog' is rendered as a blending value + blendv=lightlist[i].blend; + // If this is the same as the sector's it doesn't apply! + if (blendv == viewsector->ColorMap->Fade) blendv=0; + // a little hack to make this work for Legacy maps. + if (blendv.a==0 && blendv!=0) blendv.a=128; + break; + } + } + } + } + + if (blendv.a==0) + { + blendv = R_BlendForColormap(blendv); + if (blendv.a==255) + { + // The calculated average is too dark so brighten it according to the palettes's overall brightness + int maxcol = MAX(MAX(framebuffer->palette_brightness, blendv.r), MAX(blendv.g, blendv.b)); + blendv.r = blendv.r * 255 / maxcol; + blendv.g = blendv.g * 255 / maxcol; + blendv.b = blendv.b * 255 / maxcol; + } + } + + if (blendv.a==255) + { + + extra_red = blendv.r / 255.0f; + extra_green = blendv.g / 255.0f; + extra_blue = blendv.b / 255.0f; + + // If this is a multiplicative blend do it separately and add the additive ones on top of it! + blendv=0; + + // black multiplicative blends are ignored + if (extra_red || extra_green || extra_blue) + { + gl_RenderState.EnableAlphaTest(false); + gl_RenderState.EnableTexture(false); + gl_RenderState.BlendFunc(GL_DST_COLOR,GL_ZERO); + gl.Color4f(extra_red, extra_green, extra_blue, 1.0f); + gl_RenderState.Apply(true); + gl.Begin(GL_TRIANGLE_STRIP); + gl.Vertex2f( 0.0f, 0.0f); + gl.Vertex2f( 0.0f, (float)SCREENHEIGHT); + gl.Vertex2f( (float)SCREENWIDTH, 0.0f); + gl.Vertex2f( (float)SCREENWIDTH, (float)SCREENHEIGHT); + gl.End(); + } + } + else if (blendv.a) + { + V_AddBlend (blendv.r / 255.f, blendv.g / 255.f, blendv.b / 255.f, blendv.a/255.0f,blend); + } + + // This mostly duplicates the code in shared_sbar.cpp + // When I was writing this the original was called too late so that I + // couldn't get the blend in time. However, since then I made some changes + // here that would get lost if I switched back so I won't do it. + + if (player) + { + V_AddPlayerBlend(player, blend, 0.5, 175); + } + + if (players[consoleplayer].camera != NULL) + { + // except for fadeto effects + player_t *player = (players[consoleplayer].camera->player != NULL) ? players[consoleplayer].camera->player : &players[consoleplayer]; + V_AddBlend (player->BlendR, player->BlendG, player->BlendB, player->BlendA, blend); + } + + if (blend[3]>0.0f) + { + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_RenderState.EnableAlphaTest(false); + gl_RenderState.EnableTexture(false); + gl.Color4fv(blend); + gl_RenderState.Apply(true); + gl.Begin(GL_TRIANGLE_STRIP); + gl.Vertex2f( 0.0f, 0.0f); + gl.Vertex2f( 0.0f, (float)SCREENHEIGHT); + gl.Vertex2f( (float)SCREENWIDTH, 0.0f); + gl.Vertex2f( (float)SCREENWIDTH, (float)SCREENHEIGHT); + gl.End(); + } +} + + +//----------------------------------------------------------------------------- +// +// Draws player sprites and color blend +// +//----------------------------------------------------------------------------- + + +void FGLRenderer::EndDrawScene(sector_t * viewsector) +{ + // [BB] HUD models need to be rendered here. Make sure that + // DrawPlayerSprites is only called once. Either to draw + // HUD models or to draw the weapon sprites. + const bool renderHUDModel = gl_IsHUDModelForPlayerAvailable( players[consoleplayer].camera->player ); + if ( renderHUDModel ) + { + // [BB] The HUD model should be drawn over everything else already drawn. + gl.Clear(GL_DEPTH_BUFFER_BIT); + DrawPlayerSprites (viewsector, true); + } + + gl.Disable(GL_STENCIL_TEST); + gl.Disable(GL_POLYGON_SMOOTH); + + gl_RenderState.EnableFog(false); + framebuffer->Begin2D(false); + + ResetViewport(); + // [BB] Only draw the sprites if we didn't render a HUD model before. + if ( renderHUDModel == false ) + { + DrawPlayerSprites (viewsector, false); + } + DrawTargeterSprites(); + DrawBlend(viewsector); + + // Restore standard rendering state + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl.Color3f(1.0f,1.0f,1.0f); + gl_RenderState.EnableTexture(true); + gl_RenderState.EnableAlphaTest(true); + gl.Disable(GL_SCISSOR_TEST); +} + + +//----------------------------------------------------------------------------- +// +// R_RenderView - renders one view - either the screen or a camera texture +// +//----------------------------------------------------------------------------- + +void FGLRenderer::ProcessScene(bool toscreen) +{ + FDrawInfo::StartDrawInfo(); + iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0; + GLPortal::BeginScene(); + + int mapsection = R_PointInSubsector(viewx, viewy)->mapsection; + memset(¤tmapsection[0], 0, currentmapsection.Size()); + currentmapsection[mapsection>>3] |= 1 << (mapsection & 7); + DrawScene(toscreen); + FDrawInfo::EndDrawInfo(); + +} + +//----------------------------------------------------------------------------- +// +// gl_SetFixedColormap +// +//----------------------------------------------------------------------------- + +void FGLRenderer::SetFixedColormap (player_t *player) +{ + gl_fixedcolormap=CM_DEFAULT; + + // check for special colormaps + player_t * cplayer = player->camera->player; + if (cplayer) + { + if (cplayer->extralight<0) + { + gl_fixedcolormap=CM_FIRSTSPECIALCOLORMAP + INVERSECOLORMAP; + extralight=0; + } + else if (cplayer->fixedcolormap != NOFIXEDCOLORMAP) + { + gl_fixedcolormap = CM_FIRSTSPECIALCOLORMAP + cplayer->fixedcolormap; + } + else if (cplayer->fixedlightlevel != -1) + { + for(AInventory * in = cplayer->mo->Inventory; in; in = in->Inventory) + { + PalEntry color = in->GetBlend (); + + // Need special handling for light amplifiers + if (in->IsKindOf(RUNTIME_CLASS(APowerTorch))) + { + gl_fixedcolormap = cplayer->fixedlightlevel + CM_TORCH; + } + else if (in->IsKindOf(RUNTIME_CLASS(APowerLightAmp))) + { + gl_fixedcolormap = CM_LITE; + } + } + } + } +} + +//----------------------------------------------------------------------------- +// +// Renders one viewpoint in a scene +// +//----------------------------------------------------------------------------- + +sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) +{ + sector_t * retval; + R_SetupFrame (camera); + SetViewArea(); + mAngles.Pitch = clamp((float)((double)(int)(viewpitch))/ANGLE_1, -90, 90); + + // Scroll the sky + mSky1Pos = (float)fmod(gl_frameMS * level.skyspeed1, 1024.f) * 90.f/256.f; + mSky2Pos = (float)fmod(gl_frameMS * level.skyspeed2, 1024.f) * 90.f/256.f; + + + + if (camera->player && camera->player-players==consoleplayer && + ((camera->player->cheats & CF_CHASECAM) || (r_deathcamera && camera->health <= 0)) && camera==camera->player->mo) + { + mViewActor=NULL; + } + else + { + mViewActor=camera; + } + + retval = viewsector; + + SetViewport(bounds); + mCurrentFoV = fov; + SetProjection(fov, ratio, fovratio); // switch to perspective mode and set up clipper + SetCameraPos(viewx, viewy, viewz, viewangle); + SetViewMatrix(false, false); + + clipper.Clear(); + angle_t a1 = FrustumAngle(); + clipper.SafeAddClipRangeRealAngles(viewangle+a1, viewangle-a1); + + ProcessScene(toscreen); + + gl_frameCount++; // This counter must be increased right before the interpolations are restored. + interpolator.RestoreInterpolations (); + return retval; +} + + +//----------------------------------------------------------------------------- +// +// renders the view +// +//----------------------------------------------------------------------------- + +void FGLRenderer::RenderView (player_t* player) +{ + OpenGLFrameBuffer* GLTarget = static_cast(screen); + AActor *&LastCamera = GLTarget->LastCamera; + + if (player->camera != LastCamera) + { + // If the camera changed don't interpolate + // Otherwise there will be some not so nice effects. + R_ResetViewInterpolation(); + LastCamera=player->camera; + } + + mVBO->BindVBO(); + + // reset statistics counters + ResetProfilingData(); + + // Get this before everything else + if (cl_capfps || r_NoInterpolate) r_TicFrac = FRACUNIT; + else r_TicFrac = I_GetTimeFrac (&r_FrameTime); + gl_frameMS = I_MSTime(); + + P_FindParticleSubsectors (); + + // prepare all camera textures that have been used in the last frame + FCanvasTextureInfo::UpdateAll(); + + + // I stopped using BaseRatioSizes here because the information there wasn't well presented. + #define RMUL (1.6f/1.333333f) + // 4:3 16:9 16:10 17:10 5:4 + static float ratios[]={RMUL*1.333333f, RMUL*1.777777f, RMUL*1.6f, RMUL*1.7f, RMUL*1.25f}; + + // now render the main view + float fovratio; + float ratio = ratios[WidescreenRatio]; + if (!(WidescreenRatio&4)) + { + fovratio = 1.6f; + } + else + { + fovratio = ratio; + } + + SetFixedColormap (player); + + // Check if there's some lights. If not some code can be skipped. + TThinkerIterator it(STAT_DLIGHT); + GLRenderer->mLightCount = ((it.Next()) != NULL); + + sector_t * viewsector = RenderViewpoint(player->camera, NULL, FieldOfView * 360.0f / FINEANGLES, ratio, fovratio, true, true); + EndDrawScene(viewsector); + + All.Unclock(); +} + +//=========================================================================== +// +// Render the view to a savegame picture +// +//=========================================================================== + +void FGLRenderer::WriteSavePic (player_t *player, FILE *file, int width, int height) +{ + GL_IRECT bounds; + + bounds.left=0; + bounds.top=0; + bounds.width=width; + bounds.height=height; + gl.Flush(); + SetFixedColormap(player); + + // Check if there's some lights. If not some code can be skipped. + TThinkerIterator it(STAT_DLIGHT); + GLRenderer->mLightCount = ((it.Next()) != NULL); + + sector_t *viewsector = RenderViewpoint(players[consoleplayer].camera, &bounds, + FieldOfView * 360.0f / FINEANGLES, 1.6f, 1.6f, true, false); + gl.Disable(GL_STENCIL_TEST); + screen->Begin2D(false); + DrawBlend(viewsector); + gl.Flush(); + + byte * scr = (byte *)M_Malloc(width * height * 3); + gl.ReadPixels(0,0,width, height,GL_RGB,GL_UNSIGNED_BYTE,scr); + M_CreatePNG (file, scr + ((height-1) * width * 3), NULL, SS_RGB, width, height, -width*3); + M_Free(scr); +} + + +//=========================================================================== +// +// +// +//=========================================================================== + +struct FGLInterface : public FRenderer +{ + bool UsesColormap() const; + void PrecacheTexture(FTexture *tex, int cache); + void RenderView(player_t *player); + void WriteSavePic (player_t *player, FILE *file, int width, int height); + void StateChanged(AActor *actor); + void StartSerialize(FArchive &arc); + void EndSerialize(FArchive &arc); + void RenderTextureView (FCanvasTexture *self, AActor *viewpoint, int fov); + sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back); + void SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog); + void PreprocessLevel(); + void CleanLevelData(); + bool RequireGLNodes(); + + int GetMaxViewPitch(bool down); + void ClearBuffer(int color); + void Init(); +}; + +//=========================================================================== +// +// The GL renderer has no use for colormaps so let's +// not create them and save us some time. +// +//=========================================================================== + +bool FGLInterface::UsesColormap() const +{ + return false; +} + +//========================================================================== +// +// DFrameBuffer :: PrecacheTexture +// +//========================================================================== + +void FGLInterface::PrecacheTexture(FTexture *tex, int cache) +{ + if (tex != NULL) + { + if (cache) + { + tex->PrecacheGL(); + } + else + { + tex->UncacheGL(); + } + } +} + +//========================================================================== +// +// DFrameBuffer :: StateChanged +// +//========================================================================== + +void FGLInterface::StateChanged(AActor *actor) +{ + gl_SetActorLights(actor); +} + +//=========================================================================== +// +// notify the renderer that serialization of the curent level is about to start/end +// +//=========================================================================== + +void FGLInterface::StartSerialize(FArchive &arc) +{ + gl_DeleteAllAttachedLights(); + arc << fogdensity << outsidefogdensity << skyfog; +} + +void FGLInterface::EndSerialize(FArchive &arc) +{ + gl_RecreateAllAttachedLights(); + if (arc.IsLoading()) gl_InitPortals(); +} + +//=========================================================================== +// +// Get max. view angle (renderer specific information so it goes here now) +// +//=========================================================================== + +EXTERN_CVAR(Float, maxviewpitch) + +int FGLInterface::GetMaxViewPitch(bool down) +{ + return int(maxviewpitch); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLInterface::ClearBuffer(int color) +{ + PalEntry pe = GPalette.BaseColors[color]; + gl.ClearColor(pe.r/255.f, pe.g/255.f, pe.b/255.f, 1.f); + gl.Clear(GL_COLOR_BUFFER_BIT); +} + +//=========================================================================== +// +// Render the view to a savegame picture +// +//=========================================================================== + +void FGLInterface::WriteSavePic (player_t *player, FILE *file, int width, int height) +{ + GLRenderer->WriteSavePic(player, file, width, height); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLInterface::RenderView(player_t *player) +{ + GLRenderer->RenderView(player); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLInterface::Init() +{ + gl_ParseDefs(); +} + +//=========================================================================== +// +// Camera texture rendering +// +//=========================================================================== +CVAR(Bool, gl_usefb, false , CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +extern TexFilter_s TexFilter[]; + +void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, int FOV) +{ + FMaterial * gltex = FMaterial::ValidateTexture(tex); + + int width = gltex->TextureWidth(GLUSE_TEXTURE); + int height = gltex->TextureHeight(GLUSE_TEXTURE); + + gl_fixedcolormap=CM_DEFAULT; + + bool usefb; + + if (gl.flags & RFL_FRAMEBUFFER) + { + usefb = gl_usefb || width > screen->GetWidth() || height > screen->GetHeight(); + } + else usefb = false; + + + if (!usefb) + { + gl.Flush(); + } + else + { +#if defined(_WIN32) && (defined(_MSC_VER) || defined(__INTEL_COMPILER)) + __try +#endif + { + GLRenderer->StartOffscreen(); + gltex->BindToFrameBuffer(); + } +#if defined(_WIN32) && (defined(_MSC_VER) || defined(__INTEL_COMPILER)) + __except(1) + { + usefb = false; + gl_usefb = false; + GLRenderer->EndOffscreen(); + gl.Flush(); + } +#endif + } + + GL_IRECT bounds; + bounds.left=bounds.top=0; + bounds.width=FHardwareTexture::GetTexDimension(gltex->GetWidth(GLUSE_TEXTURE)); + bounds.height=FHardwareTexture::GetTexDimension(gltex->GetHeight(GLUSE_TEXTURE)); + + GLRenderer->RenderViewpoint(Viewpoint, &bounds, FOV, (float)width/height, (float)width/height, false, false); + + if (!usefb) + { + gl.Flush(); + gltex->Bind(CM_DEFAULT, 0, 0); + gl.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, bounds.width, bounds.height); + } + else + { + GLRenderer->EndOffscreen(); + } + + gltex->Bind(CM_DEFAULT, 0, 0); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TexFilter[gl_texture_filter].magfilter); + tex->SetUpdated(); +} + +//========================================================================== +// +// +// +//========================================================================== + +sector_t *FGLInterface::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) +{ + if (floorlightlevel != NULL) + { + *floorlightlevel = sec->GetFloorLight (); + } + if (ceilinglightlevel != NULL) + { + *ceilinglightlevel = sec->GetCeilingLight (); + } + return gl_FakeFlat(sec, tempsec, back); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLInterface::SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog) +{ + gl_SetFogParams(_fogdensity, _outsidefogcolor, _outsidefogdensity, _skyfog); +} + +void FGLInterface::PreprocessLevel() +{ + gl_PreprocessLevel(); +} + +void FGLInterface::CleanLevelData() +{ + gl_CleanLevelData(); +} + +bool FGLInterface::RequireGLNodes() +{ + return true; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +FRenderer *gl_CreateInterface() +{ + return new FGLInterface; +} + + diff --git a/src/gl/scene/gl_sky.cpp b/src/gl/scene/gl_sky.cpp new file mode 100644 index 000000000..b236f45d8 --- /dev/null +++ b/src/gl/scene/gl_sky.cpp @@ -0,0 +1,353 @@ +/* +** gl_sky.cpp +** Sky preparation code. +** +**--------------------------------------------------------------------------- +** Copyright 2002-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "a_sharedglobal.h" +#include "g_level.h" +#include "r_sky.h" +#include "r_state.h" +#include "r_utility.h" +#include "doomdata.h" +#include "gl/gl_functions.h" + +#include "gl/data/gl_data.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_portal.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_convert.h" + +CVAR(Bool,gl_noskyboxes, false, 0) +extern int skyfog; + +enum +{ + NoSkyDraw = 89 +}; + + +//========================================================================== +// +// Calculate sky texture +// +//========================================================================== +void GLWall::SkyPlane(sector_t *sector, int plane, bool allowreflect) +{ + FPortal *portal = sector->portals[plane]; + if (portal != NULL) + { + if (GLPortal::instack[1-plane]) return; + type=RENDERWALL_SECTORSTACK; + this->portal = portal; + } + else if (sector->GetTexture(plane)==skyflatnum) + { + GLSkyInfo skyinfo; + ASkyViewpoint * skyboxx = plane == sector_t::floor? sector->FloorSkyBox : sector->CeilingSkyBox; + if (skyboxx == NULL) skyboxx = level.DefaultSkybox; + + // JUSTHIT is used as an indicator that a skybox is in use. + // This is to avoid recursion + + if (!gl_noskyboxes && skyboxx && GLRenderer->mViewActor!=skyboxx && !(skyboxx->flags&MF_JUSTHIT)) + { + type=RENDERWALL_SKYBOX; + skybox=skyboxx; + } + else + { + int sky1 = sector->sky; + memset(&skyinfo, 0, sizeof(skyinfo)); + if ((sky1 & PL_SKYFLAT) && (sky1 & (PL_SKYFLAT-1))) + { + const line_t *l = &lines[(sky1&(PL_SKYFLAT-1))-1]; + const side_t *s = l->sidedef[0]; + int pos; + + if (level.flags & LEVEL_SWAPSKIES && s->GetTexture(side_t::bottom).isValid()) + { + pos = side_t::bottom; + } + else + { + pos = side_t::top; + } + + FTextureID texno = s->GetTexture(pos); + skyinfo.texture[0] = FMaterial::ValidateTexture(texno, true); + if (!skyinfo.texture[0] || skyinfo.texture[0]->tex->UseType == FTexture::TEX_Null) goto normalsky; + skyinfo.skytexno1 = texno; + skyinfo.x_offset[0] = ANGLE_TO_FLOAT(s->GetTextureXOffset(pos)); + skyinfo.y_offset = FIXED2FLOAT(s->GetTextureYOffset(pos)); + skyinfo.mirrored = !l->args[2]; + } + else + { + normalsky: + if (level.flags&LEVEL_DOUBLESKY) + { + skyinfo.texture[1]=FMaterial::ValidateTexture(sky1texture, true); + skyinfo.x_offset[1] = GLRenderer->mSky1Pos; + skyinfo.doublesky = true; + } + + if ((level.flags&LEVEL_SWAPSKIES || (sky1==PL_SKYFLAT) || (level.flags&LEVEL_DOUBLESKY)) && + sky2texture!=sky1texture) // If both skies are equal use the scroll offset of the first! + { + skyinfo.texture[0]=FMaterial::ValidateTexture(sky2texture, true); + skyinfo.skytexno1=sky2texture; + skyinfo.sky2 = true; + skyinfo.x_offset[0] = GLRenderer->mSky2Pos; + } + else + { + skyinfo.texture[0]=FMaterial::ValidateTexture(sky1texture, true); + skyinfo.skytexno1=sky1texture; + skyinfo.x_offset[0] = GLRenderer->mSky1Pos; + } + } + if (skyfog>0) + { + skyinfo.fadecolor=Colormap.FadeColor; + skyinfo.fadecolor.a=0; + } + else skyinfo.fadecolor=0; + + type=RENDERWALL_SKY; + sky=UniqueSkies.Get(&skyinfo); + } + } + else if (allowreflect && sector->GetReflect(plane) > 0) + { + if ((plane == sector_t::ceiling && viewz > sector->ceilingplane.d) || + (plane == sector_t::floor && viewz < -sector->floorplane.d)) return; + type=RENDERWALL_PLANEMIRROR; + planemirror = plane == sector_t::ceiling? §or->ceilingplane : §or->floorplane; + } + else return; + PutWall(0); +} + + +//========================================================================== +// +// Skies on one sided walls +// +//========================================================================== + +void GLWall::SkyNormal(sector_t * fs,vertex_t * v1,vertex_t * v2) +{ + ztop[0]=ztop[1]=32768.0f; + zbottom[0]=zceil[0]; + zbottom[1]=zceil[1]; + SkyPlane(fs, sector_t::ceiling, true); + + ztop[0]=zfloor[0]; + ztop[1]=zfloor[1]; + zbottom[0]=zbottom[1]=-32768.0f; + SkyPlane(fs, sector_t::floor, true); +} + +//========================================================================== +// +// Upper Skies on two sided walls +// +//========================================================================== + +void GLWall::SkyTop(seg_t * seg,sector_t * fs,sector_t * bs,vertex_t * v1,vertex_t * v2) +{ + if (fs->GetTexture(sector_t::ceiling)==skyflatnum) + { + if ((bs->special&0xff) == NoSkyDraw) return; + if (bs->GetTexture(sector_t::ceiling)==skyflatnum) + { + // if the back sector is closed the sky must be drawn! + if (bs->ceilingplane.ZatPoint(v1) > bs->floorplane.ZatPoint(v1) || + bs->ceilingplane.ZatPoint(v2) > bs->floorplane.ZatPoint(v2) || bs->transdoor) + return; + + // one more check for some ugly transparent door hacks + if (bs->floorplane.a==0 && bs->floorplane.b==0 && fs->floorplane.a==0 && fs->floorplane.b==0) + { + if (bs->GetPlaneTexZ(sector_t::floor)==fs->GetPlaneTexZ(sector_t::floor)+FRACUNIT) + { + FTexture * tex = TexMan(seg->sidedef->GetTexture(side_t::bottom)); + if (!tex || tex->UseType==FTexture::TEX_Null) return; + + // very, very, very ugly special case (See Icarus MAP14) + // It is VERY important that this is only done for a floor height difference of 1 + // or it will cause glitches elsewhere. + tex = TexMan(seg->sidedef->GetTexture(side_t::mid)); + if (tex != NULL && !(seg->linedef->flags & ML_DONTPEGTOP) && + seg->sidedef->GetTextureYOffset(side_t::mid) > 0) + { + ztop[0]=ztop[1]=32768.0f; + zbottom[0]=zbottom[1]= + FIXED2FLOAT(bs->ceilingplane.ZatPoint(v2) + seg->sidedef->GetTextureYOffset(side_t::mid)); + SkyPlane(fs, sector_t::ceiling, false); + return; + } + } + } + } + + ztop[0]=ztop[1]=32768.0f; + + FTexture * tex = TexMan(seg->sidedef->GetTexture(side_t::top)); + if (bs->GetTexture(sector_t::ceiling) != skyflatnum) + + { + zbottom[0]=zceil[0]; + zbottom[1]=zceil[1]; + } + else + { + zbottom[0]=FIXED2FLOAT(bs->ceilingplane.ZatPoint(v1)); + zbottom[1]=FIXED2FLOAT(bs->ceilingplane.ZatPoint(v2)); + flags|=GLWF_SKYHACK; // mid textures on such lines need special treatment! + } + } + else + { + FPortal *pfront = fs->portals[sector_t::ceiling]; + FPortal *pback = bs->portals[sector_t::ceiling]; + float frontreflect = fs->GetReflect(sector_t::ceiling); + if (frontreflect > 0) + { + float backreflect = bs->GetReflect(sector_t::ceiling); + if (backreflect > 0 && bs->ceilingplane.d == fs->ceilingplane.d) + { + // Don't add intra-portal line to the portal. + return; + } + } + else if (pfront == NULL || pfront == pback) + { + return; + } + + // stacked sectors + fixed_t fsc1=fs->ceilingplane.ZatPoint(v1); + fixed_t fsc2=fs->ceilingplane.ZatPoint(v2); + + ztop[0]=ztop[1]=32768.0f; + zbottom[0]=FIXED2FLOAT(fsc1); + zbottom[1]=FIXED2FLOAT(fsc2); + } + + SkyPlane(fs, sector_t::ceiling, true); +} + + +//========================================================================== +// +// Lower Skies on two sided walls +// +//========================================================================== + +void GLWall::SkyBottom(seg_t * seg,sector_t * fs,sector_t * bs,vertex_t * v1,vertex_t * v2) +{ + if (fs->GetTexture(sector_t::floor)==skyflatnum) + { + if ((bs->special&0xff) == NoSkyDraw) return; + FTexture * tex = TexMan(seg->sidedef->GetTexture(side_t::bottom)); + + // For lower skies the normal logic only applies to walls with no lower texture! + if (tex->UseType==FTexture::TEX_Null) + { + if (bs->GetTexture(sector_t::floor)==skyflatnum) + { + // if the back sector is closed the sky must be drawn! + if (bs->ceilingplane.ZatPoint(v1) > bs->floorplane.ZatPoint(v1) || + bs->ceilingplane.ZatPoint(v2) > bs->floorplane.ZatPoint(v2)) + return; + + } + else + { + // Special hack for Vrack2b + if (bs->floorplane.ZatPoint(FIXED2FLOAT(viewx), FIXED2FLOAT(viewy)) > FIXED2FLOAT(viewz)) return; + } + } + zbottom[0]=zbottom[1]=-32768.0f; + + if ((tex && tex->UseType!=FTexture::TEX_Null) || bs->GetTexture(sector_t::floor)!=skyflatnum) + { + ztop[0]=zfloor[0]; + ztop[1]=zfloor[1]; + } + else + { + ztop[0]=FIXED2FLOAT(bs->floorplane.ZatPoint(v1)); + ztop[1]=FIXED2FLOAT(bs->floorplane.ZatPoint(v2)); + flags|=GLWF_SKYHACK; // mid textures on such lines need special treatment! + } + } + else + { + FPortal *pfront = fs->portals[sector_t::floor]; + FPortal *pback = bs->portals[sector_t::floor]; + float frontreflect = fs->GetReflect(sector_t::floor); + if (frontreflect > 0) + { + float backreflect = bs->GetReflect(sector_t::floor); + if (backreflect > 0 && bs->floorplane.d == fs->floorplane.d) + { + // Don't add intra-portal line to the portal. + return; + } + } + else if (pfront == NULL || pfront == pback) + { + return; + } + + // stacked sectors + fixed_t fsc1=fs->floorplane.ZatPoint(v1); + fixed_t fsc2=fs->floorplane.ZatPoint(v2); + + zbottom[0]=zbottom[1]=-32768.0f; + ztop[0]=FIXED2FLOAT(fsc1); + ztop[1]=FIXED2FLOAT(fsc2); + } + + SkyPlane(fs, sector_t::floor, true); +} + diff --git a/src/gl/scene/gl_skydome.cpp b/src/gl/scene/gl_skydome.cpp new file mode 100644 index 000000000..8bed575b3 --- /dev/null +++ b/src/gl/scene/gl_skydome.cpp @@ -0,0 +1,613 @@ +/* +** 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/data/gl_data.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 (Int, gl_sky_detail, 16, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +EXTERN_CVAR (Bool, r_stretchsky) + +extern int skyfog; + +// The texture offset to be applied to the texture coordinates in SkyVertex(). + +static angle_t maxSideAngle = ANGLE_180 / 3; +static int rows, columns; +static fixed_t scale = 10000 << FRACBITS; +static bool yflip; +static int texw; +static float yAdd; +static bool foglayer; +static bool secondlayer; +static float R,G,B; +static bool skymirror; + +#define SKYHEMI_UPPER 0x1 +#define SKYHEMI_LOWER 0x2 +#define SKYHEMI_JUST_CAP 0x4 // Just draw the top or bottom cap. + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +static void SkyVertex(int r, int c) +{ + angle_t topAngle= (angle_t)(c / (float)columns * ANGLE_MAX); + angle_t sideAngle = maxSideAngle * (rows - r) / rows; + 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]); + float fx, fy, fz; + float color = r * 1.f / rows; + float u, v; + float timesRepeat; + + timesRepeat = (short)(4 * (256.f / texw)); + if (timesRepeat == 0.f) timesRepeat = 1.f; + + if (!foglayer) + { + gl_SetColor(255, 0, NULL, r==0? 0.0f : 1.0f); + + // And the texture coordinates. + if(!yflip) // Flipped Y is for the lower hemisphere. + { + u = (-timesRepeat * c / (float)columns) ; + v = (r / (float)rows) + yAdd; + } + else + { + u = (-timesRepeat * c / (float)columns) ; + v = 1.0f + ((rows-r)/(float)rows) + yAdd; + } + + + gl.TexCoord2f(skymirror? -u:u, v); + } + if (r != 4) y+=FRACUNIT*300; + // And finally the vertex. + fx =-FIXED2FLOAT(x); // Doom mirrors the sky vertically! + fy = FIXED2FLOAT(y); + fz = FIXED2FLOAT(z); + gl.Vertex3f(fx, fy - 1.f, fz); +} + + +//----------------------------------------------------------------------------- +// +// Hemi is Upper or Lower. Zero is not acceptable. +// The current texture is used. SKYHEMI_NO_TOPCAP can be used. +// +//----------------------------------------------------------------------------- + +static void RenderSkyHemisphere(int hemi, bool mirror) +{ + int r, c; + + if (hemi & SKYHEMI_LOWER) + { + yflip = true; + } + else + { + yflip = false; + } + + skymirror = mirror; + + // The top row (row 0) is the one that's faded out. + // There must be at least 4 columns. The preferable number + // is 4n, where n is 1, 2, 3... There should be at least + // two rows because the first one is always faded. + rows = 4; + + if (hemi & SKYHEMI_JUST_CAP) + { + return; + } + + + // Draw the cap as one solid color polygon + if (!foglayer) + { + columns = 4 * (gl_sky_detail > 0 ? gl_sky_detail : 1); + foglayer=true; + gl_RenderState.EnableTexture(false); + gl_RenderState.Apply(true); + + + if (!secondlayer) + { + gl.Color3f(R, G ,B); + gl.Begin(GL_TRIANGLE_FAN); + for(c = 0; c < columns; c++) + { + SkyVertex(1, c); + } + gl.End(); + } + + gl_RenderState.EnableTexture(true); + foglayer=false; + gl_RenderState.Apply(); + } + else + { + gl_RenderState.Apply(true); + columns=4; // no need to do more! + gl.Begin(GL_TRIANGLE_FAN); + for(c = 0; c < columns; c++) + { + SkyVertex(0, c); + } + gl.End(); + } + + // The total number of triangles per hemisphere can be calculated + // as follows: rows * columns * 2 + 2 (for the top cap). + for(r = 0; r < rows; r++) + { + if (yflip) + { + gl.Begin(GL_TRIANGLE_STRIP); + SkyVertex(r + 1, 0); + SkyVertex(r, 0); + for(c = 1; c <= columns; c++) + { + SkyVertex(r + 1, c); + SkyVertex(r, c); + } + gl.End(); + } + else + { + gl.Begin(GL_TRIANGLE_STRIP); + SkyVertex(r, 0); + SkyVertex(r + 1, 0); + for(c = 1; c <= columns; c++) + { + SkyVertex(r, c); + SkyVertex(r + 1, c); + } + gl.End(); + } + } +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- +CVAR(Float, skyoffset, 0, 0) // for testing + +static void RenderDome(FTextureID texno, FMaterial * tex, float x_offset, float y_offset, bool mirror, int CM_Index) +{ + int texh = 0; + bool texscale = false; + + // 57 worls units roughly represent one sky texel for the glTranslate call. + const float skyoffsetfactor = 57; + + if (tex) + { + gl.PushMatrix(); + tex->Bind(CM_Index, 0, 0); + texw = tex->TextureWidth(GLUSE_TEXTURE); + texh = tex->TextureHeight(GLUSE_TEXTURE); + + gl.Rotatef(-180.0f+x_offset, 0.f, 1.f, 0.f); + yAdd = y_offset/texh; + + if (texh < 200) + { + gl.Translatef(0.f, -1250.f, 0.f); + gl.Scalef(1.f, texh/230.f, 1.f); + } + else if (texh <= 240) + { + gl.Translatef(0.f, (200 - texh + tex->tex->SkyOffset + skyoffset)*skyoffsetfactor, 0.f); + gl.Scalef(1.f, 1.f + ((texh-200.f)/200.f) * 1.17f, 1.f); + } + else + { + gl.Translatef(0.f, (-40 + tex->tex->SkyOffset + skyoffset)*skyoffsetfactor, 0.f); + gl.Scalef(1.f, 1.2f * 1.17f, 1.f); + gl.MatrixMode(GL_TEXTURE); + gl.PushMatrix(); + gl.LoadIdentity(); + gl.Scalef(1.f, 240.f / texh, 1.f); + gl.MatrixMode(GL_MODELVIEW); + texscale = true; + } + } + + if (tex && !secondlayer) + { + PalEntry pe = tex->tex->GetSkyCapColor(false); + if (CM_Index!=CM_DEFAULT) ModifyPalette(&pe, &pe, CM_Index, 1); + + R=pe.r/255.0f; + G=pe.g/255.0f; + B=pe.b/255.0f; + + if (gl_fixedcolormap) + { + float rr, gg, bb; + + gl_GetLightColor(255, 0, NULL, &rr, &gg, &bb); + R*=rr; + G*=gg; + B*=bb; + } + } + + RenderSkyHemisphere(SKYHEMI_UPPER, mirror); + + if (tex && !secondlayer) + { + PalEntry pe = tex->tex->GetSkyCapColor(true); + if (CM_Index!=CM_DEFAULT) ModifyPalette(&pe, &pe, CM_Index, 1); + R=pe.r/255.0f; + G=pe.g/255.0f; + B=pe.b/255.0f; + + if (gl_fixedcolormap != CM_DEFAULT) + { + float rr,gg,bb; + + gl_GetLightColor(255, 0, NULL, &rr, &gg, &bb); + R*=rr; + G*=gg; + B*=bb; + } + } + + RenderSkyHemisphere(SKYHEMI_LOWER, mirror); + if (texscale) + { + gl.MatrixMode(GL_TEXTURE); + gl.PopMatrix(); + gl.MatrixMode(GL_MODELVIEW); + } + if (tex) gl.PopMatrix(); + +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +static void RenderBox(FTextureID texno, FMaterial * gltex, float x_offset, int CM_Index, bool sky2) +{ + FSkyBox * sb = static_cast(gltex->tex); + int faces; + FMaterial * tex; + + if (!sky2) + gl.Rotatef(-180.0f+x_offset, glset.skyrotatevector.X, glset.skyrotatevector.Z, glset.skyrotatevector.Y); + else + gl.Rotatef(-180.0f+x_offset, glset.skyrotatevector2.X, glset.skyrotatevector2.Z, glset.skyrotatevector2.Y); + + gl.Color3f(R, G ,B); + + if (sb->faces[5]) + { + faces=4; + + // north + tex = FMaterial::ValidateTexture(sb->faces[0]); + tex->Bind(CM_Index, GLT_CLAMPX|GLT_CLAMPY, 0); + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_FAN); + gl.TexCoord2f(0, 0); + gl.Vertex3f(128.f, 128.f, -128.f); + gl.TexCoord2f(1, 0); + gl.Vertex3f(-128.f, 128.f, -128.f); + gl.TexCoord2f(1, 1); + gl.Vertex3f(-128.f, -128.f, -128.f); + gl.TexCoord2f(0, 1); + gl.Vertex3f(128.f, -128.f, -128.f); + gl.End(); + + // east + tex = FMaterial::ValidateTexture(sb->faces[1]); + tex->Bind(CM_Index, GLT_CLAMPX|GLT_CLAMPY, 0); + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_FAN); + gl.TexCoord2f(0, 0); + gl.Vertex3f(-128.f, 128.f, -128.f); + gl.TexCoord2f(1, 0); + gl.Vertex3f(-128.f, 128.f, 128.f); + gl.TexCoord2f(1, 1); + gl.Vertex3f(-128.f, -128.f, 128.f); + gl.TexCoord2f(0, 1); + gl.Vertex3f(-128.f, -128.f, -128.f); + gl.End(); + + // south + tex = FMaterial::ValidateTexture(sb->faces[2]); + tex->Bind(CM_Index, GLT_CLAMPX|GLT_CLAMPY, 0); + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_FAN); + gl.TexCoord2f(0, 0); + gl.Vertex3f(-128.f, 128.f, 128.f); + gl.TexCoord2f(1, 0); + gl.Vertex3f(128.f, 128.f, 128.f); + gl.TexCoord2f(1, 1); + gl.Vertex3f(128.f, -128.f, 128.f); + gl.TexCoord2f(0, 1); + gl.Vertex3f(-128.f, -128.f, 128.f); + gl.End(); + + // west + tex = FMaterial::ValidateTexture(sb->faces[3]); + tex->Bind(CM_Index, GLT_CLAMPX|GLT_CLAMPY, 0); + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_FAN); + gl.TexCoord2f(0, 0); + gl.Vertex3f(128.f, 128.f, 128.f); + gl.TexCoord2f(1, 0); + gl.Vertex3f(128.f, 128.f, -128.f); + gl.TexCoord2f(1, 1); + gl.Vertex3f(128.f, -128.f, -128.f); + gl.TexCoord2f(0, 1); + gl.Vertex3f(128.f, -128.f, 128.f); + gl.End(); + } + else + { + faces=1; + // all 4 sides + tex = FMaterial::ValidateTexture(sb->faces[0]); + tex->Bind(CM_Index, GLT_CLAMPX|GLT_CLAMPY, 0); + + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_FAN); + gl.TexCoord2f(0, 0); + gl.Vertex3f(128.f, 128.f, -128.f); + gl.TexCoord2f(.25f, 0); + gl.Vertex3f(-128.f, 128.f, -128.f); + gl.TexCoord2f(.25f, 1); + gl.Vertex3f(-128.f, -128.f, -128.f); + gl.TexCoord2f(0, 1); + gl.Vertex3f(128.f, -128.f, -128.f); + gl.End(); + + // east + gl.Begin(GL_TRIANGLE_FAN); + gl.TexCoord2f(.25f, 0); + gl.Vertex3f(-128.f, 128.f, -128.f); + gl.TexCoord2f(.5f, 0); + gl.Vertex3f(-128.f, 128.f, 128.f); + gl.TexCoord2f(.5f, 1); + gl.Vertex3f(-128.f, -128.f, 128.f); + gl.TexCoord2f(.25f, 1); + gl.Vertex3f(-128.f, -128.f, -128.f); + gl.End(); + + // south + gl.Begin(GL_TRIANGLE_FAN); + gl.TexCoord2f(.5f, 0); + gl.Vertex3f(-128.f, 128.f, 128.f); + gl.TexCoord2f(.75f, 0); + gl.Vertex3f(128.f, 128.f, 128.f); + gl.TexCoord2f(.75f, 1); + gl.Vertex3f(128.f, -128.f, 128.f); + gl.TexCoord2f(.5f, 1); + gl.Vertex3f(-128.f, -128.f, 128.f); + gl.End(); + + // west + gl.Begin(GL_TRIANGLE_FAN); + gl.TexCoord2f(.75f, 0); + gl.Vertex3f(128.f, 128.f, 128.f); + gl.TexCoord2f(1, 0); + gl.Vertex3f(128.f, 128.f, -128.f); + gl.TexCoord2f(1, 1); + gl.Vertex3f(128.f, -128.f, -128.f); + gl.TexCoord2f(.75f, 1); + gl.Vertex3f(128.f, -128.f, 128.f); + gl.End(); + } + + // top + tex = FMaterial::ValidateTexture(sb->faces[faces]); + tex->Bind(CM_Index, GLT_CLAMPX|GLT_CLAMPY, 0); + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_FAN); + if (!sb->fliptop) + { + gl.TexCoord2f(0, 0); + gl.Vertex3f(128.f, 128.f, -128.f); + gl.TexCoord2f(1, 0); + gl.Vertex3f(-128.f, 128.f, -128.f); + gl.TexCoord2f(1, 1); + gl.Vertex3f(-128.f, 128.f, 128.f); + gl.TexCoord2f(0, 1); + gl.Vertex3f(128.f, 128.f, 128.f); + } + else + { + gl.TexCoord2f(0, 0); + gl.Vertex3f(128.f, 128.f, 128.f); + gl.TexCoord2f(1, 0); + gl.Vertex3f(-128.f, 128.f, 128.f); + gl.TexCoord2f(1, 1); + gl.Vertex3f(-128.f, 128.f, -128.f); + gl.TexCoord2f(0, 1); + gl.Vertex3f(128.f, 128.f, -128.f); + } + gl.End(); + + + // bottom + tex = FMaterial::ValidateTexture(sb->faces[faces+1]); + tex->Bind(CM_Index, GLT_CLAMPX|GLT_CLAMPY, 0); + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_FAN); + gl.TexCoord2f(0, 0); + gl.Vertex3f(128.f, -128.f, -128.f); + gl.TexCoord2f(1, 0); + gl.Vertex3f(-128.f, -128.f, -128.f); + gl.TexCoord2f(1, 1); + gl.Vertex3f(-128.f, -128.f, 128.f); + gl.TexCoord2f(0, 1); + gl.Vertex3f(128.f, -128.f, 128.f); + gl.End(); + + +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- +void GLSkyPortal::DrawContents() +{ + bool drawBoth = false; + int CM_Index; + 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) + { + CM_Index=gl_fixedcolormapfadecolor; + } + + gl_RenderState.EnableFog(false); + gl_RenderState.EnableAlphaTest(false); + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + gl.MatrixMode(GL_MODELVIEW); + gl.PushMatrix(); + GLRenderer->SetupView(0, 0, 0, viewangle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1)); + + if (origin->texture[0] && origin->texture[0]->tex->gl_info.bSkybox) + { + if (gl_fixedcolormap != CM_DEFAULT) + { + float rr,gg,bb; + + gl_GetLightColor(255, 0, NULL, &rr, &gg, &bb); + R=rr; + G=gg; + B=bb; + } + else R=G=B=1.f; + + RenderBox(origin->skytexno1, origin->texture[0], origin->x_offset[0], CM_Index, origin->sky2); + gl_RenderState.EnableAlphaTest(true); + } + else + { + if (origin->texture[0]==origin->texture[1] && origin->doublesky) origin->doublesky=false; + + if (origin->texture[0]) + { + gl_RenderState.SetTextureMode(TM_OPAQUE); + RenderDome(origin->skytexno1, origin->texture[0], origin->x_offset[0], origin->y_offset, origin->mirrored, CM_Index); + gl_RenderState.SetTextureMode(TM_MODULATE); + } + + gl_RenderState.EnableAlphaTest(true); + gl_RenderState.AlphaFunc(GL_GEQUAL,0.05f); + + if (origin->doublesky && origin->texture[1]) + { + secondlayer=true; + RenderDome(FNullTextureID(), origin->texture[1], origin->x_offset[1], origin->y_offset, false, CM_Index); + secondlayer=false; + } + + if (skyfog>0 && (FadeColor.r ||FadeColor.g || FadeColor.b)) + { + gl_RenderState.EnableTexture(false); + foglayer=true; + gl.Color4f(FadeColor.r/255.0f,FadeColor.g/255.0f,FadeColor.b/255.0f,skyfog/255.0f); + RenderDome(FNullTextureID(), NULL, 0, 0, false, CM_DEFAULT); + gl_RenderState.EnableTexture(true); + foglayer=false; + } + } + gl.PopMatrix(); + glset.lightmode = oldlightmode; +} + diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp new file mode 100644 index 000000000..2fd250f93 --- /dev/null +++ b/src/gl/scene/gl_sprite.cpp @@ -0,0 +1,991 @@ +/* +** gl_sprite.cpp +** Sprite/Particle rendering +** +**--------------------------------------------------------------------------- +** Copyright 2002-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "p_local.h" +#include "p_effect.h" +#include "g_level.h" +#include "doomstat.h" +#include "gl/gl_functions.h" +#include "r_defs.h" +#include "r_sky.h" +#include "r_utility.h" + +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/data/gl_data.h" +#include "gl/dynlights/gl_glow.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_portal.h" +#include "gl/models/gl_models.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_clock.h" + +CVAR(Bool, gl_usecolorblending, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Bool, gl_spritebrightfog, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); +CVAR(Bool, gl_sprite_blend, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); +CVAR(Int, gl_spriteclip, 1, CVAR_ARCHIVE) +CVAR(Float, gl_sclipthreshold, 10.0, CVAR_ARCHIVE) +CVAR(Float, gl_sclipfactor, 1.8, CVAR_ARCHIVE) +CVAR(Int, gl_particles_style, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // 0 = square, 1 = round, 2 = smooth +CVAR(Int, gl_billboard_mode, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Bool, gl_billboard_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, gl_enhanced_nv_stealth, 3, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Int, gl_fuzztype, 0, CVAR_ARCHIVE) +{ + if (self < 0 || self > 7) self = 0; +} + +extern bool r_showviewer; +EXTERN_CVAR (Float, transsouls) + +extern TArray sprites; +extern TArray SpriteFrames; +extern TArray BloodTranslationColors; + +enum HWRenderStyle +{ + STYLEHW_Normal, // default + STYLEHW_Solid, // drawn solid (needs special treatment for sprites) + STYLEHW_NoAlphaTest, // disable alpha test +}; + + +void gl_SetRenderStyle(FRenderStyle style, bool drawopaque, bool allowcolorblending) +{ + int tm, sb, db, be; + + gl_GetRenderStyle(style, drawopaque, allowcolorblending, &tm, &sb, &db, &be); + gl_RenderState.BlendEquation(be); + gl_RenderState.BlendFunc(sb, db); + gl_RenderState.SetTextureMode(tm); +} + +//========================================================================== +// +// +// +//========================================================================== +void GLSprite::Draw(int pass) +{ + if (pass!=GLPASS_PLAIN && pass != GLPASS_ALL && pass!=GLPASS_TRANSLUCENT) return; + + // Hack to enable bright sprites in faded maps + uint32 backupfade = Colormap.FadeColor.d; + if (gl_spritebrightfog && fullbright) + Colormap.FadeColor = 0; + + + bool additivefog = false; + int rel = getExtraLight(); + + if (pass==GLPASS_TRANSLUCENT) + { + // The translucent pass requires special setup for the various modes. + + // Brightmaps will only be used when doing regular drawing ops and having no fog + if (!gl_spritebrightfog && (!gl_isBlack(Colormap.FadeColor) || level.flags&LEVEL_HASFADETABLE || + RenderStyle.BlendOp != STYLEOP_Add)) + { + gl_RenderState.EnableBrightmap(false); + } + + gl_SetRenderStyle(RenderStyle, false, + // The rest of the needed checks are done inside gl_SetRenderStyle + trans > 1.f - FLT_EPSILON && gl_usecolorblending && gl_fixedcolormap < CM_FIRSTSPECIALCOLORMAP && actor && + fullbright && gltexture && !gltexture->GetTransparent()); + + if (hw_styleflags == STYLEHW_NoAlphaTest) + { + gl_RenderState.EnableAlphaTest(false); + } + else + { + gl_RenderState.AlphaFunc(GL_GEQUAL,trans*gl_mask_sprite_threshold); + } + + if (RenderStyle.BlendOp == STYLEOP_Shadow) + { + float fuzzalpha=0.44f; + float minalpha=0.1f; + + // fog + fuzz don't work well without some fiddling with the alpha value! + if (!gl_isBlack(Colormap.FadeColor)) + { + float xcamera=FIXED2FLOAT(viewx); + float ycamera=FIXED2FLOAT(viewy); + + float dist=Dist2(xcamera,ycamera, x,y); + + if (!Colormap.FadeColor.a) Colormap.FadeColor.a=clamp(255-lightlevel,60,255); + + // this value was determined by trial and error and is scale dependent! + float factor=0.05f+exp(-Colormap.FadeColor.a*dist/62500.f); + fuzzalpha*=factor; + minalpha*=factor; + } + + gl_RenderState.AlphaFunc(GL_GEQUAL,minalpha*gl_mask_sprite_threshold); + gl.Color4f(0.2f,0.2f,0.2f,fuzzalpha); + additivefog = true; + } + else if (RenderStyle.BlendOp == STYLEOP_Add && RenderStyle.DestAlpha == STYLEALPHA_One) + { + additivefog = true; + } + } + if (RenderStyle.BlendOp!=STYLEOP_Shadow) + { + if (actor) + { + lightlevel = gl_SetSpriteLighting(RenderStyle, actor, lightlevel, rel, &Colormap, ThingColor, trans, + fullbright || gl_fixedcolormap >= CM_FIRSTSPECIALCOLORMAP, false); + } + else if (particle) + { + if (gl_light_particles) + { + lightlevel = gl_SetSpriteLight(particle, lightlevel, rel, &Colormap, trans, ThingColor); + } + else + { + gl_SetColor(lightlevel, rel, &Colormap, trans, ThingColor); + } + } + else return; + } + + if (gl_isBlack(Colormap.FadeColor)) foglevel=lightlevel; + + if (RenderStyle.Flags & STYLEF_FadeToBlack) + { + Colormap.FadeColor=0; + additivefog = true; + } + + if (RenderStyle.Flags & STYLEF_InvertOverlay) + { + Colormap.FadeColor = Colormap.FadeColor.InverseColor(); + additivefog=false; + } + + gl_SetFog(foglevel, rel, &Colormap, additivefog); + + if (gltexture) gltexture->BindPatch(Colormap.colormap, translation, OverrideShader); + else if (!modelframe) gl_RenderState.EnableTexture(false); + + if (!modelframe) + { + // [BB] Billboard stuff + const bool drawWithXYBillboard = ( (particle && gl_billboard_particles) || (!(actor && actor->renderflags & RF_FORCEYBILLBOARD) + //&& GLRenderer->mViewActor != NULL + && (gl_billboard_mode == 1 || (actor && actor->renderflags & RF_FORCEXYBILLBOARD ))) ); + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_STRIP); + if ( drawWithXYBillboard ) + { + // Rotate the sprite about the vector starting at the center of the sprite + // triangle strip and with direction orthogonal to where the player is looking + // in the x/y plane. + float xcenter = (x1+x2)*0.5; + float ycenter = (y1+y2)*0.5; + float zcenter = (z1+z2)*0.5; + float angleRad = DEG2RAD(270. - float(GLRenderer->mAngles.Yaw)); + + Matrix3x4 mat; + mat.MakeIdentity(); + mat.Translate( xcenter, zcenter, ycenter); + mat.Rotate(-sin(angleRad), 0, cos(angleRad), -GLRenderer->mAngles.Pitch); + mat.Translate( -xcenter, -zcenter, -ycenter); + Vector v1 = mat * Vector(x1,z1,y1); + Vector v2 = mat * Vector(x2,z1,y2); + Vector v3 = mat * Vector(x1,z2,y1); + Vector v4 = mat * Vector(x2,z2,y2); + + if (gltexture) + { + gl.TexCoord2f(ul, vt); gl.Vertex3fv(&v1[0]); + gl.TexCoord2f(ur, vt); gl.Vertex3fv(&v2[0]); + gl.TexCoord2f(ul, vb); gl.Vertex3fv(&v3[0]); + gl.TexCoord2f(ur, vb); gl.Vertex3fv(&v4[0]); + } + else // Particle + { + gl.Vertex3fv(&v1[0]); + gl.Vertex3fv(&v2[0]); + gl.Vertex3fv(&v3[0]); + gl.Vertex3fv(&v4[0]); + } + + } + else + { + if (gltexture) + { + gl.TexCoord2f(ul, vt); gl.Vertex3f(x1, z1, y1); + gl.TexCoord2f(ur, vt); gl.Vertex3f(x2, z1, y2); + gl.TexCoord2f(ul, vb); gl.Vertex3f(x1, z2, y1); + gl.TexCoord2f(ur, vb); gl.Vertex3f(x2, z2, y2); + } + else // Particle + { + gl.Vertex3f(x1, z1, y1); + gl.Vertex3f(x2, z1, y2); + gl.Vertex3f(x1, z2, y1); + gl.Vertex3f(x2, z2, y2); + } + } + gl.End(); + } + else + { + gl_RenderModel(this, Colormap.colormap); + } + + if (pass==GLPASS_TRANSLUCENT) + { + gl_RenderState.EnableBrightmap(true); + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_RenderState.BlendEquation(GL_FUNC_ADD); + gl_RenderState.SetTextureMode(TM_MODULATE); + + // [BB] Restore the alpha test after drawing a smooth particle. + if (hw_styleflags == STYLEHW_NoAlphaTest) + { + gl_RenderState.EnableAlphaTest(true); + } + else + { + gl_RenderState.AlphaFunc(GL_GEQUAL,gl_mask_sprite_threshold); + } + } + + // End of gl_sprite_brightfog hack: restore FadeColor to normalcy + if (backupfade != Colormap.FadeColor.d) + Colormap.FadeColor = backupfade; + + gl_RenderState.EnableTexture(true); + gl_RenderState.SetDynLight(0,0,0); +} + + +//========================================================================== +// +// +// +//========================================================================== +inline void GLSprite::PutSprite(bool translucent) +{ + int list; + // [BB] Allow models to be drawn in the GLDL_TRANSLUCENT pass. + if (translucent || !modelframe) + { + list = GLDL_TRANSLUCENT; + } + else if ((!gl_isBlack (Colormap.FadeColor) || level.flags&LEVEL_HASFADETABLE)) + { + list = GLDL_FOGMASKED; + } + else + { + list = GLDL_MASKED; + } + gl_drawinfo->drawlists[list].AddSprite(this); +} + +//========================================================================== +// +// +// +//========================================================================== +void GLSprite::SplitSprite(sector_t * frontsector, bool translucent) +{ + GLSprite copySprite; + fixed_t lightbottom; + float maplightbottom; + unsigned int i; + bool put=false; + TArray & lightlist=frontsector->e->XFloor.lightlist; + + //y1+=y; + //y2+=y; + //y=0; + for(i=0;ix,actor->y); + else lightbottom=frontsector->floorplane.ZatPoint(actor->x,actor->y); + + //maplighttop=FIXED2FLOAT(lightlist[i].height); + maplightbottom=FIXED2FLOAT(lightbottom); + if (maplightbottom>8; + copySprite.Colormap.LightColor.g=(copySprite.Colormap.LightColor.g*ThingColor.g)>>8; + copySprite.Colormap.LightColor.b=(copySprite.Colormap.LightColor.b*ThingColor.b)>>8; + } + + z1=copySprite.z2=maplightbottom; + vt=copySprite.vb=copySprite.vt+ + (maplightbottom-copySprite.z1)*(copySprite.vb-copySprite.vt)/(z2-copySprite.z1); + copySprite.PutSprite(translucent); + put=true; + } + } + //if (y1 & lightlist=actor->Sector->e->XFloor.lightlist; + + for(i=0;ix,actor->y); + else lightbottom=sector->floorplane.ZatPoint(actor->x,actor->y); + + //maplighttop=FIXED2FLOAT(lightlist[i].height); + maplightbottom=FIXED2FLOAT(lightbottom); + if (maplightbottom>8; + Colormap.LightColor.g=(Colormap.LightColor.g*ThingColor.g)>>8; + Colormap.LightColor.b=(Colormap.LightColor.b*ThingColor.b)>>8; + } + return; + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void GLSprite::Process(AActor* thing,sector_t * sector) +{ + sector_t rs; + sector_t * rendersector; + // don't draw the thing that's used as camera (for viewshifts during quakes!) + if (thing==GLRenderer->mViewActor) return; + + // Don't waste time projecting sprites that are definitely not visible. + if (thing == NULL || thing->sprite == 0 || !thing->IsVisibleToPlayer()) + { + return; + } + + int spritenum = thing->sprite; + fixed_t spritescaleX = thing->scaleX; + fixed_t spritescaleY = thing->scaleY; + if (thing->player != NULL) + { + P_CheckPlayerSprite(thing, spritenum, spritescaleX, spritescaleY); + } + + if (thing->renderflags & RF_INVISIBLE || !thing->RenderStyle.IsVisible(thing->alpha)) + { + if (!(thing->flags & MF_STEALTH) || !gl_fixedcolormap || !gl_enhanced_nightvision) + return; + } + + // If this thing is in a map section that's not in view it can't possibly be visible + if (!(currentmapsection[thing->subsector->mapsection>>3] & (1 << (thing->subsector->mapsection & 7)))) return; + + // [RH] Interpolate the sprite's position to make it look smooth + fixed_t thingx = thing->PrevX + FixedMul (r_TicFrac, thing->x - thing->PrevX); + fixed_t thingy = thing->PrevY + FixedMul (r_TicFrac, thing->y - thing->PrevY); + fixed_t thingz = thing->PrevZ + FixedMul (r_TicFrac, thing->z - thing->PrevZ); + + // Too close to the camera. This doesn't look good if it is a sprite. + if (P_AproxDistance(thingx-viewx, thingy-viewy)<2*FRACUNIT) + { + // exclude vertically moving objects from this check. + if (!(thing->velx==0 && thing->vely==0 && thing->velz!=0)) + { + if (!gl_FindModelFrame(RUNTIME_TYPE(thing), spritenum, thing->frame, false)) + { + return; + } + } + } + + // don't draw first frame of a player missile + if (thing->flags&MF_MISSILE && thing->target==GLRenderer->mViewActor && GLRenderer->mViewActor != NULL) + { + if (P_AproxDistance(thingx-viewx, thingy-viewy) < thing->Speed ) return; + } + + if (GLRenderer->mCurrentPortal) + { + int clipres = GLRenderer->mCurrentPortal->ClipPoint(thingx, thingy); + if (clipres == GLPortal::PClip_InFront) return; + } + + player_t *player=&players[consoleplayer]; + FloatRect r; + + if (sector->sectornum!=thing->Sector->sectornum) + { + rendersector=gl_FakeFlat(thing->Sector, &rs, false); + } + else + { + rendersector=sector; + } + + + x = FIXED2FLOAT(thingx); + z = FIXED2FLOAT(thingz-thing->floorclip); + y = FIXED2FLOAT(thingy); + + // [RH] Make floatbobbing a renderer-only effect. + if (thing->flags2 & MF2_FLOATBOB) + { + float fz = FIXED2FLOAT(thing->GetBobOffset(r_TicFrac)); + z += fz; + } + + modelframe = gl_FindModelFrame(RUNTIME_TYPE(thing), spritenum, thing->frame, !!(thing->flags & MF_DROPPED)); + if (!modelframe) + { + angle_t ang = R_PointToAngle(thingx, thingy); + + bool mirror; + FTextureID patch = gl_GetSpriteFrame(spritenum, thing->frame, -1, ang - thing->angle, &mirror); + if (!patch.isValid()) return; + gltexture=FMaterial::ValidateTexture(patch, false); + if (!gltexture) return; + + if (gl.flags & RFL_NPOT_TEXTURE) // trimming only works if non-power-of-2 textures are supported + { + vt = gltexture->GetSpriteVT(); + vb = gltexture->GetSpriteVB(); + gltexture->GetRect(&r, GLUSE_SPRITE); + if (mirror) + { + r.left=-r.width-r.left; // mirror the sprite's x-offset + ul = gltexture->GetSpriteUL(); + ur = gltexture->GetSpriteUR(); + } + else + { + ul = gltexture->GetSpriteUR(); + ur = gltexture->GetSpriteUL(); + } + } + else + { + vt = gltexture->GetVT(); + vb = gltexture->GetVB(); + gltexture->GetRect(&r, GLUSE_PATCH); + if (mirror) + { + r.left=-r.width-r.left; // mirror the sprite's x-offset + ul = gltexture->GetUL(); + ur = gltexture->GetUR(); + } + else + { + ul = gltexture->GetUR(); + ur = gltexture->GetUL(); + } + } + + r.Scale(FIXED2FLOAT(spritescaleX),FIXED2FLOAT(spritescaleY)); + + float rightfac=-r.left; + float leftfac=rightfac-r.width; + + z1=z-r.top; + z2=z1-r.height; + + float spriteheight = FIXED2FLOAT(spritescaleY) * gltexture->GetScaledHeightFloat(GLUSE_SPRITE); + + // Tests show that this doesn't look good for many decorations and corpses + if (spriteheight>0 && gl_spriteclip>0) + { + bool smarterclip = false; // Set to true if one condition triggers the test below + if (((thing->player || thing->flags3&MF3_ISMONSTER || + thing->IsKindOf(RUNTIME_CLASS(AInventory))) && (thing->flags&MF_ICECORPSE || + !(thing->flags&MF_CORPSE))) || (gl_spriteclip==3 && (smarterclip = true)) || gl_spriteclip > 1) + { + float btm= 1000000.0f; + float top=-1000000.0f; + extsector_t::xfloor &x = thing->Sector->e->XFloor; + + if (x.ffloors.Size()) + { + for(unsigned int i=0;itop.plane->ZatPoint(thingx, thingy); + fixed_t ceilingh=ff->bottom.plane->ZatPoint(thingx, thingy); + if (floorh==thing->floorz) + { + btm=FIXED2FLOAT(floorh); + } + if (ceilingh==thing->ceilingz) + { + top=FIXED2FLOAT(ceilingh); + } + if (btm != 1000000.0f && top != -1000000.0f) + { + break; + } + } + } + else if (thing->Sector->heightsec && !(thing->Sector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) + { + if (thing->flags2&MF2_ONMOBJ && thing->floorz== + thing->Sector->heightsec->floorplane.ZatPoint(thingx, thingy)) + { + btm=FIXED2FLOAT(thing->floorz); + top=FIXED2FLOAT(thing->ceilingz); + } + } + if (btm==1000000.0f) + btm= FIXED2FLOAT(thing->Sector->floorplane.ZatPoint(thingx, thingy)-thing->floorclip); + if (top==-1000000.0f) + top= FIXED2FLOAT(thing->Sector->ceilingplane.ZatPoint(thingx, thingy)); + + float diffb = z2 - btm; + float difft = z1 - top; + if (diffb >= 0 /*|| !gl_sprite_clip_to_floor*/) diffb = 0; + // Adjust sprites clipping into ceiling and adjust clipping adjustment for tall graphics + if (smarterclip) + { + // Reduce slightly clipping adjustment of corpses + if (thing->flags & MF_CORPSE || spriteheight > abs(diffb)) + { + float ratio = clamp((abs(diffb) * (float)gl_sclipfactor/(spriteheight+1)), 0.5, 1.0); + diffb*=ratio; + } + if (!diffb) + { + if (difft <= 0) difft = 0; + if (difft >= (float)gl_sclipthreshold) + { + // dumb copy of the above. + if (!(thing->flags3&MF3_ISMONSTER) || (thing->flags&MF_NOGRAVITY) || (thing->flags&MF_CORPSE) || difft > (float)gl_sclipthreshold) + { + difft=0; + } + } + if (spriteheight > abs(difft)) + { + float ratio = clamp((abs(difft) * (float)gl_sclipfactor/(spriteheight+1)), 0.5, 1.0); + difft*=ratio; + } + z2-=difft; + z1-=difft; + } + } + if (diffb <= (0 - (float)gl_sclipthreshold)) // such a large displacement can't be correct! + { + // for living monsters standing on the floor allow a little more. + if (!(thing->flags3&MF3_ISMONSTER) || (thing->flags&MF_NOGRAVITY) || (thing->flags&MF_CORPSE) || diffb<(-1.8*(float)gl_sclipthreshold)) + { + diffb=0; + } + } + z2-=diffb; + z1-=diffb; + } + } + float viewvecX = GLRenderer->mViewVector.X; + float viewvecY = GLRenderer->mViewVector.Y; + + x1=x-viewvecY*leftfac; + x2=x-viewvecY*rightfac; + y1=y+viewvecX*leftfac; + y2=y+viewvecX*rightfac; + } + else + { + x1 = x2 = x; + y1 = y2 = y; + z1 = z2 = z; + gltexture=NULL; + } + + depth = DMulScale20 (thing->x-viewx, viewtancos, thing->y-viewy, viewtansin); + + // light calculation + + bool enhancedvision=false; + + // allow disabling of the fullbright flag by a brightmap definition + // (e.g. to do the gun flashes of Doom's zombies correctly. + fullbright = (thing->flags5 & MF5_BRIGHT) || + ((thing->renderflags & RF_FULLBRIGHT) && (!gl_BrightmapsActive() || !gltexture || !gltexture->tex->gl_info.bBrightmapDisablesFullbright)); + + lightlevel=fullbright? 255 : + gl_ClampLight(rendersector->GetTexture(sector_t::ceiling) == skyflatnum ? + rendersector->GetCeilingLight() : rendersector->GetFloorLight()); + foglevel = (BYTE)clamp(rendersector->lightlevel, 0, 255); + + lightlevel = (byte)gl_CheckSpriteGlow(rendersector, lightlevel, thingx, thingy, thingz); + + // colormap stuff is a little more complicated here... + if (gl_fixedcolormap) + { + if ((gl_enhanced_nv_stealth > 0 && gl_fixedcolormap == CM_LITE) // Infrared powerup only + || (gl_enhanced_nv_stealth == 2 && gl_fixedcolormap >= CM_TORCH)// Also torches + || (gl_enhanced_nv_stealth == 3)) // Any fixed colormap + enhancedvision=true; + + Colormap.GetFixedColormap(); + + if (gl_fixedcolormap==CM_LITE) + { + if (gl_enhanced_nightvision && + (thing->IsKindOf(RUNTIME_CLASS(AInventory)) || thing->flags3&MF3_ISMONSTER || thing->flags&MF_MISSILE || thing->flags&MF_CORPSE)) + { + Colormap.colormap = CM_FIRSTSPECIALCOLORMAP + INVERSECOLORMAP; + } + } + } + else + { + Colormap=rendersector->ColorMap; + if (fullbright) + { + if (rendersector == §ors[rendersector->sectornum] || in_area != area_below) + // under water areas keep their color for fullbright objects + { + // Only make the light white but keep everything else (fog, desaturation and Boom colormap.) + Colormap.LightColor.r= + Colormap.LightColor.g= + Colormap.LightColor.b=0xff; + } + else + { + Colormap.LightColor.r = (3*Colormap.LightColor.r + 0xff)/4; + Colormap.LightColor.g = (3*Colormap.LightColor.g + 0xff)/4; + Colormap.LightColor.b = (3*Colormap.LightColor.b + 0xff)/4; + } + } + else if (glset.nocoloredspritelighting) + { + int v = (Colormap.LightColor.r /* * 77 */ + Colormap.LightColor.g /**143 */ + Colormap.LightColor.b /**35*/)/3;//255; + Colormap.LightColor.r= + Colormap.LightColor.g= + Colormap.LightColor.b=(255+v+v)/3; + } + } + + translation=thing->Translation; + + ThingColor=0xffffff; + RenderStyle = thing->RenderStyle; + OverrideShader = 0; + trans = FIXED2FLOAT(thing->alpha); + hw_styleflags = STYLEHW_Normal; + + if (RenderStyle.BlendOp >= STYLEOP_Fuzz && RenderStyle.BlendOp <= STYLEOP_FuzzOrRevSub) + { + RenderStyle.CheckFuzz(); + if (RenderStyle.BlendOp == STYLEOP_Fuzz) + { + if (gl.shadermodel >= 4 && gl_fuzztype != 0) + { + // Todo: implement shader selection here + RenderStyle = LegacyRenderStyles[STYLE_Translucent]; + OverrideShader = gl_fuzztype + 4; + trans = 0.99f; // trans may not be 1 here + hw_styleflags |= STYLEHW_NoAlphaTest; + } + else + { + RenderStyle.BlendOp = STYLEOP_Shadow; + } + } + } + + if (RenderStyle.Flags & STYLEF_TransSoulsAlpha) + { + trans = transsouls; + } + else if (RenderStyle.Flags & STYLEF_Alpha1) + { + trans = 1.f; + } + + if (trans >= 1.f-FLT_EPSILON && RenderStyle.BlendOp != STYLEOP_Shadow && ( + (RenderStyle.SrcAlpha == STYLEALPHA_One && RenderStyle.DestAlpha == STYLEALPHA_Zero) || + (RenderStyle.SrcAlpha == STYLEALPHA_Src && RenderStyle.DestAlpha == STYLEALPHA_InvSrc) + )) + { + // This is a non-translucent sprite (i.e. STYLE_Normal or equivalent) + trans=1.f; + + + if (!gl_sprite_blend || modelframe) + { + RenderStyle.SrcAlpha = STYLEALPHA_One; + RenderStyle.DestAlpha = STYLEALPHA_Zero; + hw_styleflags = STYLEHW_Solid; + } + else + { + RenderStyle.SrcAlpha = STYLEALPHA_Src; + RenderStyle.DestAlpha = STYLEALPHA_InvSrc; + } + + + } + if ((gltexture && gltexture->GetTransparent()) || (RenderStyle.Flags & STYLEF_RedIsAlpha)) + { + if (hw_styleflags == STYLEHW_Solid) + { + RenderStyle.SrcAlpha = STYLEALPHA_Src; + RenderStyle.DestAlpha = STYLEALPHA_InvSrc; + } + hw_styleflags = STYLEHW_NoAlphaTest; + } + + if (enhancedvision && gl_enhanced_nightvision) + { + if (RenderStyle.BlendOp == STYLEOP_Shadow) + { + // enhanced vision makes them more visible! + trans=0.5f; + RenderStyle = STYLE_Translucent; + } + else if (thing->flags & MF_STEALTH) + { + // enhanced vision overcomes stealth! + if (trans < 0.5f) trans = 0.5f; + } + } + + if (trans==0.0f) return; + + // end of light calculation + + actor=thing; + index = GLRenderer->gl_spriteindex++; + particle=NULL; + + const bool drawWithXYBillboard = ( !(actor->renderflags & RF_FORCEYBILLBOARD) + && players[consoleplayer].camera + && (gl_billboard_mode == 1 || actor->renderflags & RF_FORCEXYBILLBOARD ) ); + + + if (drawWithXYBillboard || modelframe) + { + if (!gl_fixedcolormap && !fullbright) SetSpriteColor(actor->Sector, actor->y + (actor->height>>1)); + PutSprite(hw_styleflags != STYLEHW_Solid); + } + else if (thing->Sector->e->XFloor.lightlist.Size()==0 || gl_fixedcolormap || fullbright) + { + PutSprite(hw_styleflags != STYLEHW_Solid); + } + else + { + SplitSprite(thing->Sector, hw_styleflags != STYLEHW_Solid); + } + rendered_sprites++; +} + + +//========================================================================== +// +// +// +//========================================================================== + +void GLSprite::ProcessParticle (particle_t *particle, sector_t *sector)//, int shade, int fakeside) +{ + if (GLRenderer->mCurrentPortal) + { + int clipres = GLRenderer->mCurrentPortal->ClipPoint(particle->x, particle->y); + if (clipres == GLPortal::PClip_InFront) return; + } + + player_t *player=&players[consoleplayer]; + + if (particle->trans==0) return; + + lightlevel = gl_ClampLight(sector->GetTexture(sector_t::ceiling) == skyflatnum ? + sector->GetCeilingLight() : sector->GetFloorLight()); + foglevel = sector->lightlevel; + + if (gl_fixedcolormap) + { + Colormap.GetFixedColormap(); + } + else if (!particle->bright) + { + TArray & lightlist=sector->e->XFloor.lightlist; + int lightbottom; + + Colormap = sector->ColorMap; + for(unsigned int i=0;ix,particle->y); + else lightbottom = sector->floorplane.ZatPoint(particle->x,particle->y); + + if (lightbottom < particle->y) + { + lightlevel = *lightlist[i].p_lightlevel; + Colormap.LightColor = (lightlist[i].extra_colormap)->Color; + break; + } + } + } + else + { + lightlevel = 255; + Colormap = sector->ColorMap; + Colormap.ClearColor(); + } + + trans=particle->trans/255.0f; + RenderStyle = STYLE_Translucent; + OverrideShader = 0; + + ThingColor = particle->color; //GPalette.BaseColors[particle->color]; + ThingColor.a=0; + + modelframe=NULL; + gltexture=NULL; + + // [BB] Load the texture for round or smooth particles + if (gl_particles_style) + { + FTexture *lump = NULL; + if (gl_particles_style == 1) + { + lump = GLRenderer->glpart2; + } + else if (gl_particles_style == 2) + { + lump = GLRenderer->glpart; + } + + if (lump != NULL) + { + gltexture=FMaterial::ValidateTexture(lump); + translation = 0; + + ul = gltexture->GetUL(); + ur = gltexture->GetUR(); + vt = gltexture->GetVT(); + vb = gltexture->GetVB(); + FloatRect r; + gltexture->GetRect(&r, GLUSE_PATCH); + } + } + + x= FIXED2FLOAT(particle->x); + y= FIXED2FLOAT(particle->y); + z= FIXED2FLOAT(particle->z); + + float scalefac=particle->size/4.0f; + // [BB] The smooth particles are smaller than the other ones. Compensate for this here. + if (gl_particles_style==2) + scalefac *= 1.7; + + float viewvecX = GLRenderer->mViewVector.X; + float viewvecY = GLRenderer->mViewVector.Y; + + x1=x+viewvecY*scalefac; + x2=x-viewvecY*scalefac; + y1=y-viewvecX*scalefac; + y2=y+viewvecX*scalefac; + z1=z-scalefac; + z2=z+scalefac; + + depth = DMulScale20 (particle->x-viewx, viewtancos, particle->y-viewy, viewtansin); + + actor=NULL; + this->particle=particle; + + // [BB] Translucent particles have to be rendered without the alpha test. + if (gl_particles_style != 2 && trans>=1.0f-FLT_EPSILON) hw_styleflags = STYLEHW_Solid; + else hw_styleflags = STYLEHW_NoAlphaTest; + + PutSprite(hw_styleflags != STYLEHW_Solid); + rendered_sprites++; +} + + + diff --git a/src/gl/scene/gl_spritelight.cpp b/src/gl/scene/gl_spritelight.cpp new file mode 100644 index 000000000..56ab7030c --- /dev/null +++ b/src/gl/scene/gl_spritelight.cpp @@ -0,0 +1,300 @@ +/* +** gl_light.cpp +** Light level / fog management / dynamic lights +** +**--------------------------------------------------------------------------- +** Copyright 2002-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "c_dispatch.h" +#include "p_local.h" +#include "p_effect.h" +#include "vectors.h" +#include "gl/gl_functions.h" +#include "g_level.h" + +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/data/gl_data.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_portal.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_material.h" + + +//========================================================================== +// +// Gets the light for a sprite - takes dynamic lights into account +// +//========================================================================== + +bool gl_GetSpriteLight(AActor *self, fixed_t x, fixed_t y, fixed_t z, subsector_t * subsec, int desaturation, float * out, line_t *line, int side) +{ + ADynamicLight *light; + float frac, lr, lg, lb; + float radius; + bool changed = false; + + out[0]=out[1]=out[2]=0; + + for(int j=0;j<2;j++) + { + // Go through both light lists + FLightNode * node = subsec->lighthead[j]; + while (node) + { + light=node->lightsource; + if (!(light->flags2&MF2_DORMANT) && + (!(light->flags4&MF4_DONTLIGHTSELF) || light->target != self)) + { + float dist = FVector3( FIXED2FLOAT(x - light->x), FIXED2FLOAT(y - light->y), FIXED2FLOAT(z - light->z) ).Length(); + radius = light->GetRadius() * gl_lights_size; + + if (dist < radius) + { + frac = 1.0f - (dist / radius); + + if (frac > 0) + { + if (line != NULL) + { + if (P_PointOnLineSide(light->x, light->y, line) != side) + { + node = node->nextLight; + continue; + } + } + lr = light->GetRed() / 255.0f * gl_lights_intensity; + lg = light->GetGreen() / 255.0f * gl_lights_intensity; + lb = light->GetBlue() / 255.0f * gl_lights_intensity; + if (light->IsSubtractive()) + { + float bright = FVector3(lr, lg, lb).Length(); + FVector3 lightColor(lr, lg, lb); + lr = (bright - lr) * -1; + lg = (bright - lg) * -1; + lb = (bright - lb) * -1; + } + + out[0] += lr * frac; + out[1] += lg * frac; + out[2] += lb * frac; + changed = true; + } + } + } + node = node->nextLight; + } + } + + // Desaturate dynamic lighting if applicable + if (desaturation>0 && desaturation<=CM_DESAT31) + { + float gray=(out[0]*77 + out[1]*143 + out[2]*37)/257; + + out[0]= (out[0]*(31-desaturation)+ gray*desaturation)/31; + out[1]= (out[1]*(31-desaturation)+ gray*desaturation)/31; + out[2]= (out[2]*(31-desaturation)+ gray*desaturation)/31; + } + return changed; +} + + + +//========================================================================== +// +// Sets the light for a sprite - takes dynamic lights into account +// +//========================================================================== + +static int gl_SetSpriteLight(AActor *self, fixed_t x, fixed_t y, fixed_t z, subsector_t * subsec, + int lightlevel, int rellight, FColormap * cm, float alpha, + PalEntry ThingColor, bool weapon) +{ + float r,g,b; + float result[4]; // Korshun. + + gl_GetLightColor(lightlevel, rellight, cm, &r, &g, &b, weapon); + bool res = gl_GetSpriteLight(self, x, y, z, subsec, cm? cm->colormap : 0, result); + if (!res || glset.lightmode == 8) + { + r *= ThingColor.r/255.f; + g *= ThingColor.g/255.f; + b *= ThingColor.b/255.f; + gl.Color4f(r, g, b, alpha); + if (glset.lightmode == 8) + { + gl.VertexAttrib1f(VATTR_LIGHTLEVEL, gl_CalcLightLevel(lightlevel, rellight, weapon) / 255.0f); // Korshun. + gl_RenderState.SetDynLight(result[0], result[1], result[2]); + } + return lightlevel; + } + else + { + // Note: Due to subtractive lights the values can easily become negative so we have to clamp both + // at the low and top end of the range! + r = clamp(result[0]+r, 0, 1.0f); + g = clamp(result[1]+g, 0, 1.0f); + b = clamp(result[2]+b, 0, 1.0f); + + float dlightlevel = r*77 + g*143 + b*35; + + r *= ThingColor.r/255.f; + g *= ThingColor.g/255.f; + b *= ThingColor.b/255.f; + + gl.Color4f(r, g, b, alpha); + + if (dlightlevel == 0) return 0; + + if (glset.lightmode&2 && dlightlevel<192.f) + { + return xs_CRoundToInt(192.f - (192.f - dlightlevel) / 1.95f); + } + else + { + return xs_CRoundToInt(dlightlevel); + } + } +} + +int gl_SetSpriteLight(AActor * thing, int lightlevel, int rellight, FColormap * cm, + float alpha, PalEntry ThingColor, bool weapon) +{ + subsector_t * subsec = thing->subsector; + return gl_SetSpriteLight(thing, thing->x, thing->y, thing->z+(thing->height>>1), subsec, + lightlevel, rellight, cm, alpha, ThingColor, weapon); +} + +int gl_SetSpriteLight(particle_t * thing, int lightlevel, int rellight, FColormap *cm, float alpha, PalEntry ThingColor) +{ + return gl_SetSpriteLight(NULL, thing->x, thing->y, thing->z, thing->subsector, lightlevel, rellight, + cm, alpha, ThingColor, false); +} + +//========================================================================== +// +// Modifies the color values depending on the render style +// +//========================================================================== + +void gl_GetSpriteLighting(FRenderStyle style, AActor *thing, FColormap *cm, PalEntry &ThingColor) +{ + if (style.Flags & STYLEF_RedIsAlpha) + { + cm->colormap = CM_SHADE; + } + if (style.Flags & STYLEF_ColorIsFixed) + { + if (style.Flags & STYLEF_InvertSource) + { + ThingColor = PalEntry(thing->fillcolor).InverseColor(); + } + else + { + ThingColor = thing->fillcolor; + } + } + + // This doesn't work like in the software renderer. + if (style.Flags & STYLEF_InvertSource) + { + int gray = (cm->LightColor.r*77 + cm->LightColor.r*143 + cm->LightColor.r*36)>>8; + cm->LightColor.r = cm->LightColor.g = cm->LightColor.b = gray; + } +} + + +//========================================================================== +// +// Sets render state to draw the given render style +// +//========================================================================== + +int gl_SetSpriteLighting(FRenderStyle style, AActor *thing, int lightlevel, int rellight, FColormap *cm, + PalEntry ThingColor, float alpha, bool fullbright, bool weapon) +{ + FColormap internal_cm; + + if (style.Flags & STYLEF_RedIsAlpha) + { + cm->colormap = CM_SHADE; + } + if (style.Flags & STYLEF_ColorIsFixed) + { + if (style.Flags & STYLEF_InvertSource) + { + ThingColor = PalEntry(thing->fillcolor).InverseColor(); + } + else + { + ThingColor = thing->fillcolor; + } + gl_ModifyColor(ThingColor.r, ThingColor.g, ThingColor.b, cm->colormap); + } + + // This doesn't work like in the software renderer. + if (style.Flags & STYLEF_InvertSource) + { + internal_cm = *cm; + cm = &internal_cm; + + int gray = (internal_cm.LightColor.r*77 + internal_cm.LightColor.r*143 + internal_cm.LightColor.r*36)>>8; + cm->LightColor.r = cm->LightColor.g = cm->LightColor.b = gray; + } + + if (style.BlendOp == STYLEOP_Shadow) + { + gl.Color4f(0.2f * ThingColor.r / 255.f, 0.2f * ThingColor.g / 255.f, + 0.2f * ThingColor.b / 255.f, (alpha = 0.33f)); + } + else + { + if (gl_light_sprites && gl_lights && GLRenderer->mLightCount && !fullbright) + { + lightlevel = gl_SetSpriteLight(thing, lightlevel, rellight, cm, alpha, ThingColor, weapon); + } + else + { + gl_SetColor(lightlevel, rellight, cm, alpha, ThingColor, weapon); + } + } + gl_RenderState.AlphaFunc(GL_GEQUAL,alpha*gl_mask_sprite_threshold); + return lightlevel; +} + diff --git a/src/gl/scene/gl_vertex.cpp b/src/gl/scene/gl_vertex.cpp new file mode 100644 index 000000000..0e70b4077 --- /dev/null +++ b/src/gl/scene/gl_vertex.cpp @@ -0,0 +1,195 @@ +/* +** gl_vertex.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2006 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "gl/gl_functions.h" + +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/data/gl_data.h" +#include "gl/dynlights/gl_glow.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_portal.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_templates.h" + +EXTERN_CVAR(Bool, gl_seamless) +extern int vertexcount; + +//========================================================================== +// +// Split upper edge of wall +// +//========================================================================== + +void GLWall::SplitUpperEdge(texcoord * tcs, bool glow) +{ + if (seg == NULL || seg->sidedef == NULL || (seg->sidedef->Flags & WALLF_POLYOBJ) || seg->sidedef->numsegs == 1) return; + + side_t *sidedef = seg->sidedef; + float polyw = glseg.fracright - glseg.fracleft; + float facu = (tcs[2].u - tcs[1].u) / polyw; + float facv = (tcs[2].v - tcs[1].v) / polyw; + float fact = (ztop[1] - ztop[0]) / polyw; + float facc = (zceil[1] - zceil[0]) / polyw; + float facf = (zfloor[1] - zfloor[0]) / polyw; + + for (int i=0; i < sidedef->numsegs - 1; i++) + { + seg_t *cseg = sidedef->segs[i]; + float sidefrac = cseg->sidefrac; + if (sidefrac <= glseg.fracleft) continue; + if (sidefrac >= glseg.fracright) return; + + float fracfac = sidefrac - glseg.fracleft; + + if (glow) gl.VertexAttrib2f(VATTR_GLOWDISTANCE, zceil[0] - ztop[0] + (facc - fact) * fracfac, + ztop[0] - zfloor[0] + (fact - facf) * fracfac); + + gl.TexCoord2f(tcs[1].u + facu * fracfac, tcs[1].v + facv * fracfac); + gl.Vertex3f(cseg->v2->fx, ztop[0] + fact * fracfac, cseg->v2->fy); + } + vertexcount += sidedef->numsegs-1; +} + +//========================================================================== +// +// Split upper edge of wall +// +//========================================================================== + +void GLWall::SplitLowerEdge(texcoord * tcs, bool glow) +{ + if (seg == NULL || seg->sidedef == NULL || (seg->sidedef->Flags & WALLF_POLYOBJ) || seg->sidedef->numsegs == 1) return; + + side_t *sidedef = seg->sidedef; + float polyw = glseg.fracright - glseg.fracleft; + float facu = (tcs[3].u - tcs[0].u) / polyw; + float facv = (tcs[3].v - tcs[0].v) / polyw; + float facb = (zbottom[1] - zbottom[0]) / polyw; + float facc = (zceil[1] - zceil[0]) / polyw; + float facf = (zfloor[1] - zfloor[0]) / polyw; + + for (int i = sidedef->numsegs-2; i >= 0; i--) + { + seg_t *cseg = sidedef->segs[i]; + float sidefrac = cseg->sidefrac; + if (sidefrac >= glseg.fracright) continue; + if (sidefrac <= glseg.fracleft) return; + + float fracfac = sidefrac - glseg.fracleft; + + if (glow) gl.VertexAttrib2f(VATTR_GLOWDISTANCE, zceil[0] - zbottom[0] + (facc - facb) * fracfac, + zbottom[0] - zfloor[0] + (facb - facf) * fracfac); + + gl.TexCoord2f(tcs[0].u + facu * fracfac, tcs[0].v + facv * fracfac); + gl.Vertex3f(cseg->v2->fx, zbottom[0] + facb * fracfac, cseg->v2->fy); + } + vertexcount += sidedef->numsegs-1; +} + +//========================================================================== +// +// Split left edge of wall +// +//========================================================================== + +void GLWall::SplitLeftEdge(texcoord * tcs, bool glow) +{ + if (vertexes[0]==NULL) return; + + vertex_t * vi=vertexes[0]; + + if (vi->numheights) + { + int i=0; + + float polyh1=ztop[0] - zbottom[0]; + float factv1=polyh1? (tcs[1].v - tcs[0].v) / polyh1:0; + float factu1=polyh1? (tcs[1].u - tcs[0].u) / polyh1:0; + + while (inumheights && vi->heightlist[i] <= zbottom[0] ) i++; + while (inumheights && vi->heightlist[i] < ztop[0]) + { + if (glow) gl.VertexAttrib2f(VATTR_GLOWDISTANCE, zceil[0] - vi->heightlist[i], vi->heightlist[i] - zfloor[0]); + gl.TexCoord2f(factu1*(vi->heightlist[i] - ztop[0]) + tcs[1].u, + factv1*(vi->heightlist[i] - ztop[0]) + tcs[1].v); + gl.Vertex3f(glseg.x1, vi->heightlist[i], glseg.y1); + i++; + } + vertexcount+=i; + } +} + +//========================================================================== +// +// Split right edge of wall +// +//========================================================================== + +void GLWall::SplitRightEdge(texcoord * tcs, bool glow) +{ + if (vertexes[1]==NULL) return; + + vertex_t * vi=vertexes[1]; + + if (vi->numheights) + { + int i=vi->numheights-1; + + float polyh2 = ztop[1] - zbottom[1]; + float factv2 = polyh2? (tcs[2].v - tcs[3].v) / polyh2:0; + float factu2 = polyh2? (tcs[2].u - tcs[3].u) / polyh2:0; + + while (i>0 && vi->heightlist[i] >= ztop[1]) i--; + while (i>0 && vi->heightlist[i] > zbottom[1]) + { + if (glow) gl.VertexAttrib2f(VATTR_GLOWDISTANCE, zceil[1] - vi->heightlist[i], vi->heightlist[i] - zfloor[1]); + gl.TexCoord2f(factu2 * (vi->heightlist[i] - ztop[1]) + tcs[2].u, + factv2 * (vi->heightlist[i] - ztop[1]) + tcs[2].v); + gl.Vertex3f(glseg.x2, vi->heightlist[i], glseg.y2); + i--; + } + vertexcount+=i; + } +} + diff --git a/src/gl/scene/gl_wall.h b/src/gl/scene/gl_wall.h new file mode 100644 index 000000000..e44155aa6 --- /dev/null +++ b/src/gl/scene/gl_wall.h @@ -0,0 +1,365 @@ +#ifndef __GL_WALL_H +#define __GL_WALL_H +//========================================================================== +// +// One wall segment in the draw list +// +//========================================================================== +#include "r_defs.h" +#include "textures/textures.h" +#include "gl/renderer/gl_colormap.h" + +struct GLHorizonInfo; +struct F3DFloor; +struct model_t; +struct FSpriteModelFrame; +struct particle_t; +class ADynamicLight; +class FMaterial; +struct GLDrawList; +struct GLSkyInfo; +struct FTexCoordInfo; +struct FPortal; + + +enum WallTypes +{ + RENDERWALL_NONE, + RENDERWALL_TOP, + RENDERWALL_M1S, + RENDERWALL_M2S, + RENDERWALL_BOTTOM, + RENDERWALL_SKY, + RENDERWALL_FOGBOUNDARY, + RENDERWALL_HORIZON, + RENDERWALL_SKYBOX, + RENDERWALL_SECTORSTACK, + RENDERWALL_PLANEMIRROR, + RENDERWALL_MIRROR, + RENDERWALL_MIRRORSURFACE, + RENDERWALL_M2SNF, + RENDERWALL_M2SFOG, + RENDERWALL_COLOR, + RENDERWALL_FFBLOCK, + RENDERWALL_COLORLAYER, + // Insert new types at the end! +}; + +struct GLSeg +{ + float x1,x2; + float y1,y2; + float fracleft, fracright; // fractional offset of the 2 vertices on the linedef +}; + +struct texcoord +{ + float u,v; +}; + +//========================================================================== +// +// One sector plane, still in fixed point +// +//========================================================================== + +struct GLSectorPlane +{ + FTextureID texture; + secplane_t plane; + fixed_t texheight; + fixed_t xoffs, yoffs; + fixed_t xscale, yscale; + angle_t angle; + + void GetFromSector(sector_t * sec, int ceiling) + { + xoffs = sec->GetXOffset(ceiling); + yoffs = sec->GetYOffset(ceiling); + xscale = sec->GetXScale(ceiling); + yscale = sec->GetYScale(ceiling); + angle = sec->GetAngle(ceiling); + texture = sec->GetTexture(ceiling); + plane = sec->GetSecPlane(ceiling); + texheight = (ceiling == sector_t::ceiling)? plane.d : -plane.d; + } +}; + + + +class GLWall +{ +public: + + enum + { + //GLWF_CLAMPX=1, use GLT_* for these! + //GLWF_CLAMPY=2, + GLWF_SKYHACK=4, + GLWF_FOGGY=8, + GLWF_GLOW=16, // illuminated by glowing flats + GLWF_NOSHADER=32, // cannot be drawn with shaders. + GLWF_NOSPLITUPPER=64, + GLWF_NOSPLITLOWER=128, + }; + + friend struct GLDrawList; + friend class GLPortal; + + GLSeg glseg; + vertex_t * vertexes[2]; // required for polygon splitting + float ztop[2],zbottom[2]; + texcoord uplft, uprgt, lolft, lorgt; + float alpha; + FMaterial *gltexture; + + FColormap Colormap; + ERenderStyle RenderStyle; + + fixed_t viewdistance; + + BYTE lightlevel; + BYTE type; + BYTE flags; + short rellight; + + float topglowcolor[4]; + float bottomglowcolor[4]; + + int firstdynlight, lastdynlight; + int firstwall, numwalls; // splitting info. + + union + { + // it's either one of them but never more! + AActor * skybox; // for skyboxes + GLSkyInfo * sky; // for normal sky + GLHorizonInfo * horizon; // for horizon information + FPortal * portal; // stacked sector portals + secplane_t * planemirror; // for plane mirrors + }; + + + FTextureID topflat,bottomflat; + + // these are not the same as ytop and ybottom!!! + float zceil[2]; + float zfloor[2]; + +public: + seg_t * seg; // this gives the easiest access to all other structs involved + subsector_t * sub; // For polyobjects +private: + + void CheckGlowing(); + void PutWall(bool translucent); + void CheckTexturePosition(); + + void SetupLights(); + bool PrepareLight(texcoord * tcs, ADynamicLight * light); + void RenderWall(int textured, float * color2, ADynamicLight * light=NULL); + void RenderGlowingPoly(int textured, ADynamicLight * light=NULL); + int Intersection(FloatRect * rc,GLWall * result); + + void FloodPlane(int pass); + + void SkyPlane(sector_t *sector, int plane, bool allowmirror); + void SkyNormal(sector_t * fs,vertex_t * v1,vertex_t * v2); + void SkyTop(seg_t * seg,sector_t * fs,sector_t * bs,vertex_t * v1,vertex_t * v2); + void SkyBottom(seg_t * seg,sector_t * fs,sector_t * bs,vertex_t * v1,vertex_t * v2); + + void Put3DWall(lightlist_t * lightlist, bool translucent); + void SplitWall(sector_t * frontsector, bool translucent); + void LightPass(); + void SetHorizon(vertex_t * ul, vertex_t * ur, vertex_t * ll, vertex_t * lr); + bool DoHorizon(seg_t * seg,sector_t * fs, vertex_t * v1,vertex_t * v2); + + bool SetWallCoordinates(seg_t * seg, FTexCoordInfo *tci, float ceilingrefheight, + int topleft,int topright, int bottomleft,int bottomright, int texoffset); + + void DoTexture(int type,seg_t * seg,int peg, + int ceilingrefheight,int floorrefheight, + int CeilingHeightstart,int CeilingHeightend, + int FloorHeightstart,int FloorHeightend, + int v_offset); + + void DoMidTexture(seg_t * seg, bool drawfogboundary, + sector_t * front, sector_t * back, + sector_t * realfront, sector_t * realback, + fixed_t fch1, fixed_t fch2, fixed_t ffh1, fixed_t ffh2, + fixed_t bch1, fixed_t bch2, fixed_t bfh1, fixed_t bfh2); + + void GetPlanePos(F3DFloor::planeref *planeref, int &left, int &right); + + void BuildFFBlock(seg_t * seg, F3DFloor * rover, + fixed_t ff_topleft, fixed_t ff_topright, + fixed_t ff_bottomleft, fixed_t ff_bottomright); + void InverseFloors(seg_t * seg, sector_t * frontsector, + fixed_t topleft, fixed_t topright, + fixed_t bottomleft, fixed_t bottomright); + void ClipFFloors(seg_t * seg, F3DFloor * ffloor, sector_t * frontsector, + fixed_t topleft, fixed_t topright, + fixed_t bottomleft, fixed_t bottomright); + void DoFFloorBlocks(seg_t * seg, sector_t * frontsector, sector_t * backsector, + fixed_t fch1, fixed_t fch2, fixed_t ffh1, fixed_t ffh2, + fixed_t bch1, fixed_t bch2, fixed_t bfh1, fixed_t bfh2); + + void DrawDecal(DBaseDecal *actor); + void DoDrawDecals(); + + void RenderFogBoundary(); + void RenderMirrorSurface(); + void RenderTranslucentWall(); + + void SplitLeftEdge(texcoord * tcs, bool glow); + void SplitRightEdge(texcoord * tcs, bool glow); + void SplitUpperEdge(texcoord * tcs, bool glow); + void SplitLowerEdge(texcoord * tcs, bool glow); + +public: + + void Process(seg_t *seg, sector_t *frontsector, sector_t *backsector); + void ProcessLowerMiniseg(seg_t *seg, sector_t *frontsector, sector_t *backsector); + void Draw(int pass); + + float PointOnSide(float x,float y) + { + return -((y-glseg.y1)*(glseg.x2-glseg.x1)-(x-glseg.x1)*(glseg.y2-glseg.y1)); + } + + // Lines start-end and fdiv must intersect. + double CalcIntersectionVertex(GLWall * w2) + { + float ax = glseg.x1, ay=glseg.y1; + float bx = glseg.x2, by=glseg.y2; + float cx = w2->glseg.x1, cy=w2->glseg.y1; + float dx = w2->glseg.x2, dy=w2->glseg.y2; + return ((ay-cy)*(dx-cx)-(ax-cx)*(dy-cy)) / ((bx-ax)*(dy-cy)-(by-ay)*(dx-cx)); + } + +}; + +//========================================================================== +// +// One flat plane in the draw list +// +//========================================================================== + +class GLFlat +{ +public: + friend struct GLDrawList; + + sector_t * sector; + subsector_t * sub; // only used for translucent planes + float dz; // z offset for rendering hacks + float z; // the z position of the flat (only valid for non-sloped planes) + FMaterial *gltexture; + + FColormap Colormap; // light and fog + ERenderStyle renderstyle; + + float alpha; + GLSectorPlane plane; + int lightlevel; + bool stack; + bool foggy; + bool ceiling; + BYTE renderflags; + int vboindex; + int vboheight; + + int dynlightindex; + + bool SetupSubsectorLights(bool lightsapplied, subsector_t * sub); + void DrawSubsector(subsector_t * sub); + void DrawSubsectorLights(subsector_t * sub, int pass); + void DrawSubsectors(int pass, bool istrans); + + void PutFlat(bool fog = false); + void Process(sector_t * model, int whichplane, bool notexture); + void SetFrom3DFloor(F3DFloor *rover, bool top, bool underside); + void ProcessSector(sector_t * frontsector); + void Draw(int pass); +}; + + +//========================================================================== +// +// One sprite in the draw list +// +//========================================================================== + + +class GLSprite +{ +public: + friend struct GLDrawList; + friend void Mod_RenderModel(GLSprite * spr, model_t * mdl, int framenumber); + + BYTE lightlevel; + BYTE foglevel; + BYTE hw_styleflags; + bool fullbright; + PalEntry ThingColor; // thing's own color + FColormap Colormap; + FSpriteModelFrame * modelframe; + FRenderStyle RenderStyle; + int OverrideShader; + + int translation; + int index; + int depth; + + float x,y,z; // needed for sorting! + + float ul,ur; + float vt,vb; + float x1,y1,z1; + float x2,y2,z2; + + FMaterial *gltexture; + float trans; + AActor * actor; + particle_t * particle; + + void SplitSprite(sector_t * frontsector, bool translucent); + void SetLowerParam(); + +public: + + void Draw(int pass); + void PutSprite(bool translucent); + void Process(AActor* thing,sector_t * sector); + void ProcessParticle (particle_t *particle, sector_t *sector);//, int shade, int fakeside) + void SetThingColor(PalEntry); + void SetSpriteColor(sector_t *sector, fixed_t y); + + // Lines start-end and fdiv must intersect. + double CalcIntersectionVertex(GLWall * w2); +}; + +inline float Dist2(float x1,float y1,float x2,float y2) +{ + return sqrtf((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); +} + +// Light + color + +bool gl_GetSpriteLight(AActor *Self, fixed_t x, fixed_t y, fixed_t z, subsector_t * subsec, int desaturation, float * out, line_t *line = NULL, int side = 0); +int gl_SetSpriteLight(AActor * thing, int lightlevel, int rellight, FColormap * cm, float alpha, PalEntry ThingColor = 0xffffff, bool weapon=false); + +void gl_GetSpriteLight(AActor * thing, int lightlevel, int rellight, FColormap * cm, + float *red, float *green, float *blue, + PalEntry ThingColor, bool weapon); + +int gl_SetSpriteLighting(FRenderStyle style, AActor *thing, int lightlevel, int rellight, FColormap *cm, + PalEntry ThingColor, float alpha, bool fullbright, bool weapon); + +int gl_SetSpriteLight(particle_t * thing, int lightlevel, int rellight, FColormap *cm, float alpha, PalEntry ThingColor = 0xffffff); +void gl_GetLightForThing(AActor * thing, float upper, float lower, float & r, float & g, float & b); + + + + +#endif diff --git a/src/gl/scene/gl_walls.cpp b/src/gl/scene/gl_walls.cpp new file mode 100644 index 000000000..40c18baa9 --- /dev/null +++ b/src/gl/scene/gl_walls.cpp @@ -0,0 +1,1793 @@ +/* +** gl_wall.cpp +** Wall rendering preparation +** +**--------------------------------------------------------------------------- +** Copyright 2000-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "p_local.h" +#include "p_lnspec.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "templates.h" +#include "vectors.h" +#include "r_defs.h" +#include "r_sky.h" + +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/data/gl_data.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/dynlights/gl_glow.h" +#include "gl/dynlights/gl_lightbuffer.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_portal.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_geometric.h" +#include "gl/utility/gl_templates.h" +#include "gl/shaders/gl_shader.h" + + +//========================================================================== +// +// Checks whether a wall should glow +// +//========================================================================== +void GLWall::CheckGlowing() +{ + bottomglowcolor[3] = topglowcolor[3] = 0; + if (!gl_isFullbright(Colormap.LightColor, lightlevel) && gl_GlowActive()) + { + FTexture *tex = TexMan[topflat]; + if (tex != NULL && tex->isGlowing()) + { + flags |= GLWall::GLWF_GLOW; + tex->GetGlowColor(topglowcolor); + topglowcolor[3] = tex->gl_info.GlowHeight; + } + + tex = TexMan[bottomflat]; + if (tex != NULL && tex->isGlowing()) + { + flags |= GLWall::GLWF_GLOW; + tex->GetGlowColor(bottomglowcolor); + bottomglowcolor[3] = tex->gl_info.GlowHeight; + } + } +} + + +//========================================================================== +// +// +// +//========================================================================== +void GLWall::PutWall(bool translucent) +{ + GLPortal * portal; + int list; + + static char passflag[]={ + 0, //RENDERWALL_NONE, + 1, //RENDERWALL_TOP, // unmasked + 1, //RENDERWALL_M1S, // unmasked + 2, //RENDERWALL_M2S, // depends on render and texture settings + 1, //RENDERWALL_BOTTOM, // unmasked + 4, //RENDERWALL_SKYDOME, // special + 3, //RENDERWALL_FOGBOUNDARY, // translucent + 4, //RENDERWALL_HORIZON, // special + 4, //RENDERWALL_SKYBOX, // special + 4, //RENDERWALL_SECTORSTACK, // special + 4, //RENDERWALL_PLANEMIRROR, // special + 4, //RENDERWALL_MIRROR, // special + 1, //RENDERWALL_MIRRORSURFACE, // needs special handling + 2, //RENDERWALL_M2SNF, // depends on render and texture settings, no fog + 2, //RENDERWALL_M2SFOG, // depends on render and texture settings, no fog + 3, //RENDERWALL_COLOR, // translucent + 2, //RENDERWALL_FFBLOCK // depends on render and texture settings + 4, //RENDERWALL_COLORLAYER // color layer needs special handling + }; + + if (gltexture && gltexture->GetTransparent() && passflag[type] == 2) + { + translucent = true; + } + + if (gl_fixedcolormap) + { + // light planes don't get drawn with fullbright rendering + if (!gltexture && passflag[type]!=4) return; + + Colormap.GetFixedColormap(); + } + + CheckGlowing(); + + if (translucent) // translucent walls + { + viewdistance = P_AproxDistance( ((seg->linedef->v1->x+seg->linedef->v2->x)>>1) - viewx, + ((seg->linedef->v1->y+seg->linedef->v2->y)>>1) - viewy); + gl_drawinfo->drawlists[GLDL_TRANSLUCENT].AddWall(this); + } + else if (passflag[type]!=4) // non-translucent walls + { + static DrawListType list_indices[2][2][2]={ + { { GLDL_PLAIN, GLDL_FOG }, { GLDL_MASKED, GLDL_FOGMASKED } }, + { { GLDL_LIGHT, GLDL_LIGHTFOG }, { GLDL_LIGHTMASKED, GLDL_LIGHTFOGMASKED } } + }; + + bool masked; + bool light = gl_forcemultipass; + + if (!gl_fixedcolormap) + { + if (gl_lights && !gl_dynlight_shader) + { + if (seg->sidedef == NULL) + { + light = false; + } + else if (!(seg->sidedef->Flags & WALLF_POLYOBJ)) + { + light = seg->sidedef->lighthead[0] != NULL; + } + else if (sub) + { + // for polyobjects we cannot use the side's light list. + // We must use the subsector's. + light = sub->lighthead[0] != NULL; + } + } + } + else + { + flags&=~GLWF_FOGGY; + } + + masked = passflag[type]==1? false : (light && type!=RENDERWALL_FFBLOCK) || (gltexture && gltexture->isMasked()); + + list = list_indices[light][masked][!!(flags&GLWF_FOGGY)]; + if (list == GLDL_LIGHT) + { + if (gltexture->tex->gl_info.Brightmap && gl_BrightmapsActive()) list = GLDL_LIGHTBRIGHT; + if (flags & GLWF_GLOW) list = GLDL_LIGHTBRIGHT; + } + gl_drawinfo->drawlists[list].AddWall(this); + + } + else switch (type) + { + case RENDERWALL_COLORLAYER: + gl_drawinfo->drawlists[GLDL_TRANSLUCENT].AddWall(this); + break; + + // portals don't go into the draw list. + // Instead they are added to the portal manager + case RENDERWALL_HORIZON: + //@sync-portal + horizon=UniqueHorizons.Get(horizon); + portal=GLPortal::FindPortal(horizon); + if (!portal) portal=new GLHorizonPortal(horizon); + portal->AddLine(this); + break; + + case RENDERWALL_SKYBOX: + //@sync-portal + portal=GLPortal::FindPortal(skybox); + if (!portal) portal=new GLSkyboxPortal(skybox); + portal->AddLine(this); + break; + + case RENDERWALL_SECTORSTACK: + //@sync-portal + portal = this->portal->GetGLPortal(); + portal->AddLine(this); + break; + + case RENDERWALL_PLANEMIRROR: + if (GLPortal::PlaneMirrorMode * planemirror->c <=0) + { + //@sync-portal + planemirror=UniquePlaneMirrors.Get(planemirror); + portal=GLPortal::FindPortal(planemirror); + if (!portal) portal=new GLPlaneMirrorPortal(planemirror); + portal->AddLine(this); + } + break; + + case RENDERWALL_MIRROR: + //@sync-portal + portal=GLPortal::FindPortal(seg->linedef); + if (!portal) portal=new GLMirrorPortal(seg->linedef); + portal->AddLine(this); + if (gl_mirror_envmap) + { + // draw a reflective layer over the mirror + type=RENDERWALL_MIRRORSURFACE; + gl_drawinfo->drawlists[GLDL_TRANSLUCENTBORDER].AddWall(this); + } + break; + + case RENDERWALL_SKY: + //@sync-portal + portal=GLPortal::FindPortal(sky); + if (!portal) portal=new GLSkyPortal(sky); + portal->AddLine(this); + break; + } +} + +//========================================================================== +// +// Sets 3D-floor lighting info +// +//========================================================================== + +void GLWall::Put3DWall(lightlist_t * lightlist, bool translucent) +{ + bool fadewall = (!translucent && lightlist->caster && (lightlist->caster->flags&FF_FADEWALLS) && + !gl_isBlack((lightlist->extra_colormap)->Fade)) && gl_isBlack(Colormap.FadeColor); + + // only modify the light level if it doesn't originate from the seg's frontsector. This is to account for light transferring effects + if (lightlist->p_lightlevel != &seg->sidedef->sector->lightlevel) + { + lightlevel = gl_ClampLight(*lightlist->p_lightlevel); + } + // relative light won't get changed here. It is constant across the entire wall. + + Colormap.CopyLightColor(lightlist->extra_colormap); + if (fadewall) lightlevel=255; + PutWall(translucent); + + if (fadewall) + { + FMaterial *tex = gltexture; + type = RENDERWALL_COLORLAYER; + gltexture = NULL; + Colormap.LightColor = (lightlist->extra_colormap)->Fade; + alpha = (255-(*lightlist->p_lightlevel))/255.f*1.f; + if (alpha>0.f) PutWall(true); + + type = RENDERWALL_FFBLOCK; + alpha = 1.0; + gltexture = tex; + } +} + +//========================================================================== +// +// Splits a wall vertically if a 3D-floor +// creates different lighting across the wall +// +//========================================================================== + +void GLWall::SplitWall(sector_t * frontsector, bool translucent) +{ + GLWall copyWall1,copyWall2; + float maplightbottomleft; + float maplightbottomright; + unsigned int i; + int origlight = lightlevel; + TArray & lightlist=frontsector->e->XFloor.lightlist; + + if (glseg.x1==glseg.x2 && glseg.y1==glseg.y2) + { + return; + } + ::SplitWall.Clock(); + +#ifdef _MSC_VER +#ifdef _DEBUG + if (seg->linedef-lines==1) + __asm nop +#endif +#endif + + if (lightlist.Size()>1) + { + for(i=0;i=ztop[0] && maplightbottomright>=ztop[1]) + { + continue; + } + + // check for an intersection with the upper plane + if ((maplightbottomleftztop[1]) || + (maplightbottomleft>ztop[0] && maplightbottomright(fabsf(glseg.x2-glseg.x1), fabsf(glseg.y2-glseg.y2)); + + float dch=ztop[1]-ztop[0]; + float dfh=maplightbottomright-maplightbottomleft; + float coeff= (ztop[0]-maplightbottomleft)/(dfh-dch); + + // check for inaccuracies - let's be a little generous here! + if (coeff*clen<.1f) + { + maplightbottomleft=ztop[0]; + } + else if (coeff*clen>clen-.1f) + { + maplightbottomright=ztop[1]; + } + else + { + // split the wall in 2 at the intersection and recursively split both halves + copyWall1=copyWall2=*this; + + copyWall1.glseg.x2 = copyWall2.glseg.x1 = glseg.x1 + coeff * (glseg.x2-glseg.x1); + copyWall1.glseg.y2 = copyWall2.glseg.y1 = glseg.y1 + coeff * (glseg.y2-glseg.y1); + copyWall1.ztop[1] = copyWall2.ztop[0] = ztop[0] + coeff * (ztop[1]-ztop[0]); + copyWall1.zbottom[1] = copyWall2.zbottom[0] = zbottom[0] + coeff * (zbottom[1]-zbottom[0]); + copyWall1.glseg.fracright = copyWall2.glseg.fracleft = glseg.fracleft + coeff * (glseg.fracright-glseg.fracleft); + copyWall1.uprgt.u = copyWall2.uplft.u = uplft.u + coeff * (uprgt.u-uplft.u); + copyWall1.uprgt.v = copyWall2.uplft.v = uplft.v + coeff * (uprgt.v-uplft.v); + copyWall1.lorgt.u = copyWall2.lolft.u = lolft.u + coeff * (lorgt.u-lolft.u); + copyWall1.lorgt.v = copyWall2.lolft.v = lolft.v + coeff * (lorgt.v-lolft.v); + + ::SplitWall.Unclock(); + + copyWall1.SplitWall(frontsector, translucent); + copyWall2.SplitWall(frontsector, translucent); + return; + } + } + + // check for an intersection with the lower plane + if ((maplightbottomleftzbottom[1]) || + (maplightbottomleft>zbottom[0] && maplightbottomright(fabsf(glseg.x2-glseg.x1), fabsf(glseg.y2-glseg.y2)); + + float dch=zbottom[1]-zbottom[0]; + float dfh=maplightbottomright-maplightbottomleft; + float coeff= (zbottom[0]-maplightbottomleft)/(dfh-dch); + + // check for inaccuracies - let's be a little generous here because there's + // some conversions between floats and fixed_t's involved + if (coeff*clen<.1f) + { + maplightbottomleft=zbottom[0]; + } + else if (coeff*clen>clen-.1f) + { + maplightbottomright=zbottom[1]; + } + else + { + // split the wall in 2 at the intersection and recursively split both halves + copyWall1=copyWall2=*this; + + copyWall1.glseg.x2 = copyWall2.glseg.x1 = glseg.x1 + coeff * (glseg.x2-glseg.x1); + copyWall1.glseg.y2 = copyWall2.glseg.y1 = glseg.y1 + coeff * (glseg.y2-glseg.y1); + copyWall1.ztop[1] = copyWall2.ztop[0] = ztop[0] + coeff * (ztop[1]-ztop[0]); + copyWall1.zbottom[1] = copyWall2.zbottom[0] = zbottom[0] + coeff * (zbottom[1]-zbottom[0]); + copyWall1.glseg.fracright = copyWall2.glseg.fracleft = glseg.fracleft + coeff * (glseg.fracright-glseg.fracleft); + copyWall1.uprgt.u = copyWall2.uplft.u = uplft.u + coeff * (uprgt.u-uplft.u); + copyWall1.uprgt.v = copyWall2.uplft.v = uplft.v + coeff * (uprgt.v-uplft.v); + copyWall1.lorgt.u = copyWall2.lolft.u = lolft.u + coeff * (lorgt.u-lolft.u); + copyWall1.lorgt.v = copyWall2.lolft.v = lolft.v + coeff * (lorgt.v-lolft.v); + + ::SplitWall.Unclock(); + + copyWall1.SplitWall(frontsector, translucent); + copyWall2.SplitWall(frontsector, translucent); + return; + } + } + + // 3D floor is completely within this light + if (maplightbottomleft<=zbottom[0] && maplightbottomright<=zbottom[1]) + { + // These values must not be destroyed! + int ll=lightlevel; + FColormap lc=Colormap; + + if (i > 0) Put3DWall(&lightlist[i], translucent); + else PutWall(translucent); // uppermost section does not alter light at all. + + lightlevel=ll; + Colormap=lc; + + ::SplitWall.Unclock(); + + return; + } + + if (maplightbottomleft<=ztop[0] && maplightbottomright<=ztop[1] && + (maplightbottomleft!=ztop[0] || maplightbottomright!=ztop[1])) + { + copyWall1=*this; + + copyWall1.flags |= GLWF_NOSPLITLOWER; + flags |= GLWF_NOSPLITUPPER; + ztop[0]=copyWall1.zbottom[0]=maplightbottomleft; + ztop[1]=copyWall1.zbottom[1]=maplightbottomright; + uplft.v=copyWall1.lolft.v=copyWall1.uplft.v+ + (maplightbottomleft-copyWall1.ztop[0])*(copyWall1.lolft.v-copyWall1.uplft.v)/(zbottom[0]-copyWall1.ztop[0]); + uprgt.v=copyWall1.lorgt.v=copyWall1.uprgt.v+ + (maplightbottomright-copyWall1.ztop[1])*(copyWall1.lorgt.v-copyWall1.uprgt.v)/(zbottom[1]-copyWall1.ztop[1]); + copyWall1.Put3DWall(&lightlist[i], translucent); + } + if (ztop[0]==zbottom[0] && ztop[1]==zbottom[1]) + { + ::SplitWall.Unclock(); + return; + } + } + } + + // These values must not be destroyed! + int ll=lightlevel; + FColormap lc=Colormap; + + Put3DWall(&lightlist[lightlist.Size()-1], translucent); + + lightlevel=ll; + Colormap=lc; + flags &= ~GLWF_NOSPLITUPPER; + ::SplitWall.Unclock(); +} + + +//========================================================================== +// +// +// +//========================================================================== +bool GLWall::DoHorizon(seg_t * seg,sector_t * fs, vertex_t * v1,vertex_t * v2) +{ + GLHorizonInfo hi; + lightlist_t * light; + + // ZDoom doesn't support slopes in a horizon sector so I won't either! + ztop[1] = ztop[0] = FIXED2FLOAT(fs->GetPlaneTexZ(sector_t::ceiling)); + zbottom[1] = zbottom[0] = FIXED2FLOAT(fs->GetPlaneTexZ(sector_t::floor)); + + if (viewz < fs->GetPlaneTexZ(sector_t::ceiling)) + { + if (viewz > fs->GetPlaneTexZ(sector_t::floor)) + zbottom[1] = zbottom[0] = FIXED2FLOAT(viewz); + + if (fs->GetTexture(sector_t::ceiling) == skyflatnum) + { + SkyPlane(fs, sector_t::ceiling, false); + } + else + { + type = RENDERWALL_HORIZON; + hi.plane.GetFromSector(fs, true); + hi.lightlevel = gl_ClampLight(fs->GetCeilingLight()); + hi.colormap = fs->ColorMap; + + if (fs->e->XFloor.ffloors.Size()) + { + light = P_GetPlaneLight(fs, &fs->ceilingplane, true); + + if(!(fs->GetFlags(sector_t::ceiling)&PLANEF_ABSLIGHTING)) hi.lightlevel = gl_ClampLight(*light->p_lightlevel); + hi.colormap.LightColor = (light->extra_colormap)->Color; + } + + if (gl_fixedcolormap) hi.colormap.GetFixedColormap(); + horizon = &hi; + PutWall(0); + } + ztop[1] = ztop[0] = zbottom[0]; + } + + if (viewz > fs->GetPlaneTexZ(sector_t::floor)) + { + zbottom[1] = zbottom[0] = FIXED2FLOAT(fs->GetPlaneTexZ(sector_t::floor)); + if (fs->GetTexture(sector_t::floor) == skyflatnum) + { + SkyPlane(fs, sector_t::floor, false); + } + else + { + type = RENDERWALL_HORIZON; + hi.plane.GetFromSector(fs, false); + hi.lightlevel = gl_ClampLight(fs->GetFloorLight()); + hi.colormap = fs->ColorMap; + + if (fs->e->XFloor.ffloors.Size()) + { + light = P_GetPlaneLight(fs, &fs->floorplane, false); + + if(!(fs->GetFlags(sector_t::floor)&PLANEF_ABSLIGHTING)) hi.lightlevel = gl_ClampLight(*light->p_lightlevel); + hi.colormap.LightColor = (light->extra_colormap)->Color; + } + + if (gl_fixedcolormap) hi.colormap.GetFixedColormap(); + horizon=&hi; + PutWall(0); + } + } + return true; +} + +//========================================================================== +// +// +// +//========================================================================== +bool GLWall::SetWallCoordinates(seg_t * seg, FTexCoordInfo *tci, float texturetop, + int topleft,int topright, int bottomleft,int bottomright, int t_ofs) +{ + // + // + // set up texture coordinate stuff + // + // + float l_ul; + float texlength; + + if (gltexture) + { + float length = seg->sidedef? seg->sidedef->TexelLength: Dist2(glseg.x1, glseg.y1, glseg.x2, glseg.y2); + + l_ul=tci->FloatToTexU(FIXED2FLOAT(tci->TextureOffset(t_ofs))); + texlength = tci->FloatToTexU(length); + } + else + { + tci=NULL; + l_ul=0; + texlength=0; + } + + + // + // + // set up coordinates for the left side of the polygon + // + // check left side for intersections + if (topleft>=bottomleft) + { + // normal case + ztop[0]=FIXED2FLOAT(topleft); + zbottom[0]=FIXED2FLOAT(bottomleft); + + if (tci) + { + uplft.v=tci->FloatToTexV(-ztop[0] + texturetop); + lolft.v=tci->FloatToTexV(-zbottom[0] + texturetop); + } + } + else + { + // ceiling below floor - clip to the visible part of the wall + fixed_t dch=topright-topleft; + fixed_t dfh=bottomright-bottomleft; + fixed_t coeff=FixedDiv(bottomleft-topleft, dch-dfh); + + fixed_t inter_y=topleft+FixedMul(coeff,dch); + + float inter_x= FIXED2FLOAT(coeff); + + glseg.x1 = glseg.x1 + inter_x * (glseg.x2 - glseg.x1); + glseg.y1 = glseg.y1 + inter_x * (glseg.y2 - glseg.y1); + glseg.fracleft = inter_x; + + zbottom[0]=ztop[0]=FIXED2FLOAT(inter_y); + + if (tci) + { + lolft.v=uplft.v=tci->FloatToTexV(-ztop[0] + texturetop); + } + } + + // + // + // set up coordinates for the right side of the polygon + // + // check left side for intersections + if (topright >= bottomright) + { + // normal case + ztop[1]=FIXED2FLOAT(topright) ; + zbottom[1]=FIXED2FLOAT(bottomright) ; + + if (tci) + { + uprgt.v=tci->FloatToTexV(-ztop[1] + texturetop); + lorgt.v=tci->FloatToTexV(-zbottom[1] + texturetop); + } + } + else + { + // ceiling below floor - clip to the visible part of the wall + fixed_t dch=topright-topleft; + fixed_t dfh=bottomright-bottomleft; + fixed_t coeff=FixedDiv(bottomleft-topleft, dch-dfh); + + fixed_t inter_y=topleft+FixedMul(coeff,dch); + + float inter_x= FIXED2FLOAT(coeff); + + glseg.x2 = glseg.x1 + inter_x * (glseg.x2 - glseg.x1); + glseg.y2 = glseg.y1 + inter_x * (glseg.y2 - glseg.y1); + glseg.fracright = inter_x; + + zbottom[1]=ztop[1]=FIXED2FLOAT(inter_y); + if (tci) + { + lorgt.v=uprgt.v=tci->FloatToTexV(-ztop[1] + texturetop); + } + } + + uplft.u = lolft.u = l_ul + texlength * glseg.fracleft; + uprgt.u = lorgt.u = l_ul + texlength * glseg.fracright; + + + if (gltexture && gltexture->tex->bHasCanvas && flags&GLT_CLAMPY) + { + // Camera textures are upside down so we have to shift the y-coordinate + // from [-1..0] to [0..1] when using texture clamping + + uplft.v+=1.f; + uprgt.v+=1.f; + lolft.v+=1.f; + lorgt.v+=1.f; + } + return true; +} + +//========================================================================== +// +// Do some tweaks with the texture coordinates to reduce visual glitches +// +//========================================================================== + +void GLWall::CheckTexturePosition() +{ + float sub; + + if (gltexture->tex->bHasCanvas) return; + + // clamp texture coordinates to a reasonable range. + // Extremely large values can cause visual problems + if (uplft.v < uprgt.v) + { + sub = float(xs_FloorToInt(uplft.v)); + } + else + { + sub = float(xs_FloorToInt(uprgt.v)); + } + uplft.v -= sub; + uprgt.v -= sub; + lolft.v -= sub; + lorgt.v -= sub; + + if ((uplft.v == 0.f && uprgt.v == 0.f && lolft.v <= 1.f && lorgt.v <= 1.f) || + (uplft.v >= 0.f && uprgt.v >= 0.f && lolft.v == 1.f && lorgt.v == 1.f)) + { + flags|=GLT_CLAMPY; + } +} + +//========================================================================== +// +// Handle one sided walls, upper and lower texture +// +//========================================================================== +void GLWall::DoTexture(int _type,seg_t * seg, int peg, + int ceilingrefheight,int floorrefheight, + int topleft,int topright, + int bottomleft,int bottomright, + int v_offset) +{ + if (topleft<=bottomleft && topright<=bottomright) return; + + // The Vertex values can be destroyed in this function and must be restored aferward! + GLSeg glsave=glseg; + int lh=ceilingrefheight-floorrefheight; + int texpos; + + switch (_type) + { + case RENDERWALL_TOP: + texpos = side_t::top; + break; + case RENDERWALL_BOTTOM: + texpos = side_t::bottom; + break; + default: + texpos = side_t::mid; + break; + } + + FTexCoordInfo tci; + + gltexture->GetTexCoordInfo(&tci, seg->sidedef->GetTextureXScale(texpos), seg->sidedef->GetTextureYScale(texpos)); + + type = (seg->linedef->special == Line_Mirror && _type == RENDERWALL_M1S && gl_mirrors) ? RENDERWALL_MIRROR : _type; + + float floatceilingref = FIXED2FLOAT(ceilingrefheight) + + FIXED2FLOAT(tci.RowOffset(seg->sidedef->GetTextureYOffset(texpos))) + + FIXED2FLOAT((peg ? (gltexture->TextureHeight(GLUSE_TEXTURE)<sidedef->GetTextureXOffset(texpos))) return; + + CheckTexturePosition(); + + // Add this wall to the render list + sector_t * sec = sub? sub->sector : seg->frontsector; + + if (sec->e->XFloor.lightlist.Size()==0 || gl_fixedcolormap) PutWall(false); + else SplitWall(sec, false); + + glseg=glsave; + flags&=~GLT_CLAMPY; +} + + +//========================================================================== +// +// +// +//========================================================================== + +void GLWall::DoMidTexture(seg_t * seg, bool drawfogboundary, + sector_t * front, sector_t * back, + sector_t * realfront, sector_t * realback, + fixed_t fch1, fixed_t fch2, fixed_t ffh1, fixed_t ffh2, + fixed_t bch1, fixed_t bch2, fixed_t bfh1, fixed_t bfh2) + +{ + FTexCoordInfo tci; + fixed_t topleft,bottomleft,topright,bottomright; + GLSeg glsave=glseg; + fixed_t texturetop, texturebottom; + bool wrap = (seg->linedef->flags&ML_WRAP_MIDTEX) || (seg->sidedef->Flags&WALLF_WRAP_MIDTEX); + + // + // + // Get the base coordinates for the texture + // + // + if (gltexture) + { + // Align the texture to the ORIGINAL sector's height!! + // At this point slopes don't matter because they don't affect the texture's z-position + + gltexture->GetTexCoordInfo(&tci, seg->sidedef->GetTextureXScale(side_t::mid), seg->sidedef->GetTextureYScale(side_t::mid)); + fixed_t rowoffset = tci.RowOffset(seg->sidedef->GetTextureYOffset(side_t::mid)); + if ( (seg->linedef->flags & ML_DONTPEGBOTTOM) >0) + { + texturebottom = MAX(realfront->GetPlaneTexZ(sector_t::floor),realback->GetPlaneTexZ(sector_t::floor))+rowoffset; + texturetop=texturebottom+(gltexture->TextureHeight(GLUSE_TEXTURE)<GetPlaneTexZ(sector_t::ceiling),realback->GetPlaneTexZ(sector_t::ceiling))+rowoffset; + texturebottom=texturetop-(gltexture->TextureHeight(GLUSE_TEXTURE)<GetHeightSec()) + { + // + // + // Set up the top + // + // + FTexture * tex = TexMan(seg->sidedef->GetTexture(side_t::top)); + if (!tex || tex->UseType==FTexture::TEX_Null) + { + if (front->GetTexture(sector_t::ceiling) == skyflatnum && + back->GetTexture(sector_t::ceiling) == skyflatnum) + { + // intra-sky lines do not clip the texture at all if there's no upper texture + topleft = topright = texturetop; + } + else + { + // texture is missing - use the higher plane + topleft = MAX(bch1,fch1); + topright = MAX(bch2,fch2); + } + } + else if ((bch1>fch1 || bch2>fch2) && + (seg->frontsector->GetTexture(sector_t::ceiling)!=skyflatnum || seg->backsector->GetTexture(sector_t::ceiling)==skyflatnum)) + // (!((bch1<=fch1 && bch2<=fch2) || (bch1>=fch1 && bch2>=fch2))) + { + // Use the higher plane and let the geometry clip the extruding part + topleft = bch1; + topright = bch2; + } + else + { + // But not if there can be visual artifacts. + topleft = MIN(bch1,fch1); + topright = MIN(bch2,fch2); + } + + // + // + // Set up the bottom + // + // + tex = TexMan(seg->sidedef->GetTexture(side_t::bottom)); + if (!tex || tex->UseType==FTexture::TEX_Null) + { + // texture is missing - use the lower plane + bottomleft = MIN(bfh1,ffh1); + bottomright = MIN(bfh2,ffh2); + } + else if (bfh1=ffh1 && bfh2>=ffh2))) + { + // the floor planes intersect. Use the backsector's floor for drawing so that + // drawing the front sector's plane clips the polygon automatically. + bottomleft = bfh1; + bottomright = bfh2; + } + else + { + // normal case - use the higher plane + bottomleft = MAX(bfh1,ffh1); + bottomright = MAX(bfh2,ffh2); + } + + // + // + // if we don't need a fog sheet let's clip away some unnecessary parts of the polygon + // + // + if (!drawfogboundary && !wrap) + { + if (texturetopbottomleft && texturebottom>bottomright) bottomleft=bottomright=texturebottom; + } + } + else + { + // + // + // if both sides of the line are in the same sector and the sector + // doesn't have a Transfer_Heights special don't clip to the planes + // Clipping to the planes is not necessary and can even produce + // unwanted side effects. + // + // + topleft=topright=texturetop; + bottomleft=bottomright=texturebottom; + } + + // nothing visible - skip the rest + if (topleft<=bottomleft && topright<=bottomright) return; + + + // + // + // set up texture coordinate stuff + // + // + fixed_t t_ofs = seg->sidedef->GetTextureXOffset(side_t::mid); + + if (gltexture) + { + // First adjust the texture offset so that the left edge of the linedef is inside the range [0..1]. + fixed_t texwidth = tci.TextureAdjustWidth()<>FRACBITS)+seg->sidedef->TexelLength; + + if ((textureoffset==0 && righttex<=gltexture->TextureWidth(GLUSE_TEXTURE)) || + (textureoffset>=0 && righttex==gltexture->TextureWidth(GLUSE_TEXTURE))) + { + flags|=GLT_CLAMPX; + } + else + { + flags&=~GLT_CLAMPX; + } + if (!wrap) + { + flags|=GLT_CLAMPY; + } + } + SetWallCoordinates(seg, &tci, FIXED2FLOAT(texturetop), topleft, topright, bottomleft, bottomright, t_ofs); + + // + // + // draw fog sheet if required + // + // + if (drawfogboundary) + { + flags |= GLWF_NOSPLITUPPER|GLWF_NOSPLITLOWER; + type=RENDERWALL_FOGBOUNDARY; + PutWall(true); + if (!gltexture) + { + flags &= ~(GLWF_NOSPLITUPPER|GLWF_NOSPLITLOWER); + return; + } + type=RENDERWALL_M2SNF; + } + else type=RENDERWALL_M2S; + + // + // + // set up alpha blending + // + // + if (seg->linedef->Alpha)// && seg->linedef->special!=Line_Fogsheet) + { + bool translucent = false; + + switch (seg->linedef->flags& ML_ADDTRANS)//TRANSBITS) + { + case 0: + RenderStyle=STYLE_Translucent; + alpha=FIXED2FLOAT(seg->linedef->Alpha); + translucent = seg->linedef->Alpha < FRACUNIT || (gltexture && gltexture->GetTransparent()); + break; + + case ML_ADDTRANS: + RenderStyle=STYLE_Add; + alpha=FIXED2FLOAT(seg->linedef->Alpha); + translucent=true; + break; + } + + // + // + // for textures with large empty areas only the visible parts are drawn. + // If these textures come too close to the camera they severely affect performance + // if stacked closely together + // Recognizing vertical gaps is rather simple and well worth the effort. + // + // + FloatRect *splitrect; + int v = gltexture->GetAreas(&splitrect); + if (v>0 && !drawfogboundary && !(seg->linedef->flags&ML_WRAP_MIDTEX)) + { + // split the poly! + GLWall split; + int i,t=0; + float v_factor=(zbottom[0]-ztop[0])/(lolft.v-uplft.v); + // only split the vertical area of the polygon that does not contain slopes. + float splittopv = MAX(uplft.v, uprgt.v); + float splitbotv = MIN(lolft.v, lorgt.v); + + // this is split vertically into sections. + for(i=0;i=splitbotv) break; + + float splitbot=splitrect[i].top+splitrect[i].height; + + // the current segment is above the top line of the splittable area + if (splitbot<=splittopv) continue; + + split=*this; + + // the top line of the current segment is inside the splittable area + // use the splitrect's top as top of this segment + // if not use the top of the remaining polygon + if (splitrect[i].top>splittopv) + { + split.ztop[0]=split.ztop[1]= ztop[0]+v_factor*(splitrect[i].top-uplft.v); + split.uplft.v=split.uprgt.v=splitrect[i].top; + } + // the bottom line of the current segment is inside the splittable area + // use the splitrect's bottom as bottom of this segment + // if not use the bottom of the remaining polygon + if (splitbot<=splitbotv) + { + split.zbottom[0]=split.zbottom[1]=ztop[0]+v_factor*(splitbot-uplft.v); + split.lolft.v=split.lorgt.v=splitbot; + } + // + // + // Draw the stuff + // + // + if (realfront->e->XFloor.lightlist.Size()==0 || gl_fixedcolormap) split.PutWall(translucent); + else split.SplitWall(realfront, translucent); + + t=1; + } + render_texsplit+=t; + } + else + { + // + // + // Draw the stuff without splitting + // + // + if (realfront->e->XFloor.lightlist.Size()==0 || gl_fixedcolormap) PutWall(translucent); + else SplitWall(realfront, translucent); + } + alpha=1.0f; + } + // restore some values that have been altered in this function + glseg=glsave; + flags&=~(GLT_CLAMPX|GLT_CLAMPY|GLWF_NOSPLITUPPER|GLWF_NOSPLITLOWER); +} + + +//========================================================================== +// +// +// +//========================================================================== +void GLWall::BuildFFBlock(seg_t * seg, F3DFloor * rover, + fixed_t ff_topleft, fixed_t ff_topright, + fixed_t ff_bottomleft, fixed_t ff_bottomright) +{ + side_t * mastersd = rover->master->sidedef[0]; + float to; + lightlist_t * light; + bool translucent; + byte savelight=lightlevel; + FColormap savecolor=Colormap; + float ul; + float texlength; + FTexCoordInfo tci; + + if (rover->flags&FF_FOG) + { + if (!gl_fixedcolormap) + { + // this may not yet be done! + light=P_GetPlaneLight(rover->target, rover->top.plane,true); + Colormap.Clear(); + Colormap.LightColor=(light->extra_colormap)->Fade; + // the fog plane defines the light level, not the front sector! + lightlevel=*light->p_lightlevel; + gltexture=NULL; + type=RENDERWALL_FFBLOCK; + } + else return; + } + else + { + + if (rover->flags&FF_UPPERTEXTURE) + { + gltexture = FMaterial::ValidateTexture(seg->sidedef->GetTexture(side_t::top), true); + if (!gltexture) return; + gltexture->GetTexCoordInfo(&tci, seg->sidedef->GetTextureXScale(side_t::top), seg->sidedef->GetTextureYScale(side_t::top)); + } + else if (rover->flags&FF_LOWERTEXTURE) + { + gltexture = FMaterial::ValidateTexture(seg->sidedef->GetTexture(side_t::bottom), true); + if (!gltexture) return; + gltexture->GetTexCoordInfo(&tci, seg->sidedef->GetTextureXScale(side_t::bottom), seg->sidedef->GetTextureYScale(side_t::bottom)); + } + else + { + gltexture = FMaterial::ValidateTexture(mastersd->GetTexture(side_t::mid), true); + if (!gltexture) return; + gltexture->GetTexCoordInfo(&tci, mastersd->GetTextureXScale(side_t::mid), mastersd->GetTextureYScale(side_t::mid)); + } + + to=FIXED2FLOAT((rover->flags&(FF_UPPERTEXTURE|FF_LOWERTEXTURE))? + 0 : tci.TextureOffset(mastersd->GetTextureXOffset(side_t::mid))); + + ul=tci.FloatToTexU(to + FIXED2FLOAT(tci.TextureOffset(seg->sidedef->GetTextureXOffset(side_t::mid)))); + + texlength = tci.FloatToTexU(seg->sidedef->TexelLength); + + uplft.u = lolft.u = ul + texlength * glseg.fracleft; + uprgt.u = lorgt.u = ul + texlength * glseg.fracright; + + fixed_t rowoffset = tci.RowOffset(seg->sidedef->GetTextureYOffset(side_t::mid)); + to= (rover->flags&(FF_UPPERTEXTURE|FF_LOWERTEXTURE))? + 0.f : FIXED2FLOAT(tci.RowOffset(mastersd->GetTextureYOffset(side_t::mid))); + + to += FIXED2FLOAT(rowoffset); + + uplft.v=tci.FloatToTexV(to + FIXED2FLOAT(*rover->top.texheight-ff_topleft)); + uprgt.v=tci.FloatToTexV(to + FIXED2FLOAT(*rover->top.texheight-ff_topright)); + lolft.v=tci.FloatToTexV(to + FIXED2FLOAT(*rover->top.texheight-ff_bottomleft)); + lorgt.v=tci.FloatToTexV(to + FIXED2FLOAT(*rover->top.texheight-ff_bottomright)); + type=RENDERWALL_FFBLOCK; + CheckTexturePosition(); + } + + ztop[0]=FIXED2FLOAT(ff_topleft); + ztop[1]=FIXED2FLOAT(ff_topright); + zbottom[0]=FIXED2FLOAT(ff_bottomleft);//-0.001f; + zbottom[1]=FIXED2FLOAT(ff_bottomright); + + if (rover->flags&(FF_TRANSLUCENT|FF_ADDITIVETRANS|FF_FOG)) + { + alpha=rover->alpha/255.0f; + RenderStyle = (rover->flags&FF_ADDITIVETRANS)? STYLE_Add : STYLE_Translucent; + translucent=true; + type=gltexture? RENDERWALL_M2S:RENDERWALL_COLOR; + } + else + { + alpha=1.0f; + RenderStyle=STYLE_Normal; + translucent=false; + } + + sector_t * sec = sub? sub->sector : seg->frontsector; + + if (sec->e->XFloor.lightlist.Size()==0 || gl_fixedcolormap) PutWall(translucent); + else SplitWall(sec, translucent); + + alpha=1.0f; + lightlevel = savelight; + Colormap = savecolor; + flags&=~GLT_CLAMPY; +} + + +//========================================================================== +// +// +// +//========================================================================== + +__forceinline void GLWall::GetPlanePos(F3DFloor::planeref *planeref, int &left, int &right) +{ + if (planeref->plane->a | planeref->plane->b) + { + left=planeref->plane->ZatPoint(vertexes[0]); + right=planeref->plane->ZatPoint(vertexes[1]); + } + else if(planeref->isceiling == sector_t::ceiling) + { + left = right = planeref->plane->d; + } + else + { + left = right = -planeref->plane->d; + } +} + +//========================================================================== +// +// +// +//========================================================================== +void GLWall::InverseFloors(seg_t * seg, sector_t * frontsector, + fixed_t topleft, fixed_t topright, + fixed_t bottomleft, fixed_t bottomright) +{ + TArray & frontffloors=frontsector->e->XFloor.ffloors; + + for(unsigned int i=0;iflags&FF_EXISTS)) continue; + if (!(rover->flags&FF_RENDERSIDES)) continue; + if (!(rover->flags&(FF_INVERTSIDES|FF_ALLSIDES))) continue; + + fixed_t ff_topleft; + fixed_t ff_topright; + fixed_t ff_bottomleft; + fixed_t ff_bottomright; + + GetPlanePos(&rover->top, ff_topleft, ff_topright); + GetPlanePos(&rover->bottom, ff_bottomleft, ff_bottomright); + + // above ceiling + if (ff_bottomleft>topleft && ff_bottomright>topright) continue; + + if (ff_topleft>topleft && ff_topright>topright) + { + // the new section overlaps with the previous one - clip it! + ff_topleft=topleft; + ff_topright=topright; + } + if (ff_bottomleft & frontffloors=frontsector->e->XFloor.ffloors; + + int flags = ffloor->flags & (FF_SWIMMABLE|FF_TRANSLUCENT); + + for(unsigned int i=0;iflags&FF_EXISTS)) continue; + if (!(rover->flags&FF_RENDERSIDES)) continue; + if ((rover->flags&flags)!=flags) continue; + + fixed_t ff_topleft; + fixed_t ff_topright; + fixed_t ff_bottomleft; + fixed_t ff_bottomright; + + GetPlanePos(&rover->top, ff_topleft, ff_topright); + + // we are completely below the bottom so unless there are some + // (unsupported) intersections there won't be any more floors that + // could clip this one. + if (ff_topleftbottom, ff_bottomleft, ff_bottomright); + // above top line? + if (ff_bottomleft>topleft && ff_bottomright>topright) continue; + + // overlapping the top line + if (ff_topleft>=topleft && ff_topright>=topright) + { + // overlapping with the entire range + if (ff_bottomleft<=bottomleft && ff_bottomright<=bottomright) return; + else if (ff_bottomleft>bottomleft && ff_bottomright>bottomright) + { + topleft=ff_bottomleft; + topright=ff_bottomright; + } + else + { + // an intersecting case. + // proper handling requires splitting but + // I don't need this right now. + } + } + else if (ff_topleft<=topleft && ff_topright<=topright) + { + BuildFFBlock(seg, ffloor, topleft, topright, ff_topleft, ff_topright); + if (ff_bottomleft<=bottomleft && ff_bottomright<=bottomright) return; + topleft=ff_bottomleft; + topright=ff_bottomright; + } + else + { + // an intersecting case. + // proper handling requires splitting but + // I don't need this right now. + } + } + +done: + // if the program reaches here there is one block left to draw + BuildFFBlock(seg, ffloor, topleft, topright, bottomleft, bottomright); +} + +//========================================================================== +// +// +// +//========================================================================== +void GLWall::DoFFloorBlocks(seg_t * seg,sector_t * frontsector,sector_t * backsector, + fixed_t fch1, fixed_t fch2, fixed_t ffh1, fixed_t ffh2, + fixed_t bch1, fixed_t bch2, fixed_t bfh1, fixed_t bfh2) + +{ + TArray & backffloors=backsector->e->XFloor.ffloors; + fixed_t topleft, topright, bottomleft, bottomright; + bool renderedsomething=false; + + // if the ceilings intersect use the backsector's height because this sector's ceiling will + // obstruct the redundant parts. + + if (fch1bfh1 && ffh2>bfh2) + { + bottomleft=ffh1; + bottomright=ffh2; + } + else + { + bottomleft=bfh1; + bottomright=bfh2; + } + + for(unsigned int i=0;iflags&FF_EXISTS)) continue; + if (!(rover->flags&FF_RENDERSIDES) || (rover->flags&FF_INVERTSIDES)) continue; + + fixed_t ff_topleft; + fixed_t ff_topright; + fixed_t ff_bottomleft; + fixed_t ff_bottomright; + + GetPlanePos(&rover->top, ff_topleft, ff_topright); + GetPlanePos(&rover->bottom, ff_bottomleft, ff_bottomright); + + // completely above ceiling + if (ff_bottomleft>topleft && ff_bottomright>topright && !renderedsomething) continue; + + if (ff_topleft>topleft && ff_topright>topright) + { + // the new section overlaps with the previous one - clip it! + ff_topleft=topleft; + ff_topright=topright; + } + + // do all inverse floors above the current one it there is a gap between the + // last 3D floor and this one. + if (topleft>ff_topleft && topright>ff_topright) + InverseFloors(seg, frontsector, topleft, topright, ff_topleft, ff_topright); + + // if translucent or liquid clip away adjoining parts of the same type of FFloors on the other side + if (rover->flags&(FF_SWIMMABLE|FF_TRANSLUCENT)) + ClipFFloors(seg, rover, frontsector, ff_topleft, ff_topright, ff_bottomleft, ff_bottomright); + else + BuildFFBlock(seg, rover, ff_topleft, ff_topright, ff_bottomleft, ff_bottomright); + + topleft=ff_bottomleft; + topright=ff_bottomright; + renderedsomething=true; + if (topleft<=bottomleft && topright<=bottomright) return; + } + + // draw all inverse floors below the lowest one + if (frontsector->e->XFloor.ffloors.Size() > 0) + { + if (topleft>bottomleft || topright>bottomright) + InverseFloors(seg, frontsector, topleft, topright, bottomleft, bottomright); + } +} + +//========================================================================== +// +// +// +//========================================================================== +void GLWall::Process(seg_t *seg, sector_t * frontsector, sector_t * backsector) +{ + vertex_t * v1, * v2; + fixed_t fch1; + fixed_t ffh1; + fixed_t fch2; + fixed_t ffh2; + sector_t * realfront; + sector_t * realback; + +#ifdef _MSC_VER +#ifdef _DEBUG + if (seg->linedef-lines==2570) + __asm nop +#endif +#endif + + // note: we always have a valid sidedef and linedef reference when getting here. + + this->seg = seg; + + if ((seg->sidedef->Flags & WALLF_POLYOBJ) && seg->backsector) + { + // Textures on 2-sided polyobjects are aligned to the actual seg's sectors + realfront = seg->frontsector; + realback = seg->backsector; + } + else + { + // Need these for aligning the textures + realfront = §ors[frontsector->sectornum]; + realback = backsector? §ors[backsector->sectornum] : NULL; + } + + if (seg->sidedef == seg->linedef->sidedef[0]) + { + v1=seg->linedef->v1; + v2=seg->linedef->v2; + } + else + { + v1=seg->linedef->v2; + v2=seg->linedef->v1; + } + + if (!(seg->sidedef->Flags & WALLF_POLYOBJ)) + { + glseg.fracleft=0; + glseg.fracright=1; + if (gl_seamless) + { + if (v1->dirty) gl_RecalcVertexHeights(v1); + if (v2->dirty) gl_RecalcVertexHeights(v2); + } + } + else // polyobjects must be rendered per seg. + { + if (abs(v1->x-v2->x) > abs(v1->y-v2->y)) + { + glseg.fracleft = float(seg->v1->x - v1->x)/float(v2->x-v1->x); + glseg.fracright = float(seg->v2->x - v1->x)/float(v2->x-v1->x); + } + else + { + glseg.fracleft = float(seg->v1->y - v1->y)/float(v2->y-v1->y); + glseg.fracright = float(seg->v2->y - v1->y)/float(v2->y-v1->y); + } + v1=seg->v1; + v2=seg->v2; + } + + + vertexes[0]=v1; + vertexes[1]=v2; + + glseg.x1= FIXED2FLOAT(v1->x); + glseg.y1= FIXED2FLOAT(v1->y); + glseg.x2= FIXED2FLOAT(v2->x); + glseg.y2= FIXED2FLOAT(v2->y); + Colormap=frontsector->ColorMap; + flags = (!gl_isBlack(Colormap.FadeColor) || level.flags&LEVEL_HASFADETABLE)? GLWF_FOGGY : 0; + + int rel = 0; + int orglightlevel = gl_ClampLight(frontsector->lightlevel); + lightlevel = seg->sidedef->GetLightLevel(!!(flags&GLWF_FOGGY), orglightlevel, false, &rel); + if (orglightlevel >= 253) // with the software renderer fake contrast won't be visible above this. + { + rellight = 0; + } + else if (lightlevel - rel > 256) // the brighter part of fake contrast will be clamped so also clamp the darker part by the same amount for better looks + { + rellight = 256 - lightlevel + rel; + } + else + { + rellight = rel; + } + + alpha=1.0f; + RenderStyle=STYLE_Normal; + gltexture=NULL; + + topflat=frontsector->GetTexture(sector_t::ceiling); // for glowing textures. These must be saved because + bottomflat=frontsector->GetTexture(sector_t::floor); // the sector passed here might be a temporary copy. + + // Save a little time (up to 0.3 ms per frame ;) ) + if (frontsector->floorplane.a | frontsector->floorplane.b) + { + ffh1=frontsector->floorplane.ZatPoint(v1); + ffh2=frontsector->floorplane.ZatPoint(v2); + zfloor[0]=FIXED2FLOAT(ffh1); + zfloor[1]=FIXED2FLOAT(ffh2); + } + else + { + ffh1 = ffh2 = -frontsector->floorplane.d; + zfloor[0] = zfloor[1] = FIXED2FLOAT(ffh2); + } + + if (frontsector->ceilingplane.a | frontsector->ceilingplane.b) + { + fch1=frontsector->ceilingplane.ZatPoint(v1); + fch2=frontsector->ceilingplane.ZatPoint(v2); + zceil[0]= FIXED2FLOAT(fch1); + zceil[1]= FIXED2FLOAT(fch2); + } + else + { + fch1 = fch2 = frontsector->ceilingplane.d; + zceil[0] = zceil[1] = FIXED2FLOAT(fch2); + } + + + if (seg->linedef->special==Line_Horizon) + { + SkyNormal(frontsector,v1,v2); + DoHorizon(seg,frontsector, v1,v2); + return; + } + + //return; + // [GZ] 3D middle textures are necessarily two-sided, even if they lack the explicit two-sided flag + if (!backsector || !(seg->linedef->flags&(ML_TWOSIDED|ML_3DMIDTEX))) // one sided + { + // sector's sky + SkyNormal(frontsector,v1,v2); + + // normal texture + gltexture=FMaterial::ValidateTexture(seg->sidedef->GetTexture(side_t::mid), true); + if (gltexture) + { + DoTexture(RENDERWALL_M1S,seg,(seg->linedef->flags & ML_DONTPEGBOTTOM)>0, + realfront->GetPlaneTexZ(sector_t::ceiling),realfront->GetPlaneTexZ(sector_t::floor), // must come from the original! + fch1,fch2,ffh1,ffh2,0); + } + } + else // two sided + { + + fixed_t bch1; + fixed_t bch2; + fixed_t bfh1; + fixed_t bfh2; + + if (backsector->floorplane.a | backsector->floorplane.b) + { + bfh1=backsector->floorplane.ZatPoint(v1); + bfh2=backsector->floorplane.ZatPoint(v2); + } + else + { + bfh1 = bfh2 = -backsector->floorplane.d; + } + + if (backsector->ceilingplane.a | backsector->ceilingplane.b) + { + bch1=backsector->ceilingplane.ZatPoint(v1); + bch2=backsector->ceilingplane.ZatPoint(v2); + } + else + { + bch1 = bch2 = backsector->ceilingplane.d; + } + + SkyTop(seg,frontsector,backsector,v1,v2); + SkyBottom(seg,frontsector,backsector,v1,v2); + + // upper texture + if (frontsector->GetTexture(sector_t::ceiling)!=skyflatnum || backsector->GetTexture(sector_t::ceiling)!=skyflatnum) + { + fixed_t bch1a=bch1, bch2a=bch2; + if (frontsector->GetTexture(sector_t::floor)!=skyflatnum || backsector->GetTexture(sector_t::floor)!=skyflatnum) + { + // the back sector's floor obstructs part of this wall + if (ffh1>bch1 && ffh2>bch2) + { + bch2a=ffh2; + bch1a=ffh1; + } + } + + if (bch1asidedef->GetTexture(side_t::top), true); + if (gltexture) + { + DoTexture(RENDERWALL_TOP,seg,(seg->linedef->flags & (ML_DONTPEGTOP))==0, + realfront->GetPlaneTexZ(sector_t::ceiling),realback->GetPlaneTexZ(sector_t::ceiling), + fch1,fch2,bch1a,bch2a,0); + } + else if ((frontsector->ceilingplane.a | frontsector->ceilingplane.b | + backsector->ceilingplane.a | backsector->ceilingplane.b) && + frontsector->GetTexture(sector_t::ceiling)!=skyflatnum && + backsector->GetTexture(sector_t::ceiling)!=skyflatnum) + { + gltexture=FMaterial::ValidateTexture(frontsector->GetTexture(sector_t::ceiling), true); + if (gltexture) + { + DoTexture(RENDERWALL_TOP,seg,(seg->linedef->flags & (ML_DONTPEGTOP))==0, + realfront->GetPlaneTexZ(sector_t::ceiling),realback->GetPlaneTexZ(sector_t::ceiling), + fch1,fch2,bch1a,bch2a,0); + } + } + else if (!(seg->sidedef->Flags & WALLF_POLYOBJ)) + { + gl_drawinfo->AddUpperMissingTexture(seg->sidedef, sub, bch1a); + } + } + } + + + /* mid texture */ + bool drawfogboundary = gl_CheckFog(frontsector, backsector); + FTexture *tex = TexMan(seg->sidedef->GetTexture(side_t::mid)); + if (tex != NULL) + { + if (i_compatflags & COMPATF_MASKEDMIDTEX) + { + tex = tex->GetRawTexture(); + } + gltexture=FMaterial::ValidateTexture(tex); + } + else gltexture = NULL; + + if (gltexture || drawfogboundary) + { + DoMidTexture(seg, drawfogboundary, frontsector, backsector, realfront, realback, + fch1, fch2, ffh1, ffh2, bch1, bch2, bfh1, bfh2); + } + + if (backsector->e->XFloor.ffloors.Size() || frontsector->e->XFloor.ffloors.Size()) + { + DoFFloorBlocks(seg,frontsector,backsector, fch1, fch2, ffh1, ffh2, bch1, bch2, bfh1, bfh2); + } + + /* bottom texture */ + // the back sector's ceiling obstructs part of this wall (specially important for sky sectors) + if (fch1ffh1 || bfh2>ffh2) + { + gltexture=FMaterial::ValidateTexture(seg->sidedef->GetTexture(side_t::bottom), true); + if (gltexture) + { + DoTexture(RENDERWALL_BOTTOM,seg,(seg->linedef->flags & ML_DONTPEGBOTTOM)>0, + realback->GetPlaneTexZ(sector_t::floor),realfront->GetPlaneTexZ(sector_t::floor), + bfh1,bfh2,ffh1,ffh2, + frontsector->GetTexture(sector_t::ceiling)==skyflatnum && backsector->GetTexture(sector_t::ceiling)==skyflatnum ? + realfront->GetPlaneTexZ(sector_t::floor)-realback->GetPlaneTexZ(sector_t::ceiling) : + realfront->GetPlaneTexZ(sector_t::floor)-realfront->GetPlaneTexZ(sector_t::ceiling)); + } + else if ((frontsector->floorplane.a | frontsector->floorplane.b | + backsector->floorplane.a | backsector->floorplane.b) && + frontsector->GetTexture(sector_t::floor)!=skyflatnum && + backsector->GetTexture(sector_t::floor)!=skyflatnum) + { + // render it anyway with the sector's floor texture. With a background sky + // there are ugly holes otherwise and slopes are simply not precise enough + // to mach in any case. + gltexture=FMaterial::ValidateTexture(frontsector->GetTexture(sector_t::floor), true); + if (gltexture) + { + DoTexture(RENDERWALL_BOTTOM,seg,(seg->linedef->flags & ML_DONTPEGBOTTOM)>0, + realback->GetPlaneTexZ(sector_t::floor),realfront->GetPlaneTexZ(sector_t::floor), + bfh1,bfh2,ffh1,ffh2, realfront->GetPlaneTexZ(sector_t::floor)-realfront->GetPlaneTexZ(sector_t::ceiling)); + } + } + else if (backsector->GetTexture(sector_t::floor)!=skyflatnum && + !(seg->sidedef->Flags & WALLF_POLYOBJ)) + { + gl_drawinfo->AddLowerMissingTexture(seg->sidedef, sub, bfh1); + } + } + } +} + +//========================================================================== +// +// +// +//========================================================================== +void GLWall::ProcessLowerMiniseg(seg_t *seg, sector_t * frontsector, sector_t * backsector) +{ + if (frontsector->GetTexture(sector_t::floor)==skyflatnum) return; + + fixed_t ffh = frontsector->GetPlaneTexZ(sector_t::floor); + fixed_t bfh = backsector->GetPlaneTexZ(sector_t::floor); + if (bfh>ffh) + { + this->seg = seg; + this->sub = NULL; + + vertex_t * v1=seg->v1; + vertex_t * v2=seg->v2; + vertexes[0]=v1; + vertexes[1]=v2; + + glseg.x1 = FIXED2FLOAT(v1->x); + glseg.y1 = FIXED2FLOAT(v1->y); + glseg.x2 = FIXED2FLOAT(v2->x); + glseg.y2 = FIXED2FLOAT(v2->y); + glseg.fracleft = 0; + glseg.fracright = 1; + + flags = (!gl_isBlack(Colormap.FadeColor) || level.flags&LEVEL_HASFADETABLE)? GLWF_FOGGY : 0; + + // can't do fake contrast without a sidedef + lightlevel = gl_ClampLight(frontsector->lightlevel); + rellight = 0; + + alpha = 1.0f; + RenderStyle = STYLE_Normal; + Colormap = frontsector->ColorMap; + + topflat = frontsector->GetTexture(sector_t::ceiling); // for glowing textures + bottomflat = frontsector->GetTexture(sector_t::floor); + + zfloor[0] = zfloor[1] = FIXED2FLOAT(ffh); + + gltexture = FMaterial::ValidateTexture(frontsector->GetTexture(sector_t::floor), true); + + if (gltexture) + { + FTexCoordInfo tci; + type=RENDERWALL_BOTTOM; + gltexture->GetTexCoordInfo(&tci, FRACUNIT, FRACUNIT); + SetWallCoordinates(seg, &tci, FIXED2FLOAT(bfh), bfh, bfh, ffh, ffh, 0); + PutWall(false); + } + } +} diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp new file mode 100644 index 000000000..1689909fc --- /dev/null +++ b/src/gl/scene/gl_walls_draw.cpp @@ -0,0 +1,592 @@ +/* +** gl_walls_draw.cpp +** Wall rendering +** +**--------------------------------------------------------------------------- +** Copyright 2000-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "p_local.h" +#include "p_lnspec.h" +#include "a_sharedglobal.h" +#include "gl/gl_functions.h" + +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/data/gl_data.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/dynlights/gl_glow.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/scene/gl_portal.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_templates.h" + +EXTERN_CVAR(Bool, gl_seamless) + +//========================================================================== +// +// Sets up the texture coordinates for one light to be rendered +// +//========================================================================== +bool GLWall::PrepareLight(texcoord * tcs, ADynamicLight * light) +{ + float vtx[]={glseg.x1,zbottom[0],glseg.y1, glseg.x1,ztop[0],glseg.y1, glseg.x2,ztop[1],glseg.y2, glseg.x2,zbottom[1],glseg.y2}; + Plane p; + Vector nearPt, up, right; + float scale; + + p.Init(vtx,4); + + if (!p.ValidNormal()) + { + return false; + } + + if (!gl_SetupLight(p, light, nearPt, up, right, scale, Colormap.colormap, true, !!(flags&GLWF_FOGGY))) + { + return false; + } + + if (tcs != NULL) + { + Vector t1; + int outcnt[4]={0,0,0,0}; + + for(int i=0;i<4;i++) + { + t1.Set(&vtx[i*3]); + Vector nearToVert = t1 - nearPt; + tcs[i].u = (nearToVert.Dot(right) * scale) + 0.5f; + tcs[i].v = (nearToVert.Dot(up) * scale) + 0.5f; + + // quick check whether the light touches this polygon + if (tcs[i].u<0) outcnt[0]++; + if (tcs[i].u>1) outcnt[1]++; + if (tcs[i].v<0) outcnt[2]++; + if (tcs[i].v>1) outcnt[3]++; + + } + // The light doesn't touch this polygon + if (outcnt[0]==4 || outcnt[1]==4 || outcnt[2]==4 || outcnt[3]==4) return false; + } + + draw_dlight++; + return true; +} + +//========================================================================== +// +// Collect lights for shader +// +//========================================================================== +FDynLightData lightdata; + +void GLWall::SetupLights() +{ + float vtx[]={glseg.x1,zbottom[0],glseg.y1, glseg.x1,ztop[0],glseg.y1, glseg.x2,ztop[1],glseg.y2, glseg.x2,zbottom[1],glseg.y2}; + Plane p; + + lightdata.Clear(); + p.Init(vtx,4); + + if (!p.ValidNormal()) + { + return; + } + for(int i=0;i<2;i++) + { + FLightNode *node; + if (seg->sidedef == NULL) + { + node = NULL; + } + else if (!(seg->sidedef->Flags & WALLF_POLYOBJ)) + { + node = seg->sidedef->lighthead[i]; + } + else if (sub) + { + // Polobject segs cannot be checked per sidedef so use the subsector instead. + node = sub->lighthead[i]; + } + else node = NULL; + + // Iterate through all dynamic lights which touch this wall and render them + while (node) + { + if (!(node->lightsource->flags2&MF2_DORMANT)) + { + iter_dlight++; + + Vector fn, pos; + + float x = FIXED2FLOAT(node->lightsource->x); + float y = FIXED2FLOAT(node->lightsource->y); + float z = FIXED2FLOAT(node->lightsource->z); + float dist = fabsf(p.DistToPoint(x, z, y)); + float radius = (node->lightsource->GetRadius() * gl_lights_size); + float scale = 1.0f / ((2.f * radius) - dist); + + if (radius > 0.f && dist < radius) + { + Vector nearPt, up, right; + + pos.Set(x,z,y); + fn=p.Normal(); + fn.GetRightUp(right, up); + + Vector tmpVec = fn * dist; + nearPt = pos + tmpVec; + + Vector t1; + int outcnt[4]={0,0,0,0}; + texcoord tcs[4]; + + // do a quick check whether the light touches this polygon + for(int i=0;i<4;i++) + { + t1.Set(&vtx[i*3]); + Vector nearToVert = t1 - nearPt; + tcs[i].u = (nearToVert.Dot(right) * scale) + 0.5f; + tcs[i].v = (nearToVert.Dot(up) * scale) + 0.5f; + + if (tcs[i].u<0) outcnt[0]++; + if (tcs[i].u>1) outcnt[1]++; + if (tcs[i].v<0) outcnt[2]++; + if (tcs[i].v>1) outcnt[3]++; + + } + if (outcnt[0]!=4 && outcnt[1]!=4 && outcnt[2]!=4 && outcnt[3]!=4) + { + gl_GetLight(p, node->lightsource, Colormap.colormap, true, false, lightdata); + } + } + } + node = node->nextLight; + } + } + int numlights[3]; + + lightdata.Combine(numlights, gl.MaxLights()); + if (numlights[2] > 0) + { + draw_dlight+=numlights[2]/2; + gl_RenderState.EnableLight(true); + gl_RenderState.SetLights(numlights, &lightdata.arrays[0][0]); + } +} + +//========================================================================== +// +// General purpose wall rendering function +// everything goes through here +// +//========================================================================== + +void GLWall::RenderWall(int textured, float * color2, ADynamicLight * light) +{ + texcoord tcs[4]; + bool glowing; + bool split = (gl_seamless && !(textured&4) && seg->sidedef != NULL && !(seg->sidedef->Flags & WALLF_POLYOBJ)); + + if (!light) + { + tcs[0]=lolft; + tcs[1]=uplft; + tcs[2]=uprgt; + tcs[3]=lorgt; + glowing = !!(flags&GLWF_GLOW) && (textured & 2); + } + else + { + if (!PrepareLight(tcs, light)) return; + glowing = false; + } + + if (glowing) gl_RenderState.SetGlowParams(topglowcolor, bottomglowcolor); + + gl_RenderState.Apply(); + + // the rest of the code is identical for textured rendering and lights + + gl.Begin(GL_TRIANGLE_FAN); + + // lower left corner + if (glowing) gl.VertexAttrib2f(VATTR_GLOWDISTANCE, zceil[0] - zbottom[0], zbottom[0] - zfloor[0]); + if (textured&1) gl.TexCoord2f(tcs[0].u,tcs[0].v); + gl.Vertex3f(glseg.x1,zbottom[0],glseg.y1); + + if (split && glseg.fracleft==0) SplitLeftEdge(tcs, glowing); + + // upper left corner + if (glowing) gl.VertexAttrib2f(VATTR_GLOWDISTANCE, zceil[0] - ztop[0], ztop[0] - zfloor[0]); + if (textured&1) gl.TexCoord2f(tcs[1].u,tcs[1].v); + gl.Vertex3f(glseg.x1,ztop[0],glseg.y1); + + if (split && !(flags & GLWF_NOSPLITUPPER)) SplitUpperEdge(tcs, glowing); + + // color for right side + if (color2) gl.Color4fv(color2); + + // upper right corner + if (glowing) gl.VertexAttrib2f(VATTR_GLOWDISTANCE, zceil[1] - ztop[1], ztop[1] - zfloor[1]); + if (textured&1) gl.TexCoord2f(tcs[2].u,tcs[2].v); + gl.Vertex3f(glseg.x2,ztop[1],glseg.y2); + + if (split && glseg.fracright==1) SplitRightEdge(tcs, glowing); + + // lower right corner + if (glowing) gl.VertexAttrib2f(VATTR_GLOWDISTANCE, zceil[1] - zbottom[1], zbottom[1] - zfloor[1]); + if (textured&1) gl.TexCoord2f(tcs[3].u,tcs[3].v); + gl.Vertex3f(glseg.x2,zbottom[1],glseg.y2); + + if (split && !(flags & GLWF_NOSPLITLOWER)) SplitLowerEdge(tcs, glowing); + + gl.End(); + + vertexcount+=4; + +} + +//========================================================================== +// +// +// +//========================================================================== + +void GLWall::RenderFogBoundary() +{ + if (gl_fogmode && gl_fixedcolormap == 0) + { + // with shaders this can be done properly + if (gl.shadermodel == 4 || (gl.shadermodel == 3 && gl_fog_shader)) + { + int rel = rellight + getExtraLight(); + gl_SetFog(lightlevel, rel, &Colormap, false); + gl_RenderState.SetEffect(EFF_FOGBOUNDARY); + gl_RenderState.EnableAlphaTest(false); + RenderWall(0, NULL); + gl_RenderState.EnableAlphaTest(true); + gl_RenderState.SetEffect(EFF_NONE); + } + else + { + // otherwise some approximation is needed. This won't look as good + // as the shader version but it's an acceptable compromise. + float fogdensity=gl_GetFogDensity(lightlevel, Colormap.FadeColor); + + float xcamera=FIXED2FLOAT(viewx); + float ycamera=FIXED2FLOAT(viewy); + + float dist1=Dist2(xcamera,ycamera, glseg.x1,glseg.y1); + float dist2=Dist2(xcamera,ycamera, glseg.x2,glseg.y2); + + + // these values were determined by trial and error and are scale dependent! + float fogd1=(0.95f-exp(-fogdensity*dist1/62500.f)) * 1.05f; + float fogd2=(0.95f-exp(-fogdensity*dist2/62500.f)) * 1.05f; + + gl_ModifyColor(Colormap.FadeColor.r, Colormap.FadeColor.g, Colormap.FadeColor.b, Colormap.colormap); + float fc[4]={Colormap.FadeColor.r/255.0f,Colormap.FadeColor.g/255.0f,Colormap.FadeColor.b/255.0f,fogd2}; + + gl_RenderState.EnableTexture(false); + gl_RenderState.EnableFog(false); + gl_RenderState.AlphaFunc(GL_GREATER,0); + gl.DepthFunc(GL_LEQUAL); + gl.Color4f(fc[0],fc[1],fc[2], fogd1); + if (glset.lightmode == 8) gl.VertexAttrib1f(VATTR_LIGHTLEVEL, 1.0); // Korshun. + + flags &= ~GLWF_GLOW; + RenderWall(4,fc); + + gl.DepthFunc(GL_LESS); + gl_RenderState.EnableFog(true); + gl_RenderState.AlphaFunc(GL_GEQUAL,0.5f); + gl_RenderState.EnableTexture(true); + } + } +} + + +//========================================================================== +// +// +// +//========================================================================== +void GLWall::RenderMirrorSurface() +{ + if (GLRenderer->mirrortexture == NULL) return; + + // For the sphere map effect we need a normal of the mirror surface, + Vector v(glseg.y2-glseg.y1, 0 ,-glseg.x2+glseg.x1); + v.Normalize(); + glNormal3fv(&v[0]); + + // Use sphere mapping for this + gl_RenderState.SetEffect(EFF_SPHEREMAP); + + gl_SetColor(lightlevel, 0, &Colormap ,0.1f); + gl_RenderState.BlendFunc(GL_SRC_ALPHA,GL_ONE); + gl_RenderState.AlphaFunc(GL_GREATER,0); + gl.DepthFunc(GL_LEQUAL); + gl_SetFog(lightlevel, getExtraLight(), &Colormap, true); + + FMaterial * pat=FMaterial::ValidateTexture(GLRenderer->mirrortexture); + pat->BindPatch(Colormap.colormap, 0); + + flags &= ~GLWF_GLOW; + //flags |= GLWF_NOSHADER; + RenderWall(0,NULL); + + gl_RenderState.SetEffect(EFF_NONE); + + // Restore the defaults for the translucent pass + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_RenderState.AlphaFunc(GL_GEQUAL,0.5f*gl_mask_sprite_threshold); + gl.DepthFunc(GL_LESS); + + // This is drawn in the translucent pass which is done after the decal pass + // As a result the decals have to be drawn here. + if (seg->sidedef->AttachedDecals) + { + gl.Enable(GL_POLYGON_OFFSET_FILL); + gl.PolygonOffset(-1.0f, -128.0f); + gl.DepthMask(false); + DoDrawDecals(); + gl.DepthMask(true); + gl.PolygonOffset(0.0f, 0.0f); + gl.Disable(GL_POLYGON_OFFSET_FILL); + gl_RenderState.SetTextureMode(TM_MODULATE); + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void GLWall::RenderTranslucentWall() +{ + bool transparent = gltexture? gltexture->GetTransparent() : false; + + // currently the only modes possible are solid, additive or translucent + // and until that changes I won't fix this code for the new blending modes! + bool isadditive = RenderStyle == STYLE_Add; + + if (!transparent) gl_RenderState.AlphaFunc(GL_GEQUAL,gl_mask_threshold*fabs(alpha)); + else gl_RenderState.EnableAlphaTest(false); + if (isadditive) gl_RenderState.BlendFunc(GL_SRC_ALPHA,GL_ONE); + + int extra; + if (gltexture) + { + if (flags&GLWF_FOGGY) gl_RenderState.EnableBrightmap(false); + gl_RenderState.EnableGlow(!!(flags & GLWF_GLOW)); + gltexture->Bind(Colormap.colormap, flags, 0); + extra = getExtraLight(); + } + else + { + gl_RenderState.EnableTexture(false); + extra = 0; + } + + gl_SetColor(lightlevel, extra, &Colormap, fabsf(alpha)); + if (type!=RENDERWALL_M2SNF) gl_SetFog(lightlevel, extra, &Colormap, isadditive); + else gl_SetFog(255, 0, NULL, false); + + RenderWall(5,NULL); + + // restore default settings + if (isadditive) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (transparent) gl_RenderState.EnableAlphaTest(true); + + if (!gltexture) + { + gl_RenderState.EnableTexture(true); + } + gl_RenderState.EnableBrightmap(true); + gl_RenderState.EnableGlow(false); +} + +//========================================================================== +// +// +// +//========================================================================== +void GLWall::Draw(int pass) +{ + FLightNode * node; + int rel; + +#ifdef _MSC_VER +#ifdef _DEBUG + if (seg->linedef-lines==879) + __asm nop +#endif +#endif + + + // This allows mid textures to be drawn on lines that might overlap a sky wall + if ((flags&GLWF_SKYHACK && type==RENDERWALL_M2S) || type == RENDERWALL_COLORLAYER) + { + if (pass != GLPASS_DECALS) + { + gl.Enable(GL_POLYGON_OFFSET_FILL); + gl.PolygonOffset(-1.0f, -128.0f); + } + } + + switch (pass) + { + case GLPASS_ALL: // Single-pass rendering + SetupLights(); + // fall through + case GLPASS_PLAIN: // Single-pass rendering + rel = rellight + getExtraLight(); + gl_SetColor(lightlevel, rel, &Colormap,1.0f); + if (type!=RENDERWALL_M2SNF) gl_SetFog(lightlevel, rel, &Colormap, false); + else gl_SetFog(255, 0, NULL, false); + + gl_RenderState.EnableGlow(!!(flags & GLWF_GLOW)); + gltexture->Bind(Colormap.colormap, flags, 0); + RenderWall(3, NULL); + gl_RenderState.EnableGlow(false); + gl_RenderState.EnableLight(false); + break; + + case GLPASS_BASE: // Base pass for non-masked polygons (all opaque geometry) + case GLPASS_BASE_MASKED: // Base pass for masked polygons (2sided mid-textures and transparent 3D floors) + rel = rellight + getExtraLight(); + gl_SetColor(lightlevel, rel, &Colormap,1.0f); + if (!(flags&GLWF_FOGGY)) + { + if (type!=RENDERWALL_M2SNF) gl_SetFog(lightlevel, rel, &Colormap, false); + else gl_SetFog(255, 0, NULL, false); + } + gl_RenderState.EnableGlow(!!(flags & GLWF_GLOW)); + // fall through + + if (pass != GLPASS_BASE) + { + gltexture->Bind(Colormap.colormap, flags, 0); + } + RenderWall(pass == GLPASS_BASE? 2:3, NULL); + gl_RenderState.EnableGlow(false); + gl_RenderState.EnableLight(false); + break; + + case GLPASS_TEXTURE: // modulated texture + gltexture->Bind(Colormap.colormap, flags, 0); + RenderWall(1, NULL); + break; + + case GLPASS_LIGHT: + case GLPASS_LIGHT_ADDITIVE: + // black fog is diminishing light and should affect lights less than the rest! + if (!(flags&GLWF_FOGGY)) gl_SetFog((255+lightlevel)>>1, 0, NULL, false); + else gl_SetFog(lightlevel, 0, &Colormap, true); + + if (seg->sidedef == NULL) + { + node = NULL; + } + else if (!(seg->sidedef->Flags & WALLF_POLYOBJ)) + { + // Iterate through all dynamic lights which touch this wall and render them + node = seg->sidedef->lighthead[pass==GLPASS_LIGHT_ADDITIVE]; + } + else if (sub) + { + // To avoid constant rechecking for polyobjects use the subsector's lightlist instead + node = sub->lighthead[pass==GLPASS_LIGHT_ADDITIVE]; + } + else node = NULL; + while (node) + { + if (!(node->lightsource->flags2&MF2_DORMANT)) + { + iter_dlight++; + RenderWall(1, NULL, node->lightsource); + } + node = node->nextLight; + } + break; + + case GLPASS_DECALS: + case GLPASS_DECALS_NOFOG: + if (seg->sidedef && seg->sidedef->AttachedDecals) + { + if (pass==GLPASS_DECALS) + { + gl_SetFog(lightlevel, rellight + getExtraLight(), &Colormap, false); + } + DoDrawDecals(); + } + break; + + case GLPASS_TRANSLUCENT: + switch (type) + { + case RENDERWALL_MIRRORSURFACE: + RenderMirrorSurface(); + break; + + case RENDERWALL_FOGBOUNDARY: + RenderFogBoundary(); + break; + + default: + RenderTranslucentWall(); + break; + } + } + + if ((flags&GLWF_SKYHACK && type==RENDERWALL_M2S) || type == RENDERWALL_COLORLAYER) + { + if (pass!=GLPASS_DECALS) + { + gl.Disable(GL_POLYGON_OFFSET_FILL); + gl.PolygonOffset(0, 0); + } + } +} diff --git a/src/gl/scene/gl_weapon.cpp b/src/gl/scene/gl_weapon.cpp new file mode 100644 index 000000000..f9f1a823b --- /dev/null +++ b/src/gl/scene/gl_weapon.cpp @@ -0,0 +1,407 @@ +/* +** gl_weapon.cpp +** Weapon sprite drawing +** +**--------------------------------------------------------------------------- +** Copyright 2002-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "sbar.h" +#include "r_utility.h" +#include "v_video.h" +#include "doomstat.h" +#include "d_player.h" +#include "g_level.h" + +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/data/gl_data.h" +#include "gl/dynlights/gl_glow.h" +#include "gl/scene/gl_drawinfo.h" +#include "gl/models/gl_models.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_material.h" + +EXTERN_CVAR (Bool, r_drawplayersprites) +EXTERN_CVAR(Float, transsouls) +EXTERN_CVAR (Bool, st_scale) +EXTERN_CVAR(Int, gl_fuzztype) + + +//========================================================================== +// +// R_DrawPSprite +// +//========================================================================== + +void FGLRenderer::DrawPSprite (player_t * player,pspdef_t *psp,fixed_t sx, fixed_t sy, int cm_index, bool hudModelStep, int OverrideShader) +{ + float fU1,fV1; + float fU2,fV2; + fixed_t tx; + int x1,y1,x2,y2; + float scale; + fixed_t scalex; + fixed_t texturemid;// 4:3 16:9 16:10 17:10 5:4 + static fixed_t xratio[] = {FRACUNIT, FRACUNIT*3/4, FRACUNIT*5/6, FRACUNIT*40/51, FRACUNIT}; + + // [BB] In the HUD model step we just render the model and break out. + if ( hudModelStep ) + { + gl_RenderHUDModel( psp, sx, sy, cm_index ); + return; + } + + // decide which patch to use + bool mirror; + FTextureID lump = gl_GetSpriteFrame(psp->sprite, psp->frame, 0, 0, &mirror); + if (!lump.isValid()) return; + + FMaterial * tex = FMaterial::ValidateTexture(lump, false); + if (!tex) return; + + tex->BindPatch(cm_index, 0, OverrideShader); + + int vw = viewwidth; + int vh = viewheight; + + // calculate edges of the shape + scalex = xratio[WidescreenRatio] * vw / 320; + + tx = sx - ((160 + tex->GetScaledLeftOffset(GLUSE_PATCH))<>FRACBITS) + (vw>>1); + if (x1 > vw) return; // off the right side + x1+=viewwindowx; + + tx += tex->TextureWidth(GLUSE_PATCH) << FRACBITS; + x2 = (FixedMul(tx, scalex)>>FRACBITS) + (vw>>1); + if (x2 < 0) return; // off the left side + x2+=viewwindowx; + + // killough 12/98: fix psprite positioning problem + texturemid = (100<GetScaledTopOffset(GLUSE_PATCH)<ReadyWeapon; + if (wi && wi->YAdjust) + { + if (screenblocks>=11) + { + texturemid -= wi->YAdjust; + } + else if (!st_scale) + { + texturemid -= FixedMul (StatusBar->GetDisplacement (), wi->YAdjust); + } + } + + scale = ((SCREENHEIGHT*vw)/SCREENWIDTH) / 200.0f; + y1 = viewwindowy + (vh >> 1) - (int)(((float)texturemid / (float)FRACUNIT) * scale); + y2 = y1 + (int)((float)tex->TextureHeight(GLUSE_PATCH) * scale) + 1; + + if (!mirror) + { + fU1=tex->GetUL(); + fV1=tex->GetVT(); + fU2=tex->GetUR(); + fV2=tex->GetVB(); + } + else + { + fU2=tex->GetUL(); + fV1=tex->GetVT(); + fU1=tex->GetUR(); + fV2=tex->GetVB(); + } + + if (tex->GetTransparent() || OverrideShader != 0) + { + gl_RenderState.EnableAlphaTest(false); + } + gl_RenderState.Apply(); + gl.Begin(GL_TRIANGLE_STRIP); + gl.TexCoord2f(fU1, fV1); gl.Vertex2f(x1,y1); + gl.TexCoord2f(fU1, fV2); gl.Vertex2f(x1,y2); + gl.TexCoord2f(fU2, fV1); gl.Vertex2f(x2,y1); + gl.TexCoord2f(fU2, fV2); gl.Vertex2f(x2,y2); + gl.End(); + if (tex->GetTransparent() || OverrideShader != 0) + { + gl_RenderState.EnableAlphaTest(true); + } +} + +//========================================================================== +// +// R_DrawPlayerSprites +// +//========================================================================== + +EXTERN_CVAR(Bool, gl_brightfog) + +void FGLRenderer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep) +{ + bool statebright[2] = {false, false}; + unsigned int i; + pspdef_t *psp; + int lightlevel=0; + fixed_t ofsx, ofsy; + FColormap cm; + sector_t * fakesec, fs; + AActor * playermo=players[consoleplayer].camera; + player_t * player=playermo->player; + + if(!player || playermo->renderflags&RF_INVISIBLE || !r_drawplayersprites || + mViewActor!=playermo || playermo->RenderStyle.BlendOp == STYLEOP_None) return; + + P_BobWeapon (player, &player->psprites[ps_weapon], &ofsx, &ofsy); + + // check for fullbright + if (player->fixedcolormap==NOFIXEDCOLORMAP) + { + for (i=0, psp=player->psprites; i<=ps_flash; i++,psp++) + if (psp->state != NULL) + { + bool disablefullbright = false; + FTextureID lump = gl_GetSpriteFrame(psp->sprite, psp->frame, 0, 0, NULL); + if (lump.isValid() && gl_BrightmapsActive()) + { + FMaterial * tex=FMaterial::ValidateTexture(lump, false); + if (tex) + disablefullbright = tex->tex->gl_info.bBrightmapDisablesFullbright; + } + statebright[i] = !!psp->state->GetFullbright() && !disablefullbright; + } + + } + + if (gl_fixedcolormap) + { + lightlevel=255; + cm.GetFixedColormap(); + statebright[0] = statebright[1] = true; + fakesec = viewsector; + } + else + { + fakesec = gl_FakeFlat(viewsector, &fs, false); + + // calculate light level for weapon sprites + lightlevel = gl_ClampLight(fakesec->lightlevel); + if (glset.lightmode == 8) + { + lightlevel = gl_CalcLightLevel(lightlevel, getExtraLight(), true); + + // Korshun: the way based on max possible light level for sector like in software renderer. + float min_L = 36.0/31.0 - ((lightlevel/255.0) * (63.0/31.0)); // Lightlevel in range 0-63 + if (min_L < 0) + min_L = 0; + else if (min_L > 1.0) + min_L = 1.0; + + lightlevel = (1.0 - min_L) * 255; + } + lightlevel = gl_CheckSpriteGlow(viewsector, lightlevel, playermo->x, playermo->y, playermo->z); + + // calculate colormap for weapon sprites + if (viewsector->e->XFloor.ffloors.Size() && !glset.nocoloredspritelighting) + { + TArray & lightlist = viewsector->e->XFloor.lightlist; + for(i=0;ifloorplane.ZatPoint(viewx,viewy); + } + + if (lightbottomviewz) + { + cm = lightlist[i].extra_colormap; + lightlevel = *lightlist[i].p_lightlevel; + break; + } + } + } + else + { + cm=fakesec->ColorMap; + if (glset.nocoloredspritelighting) cm.ClearColor(); + } + } + + + // Korshun: fullbright fog in opengl, render weapon sprites fullbright. + if (glset.brightfog && ((level.flags&LEVEL_HASFADETABLE) || cm.FadeColor != 0)) + { + lightlevel = 255; + statebright[0] = statebright[1] = true; + } + + PalEntry ThingColor = playermo->fillcolor; + visstyle_t vis; + + vis.RenderStyle=playermo->RenderStyle; + vis.alpha=playermo->alpha; + vis.colormap = NULL; + if (playermo->Inventory) + { + playermo->Inventory->AlterWeaponSprite(&vis); + if (vis.colormap >= SpecialColormaps[0].Colormap && + vis.colormap < SpecialColormaps[SpecialColormaps.Size()].Colormap && + cm.colormap == CM_DEFAULT) + { + ptrdiff_t specialmap = (vis.colormap - SpecialColormaps[0].Colormap) / sizeof(FSpecialColormap); + cm.colormap = int(CM_FIRSTSPECIALCOLORMAP + specialmap); + } + } + + // Set the render parameters + + int OverrideShader = 0; + float trans = 0.f; + if (vis.RenderStyle.BlendOp >= STYLEOP_Fuzz && vis.RenderStyle.BlendOp <= STYLEOP_FuzzOrRevSub) + { + vis.RenderStyle.CheckFuzz(); + if (vis.RenderStyle.BlendOp == STYLEOP_Fuzz) + { + if (gl.shadermodel >= 4 && gl_fuzztype != 0) + { + // Todo: implement shader selection here + vis.RenderStyle = LegacyRenderStyles[STYLE_Translucent]; + OverrideShader = gl_fuzztype + 4; + trans = 0.99f; // trans may not be 1 here + } + else + { + vis.RenderStyle.BlendOp = STYLEOP_Shadow; + } + } + statebright[0] = statebright[1] = false; + } + + gl_SetRenderStyle(vis.RenderStyle, false, false); + + if (vis.RenderStyle.Flags & STYLEF_TransSoulsAlpha) + { + trans = transsouls; + } + else if (vis.RenderStyle.Flags & STYLEF_Alpha1) + { + trans = 1.f; + } + else if (trans == 0.f) + { + trans = FIXED2FLOAT(vis.alpha); + } + + // now draw the different layers of the weapon + gl_RenderState.EnableBrightmap(true); + if (statebright[0] || statebright[1]) + { + // brighten the weapon to reduce the difference between + // normal sprite and fullbright flash. + if (glset.lightmode != 8) lightlevel = (2*lightlevel+255)/3; + } + + // hack alert! Rather than changing everything in the underlying lighting code let's just temporarily change + // light mode here to draw the weapon sprite. + int oldlightmode = glset.lightmode; + if (glset.lightmode == 8) glset.lightmode = 2; + + for (i=0, psp=player->psprites; i<=ps_flash; i++,psp++) + { + if (psp->state) + { + FColormap cmc = cm; + if (statebright[i]) + { + if (fakesec == viewsector || in_area != area_below) + // under water areas keep most of their color for fullbright objects + { + cmc.LightColor.r= + cmc.LightColor.g= + cmc.LightColor.b=0xff; + } + else + { + cmc.LightColor.r = (3*cmc.LightColor.r + 0xff)/4; + cmc.LightColor.g = (3*cmc.LightColor.g + 0xff)/4; + cmc.LightColor.b = (3*cmc.LightColor.b + 0xff)/4; + } + } + // set the lighting parameters (only calls glColor and glAlphaFunc) + gl_SetSpriteLighting(vis.RenderStyle, playermo, statebright[i]? 255 : lightlevel, + 0, &cmc, 0xffffff, trans, statebright[i], true); + DrawPSprite (player,psp,psp->sx+ofsx, psp->sy+ofsy, cm.colormap, hudModelStep, OverrideShader); + } + } + gl_RenderState.EnableBrightmap(false); + glset.lightmode = oldlightmode; +} + +//========================================================================== +// +// R_DrawPlayerSprites +// +//========================================================================== + +void FGLRenderer::DrawTargeterSprites() +{ + int i; + pspdef_t *psp; + AActor * playermo=players[consoleplayer].camera; + player_t * player=playermo->player; + + if(!player || playermo->renderflags&RF_INVISIBLE || !r_drawplayersprites || + mViewActor!=playermo) return; + + gl_RenderState.EnableBrightmap(false); + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_RenderState.AlphaFunc(GL_GEQUAL,gl_mask_sprite_threshold); + gl_RenderState.BlendEquation(GL_FUNC_ADD); + gl.Color3f(1.0f,1.0f,1.0f); + gl_RenderState.SetTextureMode(TM_MODULATE); + + // The Targeter's sprites are always drawn normally. + for (i=ps_targetcenter, psp = &player->psprites[ps_targetcenter]; istate) DrawPSprite (player,psp,psp->sx, psp->sy, CM_DEFAULT, false, 0); +} \ No newline at end of file diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp new file mode 100644 index 000000000..4a3c7ca94 --- /dev/null +++ b/src/gl/shaders/gl_shader.cpp @@ -0,0 +1,679 @@ +/* +** gl_shader.cpp +** +** GLSL shader handling +** +**--------------------------------------------------------------------------- +** Copyright 2004-2009 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "c_cvars.h" +#include "v_video.h" +#include "name.h" +#include "w_wad.h" +#include "i_system.h" +#include "doomerrors.h" +#include "v_palette.h" +#include "sc_man.h" +#include "cmdlib.h" + +#include "gl/data/gl_data.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/system/gl_cvars.h" +#include "gl/shaders/gl_shader.h" +#include "gl/textures/gl_material.h" + +// these will only have an effect on SM3 cards. +// For SM4 they are always on and for SM2 always off +CVAR(Bool, gl_warp_shader, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +CVAR(Bool, gl_fog_shader, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +CVAR(Bool, gl_colormap_shader, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +CVAR(Bool, gl_brightmap_shader, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +CVAR(Bool, gl_glow_shader, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) + + +extern long gl_frameMS; + +//========================================================================== +// +// +// +//========================================================================== + +bool FShader::Load(const char * name, const char * vert_prog_lump, const char * frag_prog_lump, const char * proc_prog_lump, const char * defines) +{ + static char buffer[10000]; + FString error; + + if (gl.shadermodel > 0) + { + int vp_lump = Wads.CheckNumForFullName(vert_prog_lump); + if (vp_lump == -1) I_Error("Unable to load '%s'", vert_prog_lump); + FMemLump vp_data = Wads.ReadLump(vp_lump); + + int fp_lump = Wads.CheckNumForFullName(frag_prog_lump); + if (fp_lump == -1) I_Error("Unable to load '%s'", frag_prog_lump); + FMemLump fp_data = Wads.ReadLump(fp_lump); + + + FString vp_comb; + FString fp_comb; + vp_comb = defines; + if (gl.shadermodel < 4) + { + vp_comb << "#define NO_SM4\n"; + } + + fp_comb = vp_comb; + // This uses GetChars on the strings to get rid of terminating 0 characters. + vp_comb << vp_data.GetString().GetChars() << "\n"; + fp_comb << fp_data.GetString().GetChars() << "\n"; + + if (proc_prog_lump != NULL) + { + if (*proc_prog_lump != '#') + { + int pp_lump = Wads.CheckNumForFullName(proc_prog_lump); + if (pp_lump == -1) I_Error("Unable to load '%s'", proc_prog_lump); + FMemLump pp_data = Wads.ReadLump(pp_lump); + + fp_comb << pp_data.GetString().GetChars(); + } + else + { + // Proc_prog_lump is not a lump name but the source itself (from generated shaders) + fp_comb << proc_prog_lump+1; + } + } + + hVertProg = gl.CreateShader(GL_VERTEX_SHADER); + hFragProg = gl.CreateShader(GL_FRAGMENT_SHADER); + + + int vp_size = (int)vp_comb.Len(); + int fp_size = (int)fp_comb.Len(); + + const char *vp_ptr = vp_comb.GetChars(); + const char *fp_ptr = fp_comb.GetChars(); + + gl.ShaderSource(hVertProg, 1, &vp_ptr, &vp_size); + gl.ShaderSource(hFragProg, 1, &fp_ptr, &fp_size); + + gl.CompileShader(hVertProg); + gl.CompileShader(hFragProg); + + hShader = gl.CreateProgram(); + + gl.AttachShader(hShader, hVertProg); + gl.AttachShader(hShader, hFragProg); + + gl.BindAttribLocation(hShader, VATTR_GLOWDISTANCE, "glowdistance"); + gl.BindAttribLocation(hShader, VATTR_FOGPARAMS, "fogparams"); + gl.BindAttribLocation(hShader, VATTR_LIGHTLEVEL, "lightlevel_in"); // Korshun. + + gl.LinkProgram(hShader); + + gl.GetShaderInfoLog(hVertProg, 10000, NULL, buffer); + if (*buffer) + { + error << "Vertex shader:\n" << buffer << "\n"; + } + gl.GetShaderInfoLog(hFragProg, 10000, NULL, buffer); + if (*buffer) + { + error << "Vertex shader:\n" << buffer << "\n"; + } + + gl.GetProgramInfoLog(hShader, 10000, NULL, buffer); + if (*buffer) + { + error << "Linking:\n" << buffer << "\n"; + } + int linked; + gl.GetObjectParameteriv(hShader, GL_LINK_STATUS, &linked); + if (linked == 0) + { + // only print message if there's an error. + Printf("Init Shader '%s':\n%s\n", name, error.GetChars()); + } + timer_index = gl.GetUniformLocation(hShader, "timer"); + desaturation_index = gl.GetUniformLocation(hShader, "desaturation_factor"); + fogenabled_index = gl.GetUniformLocation(hShader, "fogenabled"); + texturemode_index = gl.GetUniformLocation(hShader, "texturemode"); + camerapos_index = gl.GetUniformLocation(hShader, "camerapos"); + lightparms_index = gl.GetUniformLocation(hShader, "lightparms"); + colormapstart_index = gl.GetUniformLocation(hShader, "colormapstart"); + colormaprange_index = gl.GetUniformLocation(hShader, "colormaprange"); + lightrange_index = gl.GetUniformLocation(hShader, "lightrange"); + fogcolor_index = gl.GetUniformLocation(hShader, "fogcolor"); + lights_index = gl.GetUniformLocation(hShader, "lights"); + dlightcolor_index = gl.GetUniformLocation(hShader, "dlightcolor"); + + glowbottomcolor_index = gl.GetUniformLocation(hShader, "bottomglowcolor"); + glowtopcolor_index = gl.GetUniformLocation(hShader, "topglowcolor"); + + gl.UseProgram(hShader); + + int texture_index = gl.GetUniformLocation(hShader, "texture2"); + if (texture_index > 0) gl.Uniform1i(texture_index, 1); + + gl.UseProgram(0); + return !!linked; + } + return false; +} + +//========================================================================== +// +// +// +//========================================================================== + +FShader::~FShader() +{ + gl.DeleteProgram(hShader); + gl.DeleteShader(hVertProg); + gl.DeleteShader(hFragProg); +} + + +//========================================================================== +// +// +// +//========================================================================== + +bool FShader::Bind(float Speed) +{ + GLRenderer->mShaderManager->SetActiveShader(this); + if (timer_index >=0 && Speed > 0.f) gl.Uniform1f(timer_index, gl_frameMS*Speed/1000.f); + return true; +} + +//========================================================================== +// +// +// +//========================================================================== + +FShaderContainer::FShaderContainer(const char *ShaderName, const char *ShaderPath) +{ + const char * shaderdefines[] = { + "#define NO_GLOW\n#define NO_DESATURATE\n", + "#define NO_DESATURATE\n", + "#define NO_GLOW\n", + "\n", + "#define NO_GLOW\n#define NO_DESATURATE\n#define DYNLIGHT\n", + "#define NO_DESATURATE\n#define DYNLIGHT\n", + "#define NO_GLOW\n#define DYNLIGHT\n", + "\n#define DYNLIGHT\n", + "#define NO_GLOW\n#define NO_DESATURATE\n#define SOFTLIGHT\n", + "#define NO_DESATURATE\n#define SOFTLIGHT\n", + "#define NO_GLOW\n#define SOFTLIGHT\n", + "\n#define SOFTLIGHT\n", + "#define NO_GLOW\n#define NO_DESATURATE\n#define DYNLIGHT\n#define SOFTLIGHT\n", + "#define NO_DESATURATE\n#define DYNLIGHT\n#define SOFTLIGHT\n", + "#define NO_GLOW\n#define DYNLIGHT\n#define SOFTLIGHT\n", + "\n#define DYNLIGHT\n#define SOFTLIGHT\n" + }; + + const char * shaderdesc[] = { + "::default", + "::glow", + "::desaturate", + "::glow+desaturate", + "::default+dynlight", + "::glow+dynlight", + "::desaturate+dynlight", + "::glow+desaturate+dynlight", + "::softlight", + "::glow+softlight", + "::desaturate+softlight", + "::glow+desaturate+softlight", + "::default+dynlight+softlight", + "::glow+dynlight+softlight", + "::desaturate+dynlight+softlight", + "::glow+desaturate+dynlight+softlight", + }; + + FString name; + + name << ShaderName << "::colormap"; + + try + { + shader_cm = new FShader; + if (!shader_cm->Load(name, "shaders/glsl/main.vp", "shaders/glsl/main_colormap.fp", ShaderPath, "#define NO_FOG\n#define NO_GLOW\n")) + { + delete shader_cm; + shader_cm = NULL; + } + } + catch(CRecoverableError &err) + { + shader_cm = NULL; + I_Error("Unable to load shader %s:\n%s\n", name.GetChars(), err.GetMessage()); + } + + if (gl.shadermodel > 2) + { + for(int i = 0;i < NUM_SHADERS; i++) + { + FString name; + + name << ShaderName << shaderdesc[i]; + + try + { + FString str; + if ((i&4) != 0) + { + if (gl.maxuniforms < 1024 || gl.shadermodel != 4) + { + shader[i] = NULL; + continue; + } + // this can't be in the shader code due to ATI strangeness. + str = "#version 120\n#extension GL_EXT_gpu_shader4 : enable\n"; + if (gl.MaxLights() == 128) str += "#define MAXLIGHTS128\n"; + } + if ((i&8) == 0) + { + if (gl.shadermodel != 4) + { + shader[i] = NULL; + continue; + } + } + str += shaderdefines[i]; + shader[i] = new FShader; + if (!shader[i]->Load(name, "shaders/glsl/main.vp", "shaders/glsl/main.fp", ShaderPath, str.GetChars())) + { + delete shader[i]; + shader[i] = NULL; + } + } + catch(CRecoverableError &err) + { + shader[i] = NULL; + I_Error("Unable to load shader %s:\n%s\n", name.GetChars(), err.GetMessage()); + } + } + } + else memset(shader, 0, sizeof(shader)); +} + +//========================================================================== +// +// +// +//========================================================================== +FShaderContainer::~FShaderContainer() +{ + delete shader_cm; + for(int i = 0;i < NUM_SHADERS; i++) + { + if (shader[i] != NULL) + { + delete shader[i]; + shader[i] = NULL; + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +FShader *FShaderContainer::Bind(int cm, bool glowing, float Speed, bool lights) +{ + FShader *sh=NULL; + + if (cm >= CM_FIRSTSPECIALCOLORMAP && cm < CM_MAXCOLORMAP) + { + // these are never used with any kind of lighting or fog + sh = shader_cm; + // [BB] If there was a problem when loading the shader, sh is NULL here. + if( sh ) + { + FSpecialColormap *map = &SpecialColormaps[cm - CM_FIRSTSPECIALCOLORMAP]; + sh->Bind(Speed); + float m[3]= {map->ColorizeEnd[0] - map->ColorizeStart[0], + map->ColorizeEnd[1] - map->ColorizeStart[1], map->ColorizeEnd[2] - map->ColorizeStart[2]}; + + gl.Uniform3fv(sh->colormapstart_index, 1, map->ColorizeStart); + gl.Uniform3fv(sh->colormaprange_index, 1, m); + } + } + else + { + bool desat = cm>=CM_DESAT1 && cm<=CM_DESAT31; + sh = shader[glowing + 2*desat + 4*lights + (glset.lightmode & 8)]; + // [BB] If there was a problem when loading the shader, sh is NULL here. + if( sh ) + { + sh->Bind(Speed); + if (desat) + { + gl.Uniform1f(sh->desaturation_index, 1.f-float(cm-CM_DESAT0)/(CM_DESAT31-CM_DESAT0)); + } + } + } + return sh; +} + + +//========================================================================== +// +// +// +//========================================================================== +struct FDefaultShader +{ + const char * ShaderName; + const char * gettexelfunc; +}; + +// Note: the FIRST_USER_SHADER constant in gl_shader.h needs +// to be updated whenever the size of this array is modified. +static const FDefaultShader defaultshaders[]= +{ + {"Default", "shaders/glsl/func_normal.fp"}, + {"Warp 1", "shaders/glsl/func_warp1.fp"}, + {"Warp 2", "shaders/glsl/func_warp2.fp"}, + {"Brightmap","shaders/glsl/func_brightmap.fp"}, + {"No Texture", "shaders/glsl/func_notexture.fp"}, + {"Basic Fuzz", "shaders/glsl/fuzz_standard.fp"}, + {"Smooth Fuzz", "shaders/glsl/fuzz_smooth.fp"}, + {"Swirly Fuzz", "shaders/glsl/fuzz_swirly.fp"}, + {"Translucent Fuzz", "shaders/glsl/fuzz_smoothtranslucent.fp"}, + {"Jagged Fuzz", "shaders/glsl/fuzz_jagged.fp"}, + {"Noise Fuzz", "shaders/glsl/fuzz_noise.fp"}, + {"Smooth Noise Fuzz", "shaders/glsl/fuzz_smoothnoise.fp"}, + {NULL,NULL} +}; + +static TArray usershaders; + +struct FEffectShader +{ + const char *ShaderName; + const char *vp; + const char *fp1; + const char *fp2; + const char *defines; +}; + +static const FEffectShader effectshaders[]= +{ + {"fogboundary", "shaders/glsl/main.vp", "shaders/glsl/fogboundary.fp", NULL, "#define NO_GLOW\n"}, + {"spheremap", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "#define NO_GLOW\n#define NO_DESATURATE\n#define SPHEREMAP\n#define SPHEREMAP_0\n"} +}; + + +//========================================================================== +// +// +// +//========================================================================== + +FShaderManager::FShaderManager() +{ + CompileShaders(); +} + +//========================================================================== +// +// +// +//========================================================================== + +FShaderManager::~FShaderManager() +{ + Clean(); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FShaderManager::Recompile() +{ + Clean(); + CompileShaders(); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FShaderManager::CompileShaders() +{ + mActiveShader = mEffectShaders[0] = mEffectShaders[1] = NULL; + if (gl.shadermodel > 0) + { + for(int i=0;defaultshaders[i].ShaderName != NULL;i++) + { + FShaderContainer * shc = new FShaderContainer(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc); + mTextureEffects.Push(shc); + if (gl.shadermodel <= 2) return; // SM2 will only initialize the default shader + } + + for(unsigned i = 0; i < usershaders.Size(); i++) + { + FString name = ExtractFileBase(usershaders[i]); + FName sfn = name; + + if (gl.shadermodel > 2) + { + FShaderContainer * shc = new FShaderContainer(sfn, usershaders[i]); + mTextureEffects.Push(shc); + } + } + + if (gl.shadermodel > 2) + { + for(int i=0;iLoad(effectshaders[i].ShaderName, effectshaders[i].vp, effectshaders[i].fp1, + effectshaders[i].fp2, effectshaders[i].defines)) + { + delete eff; + } + else mEffectShaders[i] = eff; + } + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FShaderManager::Clean() +{ + SetActiveShader(NULL); + for(unsigned int i=0;iName == sfn) + { + return i; + } + } + return -1; +} + +//========================================================================== +// +// +// +//========================================================================== + +void FShaderManager::SetActiveShader(FShader *sh) +{ + // shadermodel needs to be tested here because without it UseProgram will be NULL. + if (gl.shadermodel > 0 && mActiveShader != sh) + { + gl.UseProgram(sh == NULL? 0 : sh->GetHandle()); + mActiveShader = sh; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +FShader *FShaderManager::BindEffect(int effect) +{ + if (effect > 0 && effect <= NUM_EFFECTS && mEffectShaders[effect-1] != NULL) + { + mEffectShaders[effect-1]->Bind(0); + return mEffectShaders[effect-1]; + } + return NULL; +} + + +//========================================================================== +// +// +// +//========================================================================== + +void gl_DestroyUserShaders() +{ + // todo +} + +//========================================================================== +// +// Parses a shader definition +// +//========================================================================== + +void gl_ParseHardwareShader(FScanner &sc, int deflump) +{ + int type = FTexture::TEX_Any; + bool disable_fullbright=false; + bool thiswad = false; + bool iwad = false; + int maplump = -1; + FString maplumpname; + float speed = 1.f; + + sc.MustGetString(); + if (sc.Compare("texture")) type = FTexture::TEX_Wall; + else if (sc.Compare("flat")) type = FTexture::TEX_Flat; + else if (sc.Compare("sprite")) type = FTexture::TEX_Sprite; + else sc.UnGet(); + + sc.MustGetString(); + FTextureID no = TexMan.CheckForTexture(sc.String, type); + FTexture *tex = TexMan[no]; + + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) + { + sc.MustGetString(); + if (sc.Compare("shader")) + { + sc.MustGetString(); + maplumpname = sc.String; + } + else if (sc.Compare("speed")) + { + sc.MustGetFloat(); + speed = float(sc.Float); + } + } + if (!tex) + { + return; + } + + if (maplumpname.IsNotEmpty()) + { + if (tex->bWarped != 0) + { + Printf("Cannot combine warping with hardware shader on texture '%s'\n", tex->Name); + return; + } + tex->gl_info.shaderspeed = speed; + for(unsigned i=0;igl_info.shaderindex = i + FIRST_USER_SHADER; + return; + } + } + tex->gl_info.shaderindex = usershaders.Push(maplumpname) + FIRST_USER_SHADER; + } +} + diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h new file mode 100644 index 000000000..b43e0aec4 --- /dev/null +++ b/src/gl/shaders/gl_shader.h @@ -0,0 +1,160 @@ + +#ifndef __GL_SHADERS_H__ +#define __GL_SHADERS_H__ + +#include "gl/renderer/gl_renderstate.h" +#include "name.h" + +extern bool gl_shaderactive; + +const int VATTR_GLOWDISTANCE = 15; +const int VATTR_FOGPARAMS = 14; +const int VATTR_LIGHTLEVEL = 13; // Korshun. + +//========================================================================== +// +// +//========================================================================== + +class FShader +{ + friend class FShaderContainer; + friend class FRenderState; + + unsigned int hShader; + unsigned int hVertProg; + unsigned int hFragProg; + + int timer_index; + int desaturation_index; + int fogenabled_index; + int texturemode_index; + int camerapos_index; + int lightparms_index; + int colormapstart_index; + int colormaprange_index; + int lightrange_index; + int fogcolor_index; + int lights_index; + int dlightcolor_index; + + int glowbottomcolor_index; + int glowtopcolor_index; + + int currentfogenabled; + int currenttexturemode; + float currentlightfactor; + float currentlightdist; + + PalEntry currentfogcolor; + float currentfogdensity; + + FStateVec3 currentcamerapos; + +public: + FShader() + { + hShader = hVertProg = hFragProg = 0; + currentfogenabled = currenttexturemode = 0; + currentlightfactor = currentlightdist = 0.0f; + currentfogdensity = -1; + currentfogcolor = 0; + + timer_index = -1; + desaturation_index = -1; + fogenabled_index = -1; + texturemode_index = -1; + camerapos_index = -1; + lightparms_index = -1; + colormapstart_index = -1; + colormaprange_index = -1; + lightrange_index = -1; + fogcolor_index = -1; + lights_index = -1; + dlightcolor_index = -1; + + } + + ~FShader(); + + bool Load(const char * name, const char * vert_prog_lump, const char * fragprog, const char * fragprog2, const char *defines); + + void SetColormapColor(float r, float g, float b, float r1, float g1, float b1); + void SetGlowParams(float *topcolors, float topheight, float *bottomcolors, float bottomheight); + void SetLightRange(int start, int end, int forceadd); + + bool Bind(float Speed); + unsigned int GetHandle() const { return hShader; } + +}; + +//========================================================================== +// +// This class contains the shaders for the different lighting modes +// that are required (e.g. special colormaps etc.) +// +//========================================================================== + +class FShaderContainer +{ + friend class FShaderManager; + + FName Name; + + enum { NUM_SHADERS = 16 }; + + FShader *shader[NUM_SHADERS]; + FShader *shader_cm; // the shader for fullscreen colormaps + +public: + FShaderContainer(const char *ShaderName, const char *ShaderPath); + ~FShaderContainer(); + FShader *Bind(int cm, bool glowing, float Speed, bool lights); + +}; + + +//========================================================================== +// +// The global shader manager +// +//========================================================================== +class FShaderManager +{ + enum + { + NUM_EFFECTS = 2 + }; + + TArray mTextureEffects; + FShader *mActiveShader; + FShader *mEffectShaders[NUM_EFFECTS]; + + void Clean(); + void CompileShaders(); + +public: + FShaderManager(); + ~FShaderManager(); + int Find(const char *mame); + FShader *BindEffect(int effect); + void SetActiveShader(FShader *sh); + + FShaderContainer *Get(unsigned int eff) + { + // indices 0-2 match the warping modes, 3 is brightmap, 4 no texture, the following are custom + if (eff < mTextureEffects.Size()) + { + return mTextureEffects[eff]; + } + return NULL; + } + + void Recompile(); +}; + +#define FIRST_USER_SHADER 12 + + +#endif + diff --git a/src/gl/shaders/gl_texshader.cpp b/src/gl/shaders/gl_texshader.cpp new file mode 100644 index 000000000..f91529909 --- /dev/null +++ b/src/gl/shaders/gl_texshader.cpp @@ -0,0 +1,698 @@ +/* +** gl_shaders.cpp +** Routines parsing/managing texture shaders. +** +**--------------------------------------------------------------------------- +** Copyright 2003 Timothy Stump +** Copyright 2009 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. +** +** 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 "c_cvars.h" +#include "sc_man.h" +#include "textures/textures.h" +#include "gl/shaders/gl_texshader.h" + +CVAR(Bool, gl_texture_useshaders, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + + +//========================================================================== +// +// +// +//========================================================================== + +FShaderLayer::FShaderLayer() +{ + animate = false; + emissive = false; + blendFuncSrc = GL_SRC_ALPHA; + blendFuncDst = GL_ONE_MINUS_SRC_ALPHA; + offsetX = 0.f; + offsetY = 0.f; + centerX = 0.0f; + centerY = 0.0f; + rotate = 0.f; + rotation = 0.f; + adjustX.SetParams(0.f, 0.f, 0.f); + adjustY.SetParams(0.f, 0.f, 0.f); + scaleX.SetParams(1.f, 1.f, 0.f); + scaleY.SetParams(1.f, 1.f, 0.f); + alpha.SetParams(1.f, 1.f, 0.f); + r.SetParams(1.f, 1.f, 0.f); + g.SetParams(1.f, 1.f, 0.f); + b.SetParams(1.f, 1.f, 0.f); + flags = 0; + layerMask = NULL; + texgen = SHADER_TexGen_None; + warp = false; + warpspeed = 0; +} + +//========================================================================== +// +// +// +//========================================================================== + +FShaderLayer::FShaderLayer(const FShaderLayer &layer) +{ + texture = layer.texture; + animate = layer.animate; + emissive = layer.emissive; + adjustX = layer.adjustX; + adjustY = layer.adjustY; + blendFuncSrc = layer.blendFuncSrc; + blendFuncDst = layer.blendFuncDst; + offsetX = layer.offsetX; + offsetY = layer.offsetY; + centerX = layer.centerX; + centerY = layer.centerX; + rotate = layer.rotate; + rotation = layer.rotation; + scaleX = layer.scaleX; + scaleY = layer.scaleY; + vectorX = layer.vectorX; + vectorY = layer.vectorY; + alpha = layer.alpha; + r = layer.r; + g = layer.g; + b = layer.b; + flags = layer.flags; + if (layer.layerMask) + { + layerMask = new FShaderLayer(*(layer.layerMask)); + } + else + { + layerMask = NULL; + } + texgen = layer.texgen; + warp = layer.warp; + warpspeed = layer.warpspeed; +} + +//========================================================================== +// +// +// +//========================================================================== + +FShaderLayer::~FShaderLayer() +{ + if (layerMask) + { + delete layerMask; + layerMask = NULL; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FShaderLayer::Update(float diff) +{ + r.Update(diff); + g.Update(diff); + b.Update(diff); + alpha.Update(diff); + vectorY.Update(diff); + vectorX.Update(diff); + scaleX.Update(diff); + scaleY.Update(diff); + adjustX.Update(diff); + adjustY.Update(diff); + srcFactor.Update(diff); + dstFactor.Update(diff); + + offsetX += vectorX * diff; + if (offsetX >= 1.f) offsetX -= 1.f; + if (offsetX < 0.f) offsetX += 1.f; + + offsetY += vectorY * diff; + if (offsetY >= 1.f) offsetY -= 1.f; + if (offsetY < 0.f) offsetY += 1.f; + + rotation += rotate * diff; + if (rotation > 360.f) rotation -= 360.f; + if (rotation < 0.f) rotation += 360.f; + + if (layerMask != NULL) layerMask->Update(diff); +} + +//========================================================================== +// +// +// +//========================================================================== + +struct FParseKey +{ + const char *name; + int value; +}; + +static const FParseKey CycleTags[]= +{ + {"linear", CYCLE_Linear}, + {"sin", CYCLE_Sin}, + {"cos", CYCLE_Cos}, + {"sawtooth", CYCLE_SawTooth}, + {"square", CYCLE_Square}, + {NULL} +}; + +static const FParseKey BlendTags[]= +{ + {"GL_ZERO", GL_ZERO}, + {"GL_ONE", GL_ONE}, + + {"GL_DST_COLOR", GL_DST_COLOR}, + {"GL_ONE_MINUS_DST_COLOR", GL_ONE_MINUS_DST_COLOR}, + {"GL_DST_ALPHA", GL_DST_ALPHA}, + {"GL_ONE_MINUS_DST_ALPHA", GL_ONE_MINUS_DST_ALPHA}, + + {"GL_SRC_COLOR", GL_SRC_COLOR}, + {"GL_ONE_MINUS_SRC_COLOR", GL_ONE_MINUS_SRC_COLOR}, + {"GL_SRC_ALPHA", GL_SRC_ALPHA}, + {"GL_ONE_MINUS_SRC_ALPHA", GL_ONE_MINUS_SRC_ALPHA}, + + {"GL_SRC_ALPHA_SATURATE", GL_SRC_ALPHA_SATURATE}, + {NULL} +}; + + +//========================================================================== +// +// +// +//========================================================================== + +CycleType FShaderLayer::ParseCycleType(FScanner &sc) +{ + if (sc.GetString()) + { + int t = sc.MatchString(&CycleTags[0].name, sizeof(CycleTags[0])); + if (t > -1) return CycleType(CycleTags[t].value); + sc.UnGet(); + } + return CYCLE_Linear; +} + +//========================================================================== +// +// +// +//========================================================================== + +bool FShaderLayer::ParseLayer(FScanner &sc) +{ + bool retval = true; + float start, end, cycle, r1, r2, g1, g2, b1, b2; + int type; + + if (sc.GetString()) + { + texture = TexMan.CheckForTexture(sc.String, FTexture::TEX_Wall); + if (!texture.isValid()) + { + sc.ScriptMessage("Unknown texture '%s'", sc.String); + retval = false; + } + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + if (sc.End) + { + sc.ScriptError("Unexpected end of file encountered"); + return false; + } + + if (sc.Compare("alpha")) + { + if (sc.CheckString("cycle")) + { + alpha.ShouldCycle(true); + alpha.SetCycleType(ParseCycleType(sc)); + + sc.GetFloat(); + start = sc.Float; + sc.GetFloat(); + end = sc.Float; + sc.GetFloat(); + cycle = sc.Float; + + alpha.SetParams(start, end, cycle); + } + else + { + sc.MustGetFloat(); + alpha.SetParams(float(sc.Float), float(sc.Float), 0.f); + } + } + else if (sc.Compare("srcfactor")) + { + if (sc.CheckString("cycle")) + { + srcFactor.ShouldCycle(true); + srcFactor.SetCycleType(ParseCycleType(sc)); + + sc.GetFloat(); + start = sc.Float; + sc.GetFloat(); + end = sc.Float; + sc.GetFloat(); + cycle = sc.Float; + + srcFactor.SetParams(start, end, cycle); + } + else + { + sc.MustGetFloat(); + srcFactor.SetParams(float(sc.Float), float(sc.Float), 0.f); + } + } + if (sc.Compare("destfactor")) + { + if (sc.CheckString("cycle")) + { + dstFactor.ShouldCycle(true); + dstFactor.SetCycleType(ParseCycleType(sc)); + + sc.GetFloat(); + start = sc.Float; + sc.GetFloat(); + end = sc.Float; + sc.GetFloat(); + cycle = sc.Float; + + dstFactor.SetParams(start, end, cycle); + } + else + { + sc.MustGetFloat(); + dstFactor.SetParams(float(sc.Float), float(sc.Float), 0.f); + } + } + else if (sc.Compare("animate")) + { + sc.GetString(); + animate = sc.Compare("true"); + } + else if (sc.Compare("blendfunc")) + { + sc.GetString(); + type = sc.MustMatchString(&BlendTags[0].name, sizeof(BlendTags[0])); + blendFuncSrc = type;// BlendTags[type].value; + + sc.GetString(); + type = sc.MustMatchString(&BlendTags[0].name, sizeof(BlendTags[0])); + blendFuncDst = type; //BlendTags[type].value; + } + else if (sc.Compare("color")) + { + if (sc.CheckString("cycle")) + { + CycleType type = ParseCycleType(sc); + r.ShouldCycle(true); + g.ShouldCycle(true); + b.ShouldCycle(true); + r.SetCycleType(type); + g.SetCycleType(type); + b.SetCycleType(type); + + sc.GetFloat(); + r1 = float(sc.Float); + sc.GetFloat(); + g1 = float(sc.Float); + sc.GetFloat(); + b1 = float(sc.Float); + + // get color2 + sc.GetFloat(); + r2 = float(sc.Float); + sc.GetFloat(); + g2 = float(sc.Float); + sc.GetFloat(); + b2 = float(sc.Float); + + // get cycle time + sc.GetFloat(); + cycle = sc.Float; + + r.SetParams(r1, r2, cycle); + g.SetParams(g1, g2, cycle); + b.SetParams(b1, b2, cycle); + } + else + { + sc.GetFloat(); + r1 = float(sc.Float); + sc.GetFloat(); + g1 = sc.Float; + sc.GetFloat(); + b1 = sc.Float; + + r.SetParams(r1, r1, 0.f); + g.SetParams(g1, g1, 0.f); + b.SetParams(b1, b1, 0.f); + } + } + else if (sc.Compare("center")) + { + sc.GetFloat(); + centerX = sc.Float; + sc.GetFloat(); + centerY = sc.Float; + } + else if (sc.Compare("emissive")) + { + sc.GetString(); + emissive = sc.Compare("true"); + } + else if (sc.Compare("offset")) + { + if (sc.CheckString("cycle")) + { + adjustX.ShouldCycle(true); + adjustY.ShouldCycle(true); + + sc.GetFloat(); + r1 = sc.Float; + sc.GetFloat(); + r2 = sc.Float; + + sc.GetFloat(); + g1 = sc.Float; + sc.GetFloat(); + g2 = sc.Float; + + sc.GetFloat(); + cycle = sc.Float; + + offsetX = r1; + offsetY = r2; + + adjustX.SetParams(0.f, g1 - r1, cycle); + adjustY.SetParams(0.f, g2 - r2, cycle); + } + else + { + sc.GetFloat(); + offsetX = sc.Float; + sc.GetFloat(); + offsetY = sc.Float; + } + } + else if (sc.Compare("offsetfunc")) + { + adjustX.SetCycleType(ParseCycleType(sc)); + adjustY.SetCycleType(ParseCycleType(sc)); + } + else if (sc.Compare("mask")) + { + if (layerMask != NULL) delete layerMask; + layerMask = new FShaderLayer; + layerMask->ParseLayer(sc); + } + else if (sc.Compare("rotate")) + { + sc.GetFloat(); + rotate = sc.Float; + } + else if (sc.Compare("rotation")) + { + sc.GetFloat(); + rotation = sc.Float; + } + else if (sc.Compare("scale")) + { + if (sc.CheckString("cycle")) + { + scaleX.ShouldCycle(true); + scaleY.ShouldCycle(true); + + sc.GetFloat(); + r1 = sc.Float; + sc.GetFloat(); + r2 = sc.Float; + + sc.GetFloat(); + g1 = sc.Float; + sc.GetFloat(); + g2 = sc.Float; + + sc.GetFloat(); + cycle = sc.Float; + + scaleX.SetParams(r1, g1, cycle); + scaleY.SetParams(r2, g2, cycle); + } + else + { + sc.GetFloat(); + scaleX.SetParams(sc.Float, sc.Float, 0.f); + sc.GetFloat(); + scaleY.SetParams(sc.Float, sc.Float, 0.f); + } + } + else if (sc.Compare("scalefunc")) + { + scaleX.SetCycleType(ParseCycleType(sc)); + scaleY.SetCycleType(ParseCycleType(sc)); + } + else if (sc.Compare("texgen")) + { + sc.MustGetString(); + if (sc.Compare("sphere")) + { + texgen = SHADER_TexGen_Sphere; + } + else + { + texgen = SHADER_TexGen_None; + } + } + else if (sc.Compare("vector")) + { + if (sc.CheckString("cycle")) + { + vectorX.ShouldCycle(true); + vectorY.ShouldCycle(true); + + sc.GetFloat(); + r1 = sc.Float; + sc.GetFloat(); + g1 = sc.Float; + sc.GetFloat(); + r2 = sc.Float; + sc.GetFloat(); + g2 = sc.Float; + sc.GetFloat(); + cycle = sc.Float; + + vectorX.SetParams(r1, r2, cycle); + vectorY.SetParams(g1, g2, cycle); + } + else + { + sc.GetFloat(); + vectorX.SetParams(sc.Float, sc.Float, 0.f); + sc.GetFloat(); + vectorY.SetParams(sc.Float, sc.Float, 0.f); + } + } + else if (sc.Compare("vectorfunc")) + { + vectorX.SetCycleType(ParseCycleType(sc)); + vectorY.SetCycleType(ParseCycleType(sc)); + } + else if (sc.Compare("warp")) + { + if (sc.CheckNumber()) + { + warp = sc.Number >= 0 && sc.Number <= 2? sc.Number : 0; + } + else + { + // compatibility with ZDoomGL + sc.MustGetString(); + warp = sc.Compare("true"); + } + } + else + { + sc.ScriptError("Unknown keyword '%s' in shader layer", sc.String); + } + } + } + return retval; +} + +//========================================================================== +// +// +// +//========================================================================== + +FTextureShader::FTextureShader() +{ + layers.Clear(); + lastUpdate = 0; +} + +//========================================================================== +// +// +// +//========================================================================== + +bool FTextureShader::ParseShader(FScanner &sc, TArray &names) +{ + bool retval = true; + + if (sc.GetString()) + { + name = sc.String; + + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + if (sc.End) + { + sc.ScriptError("Unexpected end of file encountered"); + return false; + } + else if (sc.Compare("layer")) + { + FShaderLayer *lay = new FShaderLayer; + if (lay->ParseLayer(sc)) + { + if (layers.Size() < 8) + { + layers.Push(lay); + } + else + { + delete lay; + sc.ScriptMessage("Only 8 layers per texture allowed."); + } + } + else + { + delete lay; + retval = false; + } + } + else + { + sc.ScriptError("Unknown keyword '%s' in shader", sc.String); + } + } + } + return retval; +} + +//========================================================================== +// +// +// +//========================================================================== + +void FTextureShader::Update(int framems) +{ + float diff = (framems - lastUpdate) / 1000.f; + + if (lastUpdate != 0) // && !paused && !bglobal.freeze) + { + for (unsigned int i = 0; i < layers.Size(); i++) + { + layers[i]->Update(diff); + } + } + lastUpdate = framems; +} + +//========================================================================== +// +// +// +//========================================================================== + +void FTextureShader::FakeUpdate(int framems) +{ + lastUpdate = framems; +} + +//========================================================================== +// +// +// +//========================================================================== + +FString FTextureShader::CreateName() +{ + FString compose = "custom"; + for(unsigned i=0; iemissive, + layers[i]->blendFuncSrc, layers[i]->blendFuncDst, layers[i]->texgen, layers[i]->warp); + } + return compose; +} + +//========================================================================== +// +// +// +//========================================================================== + +FString FTextureShader::GenerateCode() +{ + static const char *funcnames[] = {"gettexel", "getwarp1", "getwarp2" }; + static const char *srcblend[] = { "vec4(0.0)", "src", "src*dest", "1.0-src*dest", "src*dest.a", "1.0-src*dest.a", + "src*src", "1.0-src*src", "src*src.a", "1.0-src*src", "vec4(src.rgb*src.a, 1)" }; + static const char *dstblend[] = { "vec4(0.0)", "dest", "dest*dest", "1.0-dest*dest", "dest*dest.a", "1.0-dest*dest.a", + "dest*src", "1.0-dest*src", "dest*src.a", "1.0-dest*src", "vec4(dest.rgb*src.a, 1)" }; + FString compose; + for(unsigned i=0; iwarp], i+1, i, i); + if (!layers[i]->emissive) compose.AppendFormat("src.rgb *= gl_Color.rgb;\n"); + compose.AppendFormat("dest = (%s)*srcfactor + (%s)*dstfactor;\n", + srcblend[layers[i]->blendFuncSrc], dstblend[layers[i]->blendFuncDst]); + } + return compose; +} + diff --git a/src/gl/shaders/gl_texshader.h b/src/gl/shaders/gl_texshader.h new file mode 100644 index 000000000..b3e90bf56 --- /dev/null +++ b/src/gl/shaders/gl_texshader.h @@ -0,0 +1,96 @@ + +#ifndef __GL_TEXSHADERS_H__ +#define __GL_TEXSHADERS_H__ + + +#include "tarray.h" +#include "zstring.h" +#include "gl/utility/gl_cycler.h" + + +enum +{ + SHADER_TexGen_None = 0, + SHADER_TexGen_Sphere, + NUM_TexGenTypes +}; + + +//========================================================================== +// +// +// +//========================================================================== + +class FShaderLayer +{ +public: + FShaderLayer(); + FShaderLayer(const FShaderLayer &layer); + ~FShaderLayer(); + void Update(float diff); + CycleType ParseCycleType(FScanner &sc); + bool ParseLayer(FScanner &sc); + + FTextureID texture; + int warpspeed; + unsigned char warp; + bool animate; + bool emissive; + unsigned char texgen; + float centerX, centerY; + float rotation; + float rotate; + float offsetX, offsetY; + FCycler adjustX, adjustY; + FCycler vectorX, vectorY; + FCycler scaleX, scaleY; + FCycler alpha; + FCycler r, g, b; + FCycler srcFactor, dstFactor; + unsigned int flags; + unsigned int blendFuncSrc, blendFuncDst; + FShaderLayer *layerMask; +}; + +//========================================================================== +// +// +// +//========================================================================== + +class FTextureShader +{ +public: + FTextureShader(); + bool ParseShader(FScanner &sc, TArray &names); + bool Available(); + bool Setup(float time); + void Update(int framems); + void FakeUpdate(int framems); + FString CreateName(); + FString GenerateCode(); + + FName name; + TDeletingArray layers; // layers for shader + unsigned int lastUpdate; +}; + + +/* +//extern TArray Shaders[NUM_ShaderClasses]; +//extern TArray ShaderLookup[NUM_ShaderClasses]; + +void GL_InitShaders(); +void GL_ReleaseShaders(); +void GL_UpdateShaders(); +void GL_FakeUpdateShaders(); +//void GL_UpdateShader(FShader *shader); +void GL_DrawShaders(); +//FShader *GL_ShaderForTexture(FTexture *tex); + +bool GL_ParseShader(); +*/ + + +#endif // __GL_TEXSHADERS_H__ diff --git a/src/gl/system/gl_cvars.h b/src/gl/system/gl_cvars.h new file mode 100644 index 000000000..4e8aa85fd --- /dev/null +++ b/src/gl/system/gl_cvars.h @@ -0,0 +1,59 @@ + + +#ifndef _GL_INTERN_H +#define _GL_INTERN_H + +#include "r_defs.h" +#include "c_cvars.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +EXTERN_CVAR(Bool, gl_warp_shader) +EXTERN_CVAR(Bool, gl_fog_shader) +EXTERN_CVAR(Bool, gl_colormap_shader) +EXTERN_CVAR(Bool, gl_brightmap_shader) +EXTERN_CVAR(Bool, gl_glow_shader) + +EXTERN_CVAR(Bool, gl_vid_compatibility) +EXTERN_CVAR(Bool,gl_enhanced_nightvision) +EXTERN_CVAR(Int, screenblocks); +EXTERN_CVAR(Bool, gl_texture) +EXTERN_CVAR(Int, gl_texture_filter) +EXTERN_CVAR(Float, gl_texture_filter_anisotropic) +EXTERN_CVAR(Int, gl_texture_format) +EXTERN_CVAR(Bool, gl_texture_usehires) +EXTERN_CVAR(Bool, gl_usefb) + +EXTERN_CVAR(Int, gl_weaponlight) + +EXTERN_CVAR(Bool, gl_forcemultipass) + +EXTERN_CVAR (Bool, gl_lights); +EXTERN_CVAR (Bool, gl_attachedlights); +EXTERN_CVAR (Bool, gl_lights_checkside); +EXTERN_CVAR (Float, gl_lights_intensity); +EXTERN_CVAR (Float, gl_lights_size); +EXTERN_CVAR (Bool, gl_lights_additive); +EXTERN_CVAR (Bool, gl_light_sprites); +EXTERN_CVAR (Bool, gl_light_particles); + +EXTERN_CVAR(Int, gl_fogmode) +EXTERN_CVAR(Int, gl_lightmode) +EXTERN_CVAR(Bool,gl_mirror_envmap) + +EXTERN_CVAR(Bool,gl_mirrors) +EXTERN_CVAR(Bool,gl_mirror_envmap) +EXTERN_CVAR(Bool, gl_render_segs) +EXTERN_CVAR(Bool, gl_seamless) +EXTERN_CVAR(Bool, gl_dynlight_shader) + +EXTERN_CVAR(Float, gl_mask_threshold) +EXTERN_CVAR(Float, gl_mask_sprite_threshold) + +EXTERN_CVAR(Int, gl_usevbo) + + + +#endif // _GL_INTERN_H diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp new file mode 100644 index 000000000..0409151a9 --- /dev/null +++ b/src/gl/system/gl_framebuffer.cpp @@ -0,0 +1,559 @@ +/* +** gl_framebuffer.cpp +** Implementation of the non-hardware specific parts of the +** OpenGL frame buffer +** +**--------------------------------------------------------------------------- +** Copyright 2000-2007 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "doomstat.h" +#include "m_png.h" +#include "m_crc32.h" +#include "vectors.h" +#include "v_palette.h" +#include "templates.h" +#include "farchive.h" + +#include "gl/system/gl_framebuffer.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/data/gl_data.h" +#include "gl/textures/gl_hwtexture.h" +#include "gl/textures/gl_texture.h" +#include "gl/textures/gl_translate.h" +#include "gl/textures/gl_skyboxtexture.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_templates.h" +#include "gl/gl_functions.h" + +IMPLEMENT_CLASS(OpenGLFrameBuffer) +EXTERN_CVAR (Float, vid_brightness) +EXTERN_CVAR (Float, vid_contrast) +EXTERN_CVAR (Bool, vid_vsync) + +CVAR(Bool, gl_aalines, false, CVAR_ARCHIVE) + +FGLRenderer *GLRenderer; + +void gl_SetupMenu(); + +//========================================================================== +// +// +// +//========================================================================== + +OpenGLFrameBuffer::OpenGLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen) : + Super(hMonitor, width, height, bits, refreshHz, fullscreen) +{ + GLRenderer = new FGLRenderer(this); + memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); + UpdatePalette (); + ScreenshotBuffer = NULL; + LastCamera = NULL; + + InitializeState(); + gl_SetupMenu(); + gl_GenerateGlobalBrightmapFromColormap(); + DoSetGamma(); + needsetgamma = true; + swapped = false; + Accel2D = true; + if (gl.SetVSync!=NULL) gl.SetVSync(vid_vsync); +} + +OpenGLFrameBuffer::~OpenGLFrameBuffer() +{ + delete GLRenderer; + GLRenderer = NULL; +} + +//========================================================================== +// +// Initializes the GL renderer +// +//========================================================================== + +void OpenGLFrameBuffer::InitializeState() +{ + static bool first=true; + + gl.LoadExtensions(); + Super::InitializeState(); + if (first) + { + first=false; + // [BB] For some reason this crashes, if compiled with MinGW and optimization. Has to be investigated. +#ifdef _MSC_VER + gl.PrintStartupLog(); +#endif + + if (gl.flags&RFL_NPOT_TEXTURE) + { + Printf("Support for non power 2 textures enabled.\n"); + } + if (gl.flags&RFL_OCCLUSION_QUERY) + { + Printf("Occlusion query enabled.\n"); + } + } + gl.ClearColor(0.0f, 0.0f, 0.0f, 0.0f); + gl.ClearDepth(1.0f); + gl.DepthFunc(GL_LESS); + gl.ShadeModel(GL_SMOOTH); + + gl.Enable(GL_DITHER); + gl.Enable(GL_ALPHA_TEST); + gl.Disable(GL_CULL_FACE); + gl.Disable(GL_POLYGON_OFFSET_FILL); + gl.Enable(GL_POLYGON_OFFSET_LINE); + gl.Enable(GL_BLEND); + gl.Enable(GL_DEPTH_CLAMP_NV); + gl.Disable(GL_DEPTH_TEST); + gl.Enable(GL_TEXTURE_2D); + gl.Disable(GL_LINE_SMOOTH); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glAlphaFunc(GL_GEQUAL,0.5f); + gl.Hint(GL_LINE_SMOOTH_HINT, GL_NICEST); + gl.Hint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + gl.Hint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + + // This was to work around a bug in some older driver. Probably doesn't make sense anymore. + gl.Enable(GL_FOG); + gl.Disable(GL_FOG); + + gl.Hint(GL_FOG_HINT, GL_FASTEST); + gl.Fogi(GL_FOG_MODE, GL_EXP); + + + gl.Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + int trueH = GetTrueHeight(); + int h = GetHeight(); + gl.Viewport(0, (trueH - h)/2, GetWidth(), GetHeight()); + + Begin2D(false); + GLRenderer->Initialize(); +} + +//========================================================================== +// +// Updates the screen +// +//========================================================================== + +// Testing only for now. +CVAR(Bool, gl_draw_sync, true, 0) //false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +void OpenGLFrameBuffer::Update() +{ + if (!CanUpdate()) + { + GLRenderer->Flush(); + return; + } + + Begin2D(false); + + DrawRateStuff(); + GLRenderer->Flush(); + + if (GetTrueHeight() != GetHeight()) + { + if (GLRenderer != NULL) + GLRenderer->ClearBorders(); + + Begin2D(false); + } + if (gl_draw_sync || !swapped) + { + Swap(); + } + swapped = false; + Unlock(); + CheckBench(); +} + + +//========================================================================== +// +// Swap the buffers +// +//========================================================================== + +void OpenGLFrameBuffer::Swap() +{ + Finish.Reset(); + Finish.Clock(); + gl.Finish(); + if (needsetgamma) + { + //DoSetGamma(); + needsetgamma = false; + } + gl.SwapBuffers(); + Finish.Unclock(); + swapped = true; + FHardwareTexture::UnbindAll(); +} + +//=========================================================================== +// +// DoSetGamma +// +// (Unfortunately Windows has some safety precautions that block gamma ramps +// that are considered too extreme. As a result this doesn't work flawlessly) +// +//=========================================================================== + +void OpenGLFrameBuffer::DoSetGamma() +{ + WORD gammaTable[768]; + + if (m_supportsGamma) + { + // This formula is taken from Doomsday + float gamma = clamp(Gamma, 0.1f, 4.f); + float contrast = clamp(vid_contrast, 0.1f, 3.f); + float bright = clamp(vid_brightness, -0.8f, 0.8f); + + double invgamma = 1 / gamma; + double norm = pow(255., invgamma - 1); + + for (int i = 0; i < 256; i++) + { + double val = i * contrast - (contrast - 1) * 127; + if(gamma != 1) val = pow(val, invgamma) / norm; + val += bright * 128; + + gammaTable[i] = gammaTable[i + 256] = gammaTable[i + 512] = (WORD)clamp(val*256, 0, 0xffff); + } + SetGammaTable(gammaTable); + } +} + +bool OpenGLFrameBuffer::SetGamma(float gamma) +{ + DoSetGamma(); + return true; +} + +bool OpenGLFrameBuffer::SetBrightness(float bright) +{ + DoSetGamma(); + return true; +} + +bool OpenGLFrameBuffer::SetContrast(float contrast) +{ + DoSetGamma(); + return true; +} + +//=========================================================================== +// +// +//=========================================================================== + +void OpenGLFrameBuffer::UpdatePalette() +{ + int rr=0,gg=0,bb=0; + for(int x=0;x<256;x++) + { + rr+=GPalette.BaseColors[x].r; + gg+=GPalette.BaseColors[x].g; + bb+=GPalette.BaseColors[x].b; + } + rr>>=8; + gg>>=8; + bb>>=8; + + palette_brightness = (rr*77 + gg*143 + bb*35)/255; +} + +void OpenGLFrameBuffer::GetFlashedPalette (PalEntry pal[256]) +{ + memcpy(pal, SourcePalette, 256*sizeof(PalEntry)); +} + +PalEntry *OpenGLFrameBuffer::GetPalette () +{ + return SourcePalette; +} + +bool OpenGLFrameBuffer::SetFlash(PalEntry rgb, int amount) +{ + Flash = PalEntry(amount, rgb.r, rgb.g, rgb.b); + return true; +} + +void OpenGLFrameBuffer::GetFlash(PalEntry &rgb, int &amount) +{ + rgb = Flash; + rgb.a = 0; + amount = Flash.a; +} + +int OpenGLFrameBuffer::GetPageCount() +{ + return 1; +} + + +void OpenGLFrameBuffer::GetHitlist(BYTE *hitlist) +{ + Super::GetHitlist(hitlist); + + // check skybox textures and mark the separate faces as used + for(int i=0;igl_info.bSkybox) + { + FSkyBox *sb = static_cast(tex); + for(int i=0;i<6;i++) + { + if (sb->faces[i]) + { + int index = sb->faces[i]->id.GetIndex(); + hitlist[index] |= 1; + } + } + } + } + } + + + // check model skins +} + +//========================================================================== +// +// DFrameBuffer :: CreatePalette +// +// Creates a native palette from a remap table, if supported. +// +//========================================================================== + +FNativePalette *OpenGLFrameBuffer::CreatePalette(FRemapTable *remap) +{ + return GLTranslationPalette::CreatePalette(remap); +} + +//========================================================================== +// +// +// +//========================================================================== +bool OpenGLFrameBuffer::Begin2D(bool) +{ + gl.MatrixMode(GL_MODELVIEW); + gl.LoadIdentity(); + gl.MatrixMode(GL_PROJECTION); + gl.LoadIdentity(); + gl.Ortho( + (GLdouble) 0, + (GLdouble) GetWidth(), + (GLdouble) GetHeight(), + (GLdouble) 0, + (GLdouble) -1.0, + (GLdouble) 1.0 + ); + gl.Disable(GL_DEPTH_TEST); + + // Korshun: ENABLE AUTOMAP ANTIALIASING!!! + if (gl_aalines) + gl.Enable(GL_LINE_SMOOTH); + else + { + gl.Disable(GL_MULTISAMPLE); + gl.Disable(GL_LINE_SMOOTH); + glLineWidth(1.0); + } + + if (GLRenderer != NULL) + GLRenderer->Begin2D(); + return true; +} + +//========================================================================== +// +// Draws a texture +// +//========================================================================== + +void STACK_ARGS OpenGLFrameBuffer::DrawTextureV(FTexture *img, double x0, double y0, uint32 tag, va_list tags) +{ + DrawParms parms; + + if (ParseDrawTextureTags(img, x0, y0, tag, tags, &parms, true)) + { + if (GLRenderer != NULL) GLRenderer->DrawTexture(img, parms); + } +} + +//========================================================================== +// +// +// +//========================================================================== +void OpenGLFrameBuffer::DrawLine(int x1, int y1, int x2, int y2, int palcolor, uint32 color) +{ + if (GLRenderer != NULL) + GLRenderer->DrawLine(x1, y1, x2, y2, palcolor, color); +} + +//========================================================================== +// +// +// +//========================================================================== +void OpenGLFrameBuffer::DrawPixel(int x1, int y1, int palcolor, uint32 color) +{ + if (GLRenderer != NULL) + GLRenderer->DrawPixel(x1, y1, palcolor, color); +} + +//========================================================================== +// +// +// +//========================================================================== +void OpenGLFrameBuffer::Dim(PalEntry) +{ + // Unlike in the software renderer the color is being ignored here because + // view blending only affects the actual view with the GL renderer. + Super::Dim(0); +} + +void OpenGLFrameBuffer::Dim(PalEntry color, float damount, int x1, int y1, int w, int h) +{ + if (GLRenderer != NULL) + GLRenderer->Dim(color, damount, x1, y1, w, h); +} + +//========================================================================== +// +// +// +//========================================================================== +void OpenGLFrameBuffer::FlatFill (int left, int top, int right, int bottom, FTexture *src, bool local_origin) +{ + + if (GLRenderer != NULL) + GLRenderer->FlatFill(left, top, right, bottom, src, local_origin); +} + +//========================================================================== +// +// +// +//========================================================================== +void OpenGLFrameBuffer::Clear(int left, int top, int right, int bottom, int palcolor, uint32 color) +{ + if (GLRenderer != NULL) + GLRenderer->Clear(left, top, right, bottom, palcolor, color); +} + +//========================================================================== +// +// D3DFB :: FillSimplePoly +// +// Here, "simple" means that a simple triangle fan can draw it. +// +//========================================================================== + +void OpenGLFrameBuffer::FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, + angle_t rotation, FDynamicColormap *colormap, int lightlevel) +{ + if (GLRenderer != NULL) + { + GLRenderer->FillSimplePoly(texture, points, npoints, originx, originy, scalex, scaley, + rotation, colormap, lightlevel); + } +} + + +//=========================================================================== +// +// Takes a screenshot +// +//=========================================================================== + +void OpenGLFrameBuffer::GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type) +{ + int w = SCREENWIDTH; + int h = SCREENHEIGHT; + + ReleaseScreenshotBuffer(); + ScreenshotBuffer = new BYTE[w * h * 3]; + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + gl.ReadPixels(0,(GetTrueHeight() - GetHeight()) / 2,w,h,GL_RGB,GL_UNSIGNED_BYTE,ScreenshotBuffer); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + pitch = -w*3; + color_type = SS_RGB; + buffer = ScreenshotBuffer + w * 3 * (h - 1); +} + +//=========================================================================== +// +// Releases the screenshot buffer. +// +//=========================================================================== + +void OpenGLFrameBuffer::ReleaseScreenshotBuffer() +{ + if (ScreenshotBuffer != NULL) delete [] ScreenshotBuffer; + ScreenshotBuffer = NULL; +} + + +void OpenGLFrameBuffer::GameRestart() +{ + memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); + UpdatePalette (); + ScreenshotBuffer = NULL; + LastCamera = NULL; + gl_GenerateGlobalBrightmapFromColormap(); +} \ No newline at end of file diff --git a/src/gl/system/gl_framebuffer.h b/src/gl/system/gl_framebuffer.h new file mode 100644 index 000000000..c2a4fc277 --- /dev/null +++ b/src/gl/system/gl_framebuffer.h @@ -0,0 +1,117 @@ +#ifndef __GL_FRAMEBUFFER +#define __GL_FRAMEBUFFER + +#ifdef _WIN32 +#include "win32iface.h" +#include "win32gliface.h" +#endif + +class FHardwareTexture; + +extern long gl_frameMS; +extern long gl_frameCount; +#ifdef _WIN32 +class OpenGLFrameBuffer : public Win32GLFrameBuffer +{ + typedef Win32GLFrameBuffer Super; + DECLARE_CLASS(OpenGLFrameBuffer, Win32GLFrameBuffer) +#else +#include "sdlglvideo.h" +class OpenGLFrameBuffer : public SDLGLFB +{ +// typedef SDLGLFB Super; //[C]commented, DECLARE_CLASS defines this in linux + DECLARE_CLASS(OpenGLFrameBuffer, SDLGLFB) +#endif + + +public: + + explicit OpenGLFrameBuffer() {} + OpenGLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen) ; + ~OpenGLFrameBuffer(); + + void InitializeState(); + void Update(); + + // Color correction + bool SetGamma (float gamma); + bool SetBrightness(float bright); + bool SetContrast(float contrast); + void DoSetGamma(); + + void UpdatePalette(); + void GetFlashedPalette (PalEntry pal[256]); + PalEntry *GetPalette (); + bool SetFlash(PalEntry rgb, int amount); + void GetFlash(PalEntry &rgb, int &amount); + int GetPageCount(); + bool Begin2D(bool copy3d); + void GetHitlist(BYTE *hitlist); + void GameRestart(); + + // Retrieves a buffer containing image data for a screenshot. + // Hint: Pitch can be negative for upside-down images, in which case buffer + // points to the last row in the buffer, which will be the first row output. + virtual void GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &color_type); + + // Releases the screenshot buffer. + virtual void ReleaseScreenshotBuffer(); + + // 2D drawing + void STACK_ARGS DrawTextureV(FTexture *img, double x, double y, uint32 tag, va_list tags); + void DrawLine(int x1, int y1, int x2, int y2, int palcolor, uint32 color); + void DrawPixel(int x1, int y1, int palcolor, uint32 color); + void Clear(int left, int top, int right, int bottom, int palcolor, uint32 color); + void Dim(PalEntry color=0); + void Dim (PalEntry color, float damount, int x1, int y1, int w, int h); + void FlatFill (int left, int top, int right, int bottom, FTexture *src, bool local_origin=false); + + void FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, + angle_t rotation, FDynamicColormap *colormap, int lightlevel); + + FNativePalette *CreatePalette(FRemapTable *remap); + + bool WipeStartScreen(int type); + void WipeEndScreen(); + bool WipeDo(int ticks); + void WipeCleanup(); + void Swap(); + bool Is8BitMode() { return false; } + + +private: + PalEntry Flash; + + // Texture creation info + int cm; + int translation; + bool iscomplex; + bool needsetgamma; + bool swapped; + + PalEntry SourcePalette[256]; + BYTE *ScreenshotBuffer; + + class Wiper + { + public: + virtual ~Wiper(); + virtual bool Run(int ticks, OpenGLFrameBuffer *fb) = 0; + }; + + class Wiper_Melt; friend class Wiper_Melt; + class Wiper_Burn; friend class Wiper_Burn; + class Wiper_Crossfade; friend class Wiper_Crossfade; + + Wiper *ScreenWipe; + FHardwareTexture *wipestartscreen; + FHardwareTexture *wipeendscreen; + +public: + AActor * LastCamera; + int palette_brightness; +}; + + +#endif //__GL_FRAMEBUFFER diff --git a/src/gl/system/gl_interface.cpp b/src/gl/system/gl_interface.cpp new file mode 100644 index 000000000..e3234e947 --- /dev/null +++ b/src/gl/system/gl_interface.cpp @@ -0,0 +1,1084 @@ +/* +** r_opengl.cpp +** +** OpenGL system interface +** +**--------------------------------------------------------------------------- +** Copyright 2005 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 "tarray.h" +#include "doomtype.h" +#include "m_argv.h" +#include "zstring.h" +#include "version.h" +#include "i_system.h" +#include "gl/system/gl_cvars.h" + +#if defined (unix) || defined (__APPLE__) +#include +#define wglGetProcAddress(x) (*SDL_GL_GetProcAddress)(x) +#endif +static void APIENTRY glBlendEquationDummy (GLenum mode); + + +#if !defined (unix) && !defined (__APPLE__) +PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; // = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); +PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; +#endif + +static TArray m_Extensions; + +#if !defined (unix) && !defined (__APPLE__) +HWND m_Window; +HDC m_hDC; +HGLRC m_hRC; +#endif + +#define gl pgl + +RenderContext * gl; + +int occlusion_type=0; + + +//========================================================================== +// +// +// +//========================================================================== + +#if !defined (unix) && !defined (__APPLE__) +static HWND InitDummy() +{ + HMODULE g_hInst = GetModuleHandle(NULL); + HWND dummy; + //Create a rect structure for the size/position of the window + RECT windowRect; + windowRect.left = 0; + windowRect.right = 64; + windowRect.top = 0; + windowRect.bottom = 64; + + //Window class structure + WNDCLASS wc; + + //Fill in window class struct + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC) DefWindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = g_hInst; + wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = "GZDoomOpenGLDummyWindow"; + + //Register window class + if(!RegisterClass(&wc)) + { + return 0; + } + + //Set window style & extended style + DWORD style, exStyle; + exStyle = WS_EX_CLIENTEDGE; + style = WS_SYSMENU | WS_BORDER | WS_CAPTION;// | WS_VISIBLE; + + //Adjust the window size so that client area is the size requested + AdjustWindowRectEx(&windowRect, style, false, exStyle); + + //Create Window + if(!(dummy = CreateWindowEx(exStyle, + "GZDoomOpenGLDummyWindow", + "GZDOOM", + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | style, + 0, 0, + windowRect.right-windowRect.left, + windowRect.bottom-windowRect.top, + NULL, NULL, + g_hInst, + NULL))) + { + UnregisterClass("GZDoomOpenGLDummyWindow", g_hInst); + return 0; + } + ShowWindow(dummy, SW_HIDE); + + return dummy; +} + +//========================================================================== +// +// +// +//========================================================================== + +static void ShutdownDummy(HWND dummy) +{ + DestroyWindow(dummy); + UnregisterClass("GZDoomOpenGLDummyWindow", GetModuleHandle(NULL)); +} + + +//========================================================================== +// +// +// +//========================================================================== + +static bool ReadInitExtensions() +{ + HDC hDC; + HGLRC hRC; + HWND dummy; + + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, + 32, // color depth + 0, 0, 0, 0, 0, 0, + 0, + 0, + 0, + 0, 0, 0, 0, + 16, // z depth + 0, // stencil buffer + 0, + PFD_MAIN_PLANE, + 0, + 0, 0, 0 + }; + + int pixelFormat; + + // we have to create a dummy window to init stuff from or the full init stuff fails + dummy = InitDummy(); + + hDC = GetDC(dummy); + pixelFormat = ChoosePixelFormat(hDC, &pfd); + DescribePixelFormat(hDC, pixelFormat, sizeof(pfd), &pfd); + + SetPixelFormat(hDC, pixelFormat, &pfd); + + hRC = wglCreateContext(hDC); + wglMakeCurrent(hDC, hRC); + + wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); + wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"); + // any extra stuff here? + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(hRC); + ReleaseDC(dummy, hDC); + ShutdownDummy(dummy); + + return true; +} +#endif + +//========================================================================== +// +// +// +//========================================================================== +const char *wgl_extensions; + +static void CollectExtensions() +{ + const char *supported = NULL; + char *extensions, *extension; + + supported = (char *)glGetString(GL_EXTENSIONS); + + if (supported) + { + extensions = new char[strlen(supported) + 1]; + strcpy(extensions, supported); + + extension = strtok(extensions, " "); + while(extension) + { + m_Extensions.Push(FString(extension)); + extension = strtok(NULL, " "); + } + + delete [] extensions; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +static bool CheckExtension(const char *ext) +{ + for (unsigned int i = 0; i < m_Extensions.Size(); ++i) + { + if (m_Extensions[i].CompareNoCase(ext) == 0) return true; + } + + return false; +} + + +//========================================================================== +// +// +// +//========================================================================== + +static void APIENTRY LoadExtensions() +{ + CollectExtensions(); + + const char *version = (const char*)glGetString(GL_VERSION); + + // Don't even start if it's lower than 1.2 + if (strcmp(version, "1.2") < 0) + { + I_FatalError("Unsupported OpenGL version.\nAt least GL 1.2 is required to run "GAMENAME".\n"); + } + + // This loads any function pointers and flags that require a vaild render context to + // initialize properly + + gl->shadermodel = 0; // assume no shader support + gl->vendorstring=(char*)glGetString(GL_VENDOR); + + // First try the regular function + gl->BlendEquation = (PFNGLBLENDEQUATIONPROC)wglGetProcAddress("glBlendEquation"); + // If that fails try the EXT version + if (!gl->BlendEquation) gl->BlendEquation = (PFNGLBLENDEQUATIONPROC)wglGetProcAddress("glBlendEquationEXT"); + // If that fails use a no-op dummy + if (!gl->BlendEquation) gl->BlendEquation = glBlendEquationDummy; + + if (CheckExtension("GL_ARB_texture_non_power_of_two")) gl->flags|=RFL_NPOT_TEXTURE; + if (CheckExtension("GL_ARB_texture_compression")) gl->flags|=RFL_TEXTURE_COMPRESSION; + if (CheckExtension("GL_EXT_texture_compression_s3tc")) gl->flags|=RFL_TEXTURE_COMPRESSION_S3TC; + if (strstr(gl->vendorstring, "NVIDIA")) gl->flags|=RFL_NVIDIA; + else if (strstr(gl->vendorstring, "ATI Technologies")) gl->flags|=RFL_ATI; + + if (strcmp(version, "2.0") >= 0) gl->flags|=RFL_GL_20; + if (strcmp(version, "2.1") >= 0) gl->flags|=RFL_GL_21; + if (strcmp(version, "3.0") >= 0) gl->flags|=RFL_GL_30; + + +#if !defined (unix) && !defined (__APPLE__) + PFNWGLSWAPINTERVALEXTPROC vs = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); + if (vs) gl->SetVSync = vs; +#endif + + glGetIntegerv(GL_MAX_TEXTURE_SIZE,&gl->max_texturesize); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + if (gl->flags & RFL_GL_20) + { + gl->DeleteShader = (PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader"); + gl->DeleteProgram = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress("glDeleteProgram"); + gl->DetachShader = (PFNGLDETACHSHADERPROC)wglGetProcAddress("glDetachShader"); + gl->CreateShader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader"); + gl->ShaderSource = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource"); + gl->CompileShader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"); + gl->CreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram"); + gl->AttachShader = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader"); + gl->LinkProgram = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram"); + gl->UseProgram = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"); + gl->ValidateProgram = (PFNGLVALIDATEPROGRAMPROC)wglGetProcAddress("glValidateProgram"); + + gl->VertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)wglGetProcAddress("glVertexAttrib1f"); + gl->VertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)wglGetProcAddress("glVertexAttrib2f"); + gl->VertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)wglGetProcAddress("glVertexAttrib4f"); + gl->VertexAttrib2fv = (PFNGLVERTEXATTRIB4FVPROC)wglGetProcAddress("glVertexAttrib2fv"); + gl->VertexAttrib3fv = (PFNGLVERTEXATTRIB4FVPROC)wglGetProcAddress("glVertexAttrib3fv"); + gl->VertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)wglGetProcAddress("glVertexAttrib4fv"); + gl->VertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC)wglGetProcAddress("glVertexAttrib4ubv"); + gl->GetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)wglGetProcAddress("glGetAttribLocation"); + gl->BindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)wglGetProcAddress("glBindAttribLocation"); + + + gl->Uniform1f = (PFNGLUNIFORM1FPROC)wglGetProcAddress("glUniform1f"); + gl->Uniform2f = (PFNGLUNIFORM2FPROC)wglGetProcAddress("glUniform2f"); + gl->Uniform3f = (PFNGLUNIFORM3FPROC)wglGetProcAddress("glUniform3f"); + gl->Uniform4f = (PFNGLUNIFORM4FPROC)wglGetProcAddress("glUniform4f"); + gl->Uniform1i = (PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i"); + gl->Uniform2i = (PFNGLUNIFORM2IPROC)wglGetProcAddress("glUniform2i"); + gl->Uniform3i = (PFNGLUNIFORM3IPROC)wglGetProcAddress("glUniform3i"); + gl->Uniform4i = (PFNGLUNIFORM4IPROC)wglGetProcAddress("glUniform4i"); + gl->Uniform1fv = (PFNGLUNIFORM1FVPROC)wglGetProcAddress("glUniform1fv"); + gl->Uniform2fv = (PFNGLUNIFORM2FVPROC)wglGetProcAddress("glUniform2fv"); + gl->Uniform3fv = (PFNGLUNIFORM3FVPROC)wglGetProcAddress("glUniform3fv"); + gl->Uniform4fv = (PFNGLUNIFORM4FVPROC)wglGetProcAddress("glUniform4fv"); + gl->Uniform1iv = (PFNGLUNIFORM1IVPROC)wglGetProcAddress("glUniform1iv"); + gl->Uniform2iv = (PFNGLUNIFORM2IVPROC)wglGetProcAddress("glUniform2iv"); + gl->Uniform3iv = (PFNGLUNIFORM3IVPROC)wglGetProcAddress("glUniform3iv"); + gl->Uniform4iv = (PFNGLUNIFORM4IVPROC)wglGetProcAddress("glUniform4iv"); + + gl->UniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)wglGetProcAddress("glUniformMatrix2fv"); + gl->UniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)wglGetProcAddress("glUniformMatrix3fv"); + gl->UniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)wglGetProcAddress("glUniformMatrix4fv"); + + gl->GetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog"); + gl->GetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog"); + gl->GetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation"); + gl->GetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)wglGetProcAddress("glGetActiveUniform"); + gl->GetUniformfv = (PFNGLGETUNIFORMFVPROC)wglGetProcAddress("glGetUniformfv"); + gl->GetUniformiv = (PFNGLGETUNIFORMIVPROC)wglGetProcAddress("glGetUniformiv"); + gl->GetShaderSource = (PFNGLGETSHADERSOURCEPROC)wglGetProcAddress("glGetShaderSource"); + + gl->EnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray"); + gl->DisableVertexAttribArray= (PFNGLDISABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glDisableVertexAttribArray"); + gl->VertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer"); + + // what'S the equivalent of this in GL 2.0??? + gl->GetObjectParameteriv = (PFNGLGETOBJECTPARAMETERIVARBPROC)wglGetProcAddress("glGetObjectParameterivARB"); + + // Rules: + // SM4 will always use shaders. No option to switch them off is needed here. + // SM3 has shaders optional but they are off by default (they will have a performance impact + // SM2 only uses shaders for colormaps on camera textures and has no option to use them in general. + // On SM2 cards the shaders will be too slow and show visual bugs (at least on GF 6800.) + if (strcmp((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION), "1.3") >= 0) gl->shadermodel = 4; + else if (CheckExtension("GL_NV_GPU_shader4")) gl->shadermodel = 4; // for pre-3.0 drivers that support GF8xxx. + else if (CheckExtension("GL_EXT_GPU_shader4")) gl->shadermodel = 4; // for pre-3.0 drivers that support GF8xxx. + else if (CheckExtension("GL_NV_vertex_program3")) gl->shadermodel = 3; + else if (!strstr(gl->vendorstring, "NVIDIA")) gl->shadermodel = 3; + else gl->shadermodel = 2; // Only for older NVidia cards which had notoriously bad shader support. + + // Command line overrides for testing and problem cases. + if (Args->CheckParm("-sm2") && gl->shadermodel > 2) gl->shadermodel = 2; + else if (Args->CheckParm("-sm3") && gl->shadermodel > 3) gl->shadermodel = 3; + } + + if (CheckExtension("GL_ARB_occlusion_query")) + { + gl->GenQueries = (PFNGLGENQUERIESARBPROC)wglGetProcAddress("glGenQueriesARB"); + gl->DeleteQueries = (PFNGLDELETEQUERIESARBPROC)wglGetProcAddress("glDeleteQueriesARB"); + gl->GetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVARBPROC)wglGetProcAddress("glGetQueryObjectuivARB"); + gl->BeginQuery = (PFNGLBEGINQUERYARBPROC)wglGetProcAddress("glBeginQueryARB"); + gl->EndQuery = (PFNGLENDQUERYPROC)wglGetProcAddress("glEndQueryARB"); + gl->flags|=RFL_OCCLUSION_QUERY; + } + + if (gl->flags & RFL_GL_21) + { + gl->BindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer"); + gl->DeleteBuffers = (PFNGLDELETEBUFFERSPROC)wglGetProcAddress("glDeleteBuffers"); + gl->GenBuffers = (PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers"); + gl->BufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData"); + gl->BufferSubData = (PFNGLBUFFERSUBDATAPROC)wglGetProcAddress("glBufferSubData"); + gl->MapBuffer = (PFNGLMAPBUFFERPROC)wglGetProcAddress("glMapBuffer"); + gl->UnmapBuffer = (PFNGLUNMAPBUFFERPROC)wglGetProcAddress("glUnmapBuffer"); + gl->flags |= RFL_VBO; + } + else if (CheckExtension("GL_ARB_vertex_buffer_object")) + { + gl->BindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBufferARB"); + gl->DeleteBuffers = (PFNGLDELETEBUFFERSPROC)wglGetProcAddress("glDeleteBuffersARB"); + gl->GenBuffers = (PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffersARB"); + gl->BufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferDataARB"); + gl->BufferSubData = (PFNGLBUFFERSUBDATAPROC)wglGetProcAddress("glBufferSubDataARB"); + gl->MapBuffer = (PFNGLMAPBUFFERPROC)wglGetProcAddress("glMapBufferARB"); + gl->UnmapBuffer = (PFNGLUNMAPBUFFERPROC)wglGetProcAddress("glUnmapBufferARB"); + gl->flags |= RFL_VBO; + } + + if (CheckExtension("GL_ARB_map_buffer_range")) + { + gl->MapBufferRange = (PFNGLMAPBUFFERRANGEPROC)wglGetProcAddress("glMapBufferRange"); + gl->FlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)wglGetProcAddress("glFlushMappedBufferRange"); + gl->flags|=RFL_MAP_BUFFER_RANGE; + } + + if (CheckExtension("GL_ARB_framebuffer_object")) + { + gl->GenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress("glGenFramebuffers"); + gl->DeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)wglGetProcAddress("glDeleteFramebuffers"); + gl->BindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer"); + gl->FramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)wglGetProcAddress("glFramebufferTexture2D"); + gl->GenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)wglGetProcAddress("glGenRenderbuffers"); + gl->DeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)wglGetProcAddress("glDeleteRenderbuffers"); + gl->BindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)wglGetProcAddress("glBindRenderbuffer"); + gl->RenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)wglGetProcAddress("glRenderbufferStorage"); + gl->FramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)wglGetProcAddress("glFramebufferRenderbuffer"); + + gl->flags|=RFL_FRAMEBUFFER; + } +#if 0 + else if (CheckExtension("GL_EXT_framebuffer_object") && + CheckExtension("GL_EXT_packed_depth_stencil")) + { + gl->GenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress("glGenFramebuffersEXT"); + gl->BindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebufferEXT"); + gl->FramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)wglGetProcAddress("glFramebufferTexture2DEXT"); + gl->GenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)wglGetProcAddress("glGenRenderbuffersEXT"); + gl->BindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)wglGetProcAddress("glBindRenderbufferEXT"); + gl->RenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)wglGetProcAddress("glRenderbufferStorageEXT"); + gl->FramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)wglGetProcAddress("glFramebufferRenderbufferEXT"); + + gl->flags|=RFL_FRAMEBUFFER; + } +#endif + +#if 0 + if (CheckExtension("GL_ARB_texture_buffer_object") && + CheckExtension("GL_ARB_texture_float") && + CheckExtension("GL_EXT_GPU_Shader4") && + CheckExtension("GL_ARB_texture_rg") && + gl->shadermodel == 4) + { + gl->TexBufferARB = (PFNGLTEXBUFFERARBPROC)wglGetProcAddress("glTexBufferARB"); + gl->flags|=RFL_TEXTUREBUFFER; + } +#endif + + + + gl->ActiveTexture = (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTextureARB"); + gl->MultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC) wglGetProcAddress("glMultiTexCoord2fARB"); + gl->MultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC) wglGetProcAddress("glMultiTexCoord2fvARB"); +} + +//========================================================================== +// +// +// +//========================================================================== + +static void APIENTRY PrintStartupLog() +{ + Printf ("GL_VENDOR: %s\n", glGetString(GL_VENDOR)); + Printf ("GL_RENDERER: %s\n", glGetString(GL_RENDERER)); + Printf ("GL_VERSION: %s\n", glGetString(GL_VERSION)); + Printf ("GL_SHADING_LANGUAGE_VERSION: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION)); + Printf ("GL_EXTENSIONS: %s\n", glGetString(GL_EXTENSIONS)); + int v; + + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &v); + Printf ("Max. texture units: %d\n", v); + glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &v); + Printf ("Max. fragment uniforms: %d\n", v); + if (gl->shadermodel == 4) gl->maxuniforms = v; + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &v); + Printf ("Max. vertex uniforms: %d\n", v); + glGetIntegerv(GL_MAX_VARYING_FLOATS, &v); + Printf ("Max. varying: %d\n", v); + glGetIntegerv(GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS, &v); + Printf ("Max. combined uniforms: %d\n", v); + glGetIntegerv(GL_MAX_COMBINED_UNIFORM_BLOCKS, &v); + Printf ("Max. combined uniform blocks: %d\n", v); + +} + +//========================================================================== +// +// +// +//========================================================================== + +#if !defined (unix) && !defined (__APPLE__) +static bool SetupPixelFormat(HDC hDC, bool allowsoftware, bool nostencil, int multisample) +{ + int colorDepth; + HDC deskDC; + int attributes[26]; + int pixelFormat; + unsigned int numFormats; + float attribsFloat[] = {0.0f, 0.0f}; + int stencil; + + deskDC = GetDC(GetDesktopWindow()); + colorDepth = GetDeviceCaps(deskDC, BITSPIXEL); + ReleaseDC(GetDesktopWindow(), deskDC); + + /* + if (!nostencil && colorDepth < 32) + { + Printf("R_OPENGL: Desktop not in 32 bit mode!\n"); + return false; + } + */ + + if (!nostencil) + { + for (stencil=1;stencil>=0;stencil--) + { + if (wglChoosePixelFormatARB && stencil) + { + attributes[0] = WGL_RED_BITS_ARB; //bits + attributes[1] = 8; + attributes[2] = WGL_GREEN_BITS_ARB; //bits + attributes[3] = 8; + attributes[4] = WGL_BLUE_BITS_ARB; //bits + attributes[5] = 8; + attributes[6] = WGL_ALPHA_BITS_ARB; + attributes[7] = 8; + attributes[8] = WGL_DEPTH_BITS_ARB; + attributes[9] = 24; + attributes[10] = WGL_STENCIL_BITS_ARB; + attributes[11] = 8; + + attributes[12] = WGL_DRAW_TO_WINDOW_ARB; //required to be true + attributes[13] = true; + attributes[14] = WGL_SUPPORT_OPENGL_ARB; + attributes[15] = true; + attributes[16] = WGL_DOUBLE_BUFFER_ARB; + attributes[17] = true; + + attributes[18] = WGL_ACCELERATION_ARB; //required to be FULL_ACCELERATION_ARB + if (allowsoftware) + { + attributes[19] = WGL_NO_ACCELERATION_ARB; + } + else + { + attributes[19] = WGL_FULL_ACCELERATION_ARB; + } + + if (multisample > 0) + { + attributes[20] = WGL_SAMPLE_BUFFERS_ARB; + attributes[21] = true; + attributes[22] = WGL_SAMPLES_ARB; + attributes[23] = multisample; + } + else + { + attributes[20] = 0; + attributes[21] = 0; + attributes[22] = 0; + attributes[23] = 0; + } + + attributes[24] = 0; + attributes[25] = 0; + + if (!wglChoosePixelFormatARB(hDC, attributes, attribsFloat, 1, &pixelFormat, &numFormats)) + { + Printf("R_OPENGL: Couldn't choose pixel format. Retrying in compatibility mode\n"); + goto oldmethod; + } + + if (numFormats == 0) + { + Printf("R_OPENGL: No valid pixel formats found. Retrying in compatibility mode\n"); + goto oldmethod; + } + + break; + } + else + { + oldmethod: + // If wglChoosePixelFormatARB is not found we have to do it the old fashioned way. + static PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, + 32, // color depth + 0, 0, 0, 0, 0, 0, + 0, + 0, + 0, + 0, 0, 0, 0, + 32, // z depth + stencil*8, // stencil buffer + 0, + PFD_MAIN_PLANE, + 0, + 0, 0, 0 + }; + + pixelFormat = ChoosePixelFormat(hDC, &pfd); + DescribePixelFormat(hDC, pixelFormat, sizeof(pfd), &pfd); + + if (pfd.dwFlags & PFD_GENERIC_FORMAT) + { + if (!allowsoftware) + { + if (stencil==0) + { + // not accelerated! + Printf("R_OPENGL: OpenGL driver not accelerated! Falling back to software renderer.\n"); + return false; + } + else + { + Printf("R_OPENGL: OpenGL driver not accelerated! Retrying in compatibility mode\n"); + continue; + } + } + } + break; + } + } + } + else + { + // Use the cheapest mode available and let's hope the driver can handle this... + stencil=0; + + // If wglChoosePixelFormatARB is not found we have to do it the old fashioned way. + static PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, + 16, // color depth + 0, 0, 0, 0, 0, 0, + 0, + 0, + 0, + 0, 0, 0, 0, + 16, // z depth + 0, // stencil buffer + 0, + PFD_MAIN_PLANE, + 0, + 0, 0, 0 + }; + + pixelFormat = ChoosePixelFormat(hDC, &pfd); + DescribePixelFormat(hDC, pixelFormat, sizeof(pfd), &pfd); + + if (pfd.dwFlags & PFD_GENERIC_FORMAT) + { + if (!allowsoftware) + { + Printf("R_OPENGL: OpenGL driver not accelerated! Falling back to software renderer.\n"); + return false; + } + } + } + if (stencil==0) + { + gl->flags|=RFL_NOSTENCIL; + } + + if (!SetPixelFormat(hDC, pixelFormat, NULL)) + { + Printf("R_OPENGL: Couldn't set pixel format.\n"); + return false; + } + return true; +} +#else + +static bool SetupPixelFormat(bool allowsoftware, bool nostencil, int multisample) +{ + int stencil; + + if (!nostencil) + { + stencil=1; + SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); + SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); + SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 ); + SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 ); + SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 ); + SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 8 ); +// SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + if (multisample > 0) { + SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 ); + SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, multisample ); + } + } + else + { + // Use the cheapest mode available and let's hope the driver can handle this... + stencil=0; + + SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 4 ); + SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 4 ); + SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 4 ); + SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 4 ); + SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 ); + //SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 )*/ + } + if (stencil==0) + { + gl->flags|=RFL_NOSTENCIL; + } + return true; +} +#endif + + +//========================================================================== +// +// +// +//========================================================================== + +#if !defined (unix) && !defined (__APPLE__) +CVAR(Bool, gl_debug, false, 0) + +static bool APIENTRY InitHardware (HWND Window, bool allowsoftware, bool nostencil, int multisample) +{ + m_Window=Window; + m_hDC = GetDC(Window); + + if (!SetupPixelFormat(m_hDC, allowsoftware, nostencil, multisample)) + { + Printf ("R_OPENGL: Reverting to software mode...\n"); + return false; + } + + m_hRC = 0; + if (wglCreateContextAttribsARB != NULL) + { + int ctxAttribs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGL_CONTEXT_MINOR_VERSION_ARB, 3, + WGL_CONTEXT_FLAGS_ARB, gl_debug? WGL_CONTEXT_DEBUG_BIT_ARB : 0, + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + 0 + }; + + m_hRC = wglCreateContextAttribsARB(m_hDC, 0, ctxAttribs); + } + if (m_hRC == 0) + { + m_hRC = wglCreateContext(m_hDC); + } + + if (m_hRC == NULL) + { + Printf ("R_OPENGL: Couldn't create render context. Reverting to software mode...\n"); + return false; + } + + wglMakeCurrent(m_hDC, m_hRC); + return true; +} + +#else +static bool APIENTRY InitHardware (bool allowsoftware, bool nostencil, int multisample) +{ + if (!SetupPixelFormat(allowsoftware, nostencil, multisample)) + { + Printf ("R_OPENGL: Reverting to software mode...\n"); + return false; + } + return true; +} +#endif + + +//========================================================================== +// +// +// +//========================================================================== + +#if !defined (unix) && !defined (__APPLE__) +static void APIENTRY Shutdown() +{ + if (m_hRC) + { + wglMakeCurrent(0, 0); + wglDeleteContext(m_hRC); + } + if (m_hDC) ReleaseDC(m_Window, m_hDC); +} + + +static bool APIENTRY SetFullscreen(const char *devicename, int w, int h, int bits, int hz) +{ + DEVMODE dm; + + if (w==0) + { + ChangeDisplaySettingsEx(devicename, 0, 0, 0, 0); + } + else + { + dm.dmSize = sizeof(DEVMODE); + dm.dmSpecVersion = DM_SPECVERSION;//Somebody owes me... + dm.dmDriverExtra = 0;//...1 hour of my life back + dm.dmPelsWidth = w; + dm.dmPelsHeight = h; + dm.dmBitsPerPel = bits; + dm.dmDisplayFrequency = hz; + dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; + if (DISP_CHANGE_SUCCESSFUL != ChangeDisplaySettingsEx(devicename, &dm, 0, CDS_FULLSCREEN, 0)) + { + dm.dmFields &= ~DM_DISPLAYFREQUENCY; + return DISP_CHANGE_SUCCESSFUL == ChangeDisplaySettingsEx(devicename, &dm, 0, CDS_FULLSCREEN, 0); + } + } + return true; +} +#endif +//========================================================================== +// +// +// +//========================================================================== + +static void APIENTRY iSwapBuffers() +{ +#if !defined (unix) && !defined (__APPLE__) + SwapBuffers(m_hDC); +#else + SDL_GL_SwapBuffers (); +#endif +} + +static BOOL APIENTRY SetVSync( int vsync ) +{ +#if defined (__APPLE__) + return kCGLNoError == CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, &vsync ); +#else // !__APPLE__ + // empty placeholder + return false; +#endif // __APPLE__ +} + +//========================================================================== +// +// +// +//========================================================================== + +static void APIENTRY glBlendEquationDummy (GLenum mode) +{ + // If this is not supported all non-existent modes are + // made to draw nothing. + if (mode == GL_FUNC_ADD) + { + glColorMask(true, true, true, true); + } + else + { + glColorMask(false, false, false, false); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +static void APIENTRY SetTextureMode(int type) +{ + static float white[] = {1.f,1.f,1.f,1.f}; + + if (gl_vid_compatibility) + { + type = TM_MODULATE; + } + if (type == TM_MASK) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + } + else if (type == TM_OPAQUE) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + } + else if (type == TM_INVERT) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + } + else if (type == TM_INVERTOPAQUE) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + } + else // if (type == TM_MODULATE) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +/* +extern "C" +{ + +__declspec(dllexport) +*/ +void APIENTRY GetContext(RenderContext & gl) +{ + ::gl=≷ + + gl.flags=0; + + gl.LoadExtensions = LoadExtensions; + gl.SetTextureMode = SetTextureMode; + gl.PrintStartupLog = PrintStartupLog; + gl.InitHardware = InitHardware; +#if !defined (unix) && !defined (__APPLE__) + gl.Shutdown = Shutdown; +#endif + gl.SwapBuffers = iSwapBuffers; +#if !defined (unix) && !defined (__APPLE__) + gl.SetFullscreen = SetFullscreen; +#endif + + gl.Begin = glBegin; + gl.End = glEnd; + gl.DrawArrays = glDrawArrays; + + gl.TexCoord2f = glTexCoord2f; + gl.TexCoord2fv = glTexCoord2fv; + + gl.Vertex2f = glVertex2f; + gl.Vertex2i = glVertex2i; + gl.Vertex3f = glVertex3f; + gl.Vertex3fv = glVertex3fv; + gl.Vertex3d = glVertex3d; + + gl.Color4f = glColor4f; + gl.Color4fv = glColor4fv; + gl.Color3f = glColor3f; + gl.Color3ub = glColor3ub; + gl.Color4ub = glColor4ub; + + gl.ColorMask = glColorMask; + + gl.DepthFunc = glDepthFunc; + gl.DepthMask = glDepthMask; + gl.DepthRange = glDepthRange; + + gl.StencilFunc = glStencilFunc; + gl.StencilMask = glStencilMask; + gl.StencilOp = glStencilOp; + + gl.MatrixMode = glMatrixMode; + gl.PushMatrix = glPushMatrix; + gl.PopMatrix = glPopMatrix; + gl.LoadIdentity = glLoadIdentity; + gl.MultMatrixd = glMultMatrixd; + gl.Translatef = glTranslatef; + gl.Ortho = glOrtho; + gl.Scalef = glScalef; + gl.Rotatef = glRotatef; + + gl.Viewport = glViewport; + gl.Scissor = glScissor; + + gl.Clear = glClear; + gl.ClearColor = glClearColor; + gl.ClearDepth = glClearDepth; + gl.ShadeModel = glShadeModel; + gl.Hint = glHint; + + gl.DisableClientState = glDisableClientState; + gl.EnableClientState = glEnableClientState; + + gl.Fogf = glFogf; + gl.Fogi = glFogi; + gl.Fogfv = glFogfv; + + gl.Enable = glEnable; + gl.IsEnabled = glIsEnabled; + gl.Disable = glDisable; + + gl.TexGeni = glTexGeni; + gl.DeleteTextures = glDeleteTextures; + gl.GenTextures = glGenTextures; + gl.BindTexture = glBindTexture; + gl.TexImage2D = glTexImage2D; + gl.TexParameterf = glTexParameterf; + gl.TexParameteri = glTexParameteri; + gl.CopyTexSubImage2D = glCopyTexSubImage2D; + + gl.ReadPixels = glReadPixels; + gl.PolygonOffset = glPolygonOffset; + gl.ClipPlane = glClipPlane; + + gl.Finish = glFinish; + gl.Flush = glFlush; + + gl.BlendEquation = glBlendEquationDummy; + gl.SetVSync = SetVSync; + +#if !defined (unix) && !defined (__APPLE__) + ReadInitExtensions(); + //GL is not yet inited in UNIX version, read them later in LoadExtensions +#endif + +} + + + +//} // extern "C" diff --git a/src/gl/system/gl_interface.h b/src/gl/system/gl_interface.h new file mode 100644 index 000000000..8766ebcfa --- /dev/null +++ b/src/gl/system/gl_interface.h @@ -0,0 +1,249 @@ +#ifndef R_RENDER +#define R_RENDER + +#if defined(__APPLE__) +#define APIENTRY +#endif // __APPLE__ + +#ifndef PFNGLMULTITEXCOORD2FPROC +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, GLfloat s, GLfloat t); +#endif +#ifndef PFNGLMULTITEXCOORD2FVPROC +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC) (GLenum target, const GLfloat *v); +#endif + +enum RenderFlags +{ + RFL_NPOT_TEXTURE=1, + RFL_NOSTENCIL=2, + RFL_OCCLUSION_QUERY=4, + // [BB] Added texture compression flags. + RFL_TEXTURE_COMPRESSION=8, + RFL_TEXTURE_COMPRESSION_S3TC=16, + + RFL_VBO = 32, + RFL_MAP_BUFFER_RANGE = 64, + RFL_FRAMEBUFFER = 128, + RFL_TEXTUREBUFFER = 256, + RFL_NVIDIA = 512, + RFL_ATI = 1024, + + + RFL_GL_20 = 0x10000000, + RFL_GL_21 = 0x20000000, + RFL_GL_30 = 0x40000000, +}; + +enum TexMode +{ + TMF_MASKBIT = 1, + TMF_OPAQUEBIT = 2, + TMF_INVERTBIT = 4, + + TM_MODULATE = 0, + TM_MASK = TMF_MASKBIT, + TM_OPAQUE = TMF_OPAQUEBIT, + TM_INVERT = TMF_INVERTBIT, + //TM_INVERTMASK = TMF_MASKBIT | TMF_INVERTBIT + TM_INVERTOPAQUE = TMF_INVERTBIT | TMF_OPAQUEBIT, +}; + +struct RenderContext +{ + unsigned int flags; + unsigned int shadermodel; + unsigned int maxuniforms; + int max_texturesize; + char * vendorstring; + + int MaxLights() const + { + return maxuniforms>=2048? 128:64; + } + + void (APIENTRY * LoadExtensions) (); + void (APIENTRY * SetTextureMode) (int type); + void (APIENTRY * PrintStartupLog) (); + BOOL (APIENTRY * SetVSync) (int on); +#if !defined (unix) && !defined (__APPLE__) + bool (APIENTRY * InitHardware) (HWND, bool allowsoftware, bool nostencil, int multisample); + void (APIENTRY * Shutdown) (); +#else + bool (APIENTRY * InitHardware) (bool allowsoftware, bool nostencil, int multisample); +#endif + void (APIENTRY * SwapBuffers) (); + bool (APIENTRY * SetFullscreen) (const char *devicename, int w, int h, int bits, int hz); + + + void (APIENTRY * Begin) (GLenum mode); + void (APIENTRY * End) (void); + void (APIENTRY * DrawArrays) (GLenum mode, GLint first, GLsizei count); + + void (APIENTRY * TexCoord2f) (GLfloat s, GLfloat t); + void (APIENTRY * TexCoord2fv) (const GLfloat *v); + + void (APIENTRY * Vertex2f) (GLfloat x, GLfloat y); + void (APIENTRY * Vertex2i) (GLint x, GLint y); + void (APIENTRY * Vertex3f) (GLfloat x, GLfloat y, GLfloat z); + void (APIENTRY * Vertex3d) (GLdouble x, GLdouble y, GLdouble z); + void (APIENTRY * Vertex3fv) (const GLfloat *v); + + void (APIENTRY * Color4f) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); + void (APIENTRY * Color4fv) (const GLfloat *v); + void (APIENTRY * Color3f) (GLfloat red, GLfloat green, GLfloat blue); + void (APIENTRY * Color3ub) (GLubyte red, GLubyte green, GLubyte blue); + void (APIENTRY * Color4ub) (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); + + void (APIENTRY * BlendEquation) (GLenum); + void (APIENTRY * ColorMask) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); + + void (APIENTRY * DepthFunc) (GLenum func); + void (APIENTRY * DepthMask) (GLboolean flag); + void (APIENTRY * DepthRange) (GLclampd zNear, GLclampd zFar); + + void (APIENTRY * StencilFunc) (GLenum func, GLint ref, GLuint mask); + void (APIENTRY * StencilMask) (GLuint mask); + void (APIENTRY * StencilOp) (GLenum fail, GLenum zfail, GLenum zpass); + + void (APIENTRY * MatrixMode) (GLenum mode); + void (APIENTRY * PushMatrix) (void); + void (APIENTRY * PopMatrix) (void); + void (APIENTRY * LoadIdentity) (void); + void (APIENTRY * MultMatrixd) (const GLdouble *m); + void (APIENTRY * Translatef) (GLfloat x, GLfloat y, GLfloat z); + void (APIENTRY * Ortho) (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); + void (APIENTRY * Scalef) (GLfloat x, GLfloat y, GLfloat z); + void (APIENTRY * Rotatef) (GLfloat angle, GLfloat x, GLfloat y, GLfloat z); + + void (APIENTRY * Viewport) (GLint x, GLint y, GLsizei width, GLsizei height); + void (APIENTRY * Scissor) (GLint x, GLint y, GLsizei width, GLsizei height); + + void (APIENTRY * Clear) (GLbitfield mask); + void (APIENTRY * ClearColor) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha); + void (APIENTRY * ClearDepth) (GLclampd depth); + void (APIENTRY * ShadeModel) (GLenum mode); + void (APIENTRY * Hint) (GLenum target, GLenum mode); + + void (APIENTRY * DisableClientState) (GLenum array); + void (APIENTRY * EnableClientState) (GLenum array); + + void (APIENTRY * Fogf) (GLenum pname, GLfloat param); + void (APIENTRY * Fogi) (GLenum pname, GLint param); + void (APIENTRY * Fogfv) (GLenum pname, const GLfloat *params); + + void (APIENTRY * Enable) (GLenum cap); + GLboolean (APIENTRY * IsEnabled) (GLenum cap); + void (APIENTRY * Disable) (GLenum cap); + + void (APIENTRY * TexGeni) (GLenum coord, GLenum pname, GLint param); + void (APIENTRY * DeleteTextures) (GLsizei n, const GLuint *textures); + void (APIENTRY * GenTextures) (GLsizei n, GLuint *textures); + void (APIENTRY * BindTexture) (GLenum target, GLuint texture); + void (APIENTRY * TexImage2D) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); + void (APIENTRY * TexParameterf) (GLenum target, GLenum pname, GLfloat param); + void (APIENTRY * TexParameteri) (GLenum target, GLenum pname, GLint param); + void (APIENTRY * CopyTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); + + void (APIENTRY * ReadPixels) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); + void (APIENTRY * PolygonOffset) (GLfloat factor, GLfloat units); + void (APIENTRY * ClipPlane) (GLenum which, const GLdouble *); + + void (APIENTRY * Finish) (void); + void (APIENTRY * Flush) (void); + + // ARB_SHADER_OBJECTS + PFNGLDELETESHADERPROC DeleteShader; + PFNGLDELETEPROGRAMPROC DeleteProgram; + PFNGLDETACHSHADERPROC DetachShader; + PFNGLCREATESHADERPROC CreateShader; + PFNGLSHADERSOURCEPROC ShaderSource; + PFNGLCOMPILESHADERPROC CompileShader; + PFNGLCREATEPROGRAMPROC CreateProgram; + PFNGLATTACHSHADERPROC AttachShader; + PFNGLLINKPROGRAMPROC LinkProgram; + PFNGLUSEPROGRAMPROC UseProgram; + PFNGLVALIDATEPROGRAMPROC ValidateProgram; + PFNGLVERTEXATTRIB1FPROC VertexAttrib1f; + PFNGLVERTEXATTRIB2FPROC VertexAttrib2f; + PFNGLVERTEXATTRIB4FPROC VertexAttrib4f; + PFNGLVERTEXATTRIB2FVPROC VertexAttrib2fv; + PFNGLVERTEXATTRIB3FVPROC VertexAttrib3fv; + PFNGLVERTEXATTRIB4FVPROC VertexAttrib4fv; + PFNGLVERTEXATTRIB4UBVPROC VertexAttrib4ubv; + PFNGLGETATTRIBLOCATIONPROC GetAttribLocation; + PFNGLBINDATTRIBLOCATIONPROC BindAttribLocation; + + PFNGLUNIFORM1FPROC Uniform1f; + PFNGLUNIFORM2FPROC Uniform2f; + PFNGLUNIFORM3FPROC Uniform3f; + PFNGLUNIFORM4FPROC Uniform4f; + PFNGLUNIFORM1IPROC Uniform1i; + PFNGLUNIFORM2IPROC Uniform2i; + PFNGLUNIFORM3IPROC Uniform3i; + PFNGLUNIFORM4IPROC Uniform4i; + PFNGLUNIFORM1FVPROC Uniform1fv; + PFNGLUNIFORM2FVPROC Uniform2fv; + PFNGLUNIFORM3FVPROC Uniform3fv; + PFNGLUNIFORM4FVPROC Uniform4fv; + PFNGLUNIFORM1IVPROC Uniform1iv; + PFNGLUNIFORM2IVPROC Uniform2iv; + PFNGLUNIFORM3IVPROC Uniform3iv; + PFNGLUNIFORM4IVPROC Uniform4iv; + + PFNGLUNIFORMMATRIX2FVPROC UniformMatrix2fv; + PFNGLUNIFORMMATRIX3FVPROC UniformMatrix3fv; + PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv; + + PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog; + PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog; + PFNGLGETUNIFORMLOCATIONPROC GetUniformLocation; + PFNGLGETACTIVEUNIFORMPROC GetActiveUniform; + PFNGLGETUNIFORMFVPROC GetUniformfv; + PFNGLGETUNIFORMIVPROC GetUniformiv; + PFNGLGETSHADERSOURCEPROC GetShaderSource; + PFNGLGETOBJECTPARAMETERIVARBPROC GetObjectParameteriv; + + PFNGLGENQUERIESARBPROC GenQueries; + PFNGLDELETEQUERIESARBPROC DeleteQueries; + PFNGLBEGINQUERYARBPROC BeginQuery; + PFNGLENDQUERYARBPROC EndQuery; + PFNGLGETQUERYOBJECTUIVARBPROC GetQueryObjectuiv; + + PFNGLACTIVETEXTUREPROC ActiveTexture; + PFNGLMULTITEXCOORD2FPROC MultiTexCoord2f; + PFNGLMULTITEXCOORD2FVPROC MultiTexCoord2fv; + + PFNGLBINDBUFFERPROC BindBuffer; + PFNGLDELETEBUFFERSPROC DeleteBuffers; + PFNGLGENBUFFERSPROC GenBuffers; + PFNGLBUFFERDATAPROC BufferData; + PFNGLBUFFERSUBDATAPROC BufferSubData; + PFNGLMAPBUFFERPROC MapBuffer; + PFNGLUNMAPBUFFERPROC UnmapBuffer; + PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray; + PFNGLDISABLEVERTEXATTRIBARRAYPROC DisableVertexAttribArray; + PFNGLVERTEXATTRIBPOINTERPROC VertexAttribPointer; + + PFNGLMAPBUFFERRANGEPROC MapBufferRange; + PFNGLFLUSHMAPPEDBUFFERRANGEPROC FlushMappedBufferRange; + + PFNGLGENFRAMEBUFFERSPROC GenFramebuffers; + PFNGLDELETEFRAMEBUFFERSPROC DeleteFramebuffers; + PFNGLBINDFRAMEBUFFERPROC BindFramebuffer; + PFNGLFRAMEBUFFERTEXTURE2DPROC FramebufferTexture2D; + PFNGLGENRENDERBUFFERSPROC GenRenderbuffers; + PFNGLDELETERENDERBUFFERSPROC DeleteRenderbuffers; + PFNGLBINDRENDERBUFFERPROC BindRenderbuffer; + PFNGLRENDERBUFFERSTORAGEPROC RenderbufferStorage; + PFNGLFRAMEBUFFERRENDERBUFFERPROC FramebufferRenderbuffer; + + PFNGLTEXBUFFERARBPROC TexBufferARB; +}; + + +typedef void (APIENTRY * GetContextProc)(RenderContext & gl); + +void APIENTRY GetContext(RenderContext & gl); + +#endif + diff --git a/src/gl/system/gl_menu.cpp b/src/gl/system/gl_menu.cpp new file mode 100644 index 000000000..5429da9c7 --- /dev/null +++ b/src/gl/system/gl_menu.cpp @@ -0,0 +1,137 @@ + + +#include "gl/system/gl_system.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "v_video.h" +#include "version.h" +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_renderer.h" +#include "menu/menu.h" + + + +// OpenGL stuff moved here +// GL related CVARs +CVAR(Bool, gl_portals, true, 0) +CVAR(Bool, gl_noquery, false, 0) +CVAR(Bool,gl_mirrors,true,0) // This is for debugging only! +CVAR(Bool,gl_mirror_envmap, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) +CVAR(Bool, gl_render_segs, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Bool, gl_seamless, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Bool, gl_vid_compatibility, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +CUSTOM_CVAR(Int, r_mirror_recursions,4,CVAR_GLOBALCONFIG|CVAR_ARCHIVE) +{ + if (self<0) self=0; + if (self>10) self=10; +} +bool gl_plane_reflection_i; // This is needed in a header that cannot include the CVAR stuff... +CUSTOM_CVAR(Bool, gl_plane_reflection, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE) +{ + gl_plane_reflection_i = self; +} + +CUSTOM_CVAR(Bool, gl_render_precise, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + //gl_render_segs=self; + gl_seamless=self; +} + +CUSTOM_CVAR (Float, vid_brightness, 0.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma(Gamma); //Brightness (self); + } +} + +CUSTOM_CVAR (Float, vid_contrast, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma(Gamma); //SetContrast (self); + } +} + + +// Do some tinkering with the menus so that certain options only appear +// when they are actually valid. +void gl_SetupMenu() +{ +#ifndef _MSC_VER + FOptionValues **opt = OptionValues.CheckKey("HqResizeModes"); + if (opt != NULL) + { + for(int i = (*opt)->mValues.Size()-1; i>=0; i--) + { + // Delete HQnX resize modes for non MSVC targets + if ((*opt)->mValues[i].Value >= 4.0) + { + (*opt)->mValues.Delete(i); + } + } + } +#endif + + if (gl.shadermodel < 4) + { + // Radial fog and Doom lighting are not available in SM < 4 cards + // The way they are implemented does not work well on older hardware. + + FOptionValues **opt = OptionValues.CheckKey("LightingModes"); + if (opt != NULL) + { + for(int i = (*opt)->mValues.Size()-1; i>=0; i--) + { + // Delete 'Doom' lighting mode + if ((*opt)->mValues[i].Value == 2.0 || (*opt)->mValues[i].Value == 8.0) + { + (*opt)->mValues.Delete(i); + } + } + } + + opt = OptionValues.CheckKey("FogMode"); + if (opt != NULL) + { + for(int i = (*opt)->mValues.Size()-1; i>=0; i--) + { + // Delete 'Radial' fog mode + if ((*opt)->mValues[i].Value == 2.0) + { + (*opt)->mValues.Delete(i); + } + } + } + + // disable features that don't work without shaders. + if (gl_lightmode == 2 || gl_lightmode == 8) gl_lightmode = 3; + if (gl_fogmode == 2) gl_fogmode = 1; + if (gl_dynlight_shader) gl_dynlight_shader = false; + } + + if (gl.shadermodel != 3) + { + // The shader menu will only be visible on SM3. + // SM2 won't use shaders unless unavoidable (and then it's automatic) and SM4 will always use shaders. + // Find the OpenGLOptions menu and remove the item named GLShaderOptions. + + FMenuDescriptor **desc = MenuDescriptors.CheckKey("OpenGLOptions"); + if (desc != NULL && (*desc)->mType == MDESC_OptionsMenu) + { + FOptionMenuDescriptor *opt = (FOptionMenuDescriptor *)*desc; + + FName shader = "GLShaderOptions"; + for(unsigned i=0;imItems.Size();i++) + { + FName nm = opt->mItems[i]->GetAction(NULL); + if (nm == shader) + { + delete opt->mItems[i]; + opt->mItems.Delete(i); + } + } + } + } +} diff --git a/src/gl/system/gl_system.h b/src/gl/system/gl_system.h new file mode 100644 index 000000000..d48b55976 --- /dev/null +++ b/src/gl/system/gl_system.h @@ -0,0 +1,124 @@ +#ifndef __GL_PCH_H +#define __GL_PCH_H +#ifdef _WIN32 +//#define __RPCNDR_H__ // this header causes problems! +//#define __wtypes_h__ +#define WIN32_LEAN_AND_MEAN +#define _WIN32_WINDOWS 0x410 +#define _WIN32_WINNT 0x0501 // Support the mouse wheel and session notification. +#define _WIN32_IE 0x0500 +#define DIRECTINPUT_VERSION 0x800 +#define DIRECTDRAW_VERSION 0x0300 + +#define DWORD WINDOWS_DWORD // I don't want to depend on this throughout the GL code! + +#ifdef _MSC_VER +#pragma warning(disable : 4995) // MIPS +#endif + +#include +#include +#include +#ifndef __WINE__ +#include +#endif +#include +//#include +//#include +//#include +//#include +#endif + +#undef DWORD +#ifndef CALLBACK +#define CALLBACK +#endif +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(__APPLE__) +#include +#endif +#include + +#ifdef _MSC_VER +#define F_OK 0 /* Check for file existence */ +#define W_OK 2 /* Check for write permission */ +#define R_OK 4 /* Check for read permission */ +#include +#else +#include +#endif +#include +#include +#include + +#if defined(__APPLE__) +#include +#include +#else // !__APPLE__ +#include +#include +#include +#endif // __APPLE__ + +#ifdef _WIN32 +#define DWORD WINDOWS_DWORD // I don't want to depend on this throughout the GL code! +#include +#ifndef __WINE__ +#undef DWORD +#endif +#else +typedef unsigned char byte; +typedef float FLOAT; +template +inline T max( T a, T b) { return (((a)>(b)) ? (a) : (b)); } +#define __cdecl +#define _access(a,b) access(a,b) +#endif +#ifndef _WIN32 +#include "platform.h" +#include +#endif +#include "gl/system/gl_interface.h" + +extern RenderContext gl; + +#ifdef LoadMenu +#undef LoadMenu +#endif +#ifdef DrawText +#undef DrawText +#endif +#ifdef GetCharWidth +#undef GetCharWidth +#endif + +#undef S_NORMAL +#undef OPAQUE + + +#ifdef _MSC_VER +#pragma warning(disable : 4244) // MIPS +#pragma warning(disable : 4136) // X86 +#pragma warning(disable : 4051) // ALPHA + +#pragma warning(disable : 4018) // signed/unsigned mismatch +#pragma warning(disable : 4305) // truncate from double to float +#endif + +#ifdef WIN32 +#undef WIN32 +#endif +#endif //__GL_PCH_H diff --git a/src/gl/system/gl_threads.cpp b/src/gl/system/gl_threads.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/gl/system/gl_threads.h b/src/gl/system/gl_threads.h new file mode 100644 index 000000000..68919776c --- /dev/null +++ b/src/gl/system/gl_threads.h @@ -0,0 +1,247 @@ +#if 0 //ndef __GL_THREADS_H +#define __GL_THREADS_H + +#ifdef WIN32 +#include +#endif + +#include "critsec.h" + +// system specific Base classes - should be externalized to a separate header later. +#ifdef WIN32 +class FEvent +{ + HANDLE mEvent; + +public: + + FEvent(bool manual = true, bool initial = false) + { + mEvent = CreateEvent(NULL, manual, initial, NULL); + } + + ~FEvent() + { + CloseHandle(mEvent); + } + + void Set() + { + SetEvent(mEvent); + } + + void Reset() + { + ResetEvent(mEvent); + } +}; + +class FThread +{ +protected: + uintptr_t hThread; + bool mTerminateRequest; +public: + + FThread(int stacksize) + { + hThread = _beginthreadex(NULL, stacksize, StaticRun, this, 0, NULL); + } + + virtual ~FThread() + { + CloseHandle((HANDLE)hThread); + } + + void SignalTerminate() + { + mTerminateRequest = true; + } + + virtual void Run() = 0; + +private: + static unsigned __stdcall StaticRun(void *param) + { + FThread *thread = (FThread*)param; + thread->Run(); + return 0; + } + +}; + +#else +class FEvent +{ +public: + + FEvent(bool manual = true, bool initial = false) + { + } + + ~FEvent() + { + } + + void Set() + { + } + + void Reset() + { + } +}; + +class FThread +{ +protected: + bool mTerminateRequest; +public: + + FThread(int stacksize) + { + } + + virtual ~FThread() + { + } + + void SignalTerminate() + { + mTerminateRequest = true; + } + + virtual void Run() = 0; +}; + +#endif + + + +enum +{ + CS_ValidateTexture, + CS_Hacks, + CS_Drawlist, + CS_Portals, + + MAX_GL_CRITICAL_SECTIONS +}; + +class FJob +{ + friend class FJobQueue; + FJob *pNext; + FJob *pPrev; + +public: + FJob() { pNext = pPrev = NULL; } + virtual ~FJob(); + virtual void Run() = 0; +}; + +class FJobQueue +{ + FCriticalSection mCritSec; // for limiting access + FEvent mEvent; // signals that the queue is emoty or not + FJob *pFirst; + FJob *pLast; + +public: + FJobQueue(); + ~FJobQueue(); + + void AddJob(FJob *job); + FJob *GetJob(); +}; + +class FJobThread : public FThread +{ + FJobQueue *mQueue; + +public: + FJobThread(FJobQueue *queue); + ~FJobThread(); + void Run(); +}; + +class FGLJobProcessWall : public FJob +{ + seg_t *mSeg; + subsector_t *mSub; + int mFrontsector, mBacksector; +public: + FGLJobProcessWall(seg_t *seg, subsector_t *sub, int frontsector, int backsector) + { + mSeg = seg; + mSub = sub; + mFrontsector = frontsector; + mBacksector = backsector; + } + + void Run(); +}; + +class FGLJobProcessSprites : public FJob +{ + sector_t *mSector; +public: + FGLJobProcessSprites(sector_t *sector); + void Run(); +}; + +class FGLJobProcessParticles : public FJob +{ + subsector_t *mSubsector; +public: + FGLJobProcessParticles(subsector_t *subsector); + void Run(); +}; + +class FGLJobProcessFlats : public FJob +{ + subsector_t *mSubsector; +public: + FGLJobProcessFlats(subsector_t *subsector); + void Run(); +}; + + +class FGLThreadManager +{ +#if 0 + FCriticalSection mCritSecs[MAX_GL_CRITICAL_SECTIONS]; + FJobQueue mJobs; + FJobThread *mThreads[2]; + +public: + FGLThreadManager() {} + ~FGLThreadManager() {} + + void StartJobs(); + + void PauseJobs(); + + void EnterCS(int index) + { + mCritSecs[index].Enter(); + } + + void LeaveCS(int index) + { + mCritSecs[index].Leave(); + } + + void AddJob(FJob *job) + { + mJobs.AddJob(job); + } + + FJob *GetJob() + { + return mJobs.GetJob(); + } +#endif +}; + +#endif \ No newline at end of file diff --git a/src/gl/system/gl_wipe.cpp b/src/gl/system/gl_wipe.cpp new file mode 100644 index 000000000..f9f73a4ea --- /dev/null +++ b/src/gl/system/gl_wipe.cpp @@ -0,0 +1,551 @@ +/* +** gl_wipe.cpp +** Screen wipe stuff +** (This uses immediate mode and the fixed function pipeline +** even if the new renderer is active) +** +**--------------------------------------------------------------------------- +** Copyright 2008 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "files.h" +#include "f_wipe.h" +#include "m_random.h" +#include "w_wad.h" +#include "v_palette.h" +#include "templates.h" +#include "vectors.h" + +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_renderstate.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/textures/gl_translate.h" +#include "gl/textures/gl_material.h" +#include "gl/utility/gl_templates.h" + +EXTERN_CVAR(Bool, gl_vid_compatibility) + +//=========================================================================== +// +// Screen wipes +// +//=========================================================================== + +// TYPES ------------------------------------------------------------------- + +class OpenGLFrameBuffer::Wiper_Crossfade : public OpenGLFrameBuffer::Wiper +{ +public: + Wiper_Crossfade(); + bool Run(int ticks, OpenGLFrameBuffer *fb); + +private: + int Clock; +}; + +class OpenGLFrameBuffer::Wiper_Melt : public OpenGLFrameBuffer::Wiper +{ +public: + Wiper_Melt(); + bool Run(int ticks, OpenGLFrameBuffer *fb); + +private: + static const int WIDTH = 320, HEIGHT = 200; + int y[WIDTH]; +}; + +class OpenGLFrameBuffer::Wiper_Burn : public OpenGLFrameBuffer::Wiper +{ +public: + Wiper_Burn(); + ~Wiper_Burn(); + bool Run(int ticks, OpenGLFrameBuffer *fb); + +private: + static const int WIDTH = 64, HEIGHT = 64; + BYTE BurnArray[WIDTH * (HEIGHT + 5)]; + FHardwareTexture *BurnTexture; + int Density; + int BurnTime; +}; + +//========================================================================== +// +// OpenGLFrameBuffer :: WipeStartScreen +// +// Called before the current screen has started rendering. This needs to +// save what was drawn the previous frame so that it can be animated into +// what gets drawn this frame. +// +//========================================================================== + +bool OpenGLFrameBuffer::WipeStartScreen(int type) +{ + if (gl_vid_compatibility) + { + return false; // not all required features present. + } + switch (type) + { + case wipe_Burn: + ScreenWipe = new Wiper_Burn; + break; + + case wipe_Fade: + ScreenWipe = new Wiper_Crossfade; + break; + + case wipe_Melt: + ScreenWipe = new Wiper_Melt; + break; + + default: + return false; + } + + wipestartscreen = new FHardwareTexture(Width, Height, false, false, false, true); + wipestartscreen->CreateTexture(NULL, Width, Height, false, 0, CM_DEFAULT); + gl.Finish(); + wipestartscreen->Bind(0, CM_DEFAULT); + gl.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + return true; +} + +//========================================================================== +// +// OpenGLFrameBuffer :: WipeEndScreen +// +// The screen we want to animate to has just been drawn. +// +//========================================================================== + +void OpenGLFrameBuffer::WipeEndScreen() +{ + wipeendscreen = new FHardwareTexture(Width, Height, false, false, false, true); + wipeendscreen->CreateTexture(NULL, Width, Height, false, 0, CM_DEFAULT); + gl.Flush(); + wipeendscreen->Bind(0, CM_DEFAULT); + gl.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + Unlock(); +} + +//========================================================================== +// +// OpenGLFrameBuffer :: WipeDo +// +// Perform the actual wipe animation. The number of tics since the last +// time this function was called is passed in. Returns true when the wipe +// is over. The first time this function has been called, the screen is +// still locked from before and EndScene() still has not been called. +// Successive times need to call BeginScene(). +// +//========================================================================== + +bool OpenGLFrameBuffer::WipeDo(int ticks) +{ + // Sanity checks. + if (wipestartscreen == NULL || wipeendscreen == NULL) + { + return true; + } + + Lock(true); + + gl_RenderState.EnableTexture(true); + gl_RenderState.EnableFog(false); + gl.Disable(GL_DEPTH_TEST); + gl.DepthMask(false); + + bool done = ScreenWipe->Run(ticks, this); + gl.DepthMask(true); + //DrawLetterbox(); + return done; +} + +//========================================================================== +// +// OpenGLFrameBuffer :: WipeCleanup +// +// Release any resources that were specifically created for the wipe. +// +//========================================================================== + +void OpenGLFrameBuffer::WipeCleanup() +{ + if (ScreenWipe != NULL) + { + delete ScreenWipe; + ScreenWipe = NULL; + } + if (wipestartscreen != NULL) + { + delete wipestartscreen; + wipestartscreen = NULL; + } + if (wipeendscreen != NULL) + { + delete wipeendscreen; + wipeendscreen = NULL; + } +} + +//========================================================================== +// +// OpenGLFrameBuffer :: Wiper Constructor +// +//========================================================================== + +OpenGLFrameBuffer::Wiper::~Wiper() +{ +} + +// WIPE: CROSSFADE --------------------------------------------------------- + +//========================================================================== +// +// OpenGLFrameBuffer :: Wiper_Crossfade Constructor +// +//========================================================================== + +OpenGLFrameBuffer::Wiper_Crossfade::Wiper_Crossfade() +: Clock(0) +{ +} + +//========================================================================== +// +// OpenGLFrameBuffer :: Wiper_Crossfade :: Run +// +// Fades the old screen into the new one over 32 ticks. +// +//========================================================================== + +bool OpenGLFrameBuffer::Wiper_Crossfade::Run(int ticks, OpenGLFrameBuffer *fb) +{ + Clock += ticks; + + float ur = fb->GetWidth() / FHardwareTexture::GetTexDimension(fb->GetWidth()); + float vb = fb->GetHeight() / FHardwareTexture::GetTexDimension(fb->GetHeight()); + + gl_RenderState.SetTextureMode(TM_OPAQUE); + gl_RenderState.EnableAlphaTest(false); + gl_RenderState.Apply(); + fb->wipestartscreen->Bind(0, CM_DEFAULT); + gl.Color4f(1.f, 1.f, 1.f, 1.f); + gl.Begin(GL_TRIANGLE_STRIP); + gl.TexCoord2f(0, vb); + gl.Vertex2i(0, 0); + gl.TexCoord2f(0, 0); + gl.Vertex2i(0, fb->Height); + gl.TexCoord2f(ur, vb); + gl.Vertex2i(fb->Width, 0); + gl.TexCoord2f(ur, 0); + gl.Vertex2i(fb->Width, fb->Height); + gl.End(); + + fb->wipeendscreen->Bind(0, CM_DEFAULT); + gl.Color4f(1.f, 1.f, 1.f, clamp(Clock/32.f, 0.f, 1.f)); + gl.Begin(GL_TRIANGLE_STRIP); + gl.TexCoord2f(0, vb); + gl.Vertex2i(0, 0); + gl.TexCoord2f(0, 0); + gl.Vertex2i(0, fb->Height); + gl.TexCoord2f(ur, vb); + gl.Vertex2i(fb->Width, 0); + gl.TexCoord2f(ur, 0); + gl.Vertex2i(fb->Width, fb->Height); + gl.End(); + gl_RenderState.EnableAlphaTest(true); + gl_RenderState.SetTextureMode(TM_MODULATE); + + return Clock >= 32; +} + +//========================================================================== +// +// OpenGLFrameBuffer :: Wiper_Melt Constructor +// +//========================================================================== + +OpenGLFrameBuffer::Wiper_Melt::Wiper_Melt() +{ + int i, r; + + // setup initial column positions + // (y<0 => not ready to scroll yet) + y[0] = -(M_Random() & 15); + for (i = 1; i < WIDTH; ++i) + { + r = (M_Random()%3) - 1; + y[i] = clamp(y[i-1] + r, -15, 0); + } +} + +//========================================================================== +// +// OpenGLFrameBuffer :: Wiper_Melt :: Run +// +// Fades the old screen into the new one over 32 ticks. +// +//========================================================================== + +bool OpenGLFrameBuffer::Wiper_Melt::Run(int ticks, OpenGLFrameBuffer *fb) +{ + float ur = fb->GetWidth() / FHardwareTexture::GetTexDimension(fb->GetWidth()); + float vb = fb->GetHeight() / FHardwareTexture::GetTexDimension(fb->GetHeight()); + + // Draw the new screen on the bottom. + gl_RenderState.SetTextureMode(TM_OPAQUE); + gl_RenderState.Apply(); + fb->wipeendscreen->Bind(0, CM_DEFAULT); + gl.Color4f(1.f, 1.f, 1.f, 1.f); + gl.Begin(GL_TRIANGLE_STRIP); + gl.TexCoord2f(0, vb); + gl.Vertex2i(0, 0); + gl.TexCoord2f(0, 0); + gl.Vertex2i(0, fb->Height); + gl.TexCoord2f(ur, vb); + gl.Vertex2i(fb->Width, 0); + gl.TexCoord2f(ur, 0); + gl.Vertex2i(fb->Width, fb->Height); + gl.End(); + + int i, dy; + bool done = false; + + fb->wipestartscreen->Bind(0, CM_DEFAULT); + // Copy the old screen in vertical strips on top of the new one. + while (ticks--) + { + done = true; + for (i = 0; i < WIDTH; i++) + { + if (y[i] < 0) + { + y[i]++; + done = false; + } + else if (y[i] < HEIGHT) + { + dy = (y[i] < 16) ? y[i]+1 : 8; + y[i] = MIN(y[i] + dy, HEIGHT); + done = false; + } + if (ticks == 0) + { // Only draw for the final tick. + RECT rect; + POINT dpt; + + dpt.x = i * fb->Width / WIDTH; + dpt.y = MAX(0, y[i] * fb->Height / HEIGHT); + rect.left = dpt.x; + rect.top = 0; + rect.right = (i + 1) * fb->Width / WIDTH; + rect.bottom = fb->Height - dpt.y; + if (rect.bottom > rect.top) + { + float tw = (float)FHardwareTexture::GetTexDimension(fb->Width); + float th = (float)FHardwareTexture::GetTexDimension(fb->Height); + rect.bottom = fb->Height - rect.bottom; + rect.top = fb->Height - rect.top; + gl.Color4f(1.f, 1.f, 1.f, 1.f); + gl.Begin(GL_TRIANGLE_STRIP); + gl.TexCoord2f(rect.left / tw, rect.top / th); + gl.Vertex2i(rect.left, rect.bottom); + gl.TexCoord2f(rect.left / tw, rect.bottom / th); + gl.Vertex2i(rect.left, rect.top); + gl.TexCoord2f(rect.right / tw, rect.top / th); + gl.Vertex2i(rect.right, rect.bottom); + gl.TexCoord2f(rect.right / tw, rect.bottom / th); + gl.Vertex2i(rect.right, rect.top); + gl.End(); + } + } + } + } + gl_RenderState.SetTextureMode(TM_MODULATE); + return done; +} + +//========================================================================== +// +// OpenGLFrameBuffer :: Wiper_Burn Constructor +// +//========================================================================== + +OpenGLFrameBuffer::Wiper_Burn::Wiper_Burn() +{ + Density = 4; + BurnTime = 0; + memset(BurnArray, 0, sizeof(BurnArray)); + BurnTexture = NULL; +} + +//========================================================================== +// +// OpenGLFrameBuffer :: Wiper_Burn Destructor +// +//========================================================================== + +OpenGLFrameBuffer::Wiper_Burn::~Wiper_Burn() +{ + if (BurnTexture != NULL) + { + delete BurnTexture; + BurnTexture = NULL; + } +} + +//========================================================================== +// +// OpenGLFrameBuffer :: Wiper_Burn :: Run +// +//========================================================================== + +bool OpenGLFrameBuffer::Wiper_Burn::Run(int ticks, OpenGLFrameBuffer *fb) +{ + bool done; + + BurnTime += ticks; + ticks *= 2; + + // Make the fire burn + done = false; + while (!done && ticks--) + { + Density = wipe_CalcBurn(BurnArray, WIDTH, HEIGHT, Density); + done = (Density < 0); + } + + if (BurnTexture != NULL) delete BurnTexture; + BurnTexture = new FHardwareTexture(WIDTH, HEIGHT, false, false, false, true); + + // Update the burn texture with the new burn data + BYTE rgb_buffer[WIDTH*HEIGHT*4]; + + const BYTE *src = BurnArray; + DWORD *dest = (DWORD *)rgb_buffer; + for (int y = HEIGHT; y != 0; --y) + { + for (int x = WIDTH; x != 0; --x) + { + BYTE s = clamp((*src++)*2, 0, 255); + *dest++ = MAKEARGB(s,255,255,255); + } + } + + float ur = fb->GetWidth() / FHardwareTexture::GetTexDimension(fb->GetWidth()); + float vb = fb->GetHeight() / FHardwareTexture::GetTexDimension(fb->GetHeight()); + + + // Put the initial screen back to the buffer. + gl_RenderState.SetTextureMode(TM_OPAQUE); + gl_RenderState.EnableAlphaTest(false); + gl_RenderState.Apply(); + fb->wipestartscreen->Bind(0, CM_DEFAULT); + gl.Color4f(1.f, 1.f, 1.f, 1.f); + gl.Begin(GL_TRIANGLE_STRIP); + gl.TexCoord2f(0, vb); + gl.Vertex2i(0, 0); + gl.TexCoord2f(0, 0); + gl.Vertex2i(0, fb->Height); + gl.TexCoord2f(ur, vb); + gl.Vertex2i(fb->Width, 0); + gl.TexCoord2f(ur, 0); + gl.Vertex2i(fb->Width, fb->Height); + gl.End(); + + gl_RenderState.SetTextureMode(TM_MODULATE); + gl_RenderState.Apply(true); + gl.ActiveTexture(GL_TEXTURE1); + gl.Enable(GL_TEXTURE_2D); + + // mask out the alpha channel of the wipeendscreen. + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE1); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + + gl.ActiveTexture(GL_TEXTURE0); + + // Burn the new screen on top of it. + gl.Color4f(1.f, 1.f, 1.f, 1.f); + fb->wipeendscreen->Bind(1, CM_DEFAULT); + //BurnTexture->Bind(0, CM_DEFAULT); + + BurnTexture->CreateTexture(rgb_buffer, WIDTH, HEIGHT, false, 0, CM_DEFAULT); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + + gl.Begin(GL_TRIANGLE_STRIP); + gl.MultiTexCoord2f(GL_TEXTURE0, 0, 0); + gl.MultiTexCoord2f(GL_TEXTURE1, 0, vb); + gl.Vertex2i(0, 0); + gl.MultiTexCoord2f(GL_TEXTURE0, 0, 1); + gl.MultiTexCoord2f(GL_TEXTURE1, 0, 0); + gl.Vertex2i(0, fb->Height); + gl.MultiTexCoord2f(GL_TEXTURE0, 1, 0); + gl.MultiTexCoord2f(GL_TEXTURE1, ur, vb); + gl.Vertex2i(fb->Width, 0); + gl.MultiTexCoord2f(GL_TEXTURE0, 1, 1); + gl.MultiTexCoord2f(GL_TEXTURE1, ur, 0); + gl.Vertex2i(fb->Width, fb->Height); + gl.End(); + + gl.ActiveTexture(GL_TEXTURE1); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + gl.Disable(GL_TEXTURE_2D); + gl.ActiveTexture(GL_TEXTURE0); + + // The fire may not always stabilize, so the wipe is forced to end + // after an arbitrary maximum time. + return done || (BurnTime > 40); +} diff --git a/src/gl/textures/gl_bitmap.cpp b/src/gl/textures/gl_bitmap.cpp new file mode 100644 index 000000000..d90fd36b7 --- /dev/null +++ b/src/gl/textures/gl_bitmap.cpp @@ -0,0 +1,286 @@ +/* +** gl_bitmap.cpp +** Bitmap class for texture composition +** +**--------------------------------------------------------------------------- +** Copyright 2004-2009 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** +** 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 "v_palette.h" +#include "templates.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/textures/gl_translate.h" +#include "gl/textures/gl_bitmap.h" + +//=========================================================================== +// +// multi-format pixel copy with colormap application +// requires one of the previously defined conversion classes to work +// +//=========================================================================== +template +void iCopyColors(unsigned char * pout, const unsigned char * pin, int cm, int count, int step) +{ + int i; + int fac; + + if (cm == CM_DEFAULT) + { + for(i=0;i= CM_FIRSTSPECIALCOLORMAP && cm < CM_FIRSTSPECIALCOLORMAP + int(SpecialColormaps.Size())) + { + for(i=0;i, + iCopyColors, + iCopyColors, + iCopyColors, + iCopyColors, + iCopyColors, + iCopyColors, + iCopyColors, + iCopyColors +}; + +//=========================================================================== +// +// True Color texture copy function +// This excludes all the cases that force downconversion to the +// base palette because they wouldn't be used anyway. +// +//=========================================================================== +void FGLBitmap::CopyPixelDataRGB(int originx, int originy, + const BYTE * patch, int srcwidth, int srcheight, int step_x, int step_y, + int rotate, int ct, FCopyInfo *inf) +{ + if (ClipCopyPixelRect(&ClipRect, originx, originy, patch, srcwidth, srcheight, step_x, step_y, rotate)) + { + BYTE *buffer = GetPixels() + 4*originx + Pitch*originy; + for (int y=0;y= CM_FIRSTSPECIALCOLORMAP && cm < CM_FIRSTSPECIALCOLORMAP + int(SpecialColormaps.Size())) + { + for(i=0;i> 8; + // This can be done in place so we cannot copy the color directly. + PalEntry pe = SpecialColormaps[cm - CM_FIRSTSPECIALCOLORMAP].GrayscaleToColor[gray]; + pout[i].r = pe.r; + pout[i].g = pe.g; + pout[i].b = pe.b; + pout[i].a = pin[i].a; + } + } + else if (cm<=CM_DESAT31) + { + // Desaturated light settings. + fac=cm-CM_DESAT0; + for(i=0;i>8; + gl_Desaturate(gray, pin[i].r, pin[i].g, pin[i].b, pout[i].r, pout[i].g, pout[i].b, fac); + pout[i].a = pin[i].a; + } + } + else if (pin!=pout) + { + memcpy(pout, pin, count * sizeof(PalEntry)); + } +} + + +//=========================================================================== +// +// Paletted to True Color texture copy function +// +//=========================================================================== +void FGLBitmap::CopyPixelData(int originx, int originy, const BYTE * patch, int srcwidth, int srcheight, + int step_x, int step_y, int rotate, PalEntry * palette, FCopyInfo *inf) +{ + PalEntry penew[256]; + + int x,y,pos,i; + + if (ClipCopyPixelRect(&ClipRect, originx, originy, patch, srcwidth, srcheight, step_x, step_y, rotate)) + { + BYTE *buffer = GetPixels() + 4*originx + Pitch*originy; + + // CM_SHADE is an alpha map with 0==transparent and 1==opaque + if (cm == CM_SHADE) + { + for(int i=0;i<256;i++) + { + if (palette[i].a != 0) + penew[i]=PalEntry(i, 255,255,255); + else + penew[i]=PalEntry(0,255,255,255); // If the palette contains transparent colors keep them. + } + } + else + { + // apply any translation. + // The ice and blood color translations are done directly + // because that yields better results. + switch(translation) + { + default: + { + PalEntry *ptrans = GLTranslationPalette::GetPalette(translation); + if (ptrans) + { + for(i = 0; i < 256; i++) + { + penew[i] = (ptrans[i]&0xffffff) | (palette[i]&0xff000000); + } + break; + } + } + + case 0: + memcpy(penew, palette, 256*sizeof(PalEntry)); + break; + } + if (cm!=0) + { + // Apply color modifications like invulnerability, desaturation and Boom colormaps + ModifyPalette(penew, penew, cm, 256); + } + } + // Now penew contains the actual palette that is to be used for creating the image. + + // convert the image according to the translated palette. + for (y=0;y(buffer[pos+3] + (( 255-buffer[pos+3]) * (255-penew[v].a))/255, 0, 255); + } + */ + } + } + } +} diff --git a/src/gl/textures/gl_bitmap.h b/src/gl/textures/gl_bitmap.h new file mode 100644 index 000000000..db3722a6e --- /dev/null +++ b/src/gl/textures/gl_bitmap.h @@ -0,0 +1,34 @@ +#ifndef __GL_BITMAP_H +#define __GL_BITMAP_H + +#include "textures/bitmap.h" +#include "gl/textures/gl_material.h" + + +void ModifyPalette(PalEntry * pout, PalEntry * pin, int cm, int count); + +class FGLBitmap : public FBitmap +{ + int cm; + int translation; +public: + + FGLBitmap() { cm = CM_DEFAULT; translation = 0; } + FGLBitmap(BYTE *buffer, int pitch, int width, int height) + : FBitmap(buffer, pitch, width, height) + { cm = CM_DEFAULT; translation = 0; } + + void SetTranslationInfo(int _cm, int _trans=-1337) + { + if (_cm != -1) cm = _cm; + if (_trans != -1337) translation = _trans; + + } + + virtual void CopyPixelDataRGB(int originx, int originy, const BYTE *patch, int srcwidth, + int srcheight, int step_x, int step_y, int rotate, int ct, FCopyInfo *inf = NULL); + virtual void CopyPixelData(int originx, int originy, const BYTE * patch, int srcwidth, int srcheight, + int step_x, int step_y, int rotate, PalEntry * palette, FCopyInfo *inf = NULL); +}; + +#endif diff --git a/src/gl/textures/gl_hirestex.cpp b/src/gl/textures/gl_hirestex.cpp new file mode 100644 index 000000000..423a783fb --- /dev/null +++ b/src/gl/textures/gl_hirestex.cpp @@ -0,0 +1,373 @@ +/* +** Hires texture management +** +**--------------------------------------------------------------------------- +** 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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. +**--------------------------------------------------------------------------- +** +*/ + +#ifdef _MSC_VER +#define F_OK 0 /* Check for file existence */ +#define W_OK 2 /* Check for write permission */ +#define R_OK 4 /* Check for read permission */ +#include +#else +#include +#endif + +#include "w_wad.h" +#include "m_png.h" +#include "sbar.h" +#include "gi.h" +#include "cmdlib.h" +#include "sc_man.h" +#include "doomstat.h" +#include "d_main.h" +#include "zstring.h" + +#ifdef __GNUC__ +#include "Linux/platform.h" /* Without this it would fail on _access on line 374 (378 now) */ +#endif + +static int Doom2Wad = -1; + +// quick'n dirty hack. Should be enough here... +static void SetDoom2Wad() +{ + if (Doom2Wad == -1) + { + FString iwad = Wads.GetWadFullName(1); + iwad.ToLower(); + if (iwad.IndexOf("plutonia") >= 0) Doom2Wad = 1; + else if (iwad.IndexOf("tnt") >= 0) Doom2Wad = 2; + else Doom2Wad = 0; + } +} +//========================================================================== +// +// Checks for the presence of a hires texture replacement in a Doomsday style PK3 +// +//========================================================================== +int CheckDDPK3(FTexture *tex) +{ + static const char * doom1texpath[]= { + "data/jdoom/textures/doom/%s.%s", "data/jdoom/textures/doom-ult/%s.%s", "data/jdoom/textures/doom1/%s.%s", "data/jdoom/textures/%s.%s", NULL }; + + static const char * doom2texpath[]= { + "data/jdoom/textures/doom2/%s.%s", "data/jdoom/textures/%s.%s", NULL }; + + static const char * pluttexpath[]= { + "data/jdoom/textures/doom2-plut/%s.%s", "data/jdoom/textures/plutonia/%s.%s", "data/jdoom/textures/%s.%s", NULL }; + + static const char * tnttexpath[]= { + "data/jdoom/textures/doom2-tnt/%s.%s", "data/jdoom/textures/tnt/%s.%s", "data/jdoom/textures/%s.%s", NULL }; + + static const char * heretictexpath[]= { + "data/jheretic/textures/%s.%s", NULL }; + + static const char * hexentexpath[]= { + "data/jhexen/textures/%s.%s", NULL }; + + static const char * strifetexpath[]= { + "data/jstrife/textures/%s.%s", NULL }; + + static const char * chextexpath[]= { + "data/jchex/textures/%s.%s", NULL }; + + static const char * doomflatpath[]= { + "data/jdoom/flats/%s.%s", NULL }; + + static const char * hereticflatpath[]= { + "data/jheretic/flats/%s.%s", NULL }; + + static const char * hexenflatpath[]= { + "data/jhexen/flats/%s.%s", NULL }; + + static const char * strifeflatpath[]= { + "data/jstrife/flats/%s.%s", NULL }; + + static const char * chexflatpath[]= { + "data/jchex/flats/%s.%s", NULL }; + + + FString checkName; + const char ** checklist; + BYTE useType=tex->UseType; + + if (useType==FTexture::TEX_SkinSprite || useType==FTexture::TEX_Decal || useType==FTexture::TEX_FontChar) + { + return -3; + } + + bool ispatch = (useType==FTexture::TEX_MiscPatch || useType==FTexture::TEX_Sprite) ; + + // for patches this doesn't work yet + if (ispatch) return -3; + + if (!gameinfo.ConfigName.CompareNoCase("Doom")) + { + if (!(gameinfo.flags & GI_MAPxx)) + { + checklist = useType==FTexture::TEX_Flat? doomflatpath : doom1texpath; + } + else + { + SetDoom2Wad(); + if (Doom2Wad == 1) + checklist = useType==FTexture::TEX_Flat? doomflatpath : pluttexpath; + else if (Doom2Wad == 2) + checklist = useType==FTexture::TEX_Flat? doomflatpath : tnttexpath; + else + checklist = useType==FTexture::TEX_Flat? doomflatpath : doom2texpath; + } + } + else if (!gameinfo.ConfigName.CompareNoCase("Heretic")) + { + checklist = useType==FTexture::TEX_Flat? hereticflatpath : heretictexpath; + } + else if (!gameinfo.ConfigName.CompareNoCase("Hexen")) + { + checklist = useType==FTexture::TEX_Flat? hexenflatpath : hexentexpath; + } + else if (!gameinfo.ConfigName.CompareNoCase("Strife")) + { + checklist = useType==FTexture::TEX_Flat? strifeflatpath : strifetexpath; + } + else if (!gameinfo.ConfigName.CompareNoCase("Chex")) + { + checklist = useType==FTexture::TEX_Flat? chexflatpath : chextexpath; + } + else + return -3; + + while (*checklist) + { + static const char * extensions[] = { "PNG", "JPG", "TGA", "PCX", NULL }; + + for (const char ** extp=extensions; *extp; extp++) + { + checkName.Format(*checklist, tex->Name, *extp); + int lumpnum = Wads.CheckNumForFullName(checkName); + if (lumpnum >= 0) return lumpnum; + } + checklist++; + } + return -3; +} + + +//========================================================================== +// +// Checks for the presence of a hires texture replacement +// +//========================================================================== +int CheckExternalFile(FTexture *tex, bool & hascolorkey) +{ + static const char * doom1texpath[]= { + "%stextures/doom/doom1/%s.%s", "%stextures/doom/doom1/%s-ck.%s", + "%stextures/doom/%s.%s", "%stextures/doom/%s-ck.%s", "%stextures/%s.%s", "%stextures/%s-ck.%s", NULL + }; + + static const char * doom2texpath[]= { + "%stextures/doom/doom2/%s.%s", "%stextures/doom/doom2/%s-ck.%s", + "%stextures/doom/%s.%s", "%stextures/doom/%s-ck.%s", "%stextures/%s.%s", "%stextures/%s-ck.%s", NULL + }; + + static const char * pluttexpath[]= { + "%stextures/doom/plut/%s.%s", "%stextures/doom/plut/%s-ck.%s", + "%stextures/doom/doom2-plut/%s.%s", "%stextures/doom/doom2-plut/%s-ck.%s", + "%stextures/doom/%s.%s", "%stextures/doom/%s-ck.%s", "%stextures/%s.%s", "%stextures/%s-ck.%s", NULL + }; + + static const char * tnttexpath[]= { + "%stextures/doom/tnt/%s.%s", "%stextures/doom/tnt/%s-ck.%s", + "%stextures/doom/doom2-tnt/%s.%s", "%stextures/doom/doom2-tnt/%s-ck.%s", + "%stextures/doom/%s.%s", "%stextures/doom/%s-ck.%s", "%stextures/%s.%s", "%stextures/%s-ck.%s", NULL + }; + + static const char * heretictexpath[]= { + "%stextures/heretic/%s.%s", "%stextures/heretic/%s-ck.%s", "%stextures/%s.%s", "%stextures/%s-ck.%s", NULL + }; + + static const char * hexentexpath[]= { + "%stextures/hexen/%s.%s", "%stextures/hexen/%s-ck.%s", "%stextures/%s.%s", "%stextures/%s-ck.%s", NULL + }; + + static const char * strifetexpath[]= { + "%stextures/strife/%s.%s", "%stextures/strife/%s-ck.%s", "%stextures/%s.%s", "%stextures/%s-ck.%s", NULL + }; + + static const char * chextexpath[]= { + "%stextures/chex/%s.%s", "%stextures/chex/%s-ck.%s", "%stextures/%s.%s", "%stextures/%s-ck.%s", NULL + }; + + static const char * doom1flatpath[]= { + "%sflats/doom/doom1/%s.%s", "%stextures/doom/doom1/flat-%s.%s", + "%sflats/doom/%s.%s", "%stextures/doom/flat-%s.%s", "%sflats/%s.%s", "%stextures/flat-%s.%s", NULL + }; + + static const char * doom2flatpath[]= { + "%sflats/doom/doom2/%s.%s", "%stextures/doom/doom2/flat-%s.%s", + "%sflats/doom/%s.%s", "%stextures/doom/flat-%s.%s", "%sflats/%s.%s", "%stextures/flat-%s.%s", NULL + }; + + static const char * plutflatpath[]= { + "%sflats/doom/plut/%s.%s", "%stextures/doom/plut/flat-%s.%s", + "%sflats/doom/doom2-plut/%s.%s", "%stextures/doom/doom2-plut/flat-%s.%s", + "%sflats/doom/%s.%s", "%stextures/doom/flat-%s.%s", "%sflats/%s.%s", "%stextures/flat-%s.%s", NULL + }; + + static const char * tntflatpath[]= { + "%sflats/doom/tnt/%s.%s", "%stextures/doom/tnt/flat-%s.%s", + "%sflats/doom/doom2-tnt/%s.%s", "%stextures/doom/doom2-tnt/flat-%s.%s", + "%sflats/doom/%s.%s", "%stextures/doom/flat-%s.%s", "%sflats/%s.%s", "%stextures/flat-%s.%s", NULL + }; + + static const char * hereticflatpath[]= { + "%sflats/heretic/%s.%s", "%stextures/heretic/flat-%s.%s", "%sflats/%s.%s", "%stextures/flat-%s.%s", NULL + }; + + static const char * hexenflatpath[]= { + "%sflats/hexen/%s.%s", "%stextures/hexen/flat-%s.%s", "%sflats/%s.%s", "%stextures/flat-%s.%s", NULL + }; + + static const char * strifeflatpath[]= { + "%sflats/strife/%s.%s", "%stextures/strife/flat-%s.%s", "%sflats/%s.%s", "%stextures/flat-%s.%s", NULL + }; + + static const char * chexflatpath[]= { + "%sflats/chex/%s.%s", "%stextures/chex/flat-%s.%s", "%sflats/%s.%s", "%stextures/flat-%s.%s", NULL + }; + + static const char * doom1patchpath[]= { + "%spatches/doom/doom1/%s.%s", "%spatches/doom/%s.%s", "%spatches/%s.%s", NULL + }; + + static const char * doom2patchpath[]= { + "%spatches/doom/doom2/%s.%s", "%spatches/doom/%s.%s", "%spatches/%s.%s", NULL + }; + + static const char * plutpatchpath[]= { + "%spatches/doom/plut/%s.%s", "%spatches/doom/%s.%s", "%spatches/%s.%s", NULL + }; + + static const char * tntpatchpath[]= { + "%spatches/doom/tnt/%s.%s", "%spatches/doom/%s.%s", "%spatches/%s.%s", NULL + }; + + static const char * hereticpatchpath[]= { + "%spatches/heretic/%s.%s", "%spatches/%s.%s", NULL + }; + + static const char * hexenpatchpath[]= { + "%spatches/hexen/%s.%s", "%spatches/%s.%s", NULL + }; + + static const char * strifepatchpath[]= { + "%spatches/strife/%s.%s", "%spatches/%s.%s", NULL + }; + + static const char * chexpatchpath[]= { + "%spatches/chex/%s.%s", "%spatches/%s.%s", NULL + }; + + FString checkName; + const char ** checklist; + BYTE useType=tex->UseType; + + if (useType==FTexture::TEX_SkinSprite || useType==FTexture::TEX_Decal || useType==FTexture::TEX_FontChar) + { + return -3; + } + + bool ispatch = (useType==FTexture::TEX_MiscPatch || useType==FTexture::TEX_Sprite) ; + + // for patches this doesn't work yet + if (ispatch) return -3; + + if (!gameinfo.ConfigName.CompareNoCase("Doom")) + { + if (!(gameinfo.flags & GI_MAPxx)) + { + checklist = ispatch ? doom1patchpath : useType==FTexture::TEX_Flat? doom1flatpath : doom1texpath; + } + else + { + SetDoom2Wad(); + if (Doom2Wad == 1) + checklist = ispatch ? plutpatchpath : useType==FTexture::TEX_Flat? plutflatpath : pluttexpath; + else if (Doom2Wad == 2) + checklist = ispatch ? tntpatchpath : useType==FTexture::TEX_Flat? tntflatpath : tnttexpath; + else + checklist = ispatch ? doom2patchpath : useType==FTexture::TEX_Flat? doom2flatpath : doom2texpath; + } + } + else if (!gameinfo.ConfigName.CompareNoCase("Heretic")) + { + checklist = ispatch ? hereticpatchpath : useType==FTexture::TEX_Flat? hereticflatpath : heretictexpath; + } + else if (!gameinfo.ConfigName.CompareNoCase("Hexen")) + { + checklist = ispatch ? hexenpatchpath : useType==FTexture::TEX_Flat? hexenflatpath : hexentexpath; + } + else if (!gameinfo.ConfigName.CompareNoCase("Strife")) + { + checklist = ispatch ?strifepatchpath : useType==FTexture::TEX_Flat? strifeflatpath : strifetexpath; + } + else if (!gameinfo.ConfigName.CompareNoCase("Chex")) + { + checklist = ispatch ?chexpatchpath : useType==FTexture::TEX_Flat? chexflatpath : chextexpath; + } + else + return -3; + + while (*checklist) + { + static const char * extensions[] = { "PNG", "JPG", "TGA", "PCX", NULL }; + + for (const char ** extp=extensions; *extp; extp++) + { + checkName.Format(*checklist, progdir.GetChars(), tex->Name, *extp); + if (_access(checkName, 0) == 0) + { + hascolorkey = !!strstr(checkName, "-ck."); + return Wads.AddExternalFile(checkName); + } + } + checklist++; + } + return -3; +} + + diff --git a/src/gl/textures/gl_hqresize.cpp b/src/gl/textures/gl_hqresize.cpp new file mode 100644 index 000000000..36861f713 --- /dev/null +++ b/src/gl/textures/gl_hqresize.cpp @@ -0,0 +1,291 @@ +/* +** gl_hqresize.cpp +** Contains high quality upsampling functions. +** So far Scale2x/3x/4x as described in http://scale2x.sourceforge.net/ +** are implemented. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Benjamin Berkels +** 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. +** +** 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 "gl/renderer/gl_renderer.h" +#include "gl/textures/gl_texture.h" +#include "c_cvars.h" +// [BB] hqnx scaling is only supported with the MS compiler. +#if (defined _MSC_VER) && (!defined _WIN64) +#include "gl/hqnx/hqnx.h" +#endif + +CUSTOM_CVAR(Int, gl_texture_hqresize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ +#ifdef _MSC_VER + if (self < 0 || self > 6) +#else + if (self < 0 || self > 3) +#endif + self = 0; + GLRenderer->FlushTextures(); +} + +CUSTOM_CVAR(Int, gl_texture_hqresize_maxinputsize, 512, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (self > 1024) self = 1024; + GLRenderer->FlushTextures(); +} + +CUSTOM_CVAR(Int, gl_texture_hqresize_targets, 7, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + GLRenderer->FlushTextures(); +} + +CVAR (Flag, gl_texture_hqresize_textures, gl_texture_hqresize_targets, 1); +CVAR (Flag, gl_texture_hqresize_sprites, gl_texture_hqresize_targets, 2); +CVAR (Flag, gl_texture_hqresize_fonts, gl_texture_hqresize_targets, 4); + + +static void scale2x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, int inHeight ) +{ + const int width = 2* inWidth; + const int height = 2 * inHeight; + + for ( int i = 0; i < inWidth; ++i ) + { + const int iMinus = (i > 0) ? (i-1) : 0; + const int iPlus = (i < inWidth - 1 ) ? (i+1) : i; + for ( int j = 0; j < inHeight; ++j ) + { + const int jMinus = (j > 0) ? (j-1) : 0; + const int jPlus = (j < inHeight - 1 ) ? (j+1) : j; + const uint32 A = inputBuffer[ iMinus +inWidth*jMinus]; + const uint32 B = inputBuffer[ iMinus +inWidth*j ]; + const uint32 C = inputBuffer[ iMinus +inWidth*jPlus]; + const uint32 D = inputBuffer[ i +inWidth*jMinus]; + const uint32 E = inputBuffer[ i +inWidth*j ]; + const uint32 F = inputBuffer[ i +inWidth*jPlus]; + const uint32 G = inputBuffer[ iPlus +inWidth*jMinus]; + const uint32 H = inputBuffer[ iPlus +inWidth*j ]; + const uint32 I = inputBuffer[ iPlus +inWidth*jPlus]; + if (B != H && D != F) { + outputBuffer[2*i + width*2*j ] = D == B ? D : E; + outputBuffer[2*i + width*(2*j+1)] = B == F ? F : E; + outputBuffer[2*i+1 + width*2*j ] = D == H ? D : E; + outputBuffer[2*i+1 + width*(2*j+1)] = H == F ? F : E; + } else { + outputBuffer[2*i + width*2*j ] = E; + outputBuffer[2*i + width*(2*j+1)] = E; + outputBuffer[2*i+1 + width*2*j ] = E; + outputBuffer[2*i+1 + width*(2*j+1)] = E; + } + } + } +} + +static void scale3x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, int inHeight ) +{ + const int width = 3* inWidth; + const int height = 3 * inHeight; + + for ( int i = 0; i < inWidth; ++i ) + { + const int iMinus = (i > 0) ? (i-1) : 0; + const int iPlus = (i < inWidth - 1 ) ? (i+1) : i; + for ( int j = 0; j < inHeight; ++j ) + { + const int jMinus = (j > 0) ? (j-1) : 0; + const int jPlus = (j < inHeight - 1 ) ? (j+1) : j; + const uint32 A = inputBuffer[ iMinus +inWidth*jMinus]; + const uint32 B = inputBuffer[ iMinus +inWidth*j ]; + const uint32 C = inputBuffer[ iMinus +inWidth*jPlus]; + const uint32 D = inputBuffer[ i +inWidth*jMinus]; + const uint32 E = inputBuffer[ i +inWidth*j ]; + const uint32 F = inputBuffer[ i +inWidth*jPlus]; + const uint32 G = inputBuffer[ iPlus +inWidth*jMinus]; + const uint32 H = inputBuffer[ iPlus +inWidth*j ]; + const uint32 I = inputBuffer[ iPlus +inWidth*jPlus]; + if (B != H && D != F) { + outputBuffer[3*i + width*3*j ] = D == B ? D : E; + outputBuffer[3*i + width*(3*j+1)] = (D == B && E != C) || (B == F && E != A) ? B : E; + outputBuffer[3*i + width*(3*j+2)] = B == F ? F : E; + outputBuffer[3*i+1 + width*3*j ] = (D == B && E != G) || (D == H && E != A) ? D : E; + outputBuffer[3*i+1 + width*(3*j+1)] = E; + outputBuffer[3*i+1 + width*(3*j+2)] = (B == F && E != I) || (H == F && E != C) ? F : E; + outputBuffer[3*i+2 + width*3*j ] = D == H ? D : E; + outputBuffer[3*i+2 + width*(3*j+1)] = (D == H && E != I) || (H == F && E != G) ? H : E; + outputBuffer[3*i+2 + width*(3*j+2)] = H == F ? F : E; + } else { + outputBuffer[3*i + width*3*j ] = E; + outputBuffer[3*i + width*(3*j+1)] = E; + outputBuffer[3*i + width*(3*j+2)] = E; + outputBuffer[3*i+1 + width*3*j ] = E; + outputBuffer[3*i+1 + width*(3*j+1)] = E; + outputBuffer[3*i+1 + width*(3*j+2)] = E; + outputBuffer[3*i+2 + width*3*j ] = E; + outputBuffer[3*i+2 + width*(3*j+1)] = E; + outputBuffer[3*i+2 + width*(3*j+2)] = E; + } + } + } +} + +static void scale4x ( uint32* inputBuffer, uint32* outputBuffer, int inWidth, int inHeight ) +{ + int width = 2* inWidth; + int height = 2 * inHeight; + uint32 * buffer2x = new uint32[width*height]; + + scale2x ( reinterpret_cast ( inputBuffer ), reinterpret_cast ( buffer2x ), inWidth, inHeight ); + width *= 2; + height *= 2; + scale2x ( reinterpret_cast ( buffer2x ), reinterpret_cast ( outputBuffer ), 2*inWidth, 2*inHeight ); + delete[] buffer2x; +} + + +static unsigned char *scaleNxHelper( void (*scaleNxFunction) ( uint32* , uint32* , int , int), + const int N, + unsigned char *inputBuffer, + const int inWidth, + const int inHeight, + int &outWidth, + int &outHeight ) +{ + outWidth = N * inWidth; + outHeight = N *inHeight; + unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4]; + + scaleNxFunction ( reinterpret_cast ( inputBuffer ), reinterpret_cast ( newBuffer ), inWidth, inHeight ); + delete[] inputBuffer; + return newBuffer; +} + +// [BB] hqnx scaling is only supported with the MS compiler. +#if (defined _MSC_VER) && (!defined _WIN64) +static unsigned char *hqNxHelper( void (*hqNxFunction) ( int*, unsigned char*, int, int, int ), + const int N, + unsigned char *inputBuffer, + const int inWidth, + const int inHeight, + int &outWidth, + int &outHeight ) +{ + static int initdone = false; + + if (!initdone) + { + InitLUTs(); + initdone = true; + } + outWidth = N * inWidth; + outHeight = N *inHeight; + + CImage cImageIn; + cImageIn.SetImage(inputBuffer, inWidth, inHeight, 32); + cImageIn.Convert32To17(); + + unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4]; + hqNxFunction( reinterpret_cast(cImageIn.m_pBitmap), newBuffer, cImageIn.m_Xres, cImageIn.m_Yres, outWidth*4 ); + delete[] inputBuffer; + return newBuffer; +} +#endif + +//=========================================================================== +// +// [BB] Upsamples the texture in inputBuffer, frees inputBuffer and returns +// the upsampled buffer. +// +//=========================================================================== +unsigned char *gl_CreateUpsampledTextureBuffer ( const FTexture *inputTexture, unsigned char *inputBuffer, const int inWidth, const int inHeight, int &outWidth, int &outHeight, bool hasAlpha ) +{ + // [BB] Make sure that outWidth and outHeight denote the size of + // the returned buffer even if we don't upsample the input buffer. + outWidth = inWidth; + outHeight = inHeight; + + // [BB] Don't resample if the width or height of the input texture is bigger than gl_texture_hqresize_maxinputsize. + if ( ( inWidth > gl_texture_hqresize_maxinputsize ) || ( inHeight > gl_texture_hqresize_maxinputsize ) ) + return inputBuffer; + + // [BB] Don't try to upsample textures based off FCanvasTexture. + if ( inputTexture->bHasCanvas ) + return inputBuffer; + + // [BB] Don't upsample non-shader handled warped textures. Needs too much memory and time + if (gl.shadermodel == 2 || (gl.shadermodel == 3 && inputTexture->bWarped)) + return inputBuffer; + + switch (inputTexture->UseType) + { + case FTexture::TEX_Sprite: + case FTexture::TEX_SkinSprite: + if (!(gl_texture_hqresize_targets & 2)) return inputBuffer; + break; + + case FTexture::TEX_FontChar: + if (!(gl_texture_hqresize_targets & 4)) return inputBuffer; + break; + + default: + if (!(gl_texture_hqresize_targets & 1)) return inputBuffer; + break; + } + + if (inputBuffer) + { + outWidth = inWidth; + outHeight = inHeight; + int type = gl_texture_hqresize; + // hqNx does not preserve the alpha channel so fall back to ScaleNx for such textures + if (hasAlpha && type > 3) + { + type -= 3; + } + + switch (type) + { + case 1: + return scaleNxHelper( &scale2x, 2, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + case 2: + return scaleNxHelper( &scale3x, 3, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + case 3: + return scaleNxHelper( &scale4x, 4, inputBuffer, inWidth, inHeight, outWidth, outHeight ); +// [BB] hqnx scaling is only supported with the MS compiler. +#if (defined _MSC_VER) && (!defined _WIN64) + case 4: + return hqNxHelper( &hq2x_32, 2, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + case 5: + return hqNxHelper( &hq3x_32, 3, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + case 6: + return hqNxHelper( &hq4x_32, 4, inputBuffer, inWidth, inHeight, outWidth, outHeight ); +#endif + } + } + return inputBuffer; +} diff --git a/src/gl/textures/gl_hwtexture.cpp b/src/gl/textures/gl_hwtexture.cpp new file mode 100644 index 000000000..f81d003d4 --- /dev/null +++ b/src/gl/textures/gl_hwtexture.cpp @@ -0,0 +1,420 @@ +/* +** gltexture.cpp +** Low level OpenGL texture handling. These classes are also +** containers for the various translations a texture can have. +** +**--------------------------------------------------------------------------- +** Copyright 2004-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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "templates.h" +#include "m_crc32.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "v_palette.h" + +#include "gl/system/gl_cvars.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/textures/gl_material.h" + + +extern TexFilter_s TexFilter[]; +extern int TexFormat[]; +EXTERN_CVAR(Bool, gl_clamp_per_texture) + + +//=========================================================================== +// +// Static texture data +// +//=========================================================================== +unsigned int FHardwareTexture::lastbound[FHardwareTexture::MAX_TEXTURES]; + +//=========================================================================== +// +// STATIC - Gets the maximum size of hardware textures +// +//=========================================================================== +int FHardwareTexture::GetTexDimension(int value) +{ + if (value > gl.max_texturesize) return gl.max_texturesize; + if (gl.flags&RFL_NPOT_TEXTURE) return value; + + int i=1; + while (iw) + memcpy( scaledbuffer + rw * y * 4 + w * 4, + scaledbuffer + rw * y * 4 + w * 4 -4, 4); + } + // also duplicate the last line for the same reason! + memcpy( scaledbuffer + rw * h * 4, scaledbuffer + rw * (h-1) * 4, w*4 + 4); + + deletebuffer=true; + buffer=scaledbuffer; + } + } + } + gl.TexImage2D(GL_TEXTURE_2D, 0, texformat, rw, rh, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + + if (deletebuffer) free(buffer); + + // When using separate samplers the stuff below is not needed. + // if (gl.flags & RFL_SAMPLER_OBJECTS) return; + + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapparam==GL_CLAMP? GL_CLAMP_TO_EDGE : GL_REPEAT); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapparam==GL_CLAMP? GL_CLAMP_TO_EDGE : GL_REPEAT); + clampmode = wrapparam==GL_CLAMP? GLT_CLAMPX|GLT_CLAMPY : 0; + + if (forcenofiltering) + { + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl.TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.f); + } + else + { + if (mipmap && use_mipmapping) + { + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TexFilter[gl_texture_filter].minfilter); + if (gl_texture_filter_anisotropic) + { + gl.TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_filter_anisotropic); + } + } + else + { + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, TexFilter[gl_texture_filter].magfilter); + } + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, TexFilter[gl_texture_filter].magfilter); + } +} + + +//=========================================================================== +// +// Creates a texture +// +//=========================================================================== +FHardwareTexture::FHardwareTexture(int _width, int _height, bool _mipmap, bool wrap, bool nofilter, bool nocompression) +{ + forcenocompression = nocompression; + mipmap=_mipmap; + texwidth=_width; + texheight=_height; + + int cm_arraysize = CM_FIRSTSPECIALCOLORMAP + SpecialColormaps.Size(); + glTexID = new unsigned[cm_arraysize]; + memset(glTexID,0,sizeof(unsigned int)*cm_arraysize); + clampmode=0; + glDepthID = 0; + forcenofiltering = nofilter; +} + + +//=========================================================================== +// +// Deletes a texture id and unbinds it from the texture units +// +//=========================================================================== +void FHardwareTexture::DeleteTexture(unsigned int texid) +{ + if (texid != 0) + { + for(int i = 0; i < MAX_TEXTURES; i++) + { + if (lastbound[i] == texid) + { + lastbound[i] = 0; + } + } + gl.DeleteTextures(1, &texid); + } +} + +//=========================================================================== +// +// Frees all associated resources +// +//=========================================================================== +void FHardwareTexture::Clean(bool all) +{ + int cm_arraysize = CM_FIRSTSPECIALCOLORMAP + SpecialColormaps.Size(); + + if (all) + { + for (int i=0;i= CM_MAXCOLORMAP) cm=CM_DEFAULT; + + if (translation==0) + { + return &glTexID[cm]; + } + + // normally there aren't more than very few different + // translations here so this isn't performance critical. + for(unsigned int i=0;i= CM_MAXCOLORMAP) cm=CM_DEFAULT; + + unsigned int * pTexID=GetTexID(cm, translation); + + if (texunit != 0) gl.ActiveTexture(GL_TEXTURE0+texunit); + LoadImage(buffer, w, h, *pTexID, wrap? GL_REPEAT:GL_CLAMP, cm==CM_SHADE, texunit); + if (texunit != 0) gl.ActiveTexture(GL_TEXTURE0); + return *pTexID; +} + + diff --git a/src/gl/textures/gl_hwtexture.h b/src/gl/textures/gl_hwtexture.h new file mode 100644 index 000000000..da093df4e --- /dev/null +++ b/src/gl/textures/gl_hwtexture.h @@ -0,0 +1,77 @@ + +#ifndef __GLTEXTURE_H +#define __GLTEXTURE_H + +#define SHADED_TEXTURE -1 +#define DIRECT_PALETTE -2 + +#include "tarray.h" + +class FCanvasTexture; +class AActor; + +enum +{ + GLT_CLAMPX=1, + GLT_CLAMPY=2 +}; + +class FHardwareTexture +{ + enum + { + MAX_TEXTURES = 16 + }; + + struct TranslatedTexture + { + unsigned int glTexID; + int translation; + int cm; + }; + +public: + + static unsigned int lastbound[MAX_TEXTURES]; + static int lastactivetexture; + static bool supportsNonPower2; + static int max_texturesize; + + static int GetTexDimension(int value); + +private: + + short texwidth, texheight; + //float scalexfac, scaleyfac; + bool mipmap; + BYTE clampmode; + bool forcenofiltering; + bool forcenocompression; + + unsigned int * glTexID; + TArray glTexID_Translated; + unsigned int glDepthID; // only used by camera textures + + void LoadImage(unsigned char * buffer,int w, int h, unsigned int & glTexID,int wrapparam, bool alphatexture, int texunit); + unsigned * GetTexID(int cm, int translation); + + int GetDepthBuffer(); + void DeleteTexture(unsigned int texid); + +public: + FHardwareTexture(int w, int h, bool mip, bool wrap, bool nofilter, bool nocompress); + ~FHardwareTexture(); + + static void Unbind(int texunit); + static void UnbindAll(); + + void BindToFrameBuffer(); + + unsigned int Bind(int texunit, int cm, int translation=0); + unsigned int CreateTexture(unsigned char * buffer, int w, int h,bool wrap, int texunit, int cm, int translation=0); + void Resize(int _width, int _height) ; + + void Clean(bool all); +}; + +#endif diff --git a/src/gl/textures/gl_material.cpp b/src/gl/textures/gl_material.cpp new file mode 100644 index 000000000..53d01a3b9 --- /dev/null +++ b/src/gl/textures/gl_material.cpp @@ -0,0 +1,1123 @@ +/* +** gl_material.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2004-2009 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** +** 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 "w_wad.h" +#include "m_png.h" +#include "sbar.h" +#include "gi.h" +#include "cmdlib.h" +#include "c_dispatch.h" +#include "stats.h" +#include "r_utility.h" +#include "templates.h" +#include "sc_man.h" +#include "colormatcher.h" + +//#include "gl/gl_intern.h" + +#include "gl/system/gl_framebuffer.h" +#include "gl/renderer/gl_lightdata.h" +#include "gl/data/gl_data.h" +#include "gl/textures/gl_texture.h" +#include "gl/textures/gl_translate.h" +#include "gl/textures/gl_bitmap.h" +#include "gl/textures/gl_material.h" +#include "gl/shaders/gl_shader.h" + +EXTERN_CVAR(Bool, gl_render_precise) +EXTERN_CVAR(Int, gl_lightmode) +EXTERN_CVAR(Bool, gl_precache) +EXTERN_CVAR(Bool, gl_texture_usehires) + +//=========================================================================== +// +// The GL texture maintenance class +// +//=========================================================================== + +//=========================================================================== +// +// Constructor +// +//=========================================================================== +FGLTexture::FGLTexture(FTexture * tx, bool expandpatches) +{ + assert(tx->gl_info.SystemTexture == NULL); + tex = tx; + + glpatch=NULL; + for(int i=0;i<5;i++) gltexture[i]=NULL; + HiresLump=-1; + hirestexture = NULL; + currentwarp = 0; + bHasColorkey = false; + bIsTransparent = -1; + bExpand = expandpatches; + tex->gl_info.SystemTexture = this; +} + +//=========================================================================== +// +// Destructor +// +//=========================================================================== + +FGLTexture::~FGLTexture() +{ + Clean(true); + if (hirestexture) delete hirestexture; +} + +//========================================================================== +// +// Checks for the presence of a hires texture replacement and loads it +// +//========================================================================== +unsigned char *FGLTexture::LoadHiresTexture(FTexture *tex, int *width, int *height, int cm) +{ + if (HiresLump==-1) + { + bHasColorkey = false; + HiresLump = CheckDDPK3(tex); + if (HiresLump < 0) HiresLump = CheckExternalFile(tex, bHasColorkey); + + if (HiresLump >=0) + { + hirestexture = FTexture::CreateTexture(HiresLump, FTexture::TEX_Any); + } + } + if (hirestexture != NULL) + { + int w=hirestexture->GetWidth(); + int h=hirestexture->GetHeight(); + + unsigned char * buffer=new unsigned char[w*(h+1)*4]; + memset(buffer, 0, w * (h+1) * 4); + + FGLBitmap bmp(buffer, w*4, w, h); + bmp.SetTranslationInfo(cm); + + + int trans = hirestexture->CopyTrueColorPixels(&bmp, 0, 0); + hirestexture->CheckTrans(buffer, w*h, trans); + bIsTransparent = hirestexture->gl_info.mIsTransparent; + + if (bHasColorkey) + { + // This is a crappy Doomsday color keyed image + // We have to remove the key manually. :( + DWORD * dwdata=(DWORD*)buffer; + for (int i=(w*h);i>0;i--) + { + if (dwdata[i]==0xffffff00 || dwdata[i]==0xffff00ff) dwdata[i]=0; + } + } + *width = w; + *height = h; + return buffer; + } + return NULL; +} + +//=========================================================================== +// +// Deletes all allocated resources +// +//=========================================================================== + +void FGLTexture::Clean(bool all) +{ + for(int i=0;i<5;i++) + { + if (gltexture[i]) + { + if (!all) gltexture[i]->Clean(false); + else + { + delete gltexture[i]; + gltexture[i]=NULL; + } + } + } + if (glpatch) + { + if (!all) glpatch->Clean(false); + else + { + delete glpatch; + glpatch=NULL; + } + } +} + +//=========================================================================== +// +// FGLTex::WarpBuffer +// +//=========================================================================== + +BYTE *FGLTexture::WarpBuffer(BYTE *buffer, int Width, int Height, int warp) +{ + if (Width > 256 || Height > 256) return buffer; + + DWORD *in = (DWORD*)buffer; + DWORD *out = (DWORD*)new BYTE[4*Width*Height]; + float Speed = static_cast(tex)->GetSpeed(); + + static_cast(tex)->GenTime = r_FrameTime; + + static DWORD linebuffer[256]; // anything larger will bring down performance so it is excluded above. + DWORD timebase = DWORD(r_FrameTime*Speed*23/28); + int xsize = Width; + int ysize = Height; + int xmask = xsize - 1; + int ymask = ysize - 1; + int ds_xbits; + int i,x; + + if (warp == 1) + { + for(ds_xbits=-1,i=Width; i; i>>=1, ds_xbits++); + + for (x = xsize-1; x >= 0; x--) + { + int yt, yf = (finesine[(timebase+(x+17)*128)&FINEMASK]>>13) & ymask; + const DWORD *source = in + x; + DWORD *dest = out + x; + for (yt = ysize; yt; yt--, yf = (yf+1)&ymask, dest += xsize) + { + *dest = *(source+(yf<= 0; y--) + { + int xt, xf = (finesine[(timebase+y*128)&FINEMASK]>>13) & xmask; + DWORD *source = out + (y<>=1, ybits++); + + DWORD timebase = (r_FrameTime * Speed * 40 / 28); + for (x = xsize-1; x >= 0; x--) + { + for (int y = ysize-1; y >= 0; y--) + { + int xt = (x + 128 + + ((finesine[(y*128 + timebase*5 + 900) & 8191]*2)>>FRACBITS) + + ((finesine[(x*256 + timebase*4 + 300) & 8191]*2)>>FRACBITS)) & xmask; + int yt = (y + 128 + + ((finesine[(y*128 + timebase*3 + 700) & 8191]*2)>>FRACBITS) + + ((finesine[(x*256 + timebase*4 + 1200) & 8191]*2)>>FRACBITS)) & ymask; + const DWORD *source = in + (xt << ybits) + yt; + DWORD *dest = out + (x << ybits) + y; + *dest = *source; + } + } + } + delete [] buffer; + return (BYTE*)out; +} + +//=========================================================================== +// +// Initializes the buffer for the texture data +// +//=========================================================================== + +unsigned char * FGLTexture::CreateTexBuffer(int cm, int translation, int & w, int & h, bool expand, FTexture *hirescheck, int warp) +{ + unsigned char * buffer; + int W, H; + + + // Textures that are already scaled in the texture lump will not get replaced + // by hires textures + if (gl_texture_usehires && hirescheck != NULL) + { + buffer = LoadHiresTexture (hirescheck, &w, &h, cm); + if (buffer) + { + return buffer; + } + } + + W = w = tex->GetWidth() + expand*2; + H = h = tex->GetHeight() + expand*2; + + + buffer=new unsigned char[W*(H+1)*4]; + memset(buffer, 0, W * (H+1) * 4); + + FGLBitmap bmp(buffer, W*4, W, H); + bmp.SetTranslationInfo(cm, translation); + + if (tex->bComplex) + { + FBitmap imgCreate; + + // The texture contains special processing so it must be composited using the + // base bitmap class and then be converted as a whole. + if (imgCreate.Create(W, H)) + { + memset(imgCreate.GetPixels(), 0, W * H * 4); + int trans = tex->CopyTrueColorPixels(&imgCreate, expand, expand); + bmp.CopyPixelDataRGB(0, 0, imgCreate.GetPixels(), W, H, 4, W * 4, 0, CF_BGRA); + tex->CheckTrans(buffer, W*H, trans); + bIsTransparent = tex->gl_info.mIsTransparent; + } + } + else if (translation<=0) + { + int trans = tex->CopyTrueColorPixels(&bmp, expand, expand); + tex->CheckTrans(buffer, W*H, trans); + bIsTransparent = tex->gl_info.mIsTransparent; + } + else + { + // When using translations everything must be mapped to the base palette. + // Since FTexture's method is doing exactly that by calling GetPixels let's use that here + // to do all the dirty work for us. ;) + tex->FTexture::CopyTrueColorPixels(&bmp, expand, expand); + bIsTransparent = 0; + } + + if (warp != 0) + { + buffer = WarpBuffer(buffer, W, H, warp); + } + // [BB] The hqnx upsampling (not the scaleN one) destroys partial transparency, don't upsamle textures using it. + // Also don't upsample warped textures. + else //if (bIsTransparent != 1) + { + // [BB] Potentially upsample the buffer. + buffer = gl_CreateUpsampledTextureBuffer ( tex, buffer, W, H, w, h, bIsTransparent || cm == CM_SHADE ); + } + currentwarp = warp; + currentwarptime = gl_frameMS; + + return buffer; +} + + +//=========================================================================== +// +// Create hardware texture for world use +// +//=========================================================================== + +FHardwareTexture *FGLTexture::CreateTexture(int clampmode) +{ + if (tex->UseType==FTexture::TEX_Null) return NULL; // Cannot register a NULL texture + if (!gltexture[clampmode]) + { + gltexture[clampmode] = new FHardwareTexture(tex->GetWidth(), tex->GetHeight(), true, true, false, tex->gl_info.bNoCompress); + } + return gltexture[clampmode]; +} + +//=========================================================================== +// +// Create Hardware texture for patch use +// +//=========================================================================== + +bool FGLTexture::CreatePatch() +{ + if (tex->UseType==FTexture::TEX_Null) return false; // Cannot register a NULL texture + if (!glpatch) + { + glpatch=new FHardwareTexture(tex->GetWidth() + bExpand, tex->GetHeight() + bExpand, false, false, tex->gl_info.bNoFilter, tex->gl_info.bNoCompress); + } + if (glpatch) return true; + return false; +} + + +//=========================================================================== +// +// Binds a texture to the renderer +// +//=========================================================================== + +const FHardwareTexture *FGLTexture::Bind(int texunit, int cm, int clampmode, int translation, FTexture *hirescheck, int warp) +{ + int usebright = false; + + if (translation <= 0) translation = -translation; + else translation = GLTranslationPalette::GetInternalTranslation(translation); + + FHardwareTexture *hwtex; + + if (gltexture[4] != NULL && clampmode < 4 && gltexture[clampmode] == NULL) + { + hwtex = gltexture[clampmode] = gltexture[4]; + gltexture[4] = NULL; + + if (hwtex->Bind(texunit, cm, translation)) + { + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (clampmode & GLT_CLAMPX)? GL_CLAMP_TO_EDGE : GL_REPEAT); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (clampmode & GLT_CLAMPY)? GL_CLAMP_TO_EDGE : GL_REPEAT); + } + } + else + { + hwtex = CreateTexture(clampmode); + } + + if (hwtex) + { + if ((warp != 0 || currentwarp != warp) && currentwarptime != gl_frameMS) + { + // must recreate the texture + Clean(true); + hwtex = CreateTexture(clampmode); + } + + // Texture has become invalid + else if ((warp == 0 && !tex->bHasCanvas) && tex->CheckModified()) + { + Clean(true); + hwtex = CreateTexture(clampmode); + } + + // Bind it to the system. + if (!hwtex->Bind(texunit, cm, translation)) + { + + int w, h; + + // Create this texture + unsigned char * buffer = NULL; + + if (!tex->bHasCanvas) + { + buffer = CreateTexBuffer(cm, translation, w, h, false, hirescheck, warp); + tex->ProcessData(buffer, w, h, false); + } + if (!hwtex->CreateTexture(buffer, w, h, true, texunit, cm, translation)) + { + // could not create texture + delete[] buffer; + return NULL; + } + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (clampmode & GLT_CLAMPX)? GL_CLAMP_TO_EDGE : GL_REPEAT); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (clampmode & GLT_CLAMPY)? GL_CLAMP_TO_EDGE : GL_REPEAT); + delete[] buffer; + } + + if (tex->bHasCanvas) static_cast(tex)->NeedUpdate(); + return hwtex; + } + return NULL; +} + +//=========================================================================== +// +// Binds a sprite to the renderer +// +//=========================================================================== +const FHardwareTexture * FGLTexture::BindPatch(int texunit, int cm, int translation, int warp) +{ + bool usebright = false; + int transparm = translation; + + if (translation <= 0) translation = -translation; + else translation = GLTranslationPalette::GetInternalTranslation(translation); + + if (CreatePatch()) + { + if (warp != 0 || currentwarp != warp) + { + // must recreate the texture + Clean(true); + CreatePatch(); + } + + // Texture has become invalid + else if ((warp == 0 && !tex->bHasCanvas) && tex->CheckModified()) + { + Clean(true); + CreatePatch(); + } + + + // Bind it to the system. + if (!glpatch->Bind(texunit, cm, translation)) + { + int w, h; + + // Create this texture + unsigned char * buffer = CreateTexBuffer(cm, translation, w, h, bExpand, NULL, warp); + tex->ProcessData(buffer, w, h, true); + if (!glpatch->CreateTexture(buffer, w, h, false, texunit, cm, translation)) + { + // could not create texture + delete[] buffer; + return NULL; + } + delete[] buffer; + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + return glpatch; + } + return NULL; +} + + +//=========================================================================== +// +// +// +//=========================================================================== + +fixed_t FTexCoordInfo::RowOffset(fixed_t rowoffset) const +{ + if (mTempScaleY == FRACUNIT) + { + if (mScaleY==FRACUNIT || mWorldPanning) return rowoffset; + else return FixedDiv(rowoffset, mScaleY); + } + else + { + if (mWorldPanning) return FixedDiv(rowoffset, mTempScaleY); + else return FixedDiv(rowoffset, mScaleY); + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +fixed_t FTexCoordInfo::TextureOffset(fixed_t textureoffset) const +{ + if (mTempScaleX == FRACUNIT) + { + if (mScaleX==FRACUNIT || mWorldPanning) return textureoffset; + else return FixedDiv(textureoffset, mScaleX); + } + else + { + if (mWorldPanning) return FixedDiv(textureoffset, mTempScaleX); + else return FixedDiv(textureoffset, mScaleX); + } +} + +//=========================================================================== +// +// Returns the size for which texture offset coordinates are used. +// +//=========================================================================== + +fixed_t FTexCoordInfo::TextureAdjustWidth() const +{ + if (mWorldPanning) + { + if (mTempScaleX == FRACUNIT) return mRenderWidth; + else return FixedDiv(mWidth, mTempScaleX); + } + else return mWidth; +} + + + +//=========================================================================== +// +// +// +//=========================================================================== +FGLTexture * FMaterial::ValidateSysTexture(FTexture * tex, bool expand) +{ + if (tex && tex->UseType!=FTexture::TEX_Null) + { + FGLTexture *gltex = tex->gl_info.SystemTexture; + if (gltex == NULL) + { + gltex = new FGLTexture(tex, expand); + } + return gltex; + } + return NULL; +} + +//=========================================================================== +// +// Constructor +// +//=========================================================================== +TArray FMaterial::mMaterials; +int FMaterial::mMaxBound; + +FMaterial::FMaterial(FTexture * tx, bool forceexpand) +{ + assert(tx->gl_info.Material == NULL); + + bool expanded = tx->UseType == FTexture::TEX_Sprite || + tx->UseType == FTexture::TEX_SkinSprite || + tx->UseType == FTexture::TEX_Decal || + forceexpand; + + mShaderIndex = 0; + + // TODO: apply custom shader object here + /* if (tx->CustomShaderDefinition) + { + } + else + */ + if (tx->bWarped) + { + mShaderIndex = tx->bWarped; + expanded = false; + tx->gl_info.shaderspeed = static_cast(tx)->GetSpeed(); + } + else if (tx->bHasCanvas) + { + expanded = false; + } + else if (gl.shadermodel > 2) + { + if (tx->gl_info.shaderindex >= FIRST_USER_SHADER) + { + mShaderIndex = tx->gl_info.shaderindex; + expanded = false; + } + else + { + tx->CreateDefaultBrightmap(); + if (tx->gl_info.Brightmap != NULL) + { + ValidateSysTexture(tx->gl_info.Brightmap, expanded); + FTextureLayer layer = {tx->gl_info.Brightmap, false}; + mTextureLayers.Push(layer); + mShaderIndex = 3; + } + } + } + + + for (int i=GLUSE_PATCH; i<=GLUSE_TEXTURE; i++) + { + Width[i] = tx->GetWidth(); + Height[i] = tx->GetHeight(); + LeftOffset[i] = tx->LeftOffset; + TopOffset[i] = tx->TopOffset; + RenderWidth[i] = tx->GetScaledWidth(); + RenderHeight[i] = tx->GetScaledHeight(); + } + Width[GLUSE_SPRITE] = Width[GLUSE_PATCH]; + Height[GLUSE_SPRITE] = Height[GLUSE_PATCH]; + LeftOffset[GLUSE_SPRITE] = LeftOffset[GLUSE_PATCH]; + TopOffset[GLUSE_SPRITE] = TopOffset[GLUSE_PATCH]; + SpriteU[0] = SpriteV[0] = 0; + spriteright = SpriteU[1] = Width[GLUSE_PATCH] / (float)FHardwareTexture::GetTexDimension(Width[GLUSE_PATCH]); + spritebottom = SpriteV[1] = Height[GLUSE_PATCH] / (float)FHardwareTexture::GetTexDimension(Height[GLUSE_PATCH]); + + mTextureLayers.ShrinkToFit(); + mMaxBound = -1; + mMaterials.Push(this); + tx->gl_info.Material = this; + if (tx->bHasCanvas) tx->gl_info.mIsTransparent = 0; + tex = tx; + + tx->gl_info.mExpanded = expanded; + FTexture *basetex = tx->GetRedirect(gl.shadermodel < 4); + if (!expanded && !basetex->gl_info.mExpanded) + { + // check if the texture is just a simple redirect to a patch + // If so we should use the patch for texture creation to + // avoid eventual redundancies. + // This may only be done if both textures use the same expansion mode + mBaseLayer = ValidateSysTexture(basetex, false); + } + else if (!expanded) + { + // if we got a non-expanded texture that redirects to an expanded one + mBaseLayer = ValidateSysTexture(tx, false); + } + else + { + // a little adjustment to make sprites look better with texture filtering: + // create a 1 pixel wide empty frame around them. + RenderWidth[GLUSE_PATCH]+=2; + RenderHeight[GLUSE_PATCH]+=2; + Width[GLUSE_PATCH]+=2; + Height[GLUSE_PATCH]+=2; + LeftOffset[GLUSE_PATCH]+=1; + TopOffset[GLUSE_PATCH]+=1; + Width[GLUSE_SPRITE] += 2; + Height[GLUSE_SPRITE] += 2; + LeftOffset[GLUSE_SPRITE] += 1; + TopOffset[GLUSE_SPRITE] += 1; + spriteright = SpriteU[1] = Width[GLUSE_PATCH] / (float)FHardwareTexture::GetTexDimension(Width[GLUSE_PATCH]); + spritebottom = SpriteV[1] = Height[GLUSE_PATCH] / (float)FHardwareTexture::GetTexDimension(Height[GLUSE_PATCH]); + + mBaseLayer = ValidateSysTexture(tx, true); + + if (gl.flags & RFL_NPOT_TEXTURE) // trimming only works if non-power-of-2 textures are supported + { + int trim[4]; + + if (TrimBorders(trim)) + { + Width[GLUSE_SPRITE] = trim[2] + 2; + Height[GLUSE_SPRITE] = trim[3] + 2; + LeftOffset[GLUSE_SPRITE] -= trim[0]; + TopOffset[GLUSE_SPRITE] -= trim[1]; + + SpriteU[0] = SpriteU[1] * (trim[0] / (float)Width[GLUSE_PATCH]); + SpriteV[0] = SpriteV[1] * (trim[1] / (float)Height[GLUSE_PATCH]); + SpriteU[1] *= (trim[0]+trim[2]+2) / (float)Width[GLUSE_PATCH]; + SpriteV[1] *= (trim[1]+trim[3]+2) / (float)Height[GLUSE_PATCH]; + } + } + } +} + +//=========================================================================== +// +// Destructor +// +//=========================================================================== + +FMaterial::~FMaterial() +{ + for(unsigned i=0;i= size) + { + rect[0] = 0; + rect[1] = 0; + rect[2] = 1; + rect[3] = 1; + delete [] buffer; + return true; + } + + for(last = size-1; last >= first; last--) + { + if (buffer[last*4+3] != 0) break; + } + + rect[1] = first / w; + rect[3] = 1 + last/w - rect[1]; + + rect[0] = 0; + rect[2] = w; + + unsigned char *bufferoff = buffer + (rect[1] * w * 4); + h = rect[3]; + + for(int x = 0; x < w; x++) + { + for(int y = 0; y < h; y++) + { + if (bufferoff[(x+y*w)*4+3] != 0) goto outl; + } + rect[0]++; + } +outl: + rect[2] -= rect[0]; + + for(int x = w-1; rect[2] > 1; x--) + { + for(int y = 0; y < h; y++) + { + if (bufferoff[(x+y*w)*4+3] != 0) + { + delete [] buffer; + return true; + } + } + rect[2]--; + } + delete [] buffer; + return true; +} + + +//=========================================================================== +// +// Binds a texture to the renderer +// +//=========================================================================== + +void FMaterial::Bind(int cm, int clampmode, int translation, int overrideshader) +{ + int usebright = false; + int shaderindex = overrideshader > 0? overrideshader : mShaderIndex; + int maxbound = 0; + bool allowhires = tex->xScale == FRACUNIT && tex->yScale == FRACUNIT; + + int softwarewarp = gl_RenderState.SetupShader(tex->bHasCanvas, shaderindex, cm, tex->gl_info.shaderspeed); + + if (tex->bHasCanvas || tex->bWarped) clampmode = 0; + else if (clampmode != -1) clampmode &= 3; + else clampmode = 4; + + const FHardwareTexture *gltexture = mBaseLayer->Bind(0, cm, clampmode, translation, allowhires? tex:NULL, softwarewarp); + if (gltexture != NULL && shaderindex > 0 && overrideshader == 0) + { + for(unsigned i=0;iGetID(); + layer = TexMan(id); + ValidateSysTexture(layer, false); + } + else + { + layer = mTextureLayers[i].texture; + } + layer->gl_info.SystemTexture->Bind(i+1, CM_DEFAULT, clampmode, 0, NULL, false); + maxbound = i+1; + } + } + // unbind everything from the last texture that's still active + for(int i=maxbound+1; i<=mMaxBound;i++) + { + FHardwareTexture::Unbind(i); + mMaxBound = maxbound; + } +} + + +//=========================================================================== +// +// Binds a texture to the renderer +// +//=========================================================================== + +void FMaterial::BindPatch(int cm, int translation, int overrideshader) +{ + int usebright = false; + int shaderindex = overrideshader > 0? overrideshader : mShaderIndex; + int maxbound = 0; + + int softwarewarp = gl_RenderState.SetupShader(tex->bHasCanvas, shaderindex, cm, tex->gl_info.shaderspeed); + + const FHardwareTexture *glpatch = mBaseLayer->BindPatch(0, cm, translation, softwarewarp); + // The only multitexture effect usable on sprites is the brightmap. + if (glpatch != NULL && shaderindex == 3) + { + mTextureLayers[0].texture->gl_info.SystemTexture->BindPatch(1, CM_DEFAULT, 0, 0); + maxbound = 1; + } + // unbind everything from the last texture that's still active + for(int i=maxbound+1; i<=mMaxBound;i++) + { + FHardwareTexture::Unbind(i); + mMaxBound = maxbound; + } +} + + +//=========================================================================== +// +// +// +//=========================================================================== +void FMaterial::Precache() +{ + if (tex->UseType==FTexture::TEX_Sprite) + { + BindPatch(CM_DEFAULT, 0); + } + else + { + int cached = 0; + for(int i=0;i<4;i++) + { + if (mBaseLayer->gltexture[i] != 0) + { + Bind (CM_DEFAULT, i, 0); + cached++; + } + if (cached == 0) Bind(CM_DEFAULT, -1, 0); + } + } +} + +//=========================================================================== +// +// This function is needed here to temporarily manipulate the texture +// for per-wall scaling so that the coordinate functions return proper +// results. Doing this here is much easier than having the calling code +// make these calculations. +// +//=========================================================================== + +void FMaterial::GetTexCoordInfo(FTexCoordInfo *tci, fixed_t x, fixed_t y) const +{ + if (x == FRACUNIT) + { + tci->mRenderWidth = RenderWidth[GLUSE_TEXTURE]; + tci->mScaleX = tex->xScale; + tci->mTempScaleX = FRACUNIT; + } + else + { + fixed_t scale_x = FixedMul(x, tex->xScale); + int foo = (Width[GLUSE_TEXTURE] << 17) / scale_x; + tci->mRenderWidth = (foo >> 1) + (foo & 1); + tci->mScaleX = scale_x; + tci->mTempScaleX = x; + } + + if (y == FRACUNIT) + { + tci->mRenderHeight = RenderHeight[GLUSE_TEXTURE]; + tci->mScaleY = tex->yScale; + tci->mTempScaleY = FRACUNIT; + } + else + { + fixed_t scale_y = FixedMul(y, tex->yScale); + int foo = (Height[GLUSE_TEXTURE] << 17) / scale_y; + tci->mRenderHeight = (foo >> 1) + (foo & 1); + tci->mScaleY = scale_y; + tci->mTempScaleY = y; + } + if (tex->bHasCanvas) + { + tci->mScaleY = -tci->mScaleY; + tci->mRenderHeight = -tci->mRenderHeight; + } + tci->mWorldPanning = tex->bWorldPanning; + tci->mWidth = Width[GLUSE_TEXTURE]; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +int FMaterial::GetAreas(FloatRect **pAreas) const +{ + if (mShaderIndex == 0) // texture splitting can only be done if there's no attached effects + { + FTexture *tex = mBaseLayer->tex; + *pAreas = tex->gl_info.areas; + return tex->gl_info.areacount; + } + else + { + return 0; + } +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FMaterial::BindToFrameBuffer() +{ + if (mBaseLayer->gltexture == NULL) + { + // must create the hardware texture first + mBaseLayer->Bind(0, CM_DEFAULT, 0, 0, NULL, 0); + FHardwareTexture::Unbind(0); + } + mBaseLayer->gltexture[0]->BindToFrameBuffer(); +} + +//=========================================================================== +// +// GetRect +// +//=========================================================================== + +void FMaterial::GetRect(FloatRect * r, ETexUse i) const +{ + r->left = -GetScaledLeftOffsetFloat(i); + r->top = -GetScaledTopOffsetFloat(i); + r->width = GetScaledWidthFloat(i); + r->height = GetScaledHeightFloat(i); +} + + +//========================================================================== +// +// Gets a texture from the texture manager and checks its validity for +// GL rendering. +// +//========================================================================== + +FMaterial * FMaterial::ValidateTexture(FTexture * tex) +{ + if (tex && tex->UseType!=FTexture::TEX_Null) + { + FMaterial *gltex = tex->gl_info.Material; + if (gltex == NULL) + { + //@sync-tex + gltex = new FMaterial(tex, false); + } + return gltex; + } + return NULL; +} + +FMaterial * FMaterial::ValidateTexture(FTextureID no, bool translate) +{ + return ValidateTexture(translate? TexMan(no) : TexMan[no]); +} + + +//========================================================================== +// +// Flushes all hardware dependent data +// +//========================================================================== + +void FMaterial::FlushAll() +{ + for(int i=mMaterials.Size()-1;i>=0;i--) + { + mMaterials[i]->Clean(true); + } + // This is for shader layers. All shader layers must be managed by the texture manager + // so this will catch everything. + for(int i=TexMan.NumTextures()-1;i>=0;i--) + { + FGLTexture *gltex = TexMan.ByIndex(i)->gl_info.SystemTexture; + if (gltex != NULL) gltex->Clean(true); + } +} + +//========================================================================== +// +// Prints some texture info +// +//========================================================================== + +int FGLTexture::Dump(int i) +{ + int cnt = 0; + int lump = tex->GetSourceLump(); + Printf(PRINT_LOG, "Texture '%s' (Index %d, Lump %d, Name '%s'):\n", tex->Name, i, lump, Wads.GetLumpFullName(lump)); + if (hirestexture) Printf(PRINT_LOG, "\tHirestexture\n"); + if (glpatch) Printf(PRINT_LOG, "\tPatch\n"),cnt++; + if (gltexture[0]) Printf(PRINT_LOG, "\tTexture (x:no, y:no )\n"),cnt++; + if (gltexture[1]) Printf(PRINT_LOG, "\tTexture (x:yes, y:no )\n"),cnt++; + if (gltexture[2]) Printf(PRINT_LOG, "\tTexture (x:no, y:yes)\n"),cnt++; + if (gltexture[3]) Printf(PRINT_LOG, "\tTexture (x:yes, y:yes)\n"),cnt++; + if (gltexture[4]) Printf(PRINT_LOG, "\tTexture precache\n"),cnt++; + return cnt; +} + +CCMD(textureinfo) +{ + int cnth = 0, cntt = 0, pix = 0; + for(int i=0; igl_info.SystemTexture; + if (systex != NULL) + { + int cnt = systex->Dump(i); + cnth+=cnt; + cntt++; + pix += cnt * tex->GetWidth() * tex->GetHeight(); + } + } + Printf(PRINT_LOG, "%d system textures, %d hardware textures, %d pixels\n", cntt, cnth, pix); +} + diff --git a/src/gl/textures/gl_material.h b/src/gl/textures/gl_material.h new file mode 100644 index 000000000..3754e9337 --- /dev/null +++ b/src/gl/textures/gl_material.h @@ -0,0 +1,254 @@ + +#ifndef __GL_TEXTURE_H +#define __GL_TEXTURE_H + +#include "m_fixed.h" +#include "textures/textures.h" +#include "gl/textures/gl_hwtexture.h" +#include "gl/renderer/gl_colormap.h" +#include "i_system.h" + +EXTERN_CVAR(Bool, gl_precache) + +struct FRemapTable; +class FTextureShader; + + + +struct FTexCoordInfo +{ + int mRenderWidth; + int mRenderHeight; + int mWidth; + fixed_t mScaleX; + fixed_t mScaleY; + fixed_t mTempScaleX; + fixed_t mTempScaleY; + bool mWorldPanning; + + float FloatToTexU(float v) const { return v / mRenderWidth; } + float FloatToTexV(float v) const { return v / mRenderHeight; } + fixed_t RowOffset(fixed_t ofs) const; + fixed_t TextureOffset(fixed_t ofs) const; + fixed_t TextureAdjustWidth() const; +}; + +//=========================================================================== +// +// this is the texture maintenance class for OpenGL. +// +//=========================================================================== +class FMaterial; + +enum ETexUse +{ + GLUSE_PATCH, + GLUSE_TEXTURE, + GLUSE_SPRITE, +}; + + +class FGLTexture //: protected WorldTextureInfo, protected PatchTextureInfo +{ + friend class FMaterial; +public: + FTexture * tex; + FTexture * hirestexture; + char bIsTransparent; + int HiresLump; + +private: + FHardwareTexture *gltexture[5]; + FHardwareTexture *glpatch; + + int currentwarp; + int currentwarptime; + + bool bHasColorkey; // only for hires + bool bExpand; + float AlphaThreshold; + + unsigned char * LoadHiresTexture(FTexture *hirescheck, int *width, int *height, int cm); + BYTE *WarpBuffer(BYTE *buffer, int Width, int Height, int warp); + + FHardwareTexture *CreateTexture(int clampmode); + //bool CreateTexture(); + bool CreatePatch(); + + const FHardwareTexture *Bind(int texunit, int cm, int clamp, int translation, FTexture *hirescheck, int warp); + const FHardwareTexture *BindPatch(int texunit, int cm, int translation, int warp); + +public: + FGLTexture(FTexture * tx, bool expandpatches); + ~FGLTexture(); + + unsigned char * CreateTexBuffer(int cm, int translation, int & w, int & h, bool expand, FTexture *hirescheck, int warp); + + void Clean(bool all); + int Dump(int i); + +}; + +//=========================================================================== +// +// this is the material class for OpenGL. +// +//=========================================================================== + +class FMaterial +{ + struct FTextureLayer + { + FTexture *texture; + bool animated; + }; + + static TArray mMaterials; + static int mMaxBound; + + FGLTexture *mBaseLayer; + TArray mTextureLayers; + int mShaderIndex; + + short LeftOffset[3]; + short TopOffset[3]; + short Width[3]; + short Height[3]; + short RenderWidth[2]; + short RenderHeight[2]; + + float SpriteU[2], SpriteV[2]; + float spriteright, spritebottom; + + void SetupShader(int shaderindex, int &cm); + FGLTexture * ValidateSysTexture(FTexture * tex, bool expand); + bool TrimBorders(int *rect); + +public: + FTexture *tex; + + FMaterial(FTexture *tex, bool forceexpand); + ~FMaterial(); + void Precache(); + bool isMasked() const + { + return !!mBaseLayer->tex->bMasked; + } + + void Bind(int cm, int clamp = 0, int translation = 0, int overrideshader = 0); + void BindPatch(int cm, int translation = 0, int overrideshader = 0); + + unsigned char * CreateTexBuffer(int cm, int translation, int & w, int & h, bool expand = false, bool allowhires=true) const + { + return mBaseLayer->CreateTexBuffer(cm, translation, w, h, expand, allowhires? tex:NULL, 0); + } + + void Clean(bool f) + { + mBaseLayer->Clean(f); + } + + void BindToFrameBuffer(); + // Patch drawing utilities + + void GetRect(FloatRect *r, ETexUse i) const; + void GetTexCoordInfo(FTexCoordInfo *tci, fixed_t x, fixed_t y) const; + + // This is scaled size in integer units as needed by walls and flats + int TextureHeight(ETexUse i) const { return RenderHeight[i]; } + int TextureWidth(ETexUse i) const { return RenderWidth[i]; } + + int GetAreas(FloatRect **pAreas) const; + + int GetWidth(ETexUse i) const + { + return Width[i]; + } + + int GetHeight(ETexUse i) const + { + return Height[i]; + } + + int GetLeftOffset(ETexUse i) const + { + return LeftOffset[i]; + } + + int GetTopOffset(ETexUse i) const + { + return TopOffset[i]; + } + + int GetScaledLeftOffset(ETexUse i) const + { + return DivScale16(LeftOffset[i], tex->xScale); + } + + int GetScaledTopOffset(ETexUse i) const + { + return DivScale16(TopOffset[i], tex->yScale); + } + + float GetScaledLeftOffsetFloat(ETexUse i) const + { + return LeftOffset[i] / FIXED2FLOAT(tex->xScale); + } + + float GetScaledTopOffsetFloat(ETexUse i) const + { + return TopOffset[i] / FIXED2FLOAT(tex->yScale); + } + + // This is scaled size in floating point as needed by sprites + float GetScaledWidthFloat(ETexUse i) const + { + return Width[i] / FIXED2FLOAT(tex->xScale); + } + + float GetScaledHeightFloat(ETexUse i) const + { + return Height[i] / FIXED2FLOAT(tex->yScale); + } + + // Get right/bottom UV coordinates for patch drawing + float GetUL() const { return 0; } + float GetVT() const { return 0; } + float GetUR() const { return spriteright; } + float GetVB() const { return spritebottom; } + float GetU(float upix) const { return upix/(float)Width[GLUSE_PATCH] * spriteright; } + float GetV(float vpix) const { return vpix/(float)Height[GLUSE_PATCH] * spritebottom; } + + float GetSpriteUL() const { return SpriteU[0]; } + float GetSpriteVT() const { return SpriteV[0]; } + float GetSpriteUR() const { return SpriteU[1]; } + float GetSpriteVB() const { return SpriteV[1]; } + + + + bool GetTransparent() const + { + if (mBaseLayer->bIsTransparent == -1) + { + if (!mBaseLayer->tex->bHasCanvas) + { + int w, h; + unsigned char *buffer = CreateTexBuffer(CM_DEFAULT, 0, w, h); + delete [] buffer; + } + else + { + mBaseLayer->bIsTransparent = 0; + } + } + return !!mBaseLayer->bIsTransparent; + } + + static void DeleteAll(); + static void FlushAll(); + static FMaterial *ValidateTexture(FTexture * tex); + static FMaterial *ValidateTexture(FTextureID no, bool trans); + +}; + +#endif diff --git a/src/gl/textures/gl_skyboxtexture.cpp b/src/gl/textures/gl_skyboxtexture.cpp new file mode 100644 index 000000000..f9918dff2 --- /dev/null +++ b/src/gl/textures/gl_skyboxtexture.cpp @@ -0,0 +1,229 @@ +/* +** gl_skyboxtexture.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2004-2009 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** +** 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 "doomtype.h" +#include "sc_man.h" +#include "w_wad.h" +#include "textures/textures.h" +#include "gl/textures/gl_skyboxtexture.h" + + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +FSkyBox::FSkyBox() +{ + faces[0]=faces[1]=faces[2]=faces[3]=faces[4]=faces[5]=NULL; + UseType=TEX_Override; + gl_info.bSkybox = true; + fliptop = false; +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +FSkyBox::~FSkyBox() +{ + // The faces are only referenced but not owned so don't delete them. +} + +//----------------------------------------------------------------------------- +// +// If something attempts to use this as a texture just pass the information of the first face. +// +//----------------------------------------------------------------------------- + +const BYTE *FSkyBox::GetColumn (unsigned int column, const Span **spans_out) +{ + if (faces[0]) return faces[0]->GetColumn(column, spans_out); + return NULL; +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +const BYTE *FSkyBox::GetPixels () +{ + if (faces[0]) return faces[0]->GetPixels(); + return NULL; +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +int FSkyBox::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) +{ + if (faces[0]) return faces[0]->CopyTrueColorPixels(bmp, x, y, rotate, inf); + return 0; +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +bool FSkyBox::UseBasePalette() +{ + return false; // not really but here it's not important. +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void FSkyBox::Unload () +{ + //for(int i=0;i<6;i++) if (faces[i]) faces[i]->Unload(); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void FSkyBox::PrecacheGL() +{ + //for(int i=0;i<6;i++) if (faces[i]) faces[i]->PrecacheGL(); +} + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +void gl_ParseSkybox(FScanner &sc) +{ + int facecount=0; + + sc.MustGetString(); + + FSkyBox * sb = new FSkyBox; + uppercopy(sb->Name, sc.String); + sb->Name[8]=0; + if (sc.CheckString("fliptop")) + { + sb->fliptop = true; + } + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (facecount<6) + { + sb->faces[facecount] = TexMan[TexMan.GetTexture(sc.String, FTexture::TEX_Wall, FTextureManager::TEXMAN_TryAny|FTextureManager::TEXMAN_Overridable)]; + } + facecount++; + } + if (facecount != 3 && facecount != 6) + { + sc.ScriptError("%s: Skybox definition requires either 3 or 6 faces", sb->Name); + } + sb->SetSize(); + TexMan.AddTexture(sb); +} + +//----------------------------------------------------------------------------- +// +// gl_ParseVavoomSkybox +// +//----------------------------------------------------------------------------- + +void gl_ParseVavoomSkybox() +{ + int lump = Wads.CheckNumForName("SKYBOXES"); + + if (lump < 0) return; + + FScanner sc(lump); + while (sc.GetString()) + { + int facecount=0; + int maplump = -1; + FSkyBox * sb = new FSkyBox; + uppercopy(sb->Name, sc.String); + sb->Name[8]=0; + sb->fliptop = true; + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + if (facecount<6) + { + sc.MustGetStringName("{"); + sc.MustGetStringName("map"); + sc.MustGetString(); + + maplump = Wads.CheckNumForFullName(sc.String, true); + if (maplump==-1) + Printf("Texture '%s' not found in Vavoom skybox '%s'\n", sc.String, sb->Name); + + FTextureID tex = TexMan.FindTextureByLumpNum(maplump); + if (!tex.isValid()) + { + tex = TexMan.CreateTexture(maplump, FTexture::TEX_MiscPatch); + } + sb->faces[facecount] = TexMan[tex]; + sc.MustGetStringName("}"); + } + facecount++; + } + if (facecount != 6) + { + sc.ScriptError("%s: Skybox definition requires 6 faces", sb->Name); + } + sb->SetSize(); + TexMan.AddTexture(sb); + } +} + diff --git a/src/gl/textures/gl_skyboxtexture.h b/src/gl/textures/gl_skyboxtexture.h new file mode 100644 index 000000000..a85e14190 --- /dev/null +++ b/src/gl/textures/gl_skyboxtexture.h @@ -0,0 +1,45 @@ + + +//----------------------------------------------------------------------------- +// +// This is not a real texture but will be added to the texture manager +// so that it can be handled like any other sky. +// +//----------------------------------------------------------------------------- + +class FSkyBox : public FTexture +{ +public: + + FTexture * faces[6]; + bool fliptop; + + FSkyBox(); + ~FSkyBox(); + const BYTE *GetColumn (unsigned int column, const Span **spans_out); + const BYTE *GetPixels (); + int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf); + bool UseBasePalette(); + void Unload (); + void PrecacheGL(); + + void SetSize() + { + if (faces[0]) + { + Width=faces[0]->GetWidth(); + Height=faces[0]->GetHeight(); + CalcBitSize(); + } + } + + bool Is3Face() const + { + return faces[5]==NULL; + } + + bool IsFlipped() const + { + return fliptop; + } +}; diff --git a/src/gl/textures/gl_texture.cpp b/src/gl/textures/gl_texture.cpp new file mode 100644 index 000000000..6222c4b1c --- /dev/null +++ b/src/gl/textures/gl_texture.cpp @@ -0,0 +1,857 @@ +/* +** Global texture data +** +**--------------------------------------------------------------------------- +** Copyright 2004-2009 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** +** 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 "c_cvars.h" +#include "w_wad.h" +#include "templates.h" +#include "colormatcher.h" +#include "r_data/r_translate.h" +#include "c_dispatch.h" +#ifdef _WIN32 +#include "win32gliface.h" +#endif +#include "v_palette.h" +#include "sc_man.h" + +#include "gl/renderer/gl_renderer.h" +#include "gl/textures/gl_texture.h" +#include "gl/textures/gl_material.h" + +//========================================================================== +// +// Texture CVARs +// +//========================================================================== +CUSTOM_CVAR(Float,gl_texture_filter_anisotropic,8.0f,CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +{ + if (GLRenderer != NULL) GLRenderer->FlushTextures(); +} + +CCMD(gl_flush) +{ + if (GLRenderer != NULL) GLRenderer->FlushTextures(); +} + +CUSTOM_CVAR(Int, gl_texture_filter, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +{ + if (self < 0 || self > 5) self=4; + if (GLRenderer != NULL) GLRenderer->FlushTextures(); +} + +CUSTOM_CVAR(Int, gl_texture_format, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +{ + // [BB] The number of available texture modes depends on the GPU capabilities. + // RFL_TEXTURE_COMPRESSION gives us one additional mode and RFL_TEXTURE_COMPRESSION_S3TC + // another three. + int numOfAvailableTextureFormat = 4; + if ( gl.flags & RFL_TEXTURE_COMPRESSION && gl.flags & RFL_TEXTURE_COMPRESSION_S3TC ) + numOfAvailableTextureFormat = 8; + else if ( gl.flags & RFL_TEXTURE_COMPRESSION ) + numOfAvailableTextureFormat = 5; + if (self < 0 || self > numOfAvailableTextureFormat-1) self=0; + GLRenderer->FlushTextures(); +} + +CUSTOM_CVAR(Bool, gl_texture_usehires, true, CVAR_ARCHIVE|CVAR_NOINITCALL) +{ + if (GLRenderer != NULL) GLRenderer->FlushTextures(); +} + +CVAR(Bool, gl_precache, false, CVAR_ARCHIVE) +CVAR(Bool, gl_clamp_per_texture, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) + +CVAR(Bool, gl_trimsprites, true, CVAR_ARCHIVE); + +TexFilter_s TexFilter[]={ + {GL_NEAREST, GL_NEAREST, false}, + {GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST, true}, + {GL_LINEAR, GL_LINEAR, false}, + {GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, true}, + {GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, true}, + {GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST, true}, +}; + +int TexFormat[]={ + GL_RGBA8, + GL_RGB5_A1, + GL_RGBA4, + GL_RGBA2, + // [BB] Added compressed texture formats. + GL_COMPRESSED_RGBA_ARB, + GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, + GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, + GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, +}; + + + +bool HasGlobalBrightmap; +FRemapTable GlobalBrightmap; + +//=========================================================================== +// +// Examines the colormap to see if some of the colors have to be +// considered fullbright all the time. +// +//=========================================================================== + +void gl_GenerateGlobalBrightmapFromColormap() +{ + HasGlobalBrightmap = false; + int lump = Wads.CheckNumForName("COLORMAP"); + if (lump == -1) lump = Wads.CheckNumForName("COLORMAP", ns_colormaps); + if (lump == -1) return; + FMemLump cmap = Wads.ReadLump(lump); + FMemLump palette = Wads.ReadLump("PLAYPAL"); + const unsigned char *cmapdata = (const unsigned char *)cmap.GetMem(); + const unsigned char *paldata = (const unsigned char *)palette.GetMem(); + + const int black = 0; + const int white = ColorMatcher.Pick(255,255,255); + + + GlobalBrightmap.MakeIdentity(); + memset(GlobalBrightmap.Remap, white, 256); + for(int i=0;i<256;i++) GlobalBrightmap.Palette[i]=PalEntry(255,255,255,255); + for(int j=0;j<32;j++) + { + for(int i=0;i<256;i++) + { + // the palette comparison should be for ==0 but that gives false positives with Heretic + // and Hexen. + if (cmapdata[i+j*256]!=i || (paldata[3*i]<10 && paldata[3*i+1]<10 && paldata[3*i+2]<10)) + { + GlobalBrightmap.Remap[i]=black; + GlobalBrightmap.Palette[i]=PalEntry(0,0,0); + } + } + } + for(int i=0;i<256;i++) + { + HasGlobalBrightmap |= GlobalBrightmap.Remap[i] == white; + if (GlobalBrightmap.Remap[i] == white) DPrintf("Marked color %d as fullbright\n",i); + } +} + +//=========================================================================== +// +// averageColor +// input is RGBA8 pixel format. +// The resulting RGB color can be scaled uniformly so that the highest +// component becomes one. +// +//=========================================================================== +PalEntry averageColor(const DWORD *data, int size, fixed_t maxout_factor) +{ + int i; + unsigned int r, g, b; + + + + // First clear them. + r = g = b = 0; + if (size==0) + { + return PalEntry(255,255,255); + } + for(i = 0; i < size; i++) + { + r += BPART(data[i]); + g += GPART(data[i]); + b += RPART(data[i]); + } + + r = r/size; + g = g/size; + b = b/size; + + int maxv=MAX(MAX(r,g),b); + + if(maxv && maxout_factor) + { + maxout_factor = FixedMul(maxout_factor, 255); + r = Scale(r, maxout_factor, maxv); + g = Scale(g, maxout_factor, maxv); + b = Scale(b, maxout_factor, maxv); + } + return PalEntry(r,g,b); +} + + + +//========================================================================== +// +// GL status data for a texture +// +//========================================================================== + +FTexture::MiscGLInfo::MiscGLInfo() throw() +{ + bGlowing = false; + GlowColor = 0; + GlowHeight = 128; + bSkybox = false; + FloorSkyColor = 0; + CeilingSkyColor = 0; + bFullbright = false; + bSkyColorDone = false; + bBrightmapChecked = false; + bBrightmap = false; + bBrightmapDisablesFullbright = false; + bNoFilter = false; + bNoCompress = false; + mExpanded = false; + areas = NULL; + areacount = 0; + mIsTransparent = -1; + shaderspeed = 1.f; + shaderindex = 0; + + Material = NULL; + SystemTexture = NULL; + Brightmap = NULL; + DecalTexture = NULL; +} + +FTexture::MiscGLInfo::~MiscGLInfo() +{ + if (Material != NULL) delete Material; + Material = NULL; + + if (SystemTexture != NULL) delete SystemTexture; + SystemTexture = NULL; + + // this is managed by the texture manager so it may not be deleted here. + //if (Brightmap != NULL) delete Brightmap; + Brightmap = NULL; + + if (areas != NULL) delete [] areas; + areas = NULL; + + if (DecalTexture != NULL) delete DecalTexture; + DecalTexture = NULL; + +} + +//=========================================================================== +// +// Checks if the texture has a default brightmap and creates it if so +// +//=========================================================================== +void FTexture::CreateDefaultBrightmap() +{ + if (!gl_info.bBrightmapChecked) + { + // Check for brightmaps + if (UseBasePalette() && HasGlobalBrightmap && + UseType != TEX_Decal && UseType != TEX_MiscPatch && UseType != TEX_FontChar && + gl_info.Brightmap == NULL && bWarped == 0 + ) + { + // May have one - let's check when we use this texture + const BYTE *texbuf = GetPixels(); + const int white = ColorMatcher.Pick(255,255,255); + + int size = GetWidth() * GetHeight(); + for(int i=0;iPrecache(); + } +} + +//========================================================================== +// +// Precaches a GL texture +// +//========================================================================== + +void FTexture::UncacheGL() +{ + if (gl_info.Material) gl_info.Material->Clean(true); +} + +//========================================================================== +// +// Calculates glow color for a texture +// +//========================================================================== + +void FTexture::GetGlowColor(float *data) +{ + if (gl_info.bGlowing && gl_info.GlowColor == 0) + { + int w, h; + unsigned char *buffer = GLRenderer->GetTextureBuffer(this, w, h); + + if (buffer) + { + gl_info.GlowColor = averageColor((DWORD *) buffer, w*h, 6*FRACUNIT/10); + delete[] buffer; + } + + // Black glow equals nothing so switch glowing off + if (gl_info.GlowColor == 0) gl_info.bGlowing = false; + } + data[0]=gl_info.GlowColor.r/255.0f; + data[1]=gl_info.GlowColor.g/255.0f; + data[2]=gl_info.GlowColor.b/255.0f; +} + +//=========================================================================== +// +// Gets the average color of a texture for use as a sky cap color +// +//=========================================================================== + +PalEntry FTexture::GetSkyCapColor(bool bottom) +{ + PalEntry col; + int w; + int h; + + if (!gl_info.bSkyColorDone) + { + gl_info.bSkyColorDone = true; + + unsigned char *buffer = GLRenderer->GetTextureBuffer(this, w, h); + + if (buffer) + { + gl_info.CeilingSkyColor = averageColor((DWORD *) buffer, w * MIN(30, h), 0); + if (h>30) + { + gl_info.FloorSkyColor = averageColor(((DWORD *) buffer)+(h-30)*w, w * 30, 0); + } + else gl_info.FloorSkyColor = gl_info.CeilingSkyColor; + delete[] buffer; + } + } + return bottom? gl_info.FloorSkyColor : gl_info.CeilingSkyColor; +} + +//=========================================================================== +// +// Finds gaps in the texture which can be skipped by the renderer +// This was mainly added to speed up one area in E4M6 of 007LTSD +// +//=========================================================================== + +bool FTexture::FindHoles(const unsigned char * buffer, int w, int h) +{ + const unsigned char * li; + int y,x; + int startdraw,lendraw; + int gaps[5][2]; + int gapc=0; + + + // already done! + if (gl_info.areacount) return false; + if (UseType == TEX_Flat) return false; // flats don't have transparent parts + gl_info.areacount=-1; //whatever happens next, it shouldn't be done twice! + + // large textures are excluded for performance reasons + if (h>512) return false; + + startdraw=-1; + lendraw=0; + for(y=0;y>24; + + if (alpha != 0xff && alpha != 0) + { + gl_info.mIsTransparent = 1; + return; + } + } + } + gl_info.mIsTransparent = 0; + } +} + + +//=========================================================================== +// +// smooth the edges of transparent fields in the texture +// +//=========================================================================== + +#ifdef WORDS_BIGENDIAN +#define MSB 0 +#define SOME_MASK 0xffffff00 +#else +#define MSB 3 +#define SOME_MASK 0x00ffffff +#endif + +#define CHKPIX(ofs) (l1[(ofs)*4+MSB]==255 ? (( ((DWORD*)l1)[0] = ((DWORD*)l1)[ofs]&SOME_MASK), trans=true ) : false) + +bool FTexture::SmoothEdges(unsigned char * buffer,int w, int h) +{ + int x,y; + bool trans=buffer[MSB]==0; // If I set this to false here the code won't detect textures + // that only contain transparent pixels. + unsigned char * l1; + + if (h<=1 || w<=1) return false; // makes (a) no sense and (b) doesn't work with this code! + + l1=buffer; + + + if (l1[MSB]==0 && !CHKPIX(1)) CHKPIX(w); + l1+=4; + for(x=1;xbNoDecals; + Rotations = source->Rotations; + UseType = source->UseType; + gl_info.bBrightmap = true; + id.SetInvalid(); + SourceLump = -1; +} + +FBrightmapTexture::~FBrightmapTexture () +{ +} + +const BYTE *FBrightmapTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + // not needed + return NULL; +} + +const BYTE *FBrightmapTexture::GetPixels () +{ + // not needed + return NULL; +} + +void FBrightmapTexture::Unload () +{ +} + +int FBrightmapTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) +{ + SourcePic->CopyTrueColorTranslated(bmp, x, y, rotate, &GlobalBrightmap); + return 0; +} + + +//=========================================================================== +// +// A cloned texture. This is needed by the decal code which needs to assign +// a different texture type to some of its graphics. +// +//=========================================================================== + +FCloneTexture::FCloneTexture (FTexture *source, int usetype) +{ + memset(Name, 0, sizeof(Name)); + SourcePic = source; + CopySize(source); + bNoDecals = source->bNoDecals; + Rotations = source->Rotations; + UseType = usetype; + gl_info.bBrightmap = false; + id.SetInvalid(); + SourceLump = -1; +} + +FCloneTexture::~FCloneTexture () +{ +} + +const BYTE *FCloneTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + return NULL; +} + +const BYTE *FCloneTexture::GetPixels () +{ + return NULL; +} + +void FCloneTexture::Unload () +{ +} + +int FCloneTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) +{ + return SourcePic->CopyTrueColorPixels(bmp, x, y, rotate, inf); +} + +//========================================================================== +// +// Parses a brightmap definition +// +//========================================================================== + +void gl_ParseBrightmap(FScanner &sc, int deflump) +{ + int type = FTexture::TEX_Any; + bool disable_fullbright=false; + bool thiswad = false; + bool iwad = false; + int maplump = -1; + FString maplumpname; + + sc.MustGetString(); + if (sc.Compare("texture")) type = FTexture::TEX_Wall; + else if (sc.Compare("flat")) type = FTexture::TEX_Flat; + else if (sc.Compare("sprite")) type = FTexture::TEX_Sprite; + else sc.UnGet(); + + sc.MustGetString(); + FTextureID no = TexMan.CheckForTexture(sc.String, type); + FTexture *tex = TexMan[no]; + + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) + { + sc.MustGetString(); + if (sc.Compare("disablefullbright")) + { + // This can also be used without a brightness map to disable + // fullbright in rotations that only use brightness maps on + // other angles. + disable_fullbright = true; + } + else if (sc.Compare("thiswad")) + { + // only affects textures defined in the WAD containing the definition file. + thiswad = true; + } + else if (sc.Compare ("iwad")) + { + // only affects textures defined in the IWAD. + iwad = true; + } + else if (sc.Compare ("map")) + { + sc.MustGetString(); + + if (maplump >= 0) + { + Printf("Multiple brightmap definitions in texture %s\n", tex? tex->Name : "(null)"); + } + + maplump = Wads.CheckNumForFullName(sc.String, true); + + if (maplump==-1) + Printf("Brightmap '%s' not found in texture '%s'\n", sc.String, tex? tex->Name : "(null)"); + + maplumpname = sc.String; + } + } + if (!tex) + { + return; + } + if (thiswad || iwad) + { + bool useme = false; + int lumpnum = tex->GetSourceLump(); + + if (lumpnum != -1) + { + if (iwad && Wads.GetLumpFile(lumpnum) <= FWadCollection::IWAD_FILENUM) useme = true; + if (thiswad && Wads.GetLumpFile(lumpnum) == deflump) useme = true; + } + if (!useme) return; + } + + if (maplump != -1) + { + if (tex->bWarped != 0) + { + Printf("Cannot combine warping with brightmap on texture '%s'\n", tex->Name); + return; + } + + // Brightmap textures are stored in the texture manager so that multiple + // instances of the same textures can be avoided. + FTexture *brightmap; + FTextureID brightmapId = TexMan.FindTextureByLumpNum(maplump); + + if (!brightmapId.isValid()) + { + // a texture for this lump has not been created yet. + brightmap = FTexture::CreateTexture(maplump, tex->UseType); + if (!brightmap) + { + Printf("Unable to create texture from '%s' in brightmap definition for '%s'\n", + maplumpname.GetChars(), tex->Name); + return; + } + brightmap->gl_info.bBrightmap = true; + brightmap->Name[0] = 0; // brightmaps don't have names + TexMan.AddTexture(brightmap); + } + else + { + brightmap = TexMan[brightmapId]; + } + + tex->gl_info.Brightmap = brightmap; + } + tex->gl_info.bBrightmapDisablesFullbright = disable_fullbright; +} + +//========================================================================== +// +// Parses a GLBoom+ detail texture definition +// +// Syntax is this: +// detail +// { +// (walls | flats) [default_detail_name [width [height [offset_x [offset_y]]]]] +// { +// texture_name [detail_name [width [height [offset_x [offset_y]]]]] +// } +// } +// This merely parses the block and returns no error if valid. The feature +// is not actually implemented, so nothing else happens. +//========================================================================== + +void gl_ParseDetailTexture(FScanner &sc) +{ + while (!sc.CheckToken('}')) + { + sc.MustGetString(); + if (sc.Compare("walls") || sc.Compare("flats")) + { + if (!sc.CheckToken('{')) + { + sc.MustGetString(); // Default detail texture + if (sc.CheckFloat()) // Width + if (sc.CheckFloat()) // Height + if (sc.CheckFloat()) // OffsX + if (sc.CheckFloat()) // OffsY + { + // Nothing + } + } + else sc.UnGet(); + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) + { + sc.MustGetString(); // Texture + if (sc.GetString()) // Detail texture + { + if (sc.CheckFloat()) // Width + if (sc.CheckFloat()) // Height + if (sc.CheckFloat()) // OffsX + if (sc.CheckFloat()) // OffsY + { + // Nothing + } + } + else sc.UnGet(); + } + } + } +} + diff --git a/src/gl/textures/gl_texture.h b/src/gl/textures/gl_texture.h new file mode 100644 index 000000000..afb812ff0 --- /dev/null +++ b/src/gl/textures/gl_texture.h @@ -0,0 +1,88 @@ +/* +** gl_hqresize.h +** Contains high quality upsampling functions. +** +**--------------------------------------------------------------------------- +** Copyright 2008 Benjamin Berkels +** 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. +** +** 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. +**--------------------------------------------------------------------------- +** +*/ + +#ifndef __GL_HQRESIZE_H__ +#define __GL_HQRESIZE_H__ + +#include "r_defs.h" + +class FBrightmapTexture : public FTexture +{ +public: + FBrightmapTexture (FTexture *source); + ~FBrightmapTexture (); + + const BYTE *GetColumn (unsigned int column, const Span **spans_out); + const BYTE *GetPixels (); + void Unload (); + + int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf); + bool UseBasePalette() { return false; } + +protected: + FTexture *SourcePic; + //BYTE *Pixels; + //Span **Spans; +}; + +class FCloneTexture : public FTexture +{ +public: + FCloneTexture (FTexture *source, int usetype); + ~FCloneTexture (); + + const BYTE *GetColumn (unsigned int column, const Span **spans_out); + const BYTE *GetPixels (); + void Unload (); + + int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf); + bool UseBasePalette() { return false; } + +protected: + FTexture *SourcePic; +}; + + +void gl_GenerateGlobalBrightmapFromColormap(); +PalEntry averageColor(const DWORD *data, int size, fixed_t maxout); + + + +unsigned char *gl_CreateUpsampledTextureBuffer ( const FTexture *inputTexture, unsigned char *inputBuffer, const int inWidth, const int inHeight, int &outWidth, int &outHeight, bool hasAlpha ); +int CheckDDPK3(FTexture *tex); +int CheckExternalFile(FTexture *tex, bool & hascolorkey); +PalEntry averageColor(const DWORD *data, int size, fixed_t maxout); + +#endif // __GL_HQRESIZE_H__ + diff --git a/src/gl/textures/gl_translate.cpp b/src/gl/textures/gl_translate.cpp new file mode 100644 index 000000000..f9a46dba8 --- /dev/null +++ b/src/gl/textures/gl_translate.cpp @@ -0,0 +1,88 @@ +/* +** gl_translate.cpp +** GL-related translation stuff +** +**--------------------------------------------------------------------------- +** Copyright 2007 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. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. 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 "doomtype.h" +#include "r_data/r_translate.h" +#include "gl/textures/gl_translate.h" +#include "m_crc32.h" + +TArray GLTranslationPalette::AllPalettes; + + +GLTranslationPalette *GLTranslationPalette::CreatePalette(FRemapTable *remap) +{ + GLTranslationPalette *p = new GLTranslationPalette(remap); + p->Update(); + return p; +} + +bool GLTranslationPalette::Update() +{ + PalData pd; + + memset(pd.pe, 0, sizeof(pd.pe)); + memcpy(pd.pe, remap->Palette, remap->NumEntries * sizeof(*remap->Palette)); + pd.crc32 = CalcCRC32((BYTE*)pd.pe, sizeof(pd.pe)); + for(unsigned int i=0;i< AllPalettes.Size(); i++) + { + if (pd.crc32 == AllPalettes[i].crc32) + { + if (!memcmp(pd.pe, AllPalettes[i].pe, sizeof(pd.pe))) + { + Index = 1+i; + return true; + } + } + } + Index = 1+AllPalettes.Push(pd); + return true; +} + +int GLTranslationPalette::GetInternalTranslation(int trans) +{ + if (trans <= 0) return 0; + + FRemapTable *remap = TranslationToTable(trans); + if (remap == NULL || remap->Inactive) return 0; + + GLTranslationPalette *tpal = static_cast(remap->GetNative()); + if (tpal == NULL) return 0; + return tpal->GetIndex(); +} diff --git a/src/gl/textures/gl_translate.h b/src/gl/textures/gl_translate.h new file mode 100644 index 000000000..07ede1a22 --- /dev/null +++ b/src/gl/textures/gl_translate.h @@ -0,0 +1,42 @@ +#ifndef __GL_TRANSLATE__ +#define __GL_TRANSLATE__ + +#include "doomtype.h" +#include "v_video.h" + +enum +{ + TRANSLATION_ICE = -1, + TRANSLATION_INTENSITY = -2, + TRANSLATION_SHADE = -3, +}; + + +class GLTranslationPalette : public FNativePalette +{ + struct PalData + { + int crc32; + PalEntry pe[256]; + }; + static TArray AllPalettes; + + int Index; + FRemapTable *remap; + + GLTranslationPalette(FRemapTable *r) { remap=r; Index=-1; } + +public: + + static GLTranslationPalette *CreatePalette(FRemapTable *remap); + static int GetInternalTranslation(int trans); + static PalEntry *GetPalette(unsigned int index) + { + return index > 0 && index <= AllPalettes.Size()? AllPalettes[index-1].pe : NULL; + } + bool Update(); + int GetIndex() const { return Index; } +}; + + +#endif diff --git a/src/gl/utility/gl_clock.cpp b/src/gl/utility/gl_clock.cpp new file mode 100644 index 000000000..7ad32f8ee --- /dev/null +++ b/src/gl/utility/gl_clock.cpp @@ -0,0 +1,222 @@ +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include + +#define USE_WINDOWS_DWORD +#endif + +#include "i_system.h" +#include "g_level.h" +#include "c_console.h" +#include "c_dispatch.h" +#include "r_utility.h" +#include "v_video.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_convert.h" + + +glcycle_t RenderWall,SetupWall,ClipWall,SplitWall; +glcycle_t RenderFlat,SetupFlat; +glcycle_t RenderSprite,SetupSprite; +glcycle_t All, Finish, PortalAll, Bsp; +glcycle_t ProcessAll; +glcycle_t RenderAll; +glcycle_t Dirty; +int vertexcount, flatvertices, flatprimitives; + +int rendered_lines,rendered_flats,rendered_sprites,render_vertexsplit,render_texsplit,rendered_decals, rendered_portals; +int iter_dlightf, iter_dlight, draw_dlight, draw_dlightf; + +double gl_SecondsPerCycle = 1e-8; +double gl_MillisecPerCycle = 1e-5; // 100 MHz + +// For GL timing the performance counter is far too costly so we still need RDTSC +// even though it may not be perfect. + +void gl_CalculateCPUSpeed () +{ + #ifdef WIN32 + LARGE_INTEGER freq; + + QueryPerformanceFrequency (&freq); + + if (freq.QuadPart != 0) + { + LARGE_INTEGER count1, count2; + unsigned minDiff; + long long ClockCalibration = 0; + + // Count cycles for at least 55 milliseconds. + // The performance counter is very low resolution compared to CPU + // speeds today, so the longer we count, the more accurate our estimate. + // On the other hand, we don't want to count too long, because we don't + // want the user to notice us spend time here, since most users will + // probably never use the performance statistics. + minDiff = freq.LowPart * 11 / 200; + + // Minimize the chance of task switching during the testing by going very + // high priority. This is another reason to avoid timing for too long. + SetPriorityClass (GetCurrentProcess (), REALTIME_PRIORITY_CLASS); + SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL); + ClockCalibration = __rdtsc(); + QueryPerformanceCounter (&count1); + do + { + QueryPerformanceCounter (&count2); + } while ((DWORD)((unsigned __int64)count2.QuadPart - (unsigned __int64)count1.QuadPart) < minDiff); + ClockCalibration = __rdtsc() - ClockCalibration; + QueryPerformanceCounter (&count2); + SetPriorityClass (GetCurrentProcess (), NORMAL_PRIORITY_CLASS); + SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_NORMAL); + + double CyclesPerSecond = (double)ClockCalibration * + (double)freq.QuadPart / + (double)((__int64)count2.QuadPart - (__int64)count1.QuadPart); + gl_SecondsPerCycle = 1.0 / CyclesPerSecond; + gl_MillisecPerCycle = 1000.0 / CyclesPerSecond; + } + #endif +} + + +void ResetProfilingData() +{ + All.Reset(); + All.Clock(); + Bsp.Reset(); + PortalAll.Reset(); + RenderAll.Reset(); + ProcessAll.Reset(); + RenderWall.Reset(); + SetupWall.Reset(); + SplitWall.Reset(); + ClipWall.Reset(); + RenderFlat.Reset(); + SetupFlat.Reset(); + RenderSprite.Reset(); + SetupSprite.Reset(); + + flatvertices=flatprimitives=vertexcount=0; + render_texsplit=render_vertexsplit=rendered_lines=rendered_flats=rendered_sprites=rendered_decals=rendered_portals = 0; +} + +//----------------------------------------------------------------------------- +// +// Rendering statistics +// +//----------------------------------------------------------------------------- + +static void AppendRenderTimes(FString &str) +{ + double setupwall = SetupWall.TimeMS() - SplitWall.TimeMS(); + double clipwall = ClipWall.TimeMS() - SetupWall.TimeMS(); + double bsp = Bsp.TimeMS() - ClipWall.TimeMS() - SetupFlat.TimeMS() - SetupSprite.TimeMS(); + + str.AppendFormat("W: Render=%2.3f, Split = %2.3f, Setup=%2.3f, Clip=%2.3f\n" + "F: Render=%2.3f, Setup=%2.3f\n" + "S: Render=%2.3f, Setup=%2.3f\n" + "All=%2.3f, Render=%2.3f, Setup=%2.3f, BSP = %2.3f, Portal=%2.3f, Finish=%2.3f\n", + RenderWall.TimeMS(), SplitWall.TimeMS(), setupwall, clipwall, RenderFlat.TimeMS(), SetupFlat.TimeMS(), + RenderSprite.TimeMS(), SetupSprite.TimeMS(), All.TimeMS() + Finish.TimeMS(), RenderAll.TimeMS(), + ProcessAll.TimeMS(), bsp, PortalAll.TimeMS(), Finish.TimeMS()); +} + +static void AppendRenderStats(FString &out) +{ + out.AppendFormat("Walls: %d (%d splits, %d t-splits, %d vertices)\n" + "Flats: %d (%d primitives, %d vertices)\n" + "Sprites: %d, Decals=%d, Portals: %d\n", + rendered_lines, render_vertexsplit, render_texsplit, vertexcount, rendered_flats, flatprimitives, flatvertices, rendered_sprites,rendered_decals, rendered_portals ); +} + +static void AppendLightStats(FString &out) +{ + out.AppendFormat("DLight - Walls: %d processed, %d rendered - Flats: %d processed, %d rendered\n", + iter_dlight, draw_dlight, iter_dlightf, draw_dlightf ); +} + +ADD_STAT(rendertimes) +{ + static FString buff; + static int lasttime=0; + int t=I_FPSTime(); + if (t-lasttime>1000) + { + buff.Truncate(0); + AppendRenderTimes(buff); + lasttime=t; + } + return buff; +} + +ADD_STAT(renderstats) +{ + FString out; + AppendRenderStats(out); + return out; +} + +ADD_STAT(lightstats) +{ + FString out; + AppendLightStats(out); + return out; +} + +void AppendMissingTextureStats(FString &out); + + +static int printstats; +static bool switchfps; +static unsigned int waitstart; +EXTERN_CVAR(Bool, vid_fps) + +void CheckBench() +{ + if (printstats && ConsoleState == c_up) + { + // if we started the FPS counter ourselves or ran from the console + // we need to wait for it to stabilize before using it. + if (waitstart > 0 && I_MSTime() < waitstart + 5000) return; + + FString compose; + + compose.Format("Map %s: \"%s\",\nx = %1.4f, y = %1.4f, z = %1.4f, angle = %1.4f, pitch = %1.4f\n", + level.mapname, level.LevelName.GetChars(), FIXED2FLOAT(viewx), FIXED2FLOAT(viewy), FIXED2FLOAT(viewz), + ANGLE_TO_FLOAT(viewangle), ANGLE_TO_FLOAT(viewpitch)); + + AppendRenderStats(compose); + AppendRenderTimes(compose); + AppendLightStats(compose); + AppendMissingTextureStats(compose); + compose.AppendFormat("%d fps\n\n", screen->GetLastFPS()); + + FILE *f = fopen("benchmarks.txt", "at"); + if (f != NULL) + { + fputs(compose.GetChars(), f); + fclose(f); + } + Printf("Benchmark info saved\n"); + if (switchfps) vid_fps = false; + printstats = false; + } +} + +CCMD(bench) +{ + printstats = true; + if (vid_fps == 0) + { + vid_fps = 1; + waitstart = I_MSTime(); + switchfps = true; + } + else + { + if (ConsoleState == c_up) waitstart = I_MSTime(); + switchfps = false; + } + C_HideConsole (); +} \ No newline at end of file diff --git a/src/gl/utility/gl_clock.h b/src/gl/utility/gl_clock.h new file mode 100644 index 000000000..cb9d0173e --- /dev/null +++ b/src/gl/utility/gl_clock.h @@ -0,0 +1,122 @@ +#ifndef __GL_CLOCK_H +#define __GL_CLOCK_H + +#include "stats.h" +#include "x86.h" +#include "m_fixed.h" + +#ifdef _MSC_VER + +extern double gl_SecondsPerCycle; +extern double gl_MillisecPerCycle; + + +__forceinline long long GetClockCycle () +{ +#if _M_X64 + return __rdtsc(); +#else + return CPU.bRDTSC ? __rdtsc() : 0; +#endif +} + +#elif defined(__GNUG__) && defined(__i386__) + +extern double gl_SecondsPerCycle; +extern double gl_MillisecPerCycle; + +inline long long GetClockCycle() +{ + if (CPU.bRDTSC) + { + long long res; + asm volatile ("rdtsc" : "=A" (res)); + return res; + } + else + { + return 0; + } +} + +#else + +extern double gl_SecondsPerCycle; +extern double gl_MillisecPerCycle; + +inline long long GetClockCycle () +{ + return 0; +} +#endif + +#if defined (__APPLE__) + +typedef cycle_t glcycle_t; + +#else // !__APPLE__ + +class glcycle_t +{ +public: + glcycle_t &operator= (const glcycle_t &o) + { + Counter = o.Counter; + return *this; + } + + void Reset() + { + Counter = 0; + } + + __forceinline void Clock() + { + // Not using QueryPerformanceCounter directly, so we don't need + // to pull in the Windows headers for every single file that + // wants to do some profiling. + long long time = GetClockCycle(); + Counter -= time; + } + + __forceinline void Unclock() + { + long long time = GetClockCycle(); + Counter += time; + } + + double Time() + { + return double(Counter) * gl_SecondsPerCycle; + } + + double TimeMS() + { + return double(Counter) * gl_MillisecPerCycle; + } + +private: + long long Counter; +}; + +#endif // __APPLE__ + +extern glcycle_t RenderWall,SetupWall,ClipWall,SplitWall; +extern glcycle_t RenderFlat,SetupFlat; +extern glcycle_t RenderSprite,SetupSprite; +extern glcycle_t All, Finish, PortalAll, Bsp; +extern glcycle_t ProcessAll; +extern glcycle_t RenderAll; +extern glcycle_t Dirty; + +extern int iter_dlightf, iter_dlight, draw_dlight, draw_dlightf; +extern int rendered_lines,rendered_flats,rendered_sprites,rendered_decals,render_vertexsplit,render_texsplit; +extern int rendered_portals; + +extern int vertexcount, flatvertices, flatprimitives; + +void ResetProfilingData(); +void CheckBench(); + + +#endif \ No newline at end of file diff --git a/src/gl/utility/gl_convert.h b/src/gl/utility/gl_convert.h new file mode 100644 index 000000000..7f99412fa --- /dev/null +++ b/src/gl/utility/gl_convert.h @@ -0,0 +1,9 @@ + +#ifndef __GLC_CONVERT +#define __GLC_CONVERT + +#include "m_fixed.h" +#define ANGLE_TO_FLOAT(ang) ((float)((ang) * 180. / ANGLE_180)) +#define FLOAT_TO_ANGLE(ang) xs_RoundToUInt((ang) / 180. * ANGLE_180) + +#endif \ No newline at end of file diff --git a/src/gl/utility/gl_cycler.cpp b/src/gl/utility/gl_cycler.cpp new file mode 100644 index 000000000..a12cb4851 --- /dev/null +++ b/src/gl/utility/gl_cycler.cpp @@ -0,0 +1,155 @@ +/* +** gl_cycler.cpp +** Implements the cycler for dynamic lights and texture shaders. +** +**--------------------------------------------------------------------------- +** Copyright 2003 Timothy Stump +** Copyright 2006 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. +** +** 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 +#include "gl/utility/gl_cycler.h" + +//========================================================================== +// +// +// +//========================================================================== + +void FCycler::Serialize(FArchive & arc) +{ + arc << m_start << m_end << m_current + << m_time << m_cycle << m_increment << m_shouldCycle + << m_cycleType; +} + +//========================================================================== +// +// +// +//========================================================================== + +FCycler::FCycler() +{ + m_cycle = 0.f; + m_cycleType = CYCLE_Linear; + m_shouldCycle = false; + m_start = m_current = 0.f; + m_end = 0.f; + m_increment = true; +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FCycler::SetParams(float start, float end, float cycle) +{ + m_cycle = cycle; + m_time = 0.f; + m_start = m_current = start; + m_end = end; + m_increment = true; +} + + +//========================================================================== +// +// +// +//========================================================================== + +void FCycler::Update(float diff) +{ + float mult, angle; + float step = m_end - m_start; + + if (!m_shouldCycle) + { + return; + } + + m_time += diff; + if (m_time >= m_cycle) + { + m_time = m_cycle; + } + + mult = m_time / m_cycle; + + switch (m_cycleType) + { + case CYCLE_Linear: + if (m_increment) + { + m_current = m_start + (step * mult); + } + else + { + m_current = m_end - (step * mult); + } + break; + case CYCLE_Sin: + angle = float(M_PI * 2.f * mult); + mult = sinf(angle); + mult = (mult + 1.f) / 2.f; + m_current = m_start + (step * mult); + break; + case CYCLE_Cos: + angle = float(M_PI * 2.f * mult); + mult = cosf(angle); + mult = (mult + 1.f) / 2.f; + m_current = m_start + (step * mult); + break; + case CYCLE_SawTooth: + m_current = m_start + (step * mult); + break; + case CYCLE_Square: + if (m_increment) + { + m_current = m_start; + } + else + { + m_current = m_end; + } + break; + } + + if (m_time == m_cycle) + { + m_time = 0.f; + m_increment = !m_increment; + } +} + + diff --git a/src/gl/utility/gl_cycler.h b/src/gl/utility/gl_cycler.h new file mode 100644 index 000000000..8a6b55581 --- /dev/null +++ b/src/gl/utility/gl_cycler.h @@ -0,0 +1,52 @@ +#ifndef __GL_CYCLER_H +#define __GL_CYCLER_H + +#include "farchive.h" + +typedef enum +{ + CYCLE_Linear, + CYCLE_Sin, + CYCLE_Cos, + CYCLE_SawTooth, + CYCLE_Square +} CycleType; + +inline FArchive &operator<< (FArchive &arc, CycleType &type) +{ + BYTE val = (BYTE)type; + arc << val; + type = (CycleType)val; + return arc; +} + + +class FCycler +{ +public: + FCycler(); + void Update(float diff); + void SetParams(float start, float end, float cycle); + void ShouldCycle(bool sc) { m_shouldCycle = sc; } + void SetCycleType(CycleType ct) { m_cycleType = ct; } + float GetVal() { return m_current; } + + inline operator float () const { return m_current; } + + void Serialize(FArchive & arc); +protected: + float m_start, m_end, m_current; + float m_time, m_cycle; + bool m_increment, m_shouldCycle; + + CycleType m_cycleType; +}; + +inline FArchive &operator<< (FArchive &arc, FCycler &type) +{ + type.Serialize(arc); + return arc; +} + + +#endif diff --git a/src/gl/utility/gl_geometric.cpp b/src/gl/utility/gl_geometric.cpp new file mode 100644 index 000000000..5e3f93ba8 --- /dev/null +++ b/src/gl/utility/gl_geometric.cpp @@ -0,0 +1,258 @@ + + +#include +#include +#include "gl/utility/gl_geometric.h" + +static Vector axis[3] = +{ + Vector(1.0f, 0.0f, 0.0f), + Vector(0.0f, 1.0f, 0.0f), + Vector(0.0f, 0.0f, 1.0f) +}; + + + +Vector Vector::Cross(Vector &v) +{ + float x, y, z; + Vector cp; + + x = Y() * v.Z() - Z() * v.Y(); + y = Z() * v.X() - X() * v.Z(); + z = X() * v.Y() - Y() * v.X(); + + cp.Set(x, y, z); + + return cp; +} + + +Vector Vector::operator- (Vector &v) +{ + float x, y, z; + Vector vec; + + x = X() - v.X(); + y = Y() - v.Y(); + z = Z() - v.Z(); + + vec.Set(x, y, z); + + return vec; +} + + +Vector Vector::operator+ (Vector &v) +{ + float x, y, z; + Vector vec; + + x = X() + v.X(); + y = Y() + v.Y(); + z = Z() + v.Z(); + + vec.Set(x, y, z); + + return vec; +} + + +Vector Vector::operator* (float f) +{ + Vector vec(X(), Y(), Z()); + + vec.Scale(f); + + return vec; +} + + +Vector Vector::operator/ (float f) +{ + Vector vec(X(), Y(), Z()); + + vec.Scale(1.f / f); + + return vec; +} + + +bool Vector::operator== (Vector &v) +{ + return X() == v.X() && Y() == v.Y() && Z() == v.Z(); +} + + +void Vector::GetRightUp(Vector &right, Vector &up) +{ + Vector n(X(), Y(), Z()); + Vector fn(fabsf(n.X()), fabsf(n.Y()), fabsf(n.Z())); + int major = 0; + + if (fn[1] > fn[major]) major = 1; + if (fn[2] > fn[major]) major = 2; + + // build right vector by hand + if (fabsf(fn[0]-1.0f) < FLT_EPSILON || fabsf(fn[1]-1.0f) < FLT_EPSILON || fabsf(fn[2]-1.0f) < FLT_EPSILON) + { + if (major == 0 && n[0] > 0.f) + { + right.Set(0.f, 0.f, -1.f); + } + else if (major == 0) + { + right.Set(0.f, 0.f, 1.f); + } + + if (major == 1 || (major == 2 && n[2] > 0.f)) + { + right.Set(1.f, 0.f, 0.f); + } + + if (major == 2 && n[2] < 0.0f) + { + right.Set(-1.f, 0.f, 0.f); + } + } + else + { + right = axis[major].Cross(n); + } + + up = n.Cross(right); + right.Normalize(); + up.Normalize(); +} + + +void Vector::Scale(float scale) +{ + float x, y, z; + + x = X() * scale; + y = Y() * scale; + z = Z() * scale; + + Set(x, y, z); +} + + +Vector Vector::ProjectVector(Vector &a) +{ + Vector res, b; + + b.Set(X(), Y(), Z()); + res.Set(a.X(), a.Y(), a.Z()); + + res.Scale(a.Dot(b) / a.Dot(a)); + + return res; +} + + +Vector Vector::ProjectPlane(Vector &right, Vector &up) +{ + Vector src(X(), Y(), Z()); + Vector t1, t2; + + t1 = src.ProjectVector(right); + t2 = src.ProjectVector(up); + + return t1 + t2; +} + + + + + + +void Plane::Init(float *v1, float *v2, float *v3) +{ + Vector vec1, vec2, vec3; + + vec1.Set(v1); + vec2.Set(v2); + vec3.Set(v3); + +#ifdef _MSC_VER + m_normal = (vec2 - vec1).Cross(vec3 - vec1); +#else + Vector tmpVec = vec3 - vec1; + m_normal = (vec2 - vec1).Cross(tmpVec); +#endif + m_normal.Normalize(); + m_d = vec3.Dot(m_normal) * -1.f; +} + + +#define FNOTEQUAL(a, b) (fabsf(a - b) > 0.001f) +void Plane::Init(float *verts, int numVerts) +{ + float *v[3], *t; + int i, curVert; + + if (numVerts < 3) return; + + curVert = 1; + v[0] = verts + 0; + for (i = 1; i < numVerts; i++) + { + t = verts + (i * 3); + if (FNOTEQUAL(t[0], v[curVert - 1][0]) || FNOTEQUAL(t[1], v[curVert - 1][1]) || FNOTEQUAL(t[2], v[curVert - 1][2])) + { + v[curVert] = t; + curVert++; + } + if (curVert == 3) break; + } + + if (curVert != 3) + { + // degenerate triangle, no valid normal + return; + } + + Init(v[0], v[1], v[2]); +} + + +void Plane::Init(float a, float b, float c, float d) +{ + m_normal.Set(a, b, c); + m_d = d / m_normal.Length(); + m_normal.Normalize(); +} + + +void Plane::Set(secplane_t &plane) +{ + float a, b, c, d; + + a = FIXED2FLOAT(plane.a); + b = FIXED2FLOAT(plane.b); + c = FIXED2FLOAT(plane.c); + d = FIXED2FLOAT(plane.d); + + m_normal.Set(a, c, b); + //m_normal.Normalize(); the vector is already normalized + m_d = d; +} + + +float Plane::DistToPoint(float x, float y, float z) +{ + Vector p; + + p.Set(x, y, z); + + return m_normal.Dot(p) + m_d; +} + + +bool Plane::PointOnSide(float x, float y, float z) +{ + return DistToPoint(x, y, z) < 0.f; +} + + diff --git a/src/gl/utility/gl_geometric.h b/src/gl/utility/gl_geometric.h new file mode 100644 index 000000000..6868dab21 --- /dev/null +++ b/src/gl/utility/gl_geometric.h @@ -0,0 +1,251 @@ +#ifndef __GL_GEOM +#define __GL_GEOM + +#include "math.h" +#include "r_defs.h" + +class Vector +{ +public: + Vector() + { + SetX(0.f); + SetY(1.f); + SetZ(0.f); + m_length = 1.f; + } + + Vector(float x, float y, float z) + { + SetX(x); + SetY(y); + SetZ(z); + m_length=-1.0f; + } + + Vector(float *v) + { + SetX(v[0]); + SetY(v[1]); + SetZ(v[2]); + m_length=-1.0f; + } + + Vector(vertex_t * v) + { + SetX(v->fx); + SetY(v->fy); + SetZ(0); + } + + void Normalize() + { + float l = 1.f / Length(); + + SetX(X() * l); + SetY(Y() * l); + SetZ(Z() * l); + m_length=1.0f; + } + + void UpdateLength() + { + m_length = sqrtf((X() * X()) + (Y() * Y()) + (Z() * Z())); + } + + void Set(float *v) + { + SetX(v[0]); + SetY(v[1]); + SetZ(v[2]); + m_length=-1.0f; + } + + void Set(float x, float y, float z) + { + SetX(x); + SetY(y); + SetZ(z); + m_length=-1.0f; + } + + float Length() + { + if (m_length<0.0f) UpdateLength(); + return m_length; + } + + float Dist(Vector &v) + { + Vector t(X() - v.X(), Y() - v.Y(), Z() - v.Z()); + + return t.Length(); + } + + float Dot(Vector &v) + { + return (X() * v.X()) + (Y() * v.Y()) + (Z() * v.Z()); + } + + Vector Cross(Vector &v); + Vector operator- (Vector &v); + Vector operator+ (Vector &v); + Vector operator* (float f); + Vector operator/ (float f); + bool operator== (Vector &v); + bool operator!= (Vector &v) { return !((*this) == v); } + + void GetRightUp(Vector &up, Vector &right); + float operator[] (int index) const { return m_vec[index]; } + float &operator[] (int index) { return m_vec[index]; } + float X() const { return m_vec[0]; } + float Y() const { return m_vec[1]; } + float Z() const { return m_vec[2]; } + void SetX(float x) { m_vec[0] = x; } + void SetY(float y) { m_vec[1] = y; } + void SetZ(float z) { m_vec[2] = z; } + void Scale(float scale); + + Vector ProjectVector(Vector &a); + Vector ProjectPlane(Vector &right, Vector &up); +protected: + float m_vec[3]; + float m_length; +}; + + +class Plane +{ +public: + Plane() + { + m_normal.Set(0.f, 1.f, 0.f); + m_d = 0.f; + } + void Init(float *v1, float *v2, float *v3); + void Init(float a, float b, float c, float d); + void Init(float *verts, int numVerts); + void Set(secplane_t &plane); + float DistToPoint(float x, float y, float z); + bool PointOnSide(float x, float y, float z); + bool PointOnSide(Vector &v) { return PointOnSide(v.X(), v.Y(), v.Z()); } + bool ValidNormal() { return m_normal.Length() == 1.f; } + + float A() { return m_normal.X(); } + float B() { return m_normal.Y(); } + float C() { return m_normal.Z(); } + float D() { return m_d; } + + const Vector &Normal() const { return m_normal; } +protected: + Vector m_normal; + float m_d; +}; + +class Matrix3x4 // used like a 4x4 matrix with the last row always being (0,0,0,1) +{ + float m[3][4]; + +public: + + void MakeIdentity() + { + memset(m, 0, sizeof(m)); + m[0][0] = m[1][1] = m[2][2] = 1.f; + } + + void Translate(float x, float y, float z) + { + m[0][3] = m[0][0]*x + m[0][1]*y + m[0][2]*z + m[0][3]; + m[1][3] = m[1][0]*x + m[1][1]*y + m[1][2]*z + m[1][3]; + m[2][3] = m[2][0]*x + m[2][1]*y + m[2][2]*z + m[2][3]; + } + + void Scale(float x, float y, float z) + { + m[0][0] *=x; + m[1][0] *=x; + m[2][0] *=x; + + m[0][1] *=y; + m[1][1] *=y; + m[2][1] *=y; + + m[0][2] *=z; + m[1][2] *=z; + m[2][2] *=z; + } + + void Rotate(float ax, float ay, float az, float angle) + { + Matrix3x4 m1; + + Vector axis(ax, ay, az); + axis.Normalize(); + double c = cos(angle * M_PI/180.), s = sin(angle * M_PI/180.), t = 1 - c; + double sx = s*axis.X(), sy = s*axis.Y(), sz = s*axis.Z(); + double tx, ty, txx, tyy, u, v; + + tx = t*axis.X(); + m1.m[0][0] = float( (txx=tx*axis.X()) + c ); + m1.m[0][1] = float( (u=tx*axis.Y()) - sz); + m1.m[0][2] = float( (v=tx*axis.Z()) + sy); + + ty = t*axis.Y(); + m1.m[1][0] = float( u + sz); + m1.m[1][1] = float( (tyy=ty*axis.Y()) + c ); + m1.m[1][2] = float( (u=ty*axis.Z()) - sx); + + m1.m[2][0] = float( v - sy); + m1.m[2][1] = float( u + sx); + m1.m[2][2] = float( (t-txx-tyy) + c ); + + m1.m[0][3] = 0.f; + m1.m[1][3] = 0.f; + m1.m[2][3] = 0.f; + + *this = (*this) * m1; + } + + Matrix3x4 operator *(const Matrix3x4 &other) + { + Matrix3x4 result; + + result.m[0][0] = m[0][0]*other.m[0][0] + m[0][1]*other.m[1][0] + m[0][2]*other.m[2][0]; + result.m[0][1] = m[0][0]*other.m[0][1] + m[0][1]*other.m[1][1] + m[0][2]*other.m[2][1]; + result.m[0][2] = m[0][0]*other.m[0][2] + m[0][1]*other.m[1][2] + m[0][2]*other.m[2][2]; + result.m[0][3] = m[0][0]*other.m[0][3] + m[0][1]*other.m[1][3] + m[0][2]*other.m[2][3] + m[0][3]; + + result.m[1][0] = m[1][0]*other.m[0][0] + m[1][1]*other.m[1][0] + m[1][2]*other.m[2][0]; + result.m[1][1] = m[1][0]*other.m[0][1] + m[1][1]*other.m[1][1] + m[1][2]*other.m[2][1]; + result.m[1][2] = m[1][0]*other.m[0][2] + m[1][1]*other.m[1][2] + m[1][2]*other.m[2][2]; + result.m[1][3] = m[1][0]*other.m[0][3] + m[1][1]*other.m[1][3] + m[1][2]*other.m[2][3] + m[1][3]; + + result.m[2][0] = m[2][0]*other.m[0][0] + m[2][1]*other.m[1][0] + m[2][2]*other.m[2][0]; + result.m[2][1] = m[2][0]*other.m[0][1] + m[2][1]*other.m[1][1] + m[2][2]*other.m[2][1]; + result.m[2][2] = m[2][0]*other.m[0][2] + m[2][1]*other.m[1][2] + m[2][2]*other.m[2][2]; + result.m[2][3] = m[2][0]*other.m[0][3] + m[2][1]*other.m[1][3] + m[2][2]*other.m[2][3] + m[2][3]; + + return result; + } + + Vector operator *(const Vector &vec) + { + Vector result; + + result.SetX(vec.X()*m[0][0] + vec.Y()*m[0][1] + vec.Z()*m[0][2] + m[0][3]); + result.SetY(vec.X()*m[1][0] + vec.Y()*m[1][1] + vec.Z()*m[1][2] + m[1][3]); + result.SetZ(vec.X()*m[2][0] + vec.Y()*m[2][1] + vec.Z()*m[2][2] + m[2][3]); + return result; + } + + void MultiplyVector(float *f3 , float *f3o) + { + float x = f3[0] * m[0][0] + f3[1] * m[0][1] + f3[2] * m[0][2] + m[0][3]; + float y = f3[0] * m[1][0] + f3[1] * m[1][1] + f3[2] * m[1][2] + m[1][3]; + float z = f3[0] * m[2][0] + f3[1] * m[2][1] + f3[2] * m[2][2] + m[2][3]; + f3o[2] = z; f3o[1] = y; f3o[0] = x; + } +}; + +#endif diff --git a/src/gl/utility/gl_templates.h b/src/gl/utility/gl_templates.h new file mode 100644 index 000000000..1ebef1f48 --- /dev/null +++ b/src/gl/utility/gl_templates.h @@ -0,0 +1,82 @@ +#ifndef __GL_BASIC +#define __GL_BASIC + +#include +#include "stats.h" + + +// Disabled because it doesn't work and only accumulates large portions of blocked heap +// without providing any relevant performance boost. +template struct FreeList +{ + //T * freelist; + + T * GetNew() + { + /* + if (freelist) + { + T * n=freelist; + freelist=*((T**)n); + return new ((void*)n) T; + } + */ + return new T; + } + + void Release(T * node) + { + /* + node->~T(); + *((T**)node) = freelist; + freelist=node; + */ + delete node; + } + + ~FreeList() + { + /* + while (freelist!=NULL) + { + T * n = freelist; + freelist=*((T**)n); + delete n; + } + */ + } +}; + +template class UniqueList +{ + TArray Array; + FreeList TheFreeList; + +public: + + T * Get(T * t) + { + for(unsigned i=0;i #include +#include "version.h" #include "hardware.h" #include "i_video.h" #include "i_system.h" @@ -46,6 +47,7 @@ #include "v_text.h" #include "doomstat.h" #include "m_argv.h" +#include "sdlglvideo.h" #include "r_renderer.h" #include "r_swrenderer.h" @@ -55,6 +57,37 @@ EXTERN_CVAR (Float, vid_winscale) IVideo *Video; +extern int NewWidth, NewHeight, NewBits, DisplayBits; +bool V_DoModeSetup (int width, int height, int bits); +void I_RestartRenderer(); + +int currentrenderer; + +// [ZDoomGL] +CUSTOM_CVAR (Int, vid_renderer, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + // 0: Software renderer + // 1: OpenGL renderer + + if (self != currentrenderer) + { + switch (self) + { + case 0: + Printf("Switching to software renderer...\n"); + break; + case 1: + Printf("Switching to OpenGL renderer...\n"); + break; + default: + Printf("Unknown renderer (%d). Falling back to software renderer...\n", (int) vid_renderer); + self = 0; // make sure to actually switch to the software renderer + break; + } + Printf("You must restart " GAMENAME " to switch the renderer\n"); + } +} + void I_ShutdownGraphics () { if (screen) @@ -74,8 +107,11 @@ void I_InitGraphics () val.Bool = !!Args->CheckParm ("-devparm"); ticker.SetGenericRepDefault (val, CVAR_Bool); - - Video = new SDLVideo (0); + + //currentrenderer = vid_renderer; + if (currentrenderer==1) Video = new SDLGLVideo(0); + else Video = new SDLVideo (0); + if (Video == NULL) I_FatalError ("Failed to initialize display"); @@ -91,9 +127,11 @@ static void I_DeleteRenderer() void I_CreateRenderer() { + currentrenderer = vid_renderer; if (Renderer == NULL) { - Renderer = new FSoftwareRenderer; + if (currentrenderer==1) Renderer = gl_CreateInterface(); + else Renderer = new FSoftwareRenderer; atterm(I_DeleteRenderer); } } diff --git a/src/sdl/sdlglvideo.cpp b/src/sdl/sdlglvideo.cpp new file mode 100644 index 000000000..6378381e4 --- /dev/null +++ b/src/sdl/sdlglvideo.cpp @@ -0,0 +1,417 @@ + +// HEADER FILES ------------------------------------------------------------ + +#include + +#include "doomtype.h" + +#include "templates.h" +#include "i_system.h" +#include "i_video.h" +#include "v_video.h" +#include "v_pfx.h" +#include "stats.h" +#include "version.h" +#include "c_console.h" + +#include "sdlglvideo.h" +#include "gl/system/gl_system.h" +#include "r_defs.h" +#include "gl/gl_functions.h" +//#include "gl/gl_intern.h" + +#include "gl/renderer/gl_renderer.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/shaders/gl_shader.h" +#include "gl/utility/gl_templates.h" +#include "gl/textures/gl_material.h" +#include "gl/system/gl_cvars.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +IMPLEMENT_ABSTRACT_CLASS(SDLGLFB) + +struct MiniModeInfo +{ + WORD Width, Height; +}; + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern IVideo *Video; +// extern int vid_renderer; + +EXTERN_CVAR (Float, Gamma) +EXTERN_CVAR (Int, vid_displaybits) +EXTERN_CVAR (Int, vid_renderer) + + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +CUSTOM_CVAR(Int, gl_vid_multisample, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL ) +{ + Printf("This won't take effect until "GAMENAME" is restarted.\n"); +} + +RenderContext gl; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// Dummy screen sizes to pass when windowed +static MiniModeInfo WinModes[] = +{ + { 320, 200 }, + { 320, 240 }, + { 400, 225 }, // 16:9 + { 400, 300 }, + { 480, 270 }, // 16:9 + { 480, 360 }, + { 512, 288 }, // 16:9 + { 512, 384 }, + { 640, 360 }, // 16:9 + { 640, 400 }, + { 640, 480 }, + { 720, 480 }, // 16:10 + { 720, 540 }, + { 800, 450 }, // 16:9 + { 800, 500 }, // 16:10 + { 800, 600 }, + { 848, 480 }, // 16:9 + { 960, 600 }, // 16:10 + { 960, 720 }, + { 1024, 576 }, // 16:9 + { 1024, 600 }, // 17:10 + { 1024, 640 }, // 16:10 + { 1024, 768 }, + { 1088, 612 }, // 16:9 + { 1152, 648 }, // 16:9 + { 1152, 720 }, // 16:10 + { 1152, 864 }, + { 1280, 720 }, // 16:9 + { 1280, 800 }, // 16:10 + { 1280, 960 }, + { 1344, 756 }, // 16:9 + { 1360, 768 }, // 16:9 + { 1400, 787 }, // 16:9 + { 1400, 875 }, // 16:10 + { 1440, 900 }, + { 1400, 1050 }, + { 1600, 900 }, // 16:9 + { 1600, 1000 }, // 16:10 + { 1600, 1200 }, + { 1680, 1050 }, // 16:10 + { 1920, 1080 }, // 16:9 + { 1920, 1200 }, // 16:10 + { 2054, 1536 }, + { 2560, 1440 }, // 16:9 + { 2880, 1800 } // 16:10 +}; + +// CODE -------------------------------------------------------------------- + +SDLGLVideo::SDLGLVideo (int parm) +{ + IteratorBits = 0; + IteratorFS = false; + if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) { + fprintf( stderr, "Video initialization failed: %s\n", + SDL_GetError( ) ); + } + GetContext(gl); +#ifndef _WIN32 + // mouse cursor is visible by default on linux systems, we disable it by default + SDL_ShowCursor (0); +#endif +} + +SDLGLVideo::~SDLGLVideo () +{ + if (GLRenderer != NULL) GLRenderer->FlushTextures(); +} + +void SDLGLVideo::StartModeIterator (int bits, bool fs) +{ + IteratorMode = 0; + IteratorBits = bits; + IteratorFS = fs; +} + +bool SDLGLVideo::NextMode (int *width, int *height, bool *letterbox) +{ + if (IteratorBits != 8) + return false; + + if (!IteratorFS) + { + if ((unsigned)IteratorMode < sizeof(WinModes)/sizeof(WinModes[0])) + { + *width = WinModes[IteratorMode].Width; + *height = WinModes[IteratorMode].Height; + ++IteratorMode; + return true; + } + } + else + { + SDL_Rect **modes = SDL_ListModes (NULL, SDL_FULLSCREEN|SDL_HWSURFACE); + if (modes != NULL && modes[IteratorMode] != NULL) + { + *width = modes[IteratorMode]->w; + *height = modes[IteratorMode]->h; + ++IteratorMode; + return true; + } + } + return false; +} + +DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old) +{ + static int retry = 0; + static int owidth, oheight; + + PalEntry flashColor; +// int flashAmount; + + if (old != NULL) + { // Reuse the old framebuffer if its attributes are the same + SDLGLFB *fb = static_cast (old); + if (fb->Width == width && + fb->Height == height) + { + bool fsnow = (fb->Screen->flags & SDL_FULLSCREEN) != 0; + + if (fsnow != fullscreen) + { + SDL_WM_ToggleFullScreen (fb->Screen); + } + return old; + } +// old->GetFlash (flashColor, flashAmount); + delete old; + } + else + { + flashColor = 0; +// flashAmount = 0; + } + + SDLGLFB *fb = new OpenGLFrameBuffer (0, width, height, 32, 60, fullscreen); + retry = 0; + + // If we could not create the framebuffer, try again with slightly + // different parameters in this order: + // 1. Try with the closest size + // 2. Try in the opposite screen mode with the original size + // 3. Try in the opposite screen mode with the closest size + // This is a somewhat confusing mass of recursion here. + + while (fb == NULL || !fb->IsValid ()) + { + if (fb != NULL) + { + delete fb; + } + + switch (retry) + { + case 0: + owidth = width; + oheight = height; + case 2: + // Try a different resolution. Hopefully that will work. + I_ClosestResolution (&width, &height, 8); + break; + + case 1: + // Try changing fullscreen mode. Maybe that will work. + width = owidth; + height = oheight; + fullscreen = !fullscreen; + break; + + default: + // I give up! + I_FatalError ("Could not create new screen (%d x %d)", owidth, oheight); + + fprintf( stderr, "!!! [SDLGLVideo::CreateFrameBuffer] Got beyond I_FatalError !!!" ); + return NULL; //[C] actually this shouldn't be reached; probably should be replaced with an ASSERT + } + + ++retry; + fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); + } + +// fb->SetFlash (flashColor, flashAmount); + return fb; +} + +void SDLGLVideo::SetWindowedScale (float scale) +{ +} + +bool SDLGLVideo::SetResolution (int width, int height, int bits) +{ + // FIXME: Is it possible to do this without completely destroying the old + // interface? +#ifndef NO_GL + + if (GLRenderer != NULL) GLRenderer->FlushTextures(); + I_ShutdownGraphics(); + + Video = new SDLGLVideo(0); + if (Video == NULL) I_FatalError ("Failed to initialize display"); + +#if (defined(WINDOWS)) || defined(WIN32) + bits=32; +#else + bits=24; +#endif + + V_DoModeSetup(width, height, bits); +#endif + return true; // We must return true because the old video context no longer exists. +} + +// FrameBuffer implementation ----------------------------------------------- + +SDLGLFB::SDLGLFB (void *, int width, int height, int, int, bool fullscreen) + : DFrameBuffer (width, height) +{ + static int localmultisample=-1; + + if (localmultisample<0) localmultisample=gl_vid_multisample; + + int i; + + m_Lock=0; + + UpdatePending = false; + + if (!gl.InitHardware(false, gl_vid_compatibility, localmultisample)) + { + vid_renderer = 0; + return; + } + + // Mac OS X version will crash when entering fullscreen mode with BPP <= 8 + // Also it may crash with BPP == 16 on some configurations + // It seems 24 and 32 bits are safe values + // So value of vid_displaybits is ignored and hardcoded constant is used instead + + Screen = SDL_SetVideoMode (width, height, +#if defined(__APPLE__) + 32, +#else // ! __APPLE__ + vid_displaybits, +#endif // __APPLE__ + SDL_HWSURFACE|SDL_HWPALETTE|SDL_OPENGL | SDL_GL_DOUBLEBUFFER|SDL_ANYFORMAT| + (fullscreen ? SDL_FULLSCREEN : 0)); + + if (Screen == NULL) + return; + + m_supportsGamma = -1 != SDL_GetGammaRamp(m_origGamma[0], m_origGamma[1], m_origGamma[2]); + +#if defined(__APPLE__) + // Need to set title here because a window is not created yet when calling the same function from main() + SDL_WM_SetCaption( GAMESIG " " DOTVERSIONSTR " (" __DATE__ ")", NULL ); +#endif // __APPLE__ +} + +SDLGLFB::~SDLGLFB () +{ + if (m_supportsGamma) + { + SDL_SetGammaRamp(m_origGamma[0], m_origGamma[1], m_origGamma[2]); + } +} + +void SDLGLFB::InitializeState() +{ + int value = 0; + SDL_GL_GetAttribute( SDL_GL_STENCIL_SIZE, &value ); + if (!value) + { + Printf("Failed to use stencil buffer!\n"); //[C] is it needed to recreate buffer in "cheapest mode"? + gl.flags|=RFL_NOSTENCIL; + } +} + +bool SDLGLFB::CanUpdate () +{ + if (m_Lock != 1) + { + if (m_Lock > 0) + { + UpdatePending = true; + --m_Lock; + } + return false; + } + return true; +} + +void SDLGLFB::SetGammaTable(WORD *tbl) +{ + SDL_SetGammaRamp(&tbl[0], &tbl[256], &tbl[512]); +} + +bool SDLGLFB::Lock(bool buffered) +{ + m_Lock++; + Buffer = MemBuffer; + return true; +} + +bool SDLGLFB::Lock () +{ + return Lock(false); +} + +void SDLGLFB::Unlock () +{ + if (UpdatePending && m_Lock == 1) + { + Update (); + } + else if (--m_Lock <= 0) + { + m_Lock = 0; + } +} + +bool SDLGLFB::IsLocked () +{ + return m_Lock>0;// true; +} + +bool SDLGLFB::IsFullscreen () +{ + return (Screen->flags & SDL_FULLSCREEN) != 0; +} + + +bool SDLGLFB::IsValid () +{ + return DFrameBuffer::IsValid() && Screen != NULL; +} + +void SDLGLFB::SetVSync( bool vsync ) +{ +#if defined (__APPLE__) + const GLint value = vsync ? 1 : 0; + CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, &value ); +#endif +} + +void SDLGLFB::NewRefreshRate () +{ +} + diff --git a/src/sdl/sdlglvideo.h b/src/sdl/sdlglvideo.h new file mode 100644 index 000000000..35175e421 --- /dev/null +++ b/src/sdl/sdlglvideo.h @@ -0,0 +1,78 @@ +#ifndef __SDLGLVIDEO_H__ +#define __SDLGLVIDEO_H__ + +#include "hardware.h" +#include "v_video.h" +#include +#include "gl/system/gl_system.h" + +EXTERN_CVAR (Float, dimamount) +EXTERN_CVAR (Color, dimcolor) + +struct FRenderer; +FRenderer *gl_CreateInterface(); + +class SDLGLVideo : public IVideo +{ + public: + SDLGLVideo (int parm); + ~SDLGLVideo (); + + EDisplayType GetDisplayType () { return DISPLAY_Both; } + void SetWindowedScale (float scale); + + DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); + + void StartModeIterator (int bits, bool fs); + bool NextMode (int *width, int *height, bool *letterbox); + bool SetResolution (int width, int height, int bits); + +private: + int IteratorMode; + int IteratorBits; + bool IteratorFS; +}; +class SDLGLFB : public DFrameBuffer +{ + DECLARE_CLASS(SDLGLFB, DFrameBuffer) +public: + // this must have the same parameters as the Windows version, even if they are not used! + SDLGLFB (void *hMonitor, int width, int height, int, int, bool fullscreen); + ~SDLGLFB (); + + void ForceBuffering (bool force); + bool Lock(bool buffered); + bool Lock (); + void Unlock(); + bool IsLocked (); + + bool IsValid (); + bool IsFullscreen (); + + virtual void SetVSync( bool vsync ); + + void NewRefreshRate (); + + friend class SDLGLVideo; + +//[C] + int GetTrueHeight() { return GetHeight();} + +protected: + bool CanUpdate(); + void SetGammaTable(WORD *tbl); + void InitializeState(); + + SDLGLFB () {} + BYTE GammaTable[3][256]; + bool UpdatePending; + + SDL_Surface *Screen; + + void UpdateColors (); + + int m_Lock; + Uint16 m_origGamma[3][256]; + bool m_supportsGamma; +}; +#endif diff --git a/src/textures/textures.h b/src/textures/textures.h index 100ba31e9..e4e3dc92d 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -3,6 +3,27 @@ #include "doomtype.h" +struct FloatRect +{ + float left,top; + float width,height; + + + void Offset(float xofs,float yofs) + { + left+=xofs; + top+=yofs; + } + void Scale(float xfac,float yfac) + { + left*=xfac; + width*=xfac; + top*=yfac; + height*=yfac; + } +}; + + class FBitmap; struct FRemapTable; struct FCopyInfo; @@ -13,6 +34,8 @@ class FArchive; // Texture IDs class FTextureManager; class FTerrainTypeArray; +class FGLTexture; +class FMaterial; class FTextureID { @@ -295,6 +318,9 @@ protected: CopySize(other); bNoDecals = other->bNoDecals; Rotations = other->Rotations; + gl_info = other->gl_info; + gl_info.Brightmap = NULL; + gl_info.areas = NULL; } public: @@ -304,8 +330,52 @@ public: static void FlipNonSquareBlockRemap (BYTE *blockto, const BYTE *blockfrom, int x, int y, int srcpitch, const BYTE *remap); friend class D3DTex; -}; +public: + + struct MiscGLInfo + { + FMaterial *Material; + FGLTexture *SystemTexture; + FTexture *Brightmap; + FTexture *DecalTexture; // This is needed for decals of UseType TEX_MiscPatch- + PalEntry GlowColor; + PalEntry FloorSkyColor; + PalEntry CeilingSkyColor; + int GlowHeight; + FloatRect *areas; + int areacount; + int shaderindex; + float shaderspeed; + int mIsTransparent:2; + bool bGlowing:1; // Texture glows + bool bFullbright:1; // always draw fullbright + bool bSkybox:1; // This is a skybox + bool bSkyColorDone:1; // Fill color for sky + char bBrightmapChecked:1; // Set to 1 if brightmap has been checked + bool bBrightmap:1; // This is a brightmap + bool bBrightmapDisablesFullbright:1; // This disables fullbright display + bool bNoFilter:1; + bool bNoCompress:1; + bool mExpanded:1; + + MiscGLInfo() throw (); + ~MiscGLInfo(); + }; + MiscGLInfo gl_info; + + virtual void PrecacheGL(); + virtual void UncacheGL(); + void GetGlowColor(float *data); + PalEntry GetSkyCapColor(bool bottom); + bool isGlowing() { return gl_info.bGlowing; } + bool isFullbright() { return gl_info.bFullbright; } + void CreateDefaultBrightmap(); + bool FindHoles(const unsigned char * buffer, int w, int h); + static bool SmoothEdges(unsigned char * buffer,int w, int h); + void CheckTrans(unsigned char * buffer, int size, int trans); + bool ProcessData(unsigned char * buffer, int w, int h, bool ispatch); +}; // Texture manager class FTextureManager diff --git a/src/win32/hardware.cpp b/src/win32/hardware.cpp index 6b3f40f1c..cd009c206 100644 --- a/src/win32/hardware.cpp +++ b/src/win32/hardware.cpp @@ -56,12 +56,48 @@ EXTERN_CVAR (Float, vid_winscale) CVAR(Int, win_x, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Int, win_y, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -extern HWND Window; +#include "win32gliface.h" bool ForceWindowed; IVideo *Video; + +void I_RestartRenderer(); +int currentrenderer = -1; +bool changerenderer; + +// [ZDoomGL] +CUSTOM_CVAR (Int, vid_renderer, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + // 0: Software renderer + // 1: OpenGL renderer + + if (self != currentrenderer) + { + switch (self) + { + case 0: + Printf("Switching to software renderer...\n"); + break; + case 1: + Printf("Switching to OpenGL renderer...\n"); + break; + default: + Printf("Unknown renderer (%d). Falling back to software renderer...\n", *vid_renderer); + self = 0; // make sure to actually switch to the software renderer + break; + } + //changerenderer = true; + Printf("You must restart "GAMENAME" to switch the renderer\n"); + } +} + +CCMD (vid_restart) +{ +} + + void I_ShutdownGraphics () { if (screen) @@ -94,15 +130,18 @@ void I_InitGraphics () // not receive a WM_ACTIVATEAPP message, so both games think they // are the active app. Huh? } - val.Bool = !!Args->CheckParm ("-devparm"); ticker.SetGenericRepDefault (val, CVAR_Bool); - Video = new Win32Video (0); + + //currentrenderer = vid_renderer; + if (currentrenderer==1) Video = new Win32GLVideo(0); + else Video = new Win32Video (0); + if (Video == NULL) I_FatalError ("Failed to initialize display"); - + atterm (I_ShutdownGraphics); - + Video->SetWindowedScale (vid_winscale); } @@ -113,9 +152,11 @@ static void I_DeleteRenderer() void I_CreateRenderer() { + currentrenderer = vid_renderer; if (Renderer == NULL) { - Renderer = new FSoftwareRenderer; + if (currentrenderer==1) Renderer = gl_CreateInterface(); + else Renderer = new FSoftwareRenderer; atterm(I_DeleteRenderer); } } @@ -148,12 +189,12 @@ DFrameBuffer *I_SetMode (int &width, int &height, DFrameBuffer *old) } DFrameBuffer *res = Video->CreateFrameBuffer (width, height, fs, old); - /* Right now, CreateFrameBuffer cannot return NULL + //* Right now, CreateFrameBuffer cannot return NULL if (res == NULL) { I_FatalError ("Mode %dx%d is unavailable\n", width, height); } - */ + //*/ return res; } @@ -332,7 +373,7 @@ CUSTOM_CVAR (Float, vid_winscale, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) NewWidth = screen->GetWidth(); NewHeight = screen->GetHeight(); NewBits = DisplayBits; - setmodeneeded = true; + //setmodeneeded = true; // This CVAR doesn't do anything and only causes problems! } } diff --git a/src/win32/win32gliface.cpp b/src/win32/win32gliface.cpp new file mode 100644 index 000000000..ebe9e10c6 --- /dev/null +++ b/src/win32/win32gliface.cpp @@ -0,0 +1,564 @@ +#include "gl/system/gl_system.h" + +#include "win32iface.h" +#include "win32gliface.h" +//#include "gl/gl_intern.h" +#include "templates.h" +#include "version.h" +#include "c_console.h" +#include "hardware.h" +#include "v_video.h" +#include "i_input.h" +#include "i_system.h" +#include "doomstat.h" +#include "v_text.h" +//#include "gl_defs.h" + +#include "gl/renderer/gl_renderer.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/shaders/gl_shader.h" +#include "gl/utility/gl_templates.h" + +void gl_CalculateCPUSpeed(); +extern int NewWidth, NewHeight, NewBits, DisplayBits; + +CUSTOM_CVAR(Int, gl_vid_multisample, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL ) +{ + Printf("This won't take effect until "GAMENAME" is restarted.\n"); +} + +RenderContext gl; + + +EXTERN_CVAR(Bool, gl_vid_compatibility) +EXTERN_CVAR(Int, vid_refreshrate) + +Win32GLVideo::Win32GLVideo(int parm) : m_Modes(NULL), m_IsFullscreen(false) +{ + #ifdef _WIN32 + if (CPU.bRDTSC) gl_CalculateCPUSpeed(); + #endif + I_SetWndProc(); + m_DisplayWidth = vid_defwidth; + m_DisplayHeight = vid_defheight; + m_DisplayBits = gl_vid_compatibility? 16:32; + m_DisplayHz = 60; + + GetDisplayDeviceName(); + MakeModesList(); + GetContext(gl); + +} + +Win32GLVideo::~Win32GLVideo() +{ + FreeModes(); + if (GLRenderer != NULL) GLRenderer->FlushTextures(); +} + +void Win32GLVideo::SetWindowedScale(float scale) +{ +} + +struct MonitorEnumState +{ + int curIdx; + HMONITOR hFoundMonitor; +}; + +static BOOL CALLBACK GetDisplayDeviceNameMonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData) +{ + MonitorEnumState *state = reinterpret_cast(dwData); + + MONITORINFOEX mi; + mi.cbSize = sizeof mi; + GetMonitorInfo(hMonitor, &mi); + + // This assumes the monitors are returned by EnumDisplayMonitors in the + // order they're found in the Direct3D9 adapters list. Fingers crossed... + if (state->curIdx == vid_adapter) + { + state->hFoundMonitor = hMonitor; + + // Don't stop enumeration; this makes EnumDisplayMonitors fail. I like + // proper fails. + } + + ++state->curIdx; + + return TRUE; +} + +void Win32GLVideo::GetDisplayDeviceName() +{ + // If anything goes wrong, anything at all, everything uses the primary + // monitor. + m_DisplayDeviceName = 0; + m_hMonitor = 0; + + MonitorEnumState mes; + + mes.curIdx = 1; + mes.hFoundMonitor = 0; + + // Could also use EnumDisplayDevices, I guess. That might work. + if (EnumDisplayMonitors(0, 0, &GetDisplayDeviceNameMonitorEnumProc, LPARAM(&mes))) + { + if (mes.hFoundMonitor) + { + MONITORINFOEX mi; + + mi.cbSize = sizeof mi; + + if (GetMonitorInfo(mes.hFoundMonitor, &mi)) + { + strcpy(m_DisplayDeviceBuffer, mi.szDevice); + m_DisplayDeviceName = m_DisplayDeviceBuffer; + + m_hMonitor = mes.hFoundMonitor; + } + } + } +} + +void Win32GLVideo::MakeModesList() +{ + ModeInfo *pMode, *nextmode; + DEVMODE dm; + int mode = 0; + + memset(&dm, 0, sizeof(DEVMODE)); + dm.dmSize = sizeof(DEVMODE); + + while (EnumDisplaySettings(m_DisplayDeviceName, mode, &dm)) + { + this->AddMode(dm.dmPelsWidth, dm.dmPelsHeight, dm.dmBitsPerPel, dm.dmPelsHeight, dm.dmDisplayFrequency); + ++mode; + } + + for (pMode = m_Modes; pMode != NULL; pMode = nextmode) + { + nextmode = pMode->next; + if (pMode->realheight == pMode->height && pMode->height * 4/3 == pMode->width) + { + if (pMode->width >= 360) + { + AddMode (pMode->width, pMode->width * 9/16, pMode->bits, pMode->height, pMode->refreshHz); + } + if (pMode->width > 640) + { + AddMode (pMode->width, pMode->width * 10/16, pMode->bits, pMode->height, pMode->refreshHz); + } + } + } +} + +void Win32GLVideo::StartModeIterator(int bits, bool fs) +{ + m_IteratorMode = m_Modes; + // I think it's better to ignore the game-side settings of bit depth. + // The GL renderer will always default to 32 bits, except in compatibility mode + m_IteratorBits = gl_vid_compatibility? 16:32; + m_IteratorFS = fs; +} + +bool Win32GLVideo::NextMode(int *width, int *height, bool *letterbox) +{ + if (m_IteratorMode) + { + while (m_IteratorMode && m_IteratorMode->bits != m_IteratorBits) + { + m_IteratorMode = m_IteratorMode->next; + } + + if (m_IteratorMode) + { + *width = m_IteratorMode->width; + *height = m_IteratorMode->height; + if (letterbox != NULL) *letterbox = m_IteratorMode->realheight != m_IteratorMode->height; + m_IteratorMode = m_IteratorMode->next; + return true; + } + } + + return false; +} + +void Win32GLVideo::AddMode(int x, int y, int bits, int baseHeight, int refreshHz) +{ + ModeInfo **probep = &m_Modes; + ModeInfo *probe = m_Modes; + + // This mode may have been already added to the list because it is + // enumerated multiple times at different refresh rates. If it's + // not present, add it to the right spot in the list; otherwise, do nothing. + // Modes are sorted first by width, then by height, then by depth. In each + // case the order is ascending. + for (; probe != 0; probep = &probe->next, probe = probe->next) + { + if (probe->width != x) continue; + // Width is equal + if (probe->height != y) continue; + // Width is equal + if (probe->realheight != baseHeight) continue; + // Height is equal + if (probe->bits != bits) continue; + // Bits is equal + if (probe->refreshHz > refreshHz) continue; + probe->refreshHz = refreshHz; + return; + } + + *probep = new ModeInfo (x, y, bits, baseHeight, refreshHz); + (*probep)->next = probe; +} + +void Win32GLVideo::FreeModes() +{ + ModeInfo *mode = m_Modes; + + while (mode) + { + ModeInfo *tempmode = mode; + mode = mode->next; + delete tempmode; + } + + m_Modes = NULL; +} + +bool Win32GLVideo::GoFullscreen(bool yes) +{ + m_IsFullscreen = yes; + + m_trueHeight = m_DisplayHeight; + + if (yes) + { + // If in windowed mode, any height is good. + for (ModeInfo *mode = m_Modes; mode != NULL; mode = mode->next) + { + if (mode->width == m_DisplayWidth && mode->height == m_DisplayHeight) + { + m_trueHeight = mode->realheight; + break; + } + } + } + + if (yes) + { + gl.SetFullscreen(m_DisplayDeviceName, m_DisplayWidth, m_trueHeight, m_DisplayBits, m_DisplayHz); + } + else + { + gl.SetFullscreen(m_DisplayDeviceName, 0,0,0,0); + } + return yes; +} + + +DFrameBuffer *Win32GLVideo::CreateFrameBuffer(int width, int height, bool fs, DFrameBuffer *old) +{ + Win32GLFrameBuffer *fb; + + m_DisplayWidth = width; + m_DisplayHeight = height; + m_DisplayBits = gl_vid_compatibility? 16:32; + m_DisplayHz = 60; + + if (vid_refreshrate == 0) + { + for (ModeInfo *mode = m_Modes; mode != NULL; mode = mode->next) + { + if (mode->width == m_DisplayWidth && mode->height == m_DisplayHeight && mode->bits == m_DisplayBits) + { + m_DisplayHz = MAX(m_DisplayHz, mode->refreshHz); + } + } + } + else + { + m_DisplayHz = vid_refreshrate; + } + + if (old != NULL) + { // Reuse the old framebuffer if its attributes are the same + fb = static_cast (old); + if (fb->m_Width == m_DisplayWidth && + fb->m_Height == m_DisplayHeight && + fb->m_Bits == m_DisplayBits && + fb->m_RefreshHz == m_DisplayHz && + fb->m_Fullscreen == fs) + { + return old; + } + //old->GetFlash(flashColor, flashAmount); + delete old; + } + + fb = new OpenGLFrameBuffer(m_hMonitor, m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz, fs); + + return fb; +} + +bool Win32GLVideo::SetResolution (int width, int height, int bits) +{ + if (GLRenderer != NULL) GLRenderer->FlushTextures(); + I_ShutdownGraphics(); + + Video = new Win32GLVideo(0); + if (Video == NULL) I_FatalError ("Failed to initialize display"); + + bits=32; + + V_DoModeSetup(width, height, bits); + return true; // We must return true because the old video context no longer exists. +} + +struct DumpAdaptersState +{ + unsigned index; + char *displayDeviceName; +}; + +static BOOL CALLBACK DumpAdaptersMonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData) +{ + DumpAdaptersState *state = reinterpret_cast(dwData); + + MONITORINFOEX mi; + mi.cbSize=sizeof mi; + + char moreinfo[64] = ""; + + bool active = true; + + if (GetMonitorInfo(hMonitor, &mi)) + { + bool primary = !!(mi.dwFlags & MONITORINFOF_PRIMARY); + + mysnprintf(moreinfo, countof(moreinfo), " [%ldx%ld @ (%ld,%ld)]%s", + mi.rcMonitor.right - mi.rcMonitor.left, + mi.rcMonitor.bottom - mi.rcMonitor.top, + mi.rcMonitor.left, mi.rcMonitor.top, + primary ? " (Primary)" : ""); + + if (!state->displayDeviceName && !primary) + active = false;//primary selected, but this ain't primary + else if (state->displayDeviceName && strcmp(state->displayDeviceName, mi.szDevice) != 0) + active = false;//this isn't the selected one + } + + Printf("%s%u. %s\n", + active ? TEXTCOLOR_BOLD : "", + state->index, + moreinfo); + + ++state->index; + + return TRUE; +} + +void Win32GLVideo::DumpAdapters() +{ + DumpAdaptersState das; + + das.index = 1; + das.displayDeviceName = m_DisplayDeviceName; + + EnumDisplayMonitors(0, 0, DumpAdaptersMonitorEnumProc, LPARAM(&das)); +} + +IMPLEMENT_ABSTRACT_CLASS(Win32GLFrameBuffer) + +Win32GLFrameBuffer::Win32GLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen) : BaseWinFB(width, height) +{ + static int localmultisample=-1; + + if (localmultisample<0) localmultisample=gl_vid_multisample; + + m_Width = width; + m_Height = height; + m_Bits = bits; + m_RefreshHz = refreshHz; + m_Fullscreen = fullscreen; + m_Lock=0; + + RECT r; + LONG style, exStyle; + + static_cast(Video)->GoFullscreen(fullscreen); + + m_displayDeviceName = 0; + int monX = 0, monY = 0; + + if (hMonitor) + { + MONITORINFOEX mi; + mi.cbSize = sizeof mi; + + if (GetMonitorInfo(HMONITOR(hMonitor), &mi)) + { + strcpy(m_displayDeviceNameBuffer, mi.szDevice); + m_displayDeviceName = m_displayDeviceNameBuffer; + + monX = int(mi.rcMonitor.left); + monY = int(mi.rcMonitor.top); + } + } + + ShowWindow (Window, SW_SHOW); + GetWindowRect(Window, &r); + style = WS_VISIBLE | WS_CLIPSIBLINGS; + exStyle = 0; + + if (fullscreen) + style |= WS_POPUP; + else + { + style |= WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX; + exStyle |= WS_EX_WINDOWEDGE; + } + + SetWindowLong(Window, GWL_STYLE, style); + SetWindowLong(Window, GWL_EXSTYLE, exStyle); + SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + + if (fullscreen) + { + MoveWindow(Window, monX, monY, width, static_cast(Video)->GetTrueHeight(), FALSE); + + // And now, seriously, it IS in the right place. Promise. + } + else + { + MoveWindow(Window, r.left, r.top, width + (GetSystemMetrics(SM_CXSIZEFRAME) * 2), height + (GetSystemMetrics(SM_CYSIZEFRAME) * 2) + GetSystemMetrics(SM_CYCAPTION), FALSE); + + I_RestoreWindowedPos(); + } + + if (!gl.InitHardware(Window, false, gl_vid_compatibility, localmultisample)) + { + vid_renderer = 0; + return; + } + + HDC hDC = GetDC(Window); + m_supportsGamma = !!GetDeviceGammaRamp(hDC, (void *)m_origGamma); + ReleaseDC(Window, hDC); +} + +Win32GLFrameBuffer::~Win32GLFrameBuffer() +{ + if (m_supportsGamma) + { + HDC hDC = GetDC(Window); + SetDeviceGammaRamp(hDC, (void *)m_origGamma); + ReleaseDC(Window, hDC); + } + I_SaveWindowedPos(); + + gl.SetFullscreen(m_displayDeviceName, 0,0,0,0); + + ShowWindow (Window, SW_SHOW); + SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW); + SetWindowLong(Window, GWL_EXSTYLE, WS_EX_WINDOWEDGE); + SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); + I_GetEvent(); + + gl.Shutdown(); +} + + +void Win32GLFrameBuffer::InitializeState() +{ +} + +bool Win32GLFrameBuffer::CanUpdate() +{ + if (!AppActive) return false; + return true; +} + +void Win32GLFrameBuffer::SetGammaTable(WORD *tbl) +{ + HDC hDC = GetDC(Window); + SetDeviceGammaRamp(hDC, (void *)tbl); + ReleaseDC(Window, hDC); +} + +bool Win32GLFrameBuffer::Lock(bool buffered) +{ + m_Lock++; + Buffer = MemBuffer; + return true; +} + +bool Win32GLFrameBuffer::Lock () +{ + return Lock(false); +} + +void Win32GLFrameBuffer::Unlock () +{ + m_Lock--; +} + +bool Win32GLFrameBuffer::IsLocked () +{ + return m_Lock>0;// true; +} + +bool Win32GLFrameBuffer::IsFullscreen() +{ + return m_Fullscreen; +} + +void Win32GLFrameBuffer::PaletteChanged() +{ +} + +int Win32GLFrameBuffer::QueryNewPalette() +{ + return 0; +} + +HRESULT Win32GLFrameBuffer::GetHR() +{ + return 0; +} + +void Win32GLFrameBuffer::Blank () +{ +} + +bool Win32GLFrameBuffer::PaintToWindow () +{ + return false; +} + +bool Win32GLFrameBuffer::CreateResources () +{ + return false; +} + +void Win32GLFrameBuffer::ReleaseResources () +{ +} + +void Win32GLFrameBuffer::SetVSync (bool vsync) +{ + if (gl.SetVSync!=NULL) gl.SetVSync(vsync); +} + +void Win32GLFrameBuffer::NewRefreshRate () +{ + if (m_Fullscreen) + { + setmodeneeded = true; + NewWidth = screen->GetWidth(); + NewHeight = screen->GetHeight(); + NewBits = DisplayBits; + } +} + diff --git a/src/win32/win32gliface.h b/src/win32/win32gliface.h new file mode 100644 index 000000000..ba4ea001d --- /dev/null +++ b/src/win32/win32gliface.h @@ -0,0 +1,147 @@ +#ifndef __WIN32GLIFACE_H__ +#define __WIN32GLIFACE_H__ + +/* +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include "gl/glext.h" +#include "gl/wglext.h" +*/ + +#include "win32iface.h" +#include "hardware.h" +#include "v_video.h" +#include "tarray.h" + +extern IVideo *Video; + + +extern BOOL AppActive; + +EXTERN_CVAR (Float, dimamount) +EXTERN_CVAR (Color, dimcolor) + +EXTERN_CVAR(Int, vid_defwidth); +EXTERN_CVAR(Int, vid_defheight); +EXTERN_CVAR(Int, vid_renderer); +EXTERN_CVAR(Int, vid_adapter); + +extern HINSTANCE g_hInst; +extern HWND Window; +extern IVideo *Video; + +struct FRenderer; +FRenderer *gl_CreateInterface(); + + +class Win32GLVideo : public IVideo +{ +public: + Win32GLVideo(int parm); + virtual ~Win32GLVideo(); + + EDisplayType GetDisplayType () { return DISPLAY_Both; } + void SetWindowedScale (float scale); + void StartModeIterator (int bits, bool fs); + bool NextMode (int *width, int *height, bool *letterbox); + bool GoFullscreen(bool yes); + DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); + virtual bool SetResolution (int width, int height, int bits); + void DumpAdapters(); + +protected: + struct ModeInfo + { + ModeInfo (int inX, int inY, int inBits, int inRealY, int inRefresh) + : next (NULL), + width (inX), + height (inY), + bits (inBits), + refreshHz (inRefresh), + realheight (inRealY) + {} + ModeInfo *next; + int width, height, bits, refreshHz, realheight; + } *m_Modes; + + ModeInfo *m_IteratorMode; + int m_IteratorBits; + bool m_IteratorFS; + bool m_IsFullscreen; + int m_trueHeight; + int m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz; + HMODULE hmRender; + + char m_DisplayDeviceBuffer[CCHDEVICENAME]; + char *m_DisplayDeviceName; + HMONITOR m_hMonitor; + + void GetDisplayDeviceName(); + void MakeModesList(); + void AddMode(int x, int y, int bits, int baseHeight, int refreshHz); + void FreeModes(); +public: + int GetTrueHeight() { return m_trueHeight; } + +}; + + + +class Win32GLFrameBuffer : public BaseWinFB +{ + DECLARE_CLASS(Win32GLFrameBuffer, BaseWinFB) + +public: + Win32GLFrameBuffer() {} + // Actually, hMonitor is a HMONITOR, but it's passed as a void * as there + // look to be some cross-platform bits in the way. + Win32GLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen); + virtual ~Win32GLFrameBuffer(); + + // unused but must be defined + virtual void Blank (); + virtual bool PaintToWindow (); + virtual HRESULT GetHR(); + + virtual bool CreateResources (); + virtual void ReleaseResources (); + + void SetVSync (bool vsync); + void NewRefreshRate (); + + + int GetTrueHeight() { return static_cast(Video)->GetTrueHeight(); } + + bool Lock(bool buffered); + bool Lock (); + void Unlock(); + bool IsLocked (); + + + bool IsFullscreen(); + void PaletteChanged(); + int QueryNewPalette(); + + void InitializeState(); + +protected: + + bool CanUpdate(); + void SetGammaTable(WORD * tbl); + + float m_Gamma, m_Brightness, m_Contrast; + WORD m_origGamma[768]; + BOOL m_supportsGamma; + bool m_Fullscreen; + int m_Width, m_Height, m_Bits, m_RefreshHz; + int m_Lock; + char m_displayDeviceNameBuffer[CCHDEVICENAME]; + char *m_displayDeviceName; + + friend class Win32GLVideo; + +}; + +#endif //__WIN32GLIFACE_H__ diff --git a/src/win32/zdoom.rc b/src/win32/zdoom.rc index e28277e3a..5e2226c6d 100644 --- a/src/win32/zdoom.rc +++ b/src/win32/zdoom.rc @@ -72,13 +72,13 @@ BEGIN " BEGIN\r\n" " VALUE ""Comments"", ""Thanks to id Software for creating DOOM and then releasing the source code. Thanks also to TeamTNT for creating BOOM, which ZDoom is partially based on. Includes code based on the Cajun Bot 0.97 by Martin Collberg.""\r\n" " VALUE ""CompanyName"", "" ""\r\n" - " VALUE ""FileDescription"", ""ZDoom""\r\n" + " VALUE ""FileDescription"", ""GZDoom""\r\n" " VALUE ""FileVersion"", RC_FILEVERSION2\r\n" - " VALUE ""InternalName"", ""ZDoom""\r\n" + " VALUE ""InternalName"", ""GZDoom""\r\n" " VALUE ""LegalCopyright"", ""Copyright \\u00A9 1993-1996 id Software, 1998-2010 Randy Heit, 2002-2010 Christoph Oelckers, et al.""\r\n" " VALUE ""LegalTrademarks"", ""DoomR is a Registered Trademark of id Software, Inc.""\r\n" - " VALUE ""OriginalFilename"", ""zdoom.exe""\r\n" - " VALUE ""ProductName"", ""ZDoom""\r\n" + " VALUE ""OriginalFilename"", ""gzdoom.exe""\r\n" + " VALUE ""ProductName"", ""GZDoom""\r\n" " VALUE ""ProductVersion"", RC_PRODUCTVERSION2\r\n" " END\r\n" " END\r\n" @@ -86,7 +86,7 @@ BEGIN " BEGIN\r\n" " VALUE ""Translation"", 0x409, 1200\r\n" " END\r\n" - "EN\0" + "END\r\n" END #endif // APSTUDIO_INVOKED @@ -464,7 +464,7 @@ IDB_DEADGUY BITMAP "deadguy.bmp" // Generated from the TEXTINCLUDE 3 resource. // #ifndef NO_MANIFEST - CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "zdoom.exe.manifest" +// CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "zdoom.exe.manifest" #endif ///////////////////////////////////////////////////////////////////////////// @@ -492,13 +492,13 @@ BEGIN BEGIN VALUE "Comments", "Thanks to id Software for creating DOOM and then releasing the source code. Thanks also to TeamTNT for creating BOOM, which ZDoom is partially based on. Includes code based on the Cajun Bot 0.97 by Martin Collberg." VALUE "CompanyName", " " - VALUE "FileDescription", "ZDoom" + VALUE "FileDescription", "GZDoom" VALUE "FileVersion", RC_FILEVERSION2 - VALUE "InternalName", "ZDoom" + VALUE "InternalName", "GZDoom" VALUE "LegalCopyright", "Copyright \u00A9 1993-1996 id Software, 1998-2010 Randy Heit, 2002-2010 Christoph Oelckers, et al." VALUE "LegalTrademarks", "DoomR is a Registered Trademark of id Software, Inc." - VALUE "OriginalFilename", "zdoom.exe" - VALUE "ProductName", "ZDoom" + VALUE "OriginalFilename", "gzdoom.exe" + VALUE "ProductName", "GZDoom" VALUE "ProductVersion", RC_PRODUCTVERSION2 END END