Merge branch 'master' into edf-gl

This commit is contained in:
Christoph Oelckers 2016-01-10 10:31:00 +01:00
commit 76b0971067
688 changed files with 82349 additions and 2381 deletions

4
.gitignore vendored
View file

@ -7,6 +7,7 @@
/Release
/wadsrc_wad
*.user
/build
/debug
/release
*/debug
@ -37,3 +38,6 @@
/jpeg-6b/x64/
/lzma/x64/
/zlib/x64/
/build_vc2013_64bit
/build_vc2015
build_cmake

View file

@ -1,5 +1,5 @@
cmake_minimum_required( VERSION 2.4 )
project(ZDoom)
project(GZDoom)
if( COMMAND cmake_policy )
if( POLICY CMP0011 )
@ -78,7 +78,7 @@ IF( NOT CMAKE_BUILD_TYPE )
ENDIF( NOT CMAKE_BUILD_TYPE )
set( ZDOOM_OUTPUT_DIR ${CMAKE_BINARY_DIR} CACHE PATH "Directory where zdoom.pk3 and the executable will be created." )
set( ZDOOM_EXE_NAME "zdoom" CACHE FILEPATH "Name of the executable to create." )
set( ZDOOM_EXE_NAME "gzdoom" CACHE FILEPATH "Name of the executable to create" )
if( MSVC )
# Allow the user to use ZDOOM_OUTPUT_DIR as a single release point.
# Use zdoom, zdoomd, zdoom64, and zdoomd64 for the binary names
@ -113,7 +113,7 @@ find_package( BZip2 )
find_package( JPEG )
find_package( ZLIB )
# GME
find_path( GME_INCLUDE_DIR gme.h )
find_path( GME_INCLUDE_DIR gme/gme.h )
find_library( GME_LIBRARIES gme )
mark_as_advanced( GME_INCLUDE_DIR GME_LIBRARIES )
FIND_PACKAGE_HANDLE_STANDARD_ARGS( GME
@ -128,7 +128,8 @@ if( MSVC )
# String pooling
# Function-level linking
# Disable run-time type information
set( ALL_C_FLAGS "/GF /Gy /GR-" )
# Set floating point model to fast or the GL render will suffer for it.
set( ALL_C_FLAGS "/GF /Gy /GR- /fp:fast" )
if( CMAKE_SIZEOF_VOID_P MATCHES "4")
# SSE2 option (mostly to switch it off in VC2012 and later where it's the default
@ -250,6 +251,8 @@ add_subdirectory( tools )
add_subdirectory( dumb )
add_subdirectory( gdtoa )
add_subdirectory( wadsrc )
add_subdirectory( wadsrc_bm )
add_subdirectory( wadsrc_lights )
add_subdirectory( src )
if( NOT WIN32 AND NOT APPLE )

View file

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Version="9,00"
Name="bzip2"
ProjectGUID="{A7DE5C73-D623-4118-A48A-BDFD1FAE97D4}"
RootNamespace="bzip2"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform

View file

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Version="9,00"
Name="game-music-emu"
ProjectGUID="{9B465A9E-E5C7-4577-B559-3CA2F7AE7D96}"
RootNamespace="gamemusicemu"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform
@ -326,6 +327,8 @@
/>
<Tool
Name="VCLinkerTool"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
/>
<Tool
Name="VCALinkTool"
@ -345,9 +348,6 @@
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
@ -402,6 +402,8 @@
/>
<Tool
Name="VCLinkerTool"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="17"
/>
<Tool
@ -422,9 +424,6 @@
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
@ -602,10 +601,6 @@
RelativePath=".\gme\Spc_Emu.cpp"
>
</File>
<File
RelativePath=".\gme\Spc_Filter.cpp"
>
</File>
<File
RelativePath=".\gme\Vgm_Emu.cpp"
>
@ -708,10 +703,6 @@
RelativePath=".\gme\Gme_File.h"
>
</File>
<File
RelativePath=".\gme\gme_types.h"
>
</File>
<File
RelativePath=".\gme\Gym_Emu.h"
>
@ -832,10 +823,6 @@
RelativePath=".\gme\Spc_Emu.h"
>
</File>
<File
RelativePath=".\gme\Spc_Filter.h"
>
</File>
<File
RelativePath=".\gme\Vgm_Emu.h"
>

View file

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Version="9,00"
Name="gdtoa"
ProjectGUID="{B68E0ABF-B627-48A3-A92F-D8F827A75054}"
RootNamespace="gdtoa"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform

View file

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Version="9,00"
Name="jpeg-6b"
ProjectGUID="{AC3F5340-40CB-4C3A-8AA7-CB7158DB4466}"
RootNamespace="jpeg6b"
Keyword="Win32Proj"
TargetFrameworkVersion="131072"
>
<Platforms>
<Platform

View file

@ -9,6 +9,7 @@ include( CheckFunctionExists )
include( CheckCXXCompilerFlag )
include( CheckLibraryExists )
include( FindPkgConfig )
include( FindOpenGL )
if( NOT APPLE )
option( NO_ASM "Disable assembly code" OFF )
@ -213,6 +214,18 @@ else( WIN32 )
endif( FPU_CONTROL_DIR )
endif( WIN32 )
if( X64 )
set( NO_ASM ON )
endif( X64 )
# Check if we have OpenGL
if( NOT OPENGL_FOUND )
message( FATAL_ERROR "OpenGL is required for building." )
endif( NOT OPENGL_FOUND )
set( ZDOOM_LIBS ${ZDOOM_LIBS} ${OPENGL_LIBRARIES} )
include_directories( ${OPENGL_INCLUDE_DIR} )
if( NOT NO_OPENAL )
find_package( OpenAL )
@ -437,6 +450,22 @@ else( SSE_MATTERS )
set( BACKPATCH 0 )
endif( SSE_MATTERS )
if( X64 )
set( HAVE_MMX 1 )
else( X64 )
set( SAFE_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} )
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmmx")
endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
CHECK_CXX_SOURCE_COMPILES("#include <mmintrin.h>
int main(void) { __m64 v = _m_from_int(0); }"
HAVE_MMX)
set( CMAKE_CXX_FLAGS ${SAFE_CMAKE_CXX_FLAGS} )
endif( X64 )
# Set up flags for GCC
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
@ -587,6 +616,7 @@ set( PLAT_WIN32_SOURCES
win32/i_movie.cpp
win32/i_system.cpp
win32/st_start.cpp
win32/win32gliface.cpp
win32/win32video.cpp )
set( PLAT_POSIX_SOURCES
posix/i_cd.cpp
@ -602,7 +632,8 @@ set( PLAT_SDL_SOURCES
posix/sdl/i_joystick.cpp
posix/sdl/i_main.cpp
posix/sdl/i_timer.cpp
posix/sdl/sdlvideo.cpp )
posix/sdl/sdlvideo.cpp
posix/sdl/sdlglvideo.cpp )
set( PLAT_OSX_SOURCES
posix/osx/iwadpicker_cocoa.mm
posix/osx/zdoom.icns )
@ -650,6 +681,25 @@ else( WIN32 )
set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} )
endif( WIN32 )
if( HAVE_MMX )
add_definitions( -DHAVE_MMX=1 )
set( SYSTEM_SOURCES ${SYSTEM_SOURCES}
gl/hqnx_asm/hq2x_asm.cpp
gl/hqnx_asm/hq3x_asm.cpp
gl/hqnx_asm/hq4x_asm.cpp
gl/hqnx_asm/hqnx_asm_Image.cpp)
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
set_source_files_properties(
gl/hqnx_asm/hq2x_asm.cpp
gl/hqnx_asm/hq3x_asm.cpp
gl/hqnx_asm/hq4x_asm.cpp
gl/textures/gl_hqresize.cpp
PROPERTIES COMPILE_FLAGS "-mmmx" )
endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
endif( HAVE_MMX )
if( NOT ASM_SOURCES )
set( ASM_SOURCES "" )
endif( NOT ASM_SOURCES )
@ -740,6 +790,20 @@ file( GLOB HEADER_FILES
textures/*.h
thingdef/*.h
xlat/*.h
gl/*.h
gl/api/*.h
gl/data/*.h
gl/dynlights/*.h
gl/hqnx/*.h
gl/hqnx_asm/*.h
gl/models/*.h
gl/renderer/*.h
gl/scene/*.h
gl/stereo3d/*.h
gl/shaders/*.h
gl/system/*.h
gl/textures/*.h
gl/utility/*.h
*.h
)
@ -1056,6 +1120,68 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE
menu/playermenu.cpp
menu/readthis.cpp
menu/videomenu.cpp
gl/data/gl_data.cpp
gl/data/gl_portaldata.cpp
gl/data/gl_setup.cpp
gl/data/gl_matrix.cpp
gl/data/gl_vertexbuffer.cpp
gl/dynlights/a_dynlight.cpp
gl/utility/gl_clock.cpp
gl/utility/gl_cycler.cpp
gl/utility/gl_geometric.cpp
gl/renderer/gl_renderer.cpp
gl/renderer/gl_renderstate.cpp
gl/renderer/gl_lightdata.cpp
gl/hqnx/init.cpp
gl/hqnx/hq2x.cpp
gl/hqnx/hq3x.cpp
gl/hqnx/hq4x.cpp
gl/textures/gl_hwtexture.cpp
gl/textures/gl_texture.cpp
gl/textures/gl_material.cpp
gl/textures/gl_hirestex.cpp
gl/textures/gl_bitmap.cpp
gl/textures/gl_samplers.cpp
gl/textures/gl_translate.cpp
gl/textures/gl_hqresize.cpp
gl/textures/gl_skyboxtexture.cpp
gl/scene/gl_bsp.cpp
gl/scene/gl_fakeflat.cpp
gl/scene/gl_clipper.cpp
gl/scene/gl_decal.cpp
gl/scene/gl_drawinfo.cpp
gl/scene/gl_flats.cpp
gl/scene/gl_walls.cpp
gl/scene/gl_sprite.cpp
gl/scene/gl_skydome.cpp
gl/scene/gl_renderhacks.cpp
gl/scene/gl_weapon.cpp
gl/scene/gl_scene.cpp
gl/scene/gl_sky.cpp
gl/scene/gl_portal.cpp
gl/scene/gl_walls_draw.cpp
gl/scene/gl_vertex.cpp
gl/scene/gl_spritelight.cpp
gl/stereo3d/gl_stereo3d.cpp
gl/stereo3d/gl_stereo_cvars.cpp
gl/stereo3d/gl_stereo_leftright.cpp
gl/stereo3d/scoped_view_shifter.cpp
gl/stereo3d/gl_anaglyph.cpp
gl/dynlights/gl_dynlight.cpp
gl/dynlights/gl_glow.cpp
gl/dynlights/gl_dynlight1.cpp
gl/dynlights/gl_lightbuffer.cpp
gl/shaders/gl_shader.cpp
gl/shaders/gl_texshader.cpp
gl/system/gl_interface.cpp
gl/system/gl_framebuffer.cpp
gl/system/gl_menu.cpp
gl/system/gl_wipe.cpp
gl/system/gl_load.c
gl/models/gl_models_md3.cpp
gl/models/gl_models_md2.cpp
gl/models/gl_models.cpp
gl/models/gl_voxels.cpp
oplsynth/fmopl.cpp
oplsynth/mlopl.cpp
oplsynth/mlopl_io.cpp
@ -1292,6 +1418,19 @@ source_group("Games\\Raven Shared" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_D
source_group("Games\\Strife Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_strife/.+")
source_group("Intermission" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/intermission/.+")
source_group("Menu" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/menu/.+")
source_group("OpenGL Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/.+")
source_group("OpenGL Renderer\\Data" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/data/.+")
source_group("OpenGL Renderer\\Dynamic Lights" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/dynlights/.+")
source_group("OpenGL Renderer\\HQ Resize" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/hqnx/.+")
source_group("OpenGL Renderer\\HQ Resize Assembly version" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/hqnx_asm/.+")
source_group("OpenGL Renderer\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/models/.+")
source_group("OpenGL Renderer\\Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/renderer/.+")
source_group("OpenGL Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/scene/.+")
source_group("OpenGL Renderer\\Stereo3D" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/stereo3d/.+")
source_group("OpenGL Renderer\\Shaders" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/shaders/.+")
source_group("OpenGL Renderer\\System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/system/.+")
source_group("OpenGL Renderer\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/textures/.+")
source_group("OpenGL Renderer\\Utilities" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/utility/.+")
source_group("Render Core\\Render Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_.+\\.h$")
source_group("Render Core\\Render Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_.+\\.cpp$")
source_group("Render Data\\Resource Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_data/.+\\.h$")

View file

@ -1099,6 +1099,14 @@ public:
}
bool HasSpecialDeathStates () const;
// begin of GZDoom specific additions
TArray<TObjPtr<AActor> > dynamiclights;
void * lightassociations;
bool hasmodel;
// end of GZDoom specific additions
size_t PropagateMark();
};
class FActorIterator

View file

@ -1449,12 +1449,14 @@ void G_AirControlChanged ()
//
//
//==========================================================================
void gl_SerializeGlobals(FArchive &arc);
void G_SerializeLevel (FArchive &arc, bool hubLoad)
{
int i = level.totaltime;
Renderer->StartSerialize(arc);
gl_SerializeGlobals(arc);
arc << level.flags
<< level.flags2

539
src/gl/data/gl_data.cpp Normal file
View file

@ -0,0 +1,539 @@
/*
** 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/system/gl_interface.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();
int ofslumpno = Wads.GetLumpFile(lump);
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 or the file this lump originated from.
if (lumpnum >= 0 && lumpnum < Wads.GetNumLumps())
{
int wadno = Wads.GetLumpFile(lumpnum);
if (wadno==FWadCollection::IWAD_FILENUM || wadno == ofslumpno)
{
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;
FSectorTagIterator itr(arg0);
while ((secnum = itr.Next()) >= 0)
{
sector_t * s = &sectors[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;
brightfog = false;
lightmode = -1;
nocoloredspritelighting = -1;
notexturefill = -1;
skyrotatevector = FVector3(0,0,1);
skyrotatevector2 = FVector3(0,0,1);
pixelstretch = 1.2f;
}
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;
newopt->pixelstretch = pixelstretch;
return newopt;
}
int fogdensity;
int outsidefogdensity;
int skyfog;
int lightmode;
int brightfog;
SBYTE nocoloredspritelighting;
SBYTE notexturefill;
FVector3 skyrotatevector;
FVector3 skyrotatevector2;
float pixelstretch;
};
DEFINE_MAP_OPTION(fogdensity, false)
{
FGLROptions *opt = info->GetOptData<FGLROptions>("gl_renderer");
parse.ParseAssign();
parse.sc.MustGetNumber();
opt->fogdensity = parse.sc.Number;
}
DEFINE_MAP_OPTION(brightfog, false)
{
FGLROptions *opt = info->GetOptData<FGLROptions>("gl_renderer");
parse.ParseAssign();
parse.sc.MustGetNumber();
opt->brightfog = parse.sc.Number;
}
DEFINE_MAP_OPTION(outsidefogdensity, false)
{
FGLROptions *opt = info->GetOptData<FGLROptions>("gl_renderer");
parse.ParseAssign();
parse.sc.MustGetNumber();
opt->outsidefogdensity = parse.sc.Number;
}
DEFINE_MAP_OPTION(skyfog, false)
{
FGLROptions *opt = info->GetOptData<FGLROptions>("gl_renderer");
parse.ParseAssign();
parse.sc.MustGetNumber();
opt->skyfog = parse.sc.Number;
}
DEFINE_MAP_OPTION(lightmode, false)
{
FGLROptions *opt = info->GetOptData<FGLROptions>("gl_renderer");
parse.ParseAssign();
parse.sc.MustGetNumber();
opt->lightmode = BYTE(parse.sc.Number);
}
DEFINE_MAP_OPTION(nocoloredspritelighting, false)
{
FGLROptions *opt = info->GetOptData<FGLROptions>("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<FGLROptions>("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<FGLROptions>("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<FGLROptions>("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();
}
DEFINE_MAP_OPTION(pixelratio, false)
{
FGLROptions *opt = info->GetOptData<FGLROptions>("gl_renderer");
parse.ParseAssign();
parse.sc.MustGetFloat();
opt->pixelstretch = (float)parse.sc.Float;
}
bool IsLightmodeValid()
{
return (glset.map_lightmode >= 0 && glset.map_lightmode <= 4) || glset.map_lightmode == 8;
}
void InitGLRMapinfoData()
{
FGLROptions *opt = level.info->GetOptData<FGLROptions>("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;
glset.pixelstretch = opt->pixelstretch;
}
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);
glset.pixelstretch = 1.2f;
}
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<<rot));
return sprframe->Texture[rot];
}
}
//==========================================================================
//
// Recalculate all heights affectting this vertex.
//
//==========================================================================
void gl_RecalcVertexHeights(vertex_t * v)
{
int i,j,k;
float height;
v->numheights=0;
for(i=0;i<v->numsectors;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;k<v->numheights;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;
AdjustSpriteOffsets();
}
//==========================================================================
//
// dumpgeometry
//
//==========================================================================
CCMD(dumpgeometry)
{
for(int i=0;i<numsectors;i++)
{
sector_t * sector = &sectors[i];
Printf(PRINT_LOG, "Sector %d\n",i);
for(int j=0;j<sector->subsectorcount;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;k<sub->numlines;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");
}
}
}
}

66
src/gl/data/gl_data.h Normal file
View file

@ -0,0 +1,66 @@
#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;
float pixelstretch;
};
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;
}
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<FPortal *> portals;
extern TArray<BYTE> currentmapsection;
void gl_InitPortals();
void gl_BuildPortalCoverage(FPortalCoverage *coverage, subsector_t *subsector, FPortal *portal);
void gl_InitData();
extern long gl_frameMS;
#endif

495
src/gl/data/gl_matrix.cpp Normal file
View file

@ -0,0 +1,495 @@
/* --------------------------------------------------
Lighthouse3D
VSMatrix - Very Simple Matrix Library
http://www.lighthouse3d.com/very-simple-libs
This is a simplified version of VSMatrix that has been adjusted for GZDoom's needs.
----------------------------------------------------*/
#include "gl/system/gl_system.h"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "doomtype.h"
#include "gl/data/gl_matrix.h"
static inline FLOATTYPE
DegToRad(FLOATTYPE degrees)
{
return (FLOATTYPE)(degrees * (M_PI / 180.0f));
};
// sets the square matrix mat to the identity matrix,
// size refers to the number of rows (or columns)
void
VSMatrix::setIdentityMatrix( FLOATTYPE *mat, int size) {
// fill matrix with 0s
for (int i = 0; i < size * size; ++i)
mat[i] = 0.0f;
// fill diagonal with 1s
for (int i = 0; i < size; ++i)
mat[i + i * size] = 1.0f;
}
// gl LoadIdentity implementation
void
VSMatrix::loadIdentity()
{
// fill matrix with 0s
for (int i = 0; i < 16; ++i)
mMatrix[i] = 0.0f;
// fill diagonal with 1s
for (int i = 0; i < 4; ++i)
mMatrix[i + i * 4] = 1.0f;
}
// gl MultMatrix implementation
void
VSMatrix::multMatrix(const FLOATTYPE *aMatrix)
{
FLOATTYPE res[16];
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
res[j*4 + i] = 0.0f;
for (int k = 0; k < 4; ++k)
{
res[j*4 + i] += mMatrix[k*4 + i] * aMatrix[j*4 + k];
}
}
}
memcpy(mMatrix, res, 16 * sizeof(FLOATTYPE));
}
#ifdef USE_DOUBLE
// gl MultMatrix implementation
void
VSMatrix::multMatrix(const float *aMatrix)
{
FLOATTYPE res[16];
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
res[j * 4 + i] = 0.0f;
for (int k = 0; k < 4; ++k)
{
res[j*4 + i] += mMatrix[k*4 + i] * aMatrix[j*4 + k];
}
}
}
memcpy(mMatrix, res, 16 * sizeof(FLOATTYPE));
}
#endif
// gl LoadMatrix implementation
void
VSMatrix::loadMatrix(const FLOATTYPE *aMatrix)
{
memcpy(mMatrix, aMatrix, 16 * sizeof(FLOATTYPE));
}
#ifdef USE_DOUBLE
// gl LoadMatrix implementation
void
VSMatrix::loadMatrix(const float *aMatrix)
{
for (int i = 0; i < 16; ++i)
{
mMatrix[i] = aMatrix[i];
}
}
#endif
// gl Translate implementation with matrix selection
void
VSMatrix::translate(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z)
{
FLOATTYPE mat[16];
setIdentityMatrix(mat);
mat[12] = x;
mat[13] = y;
mat[14] = z;
multMatrix(mat);
}
// gl Scale implementation with matrix selection
void
VSMatrix::scale(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z)
{
FLOATTYPE mat[16];
setIdentityMatrix(mat,4);
mat[0] = x;
mat[5] = y;
mat[10] = z;
multMatrix(mat);
}
// gl Rotate implementation with matrix selection
void
VSMatrix::rotate(FLOATTYPE angle, FLOATTYPE x, FLOATTYPE y, FLOATTYPE z)
{
FLOATTYPE mat[16];
FLOATTYPE v[3];
v[0] = x;
v[1] = y;
v[2] = z;
FLOATTYPE radAngle = DegToRad(angle);
FLOATTYPE co = cos(radAngle);
FLOATTYPE si = sin(radAngle);
normalize(v);
FLOATTYPE x2 = v[0]*v[0];
FLOATTYPE y2 = v[1]*v[1];
FLOATTYPE z2 = v[2]*v[2];
// mat[0] = x2 + (y2 + z2) * co;
mat[0] = co + x2 * (1 - co);// + (y2 + z2) * co;
mat[4] = v[0] * v[1] * (1 - co) - v[2] * si;
mat[8] = v[0] * v[2] * (1 - co) + v[1] * si;
mat[12]= 0.0f;
mat[1] = v[0] * v[1] * (1 - co) + v[2] * si;
// mat[5] = y2 + (x2 + z2) * co;
mat[5] = co + y2 * (1 - co);
mat[9] = v[1] * v[2] * (1 - co) - v[0] * si;
mat[13]= 0.0f;
mat[2] = v[0] * v[2] * (1 - co) - v[1] * si;
mat[6] = v[1] * v[2] * (1 - co) + v[0] * si;
// mat[10]= z2 + (x2 + y2) * co;
mat[10]= co + z2 * (1 - co);
mat[14]= 0.0f;
mat[3] = 0.0f;
mat[7] = 0.0f;
mat[11]= 0.0f;
mat[15]= 1.0f;
multMatrix(mat);
}
// gluLookAt implementation
void
VSMatrix::lookAt(FLOATTYPE xPos, FLOATTYPE yPos, FLOATTYPE zPos,
FLOATTYPE xLook, FLOATTYPE yLook, FLOATTYPE zLook,
FLOATTYPE xUp, FLOATTYPE yUp, FLOATTYPE zUp)
{
FLOATTYPE dir[3], right[3], up[3];
up[0] = xUp; up[1] = yUp; up[2] = zUp;
dir[0] = (xLook - xPos);
dir[1] = (yLook - yPos);
dir[2] = (zLook - zPos);
normalize(dir);
crossProduct(dir,up,right);
normalize(right);
crossProduct(right,dir,up);
normalize(up);
FLOATTYPE m1[16],m2[16];
m1[0] = right[0];
m1[4] = right[1];
m1[8] = right[2];
m1[12] = 0.0f;
m1[1] = up[0];
m1[5] = up[1];
m1[9] = up[2];
m1[13] = 0.0f;
m1[2] = -dir[0];
m1[6] = -dir[1];
m1[10] = -dir[2];
m1[14] = 0.0f;
m1[3] = 0.0f;
m1[7] = 0.0f;
m1[11] = 0.0f;
m1[15] = 1.0f;
setIdentityMatrix(m2,4);
m2[12] = -xPos;
m2[13] = -yPos;
m2[14] = -zPos;
multMatrix(m1);
multMatrix(m2);
}
// gluPerspective implementation
void
VSMatrix::perspective(FLOATTYPE fov, FLOATTYPE ratio, FLOATTYPE nearp, FLOATTYPE farp)
{
FLOATTYPE f = 1.0f / tan (fov * (M_PI / 360.0f));
loadIdentity();
mMatrix[0] = f / ratio;
mMatrix[1 * 4 + 1] = f;
mMatrix[2 * 4 + 2] = (farp + nearp) / (nearp - farp);
mMatrix[3 * 4 + 2] = (2.0f * farp * nearp) / (nearp - farp);
mMatrix[2 * 4 + 3] = -1.0f;
mMatrix[3 * 4 + 3] = 0.0f;
}
// gl Ortho implementation
void
VSMatrix::ortho(FLOATTYPE left, FLOATTYPE right,
FLOATTYPE bottom, FLOATTYPE top,
FLOATTYPE nearp, FLOATTYPE farp)
{
loadIdentity();
mMatrix[0 * 4 + 0] = 2 / (right - left);
mMatrix[1 * 4 + 1] = 2 / (top - bottom);
mMatrix[2 * 4 + 2] = -2 / (farp - nearp);
mMatrix[3 * 4 + 0] = -(right + left) / (right - left);
mMatrix[3 * 4 + 1] = -(top + bottom) / (top - bottom);
mMatrix[3 * 4 + 2] = -(farp + nearp) / (farp - nearp);
}
// gl Frustum implementation
void
VSMatrix::frustum(FLOATTYPE left, FLOATTYPE right,
FLOATTYPE bottom, FLOATTYPE top,
FLOATTYPE nearp, FLOATTYPE farp)
{
FLOATTYPE m[16];
setIdentityMatrix(m,4);
m[0 * 4 + 0] = 2 * nearp / (right-left);
m[1 * 4 + 1] = 2 * nearp / (top - bottom);
m[2 * 4 + 0] = (right + left) / (right - left);
m[2 * 4 + 1] = (top + bottom) / (top - bottom);
m[2 * 4 + 2] = - (farp + nearp) / (farp - nearp);
m[2 * 4 + 3] = -1.0f;
m[3 * 4 + 2] = - 2 * farp * nearp / (farp-nearp);
m[3 * 4 + 3] = 0.0f;
multMatrix(m);
}
/*
// returns a pointer to the requested matrix
FLOATTYPE *
VSMatrix::get(MatrixTypes aType)
{
return mMatrix[aType];
}
*/
/* -----------------------------------------------------
SEND MATRICES TO OPENGL
------------------------------------------------------*/
// universal
void
VSMatrix::matrixToGL(int loc)
{
#ifdef USE_DOUBLE
float copyto[16];
copy(copyto);
glUniformMatrix4fv(loc, 1, false, copyto);
#else
glUniformMatrix4fv(loc, 1, false, mMatrix);
#endif
}
// -----------------------------------------------------
// AUX functions
// -----------------------------------------------------
// Compute res = M * point
void
VSMatrix::multMatrixPoint(const FLOATTYPE *point, FLOATTYPE *res)
{
for (int i = 0; i < 4; ++i)
{
res[i] = 0.0f;
for (int j = 0; j < 4; j++) {
res[i] += point[j] * mMatrix[j*4 + i];
}
}
}
// res = a cross b;
void
VSMatrix::crossProduct(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res) {
res[0] = a[1] * b[2] - b[1] * a[2];
res[1] = a[2] * b[0] - b[2] * a[0];
res[2] = a[0] * b[1] - b[0] * a[1];
}
// returns a . b
FLOATTYPE
VSMatrix::dotProduct(const FLOATTYPE *a, const FLOATTYPE *b) {
FLOATTYPE res = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
return res;
}
// Normalize a vec3
void
VSMatrix::normalize(FLOATTYPE *a) {
FLOATTYPE mag = sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
a[0] /= mag;
a[1] /= mag;
a[2] /= mag;
}
// res = b - a
void
VSMatrix::subtract(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res) {
res[0] = b[0] - a[0];
res[1] = b[1] - a[1];
res[2] = b[2] - a[2];
}
// res = a + b
void
VSMatrix::add(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res) {
res[0] = b[0] + a[0];
res[1] = b[1] + a[1];
res[2] = b[2] + a[2];
}
// returns |a|
FLOATTYPE
VSMatrix::length(const FLOATTYPE *a) {
return(sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]));
}
static inline int
M3(int i, int j)
{
return (i*3+j);
};
// computes the derived normal matrix for the view matrix
void
VSMatrix::computeNormalMatrix(const FLOATTYPE *aMatrix)
{
FLOATTYPE mMat3x3[9];
mMat3x3[0] = aMatrix[0];
mMat3x3[1] = aMatrix[1];
mMat3x3[2] = aMatrix[2];
mMat3x3[3] = aMatrix[4];
mMat3x3[4] = aMatrix[5];
mMat3x3[5] = aMatrix[6];
mMat3x3[6] = aMatrix[8];
mMat3x3[7] = aMatrix[9];
mMat3x3[8] = aMatrix[10];
FLOATTYPE det, invDet;
det = mMat3x3[0] * (mMat3x3[4] * mMat3x3[8] - mMat3x3[5] * mMat3x3[7]) +
mMat3x3[1] * (mMat3x3[5] * mMat3x3[6] - mMat3x3[8] * mMat3x3[3]) +
mMat3x3[2] * (mMat3x3[3] * mMat3x3[7] - mMat3x3[4] * mMat3x3[6]);
invDet = 1.0f/det;
mMatrix[0] = (mMat3x3[4] * mMat3x3[8] - mMat3x3[5] * mMat3x3[7]) * invDet;
mMatrix[1] = (mMat3x3[5] * mMat3x3[6] - mMat3x3[8] * mMat3x3[3]) * invDet;
mMatrix[2] = (mMat3x3[3] * mMat3x3[7] - mMat3x3[4] * mMat3x3[6]) * invDet;
mMatrix[3] = 0.0f;
mMatrix[4] = (mMat3x3[2] * mMat3x3[7] - mMat3x3[1] * mMat3x3[8]) * invDet;
mMatrix[5] = (mMat3x3[0] * mMat3x3[8] - mMat3x3[2] * mMat3x3[6]) * invDet;
mMatrix[6] = (mMat3x3[1] * mMat3x3[6] - mMat3x3[7] * mMat3x3[0]) * invDet;
mMatrix[7] = 0.0f;
mMatrix[8] = (mMat3x3[1] * mMat3x3[5] - mMat3x3[4] * mMat3x3[2]) * invDet;
mMatrix[9] = (mMat3x3[2] * mMat3x3[3] - mMat3x3[0] * mMat3x3[5]) * invDet;
mMatrix[10] =(mMat3x3[0] * mMat3x3[4] - mMat3x3[3] * mMat3x3[1]) * invDet;
mMatrix[11] = 0.0;
mMatrix[12] = 0.0;
mMatrix[13] = 0.0;
mMatrix[14] = 0.0;
mMatrix[15] = 1.0;
}
// aux function resMat = resMat * aMatrix
void
VSMatrix::multMatrix(FLOATTYPE *resMat, const FLOATTYPE *aMatrix)
{
FLOATTYPE res[16];
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
res[j*4 + i] = 0.0f;
for (int k = 0; k < 4; ++k)
{
res[j*4 + i] += resMat[k*4 + i] * aMatrix[j*4 + k];
}
}
}
memcpy(resMat, res, 16 * sizeof(FLOATTYPE));
}

112
src/gl/data/gl_matrix.h Normal file
View file

@ -0,0 +1,112 @@
// Matrix class based on code from VSML:
/** ----------------------------------------------------------
* \class VSMathLib
*
* Lighthouse3D
*
* VSMathLib - Very Simple Matrix Library
*
* Full documentation at
* http://www.lighthouse3d.com/very-simple-libs
*
* This class aims at easing geometric transforms, camera
* placement and projection definition for programmers
* working with OpenGL core versions.
*
*
---------------------------------------------------------------*/
#ifndef __VSMatrix__
#define __VSMatrix__
#include <stdlib.h>
#ifdef USE_DOUBLE
typedef double FLOATTYPE;
#else
typedef float FLOATTYPE;
#endif
class VSMatrix {
public:
VSMatrix()
{
}
VSMatrix(int)
{
loadIdentity();
}
void translate(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z);
void scale(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z);
void rotate(FLOATTYPE angle, FLOATTYPE x, FLOATTYPE y, FLOATTYPE z);
void loadIdentity();
#ifdef USE_DOUBLE
void multMatrix(const float *aMatrix);
#endif
void multMatrix(const FLOATTYPE *aMatrix);
void multMatrix(const VSMatrix &aMatrix)
{
multMatrix(aMatrix.mMatrix);
}
void loadMatrix(const FLOATTYPE *aMatrix);
#ifdef USE_DOUBLE
void loadMatrix(const float *aMatrix);
#endif
void lookAt(FLOATTYPE xPos, FLOATTYPE yPos, FLOATTYPE zPos, FLOATTYPE xLook, FLOATTYPE yLook, FLOATTYPE zLook, FLOATTYPE xUp, FLOATTYPE yUp, FLOATTYPE zUp);
void perspective(FLOATTYPE fov, FLOATTYPE ratio, FLOATTYPE nearp, FLOATTYPE farp);
void ortho(FLOATTYPE left, FLOATTYPE right, FLOATTYPE bottom, FLOATTYPE top, FLOATTYPE nearp=-1.0f, FLOATTYPE farp=1.0f);
void frustum(FLOATTYPE left, FLOATTYPE right, FLOATTYPE bottom, FLOATTYPE top, FLOATTYPE nearp, FLOATTYPE farp);
void copy(FLOATTYPE * pDest)
{
memcpy(pDest, mMatrix, 16 * sizeof(FLOATTYPE));
}
#ifdef USE_DOUBLE
void copy(float * pDest)
{
for (int i = 0; i < 16; i++)
{
pDest[i] = (float)mMatrix[i];
}
}
#endif
const FLOATTYPE *get() const
{
return mMatrix;
}
void matrixToGL(int location);
void multMatrixPoint(const FLOATTYPE *point, FLOATTYPE *res);
#ifdef USE_DOUBLE
void computeNormalMatrix(const float *aMatrix);
#endif
void computeNormalMatrix(const FLOATTYPE *aMatrix);
void computeNormalMatrix(const VSMatrix &aMatrix)
{
computeNormalMatrix(aMatrix.mMatrix);
}
protected:
static void crossProduct(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res);
static FLOATTYPE dotProduct(const FLOATTYPE *a, const FLOATTYPE * b);
static void normalize(FLOATTYPE *a);
static void subtract(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res);
static void add(const FLOATTYPE *a, const FLOATTYPE *b, FLOATTYPE *res);
static FLOATTYPE length(const FLOATTYPE *a);
static void multMatrix(FLOATTYPE *resMatrix, const FLOATTYPE *aMatrix);
static void setIdentityMatrix(FLOATTYPE *mat, int size = 4);
/// The storage for matrices
FLOATTYPE mMatrix[16];
};
#endif

View file

@ -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<FPortalSector> FPortalSectors;
typedef TMap<FPortalID, FPortalSectors> FPortalMap;
TArray<FPortal *> 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<int> 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<FCoverageVertex> &shape)
{
static TArray<FCoverageLine> 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;i<shape.Size(); i++)
{
FCoverageVertex *v1 = &shape[i];
FCoverageVertex *v2 = &shape[(i+1) % shape.Size()];
FCoverageLine vl = {{*v1, *v2}};
double dist_v1 = PartitionDistance(v1, bsp);
double dist_v2 = PartitionDistance(v2, bsp);
if(dist_v1 <= COVERAGE_EPSILON)
{
if (dist_v2 <= COVERAGE_EPSILON)
{
lists[centerside].Push(vl);
}
else
{
int side = PointOnSide(v2, bsp);
lists[side].Push(vl);
}
}
else if (dist_v2 <= COVERAGE_EPSILON)
{
int side = PointOnSide(v1, bsp);
lists[side].Push(vl);
}
else
{
int side1 = PointOnSide(v1, bsp);
int side2 = PointOnSide(v2, bsp);
if(side1 != side2)
{
// if the partition line crosses this seg, we must split it.
FCoverageVertex vert;
if (GetIntersection(v1, v2, bsp, &vert))
{
lists[0].Push(vl);
lists[1].Push(vl);
lists[side1].Last().v[1] = vert;
lists[side2].Last().v[0] = vert;
}
else
{
// should never happen
lists[side1].Push(vl);
}
}
else
{
// both points on the same side.
lists[side1].Push(vl);
}
}
}
if (lists[1].Size() == 0)
{
CollectNode(bsp->children[0], shape);
}
else if (lists[0].Size() == 0)
{
CollectNode(bsp->children[1], shape);
}
else
{
// copy the static arrays into local ones
TArray<FCoverageVertex> locallists[2];
for(int l=0;l<2;l++)
{
for (unsigned i=0;i<lists[l].Size(); i++)
{
locallists[l].Push(lists[l][i].v[0]);
unsigned i1= (i+1)%lists[l].Size();
if (lists[l][i1].v[0] != lists[l][i].v[1])
{
locallists[l].Push(lists[l][i].v[1]);
}
}
}
CollectNode(bsp->children[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<FCoverageVertex> shape;
double centerx=0, centery=0;
shape.Resize(subsector->numlines);
for(unsigned i=0; i<subsector->numlines; 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;i<numsectors;i++)
{
sector_t *sec = &sectors[i];
if (sec->CeilingSkyBox != 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;i<numnodes;i++)
{
node_t *no = &nodes[i];
double fdx = (double)no->dx;
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;i<pair->Value.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;j<pair->Value.Size(); j++)
{
sector_t *sec = pair->Value[j].mSub;
int plane = pair->Value[j].mPlane;
if (portal->plane == plane)
{
for(int k=0;k<sec->subsectorcount; k++)
{
subsector_t *sub = sec->subsectors[k];
gl_BuildPortalCoverage(&sub->portalcoverage[plane], sub, portal);
}
sec->portals[plane] = portal;
}
}
}
}
}
}
CCMD(dumpportals)
{
for(unsigned i=0;i<portals.Size(); i++)
{
double xdisp = portals[i]->xDisplacement/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;j<numsubsectors;j++)
{
subsector_t *sub = &subsectors[j];
FPortal *port = sub->render_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");
}
}
}
}
}

751
src/gl/data/gl_setup.cpp Normal file
View file

@ -0,0 +1,751 @@
/*
** 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();
//==========================================================================
//
//
//
//==========================================================================
static void DoSetMapSection(subsector_t *sub, int num)
{
sub->mapsection = num;
for(DWORD i=0;i<sub->numlines;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<cvertex_t, int> FSectionVertexMap;
static int MergeMapSections(int num)
{
FSectionVertexMap vmap;
FSectionVertexMap::Pair *pair;
TArray<int> sectmap;
TArray<bool> sectvalid;
sectmap.Resize(num);
sectvalid.Resize(num);
for(int i=0;i<num;i++)
{
sectmap[i] = -1;
sectvalid[i] = true;
}
int mergecount = 1;
cvertex_t vt;
// first step: Set mapsection for all vertex positions.
for(DWORD i=0;i<(DWORD)numsegs;i++)
{
seg_t * seg = &segs[i];
int section = seg->Subsector->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;i<(DWORD)numsegs;i++)
{
seg_t * seg = &segs[i];
int section = seg->Subsector->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;k<numsubsectors;k++)
{
if (subsectors[k].mapsection == vsection) subsectors[k].mapsection = section;
}
FSectionVertexMap::Iterator it(vmap);
while (it.NextPair(pair))
{
if (pair->Value == vsection) pair->Value = section;
}
sectvalid[vsection-1] = false;
}
}
}
for(int i=0;i<num;i++)
{
if (sectvalid[i]) sectmap[i] = mergecount++;
}
for(int i=0;i<numsubsectors;i++)
{
subsectors[i].mapsection = sectmap[subsectors[i].mapsection-1];
assert(subsectors[i].mapsection!=-1);
}
return mergecount-1;
}
//==========================================================================
//
//
//
//==========================================================================
static void SetMapSections()
{
bool set;
int num = 0;
do
{
set = false;
for(int i=0; i<numsubsectors; i++)
{
if (subsectors[i].mapsection == 0)
{
num++;
DoSetMapSection(&subsectors[i], num);
set = true;
break;
}
}
}
while (set);
num = MergeMapSections(num);
currentmapsection.Resize(1 + num/8);
#ifdef DEBUG
Printf("%d map sections found\n", num);
#endif
}
//==========================================================================
//
// prepare subsectors for GL rendering
// - analyze rendering hacks using open sectors
// - assign a render sector (for self referencing sectors)
// - calculate a bounding box
//
//==========================================================================
static void SpreadHackedFlag(subsector_t * sub)
{
// The subsector pointer hasn't been set yet!
for(DWORD i=0;i<sub->numlines;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;
sub->hacked &= ~4;
SpreadHackedFlag (sub2);
}
}
}
}
//==========================================================================
//
//
//
//==========================================================================
static void PrepareSectorData()
{
int i;
TArray<subsector_t *> undetermined;
subsector_t * ss;
// now group the subsectors by sector
subsector_t ** subsectorbuffer = new subsector_t * [numsubsectors];
for(i=0, ss=subsectors; i<numsubsectors; i++, ss++)
{
ss->render_sector->subsectorcount++;
}
for (i=0; i<numsectors; i++)
{
sectors[i].subsectors = subsectorbuffer;
subsectorbuffer += sectors[i].subsectorcount;
sectors[i].subsectorcount = 0;
}
for(i=0, ss = subsectors; i<numsubsectors; i++, ss++)
{
ss->render_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;j<subsectors[i].numlines;j++)
{
if (!(subsectors[i].hacked&1) && seg[j].linedef==0 &&
seg[j].PartnerSeg!=NULL &&
subsectors[i].render_sector != seg[j].PartnerSeg->Subsector->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|=5;
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 _DEBUG
if (sector-sectors==2)
{
int a = 0;
}
#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; i<sector->linecount; 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<int> & list)
{
int secno = int(sec-sectors);
for(unsigned i=0;i<list.Size();i++)
{
if (list[i]==secno) return;
}
list.Push(secno);
}
//==========================================================================
//
// Attach sectors to vertices - used to generate vertex height lists
//
//==========================================================================
static void InitVertexData()
{
TArray<int> * vt_sectorlists;
int i,j,k;
unsigned int l;
vt_sectorlists = new TArray<int>[numvertexes];
for(i=0;i<numlines;i++)
{
line_t * line = &lines[i];
for(j=0;j<2;j++)
{
vertex_t * v = j==0? line->v1 : 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;l<x.ffloors.Size();l++)
{
F3DFloor * rover = x.ffloors[l];
if(!(rover->flags & 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;i<numvertexes;i++)
{
int cnt = vt_sectorlists[i].Size();
vertexes[i].dirty = true;
vertexes[i].numheights=0;
if (cnt>1)
{
vertexes[i].numsectors= cnt;
vertexes[i].sectors=new sector_t*[cnt];
vertexes[i].heightlist = new float[cnt*2];
for(int j=0;j<cnt;j++)
{
vertexes[i].sectors[j] = &sectors[vt_sectorlists[i][j]];
}
}
else
{
vertexes[i].numsectors=0;
}
}
delete [] vt_sectorlists;
}
//==========================================================================
//
//
//
//==========================================================================
static void GetSideVertices(int sdnum, FVector2 *v1, FVector2 *v2)
{
line_t *ln = sides[sdnum].linedef;
if (ln->sidedef[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;i<numsegs;i++)
{
segs[i].PartnerSeg = NULL;
}
for (int i=0; i<numsubsectors; i++)
{
int seg = int(subsectors[i].firstline-segs);
for(DWORD j=0;j<subsectors[i].numlines;j++)
{
segs[j+seg].Subsector = &subsectors[i];
}
}
}
else
{
for(int i=0;i<numsegs;i++)
{
seg_t *seg = &segs[i];
// Account for ZDoom space optimizations that cannot be done for GL
unsigned int partner= glsegextras[i].PartnerSeg;
if (partner < unsigned(numsegs)) seg->PartnerSeg = &segs[partner];
else seg->PartnerSeg = NULL;
seg->Subsector = glsegextras[i].Subsector;
}
}
for(int i=0;i<numsegs;i++)
{
seg_t *seg = &segs[i];
if (seg->sidedef == 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;i<numsegs;i++)
{
seg_t *seg = &segs[i];
if (seg->sidedef != 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;
PrepareSegs();
PrepareSectorData();
InitVertexData();
int *checkmap = new int[numvertexes];
memset(checkmap, -1, sizeof(int)*numvertexes);
for(i=0;i<numsectors;i++)
{
sectors[i].sectornum = i;
PrepareTransparentDoors(&sectors[i]);
// This ignores vertices only used for seg splitting because those aren't needed here
for(int j = 0; j < sectors[i].linecount; j++)
{
line_t *l = sectors[i].lines[j];
if (l->sidedef[0]->Flags & WALLF_POLYOBJ) continue; // don't bother with polyobjects
int vtnum1 = int(l->v1 - vertexes);
int vtnum2 = int(l->v2 - vertexes);
if (checkmap[vtnum1] < i)
{
checkmap[vtnum1] = i;
sectors[i].e->vertices.Push(&vertexes[vtnum1]);
vertexes[vtnum1].dirty = true;
}
if (checkmap[vtnum2] < i)
{
checkmap[vtnum2] = i;
sectors[i].e->vertices.Push(&vertexes[vtnum2]);
vertexes[vtnum2].dirty = true;
}
}
}
delete[] checkmap;
gl_InitPortals();
if (GLRenderer != NULL)
{
GLRenderer->SetupLevel();
}
#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<ADynamicLight> 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;i<numsubsectors;i++)
{
for(int j=0;j<2;j++)
{
if (subsectors[i].portalcoverage[j].subsectors != NULL)
{
delete [] subsectors[i].portalcoverage[j].subsectors;
subsectors[i].portalcoverage[j].subsectors = NULL;
}
}
}
for(unsigned i=0;i<portals.Size(); i++)
{
delete portals[i];
}
portals.Clear();
}
//==========================================================================
//
//
//
//==========================================================================
CCMD(listmapsections)
{
for(int i=0;i<100;i++)
{
for (int j=0;j<numsubsectors;j++)
{
if (subsectors[j].mapsection == i)
{
Printf("Mapsection %d, sector %d, line %d\n", i, subsectors[j].render_sector->sectornum, int(subsectors[j].firstline->linedef-lines));
break;
}
}
}
}

View file

@ -0,0 +1,383 @@
/*
** 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/system/gl_interface.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/shaders/gl_shader.h"
#include "gl/data/gl_data.h"
#include "gl/data/gl_vertexbuffer.h"
//==========================================================================
//
// Create / destroy the VBO
//
//==========================================================================
FVertexBuffer::FVertexBuffer()
{
vao_id = vbo_id = 0;
glGenBuffers(1, &vbo_id);
glGenVertexArrays(1, &vao_id);
}
FVertexBuffer::~FVertexBuffer()
{
if (vbo_id != 0)
{
glDeleteBuffers(1, &vbo_id);
}
if (vao_id != 0)
{
glDeleteVertexArrays(1, &vao_id);
}
}
void FVertexBuffer::BindVBO()
{
glBindVertexArray(vao_id);
}
//==========================================================================
//
//
//
//==========================================================================
FFlatVertexBuffer::FFlatVertexBuffer()
: FVertexBuffer()
{
if (gl.flags & RFL_BUFFER_STORAGE)
{
unsigned int bytesize = BUFFER_SIZE * sizeof(FFlatVertex);
glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
glBufferStorage(GL_ARRAY_BUFFER, bytesize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
map = (FFlatVertex*)glMapBufferRange(GL_ARRAY_BUFFER, 0, bytesize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
glBindVertexArray(vao_id);
glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
glVertexAttribPointer(VATTR_VERTEX, 3,GL_FLOAT, false, sizeof(FFlatVertex), &VTO->x);
glVertexAttribPointer(VATTR_TEXCOORD, 2,GL_FLOAT, false, sizeof(FFlatVertex), &VTO->u);
glEnableVertexAttribArray(VATTR_VERTEX);
glEnableVertexAttribArray(VATTR_TEXCOORD);
glBindVertexArray(0);
}
else
{
vbo_shadowdata.Reserve(BUFFER_SIZE);
map = &vbo_shadowdata[0];
}
mNumReserved = mIndex = mCurIndex = 0;
}
FFlatVertexBuffer::~FFlatVertexBuffer()
{
if (gl.flags & RFL_BUFFER_STORAGE)
{
glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
glUnmapBuffer(GL_ARRAY_BUFFER);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
//==========================================================================
//
// immediate mode fallback for drivers without GL_ARB_buffer_storage
//
// No single core method is performant enough to handle this adequately
// so we have to resort to immediate mode instead...
//
//==========================================================================
void FFlatVertexBuffer::ImmRenderBuffer(unsigned int primtype, unsigned int offset, unsigned int count)
{
// this will only get called if we can't acquire a persistently mapped buffer.
#ifndef CORE_PROFILE
glBegin(primtype);
for (unsigned int i = 0; i < count; i++)
{
glVertexAttrib2fv(VATTR_TEXCOORD, &map[offset + i].u);
glVertexAttrib3fv(VATTR_VERTEX, &map[offset + i].x);
}
glEnd();
#endif
}
//==========================================================================
//
// 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;
}
//==========================================================================
//
// Find a 3D floor
//
//==========================================================================
static F3DFloor *Find3DFloor(sector_t *target, sector_t *model)
{
for(unsigned i=0; i<target->e->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; k<sub->numlines; 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; j<sec->subsectorcount; 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<sector_t *> &fakes = sec->e->FakeFloor.Sectors;
for (unsigned g=0; g<fakes.Size(); g++)
{
sector_t *fsec = fakes[g];
fsec->vboindex[2+h] = CreateSectorVertices(fsec, plane, false);
}
// and finally all attached 3D floors
TArray<sector_t *> &xf = sec->e->XFloor.attached;
for (unsigned g=0; g<xf.Size(); g++)
{
sector_t *fsec = xf[g];
F3DFloor *ffloor = Find3DFloor(fsec, sec);
if (ffloor != NULL && ffloor->flags & 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; i<numsectors;i++)
{
CreateVertices(h, &sectors[i], sectors[i].GetSecPlane(h), h == sector_t::floor);
}
}
// We need to do a final check for Vavoom water and FF_FIX sectors.
// No new vertices are needed here. The planes come from the actual sector
for(int i=0; i<numsectors;i++)
{
for(unsigned j=0;j<sectors[i].e->XFloor.ffloors.Size(); j++)
{
F3DFloor *ff = sectors[i].e->XFloor.ffloors[j];
if (ff->top.model == &sectors[i])
{
ff->top.vindex = sectors[i].vboindex[ff->top.isceiling];
}
if (ff->bottom.model == &sectors[i])
{
ff->bottom.vindex = sectors[i].vboindex[ff->top.isceiling];
}
}
}
}
//==========================================================================
//
//
//
//==========================================================================
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];
FFlatVertex *mapvt = &map[startvt];
for(int i=0; i<countvt; i++, vt++, mapvt++)
{
vt->z = splane.ZatPoint(vt->x, vt->y);
if (plane == sector_t::floor && sec->transdoor) vt->z -= 1;
mapvt->z = vt->z;
}
}
//==========================================================================
//
//
//
//==========================================================================
void FFlatVertexBuffer::CreateVBO()
{
if (gl.flags & RFL_BUFFER_STORAGE)
{
vbo_shadowdata.Resize(mNumReserved);
CreateFlatVBO();
mCurIndex = mIndex = vbo_shadowdata.Size();
memcpy(map, &vbo_shadowdata[0], vbo_shadowdata.Size() * sizeof(FFlatVertex));
}
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<numsectors;i++)
{
sectors[i].vboindex[3] = sectors[i].vboindex[2] =
sectors[i].vboindex[1] = sectors[i].vboindex[0] = -1;
sectors[i].vboheight[1] = sectors[i].vboheight[0] = FIXED_MIN;
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void FFlatVertexBuffer::CheckPlanes(sector_t *sector)
{
if (sector->GetPlaneTexZ(sector_t::ceiling) != sector->vboheight[sector_t::ceiling])
{
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])
{
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.
//
//==========================================================================
void FFlatVertexBuffer::CheckUpdate(sector_t *sector)
{
if (gl.flags & RFL_BUFFER_STORAGE)
{
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);
}
}

View file

@ -0,0 +1,216 @@
#ifndef __VERTEXBUFFER_H
#define __VERTEXBUFFER_H
#include "tarray.h"
#include "gl/utility/gl_clock.h"
#include "gl/system/gl_interface.h"
struct vertex_t;
struct secplane_t;
struct subsector_t;
struct sector_t;
class FVertexBuffer
{
protected:
unsigned int vbo_id;
unsigned int vao_id;
public:
FVertexBuffer();
virtual ~FVertexBuffer();
void BindVBO();
};
struct FFlatVertex
{
float x,z,y; // world position
float u,v; // texture coordinates
void SetFlatVertex(vertex_t *vt, const secplane_t &plane);
void Set(float xx, float zz, float yy, float uu, float vv)
{
x = xx;
z = zz;
y = yy;
u = uu;
v = vv;
}
};
#define VTO ((FFlatVertex*)NULL)
class FFlatVertexBuffer : public FVertexBuffer
{
FFlatVertex *map;
unsigned int mIndex;
unsigned int mCurIndex;
unsigned int mNumReserved;
void CheckPlanes(sector_t *sector);
static const unsigned int BUFFER_SIZE = 2000000;
static const unsigned int BUFFER_SIZE_TO_USE = 1999500;
void ImmRenderBuffer(unsigned int primtype, unsigned int offset, unsigned int count);
public:
TArray<FFlatVertex> vbo_shadowdata; // this is kept around for updating the actual (non-readable) buffer and as stand-in for pre GL 4.x
FFlatVertexBuffer();
~FFlatVertexBuffer();
void CreateVBO();
void CheckUpdate(sector_t *sector);
FFlatVertex *GetBuffer()
{
return &map[mCurIndex];
}
unsigned int GetCount(FFlatVertex *newptr, unsigned int *poffset)
{
unsigned int newofs = (unsigned int)(newptr - map);
unsigned int diff = newofs - mCurIndex;
*poffset = mCurIndex;
mCurIndex = newofs;
if (mCurIndex >= BUFFER_SIZE_TO_USE) mCurIndex = mIndex;
return diff;
}
#ifdef __GL_PCH_H // we need the system includes for this but we cannot include them ourselves without creating #define clashes. The affected files wouldn't try to draw anyway.
void RenderArray(unsigned int primtype, unsigned int offset, unsigned int count)
{
drawcalls.Clock();
if (gl.flags & RFL_BUFFER_STORAGE)
{
glDrawArrays(primtype, offset, count);
}
else
{
ImmRenderBuffer(primtype, offset, count);
}
drawcalls.Unclock();
}
void RenderCurrent(FFlatVertex *newptr, unsigned int primtype, unsigned int *poffset = NULL, unsigned int *pcount = NULL)
{
unsigned int offset;
unsigned int count = GetCount(newptr, &offset);
RenderArray(primtype, offset, count);
if (poffset) *poffset = offset;
if (pcount) *pcount = count;
}
#endif
void Reset()
{
mCurIndex = mIndex;
}
private:
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 UpdatePlaneVertices(sector_t *sec, int plane);
};
struct FSkyVertex
{
float x, y, z, u, v;
PalEntry color;
void Set(float xx, float zz, float yy, float uu=0, float vv=0, PalEntry col=0xffffffff)
{
x = xx;
z = zz;
y = yy;
u = uu;
v = vv;
color = col;
}
};
class FSkyVertexBuffer : public FVertexBuffer
{
public:
static const int SKYHEMI_UPPER = 1;
static const int SKYHEMI_LOWER = 2;
enum
{
SKYMODE_MAINLAYER = 0,
SKYMODE_SECONDLAYER = 1,
SKYMODE_FOGLAYER = 2
};
private:
TArray<FSkyVertex> mVertices;
TArray<unsigned int> mPrimStart;
int mRows, mColumns;
void SkyVertex(int r, int c, bool yflip);
void CreateSkyHemisphere(int hemi);
void CreateDome();
void RenderRow(int prim, int row);
public:
FSkyVertexBuffer();
virtual ~FSkyVertexBuffer();
void RenderDome(FMaterial *tex, int mode);
};
#define VSO ((FSkyVertex*)NULL)
struct FModelVertex
{
float x, y, z; // world position
float u, v; // texture coordinates
void Set(float xx, float yy, float zz, float uu, float vv)
{
x = xx;
y = yy;
z = zz;
u = uu;
v = vv;
}
void SetNormal(float nx, float ny, float nz)
{
// GZDoom currently doesn't use normals. This function is so that the high level code can pretend it does.
}
};
class FModelVertexBuffer : public FVertexBuffer
{
int mIndexFrame[2];
unsigned int ibo_id;
public:
FModelVertexBuffer(bool needindex);
~FModelVertexBuffer();
FModelVertex *LockVertexBuffer(unsigned int size);
void UnlockVertexBuffer();
unsigned int *LockIndexBuffer(unsigned int size);
void UnlockIndexBuffer();
unsigned int SetupFrame(unsigned int frame1, unsigned int frame2);
};
#define VMO ((FModelVertex*)NULL)
#endif

View file

@ -0,0 +1,768 @@
/*
** 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);
EXTERN_CVAR(Int, vid_renderer)
//==========================================================================
//
//==========================================================================
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, sizeof(args));
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 (vid_renderer == 0)
{
return;
}
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<float>(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.
//
//=============================================================================
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 = new FLightNode;
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;
delete 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;
subSec->validcount = ::validcount;
touching_subsectors = AddLightNode(&subSec->lighthead, subSec, this, touching_subsectors);
if (subSec->sector->validcount != ::validcount)
{
touching_sector = AddLightNode(&subSec->render_sector->lighthead, subSec->sector, this, touching_sector);
subSec->sector->validcount = ::validcount;
}
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, 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;
}
node = touching_sector;
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
subsector_t * subSec = R_PointInSubsector(x, y);
if (subSec)
{
float fradius = FIXED2FLOAT(radius);
::validcount++;
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;
}
node = touching_sector;
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);
while (touching_sector) touching_sector = DeleteLightNode(touching_sector);
}
void ADynamicLight::Destroy()
{
UnlinkLight();
Super::Destroy();
}
//==========================================================================
//
// Needed for garbage collection
//
//==========================================================================
size_t AActor::PropagateMark()
{
for (unsigned i=0; i<dynamiclights.Size(); i++)
{
GC::Mark(dynamiclights[i]);
}
return Super::PropagateMark();
}
CCMD(listlights)
{
int walls, sectors, subsecs;
int allwalls=0, allsectors=0, allsubsecs = 0;
int i=0;
ADynamicLight * dl;
TThinkerIterator<ADynamicLight> it;
while ((dl=it.Next()))
{
walls=0;
sectors=0;
subsecs = 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.GetChars());
}
FLightNode * node;
node=dl->touching_sides;
while (node)
{
walls++;
allwalls++;
node = node->nextTarget;
}
node=dl->touching_subsectors;
while (node)
{
allsubsecs++;
subsecs++;
node = node->nextTarget;
}
node = dl->touching_sector;
while (node)
{
allsectors++;
sectors++;
node = node->nextTarget;
}
Printf("- %d walls, %d subsectors, %d sectors\n", walls, subsecs, sectors);
}
Printf("%i dynamic lights, %d walls, %d subsectors, %d sectors\n\n\n", i, allwalls, allsubsecs, allsectors);
}
CCMD(listsublights)
{
for(int i=0;i<numsubsectors;i++)
{
subsector_t *sub = &subsectors[i];
int lights = 0;
FLightNode * node = sub->lighthead;
while (node != NULL)
{
lights++;
node = node->nextLight;
}
Printf(PRINT_LOG, "Subsector %d - %d lights\n", i, lights);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,190 @@
#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;
FLightNode * touching_sector;
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<float> 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;
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, bool checkside, bool forceadditive, FDynLightData &data);
void gl_UploadLights(FDynLightData &data);
#endif

View file

@ -0,0 +1,146 @@
/*
** 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/system/gl_interface.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();
}
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, 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;
}
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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -0,0 +1,224 @@
/*
** gl_lightbuffer.cpp
** Buffer data maintenance for dynamic lights
**
**---------------------------------------------------------------------------
** Copyright 2014 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/shaders/gl_shader.h"
#include "gl/dynlights/gl_lightbuffer.h"
#include "gl/dynlights/gl_dynlight.h"
#include "gl/system/gl_interface.h"
#include "gl/utility//gl_clock.h"
static const int INITIAL_BUFFER_SIZE = 160000; // This means 80000 lights per frame and 160000*16 bytes == 2.56 MB.
float *mMap;
FLightBuffer::FLightBuffer()
{
mBufferSize = INITIAL_BUFFER_SIZE;
mByteSize = mBufferSize * sizeof(float);
if (gl.flags & RFL_SHADER_STORAGE_BUFFER)
{
mBufferType = GL_SHADER_STORAGE_BUFFER;
mBlockAlign = 0;
mBlockSize = mBufferSize;
}
else
{
mBufferType = GL_UNIFORM_BUFFER;
mBlockSize = gl.maxuniformblock / 16;
if (mBlockSize > 2048) mBlockSize = 2048; // we don't really need a larger buffer
mBlockAlign = mBlockSize / 2;
}
glGenBuffers(1, &mBufferId);
glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, mBufferId);
glBindBuffer(mBufferType, mBufferId); // Note: Some older AMD drivers don't do that in glBindBufferBase, as they should.
if (gl.flags & RFL_BUFFER_STORAGE)
{
glBufferStorage(mBufferType, mByteSize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
}
else
{
glBufferData(mBufferType, mByteSize, NULL, GL_DYNAMIC_DRAW);
mBufferPointer = NULL;
}
Clear();
mLastMappedIndex = UINT_MAX;
}
FLightBuffer::~FLightBuffer()
{
glBindBuffer(mBufferType, 0);
glDeleteBuffers(1, &mBufferId);
}
void FLightBuffer::Clear()
{
mIndex = 0;
mIndices.Clear();
mUploadIndex = 0;
}
int FLightBuffer::UploadLights(FDynLightData &data)
{
int size0 = data.arrays[0].Size()/4;
int size1 = data.arrays[1].Size()/4;
int size2 = data.arrays[2].Size()/4;
int totalsize = size0 + size1 + size2 + 1;
// pointless type casting because some compilers can't print enough warnings.
if (mBlockAlign > 0 && (unsigned int)totalsize + (mIndex % mBlockAlign) > mBlockSize)
{
mIndex = ((mIndex + mBlockAlign) / mBlockAlign) * mBlockAlign;
// can't be rendered all at once.
if ((unsigned int)totalsize > mBlockSize)
{
int diff = totalsize - (int)mBlockSize;
size2 -= diff;
if (size2 < 0)
{
size1 += size2;
size2 = 0;
}
if (size1 < 0)
{
size0 += size1;
size1 = 0;
}
totalsize = size0 + size1 + size2 + 1;
}
}
if (totalsize <= 1) return -1;
if (mIndex + totalsize > mBufferSize/4)
{
// reallocate the buffer with twice the size
unsigned int newbuffer;
// first unmap the old buffer
glBindBuffer(mBufferType, mBufferId);
glUnmapBuffer(mBufferType);
// create and bind the new buffer, bind the old one to a copy target (too bad that DSA is not yet supported well enough to omit this crap.)
glGenBuffers(1, &newbuffer);
glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, newbuffer);
glBindBuffer(mBufferType, newbuffer); // Note: Some older AMD drivers don't do that in glBindBufferBase, as they should.
glBindBuffer(GL_COPY_READ_BUFFER, mBufferId);
// create the new buffer's storage (twice as large as the old one)
mBufferSize *= 2;
mByteSize *= 2;
if (gl.flags & RFL_BUFFER_STORAGE)
{
glBufferStorage(mBufferType, mByteSize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
}
else
{
glBufferData(mBufferType, mByteSize, NULL, GL_DYNAMIC_DRAW);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT);
}
// copy contents and delete the old buffer.
glCopyBufferSubData(GL_COPY_READ_BUFFER, mBufferType, 0, 0, mByteSize/2);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
glDeleteBuffers(1, &mBufferId);
mBufferId = newbuffer;
}
float *copyptr;
assert(mBufferPointer != NULL);
if (mBufferPointer == NULL) return -1;
copyptr = mBufferPointer + mIndex * 4;
float parmcnt[] = { 0, float(size0), float(size0 + size1), float(size0 + size1 + size2) };
memcpy(&copyptr[0], parmcnt, 4 * sizeof(float));
memcpy(&copyptr[4], &data.arrays[0][0], 4 * size0*sizeof(float));
memcpy(&copyptr[4 + 4*size0], &data.arrays[1][0], 4 * size1*sizeof(float));
memcpy(&copyptr[4 + 4*(size0 + size1)], &data.arrays[2][0], 4 * size2*sizeof(float));
unsigned int bufferindex = mIndex;
mIndex += totalsize;
draw_dlight += (totalsize-1) / 2;
return bufferindex;
}
void FLightBuffer::Begin()
{
if (!(gl.flags & RFL_BUFFER_STORAGE))
{
glBindBuffer(mBufferType, mBufferId);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT);
}
}
void FLightBuffer::Finish()
{
if (!(gl.flags & RFL_BUFFER_STORAGE))
{
glBindBuffer(mBufferType, mBufferId);
glUnmapBuffer(mBufferType);
mBufferPointer = NULL;
}
}
int FLightBuffer::BindUBO(unsigned int index)
{
unsigned int offset = (index / mBlockAlign) * mBlockAlign;
if (offset != mLastMappedIndex)
{
// this will only get called if a uniform buffer is used. For a shader storage buffer we only need to bind the buffer once at the start to all shader programs
mLastMappedIndex = offset;
glBindBufferRange(GL_UNIFORM_BUFFER, LIGHTBUF_BINDINGPOINT, mBufferId, offset*16, mBlockSize*16); // we go from counting vec4's to counting bytes here.
}
return (index - offset);
}

View file

@ -0,0 +1,39 @@
#ifndef __GL_LIGHTBUFFER_H
#define __GL_LIGHTBUFFER_H
#include "tarray.h"
struct FDynLightData;
class FLightBuffer
{
TArray<int> mIndices;
unsigned int mBufferId;
float * mBufferPointer;
unsigned int mBufferType;
unsigned int mIndex;
unsigned int mUploadIndex;
unsigned int mLastMappedIndex;
unsigned int mBlockAlign;
unsigned int mBlockSize;
unsigned int mBufferSize;
unsigned int mByteSize;
public:
FLightBuffer();
~FLightBuffer();
void Clear();
int UploadLights(FDynLightData &data);
void Begin();
void Finish();
int BindUBO(unsigned int index);
unsigned int GetBlockSize() const { return mBlockSize; }
unsigned int GetBufferType() const { return mBufferType; }
unsigned int GetIndexPtr() const { return mIndices.Size(); }
void StoreIndex(int index) { mIndices.Push(index); }
int GetIndex(int i) const { return mIndices[i]; }
};
#endif

594
src/gl/gl_builddraw.cpp Normal file
View file

@ -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<FBunch> Bunches;
TArray<int> 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-endAngle<ANGLE_180)
{
return CL_Skip;
}
if (!clipper.SafeCheckRange(startAngle, endAngle))
{
return CL_Skip;
}
if (line->otherside == -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; i<sect->numloops; i++)
{
FGLSectionLoop *loop = sect->GetLoop(i);
inbunch = false;
for(int j=0; j<loop->numlines; 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

13
src/gl/gl_functions.h Normal file
View file

@ -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

141
src/gl/hqnx/common.h Normal file
View file

@ -0,0 +1,141 @@
/*
* Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com )
*
* Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net)
* Copyright (C) 2011 Francois Gannaz <mytskine@gmail.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
*/
#ifndef __HQX_COMMON_H_
#define __HQX_COMMON_H_
#include <stdlib.h>
#include "mystdint.h"
#define MASK_2 0x0000FF00
#define MASK_13 0x00FF00FF
#define MASK_RGB 0x00FFFFFF
#define MASK_ALPHA 0xFF000000
#define Ymask 0x00FF0000
#define Umask 0x0000FF00
#define Vmask 0x000000FF
#define trY 0x00300000
#define trU 0x00000700
#define trV 0x00000006
/* RGB to YUV lookup table */
extern uint32_t *RGBtoYUV;
static inline uint32_t rgb_to_yuv(uint32_t c)
{
// Mask against MASK_RGB to discard the alpha channel
return RGBtoYUV[MASK_RGB & c];
}
/* Test if there is difference in color */
static inline int yuv_diff(uint32_t yuv1, uint32_t yuv2) {
return (( abs((int64_t)(yuv1 & Ymask) - (int64_t)(yuv2 & Ymask)) > trY ) ||
( abs((int64_t)(yuv1 & Umask) - (int64_t)(yuv2 & Umask)) > trU ) ||
( abs((int64_t)(yuv1 & Vmask) - (int64_t)(yuv2 & Vmask)) > trV ) );
}
static inline int Diff(uint32_t c1, uint32_t c2)
{
return yuv_diff(rgb_to_yuv(c1), rgb_to_yuv(c2));
}
/* Interpolate functions */
static inline uint32_t Interpolate_2(uint32_t c1, int w1, uint32_t c2, int w2, int s)
{
if (c1 == c2) {
return c1;
}
return
(((((c1 & MASK_ALPHA) >> 24) * w1 + ((c2 & MASK_ALPHA) >> 24) * w2) << (24-s)) & MASK_ALPHA) +
((((c1 & MASK_2) * w1 + (c2 & MASK_2) * w2) >> s) & MASK_2) +
((((c1 & MASK_13) * w1 + (c2 & MASK_13) * w2) >> s) & MASK_13);
}
static inline uint32_t Interpolate_3(uint32_t c1, int w1, uint32_t c2, int w2, uint32_t c3, int w3, int s)
{
return
(((((c1 & MASK_ALPHA) >> 24) * w1 + ((c2 & MASK_ALPHA) >> 24) * w2 + ((c3 & MASK_ALPHA) >> 24) * w3) << (24-s)) & MASK_ALPHA) +
((((c1 & MASK_2) * w1 + (c2 & MASK_2) * w2 + (c3 & MASK_2) * w3) >> s) & MASK_2) +
((((c1 & MASK_13) * w1 + (c2 & MASK_13) * w2 + (c3 & MASK_13) * w3) >> s) & MASK_13);
}
static inline uint32_t Interp1(uint32_t c1, uint32_t c2)
{
//(c1*3+c2) >> 2;
return Interpolate_2(c1, 3, c2, 1, 2);
}
static inline uint32_t Interp2(uint32_t c1, uint32_t c2, uint32_t c3)
{
//(c1*2+c2+c3) >> 2;
return Interpolate_3(c1, 2, c2, 1, c3, 1, 2);
}
static inline uint32_t Interp3(uint32_t c1, uint32_t c2)
{
//(c1*7+c2)/8;
return Interpolate_2(c1, 7, c2, 1, 3);
}
static inline uint32_t Interp4(uint32_t c1, uint32_t c2, uint32_t c3)
{
//(c1*2+(c2+c3)*7)/16;
return Interpolate_3(c1, 2, c2, 7, c3, 7, 4);
}
static inline uint32_t Interp5(uint32_t c1, uint32_t c2)
{
//(c1+c2) >> 1;
return Interpolate_2(c1, 1, c2, 1, 1);
}
static inline uint32_t Interp6(uint32_t c1, uint32_t c2, uint32_t c3)
{
//(c1*5+c2*2+c3)/8;
return Interpolate_3(c1, 5, c2, 2, c3, 1, 3);
}
static inline uint32_t Interp7(uint32_t c1, uint32_t c2, uint32_t c3)
{
//(c1*6+c2+c3)/8;
return Interpolate_3(c1, 6, c2, 1, c3, 1, 3);
}
static inline uint32_t Interp8(uint32_t c1, uint32_t c2)
{
//(c1*5+c2*3)/8;
return Interpolate_2(c1, 5, c2, 3, 3);
}
static inline uint32_t Interp9(uint32_t c1, uint32_t c2, uint32_t c3)
{
//(c1*2+(c2+c3)*3)/8;
return Interpolate_3(c1, 2, c2, 3, c3, 3, 3);
}
static inline uint32_t Interp10(uint32_t c1, uint32_t c2, uint32_t c3)
{
//(c1*14+c2+c3)/16;
return Interpolate_3(c1, 14, c2, 1, c3, 1, 4);
}
#endif

2809
src/gl/hqnx/hq2x.cpp Normal file

File diff suppressed because it is too large Load diff

3787
src/gl/hqnx/hq3x.cpp Normal file

File diff suppressed because it is too large Load diff

5233
src/gl/hqnx/hq4x.cpp Normal file

File diff suppressed because it is too large Load diff

55
src/gl/hqnx/hqx.h Normal file
View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com )
*
* Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net)
*
* 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 __HQX_H_
#define __HQX_H_
#include "mystdint.h"
#if defined( __GNUC__ )
#ifdef __MINGW32__
#define HQX_CALLCONV __stdcall
#else
#define HQX_CALLCONV
#endif
#else
#define HQX_CALLCONV
#endif
#if 0 //defined(_WIN32)
#ifdef DLL_EXPORT
#define HQX_API __declspec(dllexport)
#else
#define HQX_API __declspec(dllimport)
#endif
#else
#define HQX_API
#endif
HQX_API void HQX_CALLCONV hqxInit(void);
HQX_API void HQX_CALLCONV hq2x_32( uint32_t * src, uint32_t * dest, int width, int height );
HQX_API void HQX_CALLCONV hq3x_32( uint32_t * src, uint32_t * dest, int width, int height );
HQX_API void HQX_CALLCONV hq4x_32( uint32_t * src, uint32_t * dest, int width, int height );
HQX_API void HQX_CALLCONV hq2x_32_rb( uint32_t * src, uint32_t src_rowBytes, uint32_t * dest, uint32_t dest_rowBytes, int width, int height );
HQX_API void HQX_CALLCONV hq3x_32_rb( uint32_t * src, uint32_t src_rowBytes, uint32_t * dest, uint32_t dest_rowBytes, int width, int height );
HQX_API void HQX_CALLCONV hq4x_32_rb( uint32_t * src, uint32_t src_rowBytes, uint32_t * dest, uint32_t dest_rowBytes, int width, int height );
#endif

39
src/gl/hqnx/init.cpp Normal file
View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net)
*
* 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 "mystdint.h"
#include "hqx.h"
uint32_t *RGBtoYUV;
uint32_t YUV1, YUV2;
HQX_API void HQX_CALLCONV hqxInit(void)
{
/* Initalize RGB to YUV lookup table */
uint32_t c, r, g, b, y, u, v;
RGBtoYUV = new uint32_t[16777216];
for (c = 0; c < 16777215; c++) {
r = (c & 0xFF0000) >> 16;
g = (c & 0x00FF00) >> 8;
b = c & 0x0000FF;
y = (uint32_t)(0.299*r + 0.587*g + 0.114*b);
u = (uint32_t)(-0.169*r - 0.331*g + 0.5*b) + 128;
v = (uint32_t)(0.5*r - 0.419*g - 0.081*b) + 128;
RGBtoYUV[c] = (y << 16) + (u << 8) + v;
}
}

19
src/gl/hqnx/mystdint.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef __MYSTDINT_H
#define __MYSTDINT_H
#ifndef _MSC_VER
#include <stdint.h>
#else
typedef unsigned __int64 uint64_t;
typedef signed __int64 int64_t;
typedef unsigned __int32 uint32_t;
typedef signed __int32 int32_t;
typedef unsigned __int16 uint16_t;
typedef signed __int16 int16_t;
typedef unsigned __int8 uint8_t;
typedef signed __int8 int8_t;
#endif
#endif

2943
src/gl/hqnx_asm/hq2x_asm.cpp Normal file

File diff suppressed because it is too large Load diff

3843
src/gl/hqnx_asm/hq3x_asm.cpp Normal file

File diff suppressed because it is too large Load diff

5422
src/gl/hqnx_asm/hq4x_asm.cpp Normal file

File diff suppressed because it is too large Load diff

234
src/gl/hqnx_asm/hqnx_asm.h Normal file
View file

@ -0,0 +1,234 @@
//hqnx filter library
//----------------------------------------------------------
//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com )
//Copyright (C) 2009 Benjamin Berkels
//Copyright (C) 2012-2014 Alexey Lysiuk
//
//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__
#ifdef _MSC_VER
#pragma warning(disable:4799)
#endif // _MSC_VER
#include "hqnx_asm_Image.h"
// IMPORTANT NOTE!
// The following is not a generic vectorized math class
// Each member function or overloaded operator does specific task to simplify client code
// To re-implement this class for different platform you need check very carefully
// the Intel C++ Intrinsic Reference at http://software.intel.com/file/18072/
#if defined _MSC_VER && defined _M_X64
// Implementation via SSE2 intrinsics
// MSVC doesn't support MMX intrinsics on x64
#include <emmintrin.h>
class hq_vec
{
public:
hq_vec(const int value)
: m_value(_mm_cvtsi32_si128(value))
{
}
static hq_vec load(const int source)
{
return _mm_unpacklo_epi8(_mm_cvtsi32_si128(source), _mm_cvtsi32_si128(0));
}
static hq_vec expand(const short source)
{
return _mm_set_epi16(source, source, source, source, source, source, source, source);
}
void store(unsigned char* const destination) const
{
*reinterpret_cast<int*>(destination) = _mm_cvtsi128_si32(_mm_packus_epi16(m_value, _mm_cvtsi32_si128(0)));
}
static void reset()
{
}
hq_vec& operator+=(const hq_vec& right)
{
m_value = _mm_add_epi16(m_value, right.m_value);
return *this;
}
hq_vec& operator*=(const hq_vec& right)
{
m_value = _mm_mullo_epi16(m_value, right.m_value);
return *this;
}
hq_vec& operator<<(const int count)
{
m_value = _mm_sll_epi16(m_value, _mm_cvtsi32_si128(count));
return *this;
}
hq_vec& operator>>(const int count)
{
m_value = _mm_srl_epi16(m_value, _mm_cvtsi32_si128(count));
return *this;
}
private:
__m128i m_value;
hq_vec(const __m128i value)
: m_value(value)
{
}
friend hq_vec operator- (const hq_vec&, const hq_vec&);
friend hq_vec operator* (const hq_vec&, const hq_vec&);
friend hq_vec operator| (const hq_vec&, const hq_vec&);
friend bool operator!=(const int, const hq_vec&);
};
inline hq_vec operator-(const hq_vec& left, const hq_vec& right)
{
return _mm_subs_epu8(left.m_value, right.m_value);
}
inline hq_vec operator*(const hq_vec& left, const hq_vec& right)
{
return _mm_mullo_epi16(left.m_value, right.m_value);
}
inline hq_vec operator|(const hq_vec& left, const hq_vec& right)
{
return _mm_or_si128(left.m_value, right.m_value);
}
inline bool operator!=(const int left, const hq_vec& right)
{
return left != _mm_cvtsi128_si32(right.m_value);
}
#else // _M_X64
// Implementation via MMX intrinsics
#include <mmintrin.h>
class hq_vec
{
public:
hq_vec(const int value)
: m_value(_mm_cvtsi32_si64(value))
{
}
static hq_vec load(const int source)
{
return _mm_unpacklo_pi8(_mm_cvtsi32_si64(source), _mm_cvtsi32_si64(0));
}
static hq_vec expand(const short source)
{
return _mm_set_pi16(source, source, source, source);
}
void store(unsigned char* const destination) const
{
*reinterpret_cast<int*>(destination) = _mm_cvtsi64_si32(_mm_packs_pu16(m_value, _mm_cvtsi32_si64(0)));
}
static void reset()
{
_mm_empty();
}
hq_vec& operator+=(const hq_vec& right)
{
m_value = _mm_add_pi16(m_value, right.m_value);
return *this;
}
hq_vec& operator*=(const hq_vec& right)
{
m_value = _mm_mullo_pi16(m_value, right.m_value);
return *this;
}
hq_vec& operator<<(const int count)
{
m_value = _mm_sll_pi16(m_value, _mm_cvtsi32_si64(count));
return *this;
}
hq_vec& operator>>(const int count)
{
m_value = _mm_srl_pi16(m_value, _mm_cvtsi32_si64(count));
return *this;
}
private:
__m64 m_value;
hq_vec(const __m64 value)
: m_value(value)
{
}
friend hq_vec operator- (const hq_vec&, const hq_vec&);
friend hq_vec operator* (const hq_vec&, const hq_vec&);
friend hq_vec operator| (const hq_vec&, const hq_vec&);
friend bool operator!=(const int, const hq_vec&);
};
inline hq_vec operator-(const hq_vec& left, const hq_vec& right)
{
return _mm_subs_pu8(left.m_value, right.m_value);
}
inline hq_vec operator*(const hq_vec& left, const hq_vec& right)
{
return _mm_mullo_pi16(left.m_value, right.m_value);
}
inline hq_vec operator|(const hq_vec& left, const hq_vec& right)
{
return _mm_or_si64(left.m_value, right.m_value);
}
inline bool operator!=(const int left, const hq_vec& right)
{
return left != _mm_cvtsi64_si32(right.m_value);
}
#endif // _MSC_VER && _M_X64
namespace HQnX_asm
{
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__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,152 @@
//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 <stdio.h>
#pragma once
#ifdef _MSC_VER
#pragma warning(disable: 4103)
#endif // _MSC_VER
#pragma pack(1)
namespace HQnX_asm
{
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)
}

1013
src/gl/models/gl_models.cpp Normal file

File diff suppressed because it is too large Load diff

371
src/gl/models/gl_models.h Normal file
View file

@ -0,0 +1,371 @@
#ifndef __GL_MODELS_H_
#define __GL_MODELS_H_
#include "gl/utility/gl_geometric.h"
#include "gl/data/gl_vertexbuffer.h"
#include "p_pspr.h"
#include "r_data/voxels.h"
#define MAX_LODS 4
enum { VX, VZ, VY };
#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()
{
mVBuf = NULL;
}
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 frame2, double inter, int translation=0) = 0;
virtual void BuildVertexBuffer() = 0;
void DestroyVertexBuffer()
{
delete mVBuf;
mVBuf = NULL;
}
virtual float getAspectFactor() { return 1.f; }
FModelVertexBuffer *mVBuf;
FString mFileName;
};
class FDMDModel : public FModel
{
protected:
struct FTriangle
{
unsigned short vertexIndices[3];
unsigned short textureIndices[3];
};
struct DMDHeader
{
int magic;
int version;
int flags;
};
struct DMDModelVertex
{
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];
unsigned int vindex;
};
struct ModelFrameVertexData
{
DMDModelVertex *vertices;
DMDModelVertex *normals;
};
struct DMDLoDInfo
{
int numTriangles;
int numGlCommands;
int offsetTriangles;
int offsetGlCommands;
};
struct DMDLoD
{
FTriangle * triangles;
};
int mLumpNum;
DMDHeader header;
DMDInfo info;
FTexture ** skins;
ModelFrame * frames;
bool allowTexComp; // Allow texture compression with this.
// Temp data only needed for buffer construction
FTexCoord * texCoords;
ModelFrameVertexData *framevtx;
DMDLoDInfo lodInfo[MAX_LODS];
DMDLoD lods[MAX_LODS];
public:
FDMDModel()
{
mLumpNum = -1;
frames = NULL;
skins = NULL;
for (int i = 0; i < MAX_LODS; i++)
{
lods[i].triangles = NULL;
}
info.numLODs = 0;
texCoords = NULL;
framevtx = NULL;
}
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 frame2, double inter, int translation=0);
virtual void LoadGeometry();
void UnloadGeometry();
void BuildVertexBuffer();
};
// 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);
virtual void LoadGeometry();
};
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;
unsigned int vindex; // contains numframes arrays of vertices
unsigned int iindex;
MD3Surface()
{
tris=NULL;
vertices=NULL;
texcoords=NULL;
vindex = iindex = UINT_MAX;
}
~MD3Surface()
{
if (skins) delete [] skins;
UnloadGeometry();
}
void UnloadGeometry()
{
if (tris) delete [] tris;
if (vertices) delete [] vertices;
if (texcoords) delete [] texcoords;
tris = NULL;
vertices = NULL;
texcoords = NULL;
}
};
struct MD3Frame
{
// The bounding box information is of no use in the Doom engine
// That will still be done with the actor's size information.
char Name[16];
float origin[3];
};
int numFrames;
int numTags;
int numSurfaces;
int mLumpNum;
MD3Frame * frames;
MD3Surface * surfaces;
public:
FMD3Model() { }
virtual ~FMD3Model();
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 frame2, double inter, int translation=0);
void LoadGeometry();
void BuildVertexBuffer();
};
struct FVoxelVertexHash
{
// Returns the hash value for a key.
hash_t Hash(const FModelVertex &key)
{
int ix = xs_RoundToInt(key.x);
int iy = xs_RoundToInt(key.y);
int iz = xs_RoundToInt(key.z);
return (hash_t)(ix + (iy<<9) + (iz<<18));
}
// Compares two keys, returning zero if they are the same.
int Compare(const FModelVertex &left, const FModelVertex &right)
{
return left.x != right.x || left.y != right.y || left.z != right.z || left.u != right.u || left.v != right.v;
}
};
struct FIndexInit
{
void Init(unsigned int &value)
{
value = 0xffffffff;
}
};
typedef TMap<FModelVertex, unsigned int, FVoxelVertexHash, FIndexInit> FVoxelMap;
class FVoxelModel : public FModel
{
protected:
FVoxel *mVoxel;
bool mOwningVoxel; // if created through MODELDEF deleting this object must also delete the voxel object
FTexture *mPalette;
unsigned int mNumIndices;
TArray<FModelVertex> mVertices;
TArray<unsigned int> mIndices;
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);
unsigned int AddVertex(FModelVertex &vert, FVoxelMap &check);
public:
FVoxelModel(FVoxel *voxel, bool owned);
~FVoxelModel();
bool Load(const char * fn, int lumpnum, const char * buffer, int length);
void Initialize();
virtual int FindFrame(const char * name);
virtual void RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation=0);
FTexture *GetPaletteTexture() const { return mPalette; }
void BuildVertexBuffer();
float getAspectFactor();
};
#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;
FSpriteModelFrame * gl_FindModelFrame(const PClass * ti, int sprite, int frame, bool dropped);
void gl_RenderModel(GLSprite * spr);
// [BB] HUD weapon model rendering functions.
void gl_RenderHUDModel(pspdef_t *psp, fixed_t ofsx, fixed_t ofsy);
bool gl_IsHUDModelForPlayerAvailable (player_t * player);
#endif

View file

@ -0,0 +1,554 @@
/*
** 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_renderer.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"
#include "gl/data/gl_vertexbuffer.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);
}
//===========================================================================
//
// DMD file structure
//
//===========================================================================
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.
};
//===========================================================================
//
// FDMDModel::Load
//
//===========================================================================
bool FDMDModel::Load(const char * path, int lumpnum, const char * buffer, int length)
{
dmd_chunk_t * chunk = (dmd_chunk_t*)(buffer + 12);
char *temp;
ModelFrame *frame;
int i;
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);
memcpy(frame->name, pfr->name, sizeof(pfr->name));
frame->vindex = UINT_MAX;
}
mLumpNum = lumpnum;
return true;
}
//===========================================================================
//
// FDMDModel::LoadGeometry
//
//===========================================================================
void FDMDModel::LoadGeometry()
{
static int axis[3] = { VX, VY, VZ };
FMemLump lumpdata = Wads.ReadLump(mLumpNum);
const char *buffer = (const char *)lumpdata.GetMem();
texCoords = new FTexCoord[info.numTexCoords];
memcpy(texCoords, buffer + info.offsetTexCoords, info.numTexCoords * sizeof(FTexCoord));
const char *temp = buffer + info.offsetFrames;
framevtx= new ModelFrameVertexData[info.numFrames];
ModelFrameVertexData *framev;
int i, k, c;
for(i = 0, framev = framevtx; i < info.numFrames; i++, framev++)
{
dmd_packedFrame_t *pfr = (dmd_packedFrame_t *) (temp + info.frameSize * i);
dmd_packedVertex_t *pVtx;
framev->vertices = new DMDModelVertex[info.numVertices];
framev->normals = new DMDModelVertex[info.numVertices];
// Translate each vertex.
for(k = 0, pVtx = pfr->vertices; k < info.numVertices; k++, pVtx++)
{
UnpackVector((unsigned short)(pVtx->normal), framev->normals[k].xyz);
for(c = 0; c < 3; c++)
{
framev->vertices[k].xyz[axis[c]] =
(pVtx->vertex[c] * FLOAT(pfr->scale[c]) + FLOAT(pfr->translate[c]));
}
}
}
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].offsetTriangles = LittleLong(lodInfo[i].offsetTriangles);
if (lodInfo[i].numTriangles > 0)
{
lods[i].triangles = new FTriangle[lodInfo[i].numTriangles];
memcpy(lods[i].triangles, buffer + lodInfo[i].offsetTriangles, lodInfo[i].numTriangles * sizeof(FTriangle));
for (int j = 0; j < lodInfo[i].numTriangles; j++)
{
for (int k = 0; k < 3; k++)
{
lods[i].triangles[j].textureIndices[k] = LittleShort(lods[i].triangles[j].textureIndices[k]);
lods[i].triangles[j].vertexIndices[k] = LittleShort(lods[i].triangles[j].vertexIndices[k]);
}
}
}
}
}
//===========================================================================
//
// Deletes everything that's no longer needed after building the vertex buffer
//
//===========================================================================
void FDMDModel::UnloadGeometry()
{
int i;
if (framevtx != NULL)
{
for (i=0;i<info.numFrames;i++)
{
if (framevtx[i].vertices != NULL) delete [] framevtx[i].vertices;
if (framevtx[i].normals != NULL) delete [] framevtx[i].normals;
framevtx[i].vertices = NULL;
framevtx[i].normals = NULL;
}
delete[] framevtx;
framevtx = NULL;
}
for(i = 0; i < info.numLODs; i++)
{
if (lods[i].triangles != NULL) delete[] lods[i].triangles;
lods[i].triangles = NULL;
}
if (texCoords != NULL) delete[] texCoords;
texCoords = NULL;
}
//===========================================================================
//
//
//
//===========================================================================
FDMDModel::~FDMDModel()
{
UnloadGeometry();
// skins are managed by the texture manager so they must not be deleted here.
if (skins != NULL) delete [] skins;
if (frames != NULL) delete [] frames;
}
//===========================================================================
//
//
//
//===========================================================================
void FDMDModel::BuildVertexBuffer()
{
if (mVBuf == NULL)
{
LoadGeometry();
int VertexBufferSize = info.numFrames * lodInfo[0].numTriangles * 3;
unsigned int vindex = 0;
mVBuf = new FModelVertexBuffer(false);
FModelVertex *vertptr = mVBuf->LockVertexBuffer(VertexBufferSize);
for (int i = 0; i < info.numFrames; i++)
{
DMDModelVertex *vert = framevtx[i].vertices;
DMDModelVertex *norm = framevtx[i].normals;
frames[i].vindex = vindex;
FTriangle *tri = lods[0].triangles;
for (int i = 0; i < lodInfo[0].numTriangles; i++)
{
for (int j = 0; j < 3; j++)
{
int ti = tri->textureIndices[j];
int vi = tri->vertexIndices[j];
FModelVertex *bvert = &vertptr[vindex++];
bvert->Set(vert[vi].xyz[0], vert[vi].xyz[1], vert[vi].xyz[2], (float)texCoords[ti].s / info.skinWidth, (float)texCoords[ti].t / info.skinHeight);
bvert->SetNormal(norm[vi].xyz[0], norm[vi].xyz[1], norm[vi].xyz[2]);
}
tri++;
}
}
mVBuf->UnlockVertexBuffer();
UnloadGeometry();
}
}
//===========================================================================
//
// FDMDModel::FindFrame
//
//===========================================================================
int FDMDModel::FindFrame(const char * name)
{
for (int i=0;i<info.numFrames;i++)
{
if (!stricmp(name, frames[i].name)) return i;
}
return -1;
}
//===========================================================================
//
//
//
//===========================================================================
void FDMDModel::RenderFrame(FTexture * skin, int frameno, int frameno2, double inter, int translation)
{
if (frameno >= info.numFrames || frameno2 >= info.numFrames) return;
if (!skin)
{
if (info.numSkins == 0) return;
skin = skins[0];
if (!skin) return;
}
FMaterial * tex = FMaterial::ValidateTexture(skin, false);
gl_RenderState.SetMaterial(tex, CLAMP_NONE, translation, -1, false);
gl_RenderState.SetInterpolationFactor((float)inter);
gl_RenderState.Apply();
mVBuf->SetupFrame(frames[frameno].vindex, frames[frameno2].vindex);
glDrawArrays(GL_TRIANGLES, 0, lodInfo[0].numTriangles * 3);
gl_RenderState.SetInterpolationFactor(0.f);
}
//===========================================================================
//
// 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];
};
//===========================================================================
//
// FMD2Model::Load
//
//===========================================================================
bool FMD2Model::Load(const char * path, int lumpnum, const char * buffer, int length)
{
md2_header_t * md2header = (md2_header_t *)buffer;
ModelFrame *frame;
byte *md2_frames;
int i;
// Convert it to DMD.
header.magic = MD2_MAGIC;
header.version = 8;
header.flags = 0;
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;
}
skins = new FTexture *[info.numSkins];
for (i = 0; i < info.numSkins; i++)
{
skins[i] = LoadSkin(path, buffer + info.offsetSkins + i * 64);
}
// 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);
memcpy(frame->name, pfr->name, sizeof(pfr->name));
frame->vindex = UINT_MAX;
}
mLumpNum = lumpnum;
return true;
}
//===========================================================================
//
// FMD2Model::LoadGeometry
//
//===========================================================================
void FMD2Model::LoadGeometry()
{
static int axis[3] = { VX, VY, VZ };
byte *md2_frames;
FMemLump lumpdata = Wads.ReadLump(mLumpNum);
const char *buffer = (const char *)lumpdata.GetMem();
texCoords = new FTexCoord[info.numTexCoords];
memcpy(texCoords, (byte*)buffer + info.offsetTexCoords, info.numTexCoords * sizeof(FTexCoord));
md2_frames = (byte*)buffer + info.offsetFrames;
framevtx = new ModelFrameVertexData[info.numFrames];
ModelFrameVertexData *framev;
int i, k, c;
for(i = 0, framev = framevtx; i < info.numFrames; i++, framev++)
{
md2_packedFrame_t *pfr = (md2_packedFrame_t *) (md2_frames + info.frameSize * i);
md2_triangleVertex_t *pVtx;
framev->vertices = new DMDModelVertex[info.numVertices];
framev->normals = new DMDModelVertex[info.numVertices];
// Translate each vertex.
for(k = 0, pVtx = pfr->vertices; k < info.numVertices; k++, pVtx++)
{
memcpy(framev->normals[k].xyz,
avertexnormals[pVtx->lightNormalIndex], sizeof(float) * 3);
for(c = 0; c < 3; c++)
{
framev->vertices[k].xyz[axis[c]] =
(pVtx->vertex[c] * pfr->scale[c] + pfr->translate[c]);
}
}
}
lods[0].triangles = new FTriangle[lodInfo[0].numTriangles];
int cnt = lodInfo[0].numTriangles;
memcpy(lods[0].triangles, buffer + lodInfo[0].offsetTriangles, sizeof(FTriangle) * cnt);
for (int j = 0; j < cnt; j++)
{
for (int k = 0; k < 3; k++)
{
lods[0].triangles[j].textureIndices[k] = LittleShort(lods[0].triangles[j].textureIndices[k]);
lods[0].triangles[j].vertexIndices[k] = LittleShort(lods[0].triangles[j].vertexIndices[k]);
}
}
}
FMD2Model::~FMD2Model()
{
}

View file

@ -0,0 +1,375 @@
/*
** 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/renderer/gl_renderer.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
//===========================================================================
//
// decode the lat/lng normal to a 3 float normal
//
//===========================================================================
static void UnpackVector(unsigned short packed, float & nx, float & ny, float & nz)
{
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);
}
//===========================================================================
//
// MD3 File structure
//
//===========================================================================
#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()
//===========================================================================
//
//
//
//===========================================================================
bool FMD3Model::Load(const char * path, int lumpnum, const char * buffer, int length)
{
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; i < numFrames; i++)
{
strncpy(frames[i].Name, frm[i].Name, 16);
for (int j = 0; j < 3; j++) frames[i].origin[j] = frm[i].localorigin[j];
}
md3_surface_t * surf = (md3_surface_t*)(buffer + LittleLong(hdr->Ofs_Surfaces));
surfaces = new MD3Surface[numSurfaces];
for (int i = 0; i < numSurfaces; i++)
{
MD3Surface * s = &surfaces[i];
md3_surface_t * ss = surf;
surf = (md3_surface_t *)(((char*)surf) + LittleLong(surf->Ofs_End));
s->numSkins = LittleLong(ss->Num_Shaders);
s->numTriangles = LittleLong(ss->Num_Triangles);
s->numVertices = LittleLong(ss->Num_Verts);
// 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; i < s->numSkins; 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);
}
}
mLumpNum = lumpnum;
return true;
}
//===========================================================================
//
//
//
//===========================================================================
void FMD3Model::LoadGeometry()
{
FMemLump lumpdata = Wads.ReadLump(mLumpNum);
const char *buffer = (const char *)lumpdata.GetMem();
md3_header_t * hdr = (md3_header_t *)buffer;
md3_surface_t * surf = (md3_surface_t*)(buffer + LittleLong(hdr->Ofs_Surfaces));
for(int i=0;i<numSurfaces;i++)
{
MD3Surface * s = &surfaces[i];
md3_surface_t * ss = surf;
surf = (md3_surface_t *)(((char*)surf) + LittleLong(surf->Ofs_End));
// 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;i<s->numTriangles;i++) for (int j=0;j<3;j++)
{
s->tris[i].VertIndex[j]=LittleLong(tris[i].vt_index[j]);
}
// 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;i<s->numVertices;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;i<s->numVertices * 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;
UnpackVector( LittleShort(vt[i].n), s->vertices[i].nx, s->vertices[i].ny, s->vertices[i].nz);
}
}
}
//===========================================================================
//
//
//
//===========================================================================
void FMD3Model::BuildVertexBuffer()
{
if (mVBuf == NULL)
{
LoadGeometry();
unsigned int vbufsize = 0;
unsigned int ibufsize = 0;
for (int i = 0; i < numSurfaces; i++)
{
MD3Surface * surf = &surfaces[i];
vbufsize += numFrames * surf->numVertices;
ibufsize += 3 * surf->numTriangles;
}
mVBuf = new FModelVertexBuffer(true);
FModelVertex *vertptr = mVBuf->LockVertexBuffer(vbufsize);
unsigned int *indxptr = mVBuf->LockIndexBuffer(ibufsize);
assert(vertptr != NULL && indxptr != NULL);
unsigned int vindex = 0, iindex = 0;
for (int i = 0; i < numSurfaces; i++)
{
MD3Surface * surf = &surfaces[i];
surf->vindex = vindex;
surf->iindex = iindex;
for (int j = 0; j < numFrames * surf->numVertices; j++)
{
MD3Vertex* vert = surf->vertices + j;
FModelVertex *bvert = &vertptr[vindex++];
int tc = j % surf->numVertices;
bvert->Set(vert->x, vert->z, vert->y, surf->texcoords[tc].s, surf->texcoords[tc].t);
bvert->SetNormal(vert->nx, vert->nz, vert->ny);
}
for (int k = 0; k < surf->numTriangles; k++)
{
for (int l = 0; l < 3; l++)
{
indxptr[iindex++] = surf->tris[k].VertIndex[l];
}
}
surf->UnloadGeometry();
}
mVBuf->UnlockVertexBuffer();
mVBuf->UnlockIndexBuffer();
}
}
//===========================================================================
//
//
//
//===========================================================================
int FMD3Model::FindFrame(const char * name)
{
for (int i=0;i<numFrames;i++)
{
if (!stricmp(name, frames[i].Name)) return i;
}
return -1;
}
//===========================================================================
//
//
//
//===========================================================================
void FMD3Model::RenderFrame(FTexture * skin, int frameno, int frameno2, double inter, int translation)
{
if (frameno>=numFrames || frameno2>=numFrames) return;
gl_RenderState.SetInterpolationFactor((float)inter);
for(int i=0;i<numSurfaces;i++)
{
MD3Surface * surf = &surfaces[i];
// [BB] In case no skin is specified via MODELDEF, check if the MD3 has a skin for the current surface.
// Note: Each surface may have a different skin.
FTexture *surfaceSkin = skin;
if (!surfaceSkin)
{
if (surf->numSkins==0) return;
surfaceSkin = surf->skins[0];
if (!surfaceSkin) return;
}
FMaterial * tex = FMaterial::ValidateTexture(surfaceSkin, false);
gl_RenderState.SetMaterial(tex, CLAMP_NONE, translation, -1, false);
gl_RenderState.Apply();
mVBuf->SetupFrame(surf->vindex + frameno * surf->numVertices, surf->vindex + frameno2 * surf->numVertices);
glDrawElements(GL_TRIANGLES, surf->numTriangles * 3, GL_UNSIGNED_INT, (void*)(intptr_t)(surf->iindex * sizeof(unsigned int)));
}
gl_RenderState.SetInterpolationFactor(0.f);
}
//===========================================================================
//
//
//
//===========================================================================
FMD3Model::~FMD3Model()
{
if (frames) delete [] frames;
if (surfaces) delete [] surfaces;
frames = NULL;
surfaces = NULL;
}

449
src/gl/models/gl_voxels.cpp Normal file
View file

@ -0,0 +1,449 @@
/*
** 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 "colormatcher.h"
#include "textures/bitmap.h"
//#include "gl/gl_intern.h"
#include "gl/system/gl_interface.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;
BYTE *Pixels;
};
//===========================================================================
//
//
//
//===========================================================================
FVoxelTexture::FVoxelTexture(FVoxel *vox)
{
SourceVox = vox;
Width = 16;
Height = 16;
WidthBits = 4;
HeightBits = 4;
WidthMask = 15;
Pixels = NULL;
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 ()
{
// GetPixels gets called when a translated palette is used so we still need to implement it here.
if (Pixels == NULL)
{
Pixels = new BYTE[256];
BYTE *pp = SourceVox->Palette;
if(pp != NULL)
{
for(int i=0;i<256;i++, pp+=3)
{
PalEntry pe;
pe.r = (pp[0] << 2) | (pp[0] >> 4);
pe.g = (pp[1] << 2) | (pp[1] >> 4);
pe.b = (pp[2] << 2) | (pp[2] >> 4);
Pixels[i] = ColorMatcher.Pick(pe);
}
}
else
{
for(int i=0;i<256;i++, pp+=3)
{
Pixels[i] = (BYTE)i;
}
}
}
return Pixels;
}
void FVoxelTexture::Unload ()
{
if (Pixels != NULL)
{
delete[] Pixels;
Pixels = NULL;
}
}
//===========================================================================
//
// 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;
if(pp != NULL)
{
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;
}
}
else
{
for(int i=0;i<256;i++, pp+=3)
{
bitmap[i] = (BYTE)i;
pe[i] = GPalette.BaseColors[i];
pe[i].a = 255;
}
}
bmp->CopyPixelData(x, y, bitmap, Width, Height, 1, 16, rotate, pe, inf);
return 0;
}
//===========================================================================
//
//
//
//===========================================================================
FVoxelModel::FVoxelModel(FVoxel *voxel, bool owned)
{
mVoxel = voxel;
mOwningVoxel = owned;
mPalette = new FVoxelTexture(voxel);
}
//===========================================================================
//
//
//
//===========================================================================
FVoxelModel::~FVoxelModel()
{
delete mPalette;
if (mOwningVoxel) delete mVoxel;
}
//===========================================================================
//
//
//
//===========================================================================
unsigned int FVoxelModel::AddVertex(FModelVertex &vert, FVoxelMap &check)
{
unsigned int index = check[vert];
if (index == 0xffffffff)
{
index = check[vert] =mVertices.Push(vert);
}
return 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;
FModelVertex vert;
unsigned int indx[4];
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;
indx[0] = AddVertex(vert, check);
vert.x = x2 - PivotX;
vert.z = -y2 + PivotY;
vert.y = -z2 + PivotZ;
indx[1] = AddVertex(vert, check);
vert.x = x4 - PivotX;
vert.z = -y4 + PivotY;
vert.y = -z4 + PivotZ;
indx[2] = AddVertex(vert, check);
vert.x = x3 - PivotX;
vert.z = -y3 + PivotY;
vert.y = -z3 + PivotZ;
indx[3] = AddVertex(vert, check);
mIndices.Push(indx[0]);
mIndices.Push(indx[1]);
mIndices.Push(indx[3]);
mIndices.Push(indx[1]);
mIndices.Push(indx[3]);
mIndices.Push(indx[2]);
}
//===========================================================================
//
//
//
//===========================================================================
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);
}
}
}
}
//===========================================================================
//
//
//
//===========================================================================
void FVoxelModel::BuildVertexBuffer()
{
if (mVBuf == NULL)
{
Initialize();
mVBuf = new FModelVertexBuffer(true);
FModelVertex *vertptr = mVBuf->LockVertexBuffer(mVertices.Size());
unsigned int *indxptr = mVBuf->LockIndexBuffer(mIndices.Size());
memcpy(vertptr, &mVertices[0], sizeof(FModelVertex)* mVertices.Size());
memcpy(indxptr, &mIndices[0], sizeof(unsigned int)* mIndices.Size());
mVBuf->UnlockVertexBuffer();
mVBuf->UnlockIndexBuffer();
mNumIndices = mIndices.Size();
// delete our temporary buffers
mVertices.Clear();
mIndices.Clear();
mVertices.ShrinkToFit();
mIndices.ShrinkToFit();
}
}
//===========================================================================
//
//
//
//===========================================================================
bool FVoxelModel::Load(const char * fn, int lumpnum, const char * buffer, int length)
{
return false; // not needed
}
//===========================================================================
//
// Voxels don't have frames so always return 0
//
//===========================================================================
int FVoxelModel::FindFrame(const char * name)
{
return 0;
}
//===========================================================================
//
// Voxels need aspect ratio correction according to the current map's setting
//
//===========================================================================
float FVoxelModel::getAspectFactor()
{
return glset.pixelstretch;
}
//===========================================================================
//
// Voxels never interpolate between frames, they only have one.
//
//===========================================================================
void FVoxelModel::RenderFrame(FTexture * skin, int frame, int frame2, double inter, int translation)
{
FMaterial * tex = FMaterial::ValidateTexture(skin, false);
gl_RenderState.SetMaterial(tex, CLAMP_NOFILTER, translation, -1, false);
gl_RenderState.Apply();
mVBuf->SetupFrame(0, 0);
glDrawElements(GL_TRIANGLES, mNumIndices, GL_UNSIGNED_INT, (void*)(intptr_t)0);
}

489
src/gl/models/tab_anorms.h Normal file
View file

@ -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},

View file

@ -0,0 +1,76 @@
#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_FIRSTSPECIALCOLORMAP, // first special fixed colormap
CM_FOGLAYER = 0x10000000, // Sprite shaped fog layer
// 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 desaturation;
int blendfactor;
void Clear()
{
LightColor=0xffffff;
FadeColor=0;
desaturation = 0;
blendfactor=0;
}
void ClearColor()
{
LightColor.r=LightColor.g=LightColor.b=0xff;
blendfactor=0;
desaturation = 0;
}
FColormap & operator=(FDynamicColormap * from)
{
LightColor = from->Color;
desaturation = from->Desaturate;
FadeColor = from->Fade;
blendfactor = from->Color.a;
return * this;
}
void CopyLightColor(FDynamicColormap * from)
{
LightColor = from->Color;
desaturation = from->Desaturate;
blendfactor = from->Color.a;
}
void Decolorize() // this for 'nocoloredspritelighting' and not the same as desaturation. The normal formula results in a value that's too dark.
{
int v = (LightColor.r + LightColor.g + LightColor.b) / 3;
LightColor.r = LightColor.g = LightColor.b = (255 + v + v) / 3;
}
};
#endif

View file

@ -0,0 +1,559 @@
/*
** 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_interface.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);
CUSTOM_CVAR(Bool, gl_enhanced_nightvision, true, CVAR_ARCHIVE|CVAR_NOINITCALL)
{
// The fixed colormap state needs to be reset because if this happens when
// a shader is set to CM_LITE or CM_TORCH it won't register the change in behavior caused by this CVAR.
if (GLRenderer != NULL && GLRenderer->mShaderManager != NULL)
{
GLRenderer->mShaderManager->ResetFixedColormap();
}
}
CVAR(Bool, gl_brightfog, false, CVAR_ARCHIVE);
//==========================================================================
//
// 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;
}
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 (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_RedIsAlpha)
{
texturemode = TM_REDTOALPHA;
}
else if (style.Flags & STYLEF_ColorIsFixed)
{
texturemode = TM_MASK;
}
else if (style.Flags & STYLEF_InvertSource)
{
// The only place where InvertSource is used is for inverted sprites with the infrared powerup.
texturemode = TM_INVERSE;
}
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<gl_light_ambient && glset.lightmode != 8) // ambient clipping only if not using software lighting model.
{
light = gl_light_ambient;
if (rellight<0) rellight>>=1;
}
return clamp(light+rellight, 0, 255);
}
//==========================================================================
//
// Get current light color
//
//==========================================================================
static PalEntry gl_CalcLightColor(int light, PalEntry pe, int blendfactor)
{
int r,g,b;
if (glset.lightmode == 8)
{
return pe;
}
else if (blendfactor == 0)
{
r = pe.r * light / 255;
g = pe.g * light / 255;
b = pe.b * light / 255;
}
else
{
// This is what Legacy does with colored light in 3D volumes. No, it doesn't really make sense...
// It also doesn't translate well to software style lighting.
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(255, BYTE(r), BYTE(g), BYTE(b));
}
//==========================================================================
//
// set current light color
//
//==========================================================================
void gl_SetColor(int sectorlightlevel, int rellight, const FColormap &cm, float alpha, bool weapon)
{
if (gl_fixedcolormap != CM_DEFAULT)
{
gl_RenderState.SetColorAlpha(0xffffff, alpha, 0);
gl_RenderState.SetSoftLightLevel(255);
}
else
{
int hwlightlevel = gl_CalcLightLevel(sectorlightlevel, rellight, weapon);
PalEntry pe = gl_CalcLightColor(hwlightlevel, cm.LightColor, cm.blendfactor);
gl_RenderState.SetColorAlpha(pe, alpha, cm.desaturation);
gl_RenderState.SetSoftLightLevel(gl_ClampLight(sectorlightlevel + rellight));
}
}
//==========================================================================
//
// 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][gl_ClampLight(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<int>(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
//
// Requirements for a fog boundary:
// - front sector has no fog
// - back sector has fog
// - at least one of both does not have a sky ceiling.
//
//==========================================================================
bool gl_CheckFog(sector_t *frontsector, sector_t *backsector)
{
if (gl_fixedcolormap) return false;
if (frontsector == backsector) return false; // there can't be a boundary if both sides are in the same sector.
// Check for fog boundaries. This needs a few more checks for the sectors
PalEntry fogcolor = frontsector->ColorMap->Fade;
if ((fogcolor.d & 0xffffff) == 0)
{
return false;
}
else if (outsidefogdensity != 0 && outsidefogcolor.a!=0xff && (fogcolor.d & 0xffffff) == (outsidefogcolor.d & 0xffffff))
{
}
else if (fogdensity!=0 || (glset.lightmode & 4))
{
// case 3: level has fog density set
}
else
{
// case 4: use light level
if (frontsector->lightlevel >= 248) return false;
}
fogcolor = backsector->ColorMap->Fade;
if ((fogcolor.d & 0xffffff) == 0)
{
}
else if (outsidefogdensity != 0 && outsidefogcolor.a!=0xff && (fogcolor.d & 0xffffff) == (outsidefogcolor.d & 0xffffff))
{
return false;
}
else if (fogdensity!=0 || (glset.lightmode & 4))
{
// case 3: level has fog density set
return false;
}
else
{
// case 4: use light level
if (backsector->lightlevel < 248) return false;
}
// in all other cases this might create more problems than it solves.
return ((frontsector->GetTexture(sector_t::ceiling)!=skyflatnum ||
backsector->GetTexture(sector_t::ceiling)!=skyflatnum));
}
//==========================================================================
//
// Lighting stuff
//
//==========================================================================
void gl_SetShaderLight(float level, float olight)
{
const float MAXDIST = 256.f;
const float THRESHOLD = 96.f;
const float FACTOR = 0.75f;
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;
}
gl_RenderState.EnableFog(true);
gl_RenderState.SetFog(fogcolor, fogdensity);
// Korshun: fullbright fog like in software renderer.
if (glset.lightmode == 8 && glset.brightfog && fogdensity != 0 && fogcolor != 0)
{
gl_RenderState.SetSoftLightLevel(255);
}
}
}
//==========================================================================
//
// For testing sky fog sheets
//
//==========================================================================
CCMD(skyfog)
{
if (argv.argc()>1)
{
skyfog=strtol(argv[1],NULL,0);
}
}

View file

@ -0,0 +1,52 @@
#ifndef __GL_LIGHTDATA
#define __GL_LIGHTDATA
#include "v_palette.h"
#include "r_data/renderstyle.h"
#include "gl/renderer/gl_colormap.h"
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);
void gl_SetColor(int light, int rellight, const FColormap &cm, float alpha, 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);
}
void gl_DeleteAllAttachedLights();
void gl_RecreateAllAttachedLights();
extern int fogdensity;
extern int outsidefogdensity;
extern int skyfog;
#endif

View file

@ -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_interface.h"
#include "gl/system/gl_framebuffer.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/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/textures/gl_samplers.h"
#include "gl/utility/gl_clock.h"
#include "gl/utility/gl_templates.h"
#include "gl/models/gl_models.h"
#include "gl/dynlights/gl_lightbuffer.h"
//===========================================================================
//
// Renderer interface
//
//===========================================================================
//-----------------------------------------------------------------------------
//
// Initialize
//
//-----------------------------------------------------------------------------
FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb)
{
framebuffer = fb;
mCurrentPortal = NULL;
mMirrorCount = 0;
mPlaneMirrorCount = 0;
mLightCount = 0;
mAngles = FRotator(0,0,0);
mViewVector = FVector2(0,0);
mVBO = NULL;
mSkyVBO = NULL;
gl_spriteindex = 0;
mShaderManager = NULL;
glpart2 = glpart = mirrortexture = NULL;
mLights = NULL;
}
void gl_LoadModels();
void gl_FlushModels();
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);
mVBO = new FFlatVertexBuffer;
mSkyVBO = new FSkyVertexBuffer;
mLights = new FLightBuffer();
gl_RenderState.SetVertexBuffer(mVBO);
mFBID = 0;
SetupLevel();
mShaderManager = new FShaderManager;
mSamplerManager = new FSamplerManager;
gl_LoadModels();
}
FGLRenderer::~FGLRenderer()
{
gl_FlushModels();
gl_DeleteAllAttachedLights();
FMaterial::FlushAll();
if (mShaderManager != NULL) delete mShaderManager;
if (mSamplerManager != NULL) delete mSamplerManager;
if (mVBO != NULL) delete mVBO;
if (mSkyVBO != NULL) delete mSkyVBO;
if (mLights != NULL) delete mLights;
if (glpart2) delete glpart2;
if (glpart) delete glpart;
if (mirrortexture) delete mirrortexture;
if (mFBID != 0) glDeleteFramebuffers(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 (mFBID == 0) glGenFramebuffers(1, &mFBID);
glBindFramebuffer(GL_FRAMEBUFFER, mFBID);
return true;
}
//===========================================================================
//
//
//
//===========================================================================
void FGLRenderer::EndOffscreen()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
//===========================================================================
//
//
//
//===========================================================================
unsigned char *FGLRenderer::GetTextureBuffer(FTexture *tex, int &w, int &h)
{
FMaterial * gltex = FMaterial::ValidateTexture(tex, false);
if (gltex)
{
return gltex->CreateTexBuffer(0, w, h);
}
return NULL;
}
//===========================================================================
//
//
//
//===========================================================================
void FGLRenderer::ClearBorders()
{
OpenGLFrameBuffer *glscreen = static_cast<OpenGLFrameBuffer*>(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;
glViewport(0, 0, width, trueHeight);
gl_RenderState.mProjectionMatrix.loadIdentity();
gl_RenderState.mProjectionMatrix.ortho(0.0f, width * 1.0f, 0.0f, trueHeight, -1.0f, 1.0f);
gl_RenderState.SetColor(0.f ,0.f ,0.f ,1.f);
gl_RenderState.Set2DMode(true);
gl_RenderState.EnableTexture(false);
gl_RenderState.Apply();
gl_RenderState.ApplyMatrices();
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(0, borderHeight, 0, 0, 0); ptr++;
ptr->Set(0, 0, 0, 0, 0); ptr++;
ptr->Set(width, 0, 0, 0, 0); ptr++;
ptr->Set(width, borderHeight, 0, 0, 0); ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
ptr->Set(0, trueHeight, 0, 0, 0); ptr++;
ptr->Set(0, trueHeight - borderHeight, 0, 0, 0); ptr++;
ptr->Set(width, trueHeight - borderHeight, 0, 0, 0); ptr++;
ptr->Set(width, trueHeight, 0, 0, 0); ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
gl_RenderState.EnableTexture(true);
glViewport(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;
int light = 255;
FMaterial * gltex = FMaterial::ValidateTexture(img, false);
if (parms.colorOverlay && (parms.colorOverlay & 0xffffff) == 0)
{
// Right now there's only black. Should be implemented properly later
light = 255 - APART(parms.colorOverlay);
parms.colorOverlay = 0;
}
gl_SetRenderStyle(parms.style, !parms.masked, false);
if (!img->bHasCanvas)
{
int translation = 0;
if (!parms.alphaChannel)
{
if (parms.remap != NULL && !parms.remap->Inactive)
{
GLTranslationPalette * pal = static_cast<GLTranslationPalette*>(parms.remap->GetNative());
if (pal) translation = -pal->GetIndex();
}
}
gl_RenderState.SetMaterial(gltex, CLAMP_XY_NOMIP, translation, 0, !!(parms.style.Flags & STYLEF_RedIsAlpha));
u1 = gltex->GetUL();
v1 = gltex->GetVT();
u2 = gltex->GetUR();
v2 = gltex->GetVB();
}
else
{
gl_RenderState.SetMaterial(gltex, CLAMP_XY_NOMIP, 0, -1, false);
u1 = 0.f;
v1 = 1.f;
u2 = 1.f;
v2 = 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);
}
PalEntry color;
if (parms.style.Flags & STYLEF_ColorIsFixed)
{
color = parms.fillcolor;
}
else
{
color = PalEntry(light, light, light);
}
color.a = Scale(parms.alpha, 255, FRACUNIT);
// 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;
glEnable(GL_SCISSOR_TEST);
int space = (static_cast<OpenGLFrameBuffer*>(screen)->GetTrueHeight()-screen->GetHeight())/2;
glScissor(parms.lclip, btm - parms.dclip + space, parms.rclip - parms.lclip, parms.dclip - parms.uclip);
gl_RenderState.SetColor(color);
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
gl_RenderState.Apply();
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(x, y, 0, u1, v1); ptr++;
ptr->Set(x, y + h, 0, u1, v2); ptr++;
ptr->Set(x + w, y, 0, u2, v1); ptr++;
ptr->Set(x + w, y + h, 0, u2, v2); ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
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.SetColor(PalEntry(parms.colorOverlay));
gl_RenderState.Apply();
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(x, y, 0, u1, v1); ptr++;
ptr->Set(x, y + h, 0, u1, v2); ptr++;
ptr->Set(x + w, y, 0, u2, v1); ptr++;
ptr->Set(x + w, y + h, 0, u2, v2); ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
}
glScissor(0, 0, screen->GetWidth(), screen->GetHeight());
glDisable(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.SetColorAlpha(p, 1.f);
gl_RenderState.Apply();
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(x1, y1, 0, 0, 0); ptr++;
ptr->Set(x2, y2, 0, 0, 0); ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_LINES);
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.SetColorAlpha(p, 1.f);
gl_RenderState.Apply();
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(x1, y1, 0, 0, 0); ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_POINTS);
gl_RenderState.EnableTexture(true);
}
//===========================================================================
//
//
//
//===========================================================================
void FGLRenderer::Dim(PalEntry color, float damount, int x1, int y1, int w, int h)
{
gl_RenderState.EnableTexture(false);
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl_RenderState.AlphaFunc(GL_GREATER,0);
gl_RenderState.SetColorAlpha(color, damount);
gl_RenderState.Apply();
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(x1, y1, 0, 0, 0); ptr++;
ptr->Set(x1, y1+h, 0, 0, 0); ptr++;
ptr->Set(x1+w, y1+h, 0, 0, 0); ptr++;
ptr->Set(x1+w, y1, 0, 0, 0); ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_FAN);
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, false);
if (!gltexture) return;
gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false);
// 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.ResetColor();
gl_RenderState.Apply();
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(left, top, 0, fU1, fV1); ptr++;
ptr->Set(left, bottom, 0, fU1, fV2); ptr++;
ptr->Set(right, top, 0, fU2, fV1); ptr++;
ptr->Set(right, bottom, 0, fU2, fV2); ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
}
//==========================================================================
//
//
//
//==========================================================================
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<OpenGLFrameBuffer*>(screen)->GetTrueHeight()-screen->GetHeight())/2; // ugh...
rt += space;
/*
if (!m_windowed && (m_trueHeight != m_height))
{
offY = (m_trueHeight - m_height) / 2;
rt += offY;
}
*/
glEnable(GL_SCISSOR_TEST);
glScissor(left, rt - height, width, height);
glClearColor(p.r/255.0f, p.g/255.0f, p.b/255.0f, 0.f);
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0.f, 0.f, 0.f, 0.f);
glDisable(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, false);
if (gltexture == NULL)
{
return;
}
FColormap cm;
cm = colormap;
// We cannot use the software light mode here because it doesn't properly calculate the light for 2D rendering.
SBYTE savedlightmode = glset.lightmode;
if (glset.lightmode == 8) glset.lightmode = 0;
gl_SetColor(lightlevel, 0, cm, 1.f);
glset.lightmode = savedlightmode;
gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false);
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();
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
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;
}
ptr->Set(points[i].X, points[i].Y, 0, u*uscale, v*vscale);
ptr++;
}
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_FAN);
}

View file

@ -0,0 +1,168 @@
#ifndef __GL_RENDERER_H
#define __GL_RENDERER_H
#include "r_defs.h"
#include "v_video.h"
#include "vectors.h"
#include "r_renderer.h"
#include "gl/data/gl_matrix.h"
struct particle_t;
class FCanvasTexture;
class FFlatVertexBuffer;
class FSkyVertexBuffer;
class OpenGLFrameBuffer;
struct FDrawInfo;
struct pspdef_t;
class FShaderManager;
class GLPortal;
class FLightBuffer;
class FSamplerManager;
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;
FSamplerManager *mSamplerManager;
int gl_spriteindex;
unsigned int mFBID;
FTexture *glpart2;
FTexture *glpart;
FTexture *mirrortexture;
float mSky1Pos, mSky2Pos;
FRotator mAngles;
FVector2 mViewVector;
FFlatVertexBuffer *mVBO;
FSkyVertexBuffer *mSkyVBO;
FLightBuffer *mLights;
FGLRenderer(OpenGLFrameBuffer *fb);
~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 SetViewAngle(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, bool hudModelStep, int OverrideShader, bool alphatexture);
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 SetProjection(VSMatrix matrix); // raw matrix input from stereo 3d modes
void SetViewMatrix(fixed_t viewx, fixed_t viewy, fixed_t viewz, 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

View file

@ -0,0 +1,305 @@
/*
** 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 "templates.h"
#include "gl/system/gl_system.h"
#include "gl/system/gl_interface.h"
#include "gl/data/gl_data.h"
#include "gl/data/gl_vertexbuffer.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"
#include "gl/dynlights//gl_lightbuffer.h"
void gl_SetTextureMode(int type);
FRenderState gl_RenderState;
CVAR(Bool, gl_direct_state_change, true, 0)
static VSMatrix identityMatrix(1);
TArray<VSMatrix> gl_MatrixStack;
//==========================================================================
//
//
//
//==========================================================================
void FRenderState::Reset()
{
mTextureEnabled = true;
mBrightmapEnabled = mFogEnabled = mGlowEnabled = false;
mColorMask[0] = mColorMask[1] = mColorMask[2] = mColorMask[3] = true;
currentColorMask[0] = currentColorMask[1] = currentColorMask[2] = currentColorMask[3] = true;
mFogColor.d = -1;
mTextureMode = -1;
mLightIndex = -1;
mDesaturation = 0;
mSrcBlend = GL_SRC_ALPHA;
mDstBlend = GL_ONE_MINUS_SRC_ALPHA;
mAlphaThreshold = 0.5f;
mBlendEquation = GL_FUNC_ADD;
mObjectColor = 0xffffffff;
m2D = true;
mVertexBuffer = mCurrentVertexBuffer = NULL;
mColormapState = CM_DEFAULT;
mLightParms[3] = -1.f;
mSpecialEffect = EFF_NONE;
mClipHeightTop = 65536.f;
mClipHeightBottom = -65536.f;
ClearClipSplit();
stSrcBlend = stDstBlend = -1;
stBlendEquation = -1;
stAlphaThreshold = -1.f;
mLastDepthClamp = true;
}
//==========================================================================
//
// Apply shader settings
//
//==========================================================================
bool FRenderState::ApplyShader()
{
if (mSpecialEffect > EFF_NONE)
{
activeShader = GLRenderer->mShaderManager->BindEffect(mSpecialEffect);
}
else
{
activeShader = GLRenderer->mShaderManager->Get(mTextureEnabled ? mEffectState : 4, mAlphaThreshold >= 0.f);
activeShader->Bind();
}
int fogset = 0;
if (mFogEnabled)
{
if ((mFogColor & 0xffffff) == 0)
{
fogset = gl_fogmode;
}
else
{
fogset = -gl_fogmode;
}
}
glVertexAttrib4fv(VATTR_COLOR, mColor.vec);
activeShader->muDesaturation.Set(mDesaturation / 255.f);
activeShader->muFogEnabled.Set(fogset);
activeShader->muTextureMode.Set(mTextureMode);
activeShader->muCameraPos.Set(mCameraPos.vec);
activeShader->muLightParms.Set(mLightParms);
activeShader->muFogColor.Set(mFogColor);
activeShader->muObjectColor.Set(mObjectColor);
activeShader->muDynLightColor.Set(mDynColor.vec);
activeShader->muInterpolationFactor.Set(mInterpolationFactor);
activeShader->muClipHeightTop.Set(mClipHeightTop);
activeShader->muClipHeightBottom.Set(mClipHeightBottom);
activeShader->muTimer.Set(gl_frameMS * mShaderTimer / 1000.f);
activeShader->muAlphaThreshold.Set(mAlphaThreshold);
activeShader->muLightIndex.Set(mLightIndex); // will always be -1 for now
activeShader->muClipSplit.Set(mClipSplit);
if (mGlowEnabled)
{
activeShader->muGlowTopColor.Set(mGlowTop.vec);
activeShader->muGlowBottomColor.Set(mGlowBottom.vec);
activeShader->muGlowTopPlane.Set(mGlowTopPlane.vec);
activeShader->muGlowBottomPlane.Set(mGlowBottomPlane.vec);
activeShader->currentglowstate = 1;
}
else if (activeShader->currentglowstate)
{
// if glowing is on, disable it.
static const float nulvec[] = { 0.f, 0.f, 0.f, 0.f };
activeShader->muGlowTopColor.Set(nulvec);
activeShader->muGlowBottomColor.Set(nulvec);
activeShader->muGlowTopPlane.Set(nulvec);
activeShader->muGlowBottomPlane.Set(nulvec);
activeShader->currentglowstate = 0;
}
if (mColormapState != activeShader->currentfixedcolormap)
{
float r, g, b;
activeShader->currentfixedcolormap = mColormapState;
if (mColormapState == CM_DEFAULT)
{
activeShader->muFixedColormap.Set(0);
}
else if (mColormapState < CM_MAXCOLORMAP)
{
FSpecialColormap *scm = &SpecialColormaps[gl_fixedcolormap - CM_FIRSTSPECIALCOLORMAP];
float m[] = { scm->ColorizeEnd[0] - scm->ColorizeStart[0],
scm->ColorizeEnd[1] - scm->ColorizeStart[1], scm->ColorizeEnd[2] - scm->ColorizeStart[2], 0.f };
activeShader->muFixedColormap.Set(1);
activeShader->muColormapStart.Set(scm->ColorizeStart[0], scm->ColorizeStart[1], scm->ColorizeStart[2], 0.f);
activeShader->muColormapRange.Set(m);
}
else if (mColormapState == CM_FOGLAYER)
{
activeShader->muFixedColormap.Set(3);
}
else if (mColormapState == CM_LITE)
{
if (gl_enhanced_nightvision)
{
r = 0.375f, g = 1.0f, b = 0.375f;
}
else
{
r = g = b = 1.f;
}
activeShader->muFixedColormap.Set(2);
activeShader->muColormapStart.Set(r, g, b, 1.f);
}
else if (mColormapState >= CM_TORCH)
{
int flicker = mColormapState - CM_TORCH;
r = (0.8f + (7 - flicker) / 70.0f);
if (r > 1.0f) r = 1.0f;
b = g = r;
if (gl_enhanced_nightvision) b = g * 0.75f;
activeShader->muFixedColormap.Set(2);
activeShader->muColormapStart.Set(r, g, b, 1.f);
}
}
if (mTextureMatrixEnabled)
{
mTextureMatrix.matrixToGL(activeShader->texturematrix_index);
activeShader->currentTextureMatrixState = true;
}
else if (activeShader->currentTextureMatrixState)
{
activeShader->currentTextureMatrixState = false;
identityMatrix.matrixToGL(activeShader->texturematrix_index);
}
if (mModelMatrixEnabled)
{
mModelMatrix.matrixToGL(activeShader->modelmatrix_index);
activeShader->currentModelMatrixState = true;
}
else if (activeShader->currentModelMatrixState)
{
activeShader->currentModelMatrixState = false;
identityMatrix.matrixToGL(activeShader->modelmatrix_index);
}
return true;
}
//==========================================================================
//
// Apply State
//
//==========================================================================
void FRenderState::Apply()
{
if (!gl_direct_state_change)
{
if (mSrcBlend != stSrcBlend || mDstBlend != stDstBlend)
{
stSrcBlend = mSrcBlend;
stDstBlend = mDstBlend;
glBlendFunc(mSrcBlend, mDstBlend);
}
if (mBlendEquation != stBlendEquation)
{
stBlendEquation = mBlendEquation;
glBlendEquation(mBlendEquation);
}
}
//ApplyColorMask(); I don't think this is needed.
if (mVertexBuffer != mCurrentVertexBuffer)
{
if (mVertexBuffer == NULL) glBindBuffer(GL_ARRAY_BUFFER, 0);
else mVertexBuffer->BindVBO();
mCurrentVertexBuffer = mVertexBuffer;
}
ApplyShader();
}
void FRenderState::ApplyColorMask()
{
if ((mColorMask[0] != currentColorMask[0]) ||
(mColorMask[1] != currentColorMask[1]) ||
(mColorMask[2] != currentColorMask[2]) ||
(mColorMask[3] != currentColorMask[3]))
{
glColorMask(mColorMask[0], mColorMask[1], mColorMask[2], mColorMask[3]);
currentColorMask[0] = mColorMask[0];
currentColorMask[1] = mColorMask[1];
currentColorMask[2] = mColorMask[2];
currentColorMask[3] = mColorMask[3];
}
}
void FRenderState::ApplyMatrices()
{
if (GLRenderer->mShaderManager != NULL)
{
GLRenderer->mShaderManager->ApplyMatrices(&mProjectionMatrix, &mViewMatrix);
}
}
void FRenderState::ApplyLightIndex(int index)
{
if (GLRenderer->mLights->GetBufferType() == GL_UNIFORM_BUFFER && index > -1)
{
index = GLRenderer->mLights->BindUBO(index);
}
activeShader->muLightIndex.Set(index);
}

View file

@ -0,0 +1,384 @@
#ifndef __GL_RENDERSTATE_H
#define __GL_RENDERSTATE_H
#include <string.h>
#include "gl/system/gl_interface.h"
#include "gl/data/gl_data.h"
#include "gl/data/gl_matrix.h"
#include "gl/textures/gl_material.h"
#include "c_cvars.h"
#include "r_defs.h"
#include "r_data/r_translate.h"
class FVertexBuffer;
class FShader;
extern TArray<VSMatrix> gl_MatrixStack;
EXTERN_CVAR(Bool, gl_direct_state_change)
struct FStateVec4
{
float vec[4];
void Set(float r, float g, float b, float a)
{
vec[0] = r;
vec[1] = g;
vec[2] = b;
vec[3] = a;
}
};
enum EEffect
{
EFF_NONE=-1,
EFF_FOGBOUNDARY,
EFF_SPHEREMAP,
EFF_BURN,
EFF_STENCIL,
MAX_EFFECTS
};
class FRenderState
{
bool mTextureEnabled;
bool mFogEnabled;
bool mGlowEnabled;
bool mBrightmapEnabled;
bool mColorMask[4];
bool currentColorMask[4];
int mLightIndex;
int mSpecialEffect;
int mTextureMode;
int mDesaturation;
int mSoftLight;
float mLightParms[4];
int mSrcBlend, mDstBlend;
float mAlphaThreshold;
int mBlendEquation;
bool mAlphaTest;
bool m2D;
bool mModelMatrixEnabled;
bool mTextureMatrixEnabled;
float mInterpolationFactor;
float mClipHeightTop, mClipHeightBottom;
float mShaderTimer;
bool mLastDepthClamp;
FVertexBuffer *mVertexBuffer, *mCurrentVertexBuffer;
FStateVec4 mColor;
FStateVec4 mCameraPos;
FStateVec4 mGlowTop, mGlowBottom;
FStateVec4 mGlowTopPlane, mGlowBottomPlane;
PalEntry mFogColor;
PalEntry mObjectColor;
FStateVec4 mDynColor;
float mClipSplit[2];
int mEffectState;
int mColormapState;
float stAlphaThreshold;
int stSrcBlend, stDstBlend;
bool stAlphaTest;
int stBlendEquation;
FShader *activeShader;
bool ApplyShader();
public:
VSMatrix mProjectionMatrix;
VSMatrix mViewMatrix;
VSMatrix mModelMatrix;
VSMatrix mTextureMatrix;
FRenderState()
{
Reset();
}
void Reset();
void SetMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader, bool alphatexture)
{
// textures without their own palette are a special case for use as an alpha texture:
// They use the color index directly as an alpha value instead of using the palette's red.
// To handle this case, we need to set a special translation for such textures.
if (alphatexture)
{
if (mat->tex->UseBasePalette()) translation = TRANSLATION(TRANSLATION_Standard, 8);
}
mEffectState = overrideshader >= 0? overrideshader : mat->mShaderIndex;
mShaderTimer = mat->tex->gl_info.shaderspeed;
mat->Bind(clampmode, translation);
}
void Apply();
void ApplyColorMask();
void ApplyMatrices();
void ApplyLightIndex(int index);
void SetVertexBuffer(FVertexBuffer *vb)
{
mVertexBuffer = vb;
}
void ResetVertexBuffer()
{
// forces rebinding with the next 'apply' call.
mCurrentVertexBuffer = NULL;
}
void SetClipHeightTop(float clip)
{
mClipHeightTop = clip;
}
float GetClipHeightTop()
{
return mClipHeightTop;
}
void SetClipHeightBottom(float clip)
{
mClipHeightBottom = clip;
}
float GetClipHeightBottom()
{
return mClipHeightBottom;
}
void SetColor(float r, float g, float b, float a = 1.f, int desat = 0)
{
mColor.Set(r, g, b, a);
mDesaturation = desat;
}
void SetColor(PalEntry pe, int desat = 0)
{
mColor.Set(pe.r/255.f, pe.g/255.f, pe.b/255.f, pe.a/255.f);
mDesaturation = desat;
}
void SetColorAlpha(PalEntry pe, float alpha = 1.f, int desat = 0)
{
mColor.Set(pe.r/255.f, pe.g/255.f, pe.b/255.f, alpha);
mDesaturation = desat;
}
void ResetColor()
{
mColor.Set(1,1,1,1);
mDesaturation = 0;
}
void GetColorMask(bool& r, bool &g, bool& b, bool& a) const
{
r = mColorMask[0];
g = mColorMask[1];
b = mColorMask[2];
a = mColorMask[3];
}
void SetColorMask(bool r, bool g, bool b, bool a)
{
mColorMask[0] = r;
mColorMask[1] = g;
mColorMask[2] = b;
mColorMask[3] = a;
}
void ResetColorMask()
{
for (int i = 0; i < 4; ++i)
mColorMask[i] = true;
}
void SetTextureMode(int mode)
{
mTextureMode = mode;
}
int GetTextureMode()
{
return mTextureMode;
}
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 SetLightIndex(int n)
{
mLightIndex = n;
}
void EnableBrightmap(bool on)
{
mBrightmapEnabled = on;
}
void EnableModelMatrix(bool on)
{
mModelMatrixEnabled = on;
}
void EnableTextureMatrix(bool on)
{
mTextureMatrixEnabled = on;
}
void SetCameraPos(float x, float y, float z)
{
mCameraPos.Set(x, z, y, 0);
}
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 SetSoftLightLevel(int level)
{
if (glset.lightmode == 8) mLightParms[3] = level / 255.f;
else mLightParms[3] = -1.f;
}
void SetGlowPlanes(const secplane_t &top, const secplane_t &bottom)
{
mGlowTopPlane.Set(FIXED2FLOAT(top.a), FIXED2FLOAT(top.b), FIXED2FLOAT(top.ic), FIXED2FLOAT(top.d));
mGlowBottomPlane.Set(FIXED2FLOAT(bottom.a), FIXED2FLOAT(bottom.b), FIXED2FLOAT(bottom.ic), FIXED2FLOAT(bottom.d));
}
void SetDynLight(float r, float g, float b)
{
mDynColor.Set(r, g, b, 0);
}
void SetObjectColor(PalEntry pe)
{
mObjectColor = pe;
}
void SetFog(PalEntry c, float d)
{
const float LOG2E = 1.442692f; // = 1/log(2)
mFogColor = c;
if (d >= 0.0f) mLightParms[2] = d * (-LOG2E / 64000.f);
}
void SetLightParms(float f, float d)
{
mLightParms[1] = f;
mLightParms[0] = d;
}
void SetFixedColormap(int cm)
{
mColormapState = cm;
}
PalEntry GetFogColor() const
{
return mFogColor;
}
void SetClipSplit(float bottom, float top)
{
mClipSplit[0] = bottom;
mClipSplit[1] = top;
}
void SetClipSplit(float *vals)
{
memcpy(mClipSplit, vals, 2 * sizeof(float));
}
void GetClipSplit(float *out)
{
memcpy(out, mClipSplit, 2 * sizeof(float));
}
void ClearClipSplit()
{
mClipSplit[0] = -1000000.f;
mClipSplit[1] = 1000000.f;
}
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 (func == GL_GREATER) mAlphaThreshold = thresh;
else mAlphaThreshold = thresh - 0.001f;
}
void BlendEquation(int eq)
{
if (!gl_direct_state_change)
{
mBlendEquation = eq;
}
else
{
glBlendEquation(eq);
}
}
// This wraps the depth clamp setting because we frequently need to read it which OpenGL is not particularly performant at...
bool SetDepthClamp(bool on)
{
bool res = mLastDepthClamp;
if (!on) glDisable(GL_DEPTH_CLAMP);
else glEnable(GL_DEPTH_CLAMP);
mLastDepthClamp = on;
return res;
}
void Set2DMode(bool on)
{
m2D = on;
}
void SetInterpolationFactor(float fac)
{
mInterpolationFactor = fac;
}
};
extern FRenderState gl_RenderState;
#endif

525
src/gl/scene/gl_bsp.cpp Normal file
View file

@ -0,0 +1,525 @@
/*
** 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 _DEBUG
if (seg->linedef - lines == 38)
{
int a = 0;
}
#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-endAngle<ANGLE_180)
{
return;
}
if (seg->sidedef == 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();
GLWall wall;
wall.sub = currentsubsector;
wall.Process(seg, currentsector, backsector);
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();
for (unsigned i = 0; i < sub->BSP->Segs.Size(); i++)
{
sub->BSP->Segs[i].Subsector = sub;
sub->BSP->Segs[i].PartnerSeg = NULL;
}
}
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)
{
// Handle all things in sector.
for (AActor * thing = sec->thinglist; thing; thing = thing->snext)
{
GLRenderer->ProcessSprite(thing, sector);
}
}
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 _DEBUG
if (sub->sector-sectors==931)
{
int a = 0;
}
#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();
for (i = ParticlesInSubsec[DWORD(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext)
{
GLRenderer->ProcessParticle(&Particles[i], fakesector);
}
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();
GLRenderer->ProcessSector(fakesector);
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));
}

458
src/gl/scene/gl_clipper.cpp Normal file
View file

@ -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);
}

152
src/gl/scene/gl_clipper.h Normal file
View file

@ -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

363
src/gl/scene/gl_decal.cpp Normal file
View file

@ -0,0 +1,363 @@
/*
** 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/data/gl_vertexbuffer.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;
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;
tex = FMaterial::ValidateTexture(texture, true);
// 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();
}
FColormap p = Colormap;
if (glset.nocoloredspritelighting)
{
p.Decolorize();
}
a = FIXED2FLOAT(decal->Alpha);
// now clip the decal to the actual polygon
float decalwidth = tex->TextureWidth() * FIXED2FLOAT(decal->ScaleX);
float decalheight= tex->TextureHeight() * FIXED2FLOAT(decal->ScaleY);
float decallefto = tex->GetLeftOffset() * FIXED2FLOAT(decal->ScaleX);
float decaltopo = tex->GetTopOffset() * 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);
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 (topleft<dv[0].z && topright<dv[3].z)
return;
if (topleft<dv[1].z || topright<dv[2].z)
{
// decal has to be clipped at the top
// let texture clamping handle all extreme cases
dv[1].v=(dv[1].z-topleft)/(dv[1].z-dv[0].z)*dv[0].v;
dv[2].v=(dv[2].z-topright)/(dv[2].z-dv[3].z)*dv[3].v;
dv[1].z=topleft;
dv[2].z=topright;
}
// now clip to the bottom plane
float vzb=(zbottom[1]-zbottom[0])/linelength;
float bottomleft=this->zbottom[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;
}
// calculate dynamic light effect.
if (gl_lights && GLRenderer->mLightCount && !gl_fixedcolormap && gl_light_sprites)
{
// Note: This should be replaced with proper shader based lighting.
fixed_t x, y;
decal->GetXY(seg->sidedef, x, y);
gl_SetDynSpriteLight(NULL, x, y, zpos, sub);
}
// alpha color only has an effect when using an alpha texture.
if (decal->RenderStyle.Flags & STYLEF_RedIsAlpha)
{
gl_RenderState.SetObjectColor(decal->AlphaColor|0xff000000);
}
gl_SetColor(light, rel, p, a);
// for additively drawn decals we must temporarily set the fog color to black.
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);
gl_RenderState.SetMaterial(tex, CLAMP_XY, decal->Translation, 0, !!(decal->RenderStyle.Flags & STYLEF_RedIsAlpha));
// 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_sprite_threshold);
else gl_RenderState.AlphaFunc(GL_GREATER, 0.f);
gl_RenderState.Apply();
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
for (i = 0; i < 4; i++)
{
ptr->Set(dv[i].x, dv[i].z, dv[i].y, dv[i].u, dv[i].v);
ptr++;
}
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_FAN);
rendered_decals++;
gl_RenderState.SetTextureMode(TM_MODULATE);
gl_RenderState.SetObjectColor(0xffffffff);
gl_RenderState.SetFog(fc,-1);
gl_RenderState.SetDynLight(0,0,0);
}
//==========================================================================
//
//
//
//==========================================================================
void GLWall::DoDrawDecals()
{
if (seg->sidedef && seg->sidedef->AttachedDecals)
{
gl_SetFog(lightlevel, rellight + getExtraLight(), &Colormap, false);
DBaseDecal *decal = seg->sidedef->AttachedDecals;
while (decal)
{
DrawDecal(decal);
decal = decal->WallNext;
}
}
}

1254
src/gl/scene/gl_drawinfo.cpp Normal file

File diff suppressed because it is too large Load diff

271
src/gl/scene/gl_drawinfo.h Normal file
View file

@ -0,0 +1,271 @@
#ifndef __GL_DRAWINFO_H
#define __GL_DRAWINFO_H
#include "gl/scene/gl_wall.h"
enum GLDrawItemType
{
GLDIT_WALL,
GLDIT_FLAT,
GLDIT_SPRITE,
};
enum DrawListType
{
GLDL_PLAINWALLS,
GLDL_PLAINFLATS,
GLDL_MASKEDWALLS,
GLDL_MASKEDFLATS,
GLDL_MASKEDWALLSOFS,
GLDL_MODELS,
GLDL_TRANSLUCENT,
GLDL_TRANSLUCENTBORDER,
GLDL_TYPES,
};
enum Drawpasses
{
GLPASS_ALL, // Main pass with dynamic lights
GLPASS_LIGHTSONLY, // only collect dynamic lights
GLPASS_PLAIN, // Main pass without dynamic lights
GLPASS_DECALS, // Draws a decal
GLPASS_TRANSLUCENT, // Draws translucent objects
};
//==========================================================================
//
// 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<GLWall> walls;
TArray<GLFlat> flats;
TArray<GLSprite> sprites;
TArray<GLDrawItem> drawitems;
int SortNodeStart;
SortNode * sorted;
public:
GLDrawList()
{
next=NULL;
SortNodeStart=-1;
sorted=NULL;
}
~GLDrawList()
{
Reset();
}
unsigned int Size()
{
return drawitems.Size();
}
void AddWall(GLWall * wall);
void AddFlat(GLFlat * flat);
void AddSprite(GLSprite * sprite);
void Reset();
void SortWalls();
void SortFlats();
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, bool trans);
void DoDrawSorted(SortNode * node);
void DrawSorted();
void Draw(int pass, bool trans = false);
void DrawWalls(int pass);
void DrawFlats(int pass);
void DrawDecals();
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<BYTE> sectorrenderflags;
TArray<BYTE> ss_renderflags;
TArray<BYTE> no_renderflags;
TArray<MissingTextureInfo> MissingUpperTextures;
TArray<MissingTextureInfo> MissingLowerTextures;
TArray<MissingSegInfo> MissingUpperSegs;
TArray<MissingSegInfo> MissingLowerSegs;
TArray<SubsectorHackInfo> SubsectorHacks;
TArray<gl_subsectorrendernode*> otherfloorplanes;
TArray<gl_subsectorrendernode*> otherceilingplanes;
TArray<sector_t *> CeilingStacks;
TArray<sector_t *> FloorStacks;
TArray<subsector_t *> 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<otherfloorplanes.Size()) return otherfloorplanes[sector];
else return NULL;
}
gl_subsectorrendernode * GetOtherCeilingPlanes(unsigned int sector)
{
if (sector<otherceilingplanes.Size()) return otherceilingplanes[sector];
else return NULL;
}
};
class FDrawInfoList
{
TDeletingArray<FDrawInfo *> mList;
public:
FDrawInfo *GetNew();
void Release(FDrawInfo *);
};
extern FDrawInfo * gl_drawinfo;
void gl_SetPlaneTextureRotation(const GLSectorPlane * secplane, FMaterial * gltexture);
void gl_SetRenderStyle(FRenderStyle style, bool drawopaque, bool allowcolorblending);
#endif

View file

@ -0,0 +1,400 @@
/*
** 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 _DEBUG
if (sec-sectors==560)
{
int a = 0;
}
#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];
dest->portals[sector_t::ceiling] = NULL;
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];
dest->portals[sector_t::floor] = NULL;
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;
}

725
src/gl/scene/gl_flats.cpp Normal file
View file

@ -0,0 +1,725 @@
/*
** 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_interface.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"
#ifdef _DEBUG
CVAR(Int, gl_breaksec, -1, 0)
#endif
//==========================================================================
//
// Sets the texture matrix according to the plane's texture positioning
// information
//
//==========================================================================
static float tics;
void 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() != 64 ||
gltexture->TextureHeight() != 64)
{
float uoffs = FIXED2FLOAT(secplane->xoffs) / gltexture->TextureWidth();
float voffs = FIXED2FLOAT(secplane->yoffs) / gltexture->TextureHeight();
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();
float yscale2=64.f/gltexture->TextureHeight();
gl_RenderState.mTextureMatrix.loadIdentity();
gl_RenderState.mTextureMatrix.scale(xscale1 ,yscale1,1.0f);
gl_RenderState.mTextureMatrix.translate(uoffs,voffs,0.0f);
gl_RenderState.mTextureMatrix.scale(xscale2 ,yscale2,1.0f);
gl_RenderState.mTextureMatrix.rotate(angle,0.0f,0.0f,1.0f);
gl_RenderState.EnableTextureMatrix(true);
}
}
//==========================================================================
//
// Flats
//
//==========================================================================
extern FDynLightData lightdata;
void GLFlat::SetupSubsectorLights(int pass, subsector_t * sub, int *dli)
{
Plane p;
if (dli != NULL && *dli != -1)
{
gl_RenderState.ApplyLightIndex(GLRenderer->mLights->GetIndex(*dli));
(*dli)++;
return;
}
lightdata.Clear();
FLightNode * node = sub->lighthead;
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 && ((planeh<light->z && ceiling) || (planeh>light->z && !ceiling)))
{
node=node->nextLight;
continue;
}
p.Set(plane.plane);
gl_GetLight(p, light, false, false, lightdata);
node = node->nextLight;
}
int d = GLRenderer->mLights->UploadLights(lightdata);
if (pass == GLPASS_LIGHTSONLY)
{
GLRenderer->mLights->StoreIndex(d);
}
else
{
gl_RenderState.ApplyLightIndex(d);
}
}
//==========================================================================
//
//
//
//==========================================================================
void GLFlat::DrawSubsector(subsector_t * sub)
{
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
if (plane.plane.a | plane.plane.b)
{
for (unsigned int k = 0; k < sub->numlines; k++)
{
vertex_t *vt = sub->firstline[k].v1;
ptr->x = vt->fx;
ptr->y = vt->fy;
ptr->z = plane.plane.ZatPoint(vt->fx, vt->fy) + dz;
ptr->u = vt->fx / 64.f;
ptr->v = -vt->fy / 64.f;
ptr++;
}
}
else
{
float zc = FIXED2FLOAT(plane.plane.Zat0()) + dz;
for (unsigned int k = 0; k < sub->numlines; k++)
{
vertex_t *vt = sub->firstline[k].v1;
ptr->x = vt->fx;
ptr->y = vt->fy;
ptr->z = zc;
ptr->u = vt->fx / 64.f;
ptr->v = -vt->fy / 64.f;
ptr++;
}
}
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_FAN);
flatvertices += sub->numlines;
flatprimitives++;
}
//==========================================================================
//
//
//
//==========================================================================
void GLFlat::ProcessLights(bool istrans)
{
dynlightindex = GLRenderer->mLights->GetIndexPtr();
if (sub)
{
// This represents a single subsector
SetupSubsectorLights(GLPASS_LIGHTSONLY, sub);
}
else
{
// Draw the subsectors belonging to this sector
for (int i=0; i<sector->subsectorcount; i++)
{
subsector_t * sub = sector->subsectors[i];
if (gl_drawinfo->ss_renderflags[sub-subsectors]&renderflags || istrans)
{
SetupSubsectorLights(GLPASS_LIGHTSONLY, 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)
{
SetupSubsectorLights(GLPASS_LIGHTSONLY, node->sub);
node = node->next;
}
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void GLFlat::DrawSubsectors(int pass, bool processlights, bool istrans)
{
int dli = dynlightindex;
gl_RenderState.Apply();
if (sub)
{
// This represents a single subsector
if (processlights) SetupSubsectorLights(GLPASS_ALL, sub, &dli);
DrawSubsector(sub);
}
else
{
if (vboindex >= 0)
{
int index = vboindex;
for (int i=0; i<sector->subsectorcount; i++)
{
subsector_t * sub = sector->subsectors[i];
if (gl_drawinfo->ss_renderflags[sub-subsectors]&renderflags || istrans)
{
if (processlights) SetupSubsectorLights(GLPASS_ALL, sub, &dli);
drawcalls.Clock();
glDrawArrays(GL_TRIANGLE_FAN, index, sub->numlines);
drawcalls.Unclock();
flatvertices += sub->numlines;
flatprimitives++;
}
index += sub->numlines;
}
}
else
{
// Draw the subsectors belonging to this sector
for (int i=0; i<sector->subsectorcount; i++)
{
subsector_t * sub = sector->subsectors[i];
if (gl_drawinfo->ss_renderflags[sub-subsectors]&renderflags || istrans)
{
if (processlights) SetupSubsectorLights(GLPASS_ALL, sub, &dli);
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 (processlights) SetupSubsectorLights(GLPASS_ALL, node->sub, &dli);
DrawSubsector(node->sub);
node = node->next;
}
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void GLFlat::Draw(int pass, bool trans) // trans only has meaning for GLPASS_LIGHTSONLY
{
int rel = getExtraLight();
#ifdef _DEBUG
if (sector->sectornum == gl_breaksec)
{
int a = 0;
}
#endif
switch (pass)
{
case GLPASS_PLAIN: // Single-pass rendering
case GLPASS_ALL:
gl_SetColor(lightlevel, rel, Colormap,1.0f);
gl_SetFog(lightlevel, rel, &Colormap, false);
gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false);
gl_SetPlaneTextureRotation(&plane, gltexture);
DrawSubsectors(pass, (pass == GLPASS_ALL || dynlightindex > -1), false);
gl_RenderState.EnableTextureMatrix(false);
break;
case GLPASS_LIGHTSONLY:
if (!trans || gltexture)
{
ProcessLights(trans);
}
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);
if (!gltexture)
{
gl_RenderState.EnableTexture(false);
DrawSubsectors(pass, false, true);
gl_RenderState.EnableTexture(true);
}
else
{
gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false);
gl_SetPlaneTextureRotation(&plane, gltexture);
DrawSubsectors(pass, true, true);
gl_RenderState.EnableTextureMatrix(false);
}
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.Clear();
}
if (renderstyle!=STYLE_Translucent || alpha < 1.f - FLT_EPSILON || fog || gltexture == NULL)
{
// translucent 3D floors go into the regular translucent list, translucent portals go into the translucent border list.
list = (renderflags&SSRF_RENDER3DPLANES) ? GLDL_TRANSLUCENT : GLDL_TRANSLUCENTBORDER;
}
else
{
bool masked = gltexture->isMasked() && ((renderflags&SSRF_RENDER3DPLANES) || stack);
list = masked ? GLDL_MASKEDFLATS : GLDL_PLAINFLATS;
}
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, false, 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 _DEBUG
if (frontsector->sectornum==gl_breaksec)
{
int a = 0;
}
#endif
// Get the real sector for this one.
sector=&sectors[frontsector->sectornum];
extsector_t::xfloor &x = sector->e->XFloor;
this->sub=NULL;
dynlightindex = -1;
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->fromsector)
&& (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, &sector->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->top.copied && rover->flags&(FF_INVERTPLANES|FF_BOTHPLANES))
{
fixed_t ff_top=rover->top.plane->ZatPoint(CenterSpot(sector));
if (ff_top<lastceilingheight)
{
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));
}
lastceilingheight=ff_top;
}
}
if (!rover->bottom.copied && !(rover->flags&FF_INVERTPLANES))
{
fixed_t ff_bottom=rover->bottom.plane->ZatPoint(CenterSpot(sector));
if (ff_bottom<lastceilingheight)
{
if (FIXED2FLOAT(viewz)<=rover->bottom.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->bottom.copied && 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->top.copied && !(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--;
}
}
}
}
}
}

1090
src/gl/scene/gl_portal.cpp Normal file

File diff suppressed because it is too large Load diff

301
src/gl/scene/gl_portal.h Normal file
View file

@ -0,0 +1,301 @@
/*
** 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<GLSkyInfo> UniqueSkies;
extern UniqueList<GLHorizonInfo> UniqueHorizons;
extern UniqueList<secplane_t> UniquePlaneMirrors;
class GLPortal
{
static TArray<GLPortal *> portals;
static int recursion;
static unsigned int QueryObject;
protected:
static TArray<float> planestack;
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;
GLPortal *NextPortal;
TArray<BYTE> savedmapsection;
TArray<unsigned int> mPrimIndices;
protected:
TArray<GLWall> 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<subsector_t *> 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

File diff suppressed because it is too large Load diff

1232
src/gl/scene/gl_scene.cpp Normal file

File diff suppressed because it is too large Load diff

352
src/gl/scene/gl_sky.cpp Normal file
View file

@ -0,0 +1,352 @@
/*
** 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 = sector->GetSkyBox(plane);
// 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, false, 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, false, 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, false, true);
skyinfo.skytexno1=sky2texture;
skyinfo.sky2 = true;
skyinfo.x_offset[0] = GLRenderer->mSky2Pos;
}
else
{
skyinfo.texture[0]=FMaterial::ValidateTexture(sky1texture, false, 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? &sector->ceilingplane : &sector->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);
}

539
src/gl/scene/gl_skydome.cpp Normal file
View file

@ -0,0 +1,539 @@
/*
** gl_sky.cpp
**
** Draws the sky. Loosely based on the JDoom sky and the ZDoomGL 0.66.2 sky.
**
**---------------------------------------------------------------------------
** Copyright 2003 Tim Stump
** Copyright 2005 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
** 4. Full disclosure of the entire project's source code, except for third
** party libraries is mandatory. (NOTE: This clause is non-negotiable!)
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "gl/system/gl_system.h"
#include "doomtype.h"
#include "g_level.h"
#include "sc_man.h"
#include "w_wad.h"
#include "r_state.h"
//#include "gl/gl_intern.h"
#include "gl/system/gl_interface.h"
#include "gl/data/gl_data.h"
#include "gl/data/gl_vertexbuffer.h"
#include "gl/renderer/gl_lightdata.h"
#include "gl/renderer/gl_renderstate.h"
#include "gl/scene/gl_drawinfo.h"
#include "gl/scene/gl_portal.h"
#include "gl/shaders/gl_shader.h"
#include "gl/textures/gl_bitmap.h"
#include "gl/textures/gl_texture.h"
#include "gl/textures/gl_skyboxtexture.h"
#include "gl/textures/gl_material.h"
//-----------------------------------------------------------------------------
//
// Shamelessly lifted from Doomsday (written by Jaakko Keränen)
// also shamelessly lifted from ZDoomGL! ;)
//
//-----------------------------------------------------------------------------
CVAR(Float, skyoffset, 0, 0) // for testing
extern int skyfog;
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
FSkyVertexBuffer::FSkyVertexBuffer()
{
CreateDome();
glBindVertexArray(vao_id);
glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, false, sizeof(FSkyVertex), &VSO->x);
glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, false, sizeof(FSkyVertex), &VSO->u);
glVertexAttribPointer(VATTR_COLOR, 4, GL_UNSIGNED_BYTE, true, sizeof(FSkyVertex), &VSO->color);
glEnableVertexAttribArray(VATTR_VERTEX);
glEnableVertexAttribArray(VATTR_TEXCOORD);
glEnableVertexAttribArray(VATTR_COLOR);
glBindVertexArray(0);
}
FSkyVertexBuffer::~FSkyVertexBuffer()
{
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::SkyVertex(int r, int c, bool yflip)
{
static const angle_t maxSideAngle = ANGLE_180 / 3;
static const fixed_t scale = 10000 << FRACBITS;
angle_t topAngle= (angle_t)(c / (float)mColumns * ANGLE_MAX);
angle_t sideAngle = maxSideAngle * (mRows - r) / mRows;
fixed_t height = finesine[sideAngle>>ANGLETOFINESHIFT];
fixed_t realRadius = FixedMul(scale, finecosine[sideAngle>>ANGLETOFINESHIFT]);
fixed_t x = FixedMul(realRadius, finecosine[topAngle>>ANGLETOFINESHIFT]);
fixed_t y = (!yflip) ? FixedMul(scale, height) : FixedMul(scale, height) * -1;
fixed_t z = FixedMul(realRadius, finesine[topAngle>>ANGLETOFINESHIFT]);
FSkyVertex vert;
vert.color = r == 0 ? 0xffffff : 0xffffffff;
// And the texture coordinates.
if(!yflip) // Flipped Y is for the lower hemisphere.
{
vert.u = (-c / (float)mColumns) ;
vert.v = (r / (float)mRows);
}
else
{
vert.u = (-c / (float)mColumns);
vert.v = 1.0f + ((mRows - r) / (float)mRows);
}
if (r != 4) y+=FRACUNIT*300;
// And finally the vertex.
vert.x =-FIXED2FLOAT(x); // Doom mirrors the sky vertically!
vert.y = FIXED2FLOAT(y) - 1.f;
vert.z = FIXED2FLOAT(z);
mVertices.Push(vert);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::CreateSkyHemisphere(int hemi)
{
int r, c;
bool yflip = !!(hemi & SKYHEMI_LOWER);
mPrimStart.Push(mVertices.Size());
for (c = 0; c < mColumns; c++)
{
SkyVertex(1, c, yflip);
}
// The total number of triangles per hemisphere can be calculated
// as follows: rows * columns * 2 + 2 (for the top cap).
for (r = 0; r < mRows; r++)
{
mPrimStart.Push(mVertices.Size());
for (c = 0; c <= mColumns; c++)
{
SkyVertex(r + yflip, c, yflip);
SkyVertex(r + 1 - yflip, c, yflip);
}
}
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::CreateDome()
{
// the first thing we put into the buffer is the fog layer object which is just 4 triangles around the viewpoint.
mVertices.Reserve(12);
mVertices[0].Set( 1.0f, 1.0f, -1.0f);
mVertices[1].Set( 1.0f, -1.0f, -1.0f);
mVertices[2].Set(-1.0f, 0.0f, -1.0f);
mVertices[3].Set( 1.0f, 1.0f, -1.0f);
mVertices[4].Set( 1.0f, -1.0f, -1.0f);
mVertices[5].Set( 0.0f, 0.0f, 1.0f);
mVertices[6].Set(-1.0f, 0.0f, -1.0f);
mVertices[7].Set( 1.0f, 1.0f, -1.0f);
mVertices[8].Set( 0.0f, 0.0f, 1.0f);
mVertices[9].Set(1.0f, -1.0f, -1.0f);
mVertices[10].Set(-1.0f, 0.0f, -1.0f);
mVertices[11].Set( 0.0f, 0.0f, 1.0f);
mColumns = 128;
mRows = 4;
CreateSkyHemisphere(SKYHEMI_UPPER);
CreateSkyHemisphere(SKYHEMI_LOWER);
mPrimStart.Push(mVertices.Size());
glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
glBufferData(GL_ARRAY_BUFFER, mVertices.Size() * sizeof(FSkyVertex), &mVertices[0], GL_STATIC_DRAW);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
inline void FSkyVertexBuffer::RenderRow(int prim, int row)
{
glDrawArrays(prim, mPrimStart[row], mPrimStart[row + 1] - mPrimStart[row]);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::RenderDome(FMaterial *tex, int mode)
{
int rc = mRows + 1;
// The caps only get drawn for the main layer but not for the overlay.
if (mode == SKYMODE_MAINLAYER && tex != NULL)
{
PalEntry pe = tex->tex->GetSkyCapColor(false);
gl_RenderState.SetObjectColor(pe);
gl_RenderState.EnableTexture(false);
gl_RenderState.Apply();
RenderRow(GL_TRIANGLE_FAN, 0);
pe = tex->tex->GetSkyCapColor(true);
gl_RenderState.SetObjectColor(pe);
gl_RenderState.Apply();
RenderRow(GL_TRIANGLE_FAN, rc);
gl_RenderState.EnableTexture(true);
}
gl_RenderState.SetObjectColor(0xffffffff);
gl_RenderState.Apply();
for (int i = 1; i <= mRows; i++)
{
RenderRow(GL_TRIANGLE_STRIP, i);
RenderRow(GL_TRIANGLE_STRIP, rc + i);
}
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void RenderDome(FMaterial * tex, float x_offset, float y_offset, bool mirror, int mode)
{
int texh = 0;
int texw = 0;
// 57 world units roughly represent one sky texel for the glTranslate call.
const float skyoffsetfactor = 57;
if (tex)
{
gl_RenderState.SetMaterial(tex, CLAMP_NONE, 0, -1, false);
texw = tex->TextureWidth();
texh = tex->TextureHeight();
gl_RenderState.EnableModelMatrix(true);
gl_RenderState.mModelMatrix.loadIdentity();
gl_RenderState.mModelMatrix.rotate(-180.0f+x_offset, 0.f, 1.f, 0.f);
float xscale = 1024.f / float(texw);
float yscale = 1.f;
if (texh < 128)
{
// smaller sky textures must be tiled. We restrict it to 128 sky pixels, though
gl_RenderState.mModelMatrix.translate(0.f, -1250.f, 0.f);
gl_RenderState.mModelMatrix.scale(1.f, 128/230.f, 1.f);
yscale = 128 / texh; // intentionally left as integer.
}
else if (texh < 200)
{
gl_RenderState.mModelMatrix.translate(0.f, -1250.f, 0.f);
gl_RenderState.mModelMatrix.scale(1.f, texh/230.f, 1.f);
}
else if (texh <= 240)
{
gl_RenderState.mModelMatrix.translate(0.f, (200 - texh + tex->tex->SkyOffset + skyoffset)*skyoffsetfactor, 0.f);
gl_RenderState.mModelMatrix.scale(1.f, 1.f + ((texh-200.f)/200.f) * 1.17f, 1.f);
}
else
{
gl_RenderState.mModelMatrix.translate(0.f, (-40 + tex->tex->SkyOffset + skyoffset)*skyoffsetfactor, 0.f);
gl_RenderState.mModelMatrix.scale(1.f, 1.2f * 1.17f, 1.f);
yscale = 240.f / texh;
}
gl_RenderState.EnableTextureMatrix(true);
gl_RenderState.mTextureMatrix.loadIdentity();
gl_RenderState.mTextureMatrix.scale(mirror? -xscale : xscale, yscale, 1.f);
gl_RenderState.mTextureMatrix.translate(1.f, y_offset / texh, 1.f);
}
GLRenderer->mSkyVBO->RenderDome(tex, mode);
gl_RenderState.EnableTextureMatrix(false);
gl_RenderState.EnableModelMatrix(false);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
static void RenderBox(FTextureID texno, FMaterial * gltex, float x_offset, bool sky2)
{
FSkyBox * sb = static_cast<FSkyBox*>(gltex->tex);
int faces;
FMaterial * tex;
gl_RenderState.EnableModelMatrix(true);
gl_RenderState.mModelMatrix.loadIdentity();
if (!sky2)
gl_RenderState.mModelMatrix.rotate(-180.0f+x_offset, glset.skyrotatevector.X, glset.skyrotatevector.Z, glset.skyrotatevector.Y);
else
gl_RenderState.mModelMatrix.rotate(-180.0f+x_offset, glset.skyrotatevector2.X, glset.skyrotatevector2.Z, glset.skyrotatevector2.Y);
FFlatVertex *ptr;
if (sb->faces[5])
{
faces=4;
// north
tex = FMaterial::ValidateTexture(sb->faces[0], false);
gl_RenderState.SetMaterial(tex, CLAMP_XY, 0, -1, false);
gl_RenderState.Apply();
ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(128.f, 128.f, -128.f, 0, 0);
ptr++;
ptr->Set(-128.f, 128.f, -128.f, 1, 0);
ptr++;
ptr->Set(128.f, -128.f, -128.f, 0, 1);
ptr++;
ptr->Set(-128.f, -128.f, -128.f, 1, 1);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
// east
tex = FMaterial::ValidateTexture(sb->faces[1], false);
gl_RenderState.SetMaterial(tex, CLAMP_XY, 0, -1, false);
gl_RenderState.Apply();
ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(-128.f, 128.f, -128.f, 0, 0);
ptr++;
ptr->Set(-128.f, 128.f, 128.f, 1, 0);
ptr++;
ptr->Set(-128.f, -128.f, -128.f, 0, 1);
ptr++;
ptr->Set(-128.f, -128.f, 128.f, 1, 1);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
// south
tex = FMaterial::ValidateTexture(sb->faces[2], false);
gl_RenderState.SetMaterial(tex, CLAMP_XY, 0, -1, false);
gl_RenderState.Apply();
ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(-128.f, 128.f, 128.f, 0, 0);
ptr++;
ptr->Set(128.f, 128.f, 128.f, 1, 0);
ptr++;
ptr->Set(-128.f, -128.f, 128.f, 0, 1);
ptr++;
ptr->Set(128.f, -128.f, 128.f, 1, 1);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
// west
tex = FMaterial::ValidateTexture(sb->faces[3], false);
gl_RenderState.SetMaterial(tex, CLAMP_XY, 0, -1, false);
gl_RenderState.Apply();
ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(128.f, 128.f, 128.f, 0, 0);
ptr++;
ptr->Set(128.f, 128.f, -128.f, 1, 0);
ptr++;
ptr->Set(128.f, -128.f, 128.f, 0, 1);
ptr++;
ptr->Set(128.f, -128.f, -128.f, 1, 1);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
}
else
{
faces=1;
tex = FMaterial::ValidateTexture(sb->faces[0], false);
gl_RenderState.SetMaterial(tex, CLAMP_XY, 0, -1, false);
gl_RenderState.Apply();
ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(128.f, 128.f, -128.f, 0, 0);
ptr++;
ptr->Set(128.f, -128.f, -128.f, 0, 1);
ptr++;
ptr->Set(-128.f, 128.f, -128.f, 0.25f, 0);
ptr++;
ptr->Set(-128.f, -128.f, -128.f, 0.25f, 1);
ptr++;
ptr->Set(-128.f, 128.f, 128.f, 0.5f, 0);
ptr++;
ptr->Set(-128.f, -128.f, 128.f, 0.5f, 1);
ptr++;
ptr->Set(128.f, 128.f, 128.f, 0.75f, 0);
ptr++;
ptr->Set(128.f, -128.f, 128.f, 0.75f, 1);
ptr++;
ptr->Set(128.f, 128.f, -128.f, 1, 0);
ptr++;
ptr->Set(128.f, -128.f, -128.f, 1, 1);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
}
// top
tex = FMaterial::ValidateTexture(sb->faces[faces], false);
gl_RenderState.SetMaterial(tex, CLAMP_XY, 0, -1, false);
gl_RenderState.Apply();
ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(128.f, 128.f, -128.f, 0, sb->fliptop);
ptr++;
ptr->Set(-128.f, 128.f, -128.f, 1, sb->fliptop);
ptr++;
ptr->Set(128.f, 128.f, 128.f, 0, !sb->fliptop);
ptr++;
ptr->Set(-128.f, 128.f, 128.f, 1, !sb->fliptop);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
// bottom
tex = FMaterial::ValidateTexture(sb->faces[faces+1], false);
gl_RenderState.SetMaterial(tex, CLAMP_XY, 0, -1, false);
gl_RenderState.Apply();
ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(128.f, -128.f, -128.f, 0, 0);
ptr++;
ptr->Set(-128.f, -128.f, -128.f, 1, 0);
ptr++;
ptr->Set(128.f, -128.f, 128.f, 0, 1);
ptr++;
ptr->Set(-128.f, -128.f, 128.f, 1, 1);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
gl_RenderState.EnableModelMatrix(false);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void GLSkyPortal::DrawContents()
{
bool drawBoth = false;
// 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;
gl_RenderState.SetSoftLightLevel(-1);
}
gl_RenderState.ResetColor();
gl_RenderState.EnableFog(false);
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
bool oldClamp = gl_RenderState.SetDepthClamp(true);
gl_MatrixStack.Push(gl_RenderState.mViewMatrix);
GLRenderer->SetupView(0, 0, 0, viewangle, !!(MirrorFlag&1), !!(PlaneMirrorFlag&1));
if (origin->texture[0] && origin->texture[0]->tex->gl_info.bSkybox)
{
RenderBox(origin->skytexno1, origin->texture[0], origin->x_offset[0], origin->sky2);
}
else
{
gl_RenderState.SetVertexBuffer(GLRenderer->mSkyVBO);
if (origin->texture[0]==origin->texture[1] && origin->doublesky) origin->doublesky=false;
if (origin->texture[0])
{
gl_RenderState.SetTextureMode(TM_OPAQUE);
RenderDome(origin->texture[0], origin->x_offset[0], origin->y_offset, origin->mirrored, FSkyVertexBuffer::SKYMODE_MAINLAYER);
gl_RenderState.SetTextureMode(TM_MODULATE);
}
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.05f);
if (origin->doublesky && origin->texture[1])
{
RenderDome(origin->texture[1], origin->x_offset[1], origin->y_offset, false, FSkyVertexBuffer::SKYMODE_SECONDLAYER);
}
if (skyfog>0 && gl_fixedcolormap == CM_DEFAULT && (origin->fadecolor & 0xffffff) != 0)
{
PalEntry FadeColor = origin->fadecolor;
FadeColor.a = clamp<int>(skyfog, 0, 255);
gl_RenderState.EnableTexture(false);
gl_RenderState.SetObjectColor(FadeColor);
gl_RenderState.Apply();
glDrawArrays(GL_TRIANGLES, 0, 12);
gl_RenderState.EnableTexture(true);
gl_RenderState.SetObjectColor(0xffffffff);
}
gl_RenderState.SetVertexBuffer(GLRenderer->mVBO);
}
gl_MatrixStack.Pop(gl_RenderState.mViewMatrix);
gl_RenderState.ApplyMatrices();
glset.lightmode = oldlightmode;
gl_RenderState.SetDepthClamp(oldClamp);
}

975
src/gl/scene/gl_sprite.cpp Normal file
View file

@ -0,0 +1,975 @@
/*
** 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_interface.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/renderer/gl_renderer.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"
#include "gl/data/gl_vertexbuffer.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<spritedef_t> sprites;
extern TArray<spriteframe_t> SpriteFrames;
extern TArray<PalEntry> 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);
}
CVAR(Bool, gl_nolayer, false, 0)
//==========================================================================
//
//
//
//==========================================================================
void GLSprite::Draw(int pass)
{
if (pass == GLPASS_DECALS || pass == GLPASS_LIGHTSONLY) return;
bool additivefog = false;
bool foglayer = false;
int rel = fullbright? 0 : getExtraLight();
if (pass==GLPASS_TRANSLUCENT)
{
// The translucent pass requires special setup for the various modes.
// for special render styles brightmaps would not look good - especially for subtractive.
if (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_DEFAULT && actor &&
fullbright && gltexture && !gltexture->GetTransparent());
if (hw_styleflags == STYLEHW_NoAlphaTest)
{
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
}
else
{
gl_RenderState.AlphaFunc(GL_GEQUAL, 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<int>(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, gl_mask_sprite_threshold);
gl_RenderState.SetColor(0.2f,0.2f,0.2f,fuzzalpha, Colormap.desaturation);
additivefog = true;
}
else if (RenderStyle.BlendOp == STYLEOP_Add && RenderStyle.DestAlpha == STYLEALPHA_One)
{
additivefog = true;
}
}
if (RenderStyle.BlendOp!=STYLEOP_Shadow)
{
if (gl_lights && GLRenderer->mLightCount && !gl_fixedcolormap && !fullbright)
{
gl_SetDynSpriteLight(gl_light_sprites ? actor : NULL, gl_light_particles ? particle : NULL);
}
gl_SetColor(lightlevel, rel, Colormap, trans);
}
gl_RenderState.SetObjectColor(ThingColor);
if (gl_isBlack(Colormap.FadeColor)) foglevel=lightlevel;
if (RenderStyle.Flags & STYLEF_FadeToBlack)
{
Colormap.FadeColor=0;
additivefog = true;
}
if (RenderStyle.BlendOp == STYLEOP_RevSub || RenderStyle.BlendOp == STYLEOP_Sub)
{
if (!modelframe)
{
// non-black fog with subtractive style needs special treatment
if (!gl_isBlack(Colormap.FadeColor))
{
foglayer = true;
// Due to the two-layer approach we need to force an alpha test that lets everything pass
gl_RenderState.AlphaFunc(GL_GREATER, 0);
}
}
else RenderStyle.BlendOp = STYLEOP_Fuzz; // subtractive with models is not going to work.
}
if (!foglayer) gl_SetFog(foglevel, rel, &Colormap, additivefog);
else
{
gl_RenderState.EnableFog(false);
gl_RenderState.SetFog(0, 0);
}
if (gltexture) gl_RenderState.SetMaterial(gltexture, CLAMP_XY, translation, OverrideShader, !!(RenderStyle.Flags & STYLEF_RedIsAlpha));
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();
Vector v1;
Vector v2;
Vector v3;
Vector v4;
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);
v1 = mat * Vector(x1, z1, y1);
v2 = mat * Vector(x2, z1, y2);
v3 = mat * Vector(x1, z2, y1);
v4 = mat * Vector(x2, z2, y2);
}
else
{
v1 = Vector(x1, z1, y1);
v2 = Vector(x2, z1, y2);
v3 = Vector(x1, z2, y1);
v4 = Vector(x2, z2, y2);
}
FFlatVertex *ptr;
unsigned int offset, count;
ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(v1[0], v1[1], v1[2], ul, vt);
ptr++;
ptr->Set(v2[0], v2[1], v2[2], ur, vt);
ptr++;
ptr->Set(v3[0], v3[1], v3[2], ul, vb);
ptr++;
ptr->Set(v4[0], v4[1], v4[2], ur, vb);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP, &offset, &count);
if (foglayer)
{
// If we get here we know that we have colored fog and no fixed colormap.
gl_SetFog(foglevel, rel, &Colormap, additivefog);
gl_RenderState.SetFixedColormap(CM_FOGLAYER);
gl_RenderState.BlendEquation(GL_FUNC_ADD);
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl_RenderState.Apply();
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, offset, count);
gl_RenderState.SetFixedColormap(CM_DEFAULT);
}
}
else
{
gl_RenderModel(this);
}
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);
}
gl_RenderState.SetObjectColor(0xffffffff);
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
{
list = GLDL_MODELS;
}
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_t> & lightlist=frontsector->e->XFloor.lightlist;
for(i=0;i<lightlist.Size();i++)
{
// Particles don't go through here so we can safely assume that actor is not NULL
if (i<lightlist.Size()-1) lightbottom=lightlist[i+1].plane.ZatPoint(actor->x,actor->y);
else lightbottom=frontsector->floorplane.ZatPoint(actor->x,actor->y);
maplightbottom=FIXED2FLOAT(lightbottom);
if (maplightbottom<z2) maplightbottom=z2;
if (maplightbottom<z1)
{
copySprite=*this;
copySprite.lightlevel = gl_ClampLight(*lightlist[i].p_lightlevel);
copySprite.Colormap.CopyLightColor(lightlist[i].extra_colormap);
if (glset.nocoloredspritelighting)
{
int v = (copySprite.Colormap.LightColor.r + copySprite.Colormap.LightColor.g + copySprite.Colormap.LightColor.b )/3;
copySprite.Colormap.LightColor.r=
copySprite.Colormap.LightColor.g=
copySprite.Colormap.LightColor.b=(255+v+v)/3;
}
z1=copySprite.z2=maplightbottom;
vt=copySprite.vb=copySprite.vt+
(maplightbottom-copySprite.z1)*(copySprite.vb-copySprite.vt)/(z2-copySprite.z1);
copySprite.PutSprite(translucent);
put=true;
}
}
}
void GLSprite::SetSpriteColor(sector_t *sector, fixed_t center_y)
{
fixed_t lightbottom;
float maplightbottom;
unsigned int i;
TArray<lightlist_t> & lightlist=actor->Sector->e->XFloor.lightlist;
for(i=0;i<lightlist.Size();i++)
{
// Particles don't go through here so we can safely assume that actor is not NULL
if (i<lightlist.Size()-1) lightbottom=lightlist[i+1].plane.ZatPoint(actor->x,actor->y);
else lightbottom=sector->floorplane.ZatPoint(actor->x,actor->y);
//maplighttop=FIXED2FLOAT(lightlist[i].height);
maplightbottom=FIXED2FLOAT(lightbottom);
if (maplightbottom<z2) maplightbottom=z2;
if (maplightbottom<center_y)
{
lightlevel=*lightlist[i].p_lightlevel;
Colormap.CopyLightColor(lightlist[i].extra_colormap);
if (glset.nocoloredspritelighting)
{
int v = (Colormap.LightColor.r + Colormap.LightColor.g + Colormap.LightColor.b )/3;
Colormap.LightColor.r=
Colormap.LightColor.g=
Colormap.LightColor.b=(255+v+v)/3;
}
return;
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void GLSprite::PerformSpriteClipAdjustment(AActor *thing, fixed_t thingx, fixed_t thingy, float spriteheight)
{
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; i < x.ffloors.Size(); i++)
{
F3DFloor * ff = x.ffloors[i];
fixed_t floorh = ff->top.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));
// +/-1 to account for the one pixel empty frame around the sprite.
float diffb = (z2+1) - btm;
float difft = (z1-1) - 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 > fabs(diffb))
{
float ratio = clamp<float>((fabs(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 > fabs(difft))
{
float ratio = clamp<float>((fabs(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;
}
}
//==========================================================================
//
//
//
//==========================================================================
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;
int type = thing->renderflags & RF_SPRITETYPEMASK;
gltexture = FMaterial::ValidateTexture(patch, (type == RF_FACESPRITE), false);
if (!gltexture) return;
vt = gltexture->GetSpriteVT();
vb = gltexture->GetSpriteVB();
gltexture->GetSpriteRect(&r);
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();
}
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) * r.height;
// Tests show that this doesn't look good for many decorations and corpses
if (spriteheight > 0 && gl_spriteclip > 0 && (thing->renderflags & RF_SPRITETYPEMASK) == RF_FACESPRITE)
{
PerformSpriteClipAdjustment(thing, thingx, thingy, spriteheight);
}
float viewvecX;
float viewvecY;
switch (thing->renderflags & RF_SPRITETYPEMASK)
{
case RF_FACESPRITE:
viewvecX = GLRenderer->mViewVector.X;
viewvecY = GLRenderer->mViewVector.Y;
x1 = x - viewvecY*leftfac;
x2 = x - viewvecY*rightfac;
y1 = y + viewvecX*leftfac;
y2 = y + viewvecX*rightfac;
break;
case RF_WALLSPRITE:
viewvecX = FIXED2FLOAT(finecosine[thing->angle >> ANGLETOFINESHIFT]);
viewvecY = FIXED2FLOAT(finesine[thing->angle >> ANGLETOFINESHIFT]);
x1 = x + viewvecY*leftfac;
x2 = x + viewvecY*rightfac;
y1 = y - viewvecX*leftfac;
y2 = y - viewvecX*rightfac;
break;
}
}
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) && (!gltexture || !gltexture->tex->gl_info.bDisableFullbright));
lightlevel=fullbright? 255 :
gl_ClampLight(rendersector->GetTexture(sector_t::ceiling) == skyflatnum ?
rendersector->GetCeilingLight() : rendersector->GetFloorLight());
foglevel = (BYTE)clamp<short>(rendersector->lightlevel, 0, 255);
lightlevel = (byte)gl_CheckSpriteGlow(rendersector, lightlevel, thingx, thingy, thingz);
ThingColor = (thing->RenderStyle.Flags & STYLEF_ColorIsFixed) ? thing->fillcolor : 0xffffff;
ThingColor.a = 255;
RenderStyle = thing->RenderStyle;
// 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.Clear();
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))
{
RenderStyle.Flags |= STYLEF_InvertSource;
}
}
}
else
{
Colormap=rendersector->ColorMap;
if (fullbright)
{
if (rendersector == &sectors[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)
{
Colormap.Decolorize();
}
}
translation=thing->Translation;
OverrideShader = -1;
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_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;
FRenderStyle rs = RenderStyle;
RenderStyle = STYLE_Translucent;
RenderStyle.Flags = rs.Flags; // Flags must be preserved, at this point it can only be STYLEF_InvertSource
}
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)
&& (actor->renderflags & RF_SPRITETYPEMASK) == RF_FACESPRITE
&& 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.Clear();
}
else if (!particle->bright)
{
TArray<lightlist_t> & lightlist=sector->e->XFloor.lightlist;
int lightbottom;
Colormap = sector->ColorMap;
for(unsigned int i=0;i<lightlist.Size();i++)
{
if (i<lightlist.Size()-1) lightbottom = lightlist[i+1].plane.ZatPoint(particle->x,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;
ThingColor.a = 255;
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, true);
translation = 0;
ul = gltexture->GetUL();
ur = gltexture->GetUR();
vt = gltexture->GetVT();
vb = gltexture->GetVB();
FloatRect r;
gltexture->GetSpriteRect(&r);
}
}
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++;
}

View file

@ -0,0 +1,126 @@
/*
** 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"
//==========================================================================
//
// Sets a single light value from all dynamic lights affecting the specified location
//
//==========================================================================
void gl_SetDynSpriteLight(AActor *self, fixed_t x, fixed_t y, fixed_t z, subsector_t * subsec)
{
ADynamicLight *light;
float frac, lr, lg, lb;
float radius;
float out[3] = { 0.0f, 0.0f, 0.0f };
// Go through both light lists
FLightNode * node = subsec->lighthead;
while (node)
{
light=node->lightsource;
//if (!light->owned || light->target == NULL || light->target->IsVisibleToPlayer())
{
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)
{
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;
}
}
}
}
node = node->nextLight;
}
gl_RenderState.SetDynLight(out[0], out[1], out[2]);
}
void gl_SetDynSpriteLight(AActor *thing, particle_t *particle)
{
if (thing != NULL)
{
gl_SetDynSpriteLight(thing, thing->x, thing->y, thing->z + (thing->height >> 1), thing->subsector);
}
else if (particle != NULL)
{
gl_SetDynSpriteLight(NULL, particle->x, particle->y, particle->z, particle->subsector);
}
}

197
src/gl/scene/gl_vertex.cpp Normal file
View file

@ -0,0 +1,197 @@
/*
** 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/data/gl_vertexbuffer.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)
//==========================================================================
//
// Split upper edge of wall
//
//==========================================================================
void GLWall::SplitUpperEdge(texcoord * tcs, FFlatVertex *&ptr)
{
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;
ptr->x = cseg->v2->fx;
ptr->y = cseg->v2->fy;
ptr->z = ztop[0] + fact * fracfac;
ptr->u = tcs[1].u + facu * fracfac;
ptr->v = tcs[1].v + facv * fracfac;
ptr++;
}
}
//==========================================================================
//
// Split upper edge of wall
//
//==========================================================================
void GLWall::SplitLowerEdge(texcoord * tcs, FFlatVertex *&ptr)
{
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;
ptr->x = cseg->v2->fx;
ptr->y = cseg->v2->fy;
ptr->z = zbottom[0] + facb * fracfac;
ptr->u = tcs[0].u + facu * fracfac;
ptr->v = tcs[0].v + facv * fracfac;
ptr++;
}
}
//==========================================================================
//
// Split left edge of wall
//
//==========================================================================
void GLWall::SplitLeftEdge(texcoord * tcs, FFlatVertex *&ptr)
{
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 (i<vi->numheights && vi->heightlist[i] <= zbottom[0]) i++;
while (i<vi->numheights && vi->heightlist[i] < ztop[0])
{
ptr->x = glseg.x1;
ptr->y = glseg.y1;
ptr->z = vi->heightlist[i];
ptr->u = factu1*(vi->heightlist[i] - ztop[0]) + tcs[1].u;
ptr->v = factv1*(vi->heightlist[i] - ztop[0]) + tcs[1].v;
ptr++;
i++;
}
}
}
//==========================================================================
//
// Split right edge of wall
//
//==========================================================================
void GLWall::SplitRightEdge(texcoord * tcs, FFlatVertex *&ptr)
{
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])
{
ptr->x = glseg.x2;
ptr->y = glseg.y2;
ptr->z = vi->heightlist[i];
ptr->u = factu2*(vi->heightlist[i] - ztop[1]) + tcs[2].u;
ptr->v = factv2*(vi->heightlist[i] - ztop[1]) + tcs[2].v;
ptr++;
i--;
}
}
}

361
src/gl/scene/gl_wall.h Normal file
View file

@ -0,0 +1,361 @@
#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;
struct FFlatVertex;
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_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_GLOW=8, // illuminated by glowing flats
GLWF_NOSPLITUPPER=16,
GLWF_NOSPLITLOWER=32,
GLWF_NOSPLIT=64,
};
enum
{
RWF_BLANK = 0,
RWF_TEXTURED = 1, // actually not being used anymore because with buffers it's even less efficient not writing the texture coordinates - but leave it here
RWF_GLOW = 2,
RWF_NOSPLIT = 4,
RWF_NORENDER = 8,
};
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;
int lightlevel;
BYTE type;
BYTE flags;
short rellight;
float topglowcolor[4];
float bottomglowcolor[4];
int dynlightindex;
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;
secplane_t topplane, bottomplane; // we need to save these to pass them to the shader for calculating glows.
// 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, unsigned int *store = NULL);
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, FFlatVertex *&ptr);
void SplitRightEdge(texcoord * tcs, FFlatVertex *&ptr);
void SplitUpperEdge(texcoord * tcs, FFlatVertex *&ptr);
void SplitLowerEdge(texcoord * tcs, FFlatVertex *&ptr);
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;
void SetupSubsectorLights(int pass, subsector_t * sub, int *dli = NULL);
void DrawSubsector(subsector_t * sub);
void DrawSubsectorLights(subsector_t * sub, int pass);
void DrawSubsectors(int pass, bool processlights, bool istrans);
void ProcessLights(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, bool trans);
};
//==========================================================================
//
// 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();
void PerformSpriteClipAdjustment(AActor *thing, fixed_t thingx, fixed_t thingy, float spriteheight);
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
void gl_SetDynSpriteLight(AActor *self, fixed_t x, fixed_t y, fixed_t z, subsector_t *subsec);
void gl_SetDynSpriteLight(AActor *actor, particle_t *particle);
#endif

1790
src/gl/scene/gl_walls.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,444 @@
/*
** 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_interface.h"
#include "gl/system/gl_cvars.h"
#include "gl/renderer/gl_lightdata.h"
#include "gl/renderer/gl_renderstate.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/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"
#include "gl/utility/gl_clock.h"
#include "gl/utility/gl_templates.h"
EXTERN_CVAR(Bool, gl_seamless)
//==========================================================================
//
// Collect lights for shader
//
//==========================================================================
FDynLightData lightdata;
void GLWall::SetupLights()
{
// check for wall types which cannot have dynamic lights on them (portal types never get here so they don't need to be checked.)
switch (type)
{
case RENDERWALL_FOGBOUNDARY:
case RENDERWALL_MIRRORSURFACE:
case RENDERWALL_COLOR:
case RENDERWALL_COLORLAYER:
return;
}
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;
}
FLightNode *node;
if (seg->sidedef == NULL)
{
node = NULL;
}
else if (!(seg->sidedef->Flags & WALLF_POLYOBJ))
{
node = seg->sidedef->lighthead;
}
else if (sub)
{
// Polobject segs cannot be checked per sidedef so use the subsector instead.
node = sub->lighthead;
}
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, true, false, lightdata);
}
}
}
node = node->nextLight;
}
dynlightindex = GLRenderer->mLights->UploadLights(lightdata);
}
//==========================================================================
//
// General purpose wall rendering function
// everything goes through here
//
//==========================================================================
void GLWall::RenderWall(int textured, unsigned int *store)
{
static texcoord tcs[4]; // making this variable static saves us a relatively costly stack integrity check.
bool split = (gl_seamless && !(textured&RWF_NOSPLIT) && seg->sidedef != NULL && !(seg->sidedef->Flags & WALLF_POLYOBJ) && !(flags & GLWF_NOSPLIT));
tcs[0]=lolft;
tcs[1]=uplft;
tcs[2]=uprgt;
tcs[3]=lorgt;
if ((flags&GLWF_GLOW) && (textured & RWF_GLOW))
{
gl_RenderState.SetGlowPlanes(topplane, bottomplane);
gl_RenderState.SetGlowParams(topglowcolor, bottomglowcolor);
}
if (!(textured & RWF_NORENDER))
{
gl_RenderState.Apply();
gl_RenderState.ApplyLightIndex(dynlightindex);
}
// the rest of the code is identical for textured rendering and lights
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
unsigned int count, offset;
ptr->Set(glseg.x1, zbottom[0], glseg.y1, tcs[0].u, tcs[0].v);
ptr++;
if (split && glseg.fracleft == 0) SplitLeftEdge(tcs, ptr);
ptr->Set(glseg.x1, ztop[0], glseg.y1, tcs[1].u, tcs[1].v);
ptr++;
if (split && !(flags & GLWF_NOSPLITUPPER)) SplitUpperEdge(tcs, ptr);
ptr->Set(glseg.x2, ztop[1], glseg.y2, tcs[2].u, tcs[2].v);
ptr++;
if (split && glseg.fracright == 1) SplitRightEdge(tcs, ptr);
ptr->Set(glseg.x2, zbottom[1], glseg.y2, tcs[3].u, tcs[3].v);
ptr++;
if (split && !(flags & GLWF_NOSPLITLOWER)) SplitLowerEdge(tcs, ptr);
count = GLRenderer->mVBO->GetCount(ptr, &offset);
if (!(textured & RWF_NORENDER))
{
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_FAN, offset, count);
vertexcount += count;
}
if (store != NULL)
{
store[0] = offset;
store[1] = count;
}
}
//==========================================================================
//
//
//
//==========================================================================
void GLWall::RenderFogBoundary()
{
if (gl_fogmode && gl_fixedcolormap == 0)
{
int rel = rellight + getExtraLight();
gl_SetFog(lightlevel, rel, &Colormap, false);
gl_RenderState.SetEffect(EFF_FOGBOUNDARY);
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -128.0f);
RenderWall(RWF_BLANK);
glPolygonOffset(0.0f, 0.0f);
glDisable(GL_POLYGON_OFFSET_FILL);
gl_RenderState.SetEffect(EFF_NONE);
}
}
//==========================================================================
//
//
//
//==========================================================================
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();
// we use texture coordinates and texture matrix to pass the normal stuff to the shader so that the default vertex buffer format can be used as is.
lolft.u = lorgt.u = uplft.u = uprgt.u = v.X();
lolft.v = lorgt.v = uplft.v = uprgt.v = v.Z();
gl_RenderState.EnableTextureMatrix(true);
gl_RenderState.mTextureMatrix.computeNormalMatrix(gl_RenderState.mViewMatrix);
// Use sphere mapping for this
gl_RenderState.SetEffect(EFF_SPHEREMAP);
gl_SetColor(lightlevel, 0, Colormap ,0.1f);
gl_SetFog(lightlevel, 0, &Colormap, true);
gl_RenderState.BlendFunc(GL_SRC_ALPHA,GL_ONE);
gl_RenderState.AlphaFunc(GL_GREATER,0);
glDepthFunc(GL_LEQUAL);
FMaterial * pat=FMaterial::ValidateTexture(GLRenderer->mirrortexture, false);
gl_RenderState.SetMaterial(pat, CLAMP_NONE, 0, -1, false);
flags &= ~GLWF_GLOW;
RenderWall(RWF_BLANK);
gl_RenderState.EnableTextureMatrix(false);
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, gl_mask_sprite_threshold);
glDepthFunc(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)
{
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -128.0f);
glDepthMask(false);
DoDrawDecals();
glDepthMask(true);
glPolygonOffset(0.0f, 0.0f);
glDisable(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 (gl_fixedcolormap == CM_DEFAULT && gl_lights && (gl.flags & RFL_BUFFER_STORAGE))
{
SetupLights();
}
if (!transparent) gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold);
else gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
if (isadditive) gl_RenderState.BlendFunc(GL_SRC_ALPHA,GL_ONE);
int extra;
if (gltexture)
{
gl_RenderState.EnableGlow(!!(flags & GLWF_GLOW));
gl_RenderState.SetMaterial(gltexture, flags & 3, 0, -1, false);
extra = getExtraLight();
}
else
{
gl_RenderState.EnableTexture(false);
extra = 0;
}
int tmode = gl_RenderState.GetTextureMode();
gl_SetColor(lightlevel, extra, Colormap, fabsf(alpha));
if (type!=RENDERWALL_M2SNF) gl_SetFog(lightlevel, extra, &Colormap, isadditive);
else
{
if (flags & GLT_CLAMPY)
{
if (tmode == TM_MODULATE) gl_RenderState.SetTextureMode(TM_CLAMPY);
}
gl_SetFog(255, 0, NULL, false);
}
RenderWall(RWF_TEXTURED|RWF_NOSPLIT);
// restore default settings
if (isadditive) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (!gltexture)
{
gl_RenderState.EnableTexture(true);
}
gl_RenderState.EnableGlow(false);
gl_RenderState.SetTextureMode(tmode);
}
//==========================================================================
//
//
//
//==========================================================================
void GLWall::Draw(int pass)
{
int rel;
int tmode;
#ifdef _DEBUG
if (seg->linedef-lines==879)
{
int a = 0;
}
#endif
switch (pass)
{
case GLPASS_LIGHTSONLY:
SetupLights();
break;
case GLPASS_ALL:
SetupLights();
// fall through
case GLPASS_PLAIN:
rel = rellight + getExtraLight();
gl_SetColor(lightlevel, rel, Colormap,1.0f);
tmode = gl_RenderState.GetTextureMode();
if (type!=RENDERWALL_M2SNF) gl_SetFog(lightlevel, rel, &Colormap, false);
else
{
if (flags & GLT_CLAMPY)
{
if (tmode == TM_MODULATE) gl_RenderState.SetTextureMode(TM_CLAMPY);
}
gl_SetFog(255, 0, NULL, false);
}
gl_RenderState.EnableGlow(!!(flags & GLWF_GLOW));
gl_RenderState.SetMaterial(gltexture, flags & 3, false, -1, false);
RenderWall(RWF_TEXTURED|RWF_GLOW);
gl_RenderState.EnableGlow(false);
gl_RenderState.SetTextureMode(tmode);
break;
case GLPASS_TRANSLUCENT:
switch (type)
{
case RENDERWALL_MIRRORSURFACE:
RenderMirrorSurface();
break;
case RENDERWALL_FOGBOUNDARY:
RenderFogBoundary();
break;
case RENDERWALL_COLORLAYER:
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -128.0f);
RenderTranslucentWall();
glDisable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0, 0);
default:
RenderTranslucentWall();
break;
}
}
}

437
src/gl/scene/gl_weapon.cpp Normal file
View file

@ -0,0 +1,437 @@
/*
** 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_interface.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_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)
EXTERN_CVAR (Bool, r_deathcamera)
//==========================================================================
//
// R_DrawPSprite
//
//==========================================================================
void FGLRenderer::DrawPSprite (player_t * player,pspdef_t *psp,fixed_t sx, fixed_t sy, bool hudModelStep, int OverrideShader, bool alphatexture)
{
float fU1,fV1;
float fU2,fV2;
float tx;
float x1,y1,x2,y2;
float scale;
float scalex;
float ftexturemid;
// 4:3 16:9 16:10 17:10 5:4
static float xratio[] = {1.f, 3.f/4, 5.f/6, 40.f/51, 1.f};
// [BB] In the HUD model step we just render the model and break out.
if ( hudModelStep )
{
gl_RenderHUDModel( psp, sx, sy);
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, true, false);
if (!tex) return;
gl_RenderState.SetMaterial(tex, CLAMP_XY_NOMIP, 0, OverrideShader, alphatexture);
float vw = (float)viewwidth;
float vh = (float)viewheight;
FloatRect r;
tex->GetSpriteRect(&r);
// calculate edges of the shape
scalex = xratio[WidescreenRatio] * vw / 320;
tx = FIXED2FLOAT(sx) - (160 - r.left);
x1 = tx * scalex + vw/2;
if (x1 > vw) return; // off the right side
x1 += viewwindowx;
tx += r.width;
x2 = tx * scalex + vw / 2;
if (x2 < 0) return; // off the left side
x2 += viewwindowx;
// killough 12/98: fix psprite positioning problem
ftexturemid = 100.f - FIXED2FLOAT(sy) - r.top;
AWeapon * wi=player->ReadyWeapon;
if (wi && wi->YAdjust)
{
float fYAd = FIXED2FLOAT(wi->YAdjust);
if (screenblocks >= 11)
{
ftexturemid -= fYAd;
}
else if (!st_scale)
{
ftexturemid -= FIXED2FLOAT(StatusBar->GetDisplacement ()) * fYAd;
}
}
scale = (SCREENHEIGHT*vw) / (SCREENWIDTH * 200.0f);
y1 = viewwindowy + vh / 2 - (ftexturemid * scale);
y2 = y1 + (r.height * scale) + 1;
if (!mirror)
{
fU1=tex->GetSpriteUL();
fV1=tex->GetSpriteVT();
fU2=tex->GetSpriteUR();
fV2=tex->GetSpriteVB();
}
else
{
fU2=tex->GetSpriteUL();
fV1=tex->GetSpriteVT();
fU1=tex->GetSpriteUR();
fV2=tex->GetSpriteVB();
}
if (tex->GetTransparent() || OverrideShader != -1)
{
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
}
gl_RenderState.Apply();
FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(x1, y1, 0, fU1, fV1);
ptr++;
ptr->Set(x1, y2, 0, fU1, fV2);
ptr++;
ptr->Set(x2, y1, 0, fU2, fV1);
ptr++;
ptr->Set(x2, y2, 0, fU2, fV2);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.5f);
}
//==========================================================================
//
// 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;
// this is the same as the software renderer
if (!player ||
!r_drawplayersprites ||
!camera->player ||
(player->cheats & CF_CHASECAM) ||
(r_deathcamera && camera->health <= 0))
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())
{
FMaterial * tex=FMaterial::ValidateTexture(lump, false, false);
if (tex)
disablefullbright = tex->tex->gl_info.bDisableFullbright;
}
statebright[i] = !!psp->state->GetFullbright() && !disablefullbright;
}
}
}
if (gl_fixedcolormap)
{
lightlevel=255;
cm.Clear();
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_t> & lightlist = viewsector->e->XFloor.lightlist;
for(i=0;i<lightlist.Size();i++)
{
int lightbottom;
if (i<lightlist.Size()-1)
{
lightbottom=lightlist[i+1].plane.ZatPoint(viewx,viewy);
}
else
{
lightbottom=viewsector->floorplane.ZatPoint(viewx,viewy);
}
if (lightbottom<player->viewz)
{
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 (but don't cancel out the light color!)
if (glset.brightfog && ((level.flags&LEVEL_HASFADETABLE) || cm.FadeColor != 0))
{
lightlevel = 255;
}
PalEntry ThingColor = (playermo->RenderStyle.Flags & STYLEF_ColorIsFixed) ? playermo->fillcolor : 0xffffff;
ThingColor.a = 255;
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 &&
gl_fixedcolormap == CM_DEFAULT)
{
// this only happens for Strife's inverted weapon sprite
vis.RenderStyle.Flags |= STYLEF_InvertSource;
}
}
// Set the render parameters
int OverrideShader = -1;
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_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);
gl_RenderState.SetObjectColor(ThingColor);
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)
{
cmc.LightColor.r=
cmc.LightColor.g=
cmc.LightColor.b=0xff;
}
else
{
// under water areas keep most of their color for fullbright objects
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
if (vis.RenderStyle.BlendOp == STYLEOP_Shadow)
{
gl_RenderState.SetColor(0.2f, 0.2f, 0.2f, 0.33f, cmc.desaturation);
}
else
{
if (gl_lights && GLRenderer->mLightCount && !gl_fixedcolormap && gl_light_sprites)
{
gl_SetDynSpriteLight(playermo, NULL);
}
gl_SetColor(statebright[i] ? 255 : lightlevel, 0, cmc, trans, true);
}
DrawPSprite(player, psp, psp->sx + ofsx, psp->sy + ofsy, hudModelStep, OverrideShader, !!(vis.RenderStyle.Flags & STYLEF_RedIsAlpha));
}
}
gl_RenderState.SetObjectColor(0xffffffff);
gl_RenderState.SetDynLight(0, 0, 0);
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_RenderState.ResetColor();
gl_RenderState.SetTextureMode(TM_MODULATE);
// The Targeter's sprites are always drawn normally.
for (i=ps_targetcenter, psp = &player->psprites[ps_targetcenter]; i<NUMPSPRITES; i++,psp++)
if (psp->state) DrawPSprite (player,psp,psp->sx, psp->sy, false, 0, false);
}

View file

@ -0,0 +1,630 @@
/*
** 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/system/gl_interface.h"
#include "gl/data/gl_data.h"
#include "gl/data/gl_matrix.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"
#include "gl/dynlights/gl_lightbuffer.h"
//==========================================================================
//
//
//
//==========================================================================
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;
int i_lump = Wads.CheckNumForFullName("shaders/glsl/shaderdefs.i");
if (i_lump == -1) I_Error("Unable to load 'shaders/glsl/shaderdefs.i'");
FMemLump i_data = Wads.ReadLump(i_lump);
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);
//
// The following code uses GetChars on the strings to get rid of terminating 0 characters. Do not remove or the code may break!
//
unsigned int lightbuffertype = GLRenderer->mLights->GetBufferType();
unsigned int lightbuffersize = GLRenderer->mLights->GetBlockSize();
FString vp_comb;
if (lightbuffertype == GL_UNIFORM_BUFFER)
{
if (gl.glslversion < 1.4f || gl.version < 3.1f)
{
vp_comb.Format("#version 130\n#extension GL_ARB_uniform_buffer_object : require\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize);
}
else
{
vp_comb.Format("#version 140\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize);
}
}
else
{
vp_comb = "#version 400 core\n#extension GL_ARB_shader_storage_buffer_object : require\n#define SHADER_STORAGE_LIGHTS\n";
}
vp_comb << defines << i_data.GetString().GetChars();
FString fp_comb = vp_comb;
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);
if (pp_data.GetString().IndexOf("ProcessTexel") < 0)
{
// this looks like an old custom hardware shader.
// We need to replace the ProcessTexel call to make it work.
fp_comb.Substitute("vec4 frag = ProcessTexel();", "vec4 frag = Process(vec4(1.0));");
}
fp_comb << pp_data.GetString().GetChars();
fp_comb.Substitute("gl_TexCoord[0]", "vTexCoord"); // fix old custom shaders.
if (pp_data.GetString().IndexOf("ProcessLight") < 0)
{
int pl_lump = Wads.CheckNumForFullName("shaders/glsl/func_defaultlight.fp");
if (pl_lump == -1) I_Error("Unable to load '%s'", "shaders/glsl/func_defaultlight.fp");
FMemLump pl_data = Wads.ReadLump(pl_lump);
fp_comb << "\n" << pl_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 = glCreateShader(GL_VERTEX_SHADER);
hFragProg = glCreateShader(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();
glShaderSource(hVertProg, 1, &vp_ptr, &vp_size);
glShaderSource(hFragProg, 1, &fp_ptr, &fp_size);
glCompileShader(hVertProg);
glCompileShader(hFragProg);
hShader = glCreateProgram();
glAttachShader(hShader, hVertProg);
glAttachShader(hShader, hFragProg);
glBindAttribLocation(hShader, VATTR_VERTEX, "aPosition");
glBindAttribLocation(hShader, VATTR_TEXCOORD, "aTexCoord");
glBindAttribLocation(hShader, VATTR_COLOR, "aColor");
glBindAttribLocation(hShader, VATTR_VERTEX2, "aVertex2");
glLinkProgram(hShader);
glGetShaderInfoLog(hVertProg, 10000, NULL, buffer);
if (*buffer)
{
error << "Vertex shader:\n" << buffer << "\n";
}
glGetShaderInfoLog(hFragProg, 10000, NULL, buffer);
if (*buffer)
{
error << "Fragment shader:\n" << buffer << "\n";
}
glGetProgramInfoLog(hShader, 10000, NULL, buffer);
if (*buffer)
{
error << "Linking:\n" << buffer << "\n";
}
int linked;
glGetProgramiv(hShader, GL_LINK_STATUS, &linked);
if (linked == 0)
{
// only print message if there's an error.
I_Error("Init Shader '%s':\n%s\n", name, error.GetChars());
}
muDesaturation.Init(hShader, "uDesaturationFactor");
muFogEnabled.Init(hShader, "uFogEnabled");
muTextureMode.Init(hShader, "uTextureMode");
muCameraPos.Init(hShader, "uCameraPos");
muLightParms.Init(hShader, "uLightAttr");
muClipSplit.Init(hShader, "uClipSplit");
muColormapStart.Init(hShader, "uFixedColormapStart");
muColormapRange.Init(hShader, "uFixedColormapRange");
muLightIndex.Init(hShader, "uLightIndex");
muFogColor.Init(hShader, "uFogColor");
muDynLightColor.Init(hShader, "uDynLightColor");
muObjectColor.Init(hShader, "uObjectColor");
muGlowBottomColor.Init(hShader, "uGlowBottomColor");
muGlowTopColor.Init(hShader, "uGlowTopColor");
muGlowBottomPlane.Init(hShader, "uGlowBottomPlane");
muGlowTopPlane.Init(hShader, "uGlowTopPlane");
muFixedColormap.Init(hShader, "uFixedColormap");
muInterpolationFactor.Init(hShader, "uInterpolationFactor");
muClipHeightTop.Init(hShader, "uClipHeightTop");
muClipHeightBottom.Init(hShader, "uClipHeightBottom");
muAlphaThreshold.Init(hShader, "uAlphaThreshold");
muTimer.Init(hShader, "timer");
lights_index = glGetUniformLocation(hShader, "lights");
fakevb_index = glGetUniformLocation(hShader, "fakeVB");
projectionmatrix_index = glGetUniformLocation(hShader, "ProjectionMatrix");
viewmatrix_index = glGetUniformLocation(hShader, "ViewMatrix");
modelmatrix_index = glGetUniformLocation(hShader, "ModelMatrix");
texturematrix_index = glGetUniformLocation(hShader, "TextureMatrix");
int tempindex = glGetUniformBlockIndex(hShader, "LightBufferUBO");
if (tempindex != -1) glUniformBlockBinding(hShader, tempindex, LIGHTBUF_BINDINGPOINT);
glUseProgram(hShader);
// set up other texture units (if needed by the shader)
for (int i = 2; i<16; i++)
{
char stringbuf[20];
mysnprintf(stringbuf, 20, "texture%d", i);
tempindex = glGetUniformLocation(hShader, stringbuf);
if (tempindex > 0) glUniform1i(tempindex, i - 1);
}
glUseProgram(0);
return !!linked;
}
//==========================================================================
//
//
//
//==========================================================================
FShader::~FShader()
{
glDeleteProgram(hShader);
glDeleteShader(hVertProg);
glDeleteShader(hFragProg);
}
//==========================================================================
//
//
//
//==========================================================================
bool FShader::Bind()
{
GLRenderer->mShaderManager->SetActiveShader(this);
return true;
}
//==========================================================================
//
// Since all shaders are REQUIRED, any error here needs to be fatal
//
//==========================================================================
FShader *FShaderManager::Compile (const char *ShaderName, const char *ShaderPath, bool usediscard)
{
FString defines;
// this can't be in the shader code due to ATI strangeness.
if (gl.MaxLights() == 128) defines += "#define MAXLIGHTS128\n";
if (!usediscard) defines += "#define NO_ALPHATEST\n";
FShader *shader = NULL;
try
{
shader = new FShader(ShaderName);
if (!shader->Load(ShaderName, "shaders/glsl/main.vp", "shaders/glsl/main.fp", ShaderPath, defines.GetChars()))
{
I_FatalError("Unable to load shader %s\n", ShaderName);
}
}
catch(CRecoverableError &err)
{
if (shader != NULL) delete shader;
shader = NULL;
I_FatalError("Unable to load shader %s:\n%s\n", ShaderName, err.GetMessage());
}
return shader;
}
//==========================================================================
//
//
//
//==========================================================================
void FShader::ApplyMatrices(VSMatrix *proj, VSMatrix *view)
{
Bind();
glUniformMatrix4fv(projectionmatrix_index, 1, false, proj->get());
glUniformMatrix4fv(viewmatrix_index, 1, false, view->get());
}
//==========================================================================
//
//
//
//==========================================================================
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<FString> 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_ALPHATEST\n" },
{ "spheremap", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "#define SPHEREMAP\n#define NO_ALPHATEST\n" },
{ "burn", "shaders/glsl/main.vp", "shaders/glsl/burn.fp", NULL, "#define SIMPLE\n#define NO_ALPHATEST\n" },
{ "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", NULL, "#define SIMPLE\n#define NO_ALPHATEST\n" },
};
//==========================================================================
//
//
//
//==========================================================================
FShaderManager::FShaderManager()
{
CompileShaders();
}
//==========================================================================
//
//
//
//==========================================================================
FShaderManager::~FShaderManager()
{
Clean();
}
//==========================================================================
//
//
//
//==========================================================================
void FShaderManager::CompileShaders()
{
mActiveShader = NULL;
mTextureEffects.Clear();
mTextureEffectsNAT.Clear();
for (int i = 0; i < MAX_EFFECTS; i++)
{
mEffectShaders[i] = NULL;
}
for(int i=0;defaultshaders[i].ShaderName != NULL;i++)
{
FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, true);
mTextureEffects.Push(shc);
if (i <= 3)
{
FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, false);
mTextureEffectsNAT.Push(shc);
}
}
for(unsigned i = 0; i < usershaders.Size(); i++)
{
FString name = ExtractFileBase(usershaders[i]);
FName sfn = name;
FShader *shc = Compile(sfn, usershaders[i], true);
mTextureEffects.Push(shc);
}
for(int i=0;i<MAX_EFFECTS;i++)
{
FShader *eff = new FShader(effectshaders[i].ShaderName);
if (!eff->Load(effectshaders[i].ShaderName, effectshaders[i].vp, effectshaders[i].fp1,
effectshaders[i].fp2, effectshaders[i].defines))
{
delete eff;
}
else mEffectShaders[i] = eff;
}
}
//==========================================================================
//
//
//
//==========================================================================
void FShaderManager::Clean()
{
glUseProgram(0);
mActiveShader = NULL;
for (unsigned int i = 0; i < mTextureEffectsNAT.Size(); i++)
{
if (mTextureEffectsNAT[i] != NULL) delete mTextureEffectsNAT[i];
}
for (unsigned int i = 0; i < mTextureEffects.Size(); i++)
{
if (mTextureEffects[i] != NULL) delete mTextureEffects[i];
}
for (int i = 0; i < MAX_EFFECTS; i++)
{
if (mEffectShaders[i] != NULL) delete mEffectShaders[i];
mEffectShaders[i] = NULL;
}
mTextureEffects.Clear();
mTextureEffectsNAT.Clear();
}
//==========================================================================
//
//
//
//==========================================================================
int FShaderManager::Find(const char * shn)
{
FName sfn = shn;
for(unsigned int i=0;i<mTextureEffects.Size();i++)
{
if (mTextureEffects[i]->mName == sfn)
{
return i;
}
}
return -1;
}
//==========================================================================
//
//
//
//==========================================================================
void FShaderManager::SetActiveShader(FShader *sh)
{
if (mActiveShader != sh)
{
glUseProgram(sh!= NULL? sh->GetHandle() : 0);
mActiveShader = sh;
}
}
//==========================================================================
//
//
//
//==========================================================================
FShader *FShaderManager::BindEffect(int effect)
{
if (effect >= 0 && effect < MAX_EFFECTS && mEffectShaders[effect] != NULL)
{
mEffectShaders[effect]->Bind();
return mEffectShaders[effect];
}
return NULL;
}
//==========================================================================
//
//
//
//==========================================================================
EXTERN_CVAR(Int, gl_fuzztype)
void FShaderManager::ApplyMatrices(VSMatrix *proj, VSMatrix *view)
{
for (int i = 0; i < 4; i++)
{
mTextureEffects[i]->ApplyMatrices(proj, view);
mTextureEffectsNAT[i]->ApplyMatrices(proj, view);
}
mTextureEffects[4]->ApplyMatrices(proj, view);
if (gl_fuzztype != 0)
{
mTextureEffects[4+gl_fuzztype]->ApplyMatrices(proj, view);
}
for (unsigned i = 12; i < mTextureEffects.Size(); i++)
{
mTextureEffects[i]->ApplyMatrices(proj, view);
}
for (int i = 0; i < MAX_EFFECTS; i++)
{
mEffectShaders[i]->ApplyMatrices(proj, view);
}
if (mActiveShader != NULL) mActiveShader->Bind();
}
//==========================================================================
//
//
//
//==========================================================================
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.GetChars());
return;
}
tex->gl_info.shaderspeed = speed;
for(unsigned i=0;i<usershaders.Size();i++)
{
if (!usershaders[i].CompareNoCase(maplumpname))
{
tex->gl_info.shaderindex = i + FIRST_USER_SHADER;
return;
}
}
tex->gl_info.shaderindex = usershaders.Push(maplumpname) + FIRST_USER_SHADER;
}
}

330
src/gl/shaders/gl_shader.h Normal file
View file

@ -0,0 +1,330 @@
#ifndef __GL_SHADERS_H__
#define __GL_SHADERS_H__
#include "gl/renderer/gl_renderstate.h"
#include "name.h"
extern bool gl_shaderactive;
enum
{
VATTR_VERTEX = 0,
VATTR_TEXCOORD = 1,
VATTR_COLOR = 2,
VATTR_VERTEX2 = 3
};
//==========================================================================
//
//
//==========================================================================
class FUniform1i
{
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
}
void Set(int newvalue)
{
glUniform1i(mIndex, newvalue);
}
};
class FBufferedUniform1i
{
int mBuffer;
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
mBuffer = 0;
}
void Set(int newvalue)
{
if (newvalue != mBuffer)
{
mBuffer = newvalue;
glUniform1i(mIndex, newvalue);
}
}
};
class FBufferedUniform4i
{
int mBuffer[4];
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
memset(mBuffer, 0, sizeof(mBuffer));
}
void Set(const int *newvalue)
{
if (memcmp(newvalue, mBuffer, sizeof(mBuffer)))
{
memcpy(mBuffer, newvalue, sizeof(mBuffer));
glUniform4iv(mIndex, 1, newvalue);
}
}
};
class FBufferedUniform1f
{
float mBuffer;
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
mBuffer = 0;
}
void Set(float newvalue)
{
if (newvalue != mBuffer)
{
mBuffer = newvalue;
glUniform1f(mIndex, newvalue);
}
}
};
class FBufferedUniform2f
{
float mBuffer[2];
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
memset(mBuffer, 0, sizeof(mBuffer));
}
void Set(const float *newvalue)
{
if (memcmp(newvalue, mBuffer, sizeof(mBuffer)))
{
memcpy(mBuffer, newvalue, sizeof(mBuffer));
glUniform2fv(mIndex, 1, newvalue);
}
}
};
class FBufferedUniform4f
{
float mBuffer[4];
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
memset(mBuffer, 0, sizeof(mBuffer));
}
void Set(const float *newvalue)
{
if (memcmp(newvalue, mBuffer, sizeof(mBuffer)))
{
memcpy(mBuffer, newvalue, sizeof(mBuffer));
glUniform4fv(mIndex, 1, newvalue);
}
}
};
class FUniform4f
{
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
}
void Set(const float *newvalue)
{
glUniform4fv(mIndex, 1, newvalue);
}
void Set(float a, float b, float c, float d)
{
glUniform4f(mIndex, a, b, c, d);
}
};
class FBufferedUniformPE
{
PalEntry mBuffer;
int mIndex;
public:
void Init(GLuint hShader, const GLchar *name)
{
mIndex = glGetUniformLocation(hShader, name);
mBuffer = 0;
}
void Set(PalEntry newvalue)
{
if (newvalue != mBuffer)
{
mBuffer = newvalue;
glUniform4f(mIndex, newvalue.r/255.f, newvalue.g/255.f, newvalue.b/255.f, newvalue.a/255.f);
}
}
};
class FShader
{
friend class FShaderManager;
friend class FRenderState;
unsigned int hShader;
unsigned int hVertProg;
unsigned int hFragProg;
FName mName;
FBufferedUniform1f muDesaturation;
FBufferedUniform1i muFogEnabled;
FBufferedUniform1i muTextureMode;
FBufferedUniform4f muCameraPos;
FBufferedUniform4f muLightParms;
FBufferedUniform2f muClipSplit;
FUniform1i muFixedColormap;
FUniform4f muColormapStart;
FUniform4f muColormapRange;
FBufferedUniform1i muLightIndex;
FBufferedUniformPE muFogColor;
FBufferedUniform4f muDynLightColor;
FBufferedUniformPE muObjectColor;
FUniform4f muGlowBottomColor;
FUniform4f muGlowTopColor;
FUniform4f muGlowBottomPlane;
FUniform4f muGlowTopPlane;
FBufferedUniform1f muInterpolationFactor;
FBufferedUniform1f muClipHeightTop;
FBufferedUniform1f muClipHeightBottom;
FBufferedUniform1f muAlphaThreshold;
FBufferedUniform1f muTimer;
int lights_index;
int projectionmatrix_index;
int viewmatrix_index;
int modelmatrix_index;
int texturematrix_index;
public:
int fakevb_index;
private:
int currentglowstate;
int currentfixedcolormap;
bool currentTextureMatrixState;
bool currentModelMatrixState;
public:
FShader(const char *name)
: mName(name)
{
hShader = hVertProg = hFragProg = 0;
currentglowstate = 0;
currentfixedcolormap = 0;
currentTextureMatrixState = true; // by setting the matrix state to 'true' it is guaranteed to be set the first time the render state gets applied.
currentModelMatrixState = true;
}
~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();
unsigned int GetHandle() const { return hShader; }
void ApplyMatrices(VSMatrix *proj, VSMatrix *view);
};
//==========================================================================
//
// The global shader manager
//
//==========================================================================
class FShaderManager
{
TArray<FShader*> mTextureEffects;
TArray<FShader*> mTextureEffectsNAT;
FShader *mActiveShader;
FShader *mEffectShaders[MAX_EFFECTS];
void Clean();
void CompileShaders();
public:
FShaderManager();
~FShaderManager();
FShader *Compile(const char *ShaderName, const char *ShaderPath, bool usediscard);
int Find(const char *mame);
FShader *BindEffect(int effect);
void SetActiveShader(FShader *sh);
void ApplyMatrices(VSMatrix *proj, VSMatrix *view);
FShader *GetActiveShader() const
{
return mActiveShader;
}
void ResetFixedColormap()
{
for (unsigned i = 0; i < mTextureEffects.Size(); i++)
{
mTextureEffects[i]->currentfixedcolormap = -1;
}
for (unsigned i = 0; i < mTextureEffectsNAT.Size(); i++)
{
mTextureEffectsNAT[i]->currentfixedcolormap = -1;
}
}
FShader *Get(unsigned int eff, bool alphateston)
{
// indices 0-2 match the warping modes, 3 is brightmap, 4 no texture, the following are custom
if (!alphateston && eff <= 3)
{
return mTextureEffectsNAT[eff]; // Non-alphatest shaders are only created for default, warp1+2 and brightmap. The rest won't get used anyway
}
if (eff < mTextureEffects.Size())
{
return mTextureEffects[eff];
}
return NULL;
}
};
#define FIRST_USER_SHADER 12
enum
{
LIGHTBUF_BINDINGPOINT = 1
};
#endif

View file

@ -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<FTextureID> &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; i<layers.Size(); i++)
{
compose.AppendFormat("@%de%ds%ud%ut%dw%d", i, layers[i]->emissive,
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; i<layers.Size(); i++)
{
compose.AppendFormat("src = %s(texture%d, glTexCoord[%d].st) * colors[%d];\n",
funcnames[layers[i]->warp], 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;
}

View file

@ -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<FTextureID> &names);
bool Available();
bool Setup(float time);
void Update(int framems);
void FakeUpdate(int framems);
FString CreateName();
FString GenerateCode();
FName name;
TDeletingArray <FShaderLayer *> layers; // layers for shader
unsigned int lastUpdate;
};
/*
//extern TArray<FShader *> Shaders[NUM_ShaderClasses];
//extern TArray<FShader *> 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__

View file

@ -0,0 +1,64 @@
/*
** gl_anaglyph.cpp
** Color mask based stereoscopic 3D modes for GZDoom
**
**---------------------------------------------------------------------------
** Copyright 2015 Christopher Bruns
** 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_anaglyph.h"
namespace s3d {
MaskAnaglyph::MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters)
: leftEye(leftColorMask, ipdMeters), rightEye(leftColorMask.inverse(), ipdMeters)
{
eye_ptrs.Push(&leftEye);
eye_ptrs.Push(&rightEye);
}
/* static */
const GreenMagenta& GreenMagenta::getInstance(FLOATTYPE ipd)
{
static GreenMagenta instance(ipd);
return instance;
}
/* static */
const RedCyan& RedCyan::getInstance(FLOATTYPE ipd)
{
static RedCyan instance(ipd);
return instance;
}
} /* namespace s3d */

View file

@ -0,0 +1,124 @@
/*
** gl_anaglyph.h
** Color mask based stereoscopic 3D modes for GZDoom
**
**---------------------------------------------------------------------------
** Copyright 2015 Christopher Bruns
** 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_ANAGLYPH_H_
#define GL_ANAGLYPH_H_
#include "gl_stereo3d.h"
#include "gl_stereo_leftright.h"
#include "gl/system/gl_system.h"
#include "gl/renderer/gl_renderstate.h"
namespace s3d {
class ColorMask
{
public:
ColorMask(bool r, bool g, bool b) : r(r), g(g), b(b) {}
ColorMask inverse() const { return ColorMask(!r, !g, !b); }
bool r;
bool g;
bool b;
};
class AnaglyphLeftPose : public LeftEyePose
{
public:
AnaglyphLeftPose(const ColorMask& colorMask, float ipd) : LeftEyePose(ipd), colorMask(colorMask) {}
virtual void SetUp() const {
gl_RenderState.SetColorMask(colorMask.r, colorMask.g, colorMask.b, true);
gl_RenderState.ApplyColorMask();
}
virtual void TearDown() const {
gl_RenderState.ResetColorMask();
gl_RenderState.ApplyColorMask();
}
private:
ColorMask colorMask;
};
class AnaglyphRightPose : public RightEyePose
{
public:
AnaglyphRightPose(const ColorMask& colorMask, float ipd) : RightEyePose(ipd), colorMask(colorMask) {}
virtual void SetUp() const {
gl_RenderState.SetColorMask(colorMask.r, colorMask.g, colorMask.b, true);
gl_RenderState.ApplyColorMask();
}
virtual void TearDown() const {
gl_RenderState.ResetColorMask();
gl_RenderState.ApplyColorMask();
}
private:
ColorMask colorMask;
};
class MaskAnaglyph : public Stereo3DMode
{
public:
MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters);
private:
AnaglyphLeftPose leftEye;
AnaglyphRightPose rightEye;
};
class RedCyan : public MaskAnaglyph
{
public:
static const RedCyan& getInstance(float ipd);
RedCyan(float ipd) : MaskAnaglyph(ColorMask(true, false, false), ipd) {}
};
class GreenMagenta : public MaskAnaglyph
{
public:
static const GreenMagenta& getInstance(float ipd);
GreenMagenta(float ipd) : MaskAnaglyph(ColorMask(false, true, false), ipd) {}
};
// TODO matrix anaglyph
} /* namespace st3d */
#endif /* GL_ANAGLYPH_H_ */

View file

@ -0,0 +1,91 @@
/*
** gl_stereo3d.cpp
** Stereoscopic 3D API
**
**---------------------------------------------------------------------------
** Copyright 2015 Christopher Bruns
** 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/stereo3d/gl_stereo3d.h"
#include "vectors.h" // RAD2DEG
#include "doomtype.h" // M_PI
namespace s3d {
/* virtual */
VSMatrix EyePose::GetProjection(FLOATTYPE fov, FLOATTYPE aspectRatio, FLOATTYPE fovRatio) const
{
VSMatrix result;
// Lifted from gl_scene.cpp FGLRenderer::SetProjection()
float fovy = (float)(2 * RAD2DEG(atan(tan(DEG2RAD(fov) / 2) / fovRatio)));
result.perspective(fovy, aspectRatio, 5.f, 65536.f);
return result;
}
/* virtual */
Viewport EyePose::GetViewport(const Viewport& fullViewport) const
{
return fullViewport;
}
/* virtual */
void EyePose::GetViewShift(FLOATTYPE yaw, FLOATTYPE outViewShift[3]) const
{
// pass-through for Mono view
outViewShift[0] = 0;
outViewShift[1] = 0;
outViewShift[2] = 0;
}
Stereo3DMode::Stereo3DMode()
{
}
Stereo3DMode::~Stereo3DMode()
{
}
// Avoid static initialization order fiasco by declaring first Mode type (Mono) here in the
// same source file as Stereo3DMode::getCurrentMode()
// https://isocpp.org/wiki/faq/ctors#static-init-order
/* static */
const MonoView& MonoView::getInstance()
{
static MonoView instance;
return instance;
}
} /* namespace s3d */

View file

@ -0,0 +1,113 @@
/*
** gl_stereo3d.h
** Stereoscopic 3D API
**
**---------------------------------------------------------------------------
** Copyright 2015 Christopher Bruns
** 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_STEREO3D_H_
#define GL_STEREO3D_H_
#include <cstring> // needed for memcpy on linux, which is needed by VSMatrix copy ctor
#include "tarray.h"
#include "gl/data/gl_matrix.h"
/* stereoscopic 3D API */
namespace s3d {
/* Subregion of current display window */
class Viewport
{
public:
int x, y;
int width, height;
};
/* Viewpoint of one eye */
class EyePose
{
public:
EyePose() {}
virtual ~EyePose() {}
virtual VSMatrix GetProjection(FLOATTYPE fov, FLOATTYPE aspectRatio, FLOATTYPE fovRatio) const;
virtual Viewport GetViewport(const Viewport& fullViewport) const;
virtual void GetViewShift(FLOATTYPE yaw, FLOATTYPE outViewShift[3]) const;
virtual void SetUp() const {};
virtual void TearDown() const {};
};
/* Base class for stereoscopic 3D rendering modes */
class Stereo3DMode
{
public:
/* static methods for managing the selected stereoscopic view state */
static const Stereo3DMode& getCurrentMode();
Stereo3DMode();
virtual ~Stereo3DMode();
virtual int eye_count() const { return eye_ptrs.Size(); }
virtual const EyePose * getEyePose(int ix) const { return eye_ptrs(ix); }
/* hooks for setup and cleanup operations for each stereo mode */
virtual void SetUp() const {};
virtual void TearDown() const {};
protected:
TArray<const EyePose *> eye_ptrs;
private:
static Stereo3DMode const * currentStereo3DMode;
static void setCurrentMode(const Stereo3DMode& mode);
};
/**
* Ordinary non-3D rendering
*/
class MonoView : public Stereo3DMode
{
public:
static const MonoView& getInstance();
protected:
MonoView() { eye_ptrs.Push(&centralEye); }
EyePose centralEye;
};
} /* namespace st3d */
#endif /* GL_STEREO3D_H_ */

View file

@ -0,0 +1,92 @@
/*
** gl_stereo_cvars.cpp
** Console variables related to stereoscopic 3D in GZDoom
**
**---------------------------------------------------------------------------
** Copyright 2015 Christopher Bruns
** 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/stereo3d/gl_stereo3d.h"
#include "gl/stereo3d/gl_stereo_leftright.h"
#include "gl/stereo3d/gl_anaglyph.h"
#include "gl/system/gl_cvars.h"
// Set up 3D-specific console variables:
CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG)
// intraocular distance in meters
CVAR(Float, vr_ipd, 0.062f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // METERS
// distance between viewer and the display screen
CVAR(Float, vr_screendist, 0.80f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS
// default conversion between (vertical) DOOM units and meters
CVAR(Float, vr_hunits_per_meter, 41.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // METERS
// Manage changing of 3D modes:
namespace s3d {
// Initialize static member
Stereo3DMode const * Stereo3DMode::currentStereo3DMode = 0; // "nullptr" not resolved on linux (presumably not C++11)
/* static */
void Stereo3DMode::setCurrentMode(const Stereo3DMode& mode) {
Stereo3DMode::currentStereo3DMode = &mode;
}
/* static */
const Stereo3DMode& Stereo3DMode::getCurrentMode()
{
// NOTE: Ensure that these vr_mode values correspond to the ones in wadsrc/static/menudef.z
switch (vr_mode)
{
case 1:
setCurrentMode(GreenMagenta::getInstance(vr_ipd));
break;
case 2:
setCurrentMode(RedCyan::getInstance(vr_ipd));
break;
// TODO: missing indices 3, 4 for not-yet-implemented side-by-side modes, to match values from GZ3Doom
case 5:
setCurrentMode(LeftEyeView::getInstance(vr_ipd));
break;
case 6:
setCurrentMode(RightEyeView::getInstance(vr_ipd));
break;
case 0:
default:
setCurrentMode(MonoView::getInstance());
break;
}
return *currentStereo3DMode;
}
} /* namespace s3d */

View file

@ -0,0 +1,101 @@
/*
** gl_stereo_leftright.cpp
** Offsets for left and right eye views
**
**---------------------------------------------------------------------------
** Copyright 2015 Christopher Bruns
** 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_stereo_leftright.h"
#include "vectors.h" // RAD2DEG
#include "doomtype.h" // M_PI
#include "gl/system/gl_cvars.h"
#include <cmath>
EXTERN_CVAR(Float, vr_screendist)
EXTERN_CVAR(Float, vr_hunits_per_meter)
namespace s3d {
/* virtual */
VSMatrix ShiftedEyePose::GetProjection(FLOATTYPE fov, FLOATTYPE aspectRatio, FLOATTYPE fovRatio) const
{
double zNear = 5.0;
double zFar = 65536.0;
// For stereo 3D, use asymmetric frustum shift in projection matrix
// Q: shouldn't shift vary with roll angle, at least for desktop display?
// A: No. (lab) roll is not measured on desktop display (yet)
double frustumShift = zNear * shift / vr_screendist; // meters cancel, leaving doom units
// double frustumShift = 0; // Turning off shift for debugging
double fH = zNear * tan(DEG2RAD(fov) / 2) / fovRatio;
double fW = fH * aspectRatio;
double left = -fW - frustumShift;
double right = fW - frustumShift;
double bottom = -fH;
double top = fH;
VSMatrix result(1);
result.frustum(left, right, bottom, top, zNear, zFar);
return result;
}
/* virtual */
void ShiftedEyePose::GetViewShift(FLOATTYPE yaw, FLOATTYPE outViewShift[3]) const
{
FLOATTYPE dx = cos(DEG2RAD(yaw)) * vr_hunits_per_meter * shift;
FLOATTYPE dy = sin(DEG2RAD(yaw)) * vr_hunits_per_meter * shift;
outViewShift[0] = dx;
outViewShift[1] = dy;
outViewShift[2] = 0;
}
/* static */
const LeftEyeView& LeftEyeView::getInstance(FLOATTYPE ipd)
{
static LeftEyeView instance(ipd);
instance.setIpd(ipd);
return instance;
}
/* static */
const RightEyeView& RightEyeView::getInstance(FLOATTYPE ipd)
{
static RightEyeView instance(ipd);
instance.setIpd(ipd);
return instance;
}
} /* namespace s3d */

View file

@ -0,0 +1,106 @@
/*
** gl_stereo_leftright.h
** Offsets for left and right eye views
**
**---------------------------------------------------------------------------
** Copyright 2015 Christopher Bruns
** 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_STEREO_LEFTRIGHT_H_
#define GL_STEREO_LEFTRIGHT_H_
#include "gl_stereo3d.h"
namespace s3d {
class ShiftedEyePose : public EyePose
{
public:
ShiftedEyePose(FLOATTYPE shift) : shift(shift) {};
FLOATTYPE getShift() const { return shift; }
void setShift(FLOATTYPE shift) { this->shift = shift; }
virtual VSMatrix GetProjection(FLOATTYPE fov, FLOATTYPE aspectRatio, FLOATTYPE fovRatio) const;
virtual void GetViewShift(FLOATTYPE yaw, FLOATTYPE outViewShift[3]) const;
protected:
FLOATTYPE shift;
};
class LeftEyePose : public ShiftedEyePose
{
public:
LeftEyePose(FLOATTYPE ipd) : ShiftedEyePose( FLOATTYPE(-0.5) * ipd) {}
FLOATTYPE getIpd() const { return FLOATTYPE(-2.0)*getShift(); }
void setIpd(FLOATTYPE ipd) { setShift(FLOATTYPE(-0.5)*ipd); }
};
class RightEyePose : public ShiftedEyePose
{
public:
RightEyePose(FLOATTYPE ipd) : ShiftedEyePose(FLOATTYPE(+0.5)*ipd) {}
FLOATTYPE getIpd() const { return FLOATTYPE(+2.0)*shift; }
void setIpd(FLOATTYPE ipd) { setShift(FLOATTYPE(+0.5)*ipd); }
};
/**
* As if viewed through the left eye only
*/
class LeftEyeView : public Stereo3DMode
{
public:
static const LeftEyeView& getInstance(FLOATTYPE ipd);
LeftEyeView(FLOATTYPE ipd) : eye(ipd) { eye_ptrs.Push(&eye); }
FLOATTYPE getIpd() const { return eye.getIpd(); }
void setIpd(FLOATTYPE ipd) { eye.setIpd(ipd); }
protected:
LeftEyePose eye;
};
class RightEyeView : public Stereo3DMode
{
public:
static const RightEyeView& getInstance(FLOATTYPE ipd);
RightEyeView(FLOATTYPE ipd) : eye(ipd) { eye_ptrs.Push(&eye); }
FLOATTYPE getIpd() const { return eye.getIpd(); }
void setIpd(FLOATTYPE ipd) { eye.setIpd(ipd); }
protected:
RightEyePose eye;
};
} /* namespace s3d */
#endif /* GL_STEREO_LEFTRIGHT_H_ */

View file

@ -0,0 +1,62 @@
/*
** scoped_color_mask.h
** Stack-scoped class for temporarily changing the OpenGL color mask setting.
**
**---------------------------------------------------------------------------
** Copyright 2015 Christopher Bruns
** 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_STEREO3D_SCOPED_COLOR_MASK_H_
#define GL_STEREO3D_SCOPED_COLOR_MASK_H_
#include "gl/system/gl_system.h"
/**
* Temporarily change color mask
*/
class ScopedColorMask
{
public:
ScopedColorMask(bool r, bool g, bool b, bool a)
{
gl_RenderState.GetColorMask(saved[0], saved[1], saved[2], saved[3]);
gl_RenderState.SetColorMask(r, g, b, a);
gl_RenderState.ApplyColorMask();
}
~ScopedColorMask() {
gl_RenderState.SetColorMask(saved[0], saved[1], saved[2], saved[3]);
gl_RenderState.ApplyColorMask();
}
private:
bool saved[4];
};
#endif // GL_STEREO3D_SCOPED_COLOR_MASK_H_

View file

@ -0,0 +1,65 @@
/*
** scoped_view_shifter.cpp
** Stack-scoped class for temporarily changing player viewpoint global variables viewx, viewy, viewz.
** Used for stereoscopic 3D.
**
**---------------------------------------------------------------------------
** Copyright 2015 Christopher Bruns
** 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 "scoped_view_shifter.h"
#include "r_utility.h"
namespace s3d {
ScopedViewShifter::ScopedViewShifter(float dxyz[3]) // in meters
{
// save original values
cachedViewx = viewx;
cachedViewy = viewy;
cachedViewz = viewz;
// modify values
float fViewx = FIXED2FLOAT(viewx) - dxyz[0];
float fViewy = FIXED2FLOAT(viewy) + dxyz[1];
float fViewz = FIXED2FLOAT(viewz) + dxyz[2];
viewx = FLOAT2FIXED(fViewx);
viewy = FLOAT2FIXED(fViewy);
viewz = FLOAT2FIXED(fViewz);
}
ScopedViewShifter::~ScopedViewShifter()
{
// restore original values
viewx = cachedViewx;
viewy = cachedViewy;
viewz = cachedViewz;
}
}

View file

@ -0,0 +1,61 @@
/*
** scoped_view_shifter.h
** Stack-scoped class for temporarily changing player viewpoint global variables viewx, viewy, viewz.
** Used for stereoscopic 3D.
**
**---------------------------------------------------------------------------
** Copyright 2015 Christopher Bruns
** 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_STEREO3D_SCOPED_VIEW_SHIFTER_H_
#define GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_
#include "basictypes.h"
namespace s3d {
/**
* Temporarily shift viewx, viewy, viewz
*/
class ScopedViewShifter
{
public:
ScopedViewShifter(float dxyz[3]); // in meters
~ScopedViewShifter();
private:
fixed_t cachedViewx;
fixed_t cachedViewy;
fixed_t cachedViewz;
};
} /* namespace s3d */
#endif // GL_STEREO3D_SCOPED_VIEW_SHIFTER_H_

44
src/gl/system/gl_cvars.h Normal file
View file

@ -0,0 +1,44 @@
#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_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_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_seamless)
EXTERN_CVAR(Float, gl_mask_threshold)
EXTERN_CVAR(Float, gl_mask_sprite_threshold)
#endif // _GL_INTERN_H

View file

@ -0,0 +1,541 @@
/*
** 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_interface.h"
#include "gl/system/gl_framebuffer.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_renderstate.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_LoadExtensions();
void gl_PrintStartupLog();
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;
SetVSync(vid_vsync);
}
OpenGLFrameBuffer::~OpenGLFrameBuffer()
{
delete GLRenderer;
GLRenderer = NULL;
}
//==========================================================================
//
// Initializes the GL renderer
//
//==========================================================================
void OpenGLFrameBuffer::InitializeState()
{
static bool first=true;
if (first)
{
ogl_LoadFunctions();
}
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
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepth(1.0f);
glDepthFunc(GL_LESS);
glEnable(GL_DITHER);
glDisable(GL_CULL_FACE);
glDisable(GL_POLYGON_OFFSET_FILL);
glEnable(GL_POLYGON_OFFSET_LINE);
glEnable(GL_BLEND);
glEnable(GL_DEPTH_CLAMP);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glDisable(GL_LINE_SMOOTH);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
int trueH = GetTrueHeight();
int h = GetHeight();
glViewport(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();
glFinish();
if (needsetgamma)
{
//DoSetGamma();
needsetgamma = false;
}
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<float>(Gamma, 0.1f, 4.f);
float contrast = clamp<float>(vid_contrast, 0.1f, 3.f);
float bright = clamp<float>(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<double>(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;i<TexMan.NumTextures(); i++)
{
// HIT_Wall must be checked for MBF-style sky transfers.
if (hitlist[i] & (FTextureManager::HIT_Sky|FTextureManager::HIT_Wall))
{
FTexture *tex = TexMan.ByIndex(i);
if (tex->gl_info.bSkybox)
{
FSkyBox *sb = static_cast<FSkyBox*>(tex);
for(int i=0;i<6;i++)
{
if (sb->faces[i])
{
int index = sb->faces[i]->id.GetIndex();
hitlist[index] |= FTextureManager::HIT_Flat;
}
}
}
}
}
// 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_RenderState.mViewMatrix.loadIdentity();
gl_RenderState.mProjectionMatrix.ortho(0, GetWidth(), GetHeight(), 0, -1.0f, 1.0f);
gl_RenderState.ApplyMatrices();
glDisable(GL_DEPTH_TEST);
// Korshun: ENABLE AUTOMAP ANTIALIASING!!!
if (gl_aalines)
glEnable(GL_LINE_SMOOTH);
else
{
glDisable(GL_MULTISAMPLE);
glDisable(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);
glReadPixels(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();
}

View file

@ -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

View file

@ -0,0 +1,211 @@
/*
** r_opengl.cpp
**
** OpenGL system interface
**
**---------------------------------------------------------------------------
** Copyright 2005 Tim Stump
** Copyright 2005-2013 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 "v_text.h"
#include "gl/system/gl_interface.h"
#include "gl/system/gl_cvars.h"
static TArray<FString> m_Extensions;
RenderContext gl;
int occlusion_type=0;
//==========================================================================
//
//
//
//==========================================================================
static void CollectExtensions()
{
const char *extension;
int max = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &max);
for(int i = 0; i < max; i++)
{
extension = (const char*)glGetStringi(GL_EXTENSIONS, i);
m_Extensions.Push(FString(extension));
}
}
//==========================================================================
//
//
//
//==========================================================================
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 InitContext()
{
gl.flags=0;
}
//==========================================================================
//
//
//
//==========================================================================
void gl_LoadExtensions()
{
InitContext();
CollectExtensions();
const char *version = Args->CheckValue("-glversion");
if (version == NULL) version = (const char*)glGetString(GL_VERSION);
else Printf("Emulating OpenGL v %s\n", version);
// Don't even start if it's lower than 3.0
if (strcmp(version, "3.0") < 0)
{
I_FatalError("Unsupported OpenGL version.\nAt least OpenGL 3.0 is required to run " GAMENAME ".\n");
}
// add 0.01 to account for roundoff errors making the number a tad smaller than the actual version
gl.version = strtod(version, NULL) + 0.01f;
gl.glslversion = strtod((char*)glGetString(GL_SHADING_LANGUAGE_VERSION), NULL) + 0.01f;
gl.vendorstring = (char*)glGetString(GL_VENDOR);
if (gl.version < 3.3f && !CheckExtension("GL_ARB_sampler_objects"))
{
I_FatalError("'GL_ARB_sampler_objects' extension not found. Please update your graphics driver.");
}
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 (!Args->CheckParm("-gl3"))
{
// don't use GL 4.x features when running in GL 3 emulation mode.
if (CheckExtension("GL_ARB_buffer_storage"))
{
// work around a problem with older AMD drivers: Their implementation of shader storage buffer objects is piss-poor and does not match uniform buffers even closely.
// Recent drivers, GL 4.4 don't have this problem, these can easily be recognized by also supporting the GL_ARB_buffer_storage extension.
if (CheckExtension("GL_ARB_shader_storage_buffer_object"))
{
// Shader storage buffer objects are broken on current Intel drivers.
if (strstr(gl.vendorstring, "Intel") == NULL)
{
gl.flags |= RFL_SHADER_STORAGE_BUFFER;
}
}
gl.flags |= RFL_BUFFER_STORAGE;
}
}
int v;
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &v);
gl.maxuniforms = v;
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &v);
gl.maxuniformblock = v;
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &v);
gl.uniformblockalignment = v;
glGetIntegerv(GL_MAX_TEXTURE_SIZE,&gl.max_texturesize);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}
//==========================================================================
//
//
//
//==========================================================================
void gl_PrintStartupLog()
{
int v = 0;
glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &v);
Printf ("GL_VENDOR: %s\n", glGetString(GL_VENDOR));
Printf ("GL_RENDERER: %s\n", glGetString(GL_RENDERER));
Printf ("GL_VERSION: %s (%s profile)\n", glGetString(GL_VERSION), (v & GL_CONTEXT_CORE_PROFILE_BIT)? "Core" : "Compatibility");
Printf ("GL_SHADING_LANGUAGE_VERSION: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION));
Printf ("GL_EXTENSIONS:");
for (unsigned i = 0; i < m_Extensions.Size(); i++)
{
Printf(" %s", m_Extensions[i].GetChars());
}
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &v);
Printf("\nMax. texture size: %d\n", 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);
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS, &v);
Printf ("Max. vertex uniforms: %d\n", v);
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &v);
Printf ("Max. uniform block size: %d\n", v);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &v);
Printf ("Uniform block alignment: %d\n", v);
glGetIntegerv(GL_MAX_VARYING_FLOATS, &v);
Printf ("Max. varying: %d\n", v);
glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, &v);
Printf("Max. combined shader storage blocks: %d\n", v);
glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &v);
Printf("Max. vertex shader storage blocks: %d\n", v);
}

View file

@ -0,0 +1,46 @@
#ifndef R_RENDER
#define R_RENDER
#include "basictypes.h"
enum RenderFlags
{
// [BB] Added texture compression flags.
RFL_TEXTURE_COMPRESSION=1,
RFL_TEXTURE_COMPRESSION_S3TC=2,
RFL_SHADER_STORAGE_BUFFER = 4,
RFL_BUFFER_STORAGE = 8
};
enum TexMode
{
TM_MODULATE = 0, // (r, g, b, a)
TM_MASK, // (1, 1, 1, a)
TM_OPAQUE, // (r, g, b, 1)
TM_INVERSE, // (1-r, 1-g, 1-b, a)
TM_REDTOALPHA, // (1, 1, 1, r)
TM_CLAMPY, // (r, g, b, (t >= 0.0 && t <= 1.0)? a:0)
};
struct RenderContext
{
unsigned int flags;
unsigned int maxuniforms;
unsigned int maxuniformblock;
unsigned int uniformblockalignment;
float version;
float glslversion;
int max_texturesize;
char * vendorstring;
int MaxLights() const
{
return maxuniforms>=2048? 128:64;
}
};
extern RenderContext gl;
#endif

1346
src/gl/system/gl_load.c Normal file

File diff suppressed because it is too large Load diff

1793
src/gl/system/gl_load.h Normal file

File diff suppressed because it is too large Load diff

74
src/gl/system/gl_menu.cpp Normal file
View file

@ -0,0 +1,74 @@
#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_interface.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_seamless, 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_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 HAVE_MMX
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 >= 7.0)
{
(*opt)->mValues.Delete(i);
}
}
}
#endif
}

121
src/gl/system/gl_system.h Normal file
View file

@ -0,0 +1,121 @@
#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
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501 // Support the mouse wheel and session notification.
#define _WIN32_IE 0x0500
#endif
#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 <windows.h>
#include <mmsystem.h>
#include <winsock.h>
#ifndef __WINE__
#include <dshow.h>
#endif
#include <d3d9.h>
//#include <dsound.h>
//#include <dinput.h>
//#include <lmcons.h>
//#include <shlobj.h>
#endif
#undef DWORD
#ifndef CALLBACK
#define CALLBACK
#endif
#include <math.h>
#include <float.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
//#include <direct.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <signal.h>
#if !defined(__APPLE__) && !defined(__FreeBSD__)
#include <malloc.h>
#endif
#include <time.h>
#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 <io.h>
#else
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//GL headers
#include "gl_load.h"
#if defined(__APPLE__)
#include <OpenGL/OpenGL.h>
#endif
#ifdef _WIN32
#define DWORD WINDOWS_DWORD // I don't want to depend on this throughout the GL code!
//#include "gl/api/wglext.h"
#ifndef __WINE__
#undef DWORD
#endif
#else
typedef unsigned char byte;
typedef float FLOAT;
template <typename T>
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 <SDL.h>
#endif
#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

View file

529
src/gl/system/gl_wipe.cpp Normal file
View file

@ -0,0 +1,529 @@
/*
** 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/system/gl_interface.h"
#include "gl/renderer/gl_renderer.h"
#include "gl/renderer/gl_renderstate.h"
#include "gl/system/gl_framebuffer.h"
#include "gl/shaders/gl_shader.h"
#include "gl/textures/gl_translate.h"
#include "gl/textures/gl_material.h"
#include "gl/textures/gl_samplers.h"
#include "gl/utility/gl_templates.h"
#include "gl/data/gl_vertexbuffer.h"
#ifndef _WIN32
struct POINT {
SDWORD x;
SDWORD y;
};
struct RECT {
SDWORD left;
SDWORD top;
SDWORD right;
SDWORD bottom;
};
#endif
//===========================================================================
//
// 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)
{
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, true);
wipestartscreen->CreateTexture(NULL, Width, Height, 0, false, 0);
GLRenderer->mSamplerManager->Bind(0, CLAMP_NOFILTER);
GLRenderer->mSamplerManager->Bind(1, CLAMP_NONE);
glFinish();
wipestartscreen->Bind(0, false, false);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(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, true);
wipeendscreen->CreateTexture(NULL, Width, Height, 0, false, 0);
GLRenderer->mSamplerManager->Bind(0, CLAMP_NOFILTER);
glFinish();
wipeendscreen->Bind(0, false, false);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, Width, Height);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(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);
glDisable(GL_DEPTH_TEST);
glDepthMask(false);
bool done = ScreenWipe->Run(ticks, this);
glDepthMask(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;
}
FMaterial::ClearLastTexture();
}
//==========================================================================
//
// 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.AlphaFunc(GL_GEQUAL, 0.f);
gl_RenderState.ResetColor();
gl_RenderState.Apply();
fb->wipestartscreen->Bind(0, 0, false);
FFlatVertex *ptr;
unsigned int offset, count;
ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(0, 0, 0, 0, vb);
ptr++;
ptr->Set(0, fb->Height, 0, 0, 0);
ptr++;
ptr->Set(fb->Width, 0, 0, ur, vb);
ptr++;
ptr->Set(fb->Width, fb->Height, 0, ur, 0);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP, &offset, &count);
fb->wipeendscreen->Bind(0, 0, false);
gl_RenderState.SetColorAlpha(0xffffff, clamp(Clock/32.f, 0.f, 1.f));
gl_RenderState.Apply();
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, offset, count);
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.5f);
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.ResetColor();
gl_RenderState.Apply();
fb->wipeendscreen->Bind(0, 0, false);
FFlatVertex *ptr;
ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(0, 0, 0, 0, vb);
ptr++;
ptr->Set(0, fb->Height, 0, 0, 0);
ptr++;
ptr->Set(fb->Width, 0, 0, ur, vb);
ptr++;
ptr->Set(fb->Width, fb->Height, 0, ur, 0);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
int i, dy;
bool done = false;
fb->wipestartscreen->Bind(0, 0, false);
// 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.
// No need for optimization. Wipes won't ever be drawn with anything else.
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;
ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(rect.left, rect.bottom, 0, rect.left / tw, rect.top / th);
ptr++;
ptr->Set(rect.left, rect.top, 0, rect.left / tw, rect.bottom / th);
ptr++;
ptr->Set(rect.right, rect.bottom, 0, rect.right / tw, rect.top / th);
ptr++;
ptr->Set(rect.right, rect.top, 0, rect.right / tw, rect.bottom / th);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP);
}
}
}
}
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, 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<int>((*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.AlphaFunc(GL_GEQUAL, 0.f);
gl_RenderState.ResetColor();
gl_RenderState.Apply();
fb->wipestartscreen->Bind(0, 0, false);
FFlatVertex *ptr;
unsigned int offset, count;
ptr = GLRenderer->mVBO->GetBuffer();
ptr->Set(0, 0, 0, 0, vb);
ptr++;
ptr->Set(0, fb->Height, 0, 0, 0);
ptr++;
ptr->Set(fb->Width, 0, 0, ur, vb);
ptr++;
ptr->Set(fb->Width, fb->Height, 0, ur, 0);
ptr++;
GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP, &offset, &count);
gl_RenderState.SetTextureMode(TM_MODULATE);
gl_RenderState.SetEffect(EFF_BURN);
gl_RenderState.ResetColor();
gl_RenderState.Apply();
// Burn the new screen on top of it.
fb->wipeendscreen->Bind(0, 0, false);
BurnTexture->CreateTexture(rgb_buffer, WIDTH, HEIGHT, 1, true, 0);
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, offset, count);
gl_RenderState.SetEffect(EFF_NONE);
// The fire may not always stabilize, so the wipe is forced to end
// after an arbitrary maximum time.
return done || (BurnTime > 40);
}

View file

@ -0,0 +1,156 @@
/*
** 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"
#include "gl/system/gl_interface.h"
//===========================================================================
//
// multi-format pixel copy with colormap application
// requires one of the previously defined conversion classes to work
//
//===========================================================================
template<class T>
void iCopyColors(unsigned char * pout, const unsigned char * pin, int count, int step)
{
int i;
for(i=0;i<count;i++)
{
if (T::A(pin) != 0)
{
pout[0]=T::R(pin);
pout[1]=T::G(pin);
pout[2]=T::B(pin);
pout[3]=T::A(pin);
}
pout+=4;
pin+=step;
}
}
typedef void (*CopyFunc)(unsigned char * pout, const unsigned char * pin, int count, int step);
static CopyFunc copyfuncs[]={
iCopyColors<cRGB>,
iCopyColors<cRGBA>,
iCopyColors<cIA>,
iCopyColors<cCMYK>,
iCopyColors<cBGR>,
iCopyColors<cBGRA>,
iCopyColors<cI16>,
iCopyColors<cRGB555>,
iCopyColors<cPalEntry>
};
//===========================================================================
//
// 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<srcheight;y++)
{
copyfuncs[ct](&buffer[y*Pitch], &patch[y*step_y], srcwidth, step_x);
}
}
}
//===========================================================================
//
// 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;
if (translation > 0)
{
PalEntry *ptrans = GLTranslationPalette::GetPalette(translation);
if (ptrans)
{
for (i = 0; i < 256; i++)
{
penew[i] = (ptrans[i] & 0xffffff) | (palette[i] & 0xff000000);
}
}
}
else
{
memcpy(penew, palette, 256*sizeof(PalEntry));
}
// convert the image according to the translated palette.
for (y=0;y<srcheight;y++)
{
pos=(y*Pitch);
for (x=0;x<srcwidth;x++,pos+=4)
{
int v=(unsigned char)patch[y*step_y+x*step_x];
if (penew[v].a!=0)
{
buffer[pos] = penew[v].r;
buffer[pos+1] = penew[v].g;
buffer[pos+2] = penew[v].b;
buffer[pos+3] = penew[v].a;
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show more